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

1.1     ! root        1: // g_ai.c
        !             2: 
        !             3: #include "g_local.h"
        !             4: 
        !             5: qboolean FindTarget (edict_t *self);
        !             6: extern cvar_t  *maxclients;
        !             7: 
        !             8: qboolean ai_checkattack (edict_t *self, float dist);
        !             9: 
        !            10: qboolean       enemy_vis;
        !            11: qboolean       enemy_infront;
        !            12: int                    enemy_range;
        !            13: float          enemy_yaw;
        !            14: 
        !            15: //============================================================================
        !            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.