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