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

unix.superglobalmegacorp.com

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