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