Annotation of quake2/game/g_ai.c, revision 1.1.1.2

1.1.1.2 ! root        1: /*
        !             2: Copyright (C) 1997-2001 Id Software, Inc.
        !             3: 
        !             4: This program is free software; you can redistribute it and/or
        !             5: modify it under the terms of the GNU General Public License
        !             6: as published by the Free Software Foundation; either version 2
        !             7: of the License, or (at your option) any later version.
        !             8: 
        !             9: This program is distributed in the hope that it will be useful,
        !            10: but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
        !            12: 
        !            13: See the GNU General Public License for more details.
        !            14: 
        !            15: You should have received a copy of the GNU General Public License
        !            16: along with this program; if not, write to the Free Software
        !            17: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
        !            18: 
        !            19: */
1.1       root       20: // g_ai.c
                     21: 
                     22: #include "g_local.h"
                     23: 
                     24: qboolean FindTarget (edict_t *self);
                     25: extern cvar_t  *maxclients;
                     26: 
                     27: qboolean ai_checkattack (edict_t *self, float dist);
                     28: 
                     29: qboolean       enemy_vis;
                     30: qboolean       enemy_infront;
                     31: int                    enemy_range;
                     32: float          enemy_yaw;
                     33: 
                     34: //============================================================================
                     35: 
                     36: 
                     37: /*
                     38: =================
                     39: AI_SetSightClient
                     40: 
                     41: Called once each frame to set level.sight_client to the
                     42: player to be checked for in findtarget.
                     43: 
                     44: If all clients are either dead or in notarget, sight_client
                     45: will be null.
                     46: 
                     47: In coop games, sight_client will cycle between the clients.
                     48: =================
                     49: */
                     50: void AI_SetSightClient (void)
                     51: {
                     52:        edict_t *ent;
                     53:        int             start, check;
                     54: 
                     55:        if (level.sight_client == NULL)
                     56:                start = 1;
                     57:        else
                     58:                start = level.sight_client - g_edicts;
                     59: 
                     60:        check = start;
                     61:        while (1)
                     62:        {
                     63:                check++;
                     64:                if (check > game.maxclients)
                     65:                        check = 1;
                     66:                ent = &g_edicts[check];
                     67:                if (ent->inuse
                     68:                        && ent->health > 0
                     69:                        && !(ent->flags & FL_NOTARGET) )
                     70:                {
                     71:                        level.sight_client = ent;
                     72:                        return;         // got one
                     73:                }
                     74:                if (check == start)
                     75:                {
                     76:                        level.sight_client = NULL;
                     77:                        return;         // nobody to see
                     78:                }
                     79:        }
                     80: }
                     81: 
                     82: //============================================================================
                     83: 
                     84: /*
                     85: =============
                     86: ai_move
                     87: 
                     88: Move the specified distance at current facing.
                     89: This replaces the QC functions: ai_forward, ai_back, ai_pain, and ai_painforward
                     90: ==============
                     91: */
                     92: void ai_move (edict_t *self, float dist)
                     93: {
                     94:        M_walkmove (self, self->s.angles[YAW], dist);
                     95: }
                     96: 
                     97: 
                     98: /*
                     99: =============
                    100: ai_stand
                    101: 
                    102: Used for standing around and looking for players
                    103: Distance is for slight position adjustments needed by the animations
                    104: ==============
                    105: */
                    106: void ai_stand (edict_t *self, float dist)
                    107: {
                    108:        vec3_t  v;
                    109: 
                    110:        if (dist)
                    111:                M_walkmove (self, self->s.angles[YAW], dist);
                    112: 
                    113:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
                    114:        {
                    115:                if (self->enemy)
                    116:                {
                    117:                        VectorSubtract (self->enemy->s.origin, self->s.origin, v);
                    118:                        self->ideal_yaw = vectoyaw(v);
                    119:                        if (self->s.angles[YAW] != self->ideal_yaw && self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
                    120:                        {
                    121:                                self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
                    122:                                self->monsterinfo.run (self);
                    123:                        }
                    124:                        M_ChangeYaw (self);
                    125:                        ai_checkattack (self, 0);
                    126:                }
                    127:                else
                    128:                        FindTarget (self);
                    129:                return;
                    130:        }
                    131: 
                    132:        if (FindTarget (self))
                    133:                return;
                    134:        
                    135:        if (level.time > self->monsterinfo.pausetime)
                    136:        {
                    137:                self->monsterinfo.walk (self);
                    138:                return;
                    139:        }
                    140: 
                    141:        if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && (level.time > self->monsterinfo.idle_time))
                    142:        {
                    143:                if (self->monsterinfo.idle_time)
                    144:                {
                    145:                        self->monsterinfo.idle (self);
                    146:                        self->monsterinfo.idle_time = level.time + 15 + random() * 15;
                    147:                }
                    148:                else
                    149:                {
                    150:                        self->monsterinfo.idle_time = level.time + random() * 15;
                    151:                }
                    152:        }
                    153: }
                    154: 
                    155: 
                    156: /*
                    157: =============
                    158: ai_walk
                    159: 
                    160: The monster is walking it's beat
                    161: =============
                    162: */
                    163: void ai_walk (edict_t *self, float dist)
                    164: {
                    165:        M_MoveToGoal (self, dist);
                    166: 
                    167:        // check for noticing a player
                    168:        if (FindTarget (self))
                    169:                return;
                    170: 
                    171:        if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time))
                    172:        {
                    173:                if (self->monsterinfo.idle_time)
                    174:                {
                    175:                        self->monsterinfo.search (self);
                    176:                        self->monsterinfo.idle_time = level.time + 15 + random() * 15;
                    177:                }
                    178:                else
                    179:                {
                    180:                        self->monsterinfo.idle_time = level.time + random() * 15;
                    181:                }
                    182:        }
                    183: }
                    184: 
                    185: 
                    186: /*
                    187: =============
                    188: ai_charge
                    189: 
                    190: Turns towards target and advances
                    191: Use this call with a distnace of 0 to replace ai_face
                    192: ==============
                    193: */
                    194: void ai_charge (edict_t *self, float dist)
                    195: {
                    196:        vec3_t  v;
                    197: 
                    198:        VectorSubtract (self->enemy->s.origin, self->s.origin, v);
                    199:        self->ideal_yaw = vectoyaw(v);
                    200:        M_ChangeYaw (self);
                    201: 
                    202:        if (dist)
                    203:                M_walkmove (self, self->s.angles[YAW], dist);
                    204: }
                    205: 
                    206: 
                    207: /*
                    208: =============
                    209: ai_turn
                    210: 
                    211: don't move, but turn towards ideal_yaw
                    212: Distance is for slight position adjustments needed by the animations
                    213: =============
                    214: */
                    215: void ai_turn (edict_t *self, float dist)
                    216: {
                    217:        if (dist)
                    218:                M_walkmove (self, self->s.angles[YAW], dist);
                    219: 
                    220:        if (FindTarget (self))
                    221:                return;
                    222:        
                    223:        M_ChangeYaw (self);
                    224: }
                    225: 
                    226: 
                    227: /*
                    228: 
                    229: .enemy
                    230: Will be world if not currently angry at anyone.
                    231: 
                    232: .movetarget
                    233: The next path spot to walk toward.  If .enemy, ignore .movetarget.
                    234: When an enemy is killed, the monster will try to return to it's path.
                    235: 
                    236: .hunt_time
                    237: Set to time + something when the player is in sight, but movement straight for
                    238: him is blocked.  This causes the monster to use wall following code for
                    239: movement direction instead of sighting on the player.
                    240: 
                    241: .ideal_yaw
                    242: A yaw angle of the intended direction, which will be turned towards at up
                    243: to 45 deg / state.  If the enemy is in view and hunt_time is not active,
                    244: this will be the exact line towards the enemy.
                    245: 
                    246: .pausetime
                    247: A monster will leave it's stand state and head towards it's .movetarget when
                    248: time > .pausetime.
                    249: 
                    250: walkmove(angle, speed) primitive is all or nothing
                    251: */
                    252: 
                    253: /*
                    254: =============
                    255: range
                    256: 
                    257: returns the range catagorization of an entity reletive to self
                    258: 0      melee range, will become hostile even if back is turned
                    259: 1      visibility and infront, or visibility and show hostile
                    260: 2      infront and show hostile
                    261: 3      only triggered by damage
                    262: =============
                    263: */
                    264: int range (edict_t *self, edict_t *other)
                    265: {
                    266:        vec3_t  v;
                    267:        float   len;
                    268: 
                    269:        VectorSubtract (self->s.origin, other->s.origin, v);
                    270:        len = VectorLength (v);
                    271:        if (len < MELEE_DISTANCE)
                    272:                return RANGE_MELEE;
                    273:        if (len < 500)
                    274:                return RANGE_NEAR;
                    275:        if (len < 1000)
                    276:                return RANGE_MID;
                    277:        return RANGE_FAR;
                    278: }
                    279: 
                    280: /*
                    281: =============
                    282: visible
                    283: 
                    284: returns 1 if the entity is visible to self, even if not infront ()
                    285: =============
                    286: */
                    287: qboolean visible (edict_t *self, edict_t *other)
                    288: {
                    289:        vec3_t  spot1;
                    290:        vec3_t  spot2;
                    291:        trace_t trace;
                    292: 
                    293:        VectorCopy (self->s.origin, spot1);
                    294:        spot1[2] += self->viewheight;
                    295:        VectorCopy (other->s.origin, spot2);
                    296:        spot2[2] += other->viewheight;
                    297:        trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE);
                    298:        
                    299:        if (trace.fraction == 1.0)
                    300:                return true;
                    301:        return false;
                    302: }
                    303: 
                    304: 
                    305: /*
                    306: =============
                    307: infront
                    308: 
                    309: returns 1 if the entity is in front (in sight) of self
                    310: =============
                    311: */
                    312: qboolean infront (edict_t *self, edict_t *other)
                    313: {
                    314:        vec3_t  vec;
                    315:        float   dot;
                    316:        vec3_t  forward;
                    317:        
                    318:        AngleVectors (self->s.angles, forward, NULL, NULL);
                    319:        VectorSubtract (other->s.origin, self->s.origin, vec);
                    320:        VectorNormalize (vec);
                    321:        dot = DotProduct (vec, forward);
                    322:        
                    323:        if (dot > 0.3)
                    324:                return true;
                    325:        return false;
                    326: }
                    327: 
                    328: 
                    329: //============================================================================
                    330: 
                    331: void HuntTarget (edict_t *self)
                    332: {
                    333:        vec3_t  vec;
                    334: 
                    335:        self->goalentity = self->enemy;
                    336:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
                    337:                self->monsterinfo.stand (self);
                    338:        else
                    339:                self->monsterinfo.run (self);
                    340:        VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
                    341:        self->ideal_yaw = vectoyaw(vec);
                    342:        // wait a while before first attack
                    343:        if (!(self->monsterinfo.aiflags & AI_STAND_GROUND))
                    344:                AttackFinished (self, 1);
                    345: }
                    346: 
                    347: void FoundTarget (edict_t *self)
                    348: {
                    349:        // let other monsters see this monster for a while
                    350:        if (self->enemy->client)
                    351:        {
                    352:                level.sight_entity = self;
                    353:                level.sight_entity_framenum = level.framenum;
                    354:                level.sight_entity->light_level = 128;
                    355:        }
                    356: 
                    357:        self->show_hostile = level.time + 1;            // wake up other monsters
                    358: 
                    359:        VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting);
                    360:        self->monsterinfo.trail_time = level.time;
                    361: 
                    362:        if (!self->combattarget)
                    363:        {
                    364:                HuntTarget (self);
                    365:                return;
                    366:        }
                    367: 
                    368:        self->goalentity = self->movetarget = G_PickTarget(self->combattarget);
                    369:        if (!self->movetarget)
                    370:        {
                    371:                self->goalentity = self->movetarget = self->enemy;
                    372:                HuntTarget (self);
                    373:                gi.dprintf("%s at %s, combattarget %s not found\n", self->classname, vtos(self->s.origin), self->combattarget);
                    374:                return;
                    375:        }
                    376: 
                    377:        // clear out our combattarget, these are a one shot deal
                    378:        self->combattarget = NULL;
                    379:        self->monsterinfo.aiflags |= AI_COMBAT_POINT;
                    380: 
                    381:        // clear the targetname, that point is ours!
                    382:        self->movetarget->targetname = NULL;
                    383:        self->monsterinfo.pausetime = 0;
                    384: 
                    385:        // run for it
                    386:        self->monsterinfo.run (self);
                    387: }
                    388: 
                    389: 
                    390: /*
                    391: ===========
                    392: FindTarget
                    393: 
                    394: Self is currently not attacking anything, so try to find a target
                    395: 
                    396: Returns TRUE if an enemy was sighted
                    397: 
                    398: When a player fires a missile, the point of impact becomes a fakeplayer so
                    399: that monsters that see the impact will respond as if they had seen the
                    400: player.
                    401: 
                    402: To avoid spending too much time, only a single client (or fakeclient) is
                    403: checked each frame.  This means multi player games will have slightly
                    404: slower noticing monsters.
                    405: ============
                    406: */
                    407: qboolean FindTarget (edict_t *self)
                    408: {
                    409:        edict_t         *client;
                    410:        qboolean        heardit;
                    411:        int                     r;
                    412: 
                    413:        if (self->monsterinfo.aiflags & AI_GOOD_GUY)
                    414:        {
                    415:                if (self->goalentity && self->goalentity->inuse && self->goalentity->classname)
                    416:                {
                    417:                        if (strcmp(self->goalentity->classname, "target_actor") == 0)
                    418:                                return false;
                    419:                }
                    420: 
                    421:                //FIXME look for monsters?
                    422:                return false;
                    423:        }
                    424: 
                    425:        // if we're going to a combat point, just proceed
                    426:        if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
                    427:                return false;
                    428: 
                    429: // if the first spawnflag bit is set, the monster will only wake up on
                    430: // really seeing the player, not another monster getting angry or hearing
                    431: // something
                    432: 
                    433: // revised behavior so they will wake up if they "see" a player make a noise
                    434: // but not weapon impact/explosion noises
                    435: 
                    436:        heardit = false;
                    437:        if ((level.sight_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
                    438:        {
                    439:                client = level.sight_entity;
                    440:                if (client->enemy == self->enemy)
                    441:                {
                    442:                        return false;
                    443:                }
                    444:        }
                    445:        else if (level.sound_entity_framenum >= (level.framenum - 1))
                    446:        {
                    447:                client = level.sound_entity;
                    448:                heardit = true;
                    449:        }
                    450:        else if (!(self->enemy) && (level.sound2_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
                    451:        {
                    452:                client = level.sound2_entity;
                    453:                heardit = true;
                    454:        }
                    455:        else
                    456:        {
                    457:                client = level.sight_client;
                    458:                if (!client)
                    459:                        return false;   // no clients to get mad at
                    460:        }
                    461: 
                    462:        // if the entity went away, forget it
                    463:        if (!client->inuse)
                    464:                return false;
                    465: 
                    466:        if (client == self->enemy)
                    467:                return true;    // JDC false;
                    468: 
                    469:        if (client->client)
                    470:        {
                    471:                if (client->flags & FL_NOTARGET)
                    472:                        return false;
                    473:        }
                    474:        else if (client->svflags & SVF_MONSTER)
                    475:        {
                    476:                if (!client->enemy)
                    477:                        return false;
                    478:                if (client->enemy->flags & FL_NOTARGET)
                    479:                        return false;
                    480:        }
                    481:        else if (heardit)
                    482:        {
                    483:                if (client->owner->flags & FL_NOTARGET)
                    484:                        return false;
                    485:        }
                    486:        else
                    487:                return false;
                    488: 
                    489:        if (!heardit)
                    490:        {
                    491:                r = range (self, client);
                    492: 
                    493:                if (r == RANGE_FAR)
                    494:                        return false;
                    495: 
                    496: // this is where we would check invisibility
                    497: 
                    498:                // is client in an spot too dark to be seen?
                    499:                if (client->light_level <= 5)
                    500:                        return false;
                    501: 
                    502:                if (!visible (self, client))
                    503:                {
                    504:                        return false;
                    505:                }
                    506: 
                    507:                if (r == RANGE_NEAR)
                    508:                {
                    509:                        if (client->show_hostile < level.time && !infront (self, client))
                    510:                        {
                    511:                                return false;
                    512:                        }
                    513:                }
                    514:                else if (r == RANGE_MID)
                    515:                {
                    516:                        if (!infront (self, client))
                    517:                        {
                    518:                                return false;
                    519:                        }
                    520:                }
                    521: 
                    522:                self->enemy = client;
                    523: 
                    524:                if (strcmp(self->enemy->classname, "player_noise") != 0)
                    525:                {
                    526:                        self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
                    527: 
                    528:                        if (!self->enemy->client)
                    529:                        {
                    530:                                self->enemy = self->enemy->enemy;
                    531:                                if (!self->enemy->client)
                    532:                                {
                    533:                                        self->enemy = NULL;
                    534:                                        return false;
                    535:                                }
                    536:                        }
                    537:                }
                    538:        }
                    539:        else    // heardit
                    540:        {
                    541:                vec3_t  temp;
                    542: 
                    543:                if (self->spawnflags & 1)
                    544:                {
                    545:                        if (!visible (self, client))
                    546:                                return false;
                    547:                }
                    548:                else
                    549:                {
                    550:                        if (!gi.inPHS(self->s.origin, client->s.origin))
                    551:                                return false;
                    552:                }
                    553: 
                    554:                VectorSubtract (client->s.origin, self->s.origin, temp);
                    555: 
                    556:                if (VectorLength(temp) > 1000)  // too far to hear
                    557:                {
                    558:                        return false;
                    559:                }
                    560: 
                    561:                // check area portals - if they are different and not connected then we can't hear it
                    562:                if (client->areanum != self->areanum)
                    563:                        if (!gi.AreasConnected(self->areanum, client->areanum))
                    564:                                return false;
                    565: 
                    566:                self->ideal_yaw = vectoyaw(temp);
                    567:                M_ChangeYaw (self);
                    568: 
                    569:                // hunt the sound for a bit; hopefully find the real player
                    570:                self->monsterinfo.aiflags |= AI_SOUND_TARGET;
                    571:                self->enemy = client;
                    572:        }
                    573: 
                    574: //
                    575: // got one
                    576: //
                    577:        FoundTarget (self);
                    578: 
                    579:        if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight))
                    580:                self->monsterinfo.sight (self, self->enemy);
                    581: 
                    582:        return true;
                    583: }
                    584: 
                    585: 
                    586: //=============================================================================
                    587: 
                    588: /*
                    589: ============
                    590: FacingIdeal
                    591: 
                    592: ============
                    593: */
                    594: qboolean FacingIdeal(edict_t *self)
                    595: {
                    596:        float   delta;
                    597: 
                    598:        delta = anglemod(self->s.angles[YAW] - self->ideal_yaw);
                    599:        if (delta > 45 && delta < 315)
                    600:                return false;
                    601:        return true;
                    602: }
                    603: 
                    604: 
                    605: //=============================================================================
                    606: 
                    607: qboolean M_CheckAttack (edict_t *self)
                    608: {
                    609:        vec3_t  spot1, spot2;
                    610:        float   chance;
                    611:        trace_t tr;
                    612: 
                    613:        if (self->enemy->health > 0)
                    614:        {
                    615:        // see if any entities are in the way of the shot
                    616:                VectorCopy (self->s.origin, spot1);
                    617:                spot1[2] += self->viewheight;
                    618:                VectorCopy (self->enemy->s.origin, spot2);
                    619:                spot2[2] += self->enemy->viewheight;
                    620: 
                    621:                tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WINDOW);
                    622: 
                    623:                // do we have a clear shot?
                    624:                if (tr.ent != self->enemy)
                    625:                        return false;
                    626:        }
                    627:        
                    628:        // melee attack
                    629:        if (enemy_range == RANGE_MELEE)
                    630:        {
                    631:                // don't always melee in easy mode
                    632:                if (skill->value == 0 && (rand()&3) )
                    633:                        return false;
                    634:                if (self->monsterinfo.melee)
                    635:                        self->monsterinfo.attack_state = AS_MELEE;
                    636:                else
                    637:                        self->monsterinfo.attack_state = AS_MISSILE;
                    638:                return true;
                    639:        }
                    640:        
                    641: // missile attack
                    642:        if (!self->monsterinfo.attack)
                    643:                return false;
                    644:                
                    645:        if (level.time < self->monsterinfo.attack_finished)
                    646:                return false;
                    647:                
                    648:        if (enemy_range == RANGE_FAR)
                    649:                return false;
                    650: 
                    651:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
                    652:        {
                    653:                chance = 0.4;
                    654:        }
                    655:        else if (enemy_range == RANGE_MELEE)
                    656:        {
                    657:                chance = 0.2;
                    658:        }
                    659:        else if (enemy_range == RANGE_NEAR)
                    660:        {
                    661:                chance = 0.1;
                    662:        }
                    663:        else if (enemy_range == RANGE_MID)
                    664:        {
                    665:                chance = 0.02;
                    666:        }
                    667:        else
                    668:        {
                    669:                return false;
                    670:        }
                    671: 
                    672:        if (skill->value == 0)
                    673:                chance *= 0.5;
                    674:        else if (skill->value >= 2)
                    675:                chance *= 2;
                    676: 
                    677:        if (random () < chance)
                    678:        {
                    679:                self->monsterinfo.attack_state = AS_MISSILE;
                    680:                self->monsterinfo.attack_finished = level.time + 2*random();
                    681:                return true;
                    682:        }
                    683: 
                    684:        if (self->flags & FL_FLY)
                    685:        {
                    686:                if (random() < 0.3)
                    687:                        self->monsterinfo.attack_state = AS_SLIDING;
                    688:                else
                    689:                        self->monsterinfo.attack_state = AS_STRAIGHT;
                    690:        }
                    691: 
                    692:        return false;
                    693: }
                    694: 
                    695: 
                    696: /*
                    697: =============
                    698: ai_run_melee
                    699: 
                    700: Turn and close until within an angle to launch a melee attack
                    701: =============
                    702: */
                    703: void ai_run_melee(edict_t *self)
                    704: {
                    705:        self->ideal_yaw = enemy_yaw;
                    706:        M_ChangeYaw (self);
                    707: 
                    708:        if (FacingIdeal(self))
                    709:        {
                    710:                self->monsterinfo.melee (self);
                    711:                self->monsterinfo.attack_state = AS_STRAIGHT;
                    712:        }
                    713: }
                    714: 
                    715: 
                    716: /*
                    717: =============
                    718: ai_run_missile
                    719: 
                    720: Turn in place until within an angle to launch a missile attack
                    721: =============
                    722: */
                    723: void ai_run_missile(edict_t *self)
                    724: {
                    725:        self->ideal_yaw = enemy_yaw;
                    726:        M_ChangeYaw (self);
                    727: 
                    728:        if (FacingIdeal(self))
                    729:        {
                    730:                self->monsterinfo.attack (self);
                    731:                self->monsterinfo.attack_state = AS_STRAIGHT;
                    732:        }
                    733: };
                    734: 
                    735: 
                    736: /*
                    737: =============
                    738: ai_run_slide
                    739: 
                    740: Strafe sideways, but stay at aproximately the same range
                    741: =============
                    742: */
                    743: void ai_run_slide(edict_t *self, float distance)
                    744: {
                    745:        float   ofs;
                    746:        
                    747:        self->ideal_yaw = enemy_yaw;
                    748:        M_ChangeYaw (self);
                    749: 
                    750:        if (self->monsterinfo.lefty)
                    751:                ofs = 90;
                    752:        else
                    753:                ofs = -90;
                    754:        
                    755:        if (M_walkmove (self, self->ideal_yaw + ofs, distance))
                    756:                return;
                    757:                
                    758:        self->monsterinfo.lefty = 1 - self->monsterinfo.lefty;
                    759:        M_walkmove (self, self->ideal_yaw - ofs, distance);
                    760: }
                    761: 
                    762: 
                    763: /*
                    764: =============
                    765: ai_checkattack
                    766: 
                    767: Decides if we're going to attack or do something else
                    768: used by ai_run and ai_stand
                    769: =============
                    770: */
                    771: qboolean ai_checkattack (edict_t *self, float dist)
                    772: {
                    773:        vec3_t          temp;
                    774:        qboolean        hesDeadJim;
                    775: 
                    776: // this causes monsters to run blindly to the combat point w/o firing
                    777:        if (self->goalentity)
                    778:        {
                    779:                if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
                    780:                        return false;
                    781: 
                    782:                if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
                    783:                {
                    784:                        if ((level.time - self->enemy->teleport_time) > 5.0)
                    785:                        {
                    786:                                if (self->goalentity == self->enemy)
                    787:                                        if (self->movetarget)
                    788:                                                self->goalentity = self->movetarget;
                    789:                                        else
                    790:                                                self->goalentity = NULL;
                    791:                                self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
                    792:                                if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
                    793:                                        self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
                    794:                        }
                    795:                        else
                    796:                        {
                    797:                                self->show_hostile = level.time + 1;
                    798:                                return false;
                    799:                        }
                    800:                }
                    801:        }
                    802: 
                    803:        enemy_vis = false;
                    804: 
                    805: // see if the enemy is dead
                    806:        hesDeadJim = false;
                    807:        if ((!self->enemy) || (!self->enemy->inuse))
                    808:        {
                    809:                hesDeadJim = true;
                    810:        }
                    811:        else if (self->monsterinfo.aiflags & AI_MEDIC)
                    812:        {
                    813:                if (self->enemy->health > 0)
                    814:                {
                    815:                        hesDeadJim = true;
                    816:                        self->monsterinfo.aiflags &= ~AI_MEDIC;
                    817:                }
                    818:        }
                    819:        else
                    820:        {
                    821:                if (self->monsterinfo.aiflags & AI_BRUTAL)
                    822:                {
                    823:                        if (self->enemy->health <= -80)
                    824:                                hesDeadJim = true;
                    825:                }
                    826:                else
                    827:                {
                    828:                        if (self->enemy->health <= 0)
                    829:                                hesDeadJim = true;
                    830:                }
                    831:        }
                    832: 
                    833:        if (hesDeadJim)
                    834:        {
                    835:                self->enemy = NULL;
                    836:        // FIXME: look all around for other targets
                    837:                if (self->oldenemy && self->oldenemy->health > 0)
                    838:                {
                    839:                        self->enemy = self->oldenemy;
                    840:                        self->oldenemy = NULL;
                    841:                        HuntTarget (self);
                    842:                }
                    843:                else
                    844:                {
                    845:                        if (self->movetarget)
                    846:                        {
                    847:                                self->goalentity = self->movetarget;
                    848:                                self->monsterinfo.walk (self);
                    849:                        }
                    850:                        else
                    851:                        {
                    852:                                // we need the pausetime otherwise the stand code
                    853:                                // will just revert to walking with no target and
                    854:                                // the monsters will wonder around aimlessly trying
                    855:                                // to hunt the world entity
                    856:                                self->monsterinfo.pausetime = level.time + 100000000;
                    857:                                self->monsterinfo.stand (self);
                    858:                        }
                    859:                        return true;
                    860:                }
                    861:        }
                    862: 
                    863:        self->show_hostile = level.time + 1;            // wake up other monsters
                    864: 
                    865: // check knowledge of enemy
                    866:        enemy_vis = visible(self, self->enemy);
                    867:        if (enemy_vis)
                    868:        {
                    869:                self->monsterinfo.search_time = level.time + 5;
                    870:                VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
                    871:        }
                    872: 
                    873: // look for other coop players here
                    874: //     if (coop && self->monsterinfo.search_time < level.time)
                    875: //     {
                    876: //             if (FindTarget (self))
                    877: //                     return true;
                    878: //     }
                    879: 
                    880:        enemy_infront = infront(self, self->enemy);
                    881:        enemy_range = range(self, self->enemy);
                    882:        VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
                    883:        enemy_yaw = vectoyaw(temp);
                    884: 
                    885: 
                    886:        // JDC self->ideal_yaw = enemy_yaw;
                    887: 
                    888:        if (self->monsterinfo.attack_state == AS_MISSILE)
                    889:        {
                    890:                ai_run_missile (self);
                    891:                return true;
                    892:        }
                    893:        if (self->monsterinfo.attack_state == AS_MELEE)
                    894:        {
                    895:                ai_run_melee (self);
                    896:                return true;
                    897:        }
                    898: 
                    899:        // if enemy is not currently visible, we will never attack
                    900:        if (!enemy_vis)
                    901:                return false;
                    902: 
                    903:        return self->monsterinfo.checkattack (self);
                    904: }
                    905: 
                    906: 
                    907: /*
                    908: =============
                    909: ai_run
                    910: 
                    911: The monster has an enemy it is trying to kill
                    912: =============
                    913: */
                    914: void ai_run (edict_t *self, float dist)
                    915: {
                    916:        vec3_t          v;
                    917:        edict_t         *tempgoal;
                    918:        edict_t         *save;
                    919:        qboolean        new;
                    920:        edict_t         *marker;
                    921:        float           d1, d2;
                    922:        trace_t         tr;
                    923:        vec3_t          v_forward, v_right;
                    924:        float           left, center, right;
                    925:        vec3_t          left_target, right_target;
                    926: 
                    927:        // if we're going to a combat point, just proceed
                    928:        if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
                    929:        {
                    930:                M_MoveToGoal (self, dist);
                    931:                return;
                    932:        }
                    933: 
                    934:        if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
                    935:        {
                    936:                VectorSubtract (self->s.origin, self->enemy->s.origin, v);
                    937:                if (VectorLength(v) < 64)
                    938:                {
                    939:                        self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
                    940:                        self->monsterinfo.stand (self);
                    941:                        return;
                    942:                }
                    943: 
                    944:                M_MoveToGoal (self, dist);
                    945: 
                    946:                if (!FindTarget (self))
                    947:                        return;
                    948:        }
                    949: 
                    950:        if (ai_checkattack (self, dist))
                    951:                return;
                    952: 
                    953:        if (self->monsterinfo.attack_state == AS_SLIDING)
                    954:        {
                    955:                ai_run_slide (self, dist);
                    956:                return;
                    957:        }
                    958: 
                    959:        if (enemy_vis)
                    960:        {
                    961: //             if (self.aiflags & AI_LOST_SIGHT)
                    962: //                     dprint("regained sight\n");
                    963:                M_MoveToGoal (self, dist);
                    964:                self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
                    965:                VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
                    966:                self->monsterinfo.trail_time = level.time;
                    967:                return;
                    968:        }
                    969: 
                    970:        // coop will change to another enemy if visible
                    971:        if (coop->value)
                    972:        {       // FIXME: insane guys get mad with this, which causes crashes!
                    973:                if (FindTarget (self))
                    974:                        return;
                    975:        }
                    976: 
                    977:        if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20)))
                    978:        {
                    979:                M_MoveToGoal (self, dist);
                    980:                self->monsterinfo.search_time = 0;
                    981: //             dprint("search timeout\n");
                    982:                return;
                    983:        }
                    984: 
                    985:        save = self->goalentity;
                    986:        tempgoal = G_Spawn();
                    987:        self->goalentity = tempgoal;
                    988: 
                    989:        new = false;
                    990: 
                    991:        if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT))
                    992:        {
                    993:                // just lost sight of the player, decide where to go first
                    994: //             dprint("lost sight of player, last seen at "); dprint(vtos(self.last_sighting)); dprint("\n");
                    995:                self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN);
                    996:                self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP);
                    997:                new = true;
                    998:        }
                    999: 
                   1000:        if (self->monsterinfo.aiflags & AI_PURSUE_NEXT)
                   1001:        {
                   1002:                self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT;
                   1003: //             dprint("reached current goal: "); dprint(vtos(self.origin)); dprint(" "); dprint(vtos(self.last_sighting)); dprint(" "); dprint(ftos(vlen(self.origin - self.last_sighting))); dprint("\n");
                   1004: 
                   1005:                // give ourself more time since we got this far
                   1006:                self->monsterinfo.search_time = level.time + 5;
                   1007: 
                   1008:                if (self->monsterinfo.aiflags & AI_PURSUE_TEMP)
                   1009:                {
                   1010: //                     dprint("was temp goal; retrying original\n");
                   1011:                        self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP;
                   1012:                        marker = NULL;
                   1013:                        VectorCopy (self->monsterinfo.saved_goal, self->monsterinfo.last_sighting);
                   1014:                        new = true;
                   1015:                }
                   1016:                else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN)
                   1017:                {
                   1018:                        self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN;
                   1019:                        marker = PlayerTrail_PickFirst (self);
                   1020:                }
                   1021:                else
                   1022:                {
                   1023:                        marker = PlayerTrail_PickNext (self);
                   1024:                }
                   1025: 
                   1026:                if (marker)
                   1027:                {
                   1028:                        VectorCopy (marker->s.origin, self->monsterinfo.last_sighting);
                   1029:                        self->monsterinfo.trail_time = marker->timestamp;
                   1030:                        self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW];
                   1031: //                     dprint("heading is "); dprint(ftos(self.ideal_yaw)); dprint("\n");
                   1032: 
                   1033: //                     debug_drawline(self.origin, self.last_sighting, 52);
                   1034:                        new = true;
                   1035:                }
                   1036:        }
                   1037: 
                   1038:        VectorSubtract (self->s.origin, self->monsterinfo.last_sighting, v);
                   1039:        d1 = VectorLength(v);
                   1040:        if (d1 <= dist)
                   1041:        {
                   1042:                self->monsterinfo.aiflags |= AI_PURSUE_NEXT;
                   1043:                dist = d1;
                   1044:        }
                   1045: 
                   1046:        VectorCopy (self->monsterinfo.last_sighting, self->goalentity->s.origin);
                   1047: 
                   1048:        if (new)
                   1049:        {
                   1050: //             gi.dprintf("checking for course correction\n");
                   1051: 
                   1052:                tr = gi.trace(self->s.origin, self->mins, self->maxs, self->monsterinfo.last_sighting, self, MASK_PLAYERSOLID);
                   1053:                if (tr.fraction < 1)
                   1054:                {
                   1055:                        VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
                   1056:                        d1 = VectorLength(v);
                   1057:                        center = tr.fraction;
                   1058:                        d2 = d1 * ((center+1)/2);
                   1059:                        self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
                   1060:                        AngleVectors(self->s.angles, v_forward, v_right, NULL);
                   1061: 
                   1062:                        VectorSet(v, d2, -16, 0);
                   1063:                        G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
                   1064:                        tr = gi.trace(self->s.origin, self->mins, self->maxs, left_target, self, MASK_PLAYERSOLID);
                   1065:                        left = tr.fraction;
                   1066: 
                   1067:                        VectorSet(v, d2, 16, 0);
                   1068:                        G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
                   1069:                        tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, self, MASK_PLAYERSOLID);
                   1070:                        right = tr.fraction;
                   1071: 
                   1072:                        center = (d1*center)/d2;
                   1073:                        if (left >= center && left > right)
                   1074:                        {
                   1075:                                if (left < 1)
                   1076:                                {
                   1077:                                        VectorSet(v, d2 * left * 0.5, -16, 0);
                   1078:                                        G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
                   1079: //                                     gi.dprintf("incomplete path, go part way and adjust again\n");
                   1080:                                }
                   1081:                                VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
                   1082:                                self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
                   1083:                                VectorCopy (left_target, self->goalentity->s.origin);
                   1084:                                VectorCopy (left_target, self->monsterinfo.last_sighting);
                   1085:                                VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
                   1086:                                self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
                   1087: //                             gi.dprintf("adjusted left\n");
                   1088: //                             debug_drawline(self.origin, self.last_sighting, 152);
                   1089:                        }
                   1090:                        else if (right >= center && right > left)
                   1091:                        {
                   1092:                                if (right < 1)
                   1093:                                {
                   1094:                                        VectorSet(v, d2 * right * 0.5, 16, 0);
                   1095:                                        G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
                   1096: //                                     gi.dprintf("incomplete path, go part way and adjust again\n");
                   1097:                                }
                   1098:                                VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
                   1099:                                self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
                   1100:                                VectorCopy (right_target, self->goalentity->s.origin);
                   1101:                                VectorCopy (right_target, self->monsterinfo.last_sighting);
                   1102:                                VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
                   1103:                                self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
                   1104: //                             gi.dprintf("adjusted right\n");
                   1105: //                             debug_drawline(self.origin, self.last_sighting, 152);
                   1106:                        }
                   1107:                }
                   1108: //             else gi.dprintf("course was fine\n");
                   1109:        }
                   1110: 
                   1111:        M_MoveToGoal (self, dist);
                   1112: 
                   1113:        G_FreeEdict(tempgoal);
                   1114: 
                   1115:        if (self)
                   1116:                self->goalentity = save;
                   1117: }

unix.superglobalmegacorp.com

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