Annotation of quake2/rogue/g_newai.c, revision 1.1.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.