Annotation of quake2/rogue/g_newai.c, revision 1.1

1.1     ! root        1: 
        !             2: #include "g_local.h"
        !             3: 
        !             4: //===============================
        !             5: // BLOCKED Logic
        !             6: //===============================
        !             7: 
        !             8: /*
        !             9:                gi.WriteByte (svc_temp_entity);
        !            10:                gi.WriteByte (TE_DEBUGTRAIL);
        !            11:                gi.WritePosition (pt1);
        !            12:                gi.WritePosition (pt2);
        !            13:                gi.multicast (pt1, MULTICAST_PVS);      
        !            14: 
        !            15:                self->nextthink = level.time + 10;
        !            16: */
        !            17: 
        !            18: // plat states, copied from g_func.c
        !            19: 
        !            20: #define        STATE_TOP                       0
        !            21: #define        STATE_BOTTOM            1
        !            22: #define STATE_UP                       2
        !            23: #define STATE_DOWN                     3
        !            24: 
        !            25: qboolean face_wall (edict_t *self);
        !            26: 
        !            27: 
        !            28: // blocked_checkshot
        !            29: //     shotchance: 0-1, chance they'll take the shot if it's clear.
        !            30: qboolean blocked_checkshot (edict_t *self, float shotChance)
        !            31: {
        !            32:        qboolean        playerVisible;
        !            33: //     float           chance;
        !            34: 
        !            35:        if(!self->enemy)
        !            36:                return false;
        !            37: 
        !            38:        playerVisible = visible (self, self->enemy);
        !            39:        // always shoot at teslas
        !            40:        if(playerVisible)
        !            41:        {
        !            42:                if ((random() < shotChance) || (!strcmp(self->enemy->classname, "tesla")))
        !            43:                {
        !            44:                        if(g_showlogic && g_showlogic->value)
        !            45:                                gi.dprintf("blocked: taking a shot\n");
        !            46: 
        !            47:                        // turn on AI_BLOCKED to let the monster know the attack is being called
        !            48:                        // by the blocked functions...
        !            49:                        self->monsterinfo.aiflags |= AI_BLOCKED;
        !            50:                        
        !            51:                        if(self->monsterinfo.attack)
        !            52:                                self->monsterinfo.attack(self);
        !            53:                        
        !            54:                        self->monsterinfo.aiflags &= ~AI_BLOCKED;
        !            55:                        return true;
        !            56:                }
        !            57:        }
        !            58: /*
        !            59:        // if we're in coop and player is not visible, check for another player 25% of time.
        !            60:        else if (coop->value)   
        !            61:        {
        !            62:                // spawned monsters are spread out between enemies already, so make
        !            63:                // them less likely to change targets.
        !            64:                if (self->monsterinfo.aiflags & AI_SPAWNED_MASK)
        !            65:                        chance = 0.10;
        !            66:                else
        !            67:                        chance = 0.25;
        !            68: 
        !            69:                if (random() <= chance)
        !            70:                {
        !            71:                        if(blocked_checknewenemy (self))
        !            72:                        {
        !            73:                                return true;
        !            74:                        }
        !            75:                }
        !            76:        }
        !            77: */
        !            78:        return false;
        !            79: }
        !            80: 
        !            81: // blocked_checkplat
        !            82: //     dist: how far they are trying to walk.
        !            83: qboolean blocked_checkplat (edict_t *self, float dist)
        !            84: {
        !            85:        int                     playerPosition;
        !            86:        trace_t         trace;
        !            87:        vec3_t          pt1, pt2;
        !            88:        vec3_t          forward;
        !            89:        edict_t         *plat;
        !            90: 
        !            91:        if(!self->enemy)
        !            92:                return false;
        !            93: 
        !            94:        // check player's relative altitude
        !            95:        if(self->enemy->absmin[2] >= self->absmax[2])
        !            96:                playerPosition = 1;
        !            97:        else if(self->enemy->absmax[2] <= self->absmin[2])
        !            98:                playerPosition = -1;
        !            99:        else
        !           100:                playerPosition = 0;
        !           101: 
        !           102:        // if we're close to the same position, don't bother trying plats.
        !           103:        if(playerPosition == 0)
        !           104:                return false;
        !           105: 
        !           106:        plat = NULL;
        !           107: 
        !           108:        // see if we're already standing on a plat.
        !           109:        if(self->groundentity && self->groundentity != world)
        !           110:        {
        !           111:                if(!strncmp(self->groundentity->classname, "func_plat", 8))
        !           112:                        plat = self->groundentity;
        !           113:        }
        !           114: 
        !           115:        // if we're not, check to see if we'll step onto one with this move
        !           116:        if(!plat)
        !           117:        {
        !           118:                AngleVectors (self->s.angles, forward, NULL, NULL);
        !           119:                VectorMA(self->s.origin, dist, forward, pt1);
        !           120:                VectorCopy (pt1, pt2);
        !           121:                pt2[2] -= 384;
        !           122: 
        !           123:                trace = gi.trace(pt1, vec3_origin, vec3_origin, pt2, self, MASK_MONSTERSOLID);
        !           124:                if(trace.fraction < 1 && !trace.allsolid && !trace.startsolid)
        !           125:                {
        !           126:                        if(!strncmp(trace.ent->classname, "func_plat", 8))
        !           127:                        {
        !           128:                                plat = trace.ent;
        !           129:                        }
        !           130:                }
        !           131:        }
        !           132: 
        !           133:        // if we've found a plat, trigger it.
        !           134:        if(plat && plat->use)
        !           135:        {
        !           136:                if (playerPosition == 1)
        !           137:                {
        !           138:                        if((self->groundentity == plat && plat->moveinfo.state == STATE_BOTTOM) ||
        !           139:                                (self->groundentity != plat && plat->moveinfo.state == STATE_TOP))
        !           140:                        {
        !           141:                                if(g_showlogic && g_showlogic->value)
        !           142:                                        gi.dprintf("player above, and plat will raise. using!\n");
        !           143:                                plat->use (plat, self, self);
        !           144:                                return true;                    
        !           145:                        }
        !           146:                }
        !           147:                else if(playerPosition == -1)
        !           148:                {
        !           149:                        if((self->groundentity == plat && plat->moveinfo.state == STATE_TOP) ||
        !           150:                                (self->groundentity != plat && plat->moveinfo.state == STATE_BOTTOM))
        !           151:                        {
        !           152:                                if(g_showlogic && g_showlogic->value)
        !           153:                                        gi.dprintf("player below, and plat will lower. using!\n");
        !           154:                                plat->use (plat, self, self);
        !           155:                                return true;
        !           156:                        }
        !           157:                }
        !           158: //             if(g_showlogic && g_showlogic->value)
        !           159: //                     gi.dprintf("hit a plat, not using. ppos: %d   plat: %d\n", playerPosition, plat->moveinfo.state);
        !           160:        }
        !           161: 
        !           162:        return false;
        !           163: }
        !           164: 
        !           165: // blocked_checkjump
        !           166: //     dist: how far they are trying to walk.
        !           167: //  maxDown/maxUp: how far they'll ok a jump for. set to 0 to disable that direction.
        !           168: qboolean blocked_checkjump (edict_t *self, float dist, float maxDown, float maxUp)
        !           169: {
        !           170:        int                     playerPosition;
        !           171:        trace_t         trace;
        !           172:        vec3_t          pt1, pt2;
        !           173:        vec3_t          forward, up;
        !           174: 
        !           175:        if(!self->enemy)
        !           176:                return false;
        !           177: 
        !           178:        AngleVectors (self->s.angles, forward, NULL, up);
        !           179: 
        !           180:        if(self->enemy->absmin[2] > (self->absmin[2] + 16))
        !           181:                playerPosition = 1;
        !           182:        else if(self->enemy->absmin[2] < (self->absmin[2] - 16))
        !           183:                playerPosition = -1;
        !           184:        else
        !           185:                playerPosition = 0;
        !           186: 
        !           187:        if(playerPosition == -1 && maxDown)
        !           188:        {
        !           189:                // check to make sure we can even get to the spot we're going to "fall" from
        !           190:                VectorMA(self->s.origin, 48, forward, pt1);
        !           191:                trace = gi.trace(self->s.origin, self->mins, self->maxs, pt1, self, MASK_MONSTERSOLID);
        !           192:                if(trace.fraction < 1)
        !           193:                {
        !           194: //                     gi.dprintf("can't get thar from hear...\n");
        !           195:                        return false;
        !           196:                }
        !           197: 
        !           198:                VectorCopy (pt1, pt2);
        !           199:                pt2[2] = self->mins[2] - maxDown - 1;
        !           200: 
        !           201:                trace = gi.trace(pt1, vec3_origin, vec3_origin, pt2, self, MASK_MONSTERSOLID | MASK_WATER);
        !           202:                if(trace.fraction < 1 && !trace.allsolid && !trace.startsolid)
        !           203:                {
        !           204:                        if((self->absmin[2] - trace.endpos[2]) >= 24 && trace.contents & MASK_SOLID)
        !           205:                        {
        !           206:                                if( (self->enemy->absmin[2] - trace.endpos[2]) > 32)
        !           207:                                {
        !           208: //                                     if(g_showlogic && g_showlogic->value)
        !           209: //                                             gi.dprintf("That'll take me too far down...%0.1f\n", (self->enemy->absmin[2] - trace.endpos[2]));
        !           210:                                        return false;
        !           211:                                }       
        !           212: 
        !           213:                                if(trace.plane.normal[2] < 0.9)
        !           214:                                {
        !           215: //                                     gi.dprintf("Floor angle too much! %s\n", vtos(trace.plane.normal));
        !           216:                                        return false;
        !           217:                                }
        !           218: //                             if(g_showlogic && g_showlogic->value)
        !           219: //                                     gi.dprintf("Geronimo! %0.1f\n", (self->absmin[2] - trace.endpos[2]));
        !           220:                                return true;
        !           221:                        }
        !           222: //                     else if(g_showlogic && g_showlogic->value)
        !           223: //                     {
        !           224: //                             if(!(trace.contents & MASK_SOLID))
        !           225: //                                     gi.dprintf("Ooooh... Bad stuff down there...\n");
        !           226: //                             else
        !           227: //                                     gi.dprintf("Too far to fall\n");
        !           228: //                     }
        !           229:                }
        !           230: //             else if(g_showlogic && g_showlogic->value)
        !           231: //                     gi.dprintf("Ooooh... Too far to fall...\n");
        !           232:        }
        !           233:        else if(playerPosition == 1 && maxUp)
        !           234:        {
        !           235:                VectorMA(self->s.origin, 48, forward, pt1);
        !           236:                VectorCopy(pt1, pt2);
        !           237:                pt1[2] = self->absmax[2] + maxUp;
        !           238: 
        !           239:                trace = gi.trace(pt1, vec3_origin, vec3_origin, pt2, self, MASK_MONSTERSOLID | MASK_WATER);
        !           240:                if(trace.fraction < 1 && !trace.allsolid && !trace.startsolid)
        !           241:                {
        !           242:                        if((trace.endpos[2] - self->absmin[2]) <= maxUp && trace.contents & MASK_SOLID)
        !           243:                        {
        !           244: //                             if(g_showlogic && g_showlogic->value)
        !           245: //                                     gi.dprintf("Jumping Up! %0.1f\n", (trace.endpos[2] - self->absmin[2]));
        !           246:                                
        !           247:                                face_wall(self);
        !           248:                                return true;
        !           249:                        }
        !           250: //                     else if(g_showlogic && g_showlogic->value)
        !           251: //                             gi.dprintf("Too high to jump %0.1f\n", (trace.endpos[2] - self->absmin[2]));
        !           252:                }
        !           253: //             else if(g_showlogic && g_showlogic->value)
        !           254: //                             gi.dprintf("Not something I could jump onto\n");
        !           255:        }
        !           256: //     else if(g_showlogic && g_showlogic->value)
        !           257: //             gi.dprintf("Player at similar level. No need to jump up?\n");
        !           258: 
        !           259:        return false;
        !           260: }
        !           261: 
        !           262: // checks to see if another coop player is nearby, and will switch.
        !           263: qboolean blocked_checknewenemy (edict_t *self)
        !           264: {
        !           265: /*
        !           266:        int             player;
        !           267:        edict_t *ent;
        !           268: 
        !           269:        if (!(coop->value))
        !           270:                return false;
        !           271: 
        !           272:        for (player = 1; player <= game.maxclients; player++)
        !           273:        {
        !           274:                ent = &g_edicts[player];
        !           275:                if (!ent->inuse)
        !           276:                        continue;
        !           277:                if (!ent->client)
        !           278:                        continue;
        !           279:                if (ent == self->enemy)
        !           280:                        continue;
        !           281: 
        !           282:                if (visible (self, ent))
        !           283:                {
        !           284:                        if (g_showlogic && g_showlogic->value)
        !           285:                                gi.dprintf ("B_CNE: %s acquired new enemy %s\n", self->classname, ent->client->pers.netname);
        !           286: 
        !           287:                        self->enemy = ent;
        !           288:                        FoundTarget (self);
        !           289:                        return true;
        !           290:                }
        !           291:        }
        !           292: 
        !           293:        return false;
        !           294: */
        !           295:        return false;
        !           296: }
        !           297: 
        !           298: // *************************
        !           299: // HINT PATHS
        !           300: // *************************
        !           301: 
        !           302: #define HINT_ENDPOINT          0x0001
        !           303: #define        MAX_HINT_CHAINS         100
        !           304: 
        !           305: int    hint_paths_present;
        !           306: edict_t *hint_path_start[MAX_HINT_CHAINS];
        !           307: int    num_hint_paths;
        !           308: 
        !           309: //
        !           310: // AI code
        !           311: //
        !           312: 
        !           313: // =============
        !           314: // hintpath_findstart - given any hintpath node, finds the start node
        !           315: // =============
        !           316: edict_t        *hintpath_findstart(edict_t *ent)
        !           317: {
        !           318:        edict_t         *e;
        !           319:        edict_t         *last;
        !           320:        int                     field;
        !           321: 
        !           322:        if(ent->target)         // starting point
        !           323:        {
        !           324:                last = world;
        !           325:                field = FOFS(targetname);
        !           326:                e = G_Find(NULL, field, ent->target);
        !           327:                while(e)
        !           328:                {
        !           329:                        last = e;
        !           330:                        if(!e->target)
        !           331:                                break;
        !           332:                        e = G_Find(NULL, field, e->target);
        !           333:                }
        !           334:        }
        !           335:        else                            // end point
        !           336:        {
        !           337:                last = world;
        !           338:                field = FOFS(target);
        !           339:                e = G_Find(NULL, field, ent->targetname);
        !           340:                while(e)
        !           341:                {
        !           342:                        last = e;
        !           343:                        if(!e->targetname)
        !           344:                                break;
        !           345:                        e = G_Find(NULL, field, e->targetname);
        !           346:                }
        !           347:        }
        !           348: 
        !           349:        if(!(last->spawnflags & HINT_ENDPOINT))
        !           350:        {
        !           351:                gi.dprintf ("end of chain is not HINT_ENDPOINT\n");
        !           352:                return NULL;
        !           353:        }
        !           354: 
        !           355:        if(last == world)
        !           356:                last = NULL;
        !           357:        return last;
        !           358: }
        !           359: 
        !           360: // =============
        !           361: // hintpath_other_end - given one endpoint of a hintpath, returns the other end.
        !           362: // =============
        !           363: edict_t        *hintpath_other_end(edict_t *ent)
        !           364: {
        !           365:        edict_t         *e;
        !           366:        edict_t         *last;
        !           367:        int                     field;
        !           368: 
        !           369:        if(ent->target)         // starting point
        !           370:        {
        !           371:                last = world;
        !           372:                field = FOFS(targetname);
        !           373:                e = G_Find(NULL, field, ent->target);
        !           374:                while(e)
        !           375:                {
        !           376:                        last = e;
        !           377:                        if(!e->target)
        !           378:                                break;
        !           379:                        e = G_Find(NULL, field, e->target);
        !           380:                }
        !           381:        }
        !           382:        else                            // end point
        !           383:        {
        !           384:                last = world;
        !           385:                field = FOFS(target);
        !           386:                e = G_Find(NULL, field, ent->targetname);
        !           387:                while(e)
        !           388:                {
        !           389:                        last = e;
        !           390:                        if(!e->targetname)
        !           391:                                break;
        !           392:                        e = G_Find(NULL, field, e->targetname);
        !           393:                }
        !           394:        }
        !           395: 
        !           396:        if(!(last->spawnflags & HINT_ENDPOINT))
        !           397:        {
        !           398:                gi.dprintf ("end of chain is not HINT_ENDPOINT\n");
        !           399:                return NULL;
        !           400:        }
        !           401: 
        !           402:        if(last == world)
        !           403:                last = NULL;
        !           404:        return last;
        !           405: }
        !           406: 
        !           407: // =============
        !           408: // hintpath_go - starts a monster (self) moving towards the hintpath (point)
        !           409: //             disables all contrary AI flags.
        !           410: // =============
        !           411: void hintpath_go (edict_t *self, edict_t *point)
        !           412: {
        !           413:        vec3_t  dir;
        !           414:        vec3_t  angles;
        !           415: 
        !           416:        VectorSubtract(point->s.origin, self->s.origin, dir);
        !           417:        vectoangles2(dir, angles);
        !           418: 
        !           419:        self->ideal_yaw = angles[YAW];
        !           420:        self->goalentity = self->movetarget = point;
        !           421:        self->monsterinfo.pausetime = 0;
        !           422:        self->monsterinfo.aiflags |= AI_HINT_PATH;
        !           423:        self->monsterinfo.aiflags &= ~(AI_SOUND_TARGET | AI_PURSUIT_LAST_SEEN | AI_PURSUE_NEXT | AI_PURSUE_TEMP);
        !           424:        // run for it
        !           425:        self->monsterinfo.search_time = level.time;
        !           426:        self->monsterinfo.run (self);
        !           427: }
        !           428: 
        !           429: // =============
        !           430: // hintpath_stop - bails a monster out of following hint paths
        !           431: // =============
        !           432: void hintpath_stop (edict_t *self)
        !           433: {
        !           434:        self->goalentity = NULL;
        !           435:        self->movetarget = NULL;
        !           436: //     self->monsterinfo.last_hint = NULL;
        !           437:        self->monsterinfo.last_hint_time = level.time;
        !           438:        self->monsterinfo.goal_hint = NULL;
        !           439:        self->monsterinfo.aiflags &= ~AI_HINT_PATH;
        !           440:        if((has_valid_enemy(self)) && (visible (self, self->enemy)))
        !           441:                FoundTarget (self);
        !           442:        else
        !           443:        {
        !           444:                // we need the pausetime otherwise the stand code
        !           445:                // will just revert to walking with no target and
        !           446:                // the monsters will wonder around aimlessly trying
        !           447:                // to hunt the world entity
        !           448:                self->monsterinfo.pausetime = level.time + 100000000;
        !           449:                self->monsterinfo.stand (self);
        !           450:        }
        !           451:        return;
        !           452: }
        !           453: 
        !           454: // =============
        !           455: // monsterlost_checkhint - the monster (self) will check around for valid hintpaths.
        !           456: //             a valid hintpath is one where the two endpoints can see both the monster
        !           457: //             and the monster's enemy. if only one person is visible from the endpoints,
        !           458: //             it will not go for it.
        !           459: // =============
        !           460: qboolean monsterlost_checkhint2 (edict_t *self);
        !           461: 
        !           462: qboolean monsterlost_checkhint (edict_t *self)
        !           463: {
        !           464:        edict_t         *e, *monster_pathchain, *target_pathchain, *checkpoint;
        !           465:        edict_t         *closest;
        !           466:        float           closest_range = 1000000;
        !           467:        edict_t         *start, *destination;
        !           468:        int                     field;
        !           469:        int                     count1=0, count2=0, count3=0, count4=0, count5=0;
        !           470:        float           r;
        !           471:        int                     i;
        !           472:        qboolean        hint_path_represented[MAX_HINT_CHAINS];
        !           473: 
        !           474:        // if there are no hint paths on this map, exit immediately.
        !           475:        if(!hint_paths_present)
        !           476:                return false;
        !           477: 
        !           478:        if(!self->enemy)
        !           479:                return false;
        !           480: 
        !           481:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
        !           482:                return false;
        !           483:        
        !           484:        if (!strcmp(self->classname, "monster_turret"))
        !           485:                return false;
        !           486: 
        !           487:        monster_pathchain = NULL;
        !           488: 
        !           489:        field = FOFS(classname);
        !           490: 
        !           491:        // find all the hint_paths.
        !           492:        // FIXME - can we not do this every time?
        !           493:        for (i=0; i < num_hint_paths; i++)
        !           494:        {
        !           495:                e = hint_path_start[i];
        !           496:                while(e)
        !           497:                {
        !           498:                        count1++;
        !           499:                        if (e->monster_hint_chain)
        !           500:                        {
        !           501: //                             gi.dprintf ("uh, oh, I didn't clean up after myself\n");
        !           502:                                e->monster_hint_chain = NULL;
        !           503:                        }
        !           504:                        if (monster_pathchain)
        !           505:                        {
        !           506:                                checkpoint->monster_hint_chain = e;
        !           507:                                checkpoint = e;
        !           508:                        }
        !           509:                        else
        !           510:                        {
        !           511:                                monster_pathchain = e;
        !           512:                                checkpoint = e;
        !           513:                        }
        !           514:                        e = e->hint_chain;
        !           515:                }
        !           516:        }
        !           517:        
        !           518:        // filter them by distance and visibility to the monster
        !           519:        e = monster_pathchain;
        !           520:        checkpoint = NULL;
        !           521:        while (e)
        !           522:        {
        !           523:                r = realrange (self, e);
        !           524: 
        !           525: //             if (r > 512)
        !           526: //                     count3++;
        !           527: 
        !           528:                if (r > 512)
        !           529:                {
        !           530:                        count2++;
        !           531:                        if (g_showlogic && g_showlogic->value)
        !           532:                        {
        !           533:                                gi.dprintf ("MONSTER (%s) DISTANCE:  ", self->classname);
        !           534:                                if (e->targetname)
        !           535:                                        gi.dprintf ("targetname %s\n", e->targetname);
        !           536:                                else
        !           537:                                        gi.dprintf ("start -> %s\n", e->target);
        !           538:                        }
        !           539:                        if (checkpoint)
        !           540:                        {
        !           541:                                checkpoint->monster_hint_chain = e->monster_hint_chain;
        !           542:                                e->monster_hint_chain = NULL;
        !           543:                                e = checkpoint->monster_hint_chain;
        !           544:                                continue;
        !           545:                        }
        !           546:                        else
        !           547:                        {
        !           548:                                // use checkpoint as temp pointer
        !           549:                                checkpoint = e;
        !           550:                                e = e->monster_hint_chain;
        !           551:                                checkpoint->monster_hint_chain = NULL;
        !           552:                                // and clear it again
        !           553:                                checkpoint = NULL;
        !           554:                                // since we have yet to find a valid one (or else checkpoint would be set) move the
        !           555:                                // start of monster_pathchain
        !           556:                                monster_pathchain = e;
        !           557:                                continue;
        !           558:                        }
        !           559:                }
        !           560:                if (!visible(self, e))
        !           561:                {
        !           562:                        count4++;
        !           563:                        if (g_showlogic && g_showlogic->value)
        !           564:                        {
        !           565:                                gi.dprintf ("MONSTER (%s) VISIBILITY:  ", self->classname);
        !           566:                                if (e->targetname)
        !           567:                                        gi.dprintf ("targetname %s\n", e->targetname);
        !           568:                                else
        !           569:                                        gi.dprintf ("start -> %s\n", e->target);
        !           570:                        }
        !           571:                        if (checkpoint)
        !           572:                        {
        !           573:                                checkpoint->monster_hint_chain = e->monster_hint_chain;
        !           574:                                e->monster_hint_chain = NULL;
        !           575:                                e = checkpoint->monster_hint_chain;
        !           576:                                continue;
        !           577:                        }
        !           578:                        else
        !           579:                        {
        !           580:                                // use checkpoint as temp pointer
        !           581:                                checkpoint = e;
        !           582:                                e = e->monster_hint_chain;
        !           583:                                checkpoint->monster_hint_chain = NULL;
        !           584:                                // and clear it again
        !           585:                                checkpoint = NULL;
        !           586:                                // since we have yet to find a valid one (or else checkpoint would be set) move the
        !           587:                                // start of monster_pathchain
        !           588:                                monster_pathchain = e;
        !           589:                                continue;
        !           590:                        }
        !           591:                }
        !           592:                // if it passes all the tests, it's a keeper
        !           593:                if (g_showlogic && g_showlogic->value)
        !           594:                {
        !           595:                        gi.dprintf ("MONSTER (%s) ACCEPT:  ", self->classname);
        !           596:                        if (e->targetname)
        !           597:                                gi.dprintf ("targetname %s\n", e->targetname);
        !           598:                        else
        !           599:                                gi.dprintf ("start -> %s\n", e->target);
        !           600:                }
        !           601:                count5++;
        !           602:                checkpoint = e;
        !           603:                e = e->monster_hint_chain;
        !           604:        }
        !           605: 
        !           606:        // at this point, we have a list of all of the eligible hint nodes for the monster
        !           607:        // we now take them, figure out what hint chains they're on, and traverse down those chains,
        !           608:        // seeing whether any can see the player
        !           609:        //
        !           610:        // first, we figure out which hint chains we have represented in monster_pathchain
        !           611:        if (count5 == 0)
        !           612:        {
        !           613:                if ((g_showlogic) && (g_showlogic->value))
        !           614:                        gi.dprintf ("No eligible hint paths found.\n");
        !           615:                return false;
        !           616:        }
        !           617: 
        !           618:        for (i=0; i < num_hint_paths; i++)
        !           619:        {
        !           620:                hint_path_represented[i] = false;
        !           621:        }
        !           622:        e = monster_pathchain;
        !           623:        checkpoint = NULL;
        !           624:        while (e)
        !           625:        {
        !           626:                if ((e->hint_chain_id < 0) || (e->hint_chain_id > num_hint_paths))
        !           627:                {
        !           628:                        if (g_showlogic && g_showlogic->value)
        !           629:                                gi.dprintf ("bad hint_chain_id! %d\n", e->hint_chain_id);
        !           630:                        return false;
        !           631:                }
        !           632:                hint_path_represented[e->hint_chain_id] = true;
        !           633:                e = e->monster_hint_chain;
        !           634:        }
        !           635: 
        !           636:        count1 = 0;
        !           637:        count2 = 0;
        !           638:        count3 = 0;
        !           639:        count4 = 0;
        !           640:        count5 = 0;
        !           641: 
        !           642:        // now, build the target_pathchain which contains all of the hint_path nodes we need to check for
        !           643:        // validity (within range, visibility)
        !           644:        target_pathchain = NULL;
        !           645:        checkpoint = NULL;
        !           646:        for (i=0; i < num_hint_paths; i++)
        !           647:        {
        !           648:                // if this hint chain is represented in the monster_hint_chain, add all of it's nodes to the target_pathchain
        !           649:                // for validity checking
        !           650:                if (hint_path_represented[i])
        !           651:                {
        !           652:                        e = hint_path_start[i];
        !           653:                        while (e)
        !           654:                        {
        !           655:                                if (target_pathchain)
        !           656:                                {
        !           657:                                        checkpoint->target_hint_chain = e;
        !           658:                                        checkpoint = e;
        !           659:                                }
        !           660:                                else
        !           661:                                {
        !           662:                                        target_pathchain = e;
        !           663:                                        checkpoint = e;
        !           664:                                }
        !           665:                                e = e->hint_chain;
        !           666:                        }
        !           667:                }
        !           668:        }
        !           669: 
        !           670:        // target_pathchain is a list of all of the hint_path nodes we need to check for validity relative to the target
        !           671:        e = target_pathchain;
        !           672:        checkpoint = NULL;
        !           673:        while (e)
        !           674:        {
        !           675:                r = realrange (self->enemy, e);
        !           676: 
        !           677: //             if (r > 512)
        !           678: //                     count3++;
        !           679: 
        !           680:                if (r > 512)
        !           681:                {
        !           682:                        count2++;
        !           683:                        if (g_showlogic && g_showlogic->value)
        !           684:                        {
        !           685:                                gi.dprintf ("TARGET RANGE:  ");
        !           686:                                if (e->targetname)
        !           687:                                        gi.dprintf ("targetname %s\n", e->targetname);
        !           688:                                else
        !           689:                                        gi.dprintf ("start -> %s\n", e->target);
        !           690:                        }
        !           691:                        if (checkpoint)
        !           692:                        {
        !           693:                                checkpoint->target_hint_chain = e->target_hint_chain;
        !           694:                                e->target_hint_chain = NULL;
        !           695:                                e = checkpoint->target_hint_chain;
        !           696:                                continue;
        !           697:                        }
        !           698:                        else
        !           699:                        {
        !           700:                                // use checkpoint as temp pointer
        !           701:                                checkpoint = e;
        !           702:                                e = e->target_hint_chain;
        !           703:                                checkpoint->target_hint_chain = NULL;
        !           704:                                // and clear it again
        !           705:                                checkpoint = NULL;
        !           706:                                target_pathchain = e;
        !           707:                                continue;
        !           708:                        }
        !           709:                }
        !           710:                if (!visible(self->enemy, e))
        !           711:                {
        !           712:                        count4++;
        !           713:                        if (g_showlogic && g_showlogic->value)
        !           714:                        {
        !           715:                                gi.dprintf ("TARGET VISIBILITY:  ");
        !           716:                                if (e->targetname)
        !           717:                                        gi.dprintf ("targetname %s\n", e->targetname);
        !           718:                                else
        !           719:                                        gi.dprintf ("start -> %s\n", e->target);
        !           720:                        }
        !           721:                        if (checkpoint)
        !           722:                        {
        !           723:                                checkpoint->target_hint_chain = e->target_hint_chain;
        !           724:                                e->target_hint_chain = NULL;
        !           725:                                e = checkpoint->target_hint_chain;
        !           726:                                continue;
        !           727:                        }
        !           728:                        else
        !           729:                        {
        !           730:                                // use checkpoint as temp pointer
        !           731:                                checkpoint = e;
        !           732:                                e = e->target_hint_chain;
        !           733:                                checkpoint->target_hint_chain = NULL;
        !           734:                                // and clear it again
        !           735:                                checkpoint = NULL;
        !           736:                                target_pathchain = e;
        !           737:                                continue;
        !           738:                        }
        !           739:                }
        !           740:                // if it passes all the tests, it's a keeper
        !           741:                if (g_showlogic && g_showlogic->value)
        !           742:                {
        !           743:                        gi.dprintf ("TARGET ACCEPT:  ");
        !           744:                        if (e->targetname)
        !           745:                                gi.dprintf ("targetname %s\n", e->targetname);
        !           746:                        else
        !           747:                                gi.dprintf ("start -> %s\n", e->target);
        !           748:                }
        !           749:                count5++;
        !           750:                checkpoint = e;
        !           751:                e = e->target_hint_chain;
        !           752:        }
        !           753:        
        !           754:        // at this point we should have:
        !           755:        // monster_pathchain - a list of "monster valid" hint_path nodes linked together by monster_hint_chain
        !           756:        // target_pathcain - a list of "target valid" hint_path nodes linked together by target_hint_chain.  these
        !           757:        //                   are filtered such that only nodes which are on the same chain as "monster valid" nodes
        !           758:        //
        !           759:        // Now, we figure out which "monster valid" node we want to use
        !           760:        // 
        !           761:        // To do this, we first off make sure we have some target nodes.  If we don't, there are no valid hint_path nodes
        !           762:        // for us to take
        !           763:        //
        !           764:        // If we have some, we filter all of our "monster valid" nodes by which ones have "target valid" nodes on them
        !           765:        //
        !           766:        // Once this filter is finished, we select the closest "monster valid" node, and go to it.
        !           767: 
        !           768:        if (count5 == 0)
        !           769:        {
        !           770:                if ((g_showlogic) && (g_showlogic->value))
        !           771:                        gi.dprintf ("No valid target nodes found\n");
        !           772:                return false;
        !           773:        }
        !           774: 
        !           775:        // reuse the hint_chain_represented array, this time to see which chains are represented by the target
        !           776:        for (i=0; i < num_hint_paths; i++)
        !           777:        {
        !           778:                hint_path_represented[i] = false;
        !           779:        }
        !           780: 
        !           781:        e = target_pathchain;
        !           782:        checkpoint = NULL;
        !           783:        while (e)
        !           784:        {
        !           785:                if ((e->hint_chain_id < 0) || (e->hint_chain_id > num_hint_paths))
        !           786:                {
        !           787:                        gi.dprintf ("bad hint_chain_id! %d\n", e->hint_chain_id);
        !           788:                        return false;
        !           789:                }
        !           790:                hint_path_represented[e->hint_chain_id] = true;
        !           791:                e = e->target_hint_chain;
        !           792:        }
        !           793:        
        !           794:        // traverse the monster_pathchain - if the hint_node isn't represented in the "target valid" chain list, 
        !           795:        // remove it
        !           796:        // if it is on the list, check it for range from the monster.  If the range is the closest, keep it
        !           797:        //
        !           798:        closest = NULL;
        !           799:        e = monster_pathchain;
        !           800:        while (e)
        !           801:        {
        !           802:                if (!(hint_path_represented[e->hint_chain_id]))
        !           803:                {
        !           804:                        checkpoint = e->monster_hint_chain;
        !           805:                        e->monster_hint_chain = NULL;
        !           806:                        e = checkpoint;
        !           807:                        continue;
        !           808:                }
        !           809:                r = realrange(self, e);
        !           810:                if (r < closest_range)
        !           811:                        closest = e;
        !           812:                e = e->monster_hint_chain;
        !           813:        }
        !           814: 
        !           815:        if (!closest)
        !           816:        {
        !           817:                if ((g_showlogic) && (g_showlogic->value))
        !           818:                        gi.dprintf ("Failed to find closest node for monster.  Shouldn't happen.\n");
        !           819:                return false;
        !           820:        }
        !           821: 
        !           822:        start = closest;
        !           823:        // now we know which one is the closest to the monster .. this is the one the monster will go to
        !           824:        // we need to finally determine what the DESTINATION node is for the monster .. walk down the hint_chain,
        !           825:        // and find the closest one to the player
        !           826: 
        !           827:        closest = NULL;
        !           828:        closest_range = 10000000;
        !           829:        e = target_pathchain;
        !           830:        while (e)
        !           831:        {
        !           832:                if (start->hint_chain_id == e->hint_chain_id)
        !           833:                {
        !           834:                        r = realrange(self, e);
        !           835:                        if (r < closest_range)
        !           836:                                closest = e;
        !           837:                }
        !           838:                e = e->target_hint_chain;
        !           839:        }
        !           840: 
        !           841:        if (!closest)
        !           842:        {
        !           843:                if ((g_showlogic) && (g_showlogic->value))
        !           844:                        gi.dprintf ("Failed to find closest node for target.  Shouldn't happen.\n");
        !           845:                return false;
        !           846:        }
        !           847:        
        !           848:        destination = closest;
        !           849: 
        !           850:        self->monsterinfo.goal_hint = destination;
        !           851: //     self->monsterinfo.last_hint = NULL;
        !           852:        hintpath_go(self, start);
        !           853: 
        !           854:        if(g_showlogic && g_showlogic->value)
        !           855:        {
        !           856:                gi.dprintf ("found path.  proceed to ");
        !           857:                if (start->targetname)
        !           858:                        gi.dprintf ("%s to get to ", start->targetname);
        !           859:                else
        !           860:                        gi.dprintf ("start (->%s) to get to ", start->target);
        !           861:                if (destination->targetname)
        !           862:                        gi.dprintf ("%s.", destination->targetname);
        !           863:                else
        !           864:                        gi.dprintf ("start (->%s)", destination->target);
        !           865:        }
        !           866: //             gi.dprintf("found path. proceed to %s to get to %s\n", vtos(start->s.origin), vtos(destination->s.origin));
        !           867: 
        !           868:        return true;
        !           869: }
        !           870: /*
        !           871: qboolean monsterlost_checkhint2 (edict_t *self)
        !           872: {
        !           873:        edict_t         *e, *e2, *goPoint;
        !           874:        int                     field;
        !           875:        int                     playerVisible, selfVisible;
        !           876: 
        !           877:        // if there are no hint paths on this map, exit immediately.
        !           878:        if(!hint_paths_present)
        !           879:                return false;
        !           880: 
        !           881:        if(!self->enemy)
        !           882:                return false;
        !           883: 
        !           884:        goPoint = NULL;
        !           885:        field = FOFS(classname);
        !           886:        
        !           887:        // check all the hint_paths.
        !           888:        e = G_Find(NULL, field, "hint_path");
        !           889:        while(e)
        !           890:        {
        !           891:                // if it's an endpoint, check for "validity"
        !           892:                if(e->spawnflags & HINT_ENDPOINT)
        !           893:                {
        !           894:                        // check visibility from this spot
        !           895:                        selfVisible = visible(e, self);
        !           896:                        playerVisible = visible(e, self->enemy);
        !           897: //                     gi.dprintf("checking endpoint at %s %d %d\n", vtos(e->s.origin),selfVisible,playerVisible);
        !           898: 
        !           899:                        // at least one of us is visible from this endpoint.
        !           900:                        // now check the other one if needed.
        !           901:                        if(selfVisible || playerVisible)
        !           902:                        {
        !           903:                                // if endpoint 1 saw me, set my destination to it.
        !           904:                                if(selfVisible)
        !           905:                                        goPoint = e;
        !           906: 
        !           907:                                // if both aren't visible, try the other endpoint
        !           908:                                if(!selfVisible || !playerVisible)
        !           909:                                {
        !           910:                                        e2 = hintpath_other_end(e);
        !           911:                                        if(!e2)         // could not connect to the other endpoint
        !           912:                                        {
        !           913:                                                gi.dprintf("Unlinked hint paths!\n");
        !           914:                                                return false;
        !           915:                                        }
        !           916: 
        !           917:                                        // if endpoint 1 saw the enemy, see if endpoint 2 sees me
        !           918:                                        if(!selfVisible)
        !           919:                                                selfVisible = visible(e2, self);
        !           920:                                        // if endpoint 1 saw me, see if endpoint 2 sees the enemy
        !           921:                                        else if(!playerVisible)
        !           922:                                                playerVisible = visible(e2, self->enemy);
        !           923: 
        !           924:                                        // if endpoint 2 saw me, set my destination to it.
        !           925:                                        if(!goPoint && selfVisible)
        !           926:                                                goPoint = e2;
        !           927: 
        !           928: //                                     gi.dprintf("checking other endpoint at %s %d %d\n", vtos(e2->s.origin),selfVisible,playerVisible);
        !           929:                                }
        !           930: 
        !           931:                                // if both are visible from at least one endpoint,
        !           932:                                // go for it.
        !           933:                                if(selfVisible && playerVisible)
        !           934:                                {
        !           935:                                        // set me to go to goPoint
        !           936:                                        if(g_showlogic && g_showlogic->value)
        !           937:                                                gi.dprintf("found path. proceed to %s\n", vtos(goPoint->s.origin));
        !           938:                                        
        !           939:                                        // since this is a new hint path trip, set last_hint to NULL
        !           940:                                        self->monsterinfo.last_hint = NULL;
        !           941:                                        hintpath_go(self, goPoint);
        !           942:                                        return true;
        !           943:                                }
        !           944:                        }
        !           945:                }
        !           946:                e = G_Find(e, field, "hint_path");
        !           947:        }
        !           948: 
        !           949:        // if we got here, we didn't find a valid path
        !           950:        if(g_showlogic && g_showlogic->value)
        !           951:                gi.dprintf("blocked_checkhint: found no paths\n");
        !           952:        return false;
        !           953: }
        !           954: */
        !           955: //
        !           956: // Path code
        !           957: //
        !           958: 
        !           959: // =============
        !           960: // hint_path_touch - someone's touched the hint_path
        !           961: // =============
        !           962: void hint_path_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
        !           963: {
        !           964:        edict_t         *e, *goal, *next;
        !           965: //     int                     chain;                   // direction - (-1) = upstream, (1) = downstream, (0) = done
        !           966:        qboolean        goalFound = false;
        !           967: 
        !           968:        // make sure we're the target of it's obsession
        !           969:        if(other->movetarget == self)
        !           970:        {
        !           971:                goal = other->monsterinfo.goal_hint;
        !           972:                
        !           973:                // if the monster is where he wants to be
        !           974:                if (goal == self)
        !           975:                {
        !           976:                        if(g_showlogic && g_showlogic->value)
        !           977:                                gi.dprintf("Got to goal, detatching\n");
        !           978:                        hintpath_stop (other);
        !           979:                        return;
        !           980:                }
        !           981:                else
        !           982:                {
        !           983:                        // if we aren't, figure out which way we want to go
        !           984:                        e = hint_path_start[self->hint_chain_id];
        !           985:                        while (e)
        !           986:                        {
        !           987:                                // if we get up to ourselves on the hint chain, we're going down it
        !           988:                                if (e == self)
        !           989:                                {
        !           990:                                        next = e->hint_chain;
        !           991:                                        break;
        !           992:                                }
        !           993:                                if (e == goal)
        !           994:                                        goalFound = true;
        !           995:                                // if we get to where the next link on the chain is this hint_path and have found the goal on the way
        !           996:                                // we're going upstream, so remember who the previous link is
        !           997:                                if ((e->hint_chain == self) && goalFound)
        !           998:                                {
        !           999:                                        next = e;
        !          1000:                                        break;
        !          1001:                                }
        !          1002:                                e = e->hint_chain;
        !          1003:                        }
        !          1004:                }
        !          1005: 
        !          1006:                // if we couldn't find it, have the monster go back to normal hunting.
        !          1007:                if(!next)
        !          1008:                {
        !          1009:                        if(g_showlogic && g_showlogic->value)
        !          1010:                                gi.dprintf("couldn't figure out next node, dropping hint path\n");
        !          1011:                        hintpath_stop(other);
        !          1012:                        return;
        !          1013:                }
        !          1014: 
        !          1015:                // set the last_hint entry to this hint_path, and
        !          1016:                // send him on his way
        !          1017: //             other->monsterinfo.last_hint = self;
        !          1018:                if(g_showlogic && g_showlogic->value)
        !          1019:                {
        !          1020:                        gi.dprintf("moving to next point, ");
        !          1021:                        if (next->targetname)
        !          1022:                                gi.dprintf ("targetname %s\n", next->targetname);
        !          1023:                        else
        !          1024:                                gi.dprintf ("start -> %s\n", next->target);
        !          1025:                }
        !          1026:                hintpath_go(other, next);
        !          1027: 
        !          1028:                // have the monster freeze if the hint path we just touched has a wait time
        !          1029:                // on it, for example, when riding a plat.
        !          1030:                if(self->wait)
        !          1031:                {
        !          1032:                        if(g_showlogic && g_showlogic->value)
        !          1033:                                gi.dprintf("monster waiting %0.1f\n", self->wait);
        !          1034:                        other->nextthink = level.time + self->wait;
        !          1035:                }
        !          1036:        }
        !          1037: }
        !          1038: /*
        !          1039: void hint_path_touch2 (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
        !          1040: {
        !          1041:        edict_t         *next, *last;
        !          1042:        int                     chain;
        !          1043: 
        !          1044:        // make sure we're the target of it's obsession
        !          1045:        if(other->movetarget == self)
        !          1046:        {
        !          1047:                chain = 0;              // direction the monster is going in the chain
        !          1048:                next = NULL;    // next hint_path
        !          1049: 
        !          1050: //             gi.dprintf("hint_path %s\n", vtos(self->s.origin));
        !          1051:                // is this the first hintpath targeted? if so, we can do this easily.
        !          1052:                if(other->monsterinfo.last_hint == NULL)
        !          1053:                {
        !          1054:                        if(self->target)                // forward chaining
        !          1055:                                chain = 1;
        !          1056:                        else                                    // backward chaining
        !          1057:                                chain = -1;
        !          1058:                }
        !          1059:                else
        !          1060:                {
        !          1061:                        // shortcut to last_hint
        !          1062:                        last = other->monsterinfo.last_hint;
        !          1063: 
        !          1064:                        // make sure it's valid...
        !          1065:                        if ( (last < g_edicts) || (last >= &g_edicts[globals.num_edicts]))
        !          1066:                        {
        !          1067:                                if(g_showlogic && g_showlogic->value)
        !          1068:                                {
        !          1069:                                        gi.dprintf("bogus last_hint encountered.\n");
        !          1070:                                        gi.dprintf("detaching from hint path %d\n", chain);
        !          1071:                                }
        !          1072:                                hintpath_stop (other);
        !          1073:                                return;
        !          1074:                        }
        !          1075:                        
        !          1076:                        // if we're an endpoint, then the monster is done moving.
        !          1077:                        if(self->spawnflags & HINT_ENDPOINT)
        !          1078:                        {
        !          1079:                                chain = 0;
        !          1080:                        }
        !          1081:                        // if last hint's target is our targetname, it's forward chaining.
        !          1082:                        else if(last->target && self->targetname && !strcmp(last->target, self->targetname))
        !          1083:                        {
        !          1084:                                chain = 1;
        !          1085:                        }
        !          1086:                        // if last hint's targetname is our target, it's backward chaining.
        !          1087:                        // FIXME - last->targetname was 1, not NULL ????  was a screwed up hintpath
        !          1088:                        else if(self->target && last->targetname && !strcmp(last->targetname, self->target))
        !          1089:                        {
        !          1090:                                chain = -1;
        !          1091:                        }
        !          1092:                        else    // if it gets here, i'm not sure how
        !          1093:                        {
        !          1094:                                gi.dprintf("hit an uncovered possibility in hint_path_touch\n");
        !          1095:                                chain = 0;
        !          1096:                        }
        !          1097:                }
        !          1098: 
        !          1099:                // find the "next" hint_path
        !          1100:                if(chain == 1 && self->target)                                          // forward chaining
        !          1101:                        next = G_Find(NULL, FOFS(targetname), self->target);
        !          1102:                else if(chain == -1 && self->targetname)                        // backward chaining
        !          1103:                        next = G_Find(NULL, FOFS(target), self->targetname);
        !          1104: 
        !          1105:                // if we couldn't find it, have the monster go back to normal hunting.
        !          1106:                if(!next)
        !          1107:                {
        !          1108:                        if(g_showlogic && g_showlogic->value)
        !          1109:                                gi.dprintf("detaching from hint path %d\n", chain);
        !          1110:                        hintpath_stop(other);
        !          1111:                        return;
        !          1112:                }
        !          1113: 
        !          1114:                // set the last_hint entry to this hint_path, and
        !          1115:                // send him on his way
        !          1116:                other->monsterinfo.last_hint = self;
        !          1117:                if(g_showlogic && g_showlogic->value)
        !          1118:                        gi.dprintf("moving to next point, %s\n", vtos(next->s.origin));
        !          1119:                hintpath_go(other, next);
        !          1120: 
        !          1121:                // have the monster freeze if the hint path we just touched has a wait time
        !          1122:                // on it, for example, when riding a plat.
        !          1123:                if(self->wait)
        !          1124:                {
        !          1125:                        if(g_showlogic && g_showlogic->value)
        !          1126:                                gi.dprintf("monster waiting %0.1f\n", self->wait);
        !          1127:                        other->nextthink = level.time + self->wait;
        !          1128:                }
        !          1129:        }
        !          1130: }
        !          1131: */
        !          1132: 
        !          1133: /*QUAKED hint_path (.5 .3 0) (-8 -8 -8) (8 8 8) END
        !          1134: Target: next hint path
        !          1135: 
        !          1136: END - set this flag on the endpoints of each hintpath.
        !          1137: 
        !          1138: "wait" - set this if you want the monster to freeze when they touch this hintpath
        !          1139: */
        !          1140: void SP_hint_path (edict_t *self)
        !          1141: {
        !          1142:        if (deathmatch->value)
        !          1143:        {
        !          1144:                G_FreeEdict(self);
        !          1145:                return;
        !          1146:        }
        !          1147: 
        !          1148:        if (!self->targetname && !self->target)
        !          1149:        {
        !          1150:                gi.dprintf ("unlinked hint_path at %s\n", vtos(self->s.origin));
        !          1151:                G_FreeEdict (self);
        !          1152:                return;
        !          1153:        }
        !          1154: 
        !          1155:        self->solid = SOLID_TRIGGER;
        !          1156:        self->touch = hint_path_touch;
        !          1157:        VectorSet (self->mins, -8, -8, -8);
        !          1158:        VectorSet (self->maxs, 8, 8, 8);
        !          1159:        self->svflags |= SVF_NOCLIENT;
        !          1160:        gi.linkentity (self);
        !          1161: }
        !          1162: 
        !          1163: //int  hint_paths_present;
        !          1164: //edict_t *hint_path_start[100];
        !          1165: //int  num_hint_paths;
        !          1166: 
        !          1167: // ============
        !          1168: // InitHintPaths - Called by InitGame (g_save) to enable quick exits if valid
        !          1169: // ============
        !          1170: void InitHintPaths (void)
        !          1171: {
        !          1172:        edict_t         *e, *current;
        !          1173:        int                     field, i, count2;
        !          1174: 
        !          1175:        hint_paths_present = 0;
        !          1176:        
        !          1177:        // check all the hint_paths.
        !          1178:        field = FOFS(classname);
        !          1179:        e = G_Find(NULL, field, "hint_path");
        !          1180:        if(e)
        !          1181:        {
        !          1182: //             gi.dprintf("hint paths present on map\n");
        !          1183:                hint_paths_present = 1;
        !          1184:        }
        !          1185:        else
        !          1186:        {
        !          1187: //             if ((g_showlogic) && (g_showlogic->value))
        !          1188: //                     gi.dprintf ("hint paths not present on map\n");
        !          1189:                return;
        !          1190:        }
        !          1191: 
        !          1192:        memset (hint_path_start, 0, MAX_HINT_CHAINS*sizeof (edict_t *));
        !          1193:        num_hint_paths = 0;
        !          1194:        while(e)
        !          1195:        {
        !          1196:                if(e->spawnflags & HINT_ENDPOINT)
        !          1197:                {
        !          1198:                        if (e->target) // start point
        !          1199:                        {
        !          1200:                                if (num_hint_paths >= MAX_HINT_CHAINS)
        !          1201:                                {
        !          1202: //                                     gi.dprintf ("Only %d hint chains allowed.  Connect some together!\n", MAX_HINT_CHAINS);
        !          1203:                                        break;
        !          1204:                                }
        !          1205:                                hint_path_start[num_hint_paths++] = e;
        !          1206:                        }
        !          1207:                }
        !          1208:                e = G_Find(e, field, "hint_path");
        !          1209:        }
        !          1210: 
        !          1211:        field = FOFS(targetname);
        !          1212:        for (i=0; i< num_hint_paths; i++)
        !          1213:        {
        !          1214:                count2 = 1;
        !          1215:                current = hint_path_start[i];
        !          1216:                current->hint_chain_id = i;
        !          1217: //             gi.dprintf ("start ");
        !          1218:                e = G_Find(NULL, field, current->target);
        !          1219:                while (e)
        !          1220:                {
        !          1221:                        count2++;
        !          1222:                        current->hint_chain = e;
        !          1223:                        current = e;
        !          1224:                        current->hint_chain_id = i;
        !          1225: //                     gi.dprintf ("-> %s ", e->targetname);
        !          1226:                        if (!e->target)
        !          1227:                                break;
        !          1228:                        e = G_Find(NULL, field, e->target);
        !          1229:                }
        !          1230: //             if ((g_showlogic) && (g_showlogic->value))
        !          1231: //                     gi.dprintf ("\nhint_path #%d, %d elements\n", i, count2);
        !          1232:        }
        !          1233: //     if ((g_showlogic) && (g_showlogic->value))
        !          1234: //             gi.dprintf ("hint_path processing done\n");
        !          1235: }
        !          1236: 
        !          1237: // *****************************
        !          1238: //     MISCELLANEOUS STUFF
        !          1239: // *****************************
        !          1240: 
        !          1241: // PMM - inback
        !          1242: // use to see if opponent is behind you (not to side)
        !          1243: // if it looks a lot like infront, well, there's a reason
        !          1244: 
        !          1245: qboolean inback (edict_t *self, edict_t *other)
        !          1246: {
        !          1247:        vec3_t  vec;
        !          1248:        float   dot;
        !          1249:        vec3_t  forward;
        !          1250:        
        !          1251:        AngleVectors (self->s.angles, forward, NULL, NULL);
        !          1252:        VectorSubtract (other->s.origin, self->s.origin, vec);
        !          1253:        VectorNormalize (vec);
        !          1254:        dot = DotProduct (vec, forward);
        !          1255:        
        !          1256:        if (dot < -0.3)
        !          1257:                return true;
        !          1258:        return false;
        !          1259: }
        !          1260: 
        !          1261: float realrange (edict_t *self, edict_t *other)
        !          1262: {
        !          1263:        vec3_t dir;
        !          1264:        
        !          1265:        VectorSubtract (self->s.origin, other->s.origin, dir);
        !          1266: 
        !          1267:        return VectorLength(dir);
        !          1268: }
        !          1269: 
        !          1270: qboolean face_wall (edict_t *self)
        !          1271: {
        !          1272:        vec3_t  pt;
        !          1273:        vec3_t  forward;
        !          1274:        vec3_t  ang;
        !          1275:        trace_t tr;
        !          1276: 
        !          1277:        AngleVectors (self->s.angles, forward, NULL, NULL);
        !          1278:        VectorMA(self->s.origin, 64, forward, pt);
        !          1279:        tr = gi.trace(self->s.origin, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID);
        !          1280:        if(tr.fraction < 1 && !tr.allsolid && !tr.startsolid)
        !          1281:        {
        !          1282:                vectoangles2(tr.plane.normal, ang);
        !          1283:                self->ideal_yaw = ang[YAW] + 180;
        !          1284:                if(self->ideal_yaw > 360)
        !          1285:                        self->ideal_yaw -= 360;
        !          1286: 
        !          1287: //             if(g_showlogic && g_showlogic->value)
        !          1288: //                     gi.dprintf("facing wall, dir %0.1f/%0.1f\n", ang[YAW], self->ideal_yaw);
        !          1289:                M_ChangeYaw(self);
        !          1290:                return true;
        !          1291:        }
        !          1292: 
        !          1293:        return false;
        !          1294: }
        !          1295: 
        !          1296: //
        !          1297: // Monster "Bad" Areas
        !          1298: // 
        !          1299: 
        !          1300: void badarea_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
        !          1301: {
        !          1302: //     drawbbox(ent);
        !          1303: }
        !          1304: 
        !          1305: edict_t *SpawnBadArea(vec3_t mins, vec3_t maxs, float lifespan, edict_t *owner)
        !          1306: {
        !          1307:        edict_t *badarea;
        !          1308:        vec3_t  origin;
        !          1309:        
        !          1310:        VectorAdd(mins, maxs, origin);
        !          1311:        VectorScale(origin, 0.5, origin);
        !          1312: 
        !          1313:        VectorSubtract(maxs, origin, maxs);
        !          1314:        VectorSubtract(mins, origin, mins);
        !          1315: 
        !          1316:        badarea = G_Spawn();
        !          1317:        VectorCopy(origin, badarea->s.origin);
        !          1318:        VectorCopy(maxs, badarea->maxs);
        !          1319:        VectorCopy(mins, badarea->mins);
        !          1320:        badarea->touch = badarea_touch;
        !          1321:        badarea->movetype = MOVETYPE_NONE;
        !          1322:        badarea->solid = SOLID_TRIGGER;
        !          1323:        badarea->classname = "bad_area";
        !          1324:        gi.linkentity (badarea);
        !          1325: 
        !          1326: //     gi.dprintf("(%s)-(%s)\n", vtos(badarea->absmin), vtos(badarea->absmax));
        !          1327: 
        !          1328:        if(lifespan)
        !          1329:        {
        !          1330:                badarea->think = G_FreeEdict;
        !          1331:                badarea->nextthink = level.time + lifespan;
        !          1332:        }
        !          1333:        if(owner)
        !          1334:        {
        !          1335:                badarea->owner = owner;
        !          1336:        }
        !          1337: 
        !          1338: //     drawbbox(badarea);
        !          1339:        return badarea;
        !          1340: }
        !          1341: 
        !          1342: // CheckForBadArea
        !          1343: //             This is a customized version of G_TouchTriggers that will check
        !          1344: //             for bad area triggers and return them if they're touched.
        !          1345: edict_t *CheckForBadArea(edict_t *ent)
        !          1346: {
        !          1347:        int                     i, num;
        !          1348:        edict_t         *touch[MAX_EDICTS], *hit;
        !          1349:        vec3_t          mins, maxs;
        !          1350: 
        !          1351:        VectorAdd(ent->s.origin, ent->mins, mins);
        !          1352:        VectorAdd(ent->s.origin, ent->maxs, maxs);
        !          1353: 
        !          1354:        num = gi.BoxEdicts (mins, maxs, touch, MAX_EDICTS, AREA_TRIGGERS);
        !          1355: 
        !          1356: //     drawbbox(ent);
        !          1357: 
        !          1358:        // be careful, it is possible to have an entity in this
        !          1359:        // list removed before we get to it (killtriggered)
        !          1360:        for (i=0 ; i<num ; i++)
        !          1361:        {
        !          1362:                hit = touch[i];
        !          1363:                if (!hit->inuse)
        !          1364:                        continue;
        !          1365:                if (hit->touch == badarea_touch)
        !          1366:                {
        !          1367:                        return hit;
        !          1368:                }
        !          1369:        }
        !          1370:        
        !          1371:        return NULL;
        !          1372: }
        !          1373: 
        !          1374: #define TESLA_DAMAGE_RADIUS            128
        !          1375: 
        !          1376: qboolean MarkTeslaArea(edict_t *self, edict_t *tesla)
        !          1377: {
        !          1378:        vec3_t  mins, maxs;
        !          1379:        edict_t *e;
        !          1380:        edict_t *tail;
        !          1381:        edict_t *area;
        !          1382: 
        !          1383:        if(!tesla || !self)
        !          1384:                return false;
        !          1385: 
        !          1386:        area = NULL;
        !          1387: 
        !          1388:        // make sure this tesla doesn't have a bad area around it already...
        !          1389:        e = tesla->teamchain;
        !          1390:        tail = tesla;
        !          1391:        while (e)
        !          1392:        {
        !          1393:                tail = tail->teamchain;
        !          1394:                if(!strcmp(e->classname, "bad_area"))
        !          1395:                {
        !          1396: //                     gi.dprintf("tesla already has a bad area marked\n");
        !          1397:                        return false;
        !          1398:                }
        !          1399:                e = e->teamchain;
        !          1400:        }
        !          1401: 
        !          1402:        // see if we can grab the trigger directly
        !          1403:        if(tesla->teamchain && tesla->teamchain->inuse)
        !          1404:        {
        !          1405:                edict_t *trigger;
        !          1406: 
        !          1407:                trigger = tesla->teamchain;
        !          1408: 
        !          1409: //             VectorAdd (trigger->s.origin, trigger->mins, mins);
        !          1410: //             VectorAdd (trigger->s.origin, trigger->maxs, maxs);
        !          1411:                VectorCopy(trigger->absmin, mins);
        !          1412:                VectorCopy(trigger->absmax, maxs);
        !          1413: 
        !          1414:                if(tesla->air_finished)
        !          1415:                        area = SpawnBadArea (mins, maxs, tesla->air_finished, tesla);
        !          1416:                else
        !          1417:                        area = SpawnBadArea (mins, maxs, tesla->nextthink, tesla);
        !          1418:        }
        !          1419:        // otherwise we just guess at how long it'll last.
        !          1420:        else
        !          1421:        {
        !          1422:        
        !          1423:                VectorSet (mins, -TESLA_DAMAGE_RADIUS, -TESLA_DAMAGE_RADIUS, tesla->mins[2]);
        !          1424:                VectorSet (maxs, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS);
        !          1425: 
        !          1426:                area = SpawnBadArea(mins, maxs, 30, tesla);
        !          1427:        }
        !          1428: 
        !          1429:        // if we spawned a bad area, then link it to the tesla
        !          1430:        if(area)
        !          1431:        {
        !          1432: //             gi.dprintf("bad area marker spawned and linked to tesla\n");
        !          1433:                tail->teamchain = area;
        !          1434:        }
        !          1435:        return true;
        !          1436: }
        !          1437: 
        !          1438: // predictive calculator
        !          1439: // target is who you want to shoot
        !          1440: // start is where the shot comes from
        !          1441: // bolt_speed is how fast the shot is
        !          1442: // eye_height is a boolean to say whether or not to adjust to targets eye_height
        !          1443: // offset is how much time to miss by
        !          1444: // aimdir is the resulting aim direction (pass in NULL if you don't want it)
        !          1445: // aimpoint is the resulting aimpoint (pass in NULL if don't want it)
        !          1446: void PredictAim (edict_t *target, vec3_t start, float bolt_speed, qboolean eye_height, float offset, vec3_t aimdir, vec3_t aimpoint)
        !          1447: {
        !          1448:        vec3_t dir, vec;
        !          1449:        float dist, time;
        !          1450: 
        !          1451:        if (!target || !target->inuse)
        !          1452:        {
        !          1453:                VectorCopy (vec3_origin, aimdir);
        !          1454:                return;
        !          1455:        }
        !          1456: 
        !          1457:        VectorSubtract(target->s.origin, start, dir);
        !          1458:        if (eye_height)
        !          1459:                dir[2] += target->viewheight;
        !          1460:        dist = VectorLength(dir);
        !          1461:        time = dist / bolt_speed;
        !          1462: 
        !          1463: 
        !          1464:        VectorMA(target->s.origin, time - offset, target->velocity, vec);
        !          1465: 
        !          1466:        if (eye_height)
        !          1467:                vec[2] += target->viewheight;
        !          1468: 
        !          1469:        if (aimdir)
        !          1470:        {
        !          1471:                VectorSubtract (vec, start, aimdir);
        !          1472:                VectorNormalize (aimdir);
        !          1473:        }
        !          1474:        
        !          1475:        if (aimpoint)
        !          1476:        {
        !          1477:                VectorCopy (vec, aimpoint);
        !          1478:        }
        !          1479: }
        !          1480: 
        !          1481: 
        !          1482: qboolean below (edict_t *self, edict_t *other)
        !          1483: {
        !          1484:        vec3_t  vec;
        !          1485:        float   dot;
        !          1486:        vec3_t  down;
        !          1487:        
        !          1488:        VectorSubtract (other->s.origin, self->s.origin, vec);
        !          1489:        VectorNormalize (vec);
        !          1490:        VectorSet (down, 0, 0, -1);
        !          1491:        dot = DotProduct (vec, down);
        !          1492:        
        !          1493:        if (dot > 0.95)  // 18 degree arc below
        !          1494:                return true;
        !          1495:        return false;
        !          1496: }
        !          1497: 
        !          1498: void drawbbox (edict_t *self)
        !          1499: {
        !          1500:        int     lines[4][3] = {
        !          1501:                {1, 2, 4},
        !          1502:                {1, 2, 7},
        !          1503:                {1, 4, 5},
        !          1504:                {2, 4, 7}
        !          1505:        };
        !          1506: 
        !          1507:        int starts[4] = {0, 3, 5, 6};
        !          1508: 
        !          1509:        vec3_t pt[8];
        !          1510:        int i, j, k;
        !          1511:        vec3_t coords[2];
        !          1512:        vec3_t newbox;
        !          1513:        vec3_t f,r,u, dir;
        !          1514: 
        !          1515:        VectorCopy (self->absmin, coords[0]);
        !          1516:        VectorCopy (self->absmax, coords[1]);
        !          1517: 
        !          1518:        for (i=0; i<=1; i++)
        !          1519:        {
        !          1520:                for (j=0; j<=1; j++)
        !          1521:                {
        !          1522:                        for (k=0; k<=1; k++)
        !          1523:                        {
        !          1524:                                pt[4*i+2*j+k][0] = coords[i][0];
        !          1525:                                pt[4*i+2*j+k][1] = coords[j][1];
        !          1526:                                pt[4*i+2*j+k][2] = coords[k][2];
        !          1527:                        }
        !          1528:                }
        !          1529:        }
        !          1530:        
        !          1531:        for (i=0; i<= 3; i++)
        !          1532:        {
        !          1533:                for (j=0; j<= 2; j++)
        !          1534:                {
        !          1535:                        gi.WriteByte (svc_temp_entity);
        !          1536:                        gi.WriteByte (TE_DEBUGTRAIL);
        !          1537:                        gi.WritePosition (pt[starts[i]]);
        !          1538:                        gi.WritePosition (pt[lines[i][j]]);
        !          1539:                        gi.multicast (pt[starts[i]], MULTICAST_ALL);    
        !          1540:                }
        !          1541:        }
        !          1542: 
        !          1543:        vectoangles2 (self->s.angles, dir);
        !          1544:        AngleVectors (dir, f, r, u);
        !          1545: 
        !          1546:        VectorMA (self->s.origin, 50, f, newbox);
        !          1547:        gi.WriteByte (svc_temp_entity);
        !          1548:        gi.WriteByte (TE_DEBUGTRAIL);
        !          1549:        gi.WritePosition (self->s.origin);
        !          1550:        gi.WritePosition (newbox);
        !          1551:        gi.multicast (self->s.origin, MULTICAST_PVS);   
        !          1552:        VectorClear (newbox);
        !          1553: 
        !          1554:        VectorMA (self->s.origin, 50, r, newbox);
        !          1555:        gi.WriteByte (svc_temp_entity);
        !          1556:        gi.WriteByte (TE_DEBUGTRAIL);
        !          1557:        gi.WritePosition (self->s.origin);
        !          1558:        gi.WritePosition (newbox);
        !          1559:        gi.multicast (self->s.origin, MULTICAST_PVS);   
        !          1560:        VectorClear (newbox);
        !          1561: 
        !          1562:        VectorMA (self->s.origin, 50, u, newbox);
        !          1563:        gi.WriteByte (svc_temp_entity);
        !          1564:        gi.WriteByte (TE_DEBUGTRAIL);
        !          1565:        gi.WritePosition (self->s.origin);
        !          1566:        gi.WritePosition (newbox);
        !          1567:        gi.multicast (self->s.origin, MULTICAST_PVS);   
        !          1568:        VectorClear (newbox);
        !          1569: }
        !          1570: 
        !          1571: //
        !          1572: // New dodge code
        !          1573: //
        !          1574: void M_MonsterDodge (edict_t *self, edict_t *attacker, float eta, trace_t *tr)
        !          1575: {
        !          1576:        float   r = random();
        !          1577:        float   height;
        !          1578:        qboolean        ducker = false, dodger = false;
        !          1579: 
        !          1580:        // this needs to be here since this can be called after the monster has "died"
        !          1581:        if (self->health < 1)
        !          1582:                return;
        !          1583: 
        !          1584:        if ((self->monsterinfo.duck) && (self->monsterinfo.unduck))
        !          1585:                ducker = true;
        !          1586:        if ((self->monsterinfo.sidestep) && !(self->monsterinfo.aiflags & AI_STAND_GROUND))
        !          1587:                dodger = true;
        !          1588: 
        !          1589:        if ((!ducker) && (!dodger))
        !          1590:                return;
        !          1591: 
        !          1592:        if ((g_showlogic) && (g_showlogic->value))
        !          1593:        {
        !          1594:                if (self->monsterinfo.aiflags & AI_DODGING)
        !          1595:                        gi.dprintf ("dodging - ");
        !          1596:                if (self->monsterinfo.aiflags & AI_DUCKED)
        !          1597:                        gi.dprintf ("ducked - ");
        !          1598:        }
        !          1599:        if (!self->enemy)
        !          1600:        {
        !          1601:                self->enemy = attacker;
        !          1602:                FoundTarget (self);
        !          1603:        }
        !          1604: 
        !          1605:        // PMM - don't bother if it's going to hit anyway; fix for weird in-your-face etas (I was
        !          1606:        // seeing numbers like 13 and 14)
        !          1607:        if ((eta < 0.1) || (eta > 5))
        !          1608:        {
        !          1609:                if ((g_showlogic) && (g_showlogic->value))
        !          1610:                        gi.dprintf ("timeout\n");
        !          1611:                return;
        !          1612:        }
        !          1613: 
        !          1614:        // skill level determination..
        !          1615:        if (r > (0.25*((skill->value)+1)))
        !          1616:        {
        !          1617:                if ((g_showlogic) && (g_showlogic->value))
        !          1618:                        gi.dprintf ("skillout\n");
        !          1619:                return;
        !          1620:        }
        !          1621: 
        !          1622:        // stop charging, since we're going to dodge (somehow) instead
        !          1623: //     soldier_stop_charge (self);
        !          1624: 
        !          1625:        if (ducker)
        !          1626:        {
        !          1627:                height = self->absmax[2]-32-1;  // the -1 is because the absmax is s.origin + maxs + 1
        !          1628: 
        !          1629:                // FIXME, make smarter
        !          1630:                // if we only duck, and ducking won't help or we're already ducking, do nothing
        !          1631:                //
        !          1632:                // need to add monsterinfo.abort_duck() and monsterinfo.next_duck_time
        !          1633: 
        !          1634:                if ((!dodger) && ((tr->endpos[2] <= height) || (self->monsterinfo.aiflags & AI_DUCKED)))
        !          1635:                        return;
        !          1636:        }
        !          1637:        else
        !          1638:                height = self->absmax[2];
        !          1639: 
        !          1640:        if (dodger)
        !          1641:        {
        !          1642:                // if we're already dodging, just finish the sequence, i.e. don't do anything else
        !          1643:                if (self->monsterinfo.aiflags & AI_DODGING)
        !          1644:                {
        !          1645:                        if ((g_showlogic) && (g_showlogic->value))
        !          1646:                                gi.dprintf ("already dodging\n");
        !          1647:                        return;
        !          1648:                }
        !          1649: 
        !          1650:                // if we're ducking already, or the shot is at our knees
        !          1651:                if ((tr->endpos[2] <= height) || (self->monsterinfo.aiflags & AI_DUCKED))
        !          1652:                {
        !          1653:                        vec3_t right, diff;
        !          1654: 
        !          1655:                        AngleVectors (self->s.angles, NULL, right, NULL);
        !          1656:                        VectorSubtract (tr->endpos, self->s.origin, diff);
        !          1657: 
        !          1658:                        if (DotProduct (right, diff) < 0)
        !          1659:                        {
        !          1660:                                self->monsterinfo.lefty = 0;
        !          1661: //                             gi.dprintf ("left\n");
        !          1662:                        } else {
        !          1663:                                self->monsterinfo.lefty = 1;
        !          1664: //                             gi.dprintf ("right\n");
        !          1665:                        }
        !          1666:        
        !          1667:                        // if we are currently ducked, unduck
        !          1668: 
        !          1669:                        if ((ducker) && (self->monsterinfo.aiflags & AI_DUCKED))
        !          1670:                        {
        !          1671:                                if ((g_showlogic) && (g_showlogic->value))
        !          1672:                                        gi.dprintf ("unducking - ");
        !          1673:                                self->monsterinfo.unduck(self);
        !          1674:                        }
        !          1675: 
        !          1676:                        self->monsterinfo.aiflags |= AI_DODGING;
        !          1677:                        self->monsterinfo.attack_state = AS_SLIDING;
        !          1678: 
        !          1679:                        // call the monster specific code here
        !          1680:                        self->monsterinfo.sidestep (self);
        !          1681:                        return;
        !          1682:                }
        !          1683:        }
        !          1684: 
        !          1685:        if (ducker)
        !          1686:        {
        !          1687:                if (self->monsterinfo.next_duck_time > level.time)
        !          1688:                {
        !          1689:                        if ((g_showlogic) && (g_showlogic->value))
        !          1690:                                gi.dprintf ("ducked too often, not ducking\n");
        !          1691:                        return;
        !          1692:                }
        !          1693: 
        !          1694:                if ((g_showlogic) && (g_showlogic->value))
        !          1695:                        gi.dprintf ("ducking!\n");
        !          1696: 
        !          1697:                monster_done_dodge (self);
        !          1698:                // set this prematurely; it doesn't hurt, and prevents extra iterations
        !          1699:                self->monsterinfo.aiflags |= AI_DUCKED;
        !          1700: 
        !          1701:                self->monsterinfo.duck (self, eta);
        !          1702:        }
        !          1703: }
        !          1704: 
        !          1705: void monster_duck_down (edict_t *self)
        !          1706: {
        !          1707: //     if (self->monsterinfo.aiflags & AI_DUCKED)
        !          1708: //             return;
        !          1709:        self->monsterinfo.aiflags |= AI_DUCKED;
        !          1710: 
        !          1711:        if ((g_showlogic) && (g_showlogic->value))
        !          1712:                gi.dprintf ("duck down!\n");
        !          1713: //     self->maxs[2] -= 32;
        !          1714:        self->maxs[2] = self->monsterinfo.base_height - 32;
        !          1715:        self->takedamage = DAMAGE_YES;
        !          1716:        if (self->monsterinfo.duck_wait_time < level.time)
        !          1717:                self->monsterinfo.duck_wait_time = level.time + 1;
        !          1718:        gi.linkentity (self);
        !          1719: }
        !          1720: 
        !          1721: void monster_duck_hold (edict_t *self)
        !          1722: {
        !          1723:        if (level.time >= self->monsterinfo.duck_wait_time)
        !          1724:                self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
        !          1725:        else
        !          1726:                self->monsterinfo.aiflags |= AI_HOLD_FRAME;
        !          1727: }
        !          1728: 
        !          1729: void monster_duck_up (edict_t *self)
        !          1730: {
        !          1731:        self->monsterinfo.aiflags &= ~AI_DUCKED;
        !          1732: //     self->maxs[2] += 32;
        !          1733:        self->maxs[2] = self->monsterinfo.base_height;
        !          1734:        self->takedamage = DAMAGE_AIM;
        !          1735:        self->monsterinfo.next_duck_time = level.time + DUCK_INTERVAL;
        !          1736:        gi.linkentity (self);
        !          1737: }
        !          1738: 
        !          1739: //=========================
        !          1740: //=========================
        !          1741: qboolean has_valid_enemy (edict_t *self)
        !          1742: {
        !          1743:        if (!self->enemy)
        !          1744:                return false;
        !          1745: 
        !          1746:        if (!self->enemy->inuse)
        !          1747:                return false;
        !          1748: 
        !          1749:        if (self->enemy->health < 1)
        !          1750:                return false;
        !          1751: 
        !          1752:        return true;
        !          1753: }
        !          1754: 
        !          1755: void TargetTesla (edict_t *self, edict_t *tesla)
        !          1756: {
        !          1757:        if ((!self) || (!tesla))
        !          1758:                return;
        !          1759: 
        !          1760:        // PMM - medic bails on healing things
        !          1761:        if (self->monsterinfo.aiflags & AI_MEDIC)
        !          1762:        {
        !          1763:                if (self->enemy)
        !          1764:                        cleanupHealTarget(self->enemy);
        !          1765:                self->monsterinfo.aiflags &= ~AI_MEDIC;
        !          1766:        }
        !          1767: 
        !          1768:        // store the player enemy in case we lose track of him.
        !          1769:        if(self->enemy && self->enemy->client)
        !          1770:                self->monsterinfo.last_player_enemy = self->enemy;
        !          1771: 
        !          1772:        if(self->enemy != tesla)
        !          1773:        {
        !          1774:                self->oldenemy = self->enemy;
        !          1775:                self->enemy = tesla;
        !          1776:                if(self->monsterinfo.attack)
        !          1777:                {
        !          1778:                        if (self->health <= 0)
        !          1779:                        {
        !          1780:                                if ((g_showlogic) && (g_showlogic->value))
        !          1781:                                        gi.dprintf ("bad tesla attack avoided!\n");
        !          1782:                                return;
        !          1783:                        }
        !          1784:                        self->monsterinfo.attack(self);
        !          1785:                }
        !          1786:                else
        !          1787:                {
        !          1788:                        FoundTarget(self);
        !          1789:                }
        !          1790:        }
        !          1791: }
        !          1792: 
        !          1793: // this returns a randomly selected coop player who is visible to self
        !          1794: // returns NULL if bad
        !          1795: 
        !          1796: edict_t * PickCoopTarget (edict_t *self)
        !          1797: {
        !          1798:        // no more than 4 players in coop, so..
        !          1799:        edict_t *targets[4];
        !          1800:        int             num_targets = 0, targetID;
        !          1801:        edict_t *ent;
        !          1802:        int             player;
        !          1803: 
        !          1804:        // if we're not in coop, this is a noop
        !          1805:        if (!coop || !coop->value)
        !          1806:                return NULL;
        !          1807: 
        !          1808:        memset (targets, 0, 4*sizeof(edict_t *));
        !          1809: 
        !          1810:        for (player = 1; player <= game.maxclients; player++)
        !          1811:        {
        !          1812:                ent = &g_edicts[player];
        !          1813:                if (!ent->inuse)
        !          1814:                        continue;
        !          1815:                if (!ent->client)
        !          1816:                        continue;
        !          1817:                if (visible(self, ent))
        !          1818:                {
        !          1819:                        if ((g_showlogic) && (g_showlogic->value))
        !          1820:                                gi.dprintf ("%s: found coop player %s - ", self->classname, ent->client->pers.netname);
        !          1821:                        targets[num_targets++] = ent;
        !          1822:                }
        !          1823:        }
        !          1824: 
        !          1825: /*
        !          1826:        ent = g_edicts+1; // skip the worldspawn
        !          1827:        // cycle through players
        !          1828:        while (ent)
        !          1829:        {
        !          1830:                if ((ent->client) && (ent->inuse))
        !          1831:                {
        !          1832:                        if (visible(self, ent))
        !          1833:                        {
        !          1834:                                if ((g_showlogic) && (g_showlogic->value))
        !          1835:                                        gi.dprintf ("%s: found coop player %s - ", self->classname, ent->client->pers.netname);
        !          1836:                                targets[num_targets++] = ent;
        !          1837:                        }
        !          1838:                        ent++;
        !          1839:                }
        !          1840:                else
        !          1841:                        ent = NULL;
        !          1842:        }
        !          1843: */
        !          1844: 
        !          1845:        if (!num_targets)
        !          1846:                return NULL;
        !          1847: 
        !          1848:        // get a number from 0 to (num_targets-1)
        !          1849:        targetID = (random() * (float)num_targets);
        !          1850:        
        !          1851:        // just in case we got a 1.0 from random
        !          1852:        if (targetID == num_targets)
        !          1853:                targetID--;
        !          1854: 
        !          1855:        if (g_showlogic && g_showlogic->value)
        !          1856:                gi.dprintf ("using player %s\n", targets[targetID]->client->pers.netname);
        !          1857:        return targets[targetID];
        !          1858: }
        !          1859: 
        !          1860: // only meant to be used in coop
        !          1861: int CountPlayers (void)
        !          1862: {
        !          1863:        edict_t *ent;
        !          1864:        int             count = 0;
        !          1865:        int             player;
        !          1866: 
        !          1867:        // if we're not in coop, this is a noop
        !          1868:        if (!coop || !coop->value)
        !          1869:                return 1;
        !          1870: 
        !          1871:        for (player = 1; player <= game.maxclients; player++)
        !          1872:        {
        !          1873:                ent = &g_edicts[player];
        !          1874:                if (!ent->inuse)
        !          1875:                        continue;
        !          1876:                if (!ent->client)
        !          1877:                        continue;
        !          1878:                count++;
        !          1879:        }
        !          1880: /*
        !          1881:        ent = g_edicts+1; // skip the worldspawn
        !          1882:        while (ent)
        !          1883:        {
        !          1884:                if ((ent->client) && (ent->inuse))
        !          1885:                {
        !          1886:                        ent++;
        !          1887:                        count++;
        !          1888:                }
        !          1889:                else
        !          1890:                        ent = NULL;
        !          1891:        }
        !          1892: */
        !          1893:        return count;
        !          1894: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.