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

1.1     ! root        1: /*
        !             2: ==============================================================================
        !             3: 
        !             4: MEDIC
        !             5: 
        !             6: ==============================================================================
        !             7: */
        !             8: 
        !             9: #include "g_local.h"
        !            10: #include "m_medic.h"
        !            11: 
        !            12: #define        MEDIC_MIN_DISTANCE      32
        !            13: #define MEDIC_MAX_HEAL_DISTANCE        400
        !            14: 
        !            15: // FIXME -
        !            16: //
        !            17: // owner moved to monsterinfo.healer instead
        !            18: //
        !            19: // For some reason, the healed monsters are rarely ending up in the floor
        !            20: //
        !            21: // 5/15/1998 I think I fixed these, keep an eye on them
        !            22: 
        !            23: qboolean visible (edict_t *self, edict_t *other);
        !            24: void M_SetEffects (edict_t *ent);
        !            25: qboolean FindTarget (edict_t *self);
        !            26: void HuntTarget (edict_t *self);
        !            27: void FoundTarget (edict_t *self);
        !            28: char *ED_NewString (char *string);
        !            29: void spawngrow_think (edict_t *self);
        !            30: void SpawnGrow_Spawn (vec3_t startpos, int size);
        !            31: void ED_CallSpawn (edict_t *ent);
        !            32: void M_FliesOff (edict_t *self);
        !            33: void M_FliesOn (edict_t *self);
        !            34: 
        !            35: 
        !            36: static int     sound_idle1;
        !            37: static int     sound_pain1;
        !            38: static int     sound_pain2;
        !            39: static int     sound_die;
        !            40: static int     sound_sight;
        !            41: static int     sound_search;
        !            42: static int     sound_hook_launch;
        !            43: static int     sound_hook_hit;
        !            44: static int     sound_hook_heal;
        !            45: static int     sound_hook_retract;
        !            46: 
        !            47: char * reinforcements[] = {
        !            48:        {"monster_soldier_light"},      // 0
        !            49:        {"monster_soldier"},            // 1
        !            50:        {"monster_soldier_ss"},         // 2
        !            51:        {"monster_infantry"},           // 3
        !            52:        {"monster_gunner"},                     // 4
        !            53: //     {"monster_chick"},                      // 4
        !            54:        {"monster_medic"},                      // 5
        !            55:        {"monster_gladiator"}           // 6
        !            56: };
        !            57: 
        !            58: vec3_t reinforcement_mins[] = {
        !            59:        {-16, -16, -24},
        !            60:        {-16, -16, -24},
        !            61:        {-16, -16, -24},
        !            62:        {-16, -16, -24},
        !            63:        {-16, -16, -24},
        !            64:        {-16, -16, -24},
        !            65:        {-32, -32, -24}
        !            66: };
        !            67: 
        !            68: vec3_t reinforcement_maxs[] = {
        !            69:        {16, 16, 32},
        !            70:        {16, 16, 32},
        !            71:        {16, 16, 32},
        !            72:        {16, 16, 32},
        !            73:        {16, 16, 32},
        !            74:        {16, 16, 32},
        !            75:        {32, 32, 64}
        !            76: };
        !            77: 
        !            78: vec3_t reinforcement_position[] = {
        !            79:        {80, 0, 0},
        !            80:        {40, 60, 0},
        !            81:        {40, -60, 0},
        !            82:        {0, 80, 0},
        !            83:        {0, -80, 0}
        !            84: };
        !            85: 
        !            86: void cleanupHeal (edict_t *self, qboolean change_frame)
        !            87: {
        !            88:        // clean up target
        !            89:        self->enemy->monsterinfo.healer = NULL;
        !            90:        self->enemy->monsterinfo.aiflags &= ~AI_RESURRECTING;
        !            91:        self->enemy->takedamage = DAMAGE_YES;
        !            92:        M_SetEffects (self->enemy);
        !            93: 
        !            94:        if (change_frame)
        !            95:                self->monsterinfo.nextframe = FRAME_attack52;
        !            96: }
        !            97: 
        !            98: void abortHeal (edict_t *self, qboolean change_frame, qboolean gib, qboolean mark)
        !            99: {
        !           100:        int hurt;
        !           101:        static vec3_t   pain_normal = { 0, 0, 1 };
        !           102: 
        !           103:        // clean up target
        !           104:        cleanupHeal (self, change_frame);
        !           105:        // gib em!
        !           106:        if (mark)
        !           107:        {
        !           108:                if ((g_showlogic) && (g_showlogic->value))
        !           109:                        gi.dprintf ("%s - marking target as bad\n", self->classname);
        !           110:                // if the first badMedic slot is filled by a medic, skip it and use the second one
        !           111:                if ((self->enemy->monsterinfo.badMedic1) && (self->enemy->monsterinfo.badMedic1->inuse)
        !           112:                        && (!strncmp(self->enemy->monsterinfo.badMedic1->classname, "monster_medic", 13)) )
        !           113:                {
        !           114:                        self->enemy->monsterinfo.badMedic2 = self;
        !           115:                }
        !           116:                else
        !           117:                {
        !           118:                        self->enemy->monsterinfo.badMedic1 = self;
        !           119:                }
        !           120:        }
        !           121:        if (gib)
        !           122:        {
        !           123:                if ((g_showlogic) && (g_showlogic->value))
        !           124:                        gi.dprintf ("%s - gibbing bad heal target", self->classname);
        !           125: 
        !           126:                if(self->enemy->gib_health)
        !           127:                        hurt = - self->enemy->gib_health;
        !           128:                else
        !           129:                        hurt = 500;
        !           130: 
        !           131:                T_Damage (self->enemy, self, self, vec3_origin, self->enemy->s.origin,
        !           132:                                        pain_normal, hurt, 0, 0, MOD_UNKNOWN);
        !           133:        }
        !           134:        // clean up self
        !           135: 
        !           136:        self->monsterinfo.aiflags &= ~AI_MEDIC;
        !           137:        if ((self->oldenemy) && (self->oldenemy->inuse))
        !           138:                self->enemy = self->oldenemy;
        !           139:        else
        !           140:                self->enemy = NULL;
        !           141: 
        !           142:        self->monsterinfo.medicTries = 0;
        !           143: }
        !           144: 
        !           145: qboolean canReach (edict_t *self, edict_t *other)
        !           146: {
        !           147:        vec3_t  spot1;
        !           148:        vec3_t  spot2;
        !           149:        trace_t trace;
        !           150: 
        !           151:        VectorCopy (self->s.origin, spot1);
        !           152:        spot1[2] += self->viewheight;
        !           153:        VectorCopy (other->s.origin, spot2);
        !           154:        spot2[2] += other->viewheight;
        !           155:        trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_SHOT|MASK_WATER);
        !           156:        
        !           157:        if (trace.fraction == 1.0 || trace.ent == other)                // PGM
        !           158:                return true;
        !           159:        return false;
        !           160: }
        !           161: 
        !           162: edict_t *medic_FindDeadMonster (edict_t *self)
        !           163: {
        !           164:        float   radius;
        !           165:        edict_t *ent = NULL;
        !           166:        edict_t *best = NULL;
        !           167: 
        !           168:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
        !           169:                radius = MEDIC_MAX_HEAL_DISTANCE;
        !           170:        else
        !           171:                radius = 1024;
        !           172: 
        !           173:        while ((ent = findradius(ent, self->s.origin, radius)) != NULL)
        !           174:        {
        !           175:                if (ent == self)
        !           176:                        continue;
        !           177:                if (!(ent->svflags & SVF_MONSTER))
        !           178:                        continue;
        !           179:                if (ent->monsterinfo.aiflags & AI_GOOD_GUY)
        !           180:                        continue;
        !           181:                // check to make sure we haven't bailed on this guy already
        !           182:                if ((ent->monsterinfo.badMedic1 == self) || (ent->monsterinfo.badMedic2 == self))
        !           183:                        continue;
        !           184:                if (ent->monsterinfo.healer)
        !           185:                        // FIXME - this is correcting a bug that is somewhere else
        !           186:                        // if the healer is a monster, and it's in medic mode .. continue .. otherwise
        !           187:                        //   we will override the healer, if it passes all the other tests
        !           188:                        if ((ent->monsterinfo.healer->inuse) && (ent->monsterinfo.healer->health > 0) &&
        !           189:                                (ent->monsterinfo.healer->svflags & SVF_MONSTER) && (ent->monsterinfo.healer->monsterinfo.aiflags & AI_MEDIC))
        !           190:                                continue;
        !           191:                if (ent->health > 0)
        !           192:                        continue;
        !           193:                if ((ent->nextthink) && !((ent->think == M_FliesOn) || (ent->think == M_FliesOff)))
        !           194:                        continue;
        !           195:                if (!visible(self, ent))
        !           196: //             if (!canReach(self, ent))
        !           197:                        continue;
        !           198:                if (!strncmp(ent->classname, "player", 6))               // stop it from trying to heal player_noise entities
        !           199:                        continue;
        !           200:                // FIXME - there's got to be a better way ..
        !           201:                // make sure we don't spawn people right on top of us
        !           202:                if (realrange(self, ent) <= MEDIC_MIN_DISTANCE)
        !           203:                        continue;
        !           204:                if (!best)
        !           205:                {
        !           206:                        best = ent;
        !           207:                        continue;
        !           208:                }
        !           209:                if (ent->max_health <= best->max_health)
        !           210:                        continue;
        !           211:                best = ent;
        !           212:        }
        !           213: 
        !           214:        return best;
        !           215: }
        !           216: 
        !           217: void medic_idle (edict_t *self)
        !           218: {
        !           219:        edict_t *ent;
        !           220: 
        !           221:        gi.sound (self, CHAN_VOICE, sound_idle1, 1, ATTN_IDLE, 0);
        !           222: 
        !           223:        if (!self->oldenemy)
        !           224:        {
        !           225:                ent = medic_FindDeadMonster(self);
        !           226:                if (ent)
        !           227:                {
        !           228:                        self->oldenemy = self->enemy;
        !           229:                        self->enemy = ent;
        !           230:                        self->enemy->monsterinfo.healer = self;
        !           231:                        self->monsterinfo.aiflags |= AI_MEDIC;
        !           232:                        FoundTarget (self);
        !           233:                }
        !           234:        }
        !           235: }
        !           236: 
        !           237: void medic_search (edict_t *self)
        !           238: {
        !           239:        edict_t *ent;
        !           240: 
        !           241:        gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_IDLE, 0);
        !           242: 
        !           243:        if (!self->oldenemy)
        !           244:        {
        !           245:                ent = medic_FindDeadMonster(self);
        !           246:                if (ent)
        !           247:                {
        !           248:                        self->oldenemy = self->enemy;
        !           249:                        self->enemy = ent;
        !           250:                        self->enemy->monsterinfo.healer = self;
        !           251:                        self->monsterinfo.aiflags |= AI_MEDIC;
        !           252:                        FoundTarget (self);
        !           253:                }
        !           254:        }
        !           255: }
        !           256: 
        !           257: void medic_sight (edict_t *self, edict_t *other)
        !           258: {
        !           259:        gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
        !           260: }
        !           261: 
        !           262: 
        !           263: mframe_t medic_frames_stand [] =
        !           264: {
        !           265:        ai_stand, 0, medic_idle,
        !           266:        ai_stand, 0, NULL,
        !           267:        ai_stand, 0, NULL,
        !           268:        ai_stand, 0, NULL,
        !           269:        ai_stand, 0, NULL,
        !           270:        ai_stand, 0, NULL,
        !           271:        ai_stand, 0, NULL,
        !           272:        ai_stand, 0, NULL,
        !           273:        ai_stand, 0, NULL,
        !           274:        ai_stand, 0, NULL,
        !           275:        ai_stand, 0, NULL,
        !           276:        ai_stand, 0, NULL,
        !           277:        ai_stand, 0, NULL,
        !           278:        ai_stand, 0, NULL,
        !           279:        ai_stand, 0, NULL,
        !           280:        ai_stand, 0, NULL,
        !           281:        ai_stand, 0, NULL,
        !           282:        ai_stand, 0, NULL,
        !           283:        ai_stand, 0, NULL,
        !           284:        ai_stand, 0, NULL,
        !           285:        ai_stand, 0, NULL,
        !           286:        ai_stand, 0, NULL,
        !           287:        ai_stand, 0, NULL,
        !           288:        ai_stand, 0, NULL,
        !           289:        ai_stand, 0, NULL,
        !           290:        ai_stand, 0, NULL,
        !           291:        ai_stand, 0, NULL,
        !           292:        ai_stand, 0, NULL,
        !           293:        ai_stand, 0, NULL,
        !           294:        ai_stand, 0, NULL,
        !           295:        ai_stand, 0, NULL,
        !           296:        ai_stand, 0, NULL,
        !           297:        ai_stand, 0, NULL,
        !           298:        ai_stand, 0, NULL,
        !           299:        ai_stand, 0, NULL,
        !           300:        ai_stand, 0, NULL,
        !           301:        ai_stand, 0, NULL,
        !           302:        ai_stand, 0, NULL,
        !           303:        ai_stand, 0, NULL,
        !           304:        ai_stand, 0, NULL,
        !           305:        ai_stand, 0, NULL,
        !           306:        ai_stand, 0, NULL,
        !           307:        ai_stand, 0, NULL,
        !           308:        ai_stand, 0, NULL,
        !           309:        ai_stand, 0, NULL,
        !           310:        ai_stand, 0, NULL,
        !           311:        ai_stand, 0, NULL,
        !           312:        ai_stand, 0, NULL,
        !           313:        ai_stand, 0, NULL,
        !           314:        ai_stand, 0, NULL,
        !           315:        ai_stand, 0, NULL,
        !           316:        ai_stand, 0, NULL,
        !           317:        ai_stand, 0, NULL,
        !           318:        ai_stand, 0, NULL,
        !           319:        ai_stand, 0, NULL,
        !           320:        ai_stand, 0, NULL,
        !           321:        ai_stand, 0, NULL,
        !           322:        ai_stand, 0, NULL,
        !           323:        ai_stand, 0, NULL,
        !           324:        ai_stand, 0, NULL,
        !           325:        ai_stand, 0, NULL,
        !           326:        ai_stand, 0, NULL,
        !           327:        ai_stand, 0, NULL,
        !           328:        ai_stand, 0, NULL,
        !           329:        ai_stand, 0, NULL,
        !           330:        ai_stand, 0, NULL,
        !           331:        ai_stand, 0, NULL,
        !           332:        ai_stand, 0, NULL,
        !           333:        ai_stand, 0, NULL,
        !           334:        ai_stand, 0, NULL,
        !           335:        ai_stand, 0, NULL,
        !           336:        ai_stand, 0, NULL,
        !           337:        ai_stand, 0, NULL,
        !           338:        ai_stand, 0, NULL,
        !           339:        ai_stand, 0, NULL,
        !           340:        ai_stand, 0, NULL,
        !           341:        ai_stand, 0, NULL,
        !           342:        ai_stand, 0, NULL,
        !           343:        ai_stand, 0, NULL,
        !           344:        ai_stand, 0, NULL,
        !           345:        ai_stand, 0, NULL,
        !           346:        ai_stand, 0, NULL,
        !           347:        ai_stand, 0, NULL,
        !           348:        ai_stand, 0, NULL,
        !           349:        ai_stand, 0, NULL,
        !           350:        ai_stand, 0, NULL,
        !           351:        ai_stand, 0, NULL,
        !           352:        ai_stand, 0, NULL,
        !           353:        ai_stand, 0, NULL,
        !           354:        ai_stand, 0, NULL,
        !           355: 
        !           356: };
        !           357: mmove_t medic_move_stand = {FRAME_wait1, FRAME_wait90, medic_frames_stand, NULL};
        !           358: 
        !           359: void medic_stand (edict_t *self)
        !           360: {
        !           361:        self->monsterinfo.currentmove = &medic_move_stand;
        !           362: }
        !           363: 
        !           364: 
        !           365: mframe_t medic_frames_walk [] =
        !           366: {
        !           367:        ai_walk, 6.2,   NULL,
        !           368:        ai_walk, 18.1,  NULL,
        !           369:        ai_walk, 1,             NULL,
        !           370:        ai_walk, 9,             NULL,
        !           371:        ai_walk, 10,    NULL,
        !           372:        ai_walk, 9,             NULL,
        !           373:        ai_walk, 11,    NULL,
        !           374:        ai_walk, 11.6,  NULL,
        !           375:        ai_walk, 2,             NULL,
        !           376:        ai_walk, 9.9,   NULL,
        !           377:        ai_walk, 14,    NULL,
        !           378:        ai_walk, 9.3,   NULL
        !           379: };
        !           380: mmove_t medic_move_walk = {FRAME_walk1, FRAME_walk12, medic_frames_walk, NULL};
        !           381: 
        !           382: void medic_walk (edict_t *self)
        !           383: {
        !           384:        self->monsterinfo.currentmove = &medic_move_walk;
        !           385: }
        !           386: 
        !           387: 
        !           388: mframe_t medic_frames_run [] =
        !           389: {
        !           390:        ai_run, 18,             NULL,
        !           391:        ai_run, 22.5,   NULL,
        !           392:        ai_run, 25.4,   monster_done_dodge,
        !           393:        ai_run, 23.4,   NULL,
        !           394:        ai_run, 24,             NULL,
        !           395:        ai_run, 35.6,   NULL            //pmm
        !           396:        
        !           397: };
        !           398: mmove_t medic_move_run = {FRAME_run1, FRAME_run6, medic_frames_run, NULL};
        !           399: 
        !           400: void medic_run (edict_t *self)
        !           401: {
        !           402:        monster_done_dodge (self);
        !           403:        if (!(self->monsterinfo.aiflags & AI_MEDIC))
        !           404:        {
        !           405:                edict_t *ent;
        !           406: 
        !           407:                ent = medic_FindDeadMonster(self);
        !           408:                if (ent)
        !           409:                {
        !           410:                        self->oldenemy = self->enemy;
        !           411:                        self->enemy = ent;
        !           412:                        self->enemy->monsterinfo.healer = self;
        !           413:                        self->monsterinfo.aiflags |= AI_MEDIC;
        !           414:                        FoundTarget (self);
        !           415:                        return;
        !           416:                }
        !           417:        }
        !           418: //     else if (!canReach(self, self->enemy))
        !           419: //     {
        !           420: //             abortHeal (self, 0);
        !           421: //     }
        !           422: 
        !           423:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
        !           424:                self->monsterinfo.currentmove = &medic_move_stand;
        !           425:        else
        !           426:                self->monsterinfo.currentmove = &medic_move_run;
        !           427: }
        !           428: 
        !           429: 
        !           430: mframe_t medic_frames_pain1 [] =
        !           431: {
        !           432:        ai_move, 0, NULL,
        !           433:        ai_move, 0, NULL,
        !           434:        ai_move, 0, NULL,
        !           435:        ai_move, 0, NULL,
        !           436:        ai_move, 0, NULL,
        !           437:        ai_move, 0, NULL,
        !           438:        ai_move, 0, NULL,
        !           439:        ai_move, 0, NULL
        !           440: };
        !           441: mmove_t medic_move_pain1 = {FRAME_paina1, FRAME_paina8, medic_frames_pain1, medic_run};
        !           442: 
        !           443: mframe_t medic_frames_pain2 [] =
        !           444: {
        !           445:        ai_move, 0, NULL,
        !           446:        ai_move, 0, NULL,
        !           447:        ai_move, 0, NULL,
        !           448:        ai_move, 0, NULL,
        !           449:        ai_move, 0, NULL,
        !           450:        ai_move, 0, NULL,
        !           451:        ai_move, 0, NULL,
        !           452:        ai_move, 0, NULL,
        !           453:        ai_move, 0, NULL,
        !           454:        ai_move, 0, NULL,
        !           455:        ai_move, 0, NULL,
        !           456:        ai_move, 0, NULL,
        !           457:        ai_move, 0, NULL,
        !           458:        ai_move, 0, NULL,
        !           459:        ai_move, 0, NULL
        !           460: };
        !           461: mmove_t medic_move_pain2 = {FRAME_painb1, FRAME_painb15, medic_frames_pain2, medic_run};
        !           462: 
        !           463: void medic_pain (edict_t *self, edict_t *other, float kick, int damage)
        !           464: {
        !           465:        monster_done_dodge (self);
        !           466: 
        !           467:        if ((self->health < (self->max_health / 2)))
        !           468:                if (self->mass > 400)
        !           469:                        self->s.skinnum = 3;
        !           470:                else
        !           471:                        self->s.skinnum = 1;
        !           472: 
        !           473:        if (level.time < self->pain_debounce_time)
        !           474:                return;
        !           475: 
        !           476:        self->pain_debounce_time = level.time + 3;
        !           477: 
        !           478:        if (skill->value == 3)
        !           479:                return;         // no pain anims in nightmare
        !           480: 
        !           481:        // if we're healing someone, we ignore pain
        !           482:        if (self->monsterinfo.aiflags & AI_MEDIC)
        !           483:                return;
        !           484: 
        !           485:        if (self->mass > 400)
        !           486:        {
        !           487:                if (damage < 35)
        !           488:                {
        !           489:                        gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
        !           490:                        return;
        !           491:                }
        !           492: 
        !           493:                self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
        !           494:                self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
        !           495: 
        !           496:                if (random() < (min(((float)damage * 0.005), 0.5)))             // no more than 50% chance of big pain
        !           497:                        self->monsterinfo.currentmove = &medic_move_pain2;
        !           498:                else
        !           499:                        self->monsterinfo.currentmove = &medic_move_pain1;
        !           500:        }
        !           501:        else if (random() < 0.5)
        !           502:        {
        !           503:                self->monsterinfo.currentmove = &medic_move_pain1;
        !           504:                gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
        !           505:        }
        !           506:        else
        !           507:        {
        !           508:                self->monsterinfo.currentmove = &medic_move_pain2;
        !           509:                gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
        !           510:        }
        !           511:        // PMM - clear duck flag
        !           512:        if (self->monsterinfo.aiflags & AI_DUCKED)
        !           513:                monster_duck_up(self);
        !           514: }
        !           515: 
        !           516: void medic_fire_blaster (edict_t *self)
        !           517: {
        !           518:        vec3_t  start;
        !           519:        vec3_t  forward, right;
        !           520:        vec3_t  end;
        !           521:        vec3_t  dir;
        !           522:        int             effect;
        !           523: 
        !           524:        if ((self->s.frame == FRAME_attack9) || (self->s.frame == FRAME_attack12))
        !           525:                effect = EF_BLASTER;
        !           526:        else if ((self->s.frame == FRAME_attack19) || (self->s.frame == FRAME_attack22) || (self->s.frame == FRAME_attack25) || (self->s.frame == FRAME_attack28))
        !           527:                effect = EF_HYPERBLASTER;
        !           528:        else
        !           529:                effect = 0;
        !           530: 
        !           531:        AngleVectors (self->s.angles, forward, right, NULL);
        !           532:        G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_MEDIC_BLASTER_1], forward, right, start);
        !           533: 
        !           534:        VectorCopy (self->enemy->s.origin, end);
        !           535:        end[2] += self->enemy->viewheight;
        !           536:        VectorSubtract (end, start, dir);
        !           537: 
        !           538:        // medic commander shoots blaster2
        !           539:        if (self->mass > 400)
        !           540:                monster_fire_blaster2 (self, start, dir, 2, 1000, MZ2_MEDIC_BLASTER_2, effect);
        !           541:        else
        !           542:                monster_fire_blaster (self, start, dir, 2, 1000, MZ2_MEDIC_BLASTER_1, effect);
        !           543: }
        !           544: 
        !           545: void medic_dead (edict_t *self)
        !           546: {
        !           547:        VectorSet (self->mins, -16, -16, -24);
        !           548:        VectorSet (self->maxs, 16, 16, -8);
        !           549:        self->movetype = MOVETYPE_TOSS;
        !           550:        self->svflags |= SVF_DEADMONSTER;
        !           551:        self->nextthink = 0;
        !           552:        gi.linkentity (self);
        !           553: }
        !           554: 
        !           555: mframe_t medic_frames_death [] =
        !           556: {
        !           557:        ai_move, 0, NULL,
        !           558:        ai_move, 0, NULL,
        !           559:        ai_move, 0, NULL,
        !           560:        ai_move, 0, NULL,
        !           561:        ai_move, 0, NULL,
        !           562:        ai_move, 0, NULL,
        !           563:        ai_move, 0, NULL,
        !           564:        ai_move, 0, NULL,
        !           565:        ai_move, 0, NULL,
        !           566:        ai_move, 0, NULL,
        !           567:        ai_move, 0, NULL,
        !           568:        ai_move, 0, NULL,
        !           569:        ai_move, 0, NULL,
        !           570:        ai_move, 0, NULL,
        !           571:        ai_move, 0, NULL,
        !           572:        ai_move, 0, NULL,
        !           573:        ai_move, 0, NULL,
        !           574:        ai_move, 0, NULL,
        !           575:        ai_move, 0, NULL,
        !           576:        ai_move, 0, NULL,
        !           577:        ai_move, 0, NULL,
        !           578:        ai_move, 0, NULL,
        !           579:        ai_move, 0, NULL,
        !           580:        ai_move, 0, NULL,
        !           581:        ai_move, 0, NULL,
        !           582:        ai_move, 0, NULL,
        !           583:        ai_move, 0, NULL,
        !           584:        ai_move, 0, NULL,
        !           585:        ai_move, 0, NULL,
        !           586:        ai_move, 0, NULL
        !           587: };
        !           588: mmove_t medic_move_death = {FRAME_death1, FRAME_death30, medic_frames_death, medic_dead};
        !           589: 
        !           590: void medic_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
        !           591: {
        !           592:        int             n;
        !           593: 
        !           594:        // if we had a pending patient, he was already freed up in Killed
        !           595: 
        !           596: // check for gib
        !           597:        if (self->health <= self->gib_health)
        !           598:        {
        !           599:                gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
        !           600:                for (n= 0; n < 2; n++)
        !           601:                        ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
        !           602:                for (n= 0; n < 4; n++)
        !           603:                        ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
        !           604:                ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
        !           605:                self->deadflag = DEAD_DEAD;
        !           606:                return;
        !           607:        }
        !           608: 
        !           609:        if (self->deadflag == DEAD_DEAD)
        !           610:                return;
        !           611: 
        !           612: // regular death
        !           613:        gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
        !           614:        self->deadflag = DEAD_DEAD;
        !           615:        self->takedamage = DAMAGE_YES;
        !           616: 
        !           617:        self->monsterinfo.currentmove = &medic_move_death;
        !           618: }
        !           619: 
        !           620: mframe_t medic_frames_duck [] =
        !           621: {
        !           622:        ai_move, -1,    NULL,
        !           623:        ai_move, -1,    NULL,
        !           624:        ai_move, -1,    monster_duck_down,
        !           625:        ai_move, -1,    monster_duck_hold,
        !           626:        ai_move, -1,    NULL,
        !           627:        ai_move, -1,    NULL,
        !           628:        ai_move, -1,    NULL,           // PMM - duck up used to be here
        !           629:        ai_move, -1,    NULL,
        !           630:        ai_move, -1,    NULL,
        !           631:        ai_move, -1,    NULL,
        !           632:        ai_move, -1,    NULL,
        !           633:        ai_move, -1,    NULL,
        !           634:        ai_move, -1,    NULL,
        !           635:        ai_move, -1,    monster_duck_up,
        !           636:        ai_move, -1,    NULL,
        !           637:        ai_move, -1,    NULL
        !           638: };
        !           639: mmove_t medic_move_duck = {FRAME_duck1, FRAME_duck16, medic_frames_duck, medic_run};
        !           640: 
        !           641: // PMM -- moved dodge code to after attack code so I can reference attack frames
        !           642: 
        !           643: mframe_t medic_frames_attackHyperBlaster [] =
        !           644: {
        !           645:        ai_charge, 0,   NULL,
        !           646:        ai_charge, 0,   NULL,
        !           647:        ai_charge, 0,   NULL,
        !           648:        ai_charge, 0,   NULL,
        !           649:        ai_charge, 0,   medic_fire_blaster,
        !           650:        ai_charge, 0,   medic_fire_blaster,
        !           651:        ai_charge, 0,   medic_fire_blaster,
        !           652:        ai_charge, 0,   medic_fire_blaster,
        !           653:        ai_charge, 0,   medic_fire_blaster,
        !           654:        ai_charge, 0,   medic_fire_blaster,
        !           655:        ai_charge, 0,   medic_fire_blaster,
        !           656:        ai_charge, 0,   medic_fire_blaster,
        !           657:        ai_charge, 0,   medic_fire_blaster,
        !           658:        ai_charge, 0,   medic_fire_blaster,
        !           659:        ai_charge, 0,   medic_fire_blaster,
        !           660:        ai_charge, 0,   medic_fire_blaster
        !           661: };
        !           662: mmove_t medic_move_attackHyperBlaster = {FRAME_attack15, FRAME_attack30, medic_frames_attackHyperBlaster, medic_run};
        !           663: 
        !           664: 
        !           665: void medic_continue (edict_t *self)
        !           666: {
        !           667:        if (visible (self, self->enemy) )
        !           668:                if (random() <= 0.95)
        !           669:                        self->monsterinfo.currentmove = &medic_move_attackHyperBlaster;
        !           670: }
        !           671: 
        !           672: 
        !           673: mframe_t medic_frames_attackBlaster [] =
        !           674: {
        !           675:        ai_charge, 0,   NULL,
        !           676:        ai_charge, 5,   NULL,
        !           677:        ai_charge, 5,   NULL,
        !           678:        ai_charge, 3,   NULL,
        !           679:        ai_charge, 2,   NULL,
        !           680:        ai_charge, 0,   NULL,
        !           681:        ai_charge, 0,   NULL,
        !           682:        ai_charge, 0,   NULL,
        !           683:        ai_charge, 0,   medic_fire_blaster,
        !           684:        ai_charge, 0,   NULL,
        !           685:        ai_charge, 0,   NULL,
        !           686:        ai_charge, 0,   medic_fire_blaster,     
        !           687:        ai_charge, 0,   NULL,
        !           688:        ai_charge, 0,   medic_continue  // Change to medic_continue... Else, go to frame 32
        !           689: };
        !           690: mmove_t medic_move_attackBlaster = {FRAME_attack1, FRAME_attack14, medic_frames_attackBlaster, medic_run};
        !           691: 
        !           692: 
        !           693: void medic_hook_launch (edict_t *self)
        !           694: {
        !           695:        gi.sound (self, CHAN_WEAPON, sound_hook_launch, 1, ATTN_NORM, 0);
        !           696: }
        !           697: 
        !           698: static vec3_t  medic_cable_offsets[] =
        !           699: {
        !           700:        45.0,  -9.2, 15.5,
        !           701:        48.4,  -9.7, 15.2,
        !           702:        47.8,  -9.8, 15.8,
        !           703:        47.3,  -9.3, 14.3,
        !           704:        45.4, -10.1, 13.1,
        !           705:        41.9, -12.7, 12.0,
        !           706:        37.8, -15.8, 11.2,
        !           707:        34.3, -18.4, 10.7,
        !           708:        32.7, -19.7, 10.4,
        !           709:        32.7, -19.7, 10.4
        !           710: };
        !           711: 
        !           712: void medic_cable_attack (edict_t *self)
        !           713: {
        !           714:        vec3_t  offset, start, end, f, r;
        !           715:        trace_t tr;
        !           716:        vec3_t  dir;
        !           717: //     vec3_t  angles;
        !           718:        float   distance;
        !           719: 
        !           720:        if ((!self->enemy->inuse) || (self->enemy->s.effects & EF_GIB))
        !           721:        {
        !           722:                if ((g_showlogic) && (g_showlogic->value))
        !           723:                        gi.dprintf ("medic_commander - aborting heal due to target's disappearance\n");
        !           724:                abortHeal (self, true, false, false);
        !           725:                return;
        !           726:        }
        !           727: 
        !           728:        // see if our enemy has changed to a client, or our target has more than 0 health,
        !           729:        // abort it .. we got switched to someone else due to damage
        !           730:        if ((self->enemy->client) || (self->enemy->health > 0))
        !           731:        {
        !           732:                abortHeal (self, true, false, false);
        !           733:                return;
        !           734:        }
        !           735:        AngleVectors (self->s.angles, f, r, NULL);
        !           736:        VectorCopy (medic_cable_offsets[self->s.frame - FRAME_attack42], offset);
        !           737:        G_ProjectSource (self->s.origin, offset, f, r, start);
        !           738: 
        !           739:        // check for max distance
        !           740:        // not needed, done in checkattack
        !           741:        // check for min distance
        !           742:        VectorSubtract (start, self->enemy->s.origin, dir);
        !           743:        distance = VectorLength(dir);
        !           744:        if (distance < MEDIC_MIN_DISTANCE)
        !           745:        {
        !           746:                if ((g_showlogic) && (g_showlogic->value))
        !           747:                        gi.dprintf ("medic_commander - aborting heal due to proximity to target ");
        !           748:                abortHeal (self, true, true, false );
        !           749:                return;
        !           750:        }
        !           751: //     if ((g_showlogic)&&(g_showlogic->value))
        !           752: //             gi.dprintf ("distance to target is %f\n", distance);
        !           753: //     if (distance > 300)
        !           754: //             return;
        !           755: 
        !           756:        // check for min/max pitch
        !           757:        // PMM -- took out since it doesn't look bad when it fails
        !           758: //     vectoangles (dir, angles);
        !           759: //     if (angles[0] < -180)
        !           760: //             angles[0] += 360;
        !           761: //     if (fabs(angles[0]) > 45)
        !           762: //     {
        !           763: //             if ((g_showlogic) && (g_showlogic->value))
        !           764: //                     gi.dprintf ("medic_commander - aborting heal due to bad angle\n");
        !           765: //             abortHeal(self);
        !           766: //             return;
        !           767: //     }
        !           768: 
        !           769:        tr = gi.trace (start, NULL, NULL, self->enemy->s.origin, self, MASK_SOLID);
        !           770:        if (tr.fraction != 1.0 && tr.ent != self->enemy)
        !           771:        {
        !           772:                if (tr.ent == world)
        !           773:                {
        !           774:                        // give up on second try
        !           775:                        if (self->monsterinfo.medicTries > 1)
        !           776:                        {
        !           777:                                abortHeal (self, true, false, true);
        !           778:                                return;
        !           779:                        }
        !           780:                        self->monsterinfo.medicTries++;
        !           781:                        cleanupHeal (self, 1);
        !           782:                        return;
        !           783:                }
        !           784:                if ((g_showlogic) && (g_showlogic->value))
        !           785:                        gi.dprintf ("medic_commander - aborting heal due to beamus interruptus\n");
        !           786:                abortHeal (self, true, false, false);
        !           787:                return;
        !           788:        }
        !           789: 
        !           790:        if (self->s.frame == FRAME_attack43)
        !           791:        {
        !           792:                gi.sound (self->enemy, CHAN_AUTO, sound_hook_hit, 1, ATTN_NORM, 0);
        !           793:                self->enemy->monsterinfo.aiflags |= AI_RESURRECTING;
        !           794:                self->enemy->takedamage = DAMAGE_NO;
        !           795:                M_SetEffects (self->enemy);
        !           796:        }
        !           797:        else if (self->s.frame == FRAME_attack50)
        !           798:        {
        !           799:                vec3_t  maxs;
        !           800:                self->enemy->spawnflags = 0;
        !           801:                self->enemy->monsterinfo.aiflags = 0;
        !           802:                self->enemy->target = NULL;
        !           803:                self->enemy->targetname = NULL;
        !           804:                self->enemy->combattarget = NULL;
        !           805:                self->enemy->deathtarget = NULL;
        !           806:                self->enemy->monsterinfo.healer = self;
        !           807: 
        !           808:                VectorCopy (self->enemy->maxs, maxs);
        !           809:                maxs[2] += 48;   // compensate for change when they die
        !           810: 
        !           811:                tr = gi.trace (self->enemy->s.origin, self->enemy->mins, maxs, self->enemy->s.origin, self->enemy, MASK_MONSTERSOLID);
        !           812:                if (tr.startsolid || tr.allsolid)
        !           813:                {
        !           814:                        if ((g_showlogic) && (g_showlogic->value))
        !           815:                                gi.dprintf ("Spawn point obstructed, aborting heal!\n");
        !           816:                        abortHeal (self, true, true, false);
        !           817:                        return;
        !           818:                } 
        !           819:                else if (tr.ent != world)
        !           820:                {
        !           821:                        if ((g_showlogic) && (g_showlogic->value))
        !           822:                                gi.dprintf("heal in entity %s\n", tr.ent->classname);
        !           823:                        abortHeal (self, true, true, false);
        !           824:                        return;
        !           825:                }
        !           826: /*             else if (tr.ent == world)
        !           827:                {
        !           828:                        if ((g_showlogic) && (g_showlogic->value))
        !           829:                                gi.dprintf ("heal in world, aborting!\n");
        !           830:                        abortHeal (self, 1);
        !           831:                        return;
        !           832:                }
        !           833: */             else
        !           834:                {
        !           835:                        self->enemy->monsterinfo.aiflags |= AI_DO_NOT_COUNT;
        !           836:                        ED_CallSpawn (self->enemy);
        !           837: 
        !           838:                        if (self->enemy->think)
        !           839:                        {
        !           840:                                self->enemy->nextthink = level.time;
        !           841:                                self->enemy->think (self->enemy);
        !           842:                        }
        !           843:        //              self->enemy->monsterinfo.aiflags |= AI_RESURRECTING;
        !           844:                        self->enemy->monsterinfo.aiflags &= ~AI_RESURRECTING;
        !           845:                        self->enemy->monsterinfo.aiflags |= AI_IGNORE_SHOTS|AI_DO_NOT_COUNT;
        !           846:                        // turn off flies
        !           847:                        self->enemy->s.effects &= ~EF_FLIES;
        !           848:                        if ((self->oldenemy) && (self->oldenemy->inuse) && (self->oldenemy->health > 0))
        !           849:                        {
        !           850:                                if ((g_showlogic) && (g_showlogic->value))
        !           851:                                        gi.dprintf ("setting heal target's enemy to %s\n", self->oldenemy->classname);
        !           852:                                self->enemy->enemy = self->oldenemy;
        !           853: //                             HuntTarget (self->enemy);
        !           854:                                FoundTarget (self->enemy);
        !           855:                        }
        !           856:                        else
        !           857:                        {
        !           858:                                if (g_showlogic && g_showlogic->value)
        !           859:                                        gi.dprintf ("no valid enemy to set!\n");
        !           860:                                if (!FindTarget (self->enemy))
        !           861:                                        self->enemy->monsterinfo.stand (self->enemy);
        !           862:                                if (!FindTarget (self))
        !           863:                                        self->monsterinfo.stand (self);
        !           864:                        }
        !           865:                        self->enemy->monsterinfo.healer = NULL;
        !           866:                }
        !           867:        }
        !           868:        else
        !           869:        {
        !           870:                if (self->s.frame == FRAME_attack44)
        !           871:                        gi.sound (self, CHAN_WEAPON, sound_hook_heal, 1, ATTN_NORM, 0);
        !           872:        }
        !           873: 
        !           874:        // adjust start for beam origin being in middle of a segment
        !           875:        VectorMA (start, 8, f, start);
        !           876: 
        !           877:        // adjust end z for end spot since the monster is currently dead
        !           878:        VectorCopy (self->enemy->s.origin, end);
        !           879:        end[2] = self->enemy->absmin[2] + self->enemy->size[2] / 2;
        !           880: 
        !           881:        gi.WriteByte (svc_temp_entity);
        !           882:        gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
        !           883:        gi.WriteShort (self - g_edicts);
        !           884:        gi.WritePosition (start);
        !           885:        gi.WritePosition (end);
        !           886:        gi.multicast (self->s.origin, MULTICAST_PVS);
        !           887: }
        !           888: 
        !           889: void medic_hook_retract (edict_t *self)
        !           890: {
        !           891:        gi.sound (self, CHAN_WEAPON, sound_hook_retract, 1, ATTN_NORM, 0);
        !           892:        self->monsterinfo.aiflags &= ~AI_MEDIC;
        !           893:        if ((self->oldenemy) && (self->oldenemy->inuse))
        !           894:                self->enemy = self->oldenemy;
        !           895:        else
        !           896:                self->enemy = NULL;
        !           897: }
        !           898: 
        !           899: mframe_t medic_frames_attackCable [] =
        !           900: {
        !           901: // ROGUE - negated 36-40 so he scoots back from his target a little
        !           902: // ROGUE - switched 33-36 to ai_charge
        !           903: // ROGUE - changed frame 52 to 0 to compensate for changes in 36-40
        !           904:        ai_charge, 2,           NULL,                                   //33
        !           905:        ai_charge, 3,           NULL,
        !           906:        ai_charge, 5,           NULL,
        !           907:        ai_charge, -4.4,        NULL,                                   //36
        !           908:        ai_charge, -4.7,        NULL,                                   //37
        !           909:        ai_charge, -5,  NULL,
        !           910:        ai_charge, -6,  NULL,
        !           911:        ai_charge, -4,  NULL,                                   //40
        !           912:        ai_charge, 0,   NULL,
        !           913:        ai_move, 0,             medic_hook_launch,              //42
        !           914:        ai_move, 0,             medic_cable_attack,             //43
        !           915:        ai_move, 0,             medic_cable_attack,
        !           916:        ai_move, 0,             medic_cable_attack,
        !           917:        ai_move, 0,             medic_cable_attack,
        !           918:        ai_move, 0,             medic_cable_attack,
        !           919:        ai_move, 0,             medic_cable_attack,
        !           920:        ai_move, 0,             medic_cable_attack,
        !           921:        ai_move, 0,             medic_cable_attack,
        !           922:        ai_move, 0,             medic_cable_attack,             //51
        !           923:        ai_move, 0,             medic_hook_retract,             //52
        !           924:        ai_move, -1.5,  NULL,
        !           925:        ai_move, -1.2,  NULL,
        !           926:        ai_move, -3,    NULL,
        !           927:        ai_move, -2,    NULL,
        !           928:        ai_move, 0.3,   NULL,
        !           929:        ai_move, 0.7,   NULL,
        !           930:        ai_move, 1.2,   NULL,
        !           931:        ai_move, 1.3,   NULL                                    //60
        !           932: };
        !           933: mmove_t medic_move_attackCable = {FRAME_attack33, FRAME_attack60, medic_frames_attackCable, medic_run};
        !           934: 
        !           935: 
        !           936: void medic_start_spawn (edict_t *self)
        !           937: {
        !           938:        gi.sound (self, CHAN_WEAPON, sound_hook_launch, 1, ATTN_NORM, 0);
        !           939:        self->monsterinfo.nextframe = FRAME_attack48;
        !           940: }
        !           941: 
        !           942: void medic_determine_spawn (edict_t *self)
        !           943: {
        !           944:        vec3_t  f, r, offset, startpoint, spawnpoint;
        !           945:        float   lucky;
        !           946:        int             summonStr;
        !           947:        int             count;
        !           948:        int             inc;
        !           949:        int             num_summoned; // should be 1, 3, or 5
        !           950:        int             num_success = 0;
        !           951: 
        !           952:        lucky = random();
        !           953:        summonStr = skill->value;
        !           954: 
        !           955:        // bell curve - 0 = 67%, 1 = 93%, 2 = 99% -- too steep
        !           956:        // this ends up with
        !           957:        // -3 = 5%
        !           958:        // -2 = 10%
        !           959:        // -1 = 15%
        !           960:        // 0  = 40%
        !           961:        // +1 = 15%
        !           962:        // +2 = 10%
        !           963:        // +3 = 5%
        !           964:        if (lucky < 0.05)
        !           965:                summonStr -= 3;
        !           966:        else if (lucky < 0.15)
        !           967:                summonStr -= 2;
        !           968:        else if (lucky < 0.3)
        !           969:                summonStr -= 1;
        !           970:        else if (lucky > 0.95)
        !           971:                summonStr += 3;
        !           972:        else if (lucky > 0.85)
        !           973:                summonStr += 2;
        !           974:        else if (lucky > 0.7)
        !           975:                summonStr += 1;
        !           976: 
        !           977:        if (summonStr < 0)
        !           978:                summonStr = 0;
        !           979: 
        !           980:        //FIXME - need to remember this, might as well use this int that isn't used for monsters
        !           981:        self->plat2flags = summonStr;
        !           982:        AngleVectors (self->s.angles, f, r, NULL);
        !           983: 
        !           984: // this yields either 1, 3, or 5
        !           985:        if (summonStr)
        !           986:                num_summoned = (summonStr - 1) + (summonStr % 2);
        !           987:        else
        !           988:                num_summoned = 1;
        !           989: 
        !           990: //     if ((g_showlogic) && (g_showlogic->value))
        !           991: //             gi.dprintf ("medic_commander: summonStr = %d num = %d\n", summonStr, num_summoned);
        !           992: 
        !           993:        for (count = 0; count < num_summoned; count++)
        !           994:        {
        !           995:                inc = count + (count%2); // 0, 2, 2, 4, 4
        !           996:                VectorCopy (reinforcement_position[count], offset);
        !           997: 
        !           998:                G_ProjectSource (self->s.origin, offset, f, r, startpoint);
        !           999:                // a little off the ground
        !          1000:                startpoint[2] += 10;
        !          1001: 
        !          1002:                if (FindSpawnPoint (startpoint, reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc], spawnpoint, 32))
        !          1003:                {
        !          1004:                        if (CheckGroundSpawnPoint(spawnpoint, 
        !          1005:                                reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc], 
        !          1006:                                256, -1))
        !          1007:                        {
        !          1008:                                num_success++;
        !          1009:                                // we found a spot, we're done here
        !          1010:                                count = num_summoned;
        !          1011:                        }
        !          1012: //                     else if ((g_showlogic) && (g_showlogic->value))
        !          1013: //                             gi.dprintf ("medic_commander: CheckGroundSpawnPoint says bad stuff down there!\n");
        !          1014:                }
        !          1015:        }
        !          1016: 
        !          1017:        if (num_success == 0)
        !          1018:        {
        !          1019:                for (count = 0; count < num_summoned; count++)
        !          1020:                {
        !          1021:                        inc = count + (count%2); // 0, 2, 2, 4, 4
        !          1022:                        VectorCopy (reinforcement_position[count], offset);
        !          1023:                        
        !          1024:                        // check behind
        !          1025:                        offset[0] *= -1.0;
        !          1026:                        offset[1] *= -1.0;
        !          1027:                        G_ProjectSource (self->s.origin, offset, f, r, startpoint);
        !          1028:                        // a little off the ground
        !          1029:                        startpoint[2] += 10;
        !          1030: 
        !          1031:                        if (FindSpawnPoint (startpoint, reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc], spawnpoint, 32))
        !          1032:                        {
        !          1033:                                if (CheckGroundSpawnPoint(spawnpoint, 
        !          1034:                                        reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc], 
        !          1035:                                        256, -1))
        !          1036:                                {
        !          1037:                                        num_success++;
        !          1038:                                        // we found a spot, we're done here
        !          1039:                                        count = num_summoned;
        !          1040:                                }
        !          1041:                        }
        !          1042:                }
        !          1043: 
        !          1044:                if (num_success)
        !          1045:                {
        !          1046:                        self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
        !          1047:                        self->ideal_yaw = anglemod(self->s.angles[YAW]) + 180;
        !          1048:                        if (self->ideal_yaw > 360.0)
        !          1049:                                self->ideal_yaw -= 360.0;
        !          1050: //                     self->plat2flags *= -1;
        !          1051:                }
        !          1052:        }
        !          1053: 
        !          1054:        if (num_success == 0)
        !          1055:        {
        !          1056:                if ((g_showlogic) && (g_showlogic->value))
        !          1057:                        gi.dprintf ("medic_commander: failed to find any spawn points, aborting!\n");
        !          1058:                self->monsterinfo.nextframe = FRAME_attack53;
        !          1059:        }
        !          1060: }
        !          1061: 
        !          1062: void medic_spawngrows (edict_t *self)
        !          1063: {
        !          1064:        vec3_t  f, r, offset, startpoint, spawnpoint;
        !          1065:        int             summonStr;
        !          1066:        int             count;
        !          1067:        int             inc;
        !          1068:        int             num_summoned; // should be 1, 3, or 5
        !          1069:        int             num_success = 0;
        !          1070:        float   current_yaw;
        !          1071:        qboolean        behind = false;
        !          1072: 
        !          1073:        // if we've been directed to turn around
        !          1074:        if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
        !          1075:        {
        !          1076:                current_yaw = anglemod(self->s.angles[YAW]);
        !          1077:                if (fabs(current_yaw - self->ideal_yaw) > 0.1)
        !          1078:                {
        !          1079:                        self->monsterinfo.aiflags |= AI_HOLD_FRAME;
        !          1080:                        return;
        !          1081:                }
        !          1082: 
        !          1083:                // done turning around
        !          1084:                self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
        !          1085:                self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
        !          1086:        }
        !          1087: 
        !          1088: //     if (self->plat2flags < 0)
        !          1089: //     {
        !          1090: //             summonStr = -1.0 * self->plat2flags;
        !          1091: //             behind = true;
        !          1092: //     }
        !          1093: //     else
        !          1094:                summonStr = self->plat2flags;
        !          1095: 
        !          1096:        AngleVectors (self->s.angles, f, r, NULL);
        !          1097: 
        !          1098: //     num_summoned = ((((summonStr-1)/2)+1)*2)-1;  // this yields either 1, 3, or 5
        !          1099:        if (summonStr)
        !          1100:                num_summoned = (summonStr - 1) + (summonStr % 2);
        !          1101:        else
        !          1102:                num_summoned = 1;
        !          1103: 
        !          1104:        for (count = 0; count < num_summoned; count++)
        !          1105:        {
        !          1106:                inc = count + (count%2); // 0, 2, 2, 4, 4
        !          1107:                VectorCopy (reinforcement_position[count], offset);
        !          1108: 
        !          1109:                G_ProjectSource (self->s.origin, offset, f, r, startpoint);
        !          1110:                // a little off the ground
        !          1111:                startpoint[2] += 10;
        !          1112: 
        !          1113:                if (FindSpawnPoint (startpoint, reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc], spawnpoint, 32))
        !          1114:                {
        !          1115:                        if (CheckGroundSpawnPoint(spawnpoint, 
        !          1116:                                reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc], 
        !          1117:                                256, -1))
        !          1118:                        {
        !          1119:                                num_success++;
        !          1120:                                if ((summonStr-inc) > 3)
        !          1121:                                        SpawnGrow_Spawn (spawnpoint, 1);                // big monster
        !          1122:                                else
        !          1123:                                        SpawnGrow_Spawn (spawnpoint, 0);                // normal size
        !          1124:                        }
        !          1125:                }
        !          1126:        }
        !          1127: 
        !          1128:        if (num_success == 0)
        !          1129:        {
        !          1130:                if ((g_showlogic) && (g_showlogic->value))
        !          1131:                        gi.dprintf ("medic_commander: spawngrows bad, aborting!\n");
        !          1132:                self->monsterinfo.nextframe = FRAME_attack53;
        !          1133:        }
        !          1134: }
        !          1135: 
        !          1136: void medic_finish_spawn (edict_t *self)
        !          1137: {
        !          1138:        edict_t *ent;
        !          1139:        vec3_t  f, r, offset, startpoint, spawnpoint;
        !          1140:        int             summonStr;
        !          1141:        int             count;
        !          1142:        int             inc;
        !          1143:        int             num_summoned; // should be 1, 3, or 5
        !          1144:        qboolean        behind = false;
        !          1145:        edict_t *designated_enemy;
        !          1146: 
        !          1147: //     trace_t         tr;
        !          1148: //     vec3_t mins, maxs;
        !          1149: 
        !          1150:        // this is one bigger than the soldier's real mins .. just for paranoia's sake
        !          1151: //     VectorSet (mins, -17, -17, -25);
        !          1152: //     VectorSet (maxs, 17, 17, 33);
        !          1153: 
        !          1154:        //FIXME - better place to store this info?
        !          1155:        if (self->plat2flags < 0)
        !          1156:        {
        !          1157:                behind = true;
        !          1158:                self->plat2flags *= -1;
        !          1159:        }
        !          1160:        summonStr = self->plat2flags;
        !          1161: 
        !          1162:        AngleVectors (self->s.angles, f, r, NULL);
        !          1163: 
        !          1164: //     num_summoned = ((((summonStr-1)/2)+1)*2)-1;  // this yields either 1, 3, or 5
        !          1165:        if (summonStr)
        !          1166:                num_summoned = (summonStr - 1) + (summonStr % 2);
        !          1167:        else
        !          1168:                num_summoned = 1;
        !          1169: 
        !          1170: //     if ((g_showlogic) && (g_showlogic->value))
        !          1171: //             gi.dprintf ("medic_commander: summonStr = %d num = %d\n", summonStr, num_summoned);
        !          1172: 
        !          1173:        for (count = 0; count < num_summoned; count++)
        !          1174:        {
        !          1175:                inc = count + (count%2); // 0, 2, 2, 4, 4
        !          1176:                VectorCopy (reinforcement_position[count], offset);
        !          1177: 
        !          1178:                G_ProjectSource (self->s.origin, offset, f, r, startpoint);
        !          1179: 
        !          1180:                // a little off the ground
        !          1181:                startpoint[2] += 10;
        !          1182: 
        !          1183:                ent = NULL;
        !          1184:                if (FindSpawnPoint (startpoint, reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc], spawnpoint, 32))
        !          1185:                {
        !          1186:                        if (CheckSpawnPoint (spawnpoint, reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc]))
        !          1187:                                ent = CreateGroundMonster (spawnpoint, self->s.angles,
        !          1188:                                        reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc],
        !          1189:                                        reinforcements[summonStr-inc], 256);
        !          1190: //                     else if ((g_showlogic) && (g_showlogic->value))
        !          1191: //                             gi.dprintf ("CheckSpawnPoint failed volume check!\n");
        !          1192:                }
        !          1193:                else
        !          1194:                {
        !          1195: //                     if ((g_showlogic) && (g_showlogic->value))
        !          1196: //                             gi.dprintf ("FindSpawnPoint failed to find a point!\n");
        !          1197:                }
        !          1198: 
        !          1199:                if (!ent)
        !          1200:                {
        !          1201: //                     if ((g_showlogic) && (g_showlogic->value))
        !          1202: //                             gi.dprintf ("Spawn point obstructed for %s, aborting!\n", reinforcements[summonStr-inc]);
        !          1203:                        continue;
        !          1204:                }
        !          1205:                if (ent->think)
        !          1206:                {
        !          1207:                        ent->nextthink = level.time;
        !          1208:                        ent->think (ent);
        !          1209:                }
        !          1210: 
        !          1211:                ent->monsterinfo.aiflags |= AI_IGNORE_SHOTS|AI_DO_NOT_COUNT|AI_SPAWNED_MEDIC_C;
        !          1212:                ent->monsterinfo.commander = self;
        !          1213:                self->monsterinfo.monster_slots--;
        !          1214: //             if ((g_showlogic) && (g_showlogic->value))
        !          1215: //                     gi.dprintf ("medic_commander: %d slots remaining\n", self->monsterinfo.monster_slots);
        !          1216: 
        !          1217:                if (self->monsterinfo.aiflags & AI_MEDIC)
        !          1218:                        designated_enemy = self->oldenemy;
        !          1219:                else
        !          1220:                        designated_enemy = self->enemy;
        !          1221: 
        !          1222:                if (coop && coop->value)
        !          1223:                {
        !          1224:                        designated_enemy = PickCoopTarget(ent);
        !          1225:                        if (designated_enemy)
        !          1226:                        {
        !          1227:                                // try to avoid using my enemy
        !          1228:                                if (designated_enemy == self->enemy)
        !          1229:                                {
        !          1230:                                        designated_enemy = PickCoopTarget(ent);
        !          1231:                                        if (designated_enemy)
        !          1232:                                        {
        !          1233:                                                if ((g_showlogic) && (g_showlogic->value))
        !          1234:                                                {
        !          1235:                                                        gi.dprintf ("PickCoopTarget returned a %s - ", designated_enemy->classname);
        !          1236:                                                        if (designated_enemy->client)
        !          1237:                                                                gi.dprintf ("with name %s\n", designated_enemy->client->pers.netname);
        !          1238:                                                        else
        !          1239:                                                                gi.dprintf ("NOT A CLIENT\n");
        !          1240:                                                }
        !          1241:                                        }
        !          1242:                                        else
        !          1243:                                        {
        !          1244:                                                if ((g_showlogic) && (g_showlogic->value))
        !          1245:                                                        gi.dprintf ("pick coop failed, using my current enemy\n");
        !          1246:                                                designated_enemy = self->enemy;
        !          1247:                                        }
        !          1248:                                }
        !          1249:                        }
        !          1250:                        else
        !          1251:                        {
        !          1252:                                if ((g_showlogic) && (g_showlogic->value))
        !          1253:                                        gi.dprintf ("pick coop failed, using my current enemy\n");
        !          1254:                                designated_enemy = self->enemy;
        !          1255:                        }
        !          1256:                }
        !          1257: 
        !          1258:                if ((designated_enemy) && (designated_enemy->inuse) && (designated_enemy->health > 0))
        !          1259:                {
        !          1260:                        // fixme
        !          1261:                        if ((g_showlogic) && (g_showlogic -> value))
        !          1262:                                gi.dprintf  ("setting enemy to %s\n", designated_enemy->classname);
        !          1263:                        ent->enemy = designated_enemy;
        !          1264:                        FoundTarget (ent);
        !          1265:                }
        !          1266:                else
        !          1267:                {
        !          1268:                        ent->enemy = NULL;
        !          1269:                        ent->monsterinfo.stand (ent);
        !          1270:                }
        !          1271: //             ent->s.event = EV_PLAYER_TELEPORT;
        !          1272:        }
        !          1273: }
        !          1274: 
        !          1275: mframe_t medic_frames_callReinforcements [] =
        !          1276: {
        !          1277:        // ROGUE - 33-36 now ai_charge
        !          1278:        ai_charge, 2,           NULL,                                   //33
        !          1279:        ai_charge, 3,           NULL,
        !          1280:        ai_charge, 5,           NULL,
        !          1281:        ai_charge, 4.4, NULL,                                   //36
        !          1282:        ai_charge, 4.7, NULL,
        !          1283:        ai_charge, 5,   NULL,
        !          1284:        ai_charge, 6,   NULL,
        !          1285:        ai_charge, 4,   NULL,                                   //40
        !          1286:        ai_charge, 0,   NULL,
        !          1287:        ai_move, 0,             medic_start_spawn,              //42
        !          1288:        ai_move, 0,             NULL,           //43 -- 43 through 47 are skipped
        !          1289:        ai_move, 0,             NULL,
        !          1290:        ai_move, 0,             NULL,
        !          1291:        ai_move, 0,             NULL,
        !          1292:        ai_move, 0,             NULL,
        !          1293:        ai_move, 0,             medic_determine_spawn,          //48
        !          1294:        ai_charge, 0,           medic_spawngrows,                       //49
        !          1295:        ai_move, 0,             NULL,           //50
        !          1296:        ai_move, 0,             NULL,           //51
        !          1297:        ai_move, -15,   medic_finish_spawn,             //52
        !          1298:        ai_move, -1.5,  NULL,
        !          1299:        ai_move, -1.2,  NULL,
        !          1300:        ai_move, -3,    NULL,
        !          1301:        ai_move, -2,    NULL,
        !          1302:        ai_move, 0.3,   NULL,
        !          1303:        ai_move, 0.7,   NULL,
        !          1304:        ai_move, 1.2,   NULL,
        !          1305:        ai_move, 1.3,   NULL                                    //60
        !          1306: };
        !          1307: mmove_t medic_move_callReinforcements = {FRAME_attack33, FRAME_attack60, medic_frames_callReinforcements, medic_run};
        !          1308: 
        !          1309: void medic_attack(edict_t *self)
        !          1310: {
        !          1311:        int             enemy_range;
        !          1312:        float   r;
        !          1313: 
        !          1314:        monster_done_dodge (self);
        !          1315: 
        !          1316:        enemy_range = range(self, self->enemy);
        !          1317: 
        !          1318:        // signal from checkattack to spawn
        !          1319:        if (self->monsterinfo.aiflags & AI_BLOCKED)
        !          1320:        {
        !          1321:                self->monsterinfo.currentmove = &medic_move_callReinforcements;
        !          1322:                self->monsterinfo.aiflags &= ~AI_BLOCKED;
        !          1323:        }
        !          1324: 
        !          1325:        r = random();
        !          1326:        if (self->monsterinfo.aiflags & AI_MEDIC)
        !          1327:        {
        !          1328:                if ((self->mass > 400) && (r > 0.8) && (self->monsterinfo.monster_slots > 2))
        !          1329:                        self->monsterinfo.currentmove = &medic_move_callReinforcements;
        !          1330:                else    
        !          1331:                        self->monsterinfo.currentmove = &medic_move_attackCable;
        !          1332:        }
        !          1333:        else
        !          1334:        {
        !          1335:                if (self->monsterinfo.attack_state == AS_BLIND)
        !          1336:                {
        !          1337:                        self->monsterinfo.currentmove = &medic_move_callReinforcements;
        !          1338:                        return;
        !          1339:                }
        !          1340:                if ((self->mass > 400) && (r > 0.2) && (enemy_range != RANGE_MELEE) && (self->monsterinfo.monster_slots > 2))
        !          1341:                        self->monsterinfo.currentmove = &medic_move_callReinforcements;
        !          1342:                else
        !          1343:                        self->monsterinfo.currentmove = &medic_move_attackBlaster;
        !          1344:        }
        !          1345: }
        !          1346: 
        !          1347: qboolean medic_checkattack (edict_t *self)
        !          1348: {
        !          1349:        if (self->monsterinfo.aiflags & AI_MEDIC)
        !          1350:        {
        !          1351:                if (realrange(self, self->enemy) < MEDIC_MAX_HEAL_DISTANCE+10)
        !          1352:                {
        !          1353:                        medic_attack(self);
        !          1354:                        return true;
        !          1355:                }
        !          1356:                else
        !          1357:                {
        !          1358:                        self->monsterinfo.attack_state = AS_STRAIGHT;
        !          1359:                        return false;
        !          1360:                }
        !          1361:        }
        !          1362: 
        !          1363:        if (self->enemy->client && !visible (self, self->enemy) && (self->monsterinfo.monster_slots > 2))
        !          1364:        {
        !          1365:                self->monsterinfo.attack_state = AS_BLIND;
        !          1366:                return true;
        !          1367:        }
        !          1368: 
        !          1369:        // give a LARGE bias to spawning things when we have room
        !          1370:        // use AI_BLOCKED as a signal to attack to spawn
        !          1371:        if ((random() < 0.8) && (self->monsterinfo.monster_slots > 5) && (realrange(self, self->enemy) > 150))
        !          1372:        {
        !          1373:                self->monsterinfo.aiflags |= AI_BLOCKED;
        !          1374:                self->monsterinfo.attack_state = AS_MISSILE;
        !          1375:                return true;
        !          1376:        }
        !          1377:        
        !          1378:        // ROGUE
        !          1379:        // since his idle animation looks kinda bad in combat, if we're not in easy mode, always attack
        !          1380:        // when he's on a combat point
        !          1381:        if (skill->value > 0)
        !          1382:                if (self->monsterinfo.aiflags & AI_STAND_GROUND)
        !          1383:                {
        !          1384:                        self->monsterinfo.attack_state = AS_MISSILE;
        !          1385:                        return true;
        !          1386:                }
        !          1387: 
        !          1388:        return M_CheckAttack (self);
        !          1389: }
        !          1390: /*
        !          1391: void medic_dodge (edict_t *self, edict_t *attacker, float eta, trace_t *tr)
        !          1392: {
        !          1393: 
        !          1394:        if (random() > 0.25)
        !          1395:                return;
        !          1396: 
        !          1397:        if (!self->enemy)
        !          1398:                self->enemy = attacker;
        !          1399: 
        !          1400:        self->monsterinfo.currentmove = &medic_move_duck;
        !          1401: 
        !          1402: //===========
        !          1403: //PMM - rogue rewrite of dodge code.
        !          1404:        float   r;
        !          1405:        float   height;
        !          1406:        int             shooting = 0;
        !          1407: 
        !          1408:        // this needs to be before the AI_MEDIC check, because
        !          1409:        //   a) if AI_MEDIC is set, we should have an enemy anyway and
        !          1410:        //   b) FoundTarget calls medic_run, which can set AI_MEDIC
        !          1411:        if (!self->enemy)
        !          1412:        {
        !          1413:                self->enemy = attacker;
        !          1414:                FoundTarget (self);
        !          1415:        }
        !          1416: 
        !          1417: //     don't dodge if you're healing
        !          1418:        if (self->monsterinfo.aiflags & AI_MEDIC)
        !          1419:                return;
        !          1420: 
        !          1421:        // PMM - don't bother if it's going to hit anyway; fix for weird in-your-face etas (I was
        !          1422:        // seeing numbers like 13 and 14)
        !          1423:        if ((eta < 0.1) || (eta > 5))
        !          1424:                return;
        !          1425: 
        !          1426:        r = random();
        !          1427:        if (r > (0.25*((skill->value)+1)))
        !          1428:                return;
        !          1429: 
        !          1430:        if ((self->monsterinfo.currentmove == &medic_move_attackHyperBlaster) ||
        !          1431:                (self->monsterinfo.currentmove == &medic_move_attackCable) ||
        !          1432:                (self->monsterinfo.currentmove == &medic_move_attackBlaster))
        !          1433:        {
        !          1434:                shooting = 1;
        !          1435:        }
        !          1436:        if (self->monsterinfo.aiflags & AI_DODGING)
        !          1437:        {
        !          1438:                height = self->absmax[2];
        !          1439:        }
        !          1440:        else
        !          1441:        {
        !          1442:                height = self->absmax[2]-32-1;  // the -1 is because the absmax is s.origin + maxs + 1
        !          1443:        }
        !          1444: 
        !          1445:        // check to see if it makes sense to duck
        !          1446:        if (tr->endpos[2] <= height)
        !          1447:        {
        !          1448:                vec3_t right, diff;
        !          1449:                if (shooting)
        !          1450:                {
        !          1451:                        self->monsterinfo.attack_state = AS_SLIDING;
        !          1452:                        return;
        !          1453:                }
        !          1454:                AngleVectors (self->s.angles, NULL, right, NULL);
        !          1455:                VectorSubtract (tr->endpos, self->s.origin, diff);
        !          1456:                if (DotProduct (right, diff) < 0)
        !          1457:                {
        !          1458:                        self->monsterinfo.lefty = 1;
        !          1459:                }
        !          1460:                // if it doesn't sense to duck, try to strafe away
        !          1461:                monster_done_dodge (self);
        !          1462:                self->monsterinfo.currentmove = &medic_move_run;
        !          1463:                self->monsterinfo.attack_state = AS_SLIDING;
        !          1464:                return;
        !          1465:        }
        !          1466: 
        !          1467:        if (skill->value == 0)
        !          1468:        {
        !          1469:                self->monsterinfo.currentmove = &medic_move_duck;
        !          1470:                // PMM - stupid dodge
        !          1471:                self->monsterinfo.duck_wait_time = level.time + eta + 1;
        !          1472:                self->monsterinfo.aiflags |= AI_DODGING;
        !          1473:                return;
        !          1474:        }
        !          1475: 
        !          1476:        if (!shooting)
        !          1477:        {
        !          1478:                self->monsterinfo.currentmove = &medic_move_duck;
        !          1479:                self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value));
        !          1480:                self->monsterinfo.aiflags |= AI_DODGING;
        !          1481:        }
        !          1482:        return;
        !          1483: 
        !          1484: //PMM
        !          1485: //===========
        !          1486: 
        !          1487: }
        !          1488: */
        !          1489: void MedicCommanderCache (void)
        !          1490: {
        !          1491:        edict_t *newEnt;
        !          1492:        int     modelidx;
        !          1493:        int i;
        !          1494: 
        !          1495:        //FIXME - better way to do this?  this is quick and dirty
        !          1496:        for (i=0; i < 7; i++)
        !          1497:        {
        !          1498:                newEnt = G_Spawn();
        !          1499: 
        !          1500:                VectorCopy(vec3_origin, newEnt->s.origin);
        !          1501:                VectorCopy(vec3_origin, newEnt->s.angles);
        !          1502:                newEnt->classname = ED_NewString (reinforcements[i]);
        !          1503:                
        !          1504:                newEnt->monsterinfo.aiflags |= AI_DO_NOT_COUNT;
        !          1505: 
        !          1506:                ED_CallSpawn(newEnt);
        !          1507:                // FIXME - could copy mins/maxs into reinforcements from here
        !          1508:                G_FreeEdict (newEnt);
        !          1509:        }
        !          1510: 
        !          1511:        modelidx = gi.modelindex("models/items/spawngro/tris.md2");
        !          1512:        modelidx = gi.modelindex("models/items/spawngro2/tris.md2");
        !          1513: }
        !          1514: 
        !          1515: void medic_duck (edict_t *self, float eta)
        !          1516: {
        !          1517: //     don't dodge if you're healing
        !          1518:        if (self->monsterinfo.aiflags & AI_MEDIC)
        !          1519:                return;
        !          1520: 
        !          1521:        if ((self->monsterinfo.currentmove == &medic_move_attackHyperBlaster) ||
        !          1522:                (self->monsterinfo.currentmove == &medic_move_attackCable) ||
        !          1523:                (self->monsterinfo.currentmove == &medic_move_attackBlaster) ||
        !          1524:                (self->monsterinfo.currentmove == &medic_move_callReinforcements))
        !          1525:        {
        !          1526:                // he ignores skill
        !          1527:                self->monsterinfo.aiflags &= ~AI_DUCKED;
        !          1528:                return;
        !          1529:        }
        !          1530: 
        !          1531:        if (skill->value == 0)
        !          1532:                // PMM - stupid dodge
        !          1533:                self->monsterinfo.duck_wait_time = level.time + eta + 1;
        !          1534:        else
        !          1535:                self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value));
        !          1536: 
        !          1537:        // has to be done immediately otherwise he can get stuck
        !          1538:        monster_duck_down(self);
        !          1539: 
        !          1540:        self->monsterinfo.nextframe = FRAME_duck1;
        !          1541:        self->monsterinfo.currentmove = &medic_move_duck;
        !          1542:        return;
        !          1543: }
        !          1544: 
        !          1545: void medic_sidestep (edict_t *self)
        !          1546: {
        !          1547:        if ((self->monsterinfo.currentmove == &medic_move_attackHyperBlaster) ||
        !          1548:                (self->monsterinfo.currentmove == &medic_move_attackCable) ||
        !          1549:                (self->monsterinfo.currentmove == &medic_move_attackBlaster) ||
        !          1550:                (self->monsterinfo.currentmove == &medic_move_callReinforcements))
        !          1551:        {
        !          1552:                // if we're shooting, and not on easy, don't dodge
        !          1553:                if (skill->value)
        !          1554:                {
        !          1555:                        self->monsterinfo.aiflags &= ~AI_DODGING;
        !          1556:                        return;
        !          1557:                }
        !          1558:        }
        !          1559: 
        !          1560:        if (self->monsterinfo.currentmove != &medic_move_run)
        !          1561:                self->monsterinfo.currentmove = &medic_move_run;
        !          1562: }
        !          1563: 
        !          1564: //===========
        !          1565: //PGM
        !          1566: qboolean medic_blocked (edict_t *self, float dist)
        !          1567: {
        !          1568:        if(blocked_checkshot (self, 0.25 + (0.05 * skill->value) ))
        !          1569:                return true;
        !          1570: 
        !          1571:        if(blocked_checkplat (self, dist))
        !          1572:                return true;
        !          1573: 
        !          1574:        return false;
        !          1575: }
        !          1576: //PGM
        !          1577: //===========
        !          1578: 
        !          1579: /*QUAKED monster_medic_commander (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
        !          1580: */
        !          1581: /*QUAKED monster_medic (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
        !          1582: */
        !          1583: void SP_monster_medic (edict_t *self)
        !          1584: {
        !          1585:        if (deathmatch->value)
        !          1586:        {
        !          1587:                G_FreeEdict (self);
        !          1588:                return;
        !          1589:        }
        !          1590: 
        !          1591:        sound_idle1 = gi.soundindex ("medic/idle.wav");
        !          1592:        sound_pain1 = gi.soundindex ("medic/medpain1.wav");
        !          1593:        sound_pain2 = gi.soundindex ("medic/medpain2.wav");
        !          1594:        sound_die = gi.soundindex ("medic/meddeth1.wav");
        !          1595:        sound_sight = gi.soundindex ("medic/medsght1.wav");
        !          1596:        sound_search = gi.soundindex ("medic/medsrch1.wav");
        !          1597:        sound_hook_launch = gi.soundindex ("medic/medatck2.wav");
        !          1598:        sound_hook_hit = gi.soundindex ("medic/medatck3.wav");
        !          1599:        sound_hook_heal = gi.soundindex ("medic/medatck4.wav");
        !          1600:        sound_hook_retract = gi.soundindex ("medic/medatck5.wav");
        !          1601: 
        !          1602:        gi.soundindex ("medic/medatck1.wav");
        !          1603: 
        !          1604:        self->movetype = MOVETYPE_STEP;
        !          1605:        self->solid = SOLID_BBOX;
        !          1606:        self->s.modelindex = gi.modelindex ("models/monsters/medic/tris.md2");
        !          1607:        VectorSet (self->mins, -24, -24, -24);
        !          1608:        VectorSet (self->maxs, 24, 24, 32);
        !          1609: 
        !          1610: //PMM
        !          1611:        if (strcmp(self->classname, "monster_medic_commander") == 0)
        !          1612:        {
        !          1613:                self->health = 600;                     //      fixme
        !          1614:                self->gib_health = -130;
        !          1615:                self->mass = 600;
        !          1616:                self->yaw_speed = 40; // default is 20
        !          1617:                MedicCommanderCache();
        !          1618: //             self->s.skinnum = 2;
        !          1619:        }
        !          1620:        else
        !          1621:        {
        !          1622: //PMM
        !          1623:                self->health = 300;
        !          1624:                self->gib_health = -130;
        !          1625:                self->mass = 400;
        !          1626: //             self->s.skinnum = 0;
        !          1627:        }
        !          1628: 
        !          1629:        self->pain = medic_pain;
        !          1630:        self->die = medic_die;
        !          1631: 
        !          1632:        self->monsterinfo.stand = medic_stand;
        !          1633:        self->monsterinfo.walk = medic_walk;
        !          1634:        self->monsterinfo.run = medic_run;
        !          1635:        // pmm
        !          1636:        self->monsterinfo.dodge = M_MonsterDodge;
        !          1637:        self->monsterinfo.duck = medic_duck;
        !          1638:        self->monsterinfo.unduck = monster_duck_up;
        !          1639:        self->monsterinfo.sidestep = medic_sidestep;
        !          1640: //     self->monsterinfo.dodge = medic_dodge;
        !          1641:        // pmm
        !          1642:        self->monsterinfo.attack = medic_attack;
        !          1643:        self->monsterinfo.melee = NULL;
        !          1644:        self->monsterinfo.sight = medic_sight;
        !          1645:        self->monsterinfo.idle = medic_idle;
        !          1646:        self->monsterinfo.search = medic_search;
        !          1647:        self->monsterinfo.checkattack = medic_checkattack;
        !          1648:        self->monsterinfo.blocked = medic_blocked;
        !          1649: 
        !          1650:        gi.linkentity (self);
        !          1651: 
        !          1652:        self->monsterinfo.currentmove = &medic_move_stand;
        !          1653:        self->monsterinfo.scale = MODEL_SCALE;
        !          1654: 
        !          1655:        walkmonster_start (self);
        !          1656: 
        !          1657:        //PMM
        !          1658:        self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
        !          1659: 
        !          1660:        if (self->mass > 400)
        !          1661:        {
        !          1662:                self->s.skinnum = 2;
        !          1663:                if (skill->value == 0)
        !          1664:                        self->monsterinfo.monster_slots = 3;
        !          1665:                else if (skill->value == 1)
        !          1666:                        self->monsterinfo.monster_slots = 4;
        !          1667:                else if (skill->value == 2)
        !          1668:                        self->monsterinfo.monster_slots = 6;
        !          1669:                else if (skill->value == 3)
        !          1670:                        self->monsterinfo.monster_slots = 6;
        !          1671:        }
        !          1672:        else
        !          1673:                self->s.skinnum = 0;
        !          1674:        //pmm
        !          1675: }

unix.superglobalmegacorp.com

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