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

1.1     ! root        1: // g_ai.c
        !             2: 
        !             3: #include "g_local.h"
        !             4: 
        !             5: qboolean FindTarget (edict_t *self);
        !             6: extern cvar_t  *maxclients;
        !             7: 
        !             8: qboolean ai_checkattack (edict_t *self, float dist);
        !             9: 
        !            10: qboolean       enemy_vis;
        !            11: qboolean       enemy_infront;
        !            12: int                    enemy_range;
        !            13: float          enemy_yaw;
        !            14: 
        !            15: // ROGUE STUFF
        !            16: #define SLIDING_TROOPS 1
        !            17: #define        MAX_SIDESTEP    8.0
        !            18: //
        !            19: 
        !            20: //============================================================================
        !            21: 
        !            22: 
        !            23: /*
        !            24: =================
        !            25: AI_SetSightClient
        !            26: 
        !            27: Called once each frame to set level.sight_client to the
        !            28: player to be checked for in findtarget.
        !            29: 
        !            30: If all clients are either dead or in notarget, sight_client
        !            31: will be null.
        !            32: 
        !            33: In coop games, sight_client will cycle between the clients.
        !            34: =================
        !            35: */
        !            36: void AI_SetSightClient (void)
        !            37: {
        !            38:        edict_t *ent;
        !            39:        int             start, check;
        !            40: 
        !            41:        if (level.sight_client == NULL)
        !            42:                start = 1;
        !            43:        else
        !            44:                start = level.sight_client - g_edicts;
        !            45: 
        !            46:        check = start;
        !            47:        while (1)
        !            48:        {
        !            49:                check++;
        !            50:                if (check > game.maxclients)
        !            51:                        check = 1;
        !            52:                ent = &g_edicts[check];
        !            53:                if (ent->inuse
        !            54:                        && ent->health > 0
        !            55:                        && !(ent->flags & (FL_NOTARGET|FL_DISGUISED)) )
        !            56:                {
        !            57:                        level.sight_client = ent;
        !            58:                        return;         // got one
        !            59:                }
        !            60:                if (check == start)
        !            61:                {
        !            62:                        level.sight_client = NULL;
        !            63:                        return;         // nobody to see
        !            64:                }
        !            65:        }
        !            66: }
        !            67: 
        !            68: //============================================================================
        !            69: 
        !            70: /*
        !            71: =============
        !            72: ai_move
        !            73: 
        !            74: Move the specified distance at current facing.
        !            75: This replaces the QC functions: ai_forward, ai_back, ai_pain, and ai_painforward
        !            76: ==============
        !            77: */
        !            78: void ai_move (edict_t *self, float dist)
        !            79: {
        !            80:        M_walkmove (self, self->s.angles[YAW], dist);
        !            81: }
        !            82: 
        !            83: 
        !            84: /*
        !            85: =============
        !            86: ai_stand
        !            87: 
        !            88: Used for standing around and looking for players
        !            89: Distance is for slight position adjustments needed by the animations
        !            90: ==============
        !            91: */
        !            92: void ai_stand (edict_t *self, float dist)
        !            93: {
        !            94:        vec3_t  v;
        !            95: 
        !            96:        if (dist)
        !            97:                M_walkmove (self, self->s.angles[YAW], dist);
        !            98: 
        !            99:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
        !           100:        {
        !           101:                if (self->enemy)
        !           102:                {
        !           103:                        VectorSubtract (self->enemy->s.origin, self->s.origin, v);
        !           104:                        self->ideal_yaw = vectoyaw(v);
        !           105:                        if (self->s.angles[YAW] != self->ideal_yaw && self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
        !           106:                        {
        !           107:                                self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
        !           108:                                self->monsterinfo.run (self);
        !           109:                        }
        !           110:                        if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
        !           111:                                M_ChangeYaw (self);
        !           112:                        // PMM
        !           113:                        if ((self->enemy) && (self->enemy->inuse) && (visible(self, self->enemy)))
        !           114:                        {
        !           115:                                self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
        !           116:                                VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
        !           117:                                VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
        !           118:                                self->monsterinfo.trail_time = level.time;
        !           119:                                self->monsterinfo.blind_fire_delay = 0;
        !           120:                        }
        !           121:                        // pmm
        !           122:                        ai_checkattack (self, 0);
        !           123:                }
        !           124:                else
        !           125:                        FindTarget (self);
        !           126:                return;
        !           127:        }
        !           128: 
        !           129:        if (FindTarget (self))
        !           130:                return;
        !           131:        
        !           132:        if (level.time > self->monsterinfo.pausetime)
        !           133:        {
        !           134:                self->monsterinfo.walk (self);
        !           135:                return;
        !           136:        }
        !           137: 
        !           138:        if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && (level.time > self->monsterinfo.idle_time))
        !           139:        {
        !           140:                if (self->monsterinfo.idle_time)
        !           141:                {
        !           142:                        self->monsterinfo.idle (self);
        !           143:                        self->monsterinfo.idle_time = level.time + 15 + random() * 15;
        !           144:                }
        !           145:                else
        !           146:                {
        !           147:                        self->monsterinfo.idle_time = level.time + random() * 15;
        !           148:                }
        !           149:        }
        !           150: }
        !           151: 
        !           152: 
        !           153: /*
        !           154: =============
        !           155: ai_walk
        !           156: 
        !           157: The monster is walking it's beat
        !           158: =============
        !           159: */
        !           160: void ai_walk (edict_t *self, float dist)
        !           161: {
        !           162:        M_MoveToGoal (self, dist);
        !           163: 
        !           164:        // check for noticing a player
        !           165:        if (FindTarget (self))
        !           166:                return;
        !           167: 
        !           168:        if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time))
        !           169:        {
        !           170:                if (self->monsterinfo.idle_time)
        !           171:                {
        !           172:                        self->monsterinfo.search (self);
        !           173:                        self->monsterinfo.idle_time = level.time + 15 + random() * 15;
        !           174:                }
        !           175:                else
        !           176:                {
        !           177:                        self->monsterinfo.idle_time = level.time + random() * 15;
        !           178:                }
        !           179:        }
        !           180: }
        !           181: 
        !           182: 
        !           183: /*
        !           184: =============
        !           185: ai_charge
        !           186: 
        !           187: Turns towards target and advances
        !           188: Use this call with a distance of 0 to replace ai_face
        !           189: ==============
        !           190: */
        !           191: void ai_charge (edict_t *self, float dist)
        !           192: {
        !           193:        vec3_t  v;
        !           194:        // PMM
        !           195:        float   ofs;
        !           196:        // PMM
        !           197: 
        !           198:        // PMM - made AI_MANUAL_STEERING affect things differently here .. they turn, but
        !           199:        // don't set the ideal_yaw
        !           200: 
        !           201:        // This is put in there so monsters won't move towards the origin after killing
        !           202:        // a tesla. This could be problematic, so keep an eye on it.
        !           203:        if(!self->enemy || !self->enemy->inuse)         //PGM
        !           204:                return;                                                                 //PGM
        !           205: 
        !           206:        // PMM - save blindfire target
        !           207:        if (visible(self, self->enemy))
        !           208:                VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
        !           209:        // pmm 
        !           210: 
        !           211:        if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
        !           212:        {
        !           213:                VectorSubtract (self->enemy->s.origin, self->s.origin, v);
        !           214:                self->ideal_yaw = vectoyaw(v);
        !           215: //             gi.dprintf ("enemy = %s\n", vtos (self->enemy->s.origin));
        !           216: //             gi.dprintf ("enemy: ideal yaw is %f\n", self->ideal_yaw);
        !           217:        }
        !           218: //     if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
        !           219:        M_ChangeYaw (self);
        !           220: // PMM
        !           221: //     if (dist)
        !           222: //             M_walkmove (self, self->s.angles[YAW], dist);
        !           223: 
        !           224:        if (dist)
        !           225:        {
        !           226:                if (self->monsterinfo.aiflags & AI_CHARGING)
        !           227:                {
        !           228:                        M_MoveToGoal (self, dist);
        !           229:                        return;
        !           230:                }
        !           231:                // circle strafe support
        !           232:                if (self->monsterinfo.attack_state == AS_SLIDING)
        !           233:                {
        !           234:                        if (self->monsterinfo.lefty)
        !           235:                                ofs = 90;
        !           236:                        else
        !           237:                                ofs = -90;
        !           238:                        
        !           239:                        if (M_walkmove (self, self->ideal_yaw + ofs, dist))
        !           240:                                return;
        !           241:                                
        !           242:                        self->monsterinfo.lefty = 1 - self->monsterinfo.lefty;
        !           243:                        M_walkmove (self, self->ideal_yaw - ofs, dist);
        !           244:                }
        !           245:                else
        !           246:                        M_walkmove (self, self->s.angles[YAW], dist);
        !           247:        }
        !           248: // PMM
        !           249: }
        !           250: 
        !           251: 
        !           252: /*
        !           253: =============
        !           254: ai_turn
        !           255: 
        !           256: don't move, but turn towards ideal_yaw
        !           257: Distance is for slight position adjustments needed by the animations
        !           258: =============
        !           259: */
        !           260: void ai_turn (edict_t *self, float dist)
        !           261: {
        !           262:        if (dist)
        !           263:                M_walkmove (self, self->s.angles[YAW], dist);
        !           264: 
        !           265:        if (FindTarget (self))
        !           266:                return;
        !           267:        
        !           268:        if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
        !           269:                M_ChangeYaw (self);
        !           270: }
        !           271: 
        !           272: 
        !           273: /*
        !           274: 
        !           275: .enemy
        !           276: Will be world if not currently angry at anyone.
        !           277: 
        !           278: .movetarget
        !           279: The next path spot to walk toward.  If .enemy, ignore .movetarget.
        !           280: When an enemy is killed, the monster will try to return to it's path.
        !           281: 
        !           282: .hunt_time
        !           283: Set to time + something when the player is in sight, but movement straight for
        !           284: him is blocked.  This causes the monster to use wall following code for
        !           285: movement direction instead of sighting on the player.
        !           286: 
        !           287: .ideal_yaw
        !           288: A yaw angle of the intended direction, which will be turned towards at up
        !           289: to 45 deg / state.  If the enemy is in view and hunt_time is not active,
        !           290: this will be the exact line towards the enemy.
        !           291: 
        !           292: .pausetime
        !           293: A monster will leave it's stand state and head towards it's .movetarget when
        !           294: time > .pausetime.
        !           295: 
        !           296: walkmove(angle, speed) primitive is all or nothing
        !           297: */
        !           298: 
        !           299: /*
        !           300: =============
        !           301: range
        !           302: 
        !           303: returns the range catagorization of an entity reletive to self
        !           304: 0      melee range, will become hostile even if back is turned
        !           305: 1      visibility and infront, or visibility and show hostile
        !           306: 2      infront and show hostile
        !           307: 3      only triggered by damage
        !           308: =============
        !           309: */
        !           310: int range (edict_t *self, edict_t *other)
        !           311: {
        !           312:        vec3_t  v;
        !           313:        float   len;
        !           314: 
        !           315:        VectorSubtract (self->s.origin, other->s.origin, v);
        !           316:        len = VectorLength (v);
        !           317:        if (len < MELEE_DISTANCE)
        !           318:                return RANGE_MELEE;
        !           319:        if (len < 500)
        !           320:                return RANGE_NEAR;
        !           321:        if (len < 1000)
        !           322:                return RANGE_MID;
        !           323:        return RANGE_FAR;
        !           324: }
        !           325: 
        !           326: /*
        !           327: =============
        !           328: visible
        !           329: 
        !           330: returns 1 if the entity is visible to self, even if not infront ()
        !           331: =============
        !           332: */
        !           333: qboolean visible (edict_t *self, edict_t *other)
        !           334: {
        !           335:        vec3_t  spot1;
        !           336:        vec3_t  spot2;
        !           337:        trace_t trace;
        !           338: 
        !           339:        VectorCopy (self->s.origin, spot1);
        !           340:        spot1[2] += self->viewheight;
        !           341:        VectorCopy (other->s.origin, spot2);
        !           342:        spot2[2] += other->viewheight;
        !           343:        trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE);
        !           344:        
        !           345:        if (trace.fraction == 1.0 || trace.ent == other)                // PGM
        !           346:                return true;
        !           347:        return false;
        !           348: }
        !           349: 
        !           350: 
        !           351: /*
        !           352: =============
        !           353: infront
        !           354: 
        !           355: returns 1 if the entity is in front (in sight) of self
        !           356: =============
        !           357: */
        !           358: qboolean infront (edict_t *self, edict_t *other)
        !           359: {
        !           360:        vec3_t  vec;
        !           361:        float   dot;
        !           362:        vec3_t  forward;
        !           363:        
        !           364:        AngleVectors (self->s.angles, forward, NULL, NULL);
        !           365:        VectorSubtract (other->s.origin, self->s.origin, vec);
        !           366:        VectorNormalize (vec);
        !           367:        dot = DotProduct (vec, forward);
        !           368:        
        !           369:        if (dot > 0.3)
        !           370:                return true;
        !           371:        return false;
        !           372: }
        !           373: 
        !           374: 
        !           375: //============================================================================
        !           376: 
        !           377: void HuntTarget (edict_t *self)
        !           378: {
        !           379:        vec3_t  vec;
        !           380: 
        !           381:        self->goalentity = self->enemy;
        !           382:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
        !           383:                self->monsterinfo.stand (self);
        !           384:        else
        !           385:                self->monsterinfo.run (self);
        !           386:        VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
        !           387:        self->ideal_yaw = vectoyaw(vec);
        !           388:        // wait a while before first attack
        !           389:        if (!(self->monsterinfo.aiflags & AI_STAND_GROUND))
        !           390:                AttackFinished (self, 1);
        !           391: }
        !           392: 
        !           393: void FoundTarget (edict_t *self)
        !           394: {
        !           395:        // let other monsters see this monster for a while
        !           396:        if (self->enemy->client)
        !           397:        {
        !           398:                if(self->enemy->flags & FL_DISGUISED)
        !           399:                {
        !           400: //                     level.disguise_violator = self->enemy;
        !           401: //                     level.disguise_violation_framenum = level.framenum + 5;
        !           402:                        self->enemy->flags &= ~FL_DISGUISED;
        !           403:                }
        !           404:                
        !           405:                level.sight_entity = self;
        !           406:                level.sight_entity_framenum = level.framenum;
        !           407:                level.sight_entity->light_level = 128;
        !           408:        }
        !           409: 
        !           410:        self->show_hostile = level.time + 1;            // wake up other monsters
        !           411: 
        !           412:        VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting);
        !           413:        self->monsterinfo.trail_time = level.time;
        !           414:        // PMM
        !           415:        VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
        !           416:        self->monsterinfo.blind_fire_delay = 0;
        !           417:        // PMM
        !           418: 
        !           419:        if (!self->combattarget)
        !           420:        {
        !           421:                HuntTarget (self);
        !           422:                return;
        !           423:        }
        !           424: 
        !           425:        self->goalentity = self->movetarget = G_PickTarget(self->combattarget);
        !           426:        if (!self->movetarget)
        !           427:        {
        !           428:                self->goalentity = self->movetarget = self->enemy;
        !           429:                HuntTarget (self);
        !           430:                gi.dprintf("%s at %s, combattarget %s not found\n", self->classname, vtos(self->s.origin), self->combattarget);
        !           431:                return;
        !           432:        }
        !           433: 
        !           434:        // clear out our combattarget, these are a one shot deal
        !           435:        self->combattarget = NULL;
        !           436:        self->monsterinfo.aiflags |= AI_COMBAT_POINT;
        !           437: 
        !           438:        // clear the targetname, that point is ours!
        !           439:        self->movetarget->targetname = NULL;
        !           440:        self->monsterinfo.pausetime = 0;
        !           441: 
        !           442:        // run for it
        !           443:        self->monsterinfo.run (self);
        !           444: }
        !           445: 
        !           446: 
        !           447: /*
        !           448: ===========
        !           449: FindTarget
        !           450: 
        !           451: Self is currently not attacking anything, so try to find a target
        !           452: 
        !           453: Returns TRUE if an enemy was sighted
        !           454: 
        !           455: When a player fires a missile, the point of impact becomes a fakeplayer so
        !           456: that monsters that see the impact will respond as if they had seen the
        !           457: player.
        !           458: 
        !           459: To avoid spending too much time, only a single client (or fakeclient) is
        !           460: checked each frame.  This means multi player games will have slightly
        !           461: slower noticing monsters.
        !           462: ============
        !           463: */
        !           464: qboolean FindTarget (edict_t *self)
        !           465: {
        !           466:        edict_t         *client;
        !           467:        qboolean        heardit;
        !           468:        int                     r;
        !           469: 
        !           470:        if (self->monsterinfo.aiflags & AI_GOOD_GUY)
        !           471:        {
        !           472:                if (self->goalentity && self->goalentity->inuse && self->goalentity->classname)
        !           473:                {
        !           474:                        if (strcmp(self->goalentity->classname, "target_actor") == 0)
        !           475:                                return false;
        !           476:                }
        !           477: 
        !           478:                //FIXME look for monsters?
        !           479:                return false;
        !           480:        }
        !           481: 
        !           482:        // if we're going to a combat point, just proceed
        !           483:        if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
        !           484:                return false;
        !           485: 
        !           486: // if the first spawnflag bit is set, the monster will only wake up on
        !           487: // really seeing the player, not another monster getting angry or hearing
        !           488: // something
        !           489: 
        !           490: // revised behavior so they will wake up if they "see" a player make a noise
        !           491: // but not weapon impact/explosion noises
        !           492: 
        !           493:        heardit = false;
        !           494:        if ((level.sight_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
        !           495:        {
        !           496:                client = level.sight_entity;
        !           497:                if (client->enemy == self->enemy)
        !           498:                {
        !           499:                        return false;
        !           500:                }
        !           501:        }
        !           502:        else if (level.disguise_violation_framenum > level.framenum)
        !           503:        {
        !           504:                client = level.disguise_violator;
        !           505:        }
        !           506:        else if (level.sound_entity_framenum >= (level.framenum - 1))
        !           507:        {
        !           508:                client = level.sound_entity;
        !           509:                heardit = true;
        !           510:        }
        !           511:        else if (!(self->enemy) && (level.sound2_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
        !           512:        {
        !           513:                client = level.sound2_entity;
        !           514:                heardit = true;
        !           515:        }
        !           516:        else
        !           517:        {
        !           518:                client = level.sight_client;
        !           519:                if (!client)
        !           520:                        return false;   // no clients to get mad at
        !           521:        }
        !           522: 
        !           523:        // if the entity went away, forget it
        !           524:        if (!client->inuse)
        !           525:                return false;
        !           526: 
        !           527:        if (client == self->enemy)
        !           528:                return true;    // JDC false;
        !           529: 
        !           530:        if (client->client)
        !           531:        {
        !           532:                if (client->flags & FL_NOTARGET)
        !           533:                        return false;
        !           534:        }
        !           535:        else if (client->svflags & SVF_MONSTER)
        !           536:        {
        !           537:                if (!client->enemy)
        !           538:                        return false;
        !           539:                if (client->enemy->flags & FL_NOTARGET)
        !           540:                        return false;
        !           541:        }
        !           542:        else if (heardit)
        !           543:        {
        !           544:                // pgm - a little more paranoia won't hurt....
        !           545:                if ((client->owner) && (client->owner->flags & FL_NOTARGET))
        !           546:                        return false;
        !           547:        }
        !           548:        else
        !           549:                return false;
        !           550: 
        !           551:        if (!heardit)
        !           552:        {
        !           553:                r = range (self, client);
        !           554: 
        !           555:                if (r == RANGE_FAR)
        !           556:                        return false;
        !           557: 
        !           558: // this is where we would check invisibility
        !           559: 
        !           560:                // is client in an spot too dark to be seen?
        !           561:                if (client->light_level <= 5)
        !           562:                        return false;
        !           563: 
        !           564:                if (!visible (self, client))
        !           565:                {
        !           566:                        return false;
        !           567:                }
        !           568: 
        !           569:                if (r == RANGE_NEAR)
        !           570:                {
        !           571:                        if (client->show_hostile < level.time && !infront (self, client))
        !           572:                        {
        !           573:                                return false;
        !           574:                        }
        !           575:                }
        !           576:                else if (r == RANGE_MID)
        !           577:                {
        !           578:                        if (!infront (self, client))
        !           579:                        {
        !           580:                                return false;
        !           581:                        }
        !           582:                }
        !           583: 
        !           584:                self->enemy = client;
        !           585: 
        !           586:                if (strcmp(self->enemy->classname, "player_noise") != 0)
        !           587:                {
        !           588:                        self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
        !           589: 
        !           590:                        if (!self->enemy->client)
        !           591:                        {
        !           592:                                self->enemy = self->enemy->enemy;
        !           593:                                if (!self->enemy->client)
        !           594:                                {
        !           595:                                        self->enemy = NULL;
        !           596:                                        return false;
        !           597:                                }
        !           598:                        }
        !           599:                }
        !           600:        }
        !           601:        else    // heardit
        !           602:        {
        !           603:                vec3_t  temp;
        !           604: 
        !           605:                if (self->spawnflags & 1)
        !           606:                {
        !           607:                        if (!visible (self, client))
        !           608:                                return false;
        !           609:                }
        !           610:                else
        !           611:                {
        !           612:                        if (!gi.inPHS(self->s.origin, client->s.origin))
        !           613:                                return false;
        !           614:                }
        !           615: 
        !           616:                VectorSubtract (client->s.origin, self->s.origin, temp);
        !           617: 
        !           618:                if (VectorLength(temp) > 1000)  // too far to hear
        !           619:                {
        !           620:                        return false;
        !           621:                }
        !           622: 
        !           623:                // check area portals - if they are different and not connected then we can't hear it
        !           624:                if (client->areanum != self->areanum)
        !           625:                        if (!gi.AreasConnected(self->areanum, client->areanum))
        !           626:                                return false;
        !           627:        
        !           628:                self->ideal_yaw = vectoyaw(temp);
        !           629:                if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
        !           630:                        M_ChangeYaw (self);
        !           631: 
        !           632:                // hunt the sound for a bit; hopefully find the real player
        !           633:                self->monsterinfo.aiflags |= AI_SOUND_TARGET;
        !           634:                self->enemy = client;
        !           635:        }
        !           636: 
        !           637: //
        !           638: // got one
        !           639: //
        !           640:        FoundTarget (self);
        !           641: 
        !           642:        if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight))
        !           643:                self->monsterinfo.sight (self, self->enemy);
        !           644: 
        !           645:        return true;
        !           646: }
        !           647: 
        !           648: 
        !           649: //=============================================================================
        !           650: 
        !           651: /*
        !           652: ============
        !           653: FacingIdeal
        !           654: 
        !           655: ============
        !           656: */
        !           657: qboolean FacingIdeal(edict_t *self)
        !           658: {
        !           659:        float   delta;
        !           660: 
        !           661:        delta = anglemod(self->s.angles[YAW] - self->ideal_yaw);
        !           662:        if (delta > 45 && delta < 315)
        !           663:                return false;
        !           664:        return true;
        !           665: }
        !           666: 
        !           667: 
        !           668: //=============================================================================
        !           669: 
        !           670: qboolean M_CheckAttack (edict_t *self)
        !           671: {
        !           672:        vec3_t  spot1, spot2;
        !           673:        float   chance;
        !           674:        trace_t tr;
        !           675: 
        !           676:        if (self->enemy->health > 0)
        !           677:        {
        !           678:        // see if any entities are in the way of the shot
        !           679:                VectorCopy (self->s.origin, spot1);
        !           680:                spot1[2] += self->viewheight;
        !           681:                VectorCopy (self->enemy->s.origin, spot2);
        !           682:                spot2[2] += self->enemy->viewheight;
        !           683: 
        !           684:                tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WINDOW);
        !           685: 
        !           686:                // do we have a clear shot?
        !           687:                if (tr.ent != self->enemy)
        !           688:                {       
        !           689:                        // PGM - we want them to go ahead and shoot at info_notnulls if they can.
        !           690:                        if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0)                //PGM
        !           691:                        {
        !           692:                                // PMM - if we can't see our target, and we're not blocked by a monster, go into blind fire if available
        !           693:                                if ((!(tr.ent->svflags & SVF_MONSTER)) && (!visible(self, self->enemy)))
        !           694:                                {
        !           695:                                        if ((self->monsterinfo.blindfire) && (self->monsterinfo.blind_fire_delay <= 20.0))
        !           696:                                        {
        !           697:                                                if (level.time < self->monsterinfo.attack_finished)
        !           698:                                                {
        !           699:                                                        return false;
        !           700:                                                }
        !           701:                                                if (level.time < (self->monsterinfo.trail_time + self->monsterinfo.blind_fire_delay))
        !           702:                                                {
        !           703:                                                        // wait for our time
        !           704:                                                        return false;
        !           705:                                                }
        !           706:                                                else
        !           707:                                                {
        !           708: //     gi.WriteByte (svc_temp_entity);
        !           709: //     gi.WriteByte (TE_DEBUGTRAIL);
        !           710: //     gi.WritePosition (spot1);
        !           711: //     gi.WritePosition (self->monsterinfo.blind_fire_target);
        !           712: //     gi.multicast (self->s.origin, MULTICAST_ALL);
        !           713:                                                        // make sure we're not going to shoot a monster
        !           714:                                                        tr = gi.trace (spot1, NULL, NULL, self->monsterinfo.blind_fire_target, self, CONTENTS_MONSTER);
        !           715:                                                        if (tr.allsolid || tr.startsolid || ((tr.fraction < 1.0) && (tr.ent != self->enemy)))
        !           716:                                                        {
        !           717:                                                                if ((g_showlogic) && (g_showlogic->value))
        !           718:                                                                        gi.dprintf ("blindfire blocked\n");
        !           719:                                                                return false;
        !           720:                                                        }
        !           721: 
        !           722:                                                        self->monsterinfo.attack_state = AS_BLIND;
        !           723:                                                        return true;
        !           724:                                                }
        !           725:                                        }
        !           726:                                }
        !           727:                                // pmm
        !           728:                                return false;
        !           729:                        }
        !           730:                }
        !           731:        }
        !           732:        
        !           733:        // melee attack
        !           734:        if (enemy_range == RANGE_MELEE)
        !           735:        {
        !           736:                // don't always melee in easy mode
        !           737:                if (skill->value == 0 && (rand()&3) )
        !           738:                {
        !           739:                        // PMM - fix for melee only monsters & strafing
        !           740:                        self->monsterinfo.attack_state = AS_STRAIGHT;
        !           741:                        return false;
        !           742:                }
        !           743:                if (self->monsterinfo.melee)
        !           744:                        self->monsterinfo.attack_state = AS_MELEE;
        !           745:                else
        !           746:                        self->monsterinfo.attack_state = AS_MISSILE;
        !           747:                return true;
        !           748:        }
        !           749:        
        !           750: // missile attack
        !           751:        if (!self->monsterinfo.attack)
        !           752:        {
        !           753:                // PMM - fix for melee only monsters & strafing
        !           754:                self->monsterinfo.attack_state = AS_STRAIGHT;
        !           755:                return false;
        !           756:        }
        !           757:        
        !           758:        if (level.time < self->monsterinfo.attack_finished)
        !           759:                return false;
        !           760:                
        !           761:        if (enemy_range == RANGE_FAR)
        !           762:                return false;
        !           763: 
        !           764:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
        !           765:        {
        !           766:                chance = 0.4;
        !           767:        }
        !           768:        else if (enemy_range == RANGE_MELEE)
        !           769:        {
        !           770:                chance = 0.2;
        !           771:        }
        !           772:        else if (enemy_range == RANGE_NEAR)
        !           773:        {
        !           774:                chance = 0.1;
        !           775:        }
        !           776:        else if (enemy_range == RANGE_MID)
        !           777:        {
        !           778:                chance = 0.02;
        !           779:        }
        !           780:        else
        !           781:        {
        !           782:                return false;
        !           783:        }
        !           784: 
        !           785:        if (skill->value == 0)
        !           786:                chance *= 0.5;
        !           787:        else if (skill->value >= 2)
        !           788:                chance *= 2;
        !           789: 
        !           790:        // PGM - go ahead and shoot every time if it's a info_notnull
        !           791:        if ((random () < chance) || (self->enemy->solid == SOLID_NOT))
        !           792:        {
        !           793:                self->monsterinfo.attack_state = AS_MISSILE;
        !           794:                self->monsterinfo.attack_finished = level.time + 2*random();
        !           795:                return true;
        !           796:        }
        !           797: 
        !           798:        // PMM -daedalus should strafe more .. this can be done here or in a customized
        !           799:        // check_attack code for the hover.
        !           800:        if (self->flags & FL_FLY)
        !           801:        {
        !           802:                // originally, just 0.3
        !           803:                float strafe_chance;
        !           804:                if (!(strcmp(self->classname, "monster_daedalus")))
        !           805:                        strafe_chance = 0.8;
        !           806:                else
        !           807:                        strafe_chance = 0.6;
        !           808: 
        !           809:                if (random() < strafe_chance)
        !           810:                        self->monsterinfo.attack_state = AS_SLIDING;
        !           811:                else
        !           812:                        self->monsterinfo.attack_state = AS_STRAIGHT;
        !           813:        }
        !           814: // do we want the monsters strafing?
        !           815: #ifdef SLIDING_TROOPS
        !           816:        else
        !           817:        {
        !           818:                if (random() < 0.4)
        !           819:                        self->monsterinfo.attack_state = AS_SLIDING;
        !           820:                else
        !           821:                        self->monsterinfo.attack_state = AS_STRAIGHT;
        !           822:        }
        !           823: #endif
        !           824: //-PMM
        !           825: 
        !           826:        return false;
        !           827: }
        !           828: 
        !           829: 
        !           830: /*
        !           831: =============
        !           832: ai_run_melee
        !           833: 
        !           834: Turn and close until within an angle to launch a melee attack
        !           835: =============
        !           836: */
        !           837: void ai_run_melee(edict_t *self)
        !           838: {
        !           839:        self->ideal_yaw = enemy_yaw;
        !           840:        if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
        !           841:                M_ChangeYaw (self);
        !           842: 
        !           843:        if (FacingIdeal(self))
        !           844:        {
        !           845:                self->monsterinfo.melee (self);
        !           846:                self->monsterinfo.attack_state = AS_STRAIGHT;
        !           847:        }
        !           848: }
        !           849: 
        !           850: 
        !           851: /*
        !           852: =============
        !           853: ai_run_missile
        !           854: 
        !           855: Turn in place until within an angle to launch a missile attack
        !           856: =============
        !           857: */
        !           858: void ai_run_missile(edict_t *self)
        !           859: {
        !           860:        self->ideal_yaw = enemy_yaw;
        !           861:        if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
        !           862:                M_ChangeYaw (self);
        !           863: 
        !           864:        if (FacingIdeal(self))
        !           865:        {
        !           866:                self->monsterinfo.attack (self);
        !           867: //             if (self->monsterinfo.attack_state == AS_MISSILE)
        !           868:                if ((self->monsterinfo.attack_state == AS_MISSILE) || (self->monsterinfo.attack_state == AS_BLIND))
        !           869:                        self->monsterinfo.attack_state = AS_STRAIGHT;
        !           870: //             else if (self->monsterinfo.attack_state != AS_SLIDING)
        !           871: //                     gi.dprintf ("ai_run_missile: Unexpected attack state %d !\n", self->monsterinfo.attack_state);
        !           872:        }
        !           873: };
        !           874: 
        !           875: 
        !           876: /*
        !           877: =============
        !           878: ai_run_slide
        !           879: 
        !           880: Strafe sideways, but stay at aproximately the same range
        !           881: =============
        !           882: */
        !           883: void ai_run_slide(edict_t *self, float distance)
        !           884: {
        !           885:        float   ofs;
        !           886:        float   angle;
        !           887: 
        !           888:        self->ideal_yaw = enemy_yaw;
        !           889: 
        !           890: //     if (self->flags & FL_FLY)
        !           891: //             angle = 90;
        !           892: //     else
        !           893: //             angle = 45;
        !           894:        
        !           895:        angle = 90;
        !           896:        
        !           897:        if (self->monsterinfo.lefty)
        !           898:                ofs = angle;
        !           899:        else
        !           900:                ofs = -angle;
        !           901: //     
        !           902: //     if (!(self->flags & FL_FLY))
        !           903: //     {
        !           904: //             // non fliers should actually turn towards the direction their trying to run
        !           905: //             self->ideal_yaw += ofs;
        !           906: //     }
        !           907: //
        !           908:        if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
        !           909:                M_ChangeYaw (self);
        !           910: 
        !           911:        /*
        !           912:        if (!(self->flags & FL_FLY))
        !           913:        {
        !           914:                if (M_walkmove (self, self->ideal_yaw + ofs, distance))
        !           915:                        return;
        !           916:        }
        !           917:        else
        !           918:        {
        !           919:                if (M_walkmove (self, self->ideal_yaw, distance))
        !           920:                        return;
        !           921:        }
        !           922:        */
        !           923:        // PMM - clamp maximum sideways move for non flyers to make them look less jerky
        !           924:        if (!self->flags & FL_FLY)
        !           925:                distance = min (distance, MAX_SIDESTEP);
        !           926:        if (M_walkmove (self, self->ideal_yaw + ofs, distance))
        !           927:                return;
        !           928:        self->monsterinfo.lefty = 1 - self->monsterinfo.lefty;
        !           929:        if (M_walkmove (self, self->ideal_yaw - ofs, distance))
        !           930:                return;
        !           931:        // PMM - the move failed, so signal the caller (ai_run) to try going straight
        !           932:        self->monsterinfo.attack_state = AS_STRAIGHT;
        !           933:        /*
        !           934:        if (!(self->flags & FL_FLY))
        !           935:        {
        !           936:                M_walkmove (self, self->ideal_yaw + ofs, distance);
        !           937:        }
        !           938:        else
        !           939:        {
        !           940:                M_walkmove (self, self->ideal_yaw, distance);
        !           941:        }*/
        !           942: }
        !           943: 
        !           944: 
        !           945: /*
        !           946: =============
        !           947: ai_checkattack
        !           948: 
        !           949: Decides if we're going to attack or do something else
        !           950: used by ai_run and ai_stand
        !           951: =============
        !           952: */
        !           953: qboolean ai_checkattack (edict_t *self, float dist)
        !           954: {
        !           955:        vec3_t          temp;
        !           956:        qboolean        hesDeadJim;
        !           957:        // PMM
        !           958:        qboolean        retval;
        !           959: 
        !           960: // this causes monsters to run blindly to the combat point w/o firing
        !           961:        if (self->goalentity)
        !           962:        {
        !           963:                if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
        !           964:                        return false;
        !           965: 
        !           966:                if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
        !           967:                {
        !           968:                        if ((level.time - self->enemy->teleport_time) > 5.0)
        !           969:                        {
        !           970:                                if (self->goalentity == self->enemy)
        !           971:                                        if (self->movetarget)
        !           972:                                                self->goalentity = self->movetarget;
        !           973:                                        else
        !           974:                                                self->goalentity = NULL;
        !           975:                                self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
        !           976:                                if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
        !           977:                                        self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
        !           978:                        }
        !           979:                        else
        !           980:                        {
        !           981:                                self->show_hostile = level.time + 1;
        !           982:                                return false;
        !           983:                        }
        !           984:                }
        !           985:        }
        !           986: 
        !           987:        enemy_vis = false;
        !           988: 
        !           989: // see if the enemy is dead
        !           990:        hesDeadJim = false;
        !           991:        if ((!self->enemy) || (!self->enemy->inuse))
        !           992:        {
        !           993:                hesDeadJim = true;
        !           994:        }
        !           995:        else if (self->monsterinfo.aiflags & AI_MEDIC)
        !           996:        {
        !           997:                if (self->enemy->health > 0)
        !           998:                {
        !           999:                        hesDeadJim = true;
        !          1000: //                     self->monsterinfo.aiflags &= ~AI_MEDIC;
        !          1001:                }
        !          1002:        }
        !          1003:        else
        !          1004:        {
        !          1005:                if (self->monsterinfo.aiflags & AI_BRUTAL)
        !          1006:                {
        !          1007:                        if (self->enemy->health <= -80)
        !          1008:                                hesDeadJim = true;
        !          1009:                }
        !          1010:                else
        !          1011:                {
        !          1012:                        if (self->enemy->health <= 0)
        !          1013:                                hesDeadJim = true;
        !          1014:                }
        !          1015:        }
        !          1016: 
        !          1017:        if (hesDeadJim)
        !          1018:        {
        !          1019:                self->monsterinfo.aiflags &= ~AI_MEDIC;
        !          1020:                self->enemy = NULL;
        !          1021:        // FIXME: look all around for other targets
        !          1022:                if (self->oldenemy && self->oldenemy->health > 0)
        !          1023:                {
        !          1024:                        self->enemy = self->oldenemy;
        !          1025:                        self->oldenemy = NULL;
        !          1026:                        HuntTarget (self);
        !          1027:                }
        !          1028: //ROGUE - multiple teslas make monsters lose track of the player.
        !          1029:                else if(self->monsterinfo.last_player_enemy && self->monsterinfo.last_player_enemy->health > 0)
        !          1030:                {
        !          1031:                        if ((g_showlogic) && (g_showlogic->value))
        !          1032:                                gi.dprintf("resorting to last_player_enemy...\n");
        !          1033:                        self->enemy = self->monsterinfo.last_player_enemy;
        !          1034:                        self->oldenemy = NULL;
        !          1035:                        self->monsterinfo.last_player_enemy = NULL;
        !          1036:                        HuntTarget (self);
        !          1037:                }
        !          1038: //ROGUE
        !          1039:                else
        !          1040:                {
        !          1041:                        if (self->movetarget)
        !          1042:                        {
        !          1043:                                self->goalentity = self->movetarget;
        !          1044:                                self->monsterinfo.walk (self);
        !          1045:                        }
        !          1046:                        else
        !          1047:                        {
        !          1048:                                // we need the pausetime otherwise the stand code
        !          1049:                                // will just revert to walking with no target and
        !          1050:                                // the monsters will wonder around aimlessly trying
        !          1051:                                // to hunt the world entity
        !          1052:                                self->monsterinfo.pausetime = level.time + 100000000;
        !          1053:                                self->monsterinfo.stand (self);
        !          1054:                        }
        !          1055:                        return true;
        !          1056:                }
        !          1057:        }
        !          1058: 
        !          1059:        self->show_hostile = level.time + 1;            // wake up other monsters
        !          1060: 
        !          1061: // check knowledge of enemy
        !          1062:        enemy_vis = visible(self, self->enemy);
        !          1063:        if (enemy_vis)
        !          1064:        {
        !          1065:                self->monsterinfo.search_time = level.time + 5;
        !          1066:                VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
        !          1067:                // PMM
        !          1068:                self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
        !          1069:                self->monsterinfo.trail_time = level.time;
        !          1070:                VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
        !          1071:                self->monsterinfo.blind_fire_delay = 0;
        !          1072:                // pmm
        !          1073:        }
        !          1074: 
        !          1075: // look for other coop players here
        !          1076: //     if (coop && self->monsterinfo.search_time < level.time)
        !          1077: //     {
        !          1078: //             if (FindTarget (self))
        !          1079: //                     return true;
        !          1080: //     }
        !          1081: 
        !          1082:        enemy_infront = infront(self, self->enemy);
        !          1083:        enemy_range = range(self, self->enemy);
        !          1084:        VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
        !          1085:        enemy_yaw = vectoyaw(temp);
        !          1086: 
        !          1087: 
        !          1088:        // JDC self->ideal_yaw = enemy_yaw;
        !          1089: 
        !          1090:        // PMM -- reordered so the monster specific checkattack is called before the run_missle/melee/checkvis
        !          1091:        // stuff .. this allows for, among other things, circle strafing and attacking while in ai_run
        !          1092:        retval = self->monsterinfo.checkattack (self);
        !          1093:        if (retval)
        !          1094:        {
        !          1095:                // PMM
        !          1096:                if (self->monsterinfo.attack_state == AS_MISSILE)
        !          1097:                {
        !          1098:                        ai_run_missile (self);
        !          1099:                        return true;
        !          1100:                }
        !          1101:                if (self->monsterinfo.attack_state == AS_MELEE)
        !          1102:                {
        !          1103:                        ai_run_melee (self);
        !          1104:                        return true;
        !          1105:                }
        !          1106:                // PMM -- added so monsters can shoot blind
        !          1107:                if (self->monsterinfo.attack_state == AS_BLIND)
        !          1108:                {
        !          1109:                        ai_run_missile (self);
        !          1110:                        return true;
        !          1111:                }
        !          1112:                // pmm
        !          1113: 
        !          1114:                // if enemy is not currently visible, we will never attack
        !          1115:                if (!enemy_vis)
        !          1116:                        return false;
        !          1117:                // PMM
        !          1118:        }
        !          1119:        return retval;
        !          1120:        // PMM
        !          1121: //     return self->monsterinfo.checkattack (self);
        !          1122: }
        !          1123: 
        !          1124: 
        !          1125: /*
        !          1126: =============
        !          1127: ai_run
        !          1128: 
        !          1129: The monster has an enemy it is trying to kill
        !          1130: =============
        !          1131: */
        !          1132: void ai_run (edict_t *self, float dist)
        !          1133: {
        !          1134:        vec3_t          v;
        !          1135:        edict_t         *tempgoal;
        !          1136:        edict_t         *save;
        !          1137:        qboolean        new;
        !          1138:        edict_t         *marker;
        !          1139:        float           d1, d2;
        !          1140:        trace_t         tr;
        !          1141:        vec3_t          v_forward, v_right;
        !          1142:        float           left, center, right;
        !          1143:        vec3_t          left_target, right_target;
        !          1144:        //PMM
        !          1145:        qboolean        retval;
        !          1146:        qboolean        alreadyMoved = false;
        !          1147:  
        !          1148:        // if we're going to a combat point, just proceed
        !          1149:        if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
        !          1150:        {
        !          1151:                M_MoveToGoal (self, dist);
        !          1152:                return;
        !          1153:        }
        !          1154: 
        !          1155:        // PMM
        !          1156:        if (self->monsterinfo.aiflags & AI_DUCKED)
        !          1157:        {
        !          1158:                if ((g_showlogic) && (g_showlogic->value))
        !          1159:                        gi.dprintf ("%s - duck flag cleaned up!\n", self->classname);
        !          1160:                self->monsterinfo.aiflags &= ~AI_DUCKED;
        !          1161:        }
        !          1162:        if (self->maxs[2] != self->monsterinfo.base_height)
        !          1163:        {
        !          1164:                if ((g_showlogic) && (g_showlogic->value))
        !          1165:                        gi.dprintf ("%s - ducked height corrected!\n", self->classname);
        !          1166:                monster_duck_up (self);
        !          1167:        }
        !          1168:        if ((self->monsterinfo.aiflags & AI_MANUAL_STEERING) && (strcmp(self->classname, "monster_turret")))
        !          1169:        {
        !          1170:                if ((g_showlogic) && (g_showlogic->value))
        !          1171:                        gi.dprintf ("%s - manual steering in ai_run!\n", self->classname);
        !          1172:        }
        !          1173:        // pmm
        !          1174: 
        !          1175: //==========
        !          1176: //PGM
        !          1177:        // if we're currently looking for a hint path
        !          1178:        if (self->monsterinfo.aiflags & AI_HINT_PATH)
        !          1179:        {
        !          1180:                // determine direction to our destination hintpath.
        !          1181:                // FIXME - is this needed EVERY time? I was having trouble with them
        !          1182:                // sometimes not going after it, and this fixed it.
        !          1183: //             VectorSubtract(self->movetarget->s.origin, self->s.origin, v);
        !          1184: //             vectoangles(v, v_forward);
        !          1185: //             self->ideal_yaw = v_forward[YAW];
        !          1186: //             gi.dprintf("seeking hintpath. origin: %s %0.1f\n", vtos(v), self->ideal_yaw);
        !          1187:                M_MoveToGoal (self, dist);
        !          1188:                if(!self->inuse)
        !          1189:                        return;                 // PGM - g_touchtrigger free problem
        !          1190: //             return;
        !          1191: 
        !          1192:                // if we've already seen the player, and can't see him now, return
        !          1193:                if(self->enemy && !visible(self, self->enemy))
        !          1194:                        return;
        !          1195: 
        !          1196:                // if not and can't find the player, return
        !          1197: //             if(!FindTarget(self))
        !          1198: //                     return;
        !          1199: 
        !          1200:                // if we see the player, stop following hintpaths.
        !          1201:                if(g_showlogic && g_showlogic->value)
        !          1202:                        gi.dprintf("stopped following hint paths\n");
        !          1203: 
        !          1204:                // disconnect from hintpaths and start looking normally for players.
        !          1205:                hintpath_stop (self);
        !          1206:                // pmm - no longer needed, since hintpath_stop does it
        !          1207: //             HuntTarget(self);
        !          1208:                return;
        !          1209:        }
        !          1210: //PGM
        !          1211: //==========
        !          1212: 
        !          1213:        if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
        !          1214:        {
        !          1215:                VectorSubtract (self->s.origin, self->enemy->s.origin, v);
        !          1216:                if (VectorLength(v) < 64)
        !          1217:                {
        !          1218:                        self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
        !          1219:                        self->monsterinfo.stand (self);
        !          1220:                        return;
        !          1221:                }
        !          1222: 
        !          1223:                M_MoveToGoal (self, dist);
        !          1224:                // PMM - prevent double moves for sound_targets
        !          1225:                alreadyMoved = true;
        !          1226:                // pmm
        !          1227:                if(!self->inuse)
        !          1228:                        return;                 // PGM - g_touchtrigger free problem
        !          1229: 
        !          1230:                if (!FindTarget (self))
        !          1231:                        return;
        !          1232:        }
        !          1233: 
        !          1234:        // PMM -- moved ai_checkattack up here so the monsters can attack while strafing or charging
        !          1235: 
        !          1236:        // PMM -- if we're dodging, make sure to keep the attack_state AS_SLIDING
        !          1237: 
        !          1238:        retval = ai_checkattack (self, dist);
        !          1239: 
        !          1240:        if (self->monsterinfo.aiflags & AI_DODGING)
        !          1241:                self->monsterinfo.attack_state = AS_SLIDING;
        !          1242: 
        !          1243:        if (self->monsterinfo.attack_state == AS_SLIDING)
        !          1244:        {
        !          1245:                // PMM - protect against double moves
        !          1246:                if (!alreadyMoved)
        !          1247:                        ai_run_slide (self, dist);
        !          1248:                // PMM
        !          1249:                // we're using attack_state as the return value out of ai_run_slide to indicate whether or not the
        !          1250:                // move succeeded.  If the move succeeded, and we're still sliding, we're done in here (since we've
        !          1251:                // had our chance to shoot in ai_checkattack, and have moved).
        !          1252:                // if the move failed, our state is as_straight, and it will be taken care of below
        !          1253:                if ((!retval) && (self->monsterinfo.attack_state == AS_SLIDING))
        !          1254:                        return;
        !          1255:        }
        !          1256:        else if (self->monsterinfo.aiflags & AI_CHARGING)
        !          1257:        {
        !          1258:                self->ideal_yaw = enemy_yaw;
        !          1259:                if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
        !          1260:                        M_ChangeYaw (self);
        !          1261:        }
        !          1262:        if (retval)
        !          1263:        {
        !          1264:                // PMM - is this useful?  Monsters attacking usually call the ai_charge routine..
        !          1265:                // the only monster this affects should be the soldier
        !          1266:                if ((dist != 0) && (!alreadyMoved) && (self->monsterinfo.attack_state == AS_STRAIGHT) && (!(self->monsterinfo.aiflags & AI_STAND_GROUND)))
        !          1267:                {
        !          1268:                        M_MoveToGoal (self, dist);
        !          1269:                }
        !          1270:                if ((self->enemy) && (self->enemy->inuse) && (enemy_vis))
        !          1271:                {
        !          1272:                        self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
        !          1273:                        VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
        !          1274:                        self->monsterinfo.trail_time = level.time;
        !          1275:                        //PMM
        !          1276:                        VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
        !          1277:                        self->monsterinfo.blind_fire_delay = 0;
        !          1278:                        //pmm
        !          1279:                }
        !          1280:                return;
        !          1281:        }
        !          1282:        //PMM
        !          1283: //     if (ai_checkattack (self, dist))
        !          1284: //             return;
        !          1285: 
        !          1286: //     if (self->monsterinfo.attack_state == AS_SLIDING)
        !          1287: //     {
        !          1288: //             ai_run_slide (self, dist);
        !          1289: //             return;
        !          1290: //     }
        !          1291: 
        !          1292:        if (enemy_vis)
        !          1293:        {
        !          1294: //             if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
        !          1295: //                     gi.dprintf("regained sight\n");
        !          1296:                // PMM - check for alreadyMoved
        !          1297:                if (!alreadyMoved)
        !          1298:                        M_MoveToGoal (self, dist);
        !          1299:                if(!self->inuse)
        !          1300:                        return;                 // PGM - g_touchtrigger free problem
        !          1301: 
        !          1302:                self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
        !          1303:                VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
        !          1304:                self->monsterinfo.trail_time = level.time;
        !          1305:                // PMM
        !          1306:                VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
        !          1307:                self->monsterinfo.blind_fire_delay = 0;
        !          1308:                // pmm
        !          1309:                return;
        !          1310:        }
        !          1311: 
        !          1312:        // coop will change to another enemy if visible
        !          1313:        if (coop->value)
        !          1314:        {       // FIXME: insane guys get mad with this, which causes crashes!
        !          1315:                if (FindTarget (self))
        !          1316:                        return;
        !          1317:        }
        !          1318: 
        !          1319: //=======
        !          1320: //PGM
        !          1321:        // if we've been looking (unsuccessfully) for the player for 10 seconds
        !          1322:        // PMM - reduced to 5, makes them much nastier
        !          1323:        if((self->monsterinfo.trail_time + 5) <= level.time)
        !          1324:        {
        !          1325:                // and we haven't checked for valid hint paths in the last 10 seconds
        !          1326:                if((self->monsterinfo.last_hint_time + 10) <= level.time)
        !          1327:                {
        !          1328:                        // check for hint_paths.
        !          1329:                        self->monsterinfo.last_hint_time = level.time;
        !          1330:                        if(monsterlost_checkhint(self))
        !          1331:                                return;
        !          1332:                }
        !          1333:        }
        !          1334: //PGM
        !          1335: //=======
        !          1336: 
        !          1337:        if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20)))
        !          1338:        {
        !          1339:                // PMM - double move protection
        !          1340:                if (!alreadyMoved)
        !          1341:                        M_MoveToGoal (self, dist);
        !          1342:                self->monsterinfo.search_time = 0;
        !          1343: //             gi.dprintf("search timeout\n");
        !          1344:                return;
        !          1345:        }
        !          1346: 
        !          1347:        save = self->goalentity;
        !          1348:        tempgoal = G_Spawn();
        !          1349:        self->goalentity = tempgoal;
        !          1350: 
        !          1351:        new = false;
        !          1352: 
        !          1353:        if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT))
        !          1354:        {
        !          1355:                // just lost sight of the player, decide where to go first
        !          1356: //             gi.dprintf("lost sight of player, last seen at %s\n", vtos(self->monsterinfo.last_sighting));
        !          1357:                self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN);
        !          1358:                self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP);
        !          1359:                new = true;
        !          1360:        }
        !          1361: 
        !          1362:        if (self->monsterinfo.aiflags & AI_PURSUE_NEXT)
        !          1363:        {
        !          1364: //             vec3_t  debug_vec;
        !          1365: 
        !          1366:                self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT;
        !          1367: //             VectorSubtract(self->monsterinfo.last_sighting, self->s.origin, debug_vec); 
        !          1368: //             gi.dprintf("reached current goal: %s %s %f", vtos(self->s.origin), vtos(self->monsterinfo.last_sighting), VectorLength(debug_vec));
        !          1369: 
        !          1370:                // give ourself more time since we got this far
        !          1371:                self->monsterinfo.search_time = level.time + 5;
        !          1372: 
        !          1373:                if (self->monsterinfo.aiflags & AI_PURSUE_TEMP)
        !          1374:                {
        !          1375: //                     gi.dprintf("was temp goal; retrying original\n");
        !          1376:                        self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP;
        !          1377:                        marker = NULL;
        !          1378:                        VectorCopy (self->monsterinfo.saved_goal, self->monsterinfo.last_sighting);
        !          1379:                        new = true;
        !          1380:                }
        !          1381:                else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN)
        !          1382:                {
        !          1383:                        self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN;
        !          1384:                        marker = PlayerTrail_PickFirst (self);
        !          1385:                }
        !          1386:                else
        !          1387:                {
        !          1388:                        marker = PlayerTrail_PickNext (self);
        !          1389:                }
        !          1390: 
        !          1391:                if (marker)
        !          1392:                {
        !          1393:                        VectorCopy (marker->s.origin, self->monsterinfo.last_sighting);
        !          1394:                        self->monsterinfo.trail_time = marker->timestamp;
        !          1395:                        self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW];
        !          1396: //                     gi.dprintf("heading is %0.1f\n", self->ideal_yaw);
        !          1397: 
        !          1398: //                     debug_drawline(self.origin, self.last_sighting, 52);
        !          1399:                        new = true;
        !          1400:                }
        !          1401:        }
        !          1402: 
        !          1403:        VectorSubtract (self->s.origin, self->monsterinfo.last_sighting, v);
        !          1404:        d1 = VectorLength(v);
        !          1405:        if (d1 <= dist)
        !          1406:        {
        !          1407:                self->monsterinfo.aiflags |= AI_PURSUE_NEXT;
        !          1408:                dist = d1;
        !          1409:        }
        !          1410: 
        !          1411:        VectorCopy (self->monsterinfo.last_sighting, self->goalentity->s.origin);
        !          1412: 
        !          1413:        if (new)
        !          1414:        {
        !          1415: //             gi.dprintf("checking for course correction\n");
        !          1416: 
        !          1417:                tr = gi.trace(self->s.origin, self->mins, self->maxs, self->monsterinfo.last_sighting, self, MASK_PLAYERSOLID);
        !          1418:                if (tr.fraction < 1)
        !          1419:                {
        !          1420:                        VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
        !          1421:                        d1 = VectorLength(v);
        !          1422:                        center = tr.fraction;
        !          1423:                        d2 = d1 * ((center+1)/2);
        !          1424:                        self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
        !          1425:                        AngleVectors(self->s.angles, v_forward, v_right, NULL);
        !          1426: 
        !          1427:                        VectorSet(v, d2, -16, 0);
        !          1428:                        G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
        !          1429:                        tr = gi.trace(self->s.origin, self->mins, self->maxs, left_target, self, MASK_PLAYERSOLID);
        !          1430:                        left = tr.fraction;
        !          1431: 
        !          1432:                        VectorSet(v, d2, 16, 0);
        !          1433:                        G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
        !          1434:                        tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, self, MASK_PLAYERSOLID);
        !          1435:                        right = tr.fraction;
        !          1436: 
        !          1437:                        center = (d1*center)/d2;
        !          1438:                        if (left >= center && left > right)
        !          1439:                        {
        !          1440:                                if (left < 1)
        !          1441:                                {
        !          1442:                                        VectorSet(v, d2 * left * 0.5, -16, 0);
        !          1443:                                        G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
        !          1444: //                                     gi.dprintf("incomplete path, go part way and adjust again\n");
        !          1445:                                }
        !          1446:                                VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
        !          1447:                                self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
        !          1448:                                VectorCopy (left_target, self->goalentity->s.origin);
        !          1449:                                VectorCopy (left_target, self->monsterinfo.last_sighting);
        !          1450:                                VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
        !          1451:                                self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
        !          1452: //                             gi.dprintf("adjusted left\n");
        !          1453: //                             debug_drawline(self.origin, self.last_sighting, 152);
        !          1454:                        }
        !          1455:                        else if (right >= center && right > left)
        !          1456:                        {
        !          1457:                                if (right < 1)
        !          1458:                                {
        !          1459:                                        VectorSet(v, d2 * right * 0.5, 16, 0);
        !          1460:                                        G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
        !          1461: //                                     gi.dprintf("incomplete path, go part way and adjust again\n");
        !          1462:                                }
        !          1463:                                VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
        !          1464:                                self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
        !          1465:                                VectorCopy (right_target, self->goalentity->s.origin);
        !          1466:                                VectorCopy (right_target, self->monsterinfo.last_sighting);
        !          1467:                                VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
        !          1468:                                self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
        !          1469: //                             gi.dprintf("adjusted right\n");
        !          1470: //                             debug_drawline(self.origin, self.last_sighting, 152);
        !          1471:                        }
        !          1472:                }
        !          1473: //             else gi.dprintf("course was fine\n");
        !          1474:        }
        !          1475: 
        !          1476:        M_MoveToGoal (self, dist);
        !          1477:        if(!self->inuse)
        !          1478:                return;                 // PGM - g_touchtrigger free problem
        !          1479: 
        !          1480:        G_FreeEdict(tempgoal);
        !          1481: 
        !          1482:        if (self)
        !          1483:                self->goalentity = save;
        !          1484: }

unix.superglobalmegacorp.com

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