Annotation of quake2/rogue/m_widow.c, revision 1.1.1.1

1.1       root        1: /*
                      2: ==============================================================================
                      3: 
                      4: black widow
                      5: 
                      6: ==============================================================================
                      7: */
                      8: 
                      9: // self->timestamp used to prevent rapid fire of railgun
                     10: // self->plat2flags used for fire count (flashes)
                     11: // self->monsterinfo.pausetime used for timing of blaster shots
                     12: 
                     13: #include "g_local.h"
                     14: #include "m_widow.h"
                     15: 
                     16: #define        NUM_STALKERS_SPAWNED            6               // max # of stalkers she can spawn
                     17: 
                     18: #define        RAIL_TIME                                       3
                     19: #define        BLASTER_TIME                            2
                     20: #define        BLASTER2_DAMAGE                         10
                     21: #define        WIDOW_RAIL_DAMAGE                       50
                     22: 
                     23: #define        DRAWBBOX                                        NULL
                     24: #define        SHOWME                                          showme
                     25: 
                     26: void BossExplode (edict_t *self);
                     27: 
                     28: qboolean infront (edict_t *self, edict_t *other);
                     29: 
                     30: static int     sound_pain1;
                     31: static int     sound_pain2;
                     32: static int     sound_pain3;
                     33: static int     sound_death;
                     34: static int     sound_search1;
                     35: static int     sound_rail;
                     36: 
                     37: static unsigned long shotsfired;
                     38: 
                     39: static vec3_t spawnpoints[] = {
                     40:        {30,  100, 16},
                     41:        {30, -100, 16}
                     42: };
                     43: 
                     44: static vec3_t beameffects[] = {
                     45:        {12.58, -43.71, 68.88},
                     46:        {3.43, 58.72, 68.41}
                     47: };
                     48: 
                     49: static float sweep_angles[] = {
                     50: //     32.0, 26.0, 20.0, 11.5, 3.0, -8.0, -13.0, -27.0, -41.0
                     51:        32.0, 26.0, 20.0, 10.0, 0.0, -6.5, -13.0, -27.0, -41.0
                     52: };
                     53: 
                     54: vec3_t stalker_mins = {-28, -28, -18};
                     55: vec3_t stalker_maxs = {28, 28, 18};
                     56: 
                     57: unsigned int   widow_damage_multiplier;
                     58: 
                     59: void widow_run (edict_t *self);
                     60: void widow_stand (edict_t *self);
                     61: void widow_dead (edict_t *self);
                     62: void widow_attack (edict_t *self);
                     63: void widow_attack_blaster (edict_t *self);
                     64: void widow_reattack_blaster (edict_t *self);
                     65: void widow_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
                     66: 
                     67: void widow_start_spawn (edict_t *self);
                     68: void widow_done_spawn (edict_t *self);
                     69: void widow_spawn_check (edict_t *self);
                     70: void widow_prep_spawn (edict_t *self);
                     71: void widow_attack_rail (edict_t *self);
                     72: 
                     73: void widow_start_run_5 (edict_t *self);
                     74: void widow_start_run_10 (edict_t *self);
                     75: void widow_start_run_12 (edict_t *self);
                     76: 
                     77: void WidowCalcSlots (edict_t *self);
                     78: 
                     79: void drawbbox (edict_t *self);
                     80: 
                     81: void showme (edict_t *self)
                     82: {
                     83:        gi.dprintf ("frame %d\n", self->s.frame);
                     84: }
                     85: 
                     86: void widow_search (edict_t *self)
                     87: {
                     88:        if (random() < 0.5)
                     89:                gi.sound (self, CHAN_VOICE, sound_search1, 1, ATTN_NONE, 0);
                     90: }
                     91: 
                     92: void widow_sight (edict_t *self, edict_t *other)
                     93: {
                     94:        self->monsterinfo.pausetime = 0;
                     95:        if ((g_showlogic) && (g_showlogic->value))
                     96:                gi.dprintf ("widow: found target!\n");
                     97: }
                     98: 
                     99: mmove_t widow_move_attack_post_blaster;
                    100: mmove_t widow_move_attack_post_blaster_r;
                    101: mmove_t widow_move_attack_post_blaster_l;
                    102: mmove_t widow_move_attack_blaster;
                    103: 
                    104: float target_angle (edict_t *self)
                    105: {
                    106:        vec3_t target;
                    107:        float enemy_yaw;
                    108: 
                    109:        VectorSubtract (self->s.origin, self->enemy->s.origin, target);
                    110:        enemy_yaw = self->s.angles[YAW] - vectoyaw2(target);
                    111:        if (enemy_yaw < 0)
                    112:                enemy_yaw += 360.0;
                    113: 
                    114:        // this gets me 0 degrees = forward
                    115:        enemy_yaw -= 180.0;
                    116:        // positive is to right, negative to left
                    117: 
                    118:        return enemy_yaw;
                    119: }
                    120: 
                    121: int WidowTorso (edict_t *self)
                    122: {
                    123:        float enemy_yaw;
                    124: 
                    125:        enemy_yaw = target_angle (self);
                    126: 
                    127: //     if ((g_showlogic) && (g_showlogic->value))
                    128: //             gi.dprintf ("%2.2f -> ", enemy_yaw);
                    129: 
                    130:        if (enemy_yaw >= 105)
                    131:        {
                    132:                self->monsterinfo.currentmove = &widow_move_attack_post_blaster_r;
                    133:                self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
                    134:                return 0;
                    135:        }
                    136: 
                    137:        if (enemy_yaw <= -75.0)
                    138:        {
                    139:                self->monsterinfo.currentmove = &widow_move_attack_post_blaster_l;
                    140:                self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
                    141:                return 0;
                    142:        }
                    143: 
                    144:        if (enemy_yaw >= 95)
                    145:                return FRAME_fired03;
                    146:        else if (enemy_yaw >= 85)
                    147:                return FRAME_fired04;
                    148:        else if (enemy_yaw >= 75)
                    149:                return FRAME_fired05;
                    150:        else if (enemy_yaw >= 65)
                    151:                return FRAME_fired06;
                    152:        else if (enemy_yaw >= 55)
                    153:                return FRAME_fired07;
                    154:        else if (enemy_yaw >= 45)
                    155:                return FRAME_fired08;
                    156:        else if (enemy_yaw >= 35)
                    157:                return FRAME_fired09;
                    158:        else if (enemy_yaw >= 25)
                    159:                return FRAME_fired10;
                    160:        else if (enemy_yaw >= 15)
                    161:                return FRAME_fired11;
                    162:        else if (enemy_yaw >= 5)
                    163:                return FRAME_fired12;
                    164:        else if (enemy_yaw >= -5)
                    165:                return FRAME_fired13;
                    166:        else if (enemy_yaw >= -15)
                    167:                return FRAME_fired14;
                    168:        else if (enemy_yaw >= -25)
                    169:                return FRAME_fired15;
                    170:        else if (enemy_yaw >= -35)
                    171:                return FRAME_fired16;
                    172:        else if (enemy_yaw >= -45)
                    173:                return FRAME_fired17;
                    174:        else if (enemy_yaw >= -55)
                    175:                return FRAME_fired18;
                    176:        else if (enemy_yaw >= -65)
                    177:                return FRAME_fired19;
                    178:        else if (enemy_yaw >= -75)
                    179:                return FRAME_fired20;
                    180: /*
                    181:        if (fabs(enemy_yaw) < 11.25)
                    182:                return FRAME_fired03;
                    183:        else if (fabs(enemy_yaw) > 56.25)
                    184:        {
                    185:                self->monsterinfo.currentmove = &widow_move_attack_post_blaster;
                    186:                self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
                    187:                return;
                    188:        }
                    189:        else if ((enemy_yaw >= 11.25) && (enemy_yaw < 33.75))
                    190:                return FRAME_fired04;
                    191:        else if (enemy_yaw >= 33.75)
                    192:                return FRAME_fired05;
                    193:        else if ((enemy_yaw <= -11.25) && (enemy_yaw > -33.75))
                    194:                return FRAME_fired06;
                    195:        else if (enemy_yaw <= -33.75)
                    196:                return FRAME_fired07;
                    197: */
                    198: }
                    199: 
                    200: #define        VARIANCE 15.0
                    201: 
                    202: void WidowBlaster (edict_t *self)
                    203: {
                    204:        vec3_t  forward, right, target, vec, targ_angles;
                    205:        vec3_t  start;
                    206:        int             flashnum;
                    207:        int             effect;
                    208: 
                    209:        if (!self->enemy)
                    210:                return;
                    211: 
                    212:        // FIXME - this is not an int
                    213:        shotsfired++;
                    214:        if (!(shotsfired % 4))
                    215:                effect = EF_BLASTER;
                    216:        else
                    217:                effect = 0;
                    218: 
                    219:        AngleVectors (self->s.angles, forward, right, NULL);
                    220:        if ((self->s.frame >= FRAME_spawn05) && (self->s.frame <= FRAME_spawn13))
                    221:        {
                    222:                // sweep
                    223:                flashnum = MZ2_WIDOW_BLASTER_SWEEP1 + self->s.frame - FRAME_spawn05;
                    224:                G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
                    225:                VectorSubtract (self->enemy->s.origin, start, target);
                    226:                vectoangles2 (target, targ_angles);
                    227:                
                    228:                VectorCopy (self->s.angles, vec);
                    229: 
                    230:                vec[PITCH] += targ_angles[PITCH];
                    231:                vec[YAW] -= sweep_angles[flashnum-MZ2_WIDOW_BLASTER_SWEEP1];
                    232: 
                    233:                AngleVectors (vec, forward, NULL, NULL);
                    234:                monster_fire_blaster2 (self, start, forward, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);
                    235: 
                    236: /*             if (self->s.frame == FRAME_spawn13)
                    237:                {
                    238:                        VectorMA (start, 1024, forward, debugend);
                    239: 
                    240:                        gi.WriteByte (svc_temp_entity);
                    241:                        gi.WriteByte (TE_DEBUGTRAIL);
                    242:                        gi.WritePosition (start);
                    243:                        gi.WritePosition (debugend);
                    244:                        gi.multicast (start, MULTICAST_ALL);
                    245: 
                    246:                        drawbbox (self);
                    247:                        self->monsterinfo.aiflags |= AI_HOLD_FRAME|AI_MANUAL_STEERING;
                    248:                }
                    249: */
                    250:        }
                    251:        else if ((self->s.frame >= FRAME_fired02a) && (self->s.frame <= FRAME_fired20))
                    252:        {
                    253:                vec3_t angles;
                    254:                float aim_angle, target_angle;
                    255:                float error;
                    256: 
                    257:                self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
                    258: 
                    259:                self->monsterinfo.nextframe = WidowTorso (self);
                    260: 
                    261:                if (!self->monsterinfo.nextframe)
                    262:                        self->monsterinfo.nextframe = self->s.frame;
                    263: 
                    264: //             if ((g_showlogic) && (g_showlogic->value))
                    265: //                     gi.dprintf ("%d\n", self->monsterinfo.nextframe);
                    266: 
                    267:                if (self->s.frame == FRAME_fired02a)
                    268:                        flashnum = MZ2_WIDOW_BLASTER_0;
                    269:                else
                    270:                        flashnum = MZ2_WIDOW_BLASTER_100 + self->s.frame - FRAME_fired03;
                    271: 
                    272:                G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
                    273: 
                    274:                PredictAim (self->enemy, start, 1000, true, ((random()*0.1)-0.05), forward, NULL);
                    275: 
                    276:                // clamp it to within 10 degrees of the aiming angle (where she's facing)
                    277:                vectoangles2 (forward, angles);
                    278:                // give me 100 -> -70
                    279:                aim_angle = 100 - (10*(flashnum-MZ2_WIDOW_BLASTER_100));
                    280:                if (aim_angle <= 0)
                    281:                        aim_angle += 360;
                    282:                target_angle = self->s.angles[YAW] - angles[YAW];
                    283:                if (target_angle <= 0)
                    284:                        target_angle += 360;
                    285: 
                    286:                error = aim_angle - target_angle;
                    287: 
                    288:                // positive error is to entity's left, aka positive direction in engine
                    289:                // unfortunately, I decided that for the aim_angle, positive was right.  *sigh*
                    290:                if (error > VARIANCE)
                    291:                {
                    292: //                     if ((g_showlogic) && (g_showlogic->value))
                    293: //                             gi.dprintf ("angle %2.2f (really %2.2f) (%2.2f off of %2.2f) corrected to", target_angle, angles[YAW], error, aim_angle);
                    294:                        angles[YAW] = (self->s.angles[YAW] - aim_angle) + VARIANCE;
                    295: //                     if ((g_showlogic) && (g_showlogic->value))
                    296: //                     {
                    297: //                             if (angles[YAW] <= 0)
                    298: //                                     angles[YAW] += 360;
                    299: //                             gi.dprintf (" %2.2f\n", angles[YAW]);
                    300: //                     }
                    301:                        AngleVectors (angles, forward, NULL, NULL);
                    302:                }
                    303:                else if (error < -VARIANCE)
                    304:                {
                    305: //                     if ((g_showlogic) && (g_showlogic->value))
                    306: //                             gi.dprintf ("angle %2.2f (really %2.2f) (%2.2f off of %2.2f) corrected to", target_angle, angles[YAW], error, aim_angle);
                    307:                        angles[YAW] = (self->s.angles[YAW] - aim_angle) - VARIANCE;
                    308: //                     if ((g_showlogic) && (g_showlogic->value))
                    309: //                     {
                    310: //                             if (angles[YAW] <= 0)
                    311: //                                     angles[YAW] += 360;
                    312: //                             gi.dprintf (" %2.2f\n", angles[YAW]);
                    313: //                     }
                    314:                        AngleVectors (angles, forward, NULL, NULL);
                    315:                }
                    316: //             gi.dprintf ("%2.2f - %2.2f - %2.2f - %2.2f\n", aim_angle, self->s.angles[YAW] - angles[YAW], target_angle, error);
                    317: //             gi.dprintf ("%2.2f - %2.2f - %2.2f\n", angles[YAW], aim_angle, self->s.angles[YAW]);
                    318: 
                    319: /*
                    320:                if (self->s.frame == FRAME_fired20)
                    321:                {
                    322:                        VectorMA (start, 512, forward, debugend);
                    323:                        gi.WriteByte (svc_temp_entity);
                    324:                        gi.WriteByte (TE_DEBUGTRAIL);
                    325:                        gi.WritePosition (start);
                    326:                        gi.WritePosition (forward);
                    327:                        gi.multicast (start, MULTICAST_ALL);
                    328: 
                    329:                        drawbbox (self);
                    330:                        self->monsterinfo.aiflags |= AI_HOLD_FRAME;
                    331:                        self->monsterinfo.nextframe = FRAME_fired20;
                    332:                        self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
                    333:                }
                    334: */
                    335: /*
                    336:                if (!(self->plat2flags % 3))
                    337:                        effect = EF_HYPERBLASTER;
                    338:                else
                    339:                        effect = 0;
                    340:                self->plat2flags ++;
                    341: */
                    342:                monster_fire_blaster2 (self, start, forward, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);
                    343:        }
                    344:        else if ((self->s.frame >= FRAME_run01) && (self->s.frame <= FRAME_run08))
                    345:        {
                    346:                flashnum = MZ2_WIDOW_RUN_1 + self->s.frame - FRAME_run01;
                    347:                G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
                    348:                
                    349:                VectorSubtract (self->enemy->s.origin, start, target);
                    350:                target[2] += self->enemy->viewheight;
                    351: 
                    352:                monster_fire_blaster2 (self, start, target, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);
                    353:        }
                    354:        else
                    355:        {
                    356:                if ((g_showlogic) && (g_showlogic->value))
                    357:                        gi.dprintf ("widow: firing on non-fire frame!\n");
                    358:        }
                    359: }      
                    360: 
                    361: void WidowSpawn (edict_t *self)
                    362: {
                    363:        vec3_t  f, r, u, offset, startpoint, spawnpoint;
                    364:        edict_t *ent, *designated_enemy;
                    365:        int             i;
                    366: 
                    367:        AngleVectors (self->s.angles, f, r, u);
                    368: 
                    369:        for (i=0; i < 2; i++)
                    370:        {
                    371:                VectorCopy (spawnpoints[i], offset);
                    372: 
                    373:                G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
                    374: 
                    375:                if (FindSpawnPoint (startpoint, stalker_mins, stalker_maxs, spawnpoint, 64))
                    376:                {
                    377:                        ent = CreateGroundMonster (spawnpoint, self->s.angles, stalker_mins, stalker_maxs, "monster_stalker", 256);
                    378: 
                    379:                        if (!ent)
                    380:                                continue;
                    381:                        
                    382:                        self->monsterinfo.monster_used++;
                    383:                        ent->monsterinfo.commander = self;
                    384:                        if ((g_showlogic) && (g_showlogic->value))
                    385:                                gi.dprintf ("widow: post-spawn : %d slots left out of %d\n", SELF_SLOTS_LEFT, self->monsterinfo.monster_slots);
                    386: 
                    387:                        ent->nextthink = level.time;
                    388:                        ent->think (ent);
                    389:                        
                    390:                        ent->monsterinfo.aiflags |= AI_SPAWNED_WIDOW|AI_DO_NOT_COUNT|AI_IGNORE_SHOTS;
                    391: 
                    392:                        if (!(coop && coop->value))
                    393:                        {
                    394:                                designated_enemy = self->enemy;
                    395:                        }
                    396:                        else
                    397:                        {
                    398:                                designated_enemy = PickCoopTarget(ent);
                    399:                                if (designated_enemy)
                    400:                                {
                    401:                                        // try to avoid using my enemy
                    402:                                        if (designated_enemy == self->enemy)
                    403:                                        {
                    404:                                                designated_enemy = PickCoopTarget(ent);
                    405:                                                if (designated_enemy)
                    406:                                                {
                    407:                                                        if ((g_showlogic) && (g_showlogic->value))
                    408:                                                        {
                    409:                                                                gi.dprintf ("PickCoopTarget returned a %s - ", designated_enemy->classname);
                    410:                                                                if (designated_enemy->client)
                    411:                                                                        gi.dprintf ("with name %s\n", designated_enemy->client->pers.netname);
                    412:                                                                else
                    413:                                                                        gi.dprintf ("NOT A CLIENT\n");
                    414:                                                        }
                    415:                                                }
                    416:                                                else
                    417:                                                {
                    418:                                                        if ((g_showlogic) && (g_showlogic->value))
                    419:                                                                gi.dprintf ("pick coop failed, using my current enemy\n");
                    420:                                                        designated_enemy = self->enemy;
                    421:                                                }
                    422:                                        }
                    423:                                }
                    424:                                else
                    425:                                {
                    426:                                        if ((g_showlogic) && (g_showlogic->value))
                    427:                                                gi.dprintf ("pick coop failed, using my current enemy\n");
                    428:                                        designated_enemy = self->enemy;
                    429:                                }
                    430:                        }
                    431: 
                    432:                        if ((designated_enemy->inuse) && (designated_enemy->health > 0))
                    433:                        {
                    434:                                ent->enemy = designated_enemy;
                    435:                                FoundTarget (ent);
                    436:                                ent->monsterinfo.attack(ent);
                    437:                        }
                    438:                }
                    439:        }
                    440: }
                    441: 
                    442: void widow_spawn_check (edict_t *self)
                    443: {
                    444:        WidowBlaster(self);
                    445:        WidowSpawn (self);
                    446: }
                    447: 
                    448: void widow_ready_spawn (edict_t *self)
                    449: {
                    450:        vec3_t  f, r, u, offset, startpoint, spawnpoint;
                    451:        int             i;
                    452: 
                    453:        WidowBlaster(self);
                    454:        AngleVectors (self->s.angles, f, r, u);
                    455: 
                    456:        for (i=0; i < 2; i++)
                    457:        {
                    458:                VectorCopy (spawnpoints[i], offset);
                    459:                G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
                    460:                if (FindSpawnPoint (startpoint, stalker_mins, stalker_maxs, spawnpoint, 64))
                    461:                {
                    462:                        SpawnGrow_Spawn (spawnpoint, 1);
                    463:                }
                    464:        }
                    465: }
                    466: 
                    467: mframe_t widow_frames_stand [] =
                    468: {
                    469:        ai_stand, 0, NULL,
                    470:        ai_stand, 0, NULL,
                    471:        ai_stand, 0, NULL,
                    472:        ai_stand, 0, NULL,
                    473:        ai_stand, 0, NULL,
                    474:        ai_stand, 0, NULL,
                    475:        ai_stand, 0, NULL,
                    476:        ai_stand, 0, NULL,
                    477:        ai_stand, 0, NULL,
                    478:        ai_stand, 0, NULL,
                    479:        ai_stand, 0, NULL
                    480: };
                    481: mmove_t        widow_move_stand = {FRAME_idle01, FRAME_idle11, widow_frames_stand, NULL};
                    482: 
                    483: mframe_t widow_frames_walk [] =
                    484: {
                    485:        // hand generated numbers
                    486: /*
                    487:        ai_run, 6,      NULL,
                    488:        ai_run, 3,      NULL,
                    489:        ai_run, 3,      NULL,
                    490:        ai_run, 3,      NULL,
                    491:        ai_run, 4,      NULL,                   //5
                    492:        ai_run, 4,      NULL,
                    493:        ai_run, 4,      NULL,
                    494:        ai_run, 4.5,    NULL,
                    495:        ai_run, 3,      NULL,
                    496:        ai_run, 5,      NULL,                   //10
                    497:        ai_run, 8,      NULL,
                    498:        ai_run, 8,      NULL,
                    499:        ai_run, 6.5,    NULL
                    500: */
                    501:        // auto generated numbers
                    502:        ai_walk,        2.79,   NULL,
                    503:        ai_walk,        2.77,   NULL,
                    504:        ai_walk,        3.53,   NULL,
                    505:        ai_walk,        3.97,   NULL,
                    506:        ai_walk,        4.13,   NULL,                   //5
                    507:        ai_walk,        4.09,   NULL,
                    508:        ai_walk,        3.84,   NULL,
                    509:        ai_walk,        3.62,   NULL,
                    510:        ai_walk,        3.29,   NULL,
                    511:        ai_walk,        6.08,   NULL,                   //10
                    512:        ai_walk,        6.94,   NULL,
                    513:        ai_walk,        5.73,   NULL,
                    514:        ai_walk,        2.85,   NULL
                    515: };
                    516: mmove_t widow_move_walk = {FRAME_walk01, FRAME_walk13, widow_frames_walk, NULL};
                    517: 
                    518: 
                    519: mframe_t widow_frames_run [] =
                    520: {
                    521:        ai_run, 2.79,   NULL,
                    522:        ai_run, 2.77,   NULL,
                    523:        ai_run, 3.53,   NULL,
                    524:        ai_run, 3.97,   NULL,
                    525:        ai_run, 4.13,   NULL,                   //5
                    526:        ai_run, 4.09,   NULL,
                    527:        ai_run, 3.84,   NULL,
                    528:        ai_run, 3.62,   NULL,
                    529:        ai_run, 3.29,   NULL,
                    530:        ai_run, 6.08,   NULL,                   //10
                    531:        ai_run, 6.94,   NULL,
                    532:        ai_run, 5.73,   NULL,
                    533:        ai_run, 2.85,   NULL
                    534: };
                    535: mmove_t widow_move_run = {FRAME_walk01, FRAME_walk13, widow_frames_run, NULL};
                    536: 
                    537: mframe_t widow_frames_run_attack [] =
                    538: {
                    539:        ai_charge,      13,     WidowBlaster,
                    540:        ai_charge,      11.72,  WidowBlaster,
                    541:        ai_charge,      18.04,  WidowBlaster,
                    542:        ai_charge,      14.58,  WidowBlaster,
                    543:        ai_charge,      13,     WidowBlaster,                   //5
                    544:        ai_charge,      12.12,  WidowBlaster,
                    545:        ai_charge,      19.63,  WidowBlaster,
                    546:        ai_charge,      11.37,  WidowBlaster
                    547: };
                    548: mmove_t widow_move_run_attack = {FRAME_run01, FRAME_run08, widow_frames_run_attack, widow_run};
                    549: 
                    550: 
                    551: //
                    552: // These three allow specific entry into the run sequence
                    553: //
                    554: 
                    555: void widow_start_run_5 (edict_t *self)
                    556: {
                    557:        self->monsterinfo.currentmove = &widow_move_run;
                    558:        self->monsterinfo.nextframe = FRAME_walk05;
                    559: }
                    560: 
                    561: void widow_start_run_10 (edict_t *self)
                    562: {
                    563:        self->monsterinfo.currentmove = &widow_move_run;
                    564:        self->monsterinfo.nextframe = FRAME_walk10;
                    565: }
                    566: 
                    567: void widow_start_run_12 (edict_t *self)
                    568: {
                    569:        self->monsterinfo.currentmove = &widow_move_run;
                    570:        self->monsterinfo.nextframe = FRAME_walk12;
                    571: }
                    572: 
                    573: 
                    574: mframe_t widow_frames_attack_pre_blaster [] =
                    575: {
                    576:        ai_charge,      0,      NULL,
                    577:        ai_charge,      0,      NULL,
                    578:        ai_charge,      0,      widow_attack_blaster
                    579: };
                    580: mmove_t widow_move_attack_pre_blaster = {FRAME_fired01, FRAME_fired02a, widow_frames_attack_pre_blaster, NULL};
                    581: 
                    582: // Loop this
                    583: mframe_t widow_frames_attack_blaster [] =
                    584: {
                    585:        ai_charge,      0,      widow_reattack_blaster,         // straight ahead
                    586:        ai_charge,      0,      widow_reattack_blaster,         // 100 degrees right
                    587:        ai_charge,      0,      widow_reattack_blaster,
                    588:        ai_charge,      0,      widow_reattack_blaster,
                    589:        ai_charge,      0,      widow_reattack_blaster,
                    590:        ai_charge,      0,      widow_reattack_blaster,
                    591:        ai_charge,      0,      widow_reattack_blaster,         // 50 degrees right
                    592:        ai_charge,      0,      widow_reattack_blaster,
                    593:        ai_charge,      0,      widow_reattack_blaster,
                    594:        ai_charge,      0,      widow_reattack_blaster,
                    595:        ai_charge,      0,      widow_reattack_blaster,
                    596:        ai_charge,      0,      widow_reattack_blaster,         // straight
                    597:        ai_charge,      0,      widow_reattack_blaster,
                    598:        ai_charge,      0,      widow_reattack_blaster,
                    599:        ai_charge,      0,      widow_reattack_blaster,
                    600:        ai_charge,      0,      widow_reattack_blaster,
                    601:        ai_charge,      0,      widow_reattack_blaster,         // 50 degrees left
                    602:        ai_charge,      0,      widow_reattack_blaster,
                    603:        ai_charge,      0,      widow_reattack_blaster          // 70 degrees left
                    604: };
                    605: mmove_t widow_move_attack_blaster = {FRAME_fired02a, FRAME_fired20, widow_frames_attack_blaster, NULL};
                    606: 
                    607: mframe_t widow_frames_attack_post_blaster [] =
                    608: {
                    609:        ai_charge,      0,      NULL,
                    610:        ai_charge,      0,      NULL
                    611: };
                    612: mmove_t widow_move_attack_post_blaster = {FRAME_fired21, FRAME_fired22, widow_frames_attack_post_blaster, widow_run};
                    613: 
                    614: mframe_t widow_frames_attack_post_blaster_r [] =
                    615: {
                    616:        ai_charge,      -2,     NULL,
                    617:        ai_charge,      -10,    NULL,
                    618:        ai_charge,      -2,     NULL,
                    619:        ai_charge,      0,      NULL,
                    620:        ai_charge,      0,      widow_start_run_12
                    621: };
                    622: mmove_t widow_move_attack_post_blaster_r = {FRAME_transa01, FRAME_transa05, widow_frames_attack_post_blaster_r, NULL};
                    623: 
                    624: mframe_t widow_frames_attack_post_blaster_l [] =
                    625: {
                    626:        ai_charge,      0,      NULL,
                    627:        ai_charge,      14,     NULL,
                    628:        ai_charge,      -2,     NULL,
                    629:        ai_charge,      10,     NULL,
                    630:        ai_charge,      10,     widow_start_run_12
                    631: };
                    632: mmove_t widow_move_attack_post_blaster_l = {FRAME_transb01, FRAME_transb05, widow_frames_attack_post_blaster_l, NULL};
                    633: 
                    634: mmove_t widow_move_attack_rail;
                    635: mmove_t widow_move_attack_rail_l;
                    636: mmove_t widow_move_attack_rail_r;
                    637: 
                    638: void WidowRail (edict_t *self)
                    639: {
                    640:        vec3_t  start;
                    641:        vec3_t  dir;
                    642:        vec3_t  forward, right;
                    643:        int             flash;
                    644: 
                    645: //     gi.dprintf ("railing!\n");
                    646:        AngleVectors (self->s.angles, forward, right, NULL);
                    647: 
                    648:        if (self->monsterinfo.currentmove == &widow_move_attack_rail)
                    649:                flash = MZ2_WIDOW_RAIL;
                    650:        else if (self->monsterinfo.currentmove == &widow_move_attack_rail_l)
                    651:        {
                    652:                flash = MZ2_WIDOW_RAIL_LEFT;
                    653:        }
                    654:        else if (self->monsterinfo.currentmove == &widow_move_attack_rail_r)
                    655:        {
                    656:                flash = MZ2_WIDOW_RAIL_RIGHT;
                    657:        }
                    658: 
                    659:        G_ProjectSource (self->s.origin, monster_flash_offset[flash], forward, right, start);
                    660: 
                    661:        // calc direction to where we targeted
                    662:        VectorSubtract (self->pos1, start, dir);
                    663:        VectorNormalize (dir);
                    664: 
                    665:        monster_fire_railgun (self, start, dir, WIDOW_RAIL_DAMAGE*widow_damage_multiplier, 100, flash);
                    666:        self->timestamp = level.time + RAIL_TIME;
                    667: }
                    668: 
                    669: void WidowSaveLoc (edict_t *self)
                    670: {
                    671:        VectorCopy (self->enemy->s.origin, self->pos1); //save for aiming the shot
                    672:        self->pos1[2] += self->enemy->viewheight;
                    673: };
                    674: 
                    675: void widow_start_rail (edict_t *self)
                    676: {
                    677:        self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
                    678: }
                    679: 
                    680: void widow_rail_done (edict_t *self)
                    681: {
                    682:        self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
                    683: }
                    684: 
                    685: mframe_t widow_frames_attack_pre_rail [] =
                    686: {
                    687:        ai_charge,      0,      widow_start_rail,
                    688:        ai_charge,      0,      NULL,
                    689:        ai_charge,      0,      NULL,
                    690:        ai_charge,      0,      widow_attack_rail
                    691: };
                    692: mmove_t widow_move_attack_pre_rail = {FRAME_transc01, FRAME_transc04, widow_frames_attack_pre_rail, NULL};
                    693: 
                    694: mframe_t widow_frames_attack_rail [] =
                    695: {
                    696:        ai_charge, 0, NULL,
                    697:        ai_charge, 0, NULL,
                    698:        ai_charge, 0, WidowSaveLoc,
                    699:        ai_charge, -10, WidowRail,
                    700:        ai_charge, 0, NULL,
                    701:        ai_charge, 0, NULL,
                    702:        ai_charge, 0, NULL,
                    703:        ai_charge, 0, NULL,
                    704:        ai_charge, 0, widow_rail_done
                    705: };
                    706: mmove_t widow_move_attack_rail = {FRAME_firea01, FRAME_firea09, widow_frames_attack_rail, widow_run};
                    707: 
                    708: mframe_t widow_frames_attack_rail_r [] =
                    709: {
                    710:        ai_charge, 0, NULL,
                    711:        ai_charge, 0, NULL,
                    712:        ai_charge, 0, WidowSaveLoc,
                    713:        ai_charge, -10, WidowRail,
                    714:        ai_charge, 0, NULL,
                    715:        ai_charge, 0, NULL,
                    716:        ai_charge, 0, NULL,
                    717:        ai_charge, 0, NULL,
                    718:        ai_charge, 0, widow_rail_done
                    719: };
                    720: mmove_t widow_move_attack_rail_r = {FRAME_fireb01, FRAME_fireb09, widow_frames_attack_rail_r, widow_run};
                    721: 
                    722: mframe_t widow_frames_attack_rail_l [] =
                    723: {
                    724:        ai_charge, 0, NULL,
                    725:        ai_charge, 0, NULL,
                    726:        ai_charge, 0, WidowSaveLoc,
                    727:        ai_charge, -10, WidowRail,
                    728:        ai_charge, 0, NULL,
                    729:        ai_charge, 0, NULL,
                    730:        ai_charge, 0, NULL,
                    731:        ai_charge, 0, NULL,
                    732:        ai_charge, 0, widow_rail_done
                    733: };
                    734: mmove_t widow_move_attack_rail_l = {FRAME_firec01, FRAME_firec09, widow_frames_attack_rail_l, widow_run};
                    735: 
                    736: void widow_attack_rail (edict_t *self)
                    737: {
                    738:        float   enemy_angle;
                    739: //     gi.dprintf ("going to the rail!\n");
                    740: 
                    741:        enemy_angle = target_angle (self);
                    742: 
                    743:        if (enemy_angle < -15)
                    744:                self->monsterinfo.currentmove = &widow_move_attack_rail_l;
                    745:        else if (enemy_angle > 15)
                    746:                self->monsterinfo.currentmove = &widow_move_attack_rail_r;
                    747:        else
                    748:                self->monsterinfo.currentmove = &widow_move_attack_rail;
                    749: }
                    750: 
                    751: void widow_start_spawn (edict_t *self)
                    752: {
                    753:        self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
                    754: }
                    755: 
                    756: void widow_done_spawn (edict_t *self)
                    757: {
                    758:        self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
                    759: }
                    760: 
                    761: mframe_t widow_frames_spawn [] =
                    762: {
                    763:        ai_charge,      0,      NULL,                                           //1
                    764:        ai_charge,      0,      NULL,
                    765:        ai_charge,      0,      NULL,
                    766:        ai_charge,      0,      widow_start_spawn,
                    767:        ai_charge,      0,      NULL,                                           //5
                    768:        ai_charge,      0,      WidowBlaster,                           //6
                    769:        ai_charge,      0,      widow_ready_spawn,                      //7
                    770:        ai_charge,      0,      WidowBlaster,
                    771:        ai_charge,      0,      WidowBlaster,                           //9
                    772:        ai_charge,      0,      widow_spawn_check,
                    773:        ai_charge,      0,      WidowBlaster,                           //11
                    774:        ai_charge,      0,      WidowBlaster,
                    775:        ai_charge,      0,      WidowBlaster,                           //13
                    776:        ai_charge,      0,      NULL,
                    777:        ai_charge,      0,      NULL,
                    778:        ai_charge,      0,      NULL,
                    779:        ai_charge,      0,      NULL,
                    780:        ai_charge,      0,      widow_done_spawn
                    781: };
                    782: mmove_t widow_move_spawn = {FRAME_spawn01, FRAME_spawn18, widow_frames_spawn, widow_run};
                    783: 
                    784: mframe_t widow_frames_pain_heavy [] =
                    785: {
                    786:        ai_move,        0,      NULL,
                    787:        ai_move,        0,      NULL,
                    788:        ai_move,        0,      NULL,
                    789:        ai_move,        0,      NULL,
                    790:        ai_move,        0,      NULL,
                    791:        ai_move,        0,      NULL,
                    792:        ai_move,        0,      NULL,
                    793:        ai_move,        0,      NULL,
                    794:        ai_move,        0,      NULL,
                    795:        ai_move,        0,      NULL,
                    796:        ai_move,        0,      NULL,
                    797:        ai_move,        0,      NULL,
                    798:        ai_move,        0,      NULL
                    799: };
                    800: mmove_t widow_move_pain_heavy = {FRAME_pain01, FRAME_pain13, widow_frames_pain_heavy, widow_run};
                    801: 
                    802: mframe_t widow_frames_pain_light [] =
                    803: {
                    804:        ai_move,        0,      NULL,
                    805:        ai_move,        0,      NULL,
                    806:        ai_move,        0,      NULL
                    807: };
                    808: mmove_t widow_move_pain_light = {FRAME_pain201, FRAME_pain203, widow_frames_pain_light, widow_run};
                    809: 
                    810: void spawn_out_start (edict_t *self)
                    811: {
                    812:        vec3_t startpoint,f,r,u;
                    813:        self->wait = level.time + 2.0;
                    814: 
                    815: //     gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0);
                    816:        AngleVectors (self->s.angles, f, r, u);
                    817: 
                    818:        G_ProjectSource2 (self->s.origin, beameffects[0], f, r, u, startpoint);
                    819:        gi.WriteByte (svc_temp_entity);
                    820:        gi.WriteByte (TE_WIDOWBEAMOUT);
                    821:        gi.WriteShort (20001);
                    822:        gi.WritePosition (startpoint);
                    823:        gi.multicast (startpoint, MULTICAST_ALL);
                    824: 
                    825:        G_ProjectSource2 (self->s.origin, beameffects[1], f, r, u, startpoint);
                    826:        gi.WriteByte (svc_temp_entity);
                    827:        gi.WriteByte (TE_WIDOWBEAMOUT);
                    828:        gi.WriteShort (20002);
                    829:        gi.WritePosition (startpoint);
                    830:        gi.multicast (startpoint, MULTICAST_ALL);
                    831: }
                    832: 
                    833: void spawn_out_do (edict_t *self)
                    834: {
                    835:        vec3_t startpoint,f,r,u;
                    836: 
                    837:        AngleVectors (self->s.angles, f, r, u);
                    838:        G_ProjectSource2 (self->s.origin, beameffects[0], f, r, u, startpoint);
                    839:        gi.WriteByte (svc_temp_entity);
                    840:        gi.WriteByte (TE_WIDOWSPLASH);
                    841:        gi.WritePosition (startpoint);
                    842:        gi.multicast (startpoint, MULTICAST_ALL);
                    843: 
                    844:        G_ProjectSource2 (self->s.origin, beameffects[1], f, r, u, startpoint);
                    845:        gi.WriteByte (svc_temp_entity);
                    846:        gi.WriteByte (TE_WIDOWSPLASH);
                    847:        gi.WritePosition (startpoint);
                    848:        gi.multicast (startpoint, MULTICAST_ALL);
                    849: 
                    850:        VectorCopy (self->s.origin, startpoint);
                    851:        startpoint[2] += 36;
                    852:        gi.WriteByte (svc_temp_entity);
                    853:        gi.WriteByte (TE_BOSSTPORT);
                    854:        gi.WritePosition (startpoint);
                    855:        gi.multicast (startpoint, MULTICAST_PVS);
                    856: 
                    857:        Widowlegs_Spawn (self->s.origin, self->s.angles);
                    858:        
                    859:        G_FreeEdict (self);
                    860: }
                    861: 
                    862: mframe_t widow_frames_death [] =
                    863: {
                    864:        ai_move,        0,      NULL,
                    865:        ai_move,        0,      NULL,
                    866:        ai_move,        0,      NULL,
                    867:        ai_move,        0,      NULL,
                    868:        ai_move,        0,      NULL,           //5
                    869:        ai_move,        0,      NULL,
                    870:        ai_move,        0,      NULL,
                    871:        ai_move,        0,      NULL,
                    872:        ai_move,        0,      NULL,
                    873:        ai_move,        0,      spawn_out_start,        //10
                    874:        ai_move,        0,      NULL,           
                    875:        ai_move,        0,      NULL,           
                    876:        ai_move,        0,      NULL,           
                    877:        ai_move,        0,      NULL,           
                    878:        ai_move,        0,      NULL,                           //15    
                    879:        ai_move,        0,      NULL,           
                    880:        ai_move,        0,      NULL,           
                    881:        ai_move,        0,      NULL,           
                    882:        ai_move,        0,      NULL,           
                    883:        ai_move,        0,      NULL,                           //20    
                    884:        ai_move,        0,      NULL,           
                    885:        ai_move,        0,      NULL,           
                    886:        ai_move,        0,      NULL,           
                    887:        ai_move,        0,      NULL,           
                    888:        ai_move,        0,      NULL,                           //25
                    889:        ai_move,        0,      NULL,           
                    890:        ai_move,        0,      NULL,           
                    891:        ai_move,        0,      NULL,           
                    892:        ai_move,        0,      NULL,           
                    893:        ai_move,        0,      NULL,                           //30    
                    894:        ai_move,        0,      spawn_out_do
                    895: };
                    896: mmove_t widow_move_death = {FRAME_death01, FRAME_death31, widow_frames_death, NULL};
                    897: 
                    898: void widow_attack_kick (edict_t *self)
                    899: {
                    900:        vec3_t  aim;
                    901: 
                    902: //     VectorSet (aim, MELEE_DISTANCE, 0, 4);
                    903:        VectorSet (aim, 100, 0, 4);
                    904:        if (self->enemy->groundentity)
                    905:                fire_hit (self, aim, (50 + (rand() % 6)), 500);
                    906:        else    // not as much kick if they're in the air .. makes it harder to land on her head
                    907:                fire_hit (self, aim, (50 + (rand() % 6)), 250);
                    908: 
                    909: }
                    910: 
                    911: mframe_t widow_frames_attack_kick [] =
                    912: {
                    913:        ai_move, 0, NULL,
                    914:        ai_move, 0, NULL,
                    915:        ai_move, 0, NULL,
                    916:        ai_move, 0, widow_attack_kick,
                    917:        ai_move, 0, NULL,                               // 5
                    918:        ai_move, 0, NULL,
                    919:        ai_move, 0, NULL,
                    920:        ai_move, 0, NULL
                    921: };
                    922: 
                    923: mmove_t widow_move_attack_kick = {FRAME_kick01, FRAME_kick08, widow_frames_attack_kick, widow_run};
                    924: 
                    925: void widow_stand (edict_t *self)
                    926: {
                    927: //     gi.dprintf ("widow stand\n");
                    928:        self->monsterinfo.currentmove = &widow_move_stand;
                    929: }
                    930: 
                    931: void widow_run (edict_t *self)
                    932: {
                    933:        self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
                    934: 
                    935:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
                    936:                self->monsterinfo.currentmove = &widow_move_stand;
                    937:        else
                    938:                self->monsterinfo.currentmove = &widow_move_run;
                    939: }
                    940: 
                    941: void widow_walk (edict_t *self)
                    942: {
                    943:        self->monsterinfo.currentmove = &widow_move_walk;
                    944: }
                    945: 
                    946: void widow_attack (edict_t *self)
                    947: {
                    948:        float   luck;
                    949:        qboolean rail_frames = false, blaster_frames = false, blocked = false, anger = false;
                    950: 
                    951:        if (self->monsterinfo.aiflags & AI_BLOCKED)
                    952:        {
                    953:                blocked = true;
                    954:                self->monsterinfo.aiflags &= ~AI_BLOCKED;
                    955:        }
                    956:        
                    957:        if (self->monsterinfo.aiflags & AI_TARGET_ANGER)
                    958:        {
                    959:                anger = true;
                    960:                self->monsterinfo.aiflags &= ~AI_TARGET_ANGER;
                    961:        }
                    962: 
                    963:        if ((!self->enemy) || (!self->enemy->inuse))
                    964:                return;
                    965: 
                    966:        if (self->bad_area)
                    967:        {
                    968:                if ((random() < 0.1) || (level.time < self->timestamp))
                    969:                        self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
                    970:                else
                    971:                {
                    972:                        gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
                    973:                        self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
                    974:                }
                    975:                return;
                    976:        }
                    977: 
                    978:        // frames FRAME_walk13, FRAME_walk01, FRAME_walk02, FRAME_walk03 are rail gun start frames
                    979:        // frames FRAME_walk09, FRAME_walk10, FRAME_walk11, FRAME_walk12 are spawn & blaster start frames
                    980: 
                    981:        if ((self->s.frame == FRAME_walk13) || ((self->s.frame >= FRAME_walk01) && (self->s.frame <= FRAME_walk03)))
                    982:                rail_frames = true;
                    983: 
                    984:        if ((self->s.frame >= FRAME_walk09) && (self->s.frame <= FRAME_walk12))
                    985:                blaster_frames = true;
                    986: 
                    987:        WidowCalcSlots(self);
                    988: 
                    989:        // if we can't see the target, spawn stuff regardless of frame
                    990:        if ((self->monsterinfo.attack_state == AS_BLIND) && (SELF_SLOTS_LEFT >= 2))
                    991:        {
                    992:                if ((g_showlogic) && (g_showlogic->value))
                    993:                        gi.dprintf ("attacking blind!\n");
                    994:                self->monsterinfo.currentmove = &widow_move_spawn;
                    995:                return;
                    996:        }
                    997: 
                    998:        // accept bias towards spawning regardless of frame
                    999:        if (blocked && (SELF_SLOTS_LEFT >= 2))
                   1000:        {
                   1001:                self->monsterinfo.currentmove = &widow_move_spawn;
                   1002:                return;
                   1003:        }
                   1004: 
                   1005:        if ((realrange(self, self->enemy) > 300) && (!anger) && (random() < 0.5)  && (!blocked))
                   1006:        {
                   1007:                self->monsterinfo.currentmove = &widow_move_run_attack;
                   1008:                return;
                   1009:        }
                   1010: 
                   1011:        if (blaster_frames)
                   1012:        {
                   1013: //             gi.dprintf ("blaster frame %2.2f <= %2.2f\n", self->monsterinfo.pausetime + BLASTER_TIME, level.time);
                   1014:                if (SELF_SLOTS_LEFT >= 2)
                   1015:                {
                   1016:                        self->monsterinfo.currentmove = &widow_move_spawn;
                   1017:                        return;
                   1018:                }
                   1019:                else if (self->monsterinfo.pausetime + BLASTER_TIME <= level.time)
                   1020:                {
                   1021:                        self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
                   1022:                        return;
                   1023:                }
                   1024:        }
                   1025: 
                   1026:        if (rail_frames)
                   1027:        {
                   1028: //             gi.dprintf ("rail frame %2.2f - %2.2f\n", level.time, self->timestamp);
                   1029:                if (!(level.time < self->timestamp))
                   1030:                {
                   1031:                        gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
                   1032:                        self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
                   1033:                }
                   1034:        }
                   1035: 
                   1036:        if ((rail_frames) || (blaster_frames))
                   1037:                return;
                   1038: 
                   1039: //     if ((g_showlogic) && (g_showlogic->value))
                   1040: //             gi.dprintf ("widow: unknown start frame, picking randomly\n");
                   1041: 
                   1042:        luck = random();
                   1043:        if (SELF_SLOTS_LEFT >= 2)
                   1044:        {
                   1045:                if ((luck <= 0.40) && (self->monsterinfo.pausetime + BLASTER_TIME <= level.time))
                   1046:                        self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
                   1047:                else if ((luck <= 0.7) && !(level.time < self->timestamp))
                   1048:                {
                   1049:                        gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
                   1050:                        self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
                   1051:                }
                   1052:                else
                   1053:                        self->monsterinfo.currentmove = &widow_move_spawn;
                   1054:        }
                   1055:        else
                   1056:        {
                   1057:                if (level.time < self->timestamp)
                   1058:                        self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
                   1059:                else if ((luck <= 0.50) || (level.time + BLASTER_TIME >= self->monsterinfo.pausetime))
                   1060:                {
                   1061:                        gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
                   1062:                        self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
                   1063:                }
                   1064:                else // holdout to blaster
                   1065:                        self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
                   1066:        }
                   1067: }
                   1068: /*
                   1069: void widow_attack (edict_t *self)
                   1070: {
                   1071:        float   range, luck;
                   1072: 
                   1073: //     gi.dprintf ("widow attack\n");
                   1074:        
                   1075:        if ((!self->enemy) || (!self->enemy->inuse))
                   1076:                return;
                   1077: 
                   1078:        if (self->bad_area)
                   1079:        {
                   1080:                if ((random() < 0.1) || (level.time < self->timestamp))
                   1081:                        self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
                   1082:                else
                   1083:                {
                   1084:                        gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
                   1085:                        self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
                   1086:                }
                   1087:                return;
                   1088:        }
                   1089: 
                   1090:        // if we can't see the target, spawn stuff
                   1091:        if ((self->monsterinfo.attack_state == AS_BLIND) && (blaster_frames))
                   1092:        {
                   1093:                self->monsterinfo.currentmove = &widow_move_spawn;
                   1094:                return;
                   1095:        }
                   1096: 
                   1097:        range = realrange (self, self->enemy);
                   1098: 
                   1099:        if (range < 600)
                   1100:        {
                   1101:                luck = random();
                   1102:                if (SLOTS_LEFT >= 2)
                   1103:                {
                   1104:                        if (luck <= 0.40)
                   1105:                                self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
                   1106:                        else if ((luck <= 0.7) && !(level.time < self->timestamp))
                   1107:                        {
                   1108:                                gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
                   1109:                                self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
                   1110:                        }
                   1111:                        else
                   1112:                                self->monsterinfo.currentmove = &widow_move_spawn;
                   1113:                }
                   1114:                else
                   1115:                {
                   1116:                        if ((luck <= 0.50) || (level.time < self->timestamp))
                   1117:                                self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
                   1118:                        else
                   1119:                        {
                   1120:                                gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
                   1121:                                self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
                   1122:                        }
                   1123:                }
                   1124:        }
                   1125:        else
                   1126:        {
                   1127:                luck = random();
                   1128:                if (SLOTS_LEFT >= 2)
                   1129:                {
                   1130:                        if (luck < 0.3)
                   1131:                                self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
                   1132:                        else if ((luck < 0.65) || (level.time < self->timestamp))
                   1133:                                self->monsterinfo.currentmove = &widow_move_spawn;
                   1134:                        else
                   1135:                        {
                   1136:                                gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
                   1137:                                self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
                   1138:                        }
                   1139:                }
                   1140:                else
                   1141:                {
                   1142:                        if ((luck < 0.45) || (level.time < self->timestamp))
                   1143:                                self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
                   1144:                        else
                   1145:                        {
                   1146:                                gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
                   1147:                                self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
                   1148:                        }
                   1149:                }
                   1150:        }
                   1151: }
                   1152: */
                   1153: void widow_attack_blaster (edict_t *self)
                   1154: {
                   1155:        self->monsterinfo.pausetime = level.time + 1.0 + (2.0*random());
                   1156: //     self->monsterinfo.pausetime = level.time + 100;
                   1157: //     self->plat2flags = 0;
                   1158:        self->monsterinfo.currentmove = &widow_move_attack_blaster;
                   1159:        self->monsterinfo.nextframe = WidowTorso (self);
                   1160: }
                   1161: 
                   1162: void widow_reattack_blaster (edict_t *self)
                   1163: {
                   1164:        WidowBlaster(self);
                   1165: 
                   1166:        if ((g_showlogic) && (g_showlogic->value))
                   1167:        {
                   1168:                if (self->monsterinfo.currentmove == &widow_move_attack_post_blaster_l)
                   1169:                        gi.dprintf ("pulling left!\n");
                   1170:                if (self->monsterinfo.currentmove == &widow_move_attack_post_blaster_r)
                   1171:                        gi.dprintf ("pulling right!\n");
                   1172:        }
                   1173: 
                   1174: //     self->monsterinfo.currentmove = &widow_move_attack_blaster;
                   1175: //             self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
                   1176: //     return;
                   1177:        // if WidowBlaster bailed us out of the frames, just bail
                   1178:        if ((self->monsterinfo.currentmove == &widow_move_attack_post_blaster_l) ||
                   1179:                (self->monsterinfo.currentmove == &widow_move_attack_post_blaster_r))
                   1180:                return;
                   1181: 
                   1182:        // if we're not done with the attack, don't leave the sequence
                   1183:        if (self->monsterinfo.pausetime >= level.time)
                   1184:                return;
                   1185: 
                   1186:        self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
                   1187: 
                   1188:        self->monsterinfo.currentmove = &widow_move_attack_post_blaster;
                   1189: }
                   1190: /*
                   1191:        if ( infront(self, self->enemy) )
                   1192:                if (random() <= 0.5)
                   1193:                        if ((random() < 0.7) || (SLOTS_LEFT <= 1))
                   1194:                                self->monsterinfo.currentmove = &widow_move_attack_blaster;
                   1195:                        else
                   1196:                                self->monsterinfo.currentmove = &widow_move_spawn;
                   1197:                else
                   1198:                        self->monsterinfo.currentmove = &widow_move_attack_post_blaster;
                   1199:        else
                   1200:                self->monsterinfo.currentmove = &widow_move_attack_post_blaster;
                   1201: }
                   1202: */
                   1203: 
                   1204: 
                   1205: void widow_pain (edict_t *self, edict_t *other, float kick, int damage)
                   1206: {
                   1207:        if (self->health < (self->max_health / 2))
                   1208:                self->s.skinnum = 1;
                   1209: 
                   1210:        if (skill->value == 3)
                   1211:                return;         // no pain anims in nightmare
                   1212: 
                   1213:        if (level.time < self->pain_debounce_time)
                   1214:                return;
                   1215: 
                   1216:        if (self->monsterinfo.pausetime == 100000000)
                   1217:                self->monsterinfo.pausetime = 0;
                   1218: 
                   1219:        self->pain_debounce_time = level.time + 3;
                   1220: 
                   1221:        if (damage < 15)
                   1222:        {
                   1223:                gi.sound (self, CHAN_VOICE, sound_pain3, 1, ATTN_NONE, 0);
                   1224:        }
                   1225:        else if (damage < 75)
                   1226:        {
                   1227:                if ((skill->value < 3) && (random() < (0.6 - (0.2*((float)skill->value)))))
                   1228:                {
                   1229:                        self->monsterinfo.currentmove = &widow_move_pain_light;
                   1230:                        self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
                   1231:                }
                   1232:                gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NONE, 0);
                   1233:        }
                   1234:        else 
                   1235:        {
                   1236:                if ((skill->value < 3) && (random() < (0.75 - (0.1*((float)skill->value)))))
                   1237:                {
                   1238:                        self->monsterinfo.currentmove = &widow_move_pain_heavy;
                   1239:                        self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
                   1240:                }
                   1241:                gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NONE, 0);
                   1242:        }
                   1243: }
                   1244: 
                   1245: void widow_dead (edict_t *self)
                   1246: {
                   1247:        VectorSet (self->mins, -56, -56, 0);
                   1248:        VectorSet (self->maxs, 56, 56, 80);
                   1249:        self->movetype = MOVETYPE_TOSS;
                   1250:        self->svflags |= SVF_DEADMONSTER;
                   1251:        self->nextthink = 0;
                   1252:        gi.linkentity (self);
                   1253: }
                   1254: 
                   1255: void widow_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
                   1256: {
                   1257:        self->deadflag = DEAD_DEAD;
                   1258:        self->takedamage = DAMAGE_NO;
                   1259:        self->count = 0;
                   1260:        self->monsterinfo.quad_framenum = 0;
                   1261:        self->monsterinfo.double_framenum = 0;
                   1262:        self->monsterinfo.invincible_framenum = 0;
                   1263:        self->monsterinfo.currentmove = &widow_move_death;
                   1264: }
                   1265: 
                   1266: void widow_melee (edict_t *self)
                   1267: {
                   1268: //     monster_done_dodge (self);
                   1269:        self->monsterinfo.currentmove = &widow_move_attack_kick;
                   1270: }
                   1271: 
                   1272: void WidowGoinQuad (edict_t *self, float framenum)
                   1273: {
                   1274:        self->monsterinfo.quad_framenum = framenum;
                   1275:        widow_damage_multiplier = 4;
                   1276: }
                   1277: 
                   1278: void WidowDouble (edict_t *self, float framenum)
                   1279: {
                   1280:        self->monsterinfo.double_framenum = framenum;
                   1281:        widow_damage_multiplier = 2;
                   1282: }
                   1283: 
                   1284: void WidowPent (edict_t *self, float framenum)
                   1285: {
                   1286:        self->monsterinfo.invincible_framenum = framenum;
                   1287: }
                   1288: 
                   1289: void WidowPowerArmor (edict_t *self)
                   1290: {
                   1291:        self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
                   1292:        // I don't like this, but it works
                   1293:        if (self->monsterinfo.power_armor_power <= 0)
                   1294:                self->monsterinfo.power_armor_power += 250 * skill->value;
                   1295: }
                   1296: 
                   1297: void WidowRespondPowerup (edict_t *self, edict_t *other)
                   1298: {
                   1299:        if (other->s.effects & EF_QUAD)
                   1300:        {
                   1301:                if (skill->value == 1)
                   1302:                        WidowDouble (self, other->client->quad_framenum);
                   1303:                else if (skill->value == 2)
                   1304:                        WidowGoinQuad (self, other->client->quad_framenum);
                   1305:                else if (skill->value == 3)
                   1306:                {
                   1307:                        WidowGoinQuad (self, other->client->quad_framenum);
                   1308:                        WidowPowerArmor (self);
                   1309:                }
                   1310:        }
                   1311:        else if (other->s.effects & EF_DOUBLE)
                   1312:        {
                   1313:                if (skill->value == 2)
                   1314:                        WidowDouble (self, other->client->double_framenum);
                   1315:                else if (skill->value == 3)
                   1316:                {
                   1317:                        WidowDouble (self, other->client->double_framenum);
                   1318:                        WidowPowerArmor (self);
                   1319:                }
                   1320:        }
                   1321:        else
                   1322:                widow_damage_multiplier = 1;
                   1323: 
                   1324:        if (other->s.effects & EF_PENT)
                   1325:        {
                   1326:                if (skill->value == 1)
                   1327:                        WidowPowerArmor (self);
                   1328:                else if (skill->value == 2)
                   1329:                        WidowPent (self, other->client->invincible_framenum);
                   1330:                else if (skill->value == 3)
                   1331:                {
                   1332:                        WidowPent (self, other->client->invincible_framenum);
                   1333:                        WidowPowerArmor (self);
                   1334:                }
                   1335:        }
                   1336: }
                   1337: 
                   1338: void WidowPowerups (edict_t *self)
                   1339: {
                   1340:        int player;
                   1341:        edict_t *ent;
                   1342: 
                   1343:        if (!(coop && coop->value))
                   1344:        {
                   1345:                WidowRespondPowerup (self, self->enemy);
                   1346:        }
                   1347:        else
                   1348:        {
                   1349:                // in coop, check for pents, then quads, then doubles
                   1350:                for (player = 1; player <= game.maxclients; player++)
                   1351:                {
                   1352:                        ent = &g_edicts[player];
                   1353:                        if (!ent->inuse)
                   1354:                                continue;
                   1355:                        if (!ent->client)
                   1356:                                continue;
                   1357:                        if (ent->s.effects & EF_PENT)
                   1358:                        {
                   1359:                                WidowRespondPowerup (self, ent);
                   1360:                                return;
                   1361:                        }
                   1362:                }
                   1363: 
                   1364:                for (player = 1; player <= game.maxclients; player++)
                   1365:                {
                   1366:                        ent = &g_edicts[player];
                   1367:                        if (!ent->inuse)
                   1368:                                continue;
                   1369:                        if (!ent->client)
                   1370:                                continue;
                   1371:                        if (ent->s.effects & EF_QUAD)
                   1372:                        {
                   1373:                                WidowRespondPowerup (self, ent);
                   1374:                                return;
                   1375:                        }
                   1376:                }
                   1377: 
                   1378:                for (player = 1; player <= game.maxclients; player++)
                   1379:                {
                   1380:                        ent = &g_edicts[player];
                   1381:                        if (!ent->inuse)
                   1382:                                continue;
                   1383:                        if (!ent->client)
                   1384:                                continue;
                   1385:                        if (ent->s.effects & EF_DOUBLE)
                   1386:                        {
                   1387:                                WidowRespondPowerup (self, ent);
                   1388:                                return;
                   1389:                        }
                   1390:                }
                   1391:        }
                   1392: }
                   1393: 
                   1394: qboolean Widow_CheckAttack (edict_t *self)
                   1395: {
                   1396:        vec3_t  spot1, spot2;
                   1397:        vec3_t  temp;
                   1398:        float   chance;
                   1399:        trace_t tr;
                   1400:        qboolean        enemy_infront;
                   1401:        int                     enemy_range;
                   1402:        float           enemy_yaw;
                   1403:        float           real_enemy_range;
                   1404: 
                   1405:        if (!self->enemy)
                   1406:                return false;
                   1407: 
                   1408:        WidowPowerups(self);
                   1409: 
                   1410:        if (self->monsterinfo.currentmove == &widow_move_run)
                   1411:        {
                   1412:                // if we're in run, make sure we're in a good frame for attacking before doing anything else
                   1413:                // frames 1,2,3,9,10,11,13 good to fire
                   1414:                switch (self->s.frame)
                   1415:                {
                   1416:                        case FRAME_walk04:
                   1417:                        case FRAME_walk05:
                   1418:                        case FRAME_walk06:
                   1419:                        case FRAME_walk07:
                   1420:                        case FRAME_walk08:
                   1421:                        case FRAME_walk12:
                   1422:                                {
                   1423: //                                     if ((g_showlogic) && (g_showlogic->value))
                   1424: //                                             gi.dprintf ("Not in good walk frame (%d), not attacking\n", (self->s.frame - FRAME_walk01+1));
                   1425:                                        return false;
                   1426:                                }
                   1427:                        default:
                   1428:                                break;
                   1429:                }
                   1430:        }
                   1431: 
                   1432:        // give a LARGE bias to spawning things when we have room
                   1433:        // use AI_BLOCKED as a signal to attack to spawn
                   1434:        if ((random() < 0.8) && (SELF_SLOTS_LEFT >= 2) && (realrange(self, self->enemy) > 150))
                   1435:        {
                   1436:                self->monsterinfo.aiflags |= AI_BLOCKED;
                   1437:                self->monsterinfo.attack_state = AS_MISSILE;
                   1438:                return true;
                   1439:        }
                   1440: 
                   1441:        if (self->enemy->health > 0)
                   1442:        {
                   1443:        // see if any entities are in the way of the shot
                   1444:                VectorCopy (self->s.origin, spot1);
                   1445:                spot1[2] += self->viewheight;
                   1446:                VectorCopy (self->enemy->s.origin, spot2);
                   1447:                spot2[2] += self->enemy->viewheight;
                   1448: 
                   1449:                tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA);
                   1450: 
                   1451:                // do we have a clear shot?
                   1452:                if (tr.ent != self->enemy)
                   1453:                {       
                   1454:                        // go ahead and spawn stuff if we're mad a a client
                   1455:                        if (self->enemy->client && SELF_SLOTS_LEFT >= 2)
                   1456:                        {
                   1457:                                self->monsterinfo.attack_state = AS_BLIND;
                   1458:                                return true;
                   1459:                        }
                   1460:                                
                   1461:                        // PGM - we want them to go ahead and shoot at info_notnulls if they can.
                   1462:                        if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0)                //PGM
                   1463:                                return false;
                   1464:                }
                   1465:        }
                   1466:        
                   1467:        enemy_infront = infront(self, self->enemy);
                   1468: 
                   1469:        enemy_range = range(self, self->enemy);
                   1470:        VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
                   1471:        enemy_yaw = vectoyaw2(temp);
                   1472: 
                   1473:        self->ideal_yaw = enemy_yaw;
                   1474: 
                   1475:        real_enemy_range = realrange (self, self->enemy);
                   1476: 
                   1477:        if (g_showlogic->value)
                   1478:                gi.dprintf ("range = %2.2f\n", real_enemy_range);
                   1479: 
                   1480:        // melee attack
                   1481: //     if (enemy_range == RANGE_MELEE)
                   1482:        if (real_enemy_range <= (MELEE_DISTANCE+20))
                   1483:        {
                   1484:                // don't always melee in easy mode
                   1485:                if (skill->value == 0 && (rand()&3) )
                   1486:                        return false;
                   1487:                if (self->monsterinfo.melee)
                   1488:                        self->monsterinfo.attack_state = AS_MELEE;
                   1489:                else
                   1490:                        self->monsterinfo.attack_state = AS_MISSILE;
                   1491:                return true;
                   1492:        }
                   1493: 
                   1494:        if (level.time < self->monsterinfo.attack_finished)
                   1495:                return false;
                   1496:                
                   1497:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
                   1498:        {
                   1499:                chance = 0.4;
                   1500:        }
                   1501:        else if (enemy_range == RANGE_MELEE)
                   1502:        {
                   1503:                chance = 0.8;
                   1504:        }
                   1505:        else if (enemy_range == RANGE_NEAR)
                   1506:        {
                   1507:                chance = 0.7;
                   1508:        }
                   1509:        else if (enemy_range == RANGE_MID)
                   1510:        {
                   1511:                chance = 0.6;
                   1512:        }
                   1513:        else if (enemy_range == RANGE_FAR)
                   1514:        {
                   1515:                chance = 0.5;
                   1516:        }
                   1517: 
                   1518:        // PGM - go ahead and shoot every time if it's a info_notnull
                   1519:        if ((random () < chance) || (self->enemy->solid == SOLID_NOT))
                   1520:        {
                   1521:                self->monsterinfo.attack_state = AS_MISSILE;
                   1522:                return true;
                   1523:        }
                   1524: 
                   1525:        return false;
                   1526: }
                   1527: 
                   1528: qboolean widow_blocked (edict_t *self, float dist)
                   1529: {
                   1530:        // if we get blocked while we're in our run/attack mode, turn on a meaningless (in this context)AI flag, 
                   1531:        // and call attack to get a new attack sequence.  make sure to turn it off when we're done.
                   1532:        //
                   1533:        // I'm using AI_TARGET_ANGER for this purpose
                   1534: 
                   1535:        if (self->monsterinfo.currentmove == &widow_move_run_attack)
                   1536:        {
                   1537:                self->monsterinfo.aiflags |= AI_TARGET_ANGER;
                   1538:                if (self->monsterinfo.checkattack(self))
                   1539:                        self->monsterinfo.attack(self);
                   1540:                else
                   1541:                        self->monsterinfo.run(self);
                   1542:                return true;
                   1543:        }
                   1544: 
                   1545:        if(blocked_checkshot (self, 0.25 + (0.05 * skill->value) ))
                   1546:                return true;
                   1547: 
                   1548: /*
                   1549:        if(blocked_checkjump (self, dist, 192, 40))
                   1550:        {
                   1551:                infantry_jump(self);
                   1552:                return true;
                   1553:        }
                   1554: 
                   1555:        if(blocked_checkplat (self, dist))
                   1556:                return true;
                   1557: */
                   1558:        return false;
                   1559: }
                   1560: 
                   1561: void WidowCalcSlots (edict_t *self)
                   1562: {
                   1563:        int old_slots;
                   1564: 
                   1565:        old_slots = self->monsterinfo.monster_slots;
                   1566: 
                   1567:        switch ((int)skill->value)
                   1568:        {
                   1569:                case 0:
                   1570:                case 1:
                   1571:                        self->monsterinfo.monster_slots = 3;
                   1572:                        break;
                   1573:                case 2:
                   1574:                        self->monsterinfo.monster_slots = 4;
                   1575:                        break;
                   1576:                case 3:
                   1577:                        self->monsterinfo.monster_slots = 6;
                   1578:                        break;
                   1579:                default:
                   1580:                        self->monsterinfo.monster_slots = 3;
                   1581:                        break;
                   1582:        }
                   1583:        if (coop->value)
                   1584:        {
                   1585:                self->monsterinfo.monster_slots = min (8, self->monsterinfo.monster_slots + (2*(CountPlayers()-1)));
                   1586:        }
                   1587:        if ((g_showlogic) && (g_showlogic->value) && (old_slots != self->monsterinfo.monster_slots))
                   1588:                gi.dprintf ("number of slots changed from %d to %d\n", old_slots, self->monsterinfo.monster_slots);
                   1589: }
                   1590: 
                   1591: void WidowPrecache ()
                   1592: {
                   1593:        // cache in all of the stalker stuff, widow stuff, spawngro stuff, gibs
                   1594:        gi.soundindex ("parasite/parpain1.wav");        
                   1595:        gi.soundindex ("parasite/parpain2.wav");        
                   1596:        gi.soundindex ("parasite/pardeth1.wav");        
                   1597:        gi.soundindex ("parasite/paratck1.wav");
                   1598:        gi.soundindex ("parasite/parsght1.wav");
                   1599:        gi.soundindex ("infantry/melee2.wav");
                   1600: 
                   1601:        gi.soundindex ("tank/tnkatck3.wav");
                   1602:        gi.modelindex ("models/proj/laser2/tris.md2");
                   1603: 
                   1604:        gi.modelindex ("models/monsters/stalker/tris.md2");
                   1605:        gi.modelindex ("models/items/spawngro2/tris.md2");
                   1606:        gi.modelindex ("models/objects/gibs/sm_metal/tris.md2");
                   1607:        gi.modelindex ("models/objects/gibs/gear/tris.md2");
                   1608:        gi.modelindex ("models/monsters/blackwidow/gib1/tris.md2");
                   1609:        gi.modelindex ("models/monsters/blackwidow/gib2/tris.md2");
                   1610:        gi.modelindex ("models/monsters/blackwidow/gib3/tris.md2");
                   1611:        gi.modelindex ("models/monsters/blackwidow/gib4/tris.md2");
                   1612:        gi.modelindex ("models/monsters/blackwidow2/gib1/tris.md2");
                   1613:        gi.modelindex ("models/monsters/blackwidow2/gib2/tris.md2");
                   1614:        gi.modelindex ("models/monsters/blackwidow2/gib3/tris.md2");
                   1615:        gi.modelindex ("models/monsters/blackwidow2/gib4/tris.md2");
                   1616:        gi.modelindex ("models/monsters/legs/tris.md2");
                   1617: 
                   1618:        gi.soundindex ("misc/bigtele.wav");
                   1619: }
                   1620: 
                   1621: 
                   1622: /*QUAKED monster_widow (1 .5 0) (-40 -40 0) (40 40 144) Ambush Trigger_Spawn Sight
                   1623: */
                   1624: void SP_monster_widow (edict_t *self)
                   1625: {
                   1626:        if (deathmatch->value)
                   1627:        {
                   1628:                G_FreeEdict (self);
                   1629:                return;
                   1630:        }
                   1631: 
                   1632:        sound_pain1 = gi.soundindex ("bosshovr/bhvpain1.wav");
                   1633:        sound_pain2 = gi.soundindex ("bosshovr/bhvpain2.wav");
                   1634:        sound_pain3 = gi.soundindex ("bosshovr/bhvpain3.wav");
                   1635:        sound_death = gi.soundindex ("weapons/bfg__f1y.wav");
                   1636:        sound_search1 = gi.soundindex ("bosshovr/bhvunqv1.wav");
                   1637:        sound_rail = gi.soundindex ("gladiator/railgun.wav");
                   1638: 
                   1639:        self->s.sound = gi.soundindex ("bosshovr/bhvengn1.wav");
                   1640: 
                   1641:        self->movetype = MOVETYPE_STEP;
                   1642:        self->solid = SOLID_BBOX;
                   1643:        self->s.modelindex = gi.modelindex ("models/monsters/blackwidow/tris.md2");
                   1644:        VectorSet (self->mins, -40, -40, 0);
                   1645:        VectorSet (self->maxs, 40, 40, 144);
                   1646: 
                   1647:        self->health = 2000 + 1000*(skill->value);
                   1648:        if (coop->value)
                   1649:                self->health += 500*(skill->value);
                   1650: //     self->health = 1;
                   1651:        self->gib_health = -5000;
                   1652:        self->mass = 1500;
                   1653: /*
                   1654:        if (skill->value == 2)
                   1655:        {
                   1656:                self->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN;
                   1657:                self->monsterinfo.power_armor_power = 250;
                   1658:        }
                   1659:        else */if (skill->value == 3)
                   1660:        {
                   1661:                self->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN;
                   1662:                self->monsterinfo.power_armor_power = 500;
                   1663:        }
                   1664: 
                   1665:        self->yaw_speed = 30;
                   1666:        
                   1667:        self->flags |= FL_IMMUNE_LASER;
                   1668:        self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
                   1669: 
                   1670:        self->pain = widow_pain;
                   1671:        self->die = widow_die;
                   1672: 
                   1673:        self->monsterinfo.melee = widow_melee;
                   1674:        self->monsterinfo.stand = widow_stand;
                   1675:        self->monsterinfo.walk = widow_walk;
                   1676:        self->monsterinfo.run = widow_run;
                   1677:        self->monsterinfo.attack = widow_attack;
                   1678:        self->monsterinfo.search = widow_search;
                   1679:        self->monsterinfo.checkattack = Widow_CheckAttack;
                   1680:        self->monsterinfo.sight = widow_sight;
                   1681:        
                   1682:        self->monsterinfo.blocked = widow_blocked;
                   1683: 
                   1684:        gi.linkentity (self);
                   1685: 
                   1686:        self->monsterinfo.currentmove = &widow_move_stand;      
                   1687:        self->monsterinfo.scale = MODEL_SCALE;
                   1688: 
                   1689:        WidowPrecache();
                   1690:        WidowCalcSlots(self);
                   1691:        widow_damage_multiplier = 1;
                   1692: 
                   1693:        walkmonster_start (self);
                   1694: }

unix.superglobalmegacorp.com

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