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