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

1.1     ! root        1: /*
        !             2: ==============================================================================
        !             3: 
        !             4: black widow, part 2
        !             5: 
        !             6: ==============================================================================
        !             7: */
        !             8: 
        !             9: // timestamp used to prevent rapid fire of melee attack
        !            10: 
        !            11: #include "g_local.h"
        !            12: #include "m_widow2.h"
        !            13: 
        !            14: #define        NUM_STALKERS_SPAWNED            6               // max # of stalkers she can spawn
        !            15: 
        !            16: #define        DISRUPT_TIME                                    3
        !            17: 
        !            18: static int     sound_pain1;
        !            19: static int     sound_pain2;
        !            20: static int     sound_pain3;
        !            21: static int     sound_death;
        !            22: static int     sound_search1;
        !            23: static int     sound_disrupt;
        !            24: static int     sound_tentacles_retract;
        !            25: 
        !            26: // sqrt(64*64*2) + sqrt(28*28*2) => 130.1
        !            27: static vec3_t spawnpoints[] = {
        !            28:        {30,  135, 0},
        !            29:        {30, -135, 0}
        !            30: };
        !            31: 
        !            32: static float sweep_angles[] = {
        !            33:        -40.0, -32.0, -24.0, -16.0, -8.0, 0.0, 8.0, 16.0, 24.0, 32.0, 40.0
        !            34: };
        !            35: 
        !            36: extern vec3_t  stalker_mins, stalker_maxs;
        !            37: 
        !            38: qboolean infront (edict_t *self, edict_t *other);
        !            39: void WidowCalcSlots (edict_t *self);
        !            40: void WidowPowerups (edict_t *self);
        !            41: 
        !            42: void widow2_run (edict_t *self);
        !            43: void widow2_stand (edict_t *self);
        !            44: void widow2_dead (edict_t *self);
        !            45: void widow2_attack (edict_t *self);
        !            46: void widow2_attack_beam (edict_t *self);
        !            47: void widow2_reattack_beam (edict_t *self);
        !            48: void widow2_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
        !            49: void widow_start_spawn (edict_t *self);
        !            50: void widow_done_spawn (edict_t *self);
        !            51: void widow2_spawn_check (edict_t *self);
        !            52: void widow2_prep_spawn (edict_t *self);
        !            53: void Widow2SaveBeamTarget(edict_t *self);
        !            54: 
        !            55: // death stuff
        !            56: void WidowExplode (edict_t *self);
        !            57: void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
        !            58: void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
        !            59: void ThrowWidowGibReal (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean large, int hitsound, qboolean fade);
        !            60: void ThrowWidowGibSized (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, int hitsound, qboolean fade);
        !            61: void ThrowWidowGibLoc (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean fade);
        !            62: void WidowExplosion1 (edict_t *self);
        !            63: void WidowExplosion2 (edict_t *self);
        !            64: void WidowExplosion3 (edict_t *self);
        !            65: void WidowExplosion4 (edict_t *self);
        !            66: void WidowExplosion5 (edict_t *self);
        !            67: void WidowExplosion6 (edict_t *self);
        !            68: void WidowExplosion7 (edict_t *self);
        !            69: void WidowExplosionLeg (edict_t *self);
        !            70: void ThrowArm1 (edict_t *self);
        !            71: void ThrowArm2 (edict_t *self);
        !            72: void ClipGibVelocity (edict_t *ent);
        !            73: // end of death stuff
        !            74: 
        !            75: // these offsets used by the tongue
        !            76: static vec3_t offsets[] = {
        !            77:        {17.48, 0.10, 68.92},
        !            78:        {17.47, 0.29, 68.91},
        !            79:        {17.45, 0.53, 68.87},
        !            80:        {17.42, 0.78, 68.81},
        !            81:        {17.39, 1.02, 68.75},
        !            82:        {17.37, 1.20, 68.70},
        !            83:        {17.36, 1.24, 68.71},
        !            84:        {17.37, 1.21, 68.72},
        !            85: };
        !            86: 
        !            87: void showme (edict_t *self);
        !            88: 
        !            89: void pauseme (edict_t *self)
        !            90: {
        !            91:        self->monsterinfo.aiflags |= AI_HOLD_FRAME;
        !            92: }
        !            93: 
        !            94: void widow2_search (edict_t *self)
        !            95: {
        !            96:        if (random() < 0.5)
        !            97:                gi.sound (self, CHAN_VOICE, sound_search1, 1, ATTN_NONE, 0);
        !            98: }
        !            99: 
        !           100: void Widow2Beam (edict_t *self)
        !           101: {
        !           102:        vec3_t  forward, right, target;
        !           103:        vec3_t  start, targ_angles, vec;
        !           104:        int             flashnum;
        !           105: 
        !           106:        if ((!self->enemy) || (!self->enemy->inuse))
        !           107:                return;
        !           108: 
        !           109:        AngleVectors (self->s.angles, forward, right, NULL);
        !           110:        
        !           111:        if ((self->s.frame >= FRAME_fireb05) && (self->s.frame <= FRAME_fireb09))
        !           112:        {
        !           113:                // regular beam attack
        !           114:                Widow2SaveBeamTarget(self);
        !           115:                flashnum = MZ2_WIDOW2_BEAMER_1 + self->s.frame - FRAME_fireb05;
        !           116:                G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
        !           117:                VectorCopy (self->pos2, target);
        !           118:                target[2] += self->enemy->viewheight-10;
        !           119:                VectorSubtract (target, start, forward);
        !           120:                VectorNormalize (forward);
        !           121:                monster_fire_heat (self, start, forward, vec3_origin, 10, 50, flashnum);
        !           122:        }
        !           123:        else if ((self->s.frame >= FRAME_spawn04) && (self->s.frame <= FRAME_spawn14))
        !           124:        {
        !           125:                // sweep
        !           126:                flashnum = MZ2_WIDOW2_BEAM_SWEEP_1 + self->s.frame - FRAME_spawn04;
        !           127:                G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
        !           128:                VectorSubtract (self->enemy->s.origin, start, target);
        !           129:                vectoangles2 (target, targ_angles);
        !           130:                
        !           131:                VectorCopy (self->s.angles, vec);
        !           132: 
        !           133:                vec[PITCH] += targ_angles[PITCH];
        !           134:                vec[YAW] -= sweep_angles[flashnum-MZ2_WIDOW2_BEAM_SWEEP_1];
        !           135: 
        !           136:                AngleVectors (vec, forward, NULL, NULL);
        !           137:                monster_fire_heat (self, start, forward, vec3_origin, 10, 50, flashnum);
        !           138: /*
        !           139:                if (self->s.frame == FRAME_spawn04)
        !           140:                {
        !           141:                        VectorMA (start, 1024, forward, debugend);
        !           142: 
        !           143:                        gi.WriteByte (svc_temp_entity);
        !           144:                        gi.WriteByte (TE_DEBUGTRAIL);
        !           145:                        gi.WritePosition (start);
        !           146:                        gi.WritePosition (debugend);
        !           147:                        gi.multicast (start, MULTICAST_ALL);
        !           148: 
        !           149:                        drawbbox (self);
        !           150:                        self->monsterinfo.aiflags |= AI_HOLD_FRAME|AI_MANUAL_STEERING;
        !           151:                }
        !           152: */
        !           153:        }
        !           154:        else
        !           155:        {
        !           156:                if ((g_showlogic) && (g_showlogic->value))
        !           157:                        gi.dprintf ("bad fire frame for widow2 beam -- tell me you saw this!\n");
        !           158: 
        !           159:                Widow2SaveBeamTarget(self);
        !           160:                G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_WIDOW2_BEAMER_1], forward, right, start);
        !           161: 
        !           162:                VectorCopy (self->pos2, target);
        !           163:                target[2] += self->enemy->viewheight-10;
        !           164:                
        !           165:                VectorSubtract (target, start, forward);
        !           166:                VectorNormalize (forward);
        !           167: 
        !           168:                monster_fire_heat (self, start, forward, vec3_origin, 10, 50, 0);
        !           169:        }       
        !           170: }
        !           171: 
        !           172: void Widow2Spawn (edict_t *self)
        !           173: {
        !           174:        vec3_t  f, r, u, offset, startpoint, spawnpoint;
        !           175:        edict_t *ent, *designated_enemy;
        !           176:        int             i;
        !           177: 
        !           178:        AngleVectors (self->s.angles, f, r, u);
        !           179: 
        !           180:        for (i=0; i < 2; i++)
        !           181:        {
        !           182:                VectorCopy (spawnpoints[i], offset);
        !           183: 
        !           184:                G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
        !           185: 
        !           186:                if (FindSpawnPoint (startpoint, stalker_mins, stalker_maxs, spawnpoint, 64))
        !           187:                {
        !           188:                        ent = CreateGroundMonster (spawnpoint, self->s.angles, stalker_mins, stalker_maxs, "monster_stalker", 256);
        !           189: 
        !           190:                        if (!ent)
        !           191:                                continue;
        !           192: 
        !           193:                        self->monsterinfo.monster_used++;
        !           194:                        ent->monsterinfo.commander = self;
        !           195:                        if ((g_showlogic) && (g_showlogic->value))
        !           196:                                gi.dprintf ("widow: post-spawn : %d slots left\n", SELF_SLOTS_LEFT);
        !           197: 
        !           198:                        ent->nextthink = level.time;
        !           199:                        ent->think (ent);
        !           200:                        
        !           201:                        ent->monsterinfo.aiflags |= AI_SPAWNED_WIDOW|AI_DO_NOT_COUNT|AI_IGNORE_SHOTS;
        !           202: 
        !           203:                        if (!(coop && coop->value))
        !           204:                        {
        !           205:                                designated_enemy = self->enemy;
        !           206:                        }
        !           207:                        else
        !           208:                        {
        !           209:                                designated_enemy = PickCoopTarget(ent);
        !           210:                                if (designated_enemy)
        !           211:                                {
        !           212:                                        // try to avoid using my enemy
        !           213:                                        if (designated_enemy == self->enemy)
        !           214:                                        {
        !           215:                                                designated_enemy = PickCoopTarget(ent);
        !           216:                                                if (designated_enemy)
        !           217:                                                {
        !           218:                                                        if ((g_showlogic) && (g_showlogic->value))
        !           219:                                                        {
        !           220:                                                                gi.dprintf ("PickCoopTarget returned a %s - ", designated_enemy->classname);
        !           221:                                                                if (designated_enemy->client)
        !           222:                                                                        gi.dprintf ("with name %s\n", designated_enemy->client->pers.netname);
        !           223:                                                                else
        !           224:                                                                        gi.dprintf ("NOT A CLIENT\n");
        !           225:                                                        }
        !           226:                                                }
        !           227:                                                else
        !           228:                                                {
        !           229:                                                        if ((g_showlogic) && (g_showlogic->value))
        !           230:                                                                gi.dprintf ("pick coop failed, using my current enemy\n");
        !           231:                                                        designated_enemy = self->enemy;
        !           232:                                                }
        !           233:                                        }
        !           234:                                }
        !           235:                                else
        !           236:                                {
        !           237:                                        if ((g_showlogic) && (g_showlogic->value))
        !           238:                                                gi.dprintf ("pick coop failed, using my current enemy\n");
        !           239:                                        designated_enemy = self->enemy;
        !           240:                                }
        !           241:                        }
        !           242: 
        !           243:                        if ((designated_enemy->inuse) && (designated_enemy->health > 0))
        !           244:                        {
        !           245:                                ent->enemy = designated_enemy;
        !           246:                                FoundTarget (ent);
        !           247:                                ent->monsterinfo.attack(ent);
        !           248:                        }
        !           249:                }
        !           250:        }
        !           251: }
        !           252: 
        !           253: void widow2_spawn_check (edict_t *self)
        !           254: {
        !           255:        Widow2Beam(self);
        !           256:        Widow2Spawn (self);
        !           257: }
        !           258: 
        !           259: void widow2_ready_spawn (edict_t *self)
        !           260: {
        !           261:        vec3_t  f, r, u, offset, startpoint, spawnpoint;
        !           262:        int             i;
        !           263: 
        !           264:        Widow2Beam(self);
        !           265:        AngleVectors (self->s.angles, f, r, u);
        !           266: 
        !           267:        for (i=0; i < 2; i++)
        !           268:        {
        !           269:                VectorCopy (spawnpoints[i], offset);
        !           270:                G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
        !           271:                if (FindSpawnPoint (startpoint, stalker_mins, stalker_maxs, spawnpoint, 64))
        !           272:                {
        !           273:                        SpawnGrow_Spawn (spawnpoint, 1);
        !           274:                }
        !           275:        }
        !           276: }
        !           277: 
        !           278: mframe_t widow2_frames_stand [] =
        !           279: {
        !           280: //     ai_stand, 0, drawbbox
        !           281:        ai_stand, 0, NULL
        !           282: };
        !           283: mmove_t        widow2_move_stand = {FRAME_blackwidow3, FRAME_blackwidow3, widow2_frames_stand, NULL};
        !           284: 
        !           285: mframe_t widow2_frames_walk [] =
        !           286: {
        !           287: //     ai_walk,        9.01,   drawbbox,
        !           288:        ai_walk,        9.01,   NULL,
        !           289:        ai_walk,        7.55,   NULL,
        !           290:        ai_walk,        7.01,   NULL,
        !           291:        ai_walk,        6.66,   NULL,
        !           292:        ai_walk,        6.20,   NULL,
        !           293:        ai_walk,        5.78,   NULL,
        !           294:        ai_walk,        7.25,   NULL,
        !           295:        ai_walk,        8.37,   NULL,
        !           296:        ai_walk,        10.41,  NULL
        !           297: };
        !           298: mmove_t widow2_move_walk = {FRAME_walk01, FRAME_walk09, widow2_frames_walk, NULL};
        !           299: 
        !           300: 
        !           301: mframe_t widow2_frames_run [] =
        !           302: {
        !           303: //     ai_run, 9.01,   drawbbox,
        !           304:        ai_run, 9.01,   NULL,
        !           305:        ai_run, 7.55,   NULL,
        !           306:        ai_run, 7.01,   NULL,
        !           307:        ai_run, 6.66,   NULL,
        !           308:        ai_run, 6.20,   NULL,
        !           309:        ai_run, 5.78,   NULL,
        !           310:        ai_run, 7.25,   NULL,
        !           311:        ai_run, 8.37,   NULL,
        !           312:        ai_run, 10.41,  NULL
        !           313: };
        !           314: mmove_t widow2_move_run = {FRAME_walk01, FRAME_walk09, widow2_frames_run, NULL};
        !           315: 
        !           316: mframe_t widow2_frames_attack_pre_beam [] =
        !           317: {
        !           318:        ai_charge,      4,      NULL,
        !           319:        ai_charge,      4,      NULL,
        !           320:        ai_charge,      4,      NULL,
        !           321:        ai_charge,      4,      widow2_attack_beam
        !           322: };
        !           323: mmove_t widow2_move_attack_pre_beam = {FRAME_fireb01, FRAME_fireb04, widow2_frames_attack_pre_beam, NULL};
        !           324: 
        !           325: 
        !           326: // Loop this
        !           327: mframe_t widow2_frames_attack_beam [] =
        !           328: {
        !           329:        ai_charge,      0,      Widow2Beam,
        !           330:        ai_charge,      0,      Widow2Beam,
        !           331:        ai_charge,      0,      Widow2Beam,
        !           332:        ai_charge,      0,      Widow2Beam,
        !           333:        ai_charge,      0,      widow2_reattack_beam
        !           334: };
        !           335: mmove_t widow2_move_attack_beam = {FRAME_fireb05, FRAME_fireb09, widow2_frames_attack_beam, NULL};
        !           336: 
        !           337: mframe_t widow2_frames_attack_post_beam [] =
        !           338: {
        !           339:        ai_charge,      4,      NULL,
        !           340:        ai_charge,      4,      NULL,
        !           341:        ai_charge,      4,      NULL
        !           342: };
        !           343: mmove_t widow2_move_attack_post_beam = {FRAME_fireb06, FRAME_fireb07, widow2_frames_attack_post_beam, widow2_run};
        !           344: 
        !           345: 
        !           346: void WidowDisrupt (edict_t *self)
        !           347: {
        !           348:        vec3_t  start;
        !           349:        vec3_t  dir;
        !           350:        vec3_t  forward, right;
        !           351:        float   len;
        !           352: 
        !           353:        AngleVectors (self->s.angles, forward, right, NULL);
        !           354:        G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_WIDOW_DISRUPTOR], forward, right, start);
        !           355: 
        !           356:        VectorSubtract (self->pos1, self->enemy->s.origin, dir);
        !           357:        len = VectorLength (dir);
        !           358: 
        !           359:        if (len < 30)
        !           360:        {
        !           361: //             if ((g_showlogic) && (g_showlogic->value))
        !           362: //                     gi.dprintf ("target locked - dist %2.2f\n", len);
        !           363:                // calc direction to where we targeted
        !           364:                VectorSubtract (self->pos1, start, dir);
        !           365:                VectorNormalize (dir);
        !           366: 
        !           367:                monster_fire_tracker(self, start, dir, 20, 500, self->enemy, MZ2_WIDOW_DISRUPTOR);
        !           368:        }
        !           369:        else
        !           370:        {
        !           371: //             if ((g_showlogic) && (g_showlogic->value))
        !           372: //                     gi.dprintf ("target missed - dist %2.2f\n", len);
        !           373: 
        !           374:                PredictAim (self->enemy, start, 1200, true, 0, dir, NULL);
        !           375: 
        !           376: //             VectorSubtract (self->enemy->s.origin, start, dir);
        !           377: //             VectorNormalize (dir);
        !           378:                monster_fire_tracker(self, start, dir, 20, 1200, NULL, MZ2_WIDOW_DISRUPTOR);
        !           379:        }
        !           380: }
        !           381: 
        !           382: void Widow2SaveDisruptLoc (edict_t *self)
        !           383: {
        !           384:        if (self->enemy && self->enemy->inuse)
        !           385:        {
        !           386:                VectorCopy (self->enemy->s.origin, self->pos1); //save for aiming the shot
        !           387:                self->pos1[2] += self->enemy->viewheight;
        !           388:        }
        !           389:        else
        !           390:                VectorCopy (vec3_origin, self->pos1);
        !           391: };
        !           392: 
        !           393: void widow2_disrupt_reattack (edict_t *self)
        !           394: {
        !           395:        float luck;
        !           396:        
        !           397:        luck = random();
        !           398: 
        !           399:        if (luck < (0.25 + ((float)(skill->value))*0.15))
        !           400:                self->monsterinfo.nextframe = FRAME_firea01;
        !           401: }
        !           402: 
        !           403: mframe_t widow2_frames_attack_disrupt [] =
        !           404: {
        !           405:        ai_charge, 2, NULL,
        !           406:        ai_charge, 2, NULL,
        !           407:        ai_charge, 2, Widow2SaveDisruptLoc,
        !           408:        ai_charge, -20, WidowDisrupt,
        !           409:        ai_charge, 2, NULL,
        !           410:        ai_charge, 2, NULL,
        !           411:        ai_charge, 2, widow2_disrupt_reattack
        !           412: };
        !           413: mmove_t widow2_move_attack_disrupt = {FRAME_firea01, FRAME_firea07, widow2_frames_attack_disrupt, widow2_run};
        !           414: 
        !           415: void Widow2SaveBeamTarget (edict_t *self)
        !           416: {
        !           417:        if (self->enemy && self->enemy->inuse)
        !           418:        {
        !           419:                VectorCopy (self->pos1, self->pos2);
        !           420:                VectorCopy (self->enemy->s.origin, self->pos1); //save for aiming the shot
        !           421:        }
        !           422:        else
        !           423:        {
        !           424:                VectorCopy (vec3_origin, self->pos1);
        !           425:                VectorCopy (vec3_origin, self->pos2);
        !           426:        }
        !           427: }
        !           428: 
        !           429: void Widow2BeamTargetRemove (edict_t *self)
        !           430: {
        !           431:        VectorCopy (vec3_origin, self->pos1);
        !           432:        VectorCopy (vec3_origin, self->pos2);
        !           433: }
        !           434: 
        !           435: void Widow2StartSweep (edict_t *self)
        !           436: {
        !           437:        Widow2SaveBeamTarget (self);
        !           438: }
        !           439: 
        !           440: mframe_t widow2_frames_spawn [] =
        !           441: {
        !           442:        ai_charge,      0,      NULL,
        !           443:        ai_charge,      0,      NULL,
        !           444:        ai_charge,      0,      widow_start_spawn,
        !           445:        ai_charge,      0,      Widow2Beam,
        !           446:        ai_charge,      0,      Widow2Beam,                             //5
        !           447:        ai_charge,      0,      Widow2Beam,
        !           448:        ai_charge,      0,      Widow2Beam,
        !           449:        ai_charge,      0,      Widow2Beam,
        !           450:        ai_charge,      0,      Widow2Beam,
        !           451:        ai_charge,      0,      widow2_ready_spawn,                             //10
        !           452:        ai_charge,      0,      Widow2Beam,
        !           453:        ai_charge,      0,      Widow2Beam,
        !           454:        ai_charge,      0,      Widow2Beam,
        !           455:        ai_charge,      0,      widow2_spawn_check,
        !           456:        ai_charge,      0,      NULL,                           //15
        !           457:        ai_charge,      0,      NULL,
        !           458:        ai_charge,      0,      NULL,
        !           459:        ai_charge,      0,      widow2_reattack_beam
        !           460: };
        !           461: mmove_t widow2_move_spawn = {FRAME_spawn01, FRAME_spawn18, widow2_frames_spawn, NULL};
        !           462: 
        !           463: static qboolean widow2_tongue_attack_ok (vec3_t start, vec3_t end, float range)
        !           464: {
        !           465:        vec3_t  dir, angles;
        !           466: 
        !           467:        // check for max distance
        !           468:        VectorSubtract (start, end, dir);
        !           469:        if (VectorLength(dir) > range)
        !           470:                return false;
        !           471: 
        !           472:        // check for min/max pitch
        !           473:        vectoangles (dir, angles);
        !           474:        if (angles[0] < -180)
        !           475:                angles[0] += 360;
        !           476:        if (fabs(angles[0]) > 30)
        !           477:                return false;
        !           478: 
        !           479:        return true;
        !           480: }
        !           481: 
        !           482: void Widow2Tongue (edict_t *self)
        !           483: {
        !           484:        vec3_t  f, r, u;
        !           485:        vec3_t  start, end, dir;
        !           486:        trace_t tr;
        !           487: 
        !           488:        AngleVectors (self->s.angles, f, r, u);
        !           489:        G_ProjectSource2 (self->s.origin, offsets[self->s.frame - FRAME_tongs01], f, r, u, start);
        !           490:        VectorCopy (self->enemy->s.origin, end);
        !           491:        if (!widow2_tongue_attack_ok(start, end, 256))
        !           492:        {
        !           493:                end[2] = self->enemy->s.origin[2] + self->enemy->maxs[2] - 8;
        !           494:                if (!widow2_tongue_attack_ok(start, end, 256))
        !           495:                {
        !           496:                        end[2] = self->enemy->s.origin[2] + self->enemy->mins[2] + 8;
        !           497:                        if (!widow2_tongue_attack_ok(start, end, 256))
        !           498:                                return;
        !           499:                }
        !           500:        }
        !           501:        VectorCopy (self->enemy->s.origin, end);
        !           502: 
        !           503:        tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
        !           504:        if (tr.ent != self->enemy)
        !           505:                return;
        !           506: 
        !           507:        gi.sound (self, CHAN_WEAPON, sound_tentacles_retract, 1, ATTN_NORM, 0);
        !           508: 
        !           509:        gi.WriteByte (svc_temp_entity);
        !           510:        gi.WriteByte (TE_PARASITE_ATTACK);
        !           511:        gi.WriteShort (self - g_edicts);
        !           512:        gi.WritePosition (start);
        !           513:        gi.WritePosition (end);
        !           514:        gi.multicast (self->s.origin, MULTICAST_PVS);
        !           515: 
        !           516:        VectorSubtract (start, end, dir);
        !           517:        T_Damage (self->enemy, self, self, dir, self->enemy->s.origin, vec3_origin, 2, 0, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN);
        !           518: }
        !           519: 
        !           520: void Widow2TonguePull (edict_t *self)
        !           521: {
        !           522:        vec3_t  vec;
        !           523:        float   len;
        !           524:        vec3_t  f, r, u;
        !           525:        vec3_t  start, end;
        !           526: 
        !           527:        if ((!self->enemy) || (!self->enemy->inuse))
        !           528:        {
        !           529:                self->monsterinfo.run (self);
        !           530:                return;
        !           531:        }
        !           532: 
        !           533:        AngleVectors (self->s.angles, f, r, u);
        !           534:        G_ProjectSource2 (self->s.origin, offsets[self->s.frame - FRAME_tongs01], f, r, u, start);
        !           535:        VectorCopy (self->enemy->s.origin, end);
        !           536: 
        !           537:        if (!widow2_tongue_attack_ok(start, end, 256))
        !           538:        {
        !           539:                return;
        !           540:        }
        !           541: 
        !           542:        if (self->enemy->groundentity)
        !           543:        {
        !           544:                self->enemy->s.origin[2] += 1;
        !           545:                self->enemy->groundentity = NULL;
        !           546:                // interesting, you don't have to relink the player
        !           547:        }
        !           548:        
        !           549:        VectorSubtract (self->s.origin, self->enemy->s.origin, vec);
        !           550:        len = VectorLength (vec);
        !           551:        if (self->enemy->client)
        !           552:        {
        !           553:                VectorNormalize (vec);
        !           554:                VectorMA (self->enemy->velocity, 1000, vec, self->enemy->velocity);
        !           555:        }
        !           556:        else
        !           557:        {
        !           558:                self->enemy->ideal_yaw = vectoyaw(vec); 
        !           559:                M_ChangeYaw (self->enemy);
        !           560:                VectorScale (f, 1000, self->enemy->velocity);
        !           561:        }
        !           562: }
        !           563: 
        !           564: void Widow2Crunch (edict_t *self)
        !           565: {
        !           566:        vec3_t  aim;
        !           567: 
        !           568:        if ((!self->enemy) || (!self->enemy->inuse))
        !           569:        {
        !           570:                self->monsterinfo.run (self);
        !           571:                return;
        !           572:        }
        !           573: 
        !           574:        Widow2TonguePull (self);
        !           575: 
        !           576:        // 70 + 32
        !           577:        VectorSet (aim, 150, 0, 4);
        !           578:        if (self->s.frame != FRAME_tongs07)
        !           579:                fire_hit (self, aim, 20 + (rand() % 6), 0);
        !           580:        else
        !           581:        {
        !           582:                if (self->enemy->groundentity)
        !           583:                        fire_hit (self, aim, (20 + (rand() % 6)), 500);
        !           584:                else    // not as much kick if they're in the air .. makes it harder to land on her head
        !           585:                        fire_hit (self, aim, (20 + (rand() % 6)), 250);
        !           586:        }
        !           587: }
        !           588: 
        !           589: void Widow2Toss (edict_t *self)
        !           590: {
        !           591:        self->timestamp = level.time + 3;
        !           592:        return;
        !           593: }
        !           594: 
        !           595: mframe_t widow2_frames_tongs [] =
        !           596: {
        !           597:        ai_charge,      0,      Widow2Tongue,
        !           598:        ai_charge,      0,      Widow2Tongue,
        !           599:        ai_charge,      0,      Widow2Tongue,
        !           600:        ai_charge,      0,      Widow2TonguePull,
        !           601:        ai_charge,      0,      Widow2TonguePull,                               //5
        !           602:        ai_charge,      0,      Widow2TonguePull,
        !           603:        ai_charge,      0,      Widow2Crunch,
        !           604:        ai_charge,      0,      Widow2Toss
        !           605: };
        !           606: mmove_t widow2_move_tongs = {FRAME_tongs01, FRAME_tongs08, widow2_frames_tongs, widow2_run};
        !           607: 
        !           608: mframe_t widow2_frames_pain [] =
        !           609: {
        !           610:        ai_move,        0,      NULL,
        !           611:        ai_move,        0,      NULL,
        !           612:        ai_move,        0,      NULL,
        !           613:        ai_move,        0,      NULL,
        !           614:        ai_move,        0,      NULL
        !           615: };
        !           616: mmove_t widow2_move_pain = {FRAME_pain01, FRAME_pain05, widow2_frames_pain, widow2_run};
        !           617: 
        !           618: mframe_t widow2_frames_death [] =
        !           619: {
        !           620:        ai_move,        0,      NULL,
        !           621:        ai_move,        0,      NULL,
        !           622:        ai_move,        0,      WidowExplosion1,        // 3 boom
        !           623:        ai_move,        0,      NULL,
        !           624:        ai_move,        0,      NULL,                           // 5
        !           625: 
        !           626:        ai_move,        0,      WidowExplosion2,        // 6 boom
        !           627:        ai_move,        0,      NULL,
        !           628:        ai_move,        0,      NULL,
        !           629:        ai_move,        0,      NULL,
        !           630:        ai_move,        0,      NULL,                           // 10
        !           631: 
        !           632:        ai_move,        0,      NULL,
        !           633:        ai_move,        0,      NULL,                           // 12
        !           634:        ai_move,        0,      NULL,
        !           635:        ai_move,        0,      NULL,
        !           636:        ai_move,        0,      NULL,                           // 15
        !           637: 
        !           638:        ai_move,        0,      NULL,
        !           639:        ai_move,        0,      NULL,
        !           640:        ai_move,        0,      WidowExplosion3,        // 18
        !           641:        ai_move,        0,      NULL,                           // 19
        !           642:        ai_move,        0,      NULL,                           // 20
        !           643: 
        !           644:        ai_move,        0,      NULL,
        !           645:        ai_move,        0,      NULL,
        !           646:        ai_move,        0,      NULL,
        !           647:        ai_move,        0,      NULL,
        !           648:        ai_move,        0,      WidowExplosion4,        // 25
        !           649: 
        !           650:        ai_move,        0,      NULL,                           // 26
        !           651:        ai_move,        0,      NULL,
        !           652:        ai_move,        0,      NULL,
        !           653:        ai_move,        0,      WidowExplosion5,
        !           654:        ai_move,        0,      WidowExplosionLeg,      // 30
        !           655: 
        !           656:        ai_move,        0,      NULL,
        !           657:        ai_move,        0,      NULL,
        !           658:        ai_move,        0,      NULL,
        !           659:        ai_move,        0,      WidowExplosion6,
        !           660:        ai_move,        0,      NULL,                           // 35
        !           661: 
        !           662:        ai_move,        0,      NULL,
        !           663:        ai_move,        0,      NULL,
        !           664:        ai_move,        0,      WidowExplosion7,
        !           665:        ai_move,        0,      NULL,
        !           666:        ai_move,        0,      NULL,                           // 40
        !           667: 
        !           668:        ai_move,        0,      NULL,
        !           669:        ai_move,        0,      NULL,
        !           670:        ai_move,        0,      NULL,
        !           671:        ai_move,        0,      WidowExplode            // 44
        !           672: };
        !           673: mmove_t widow2_move_death = {FRAME_death01, FRAME_death44, widow2_frames_death, NULL};
        !           674: 
        !           675: void widow2_start_searching (edict_t *self);
        !           676: void widow2_keep_searching (edict_t *self);
        !           677: void widow2_finaldeath (edict_t *self);
        !           678: 
        !           679: mframe_t widow2_frames_dead [] =
        !           680: {
        !           681:        ai_move,        0,      widow2_start_searching,
        !           682:        ai_move,        0,      NULL,
        !           683:        ai_move,        0,      NULL,
        !           684:        ai_move,        0,      NULL,
        !           685:        ai_move,        0,      NULL,
        !           686: 
        !           687:        ai_move,        0,      NULL,
        !           688:        ai_move,        0,      NULL,
        !           689:        ai_move,        0,      NULL,
        !           690:        ai_move,        0,      NULL,
        !           691:        ai_move,        0,      NULL,
        !           692: 
        !           693:        ai_move,        0,      NULL,
        !           694:        ai_move,        0,      NULL,
        !           695:        ai_move,        0,      NULL,
        !           696:        ai_move,        0,      NULL,
        !           697:        ai_move,        0,      widow2_keep_searching
        !           698: };
        !           699: mmove_t widow2_move_dead = {FRAME_dthsrh01, FRAME_dthsrh15, widow2_frames_dead, NULL};
        !           700: 
        !           701: mframe_t widow2_frames_really_dead [] =
        !           702: {
        !           703:        ai_move,        0,      NULL,
        !           704:        ai_move,        0,      NULL,
        !           705:        ai_move,        0,      NULL,
        !           706:        ai_move,        0,      NULL,
        !           707:        ai_move,        0,      NULL,
        !           708: 
        !           709:        ai_move,        0,      NULL,
        !           710:        ai_move,        0,      widow2_finaldeath
        !           711: };
        !           712: mmove_t widow2_move_really_dead = {FRAME_dthsrh16, FRAME_dthsrh22, widow2_frames_really_dead, NULL};
        !           713: 
        !           714: void widow2_start_searching (edict_t *self)
        !           715: {
        !           716:        self->count = 0;
        !           717: }
        !           718: 
        !           719: void widow2_keep_searching (edict_t *self)
        !           720: {
        !           721:        if (self->count <= 2)
        !           722:        {
        !           723:                self->monsterinfo.currentmove = &widow2_move_dead;
        !           724:                self->s.frame = FRAME_dthsrh01;
        !           725:                self->count++;
        !           726:                return;
        !           727:        }
        !           728: 
        !           729:        self->monsterinfo.currentmove = &widow2_move_really_dead;
        !           730: }
        !           731: 
        !           732: void widow2_finaldeath (edict_t *self)
        !           733: {
        !           734:        VectorSet (self->mins, -70, -70, 0);
        !           735:        VectorSet (self->maxs, 70, 70, 80);
        !           736:        self->movetype = MOVETYPE_TOSS;
        !           737: //     self->svflags |= SVF_DEADMONSTER;
        !           738:        self->takedamage = DAMAGE_YES;
        !           739:        self->nextthink = 0;
        !           740:        gi.linkentity (self);
        !           741: }
        !           742: 
        !           743: void widow2_stand (edict_t *self)
        !           744: {
        !           745: //     gi.dprintf ("widow2 stand\n");
        !           746:        self->monsterinfo.currentmove = &widow2_move_stand;
        !           747: }
        !           748: 
        !           749: void widow2_run (edict_t *self)
        !           750: {
        !           751: 
        !           752: //     gi.dprintf ("widow2 run - %2.2f - %s \n", level.time, self->enemy->classname);
        !           753:        self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
        !           754: 
        !           755:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
        !           756:                self->monsterinfo.currentmove = &widow2_move_stand;
        !           757:        else
        !           758:                self->monsterinfo.currentmove = &widow2_move_run;
        !           759: }
        !           760: 
        !           761: void widow2_walk (edict_t *self)
        !           762: {
        !           763:        self->monsterinfo.currentmove = &widow2_move_walk;
        !           764: }
        !           765: 
        !           766: void widow2_melee (edict_t *self)
        !           767: {
        !           768:        self->monsterinfo.currentmove = &widow2_move_tongs;
        !           769: }
        !           770: 
        !           771: void widow2_attack (edict_t *self)
        !           772: {
        !           773:        float   range, luck;
        !           774:        qboolean blocked = false;
        !           775: 
        !           776:        if (self->monsterinfo.aiflags & AI_BLOCKED)
        !           777:        {
        !           778:                blocked = true;
        !           779:                self->monsterinfo.aiflags &= ~AI_BLOCKED;
        !           780:        }
        !           781: 
        !           782: //     gi.dprintf ("widow2 attack\n");
        !           783:        
        !           784:        if (!self->enemy)
        !           785:                return;
        !           786: 
        !           787:        if (self->bad_area)
        !           788:        {
        !           789:                if ((random() < 0.1) || (level.time < self->monsterinfo.attack_finished))
        !           790:                        self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
        !           791:                else
        !           792:                {
        !           793:                        gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
        !           794:                        self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
        !           795:                }
        !           796:                return;
        !           797:        }
        !           798: 
        !           799:        WidowCalcSlots(self);
        !           800: 
        !           801:        // if we can't see the target, spawn stuff
        !           802:        if ((self->monsterinfo.attack_state == AS_BLIND) && (SELF_SLOTS_LEFT >= 2))
        !           803:        {
        !           804:                self->monsterinfo.currentmove = &widow2_move_spawn;
        !           805:                return;
        !           806:        }
        !           807: 
        !           808:        // accept bias towards spawning
        !           809:        if (blocked  && (SELF_SLOTS_LEFT >= 2))
        !           810:        {
        !           811:                self->monsterinfo.currentmove = &widow2_move_spawn;
        !           812:                return;
        !           813:        }
        !           814: 
        !           815:        range = realrange (self, self->enemy);
        !           816: 
        !           817:        if (range < 600)
        !           818:        {
        !           819:                luck = random();
        !           820:                if (SELF_SLOTS_LEFT >= 2)
        !           821:                {
        !           822:                        if (luck <= 0.40)
        !           823:                                self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
        !           824:                        else if ((luck <= 0.7) && !(level.time < self->monsterinfo.attack_finished))
        !           825:                        {
        !           826:                                gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
        !           827:                                self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
        !           828:                        }
        !           829:                        else
        !           830:                                self->monsterinfo.currentmove = &widow2_move_spawn;
        !           831:                }
        !           832:                else
        !           833:                {
        !           834:                        if ((luck <= 0.50) || (level.time < self->monsterinfo.attack_finished))
        !           835:                                self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
        !           836:                        else
        !           837:                        {
        !           838:                                gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
        !           839:                                self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
        !           840:                        }
        !           841:                }
        !           842:        }
        !           843:        else
        !           844:        {
        !           845:                luck = random();
        !           846:                if (SELF_SLOTS_LEFT >= 2)
        !           847:                {
        !           848:                        if (luck < 0.3)
        !           849:                                self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
        !           850:                        else if ((luck < 0.65) || (level.time < self->monsterinfo.attack_finished))
        !           851:                                self->monsterinfo.currentmove = &widow2_move_spawn;
        !           852:                        else
        !           853:                        {
        !           854:                                gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
        !           855:                                self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
        !           856:                        }
        !           857:                }
        !           858:                else
        !           859:                {
        !           860:                        if ((luck < 0.45) || (level.time < self->monsterinfo.attack_finished))
        !           861:                                self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
        !           862:                        else
        !           863:                        {
        !           864:                                gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
        !           865:                                self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
        !           866:                        }
        !           867:                }
        !           868:        }
        !           869: }
        !           870: 
        !           871: void widow2_attack_beam (edict_t *self)
        !           872: {
        !           873:        self->monsterinfo.currentmove = &widow2_move_attack_beam;
        !           874: }
        !           875: 
        !           876: void widow2_reattack_beam (edict_t *self)
        !           877: {
        !           878:        self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
        !           879: 
        !           880:        if ( infront(self, self->enemy) )
        !           881:                if (random() <= 0.5)
        !           882:                        if ((random() < 0.7) || (SELF_SLOTS_LEFT < 2))
        !           883:                                self->monsterinfo.currentmove = &widow2_move_attack_beam;
        !           884:                        else
        !           885:                                self->monsterinfo.currentmove = &widow2_move_spawn;
        !           886:                else
        !           887:                        self->monsterinfo.currentmove = &widow2_move_attack_post_beam;
        !           888:        else
        !           889:                self->monsterinfo.currentmove = &widow2_move_attack_post_beam;
        !           890: }
        !           891: 
        !           892: 
        !           893: 
        !           894: void widow2_pain (edict_t *self, edict_t *other, float kick, int damage)
        !           895: {
        !           896:        if (self->health < (self->max_health / 2))
        !           897:                self->s.skinnum = 1;
        !           898: 
        !           899:        if (skill->value == 3)
        !           900:                return;         // no pain anims in nightmare
        !           901: 
        !           902: //     gi.dprintf ("widow2 pain\n");
        !           903:        if (level.time < self->pain_debounce_time)
        !           904:                return;
        !           905: 
        !           906:        self->pain_debounce_time = level.time + 3;
        !           907: 
        !           908:        if (damage < 15)
        !           909:        {
        !           910:                gi.sound (self, CHAN_VOICE, sound_pain3, 1, ATTN_NONE, 0);
        !           911:        }
        !           912:        else if (damage < 75)
        !           913:        {
        !           914:                gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NONE, 0);
        !           915:                if ((skill->value < 3) && (random() < (0.6 - (0.2*((float)skill->value)))))
        !           916:                {
        !           917:                        self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
        !           918:                        self->monsterinfo.currentmove = &widow2_move_pain;
        !           919:                }
        !           920:        }
        !           921:        else 
        !           922:        {
        !           923:                gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NONE, 0);
        !           924:                if ((skill->value < 3) && (random() < (0.75 - (0.1*((float)skill->value)))))
        !           925:                {
        !           926:                        self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
        !           927:                        self->monsterinfo.currentmove = &widow2_move_pain;
        !           928:                }
        !           929:        }
        !           930: }
        !           931: 
        !           932: void widow2_dead (edict_t *self)
        !           933: {
        !           934: }
        !           935: 
        !           936: void KillChildren (edict_t *self)
        !           937: {
        !           938:        edict_t *ent;
        !           939:        int             field;
        !           940: 
        !           941:        ent = NULL;
        !           942:        field = FOFS(classname);
        !           943:        while (1)
        !           944:        {
        !           945:                ent = G_Find (ent, field, "monster_stalker");
        !           946:                if(!ent)
        !           947:                        return;
        !           948:                
        !           949:                // FIXME - may need to stagger
        !           950:                if ((ent->inuse) && (ent->health > 0))
        !           951:                        T_Damage (ent, self, self, vec3_origin, self->enemy->s.origin, vec3_origin, (ent->health + 1), 0, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN);
        !           952:        }
        !           953: }
        !           954: 
        !           955: void widow2_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
        !           956: {
        !           957:        int n;
        !           958: 
        !           959: // check for gib
        !           960:        if (self->health <= self->gib_health)
        !           961:        {
        !           962:                gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
        !           963:                for (n= 0; n < 2; n++)
        !           964:                        ThrowWidowGibLoc (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC, NULL, false);
        !           965:                for (n= 0; n < 3; n++)
        !           966:                        ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC, NULL, false);
        !           967:                for (n= 0; n < 3; n++)
        !           968:                {
        !           969:                        ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib1/tris.md2", damage, GIB_METALLIC, NULL,
        !           970:                                0, false);
        !           971:                        ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib2/tris.md2", damage, GIB_METALLIC, NULL, 
        !           972:                                gi.soundindex ("misc/fhit3.wav"), false);
        !           973:                }
        !           974:                for (n= 0; n < 2; n++)
        !           975:                {
        !           976:                        ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib3/tris.md2", damage, GIB_METALLIC, NULL, 
        !           977:                                0, false);
        !           978:                        ThrowWidowGibSized (self, "models/monsters/blackwidow/gib3/tris.md2", damage, GIB_METALLIC, NULL, 
        !           979:                                0, false);
        !           980:                }
        !           981:                ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC);
        !           982:                ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
        !           983:                self->deadflag = DEAD_DEAD;
        !           984:                return;
        !           985:        }
        !           986: 
        !           987:        if (self->deadflag == DEAD_DEAD)
        !           988:                return;
        !           989: 
        !           990:        gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0);
        !           991:        self->deadflag = DEAD_DEAD;
        !           992:        self->takedamage = DAMAGE_NO;
        !           993:        self->count = 0;
        !           994:        KillChildren (self);
        !           995:        self->monsterinfo.quad_framenum = 0;
        !           996:        self->monsterinfo.double_framenum = 0;
        !           997:        self->monsterinfo.invincible_framenum = 0;
        !           998:        self->monsterinfo.currentmove = &widow2_move_death;
        !           999: }
        !          1000: 
        !          1001: qboolean Widow2_CheckAttack (edict_t *self)
        !          1002: {
        !          1003:        vec3_t          spot1, spot2;
        !          1004:        vec3_t          temp;
        !          1005:        float           chance;
        !          1006:        trace_t         tr;
        !          1007:        qboolean        enemy_infront;
        !          1008:        int                     enemy_range;
        !          1009:        float           enemy_yaw;
        !          1010:        float           real_enemy_range;
        !          1011:        vec3_t          f, r, u;
        !          1012: 
        !          1013:        if (!self->enemy)
        !          1014:                return false;
        !          1015: 
        !          1016:        WidowPowerups(self);
        !          1017: 
        !          1018:        if ((random() < 0.8) && (SELF_SLOTS_LEFT >= 2) && (realrange(self, self->enemy) > 150))
        !          1019:        {
        !          1020:                self->monsterinfo.aiflags |= AI_BLOCKED;
        !          1021:                self->monsterinfo.attack_state = AS_MISSILE;
        !          1022:                return true;
        !          1023:        }
        !          1024: 
        !          1025:        if (self->enemy->health > 0)
        !          1026:        {
        !          1027:        // see if any entities are in the way of the shot
        !          1028:                VectorCopy (self->s.origin, spot1);
        !          1029:                spot1[2] += self->viewheight;
        !          1030:                VectorCopy (self->enemy->s.origin, spot2);
        !          1031:                spot2[2] += self->enemy->viewheight;
        !          1032: 
        !          1033:                tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA);
        !          1034: 
        !          1035:                // do we have a clear shot?
        !          1036:                if (tr.ent != self->enemy)
        !          1037:                {       
        !          1038:                        // go ahead and spawn stuff if we're mad a a client
        !          1039:                        if (self->enemy->client && SELF_SLOTS_LEFT >= 2)
        !          1040:                        {
        !          1041:                                self->monsterinfo.attack_state = AS_BLIND;
        !          1042:                                return true;
        !          1043:                        }
        !          1044:                                
        !          1045:                        // PGM - we want them to go ahead and shoot at info_notnulls if they can.
        !          1046:                        if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0)                //PGM
        !          1047:                                return false;
        !          1048:                }
        !          1049:        }
        !          1050:        
        !          1051:        enemy_infront = infront(self, self->enemy);
        !          1052: 
        !          1053:        enemy_range = range(self, self->enemy);
        !          1054:        VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
        !          1055:        enemy_yaw = vectoyaw2(temp);
        !          1056: 
        !          1057:        self->ideal_yaw = enemy_yaw;
        !          1058: 
        !          1059:        // melee attack
        !          1060:        if (self->timestamp < level.time)
        !          1061:        {
        !          1062:                real_enemy_range = realrange (self, self->enemy);
        !          1063:                if (real_enemy_range < 300)
        !          1064:                {
        !          1065:                        AngleVectors (self->s.angles, f, r, u);
        !          1066:                        G_ProjectSource2 (self->s.origin, offsets[0], f, r, u, spot1);
        !          1067:                        VectorCopy (self->enemy->s.origin, spot2);
        !          1068:                        if (widow2_tongue_attack_ok(spot1, spot2, 256))
        !          1069:                        {
        !          1070:                                // melee attack ok
        !          1071: 
        !          1072:                                // be nice in easy mode
        !          1073:                                if (skill->value == 0 && (rand()&3) )
        !          1074:                                        return false;
        !          1075: 
        !          1076:                                if (self->monsterinfo.melee)
        !          1077:                                        self->monsterinfo.attack_state = AS_MELEE;
        !          1078:                                else
        !          1079:                                        self->monsterinfo.attack_state = AS_MISSILE;
        !          1080:                                return true;
        !          1081:                        }
        !          1082:                }
        !          1083:        }
        !          1084:        
        !          1085:        if (level.time < self->monsterinfo.attack_finished)
        !          1086:                return false;
        !          1087:                
        !          1088:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
        !          1089:        {
        !          1090:                chance = 0.4;
        !          1091:        }
        !          1092:        else if (enemy_range == RANGE_NEAR)
        !          1093:        {
        !          1094:                chance = 0.8;
        !          1095:        }
        !          1096:        else if (enemy_range == RANGE_MID)
        !          1097:        {
        !          1098:                chance = 0.8;
        !          1099:        }
        !          1100:        else if (enemy_range == RANGE_FAR)
        !          1101:        {
        !          1102:                chance = 0.5;
        !          1103:        }
        !          1104: 
        !          1105:        // PGM - go ahead and shoot every time if it's a info_notnull
        !          1106:        if ((random () < chance) || (self->enemy->solid == SOLID_NOT))
        !          1107:        {
        !          1108:                self->monsterinfo.attack_state = AS_MISSILE;
        !          1109: //             self->monsterinfo.attack_finished = level.time + 1.0 + 2*random();
        !          1110:                return true;
        !          1111:        }
        !          1112: 
        !          1113:        return false;
        !          1114: }
        !          1115: 
        !          1116: void Widow2Precache ()
        !          1117: {
        !          1118:        // cache in all of the stalker stuff, widow stuff, spawngro stuff, gibs
        !          1119:        gi.soundindex ("parasite/parpain1.wav");        
        !          1120:        gi.soundindex ("parasite/parpain2.wav");        
        !          1121:        gi.soundindex ("parasite/pardeth1.wav");        
        !          1122:        gi.soundindex ("parasite/paratck1.wav");
        !          1123:        gi.soundindex ("parasite/parsght1.wav");
        !          1124:        gi.soundindex ("infantry/melee2.wav");
        !          1125:        gi.soundindex ("misc/fhit3.wav");
        !          1126: 
        !          1127:        gi.soundindex ("tank/tnkatck3.wav");
        !          1128:        gi.soundindex ("weapons/disrupt.wav");
        !          1129:        gi.soundindex ("weapons/disint2.wav");
        !          1130: 
        !          1131:        gi.modelindex ("models/monsters/stalker/tris.md2");
        !          1132:        gi.modelindex ("models/items/spawngro2/tris.md2");
        !          1133:        gi.modelindex ("models/objects/gibs/sm_metal/tris.md2");
        !          1134:        gi.modelindex ("models/proj/laser2/tris.md2");
        !          1135:        gi.modelindex ("models/proj/disintegrator/tris.md2");
        !          1136: 
        !          1137:        gi.modelindex ("models/monsters/blackwidow/gib1/tris.md2");
        !          1138:        gi.modelindex ("models/monsters/blackwidow/gib2/tris.md2");
        !          1139:        gi.modelindex ("models/monsters/blackwidow/gib3/tris.md2");
        !          1140:        gi.modelindex ("models/monsters/blackwidow/gib4/tris.md2");
        !          1141:        gi.modelindex ("models/monsters/blackwidow2/gib1/tris.md2");
        !          1142:        gi.modelindex ("models/monsters/blackwidow2/gib2/tris.md2");
        !          1143:        gi.modelindex ("models/monsters/blackwidow2/gib3/tris.md2");
        !          1144:        gi.modelindex ("models/monsters/blackwidow2/gib4/tris.md2");
        !          1145: }
        !          1146: 
        !          1147: /*QUAKED monster_widow2 (1 .5 0) (-70 -70 0) (70 70 144) Ambush Trigger_Spawn Sight
        !          1148: */
        !          1149: void SP_monster_widow2 (edict_t *self)
        !          1150: {
        !          1151:        if (deathmatch->value)
        !          1152:        {
        !          1153:                G_FreeEdict (self);
        !          1154:                return;
        !          1155:        }
        !          1156: 
        !          1157:        sound_pain1 = gi.soundindex ("bosshovr/bhvpain1.wav");
        !          1158:        sound_pain2 = gi.soundindex ("bosshovr/bhvpain2.wav");
        !          1159:        sound_pain3 = gi.soundindex ("bosshovr/bhvpain3.wav");
        !          1160:        sound_death = gi.soundindex ("bosshovr/bhvdeth1.wav");
        !          1161:        sound_search1 = gi.soundindex ("bosshovr/bhvunqv1.wav");
        !          1162:        sound_disrupt = gi.soundindex ("gladiator/railgun.wav");
        !          1163:        sound_tentacles_retract = gi.soundindex ("brain/brnatck3.wav");
        !          1164: 
        !          1165:        self->s.sound = gi.soundindex ("bosshovr/bhvengn1.wav");
        !          1166: 
        !          1167:        self->movetype = MOVETYPE_STEP;
        !          1168:        self->solid = SOLID_BBOX;
        !          1169:        self->s.modelindex = gi.modelindex ("models/monsters/blackwidow2/tris.md2");
        !          1170:        VectorSet (self->mins, -70, -70, 0);
        !          1171:        VectorSet (self->maxs, 70, 70, 144);
        !          1172: 
        !          1173:        self->health = 2000 + 1000*(skill->value);
        !          1174:        if (coop->value)
        !          1175:                self->health += 500*(skill->value);
        !          1176: //     self->health = 1;
        !          1177:        self->gib_health = -900;
        !          1178:        self->mass = 2500;
        !          1179: 
        !          1180: /*     if (skill->value == 2)
        !          1181:        {
        !          1182:                self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
        !          1183:                self->monsterinfo.power_armor_power = 500;
        !          1184:        }
        !          1185:        else */if (skill->value == 3)
        !          1186:        {
        !          1187:                self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
        !          1188:                self->monsterinfo.power_armor_power = 750;
        !          1189:        }
        !          1190: 
        !          1191:        self->yaw_speed = 30;
        !          1192:        
        !          1193:        self->flags |= FL_IMMUNE_LASER;
        !          1194:        self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
        !          1195: 
        !          1196:        self->pain = widow2_pain;
        !          1197:        self->die = widow2_die;
        !          1198: 
        !          1199:        self->monsterinfo.melee = widow2_melee;
        !          1200:        self->monsterinfo.stand = widow2_stand;
        !          1201:        self->monsterinfo.walk = widow2_walk;
        !          1202:        self->monsterinfo.run = widow2_run;
        !          1203:        self->monsterinfo.attack = widow2_attack;
        !          1204:        self->monsterinfo.search = widow2_search;
        !          1205:        self->monsterinfo.checkattack = Widow2_CheckAttack;
        !          1206:        gi.linkentity (self);
        !          1207: 
        !          1208:        self->monsterinfo.currentmove = &widow2_move_stand;     
        !          1209:        self->monsterinfo.scale = MODEL_SCALE;
        !          1210: 
        !          1211:        Widow2Precache();
        !          1212:        WidowCalcSlots(self);
        !          1213:        walkmonster_start (self);
        !          1214: }
        !          1215: 
        !          1216: //
        !          1217: // Death sequence stuff
        !          1218: //
        !          1219: 
        !          1220: void WidowVelocityForDamage (int damage, vec3_t v)
        !          1221: {
        !          1222:        v[0] = damage * crandom();
        !          1223:        v[1] = damage * crandom();
        !          1224:        v[2] = damage * crandom() + 200.0;
        !          1225: }
        !          1226: 
        !          1227: void widow_gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
        !          1228: {
        !          1229: 
        !          1230:        self->solid = SOLID_NOT;
        !          1231:        self->touch = NULL;
        !          1232:        self->s.angles[PITCH] = 0;
        !          1233:        self->s.angles[ROLL] = 0;
        !          1234:        VectorClear (self->avelocity);
        !          1235: 
        !          1236:        if (self->plat2flags)
        !          1237:                gi.sound (self, CHAN_VOICE, self->plat2flags, 1, ATTN_NORM, 0);
        !          1238: /*
        !          1239:        if (plane)
        !          1240:        {
        !          1241:                if (plane->normal[2] < -0.8)
        !          1242:                {
        !          1243:                        gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0);
        !          1244:                }
        !          1245:                
        !          1246:                //vectoangles (plane->normal, normal_angles);
        !          1247:                //AngleVectors (normal_angles, NULL, right, NULL);
        !          1248:                //vectoangles (right, self->s.angles);
        !          1249:                //VectorClear (self->avelocity);
        !          1250:        }
        !          1251: */
        !          1252: }
        !          1253: 
        !          1254: void ThrowWidowGib (edict_t *self, char *gibname, int damage, int type)
        !          1255: {
        !          1256:        ThrowWidowGibReal (self, gibname, damage, type, NULL, false, 0, true);
        !          1257: }
        !          1258: 
        !          1259: void ThrowWidowGibLoc (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean fade)
        !          1260: {
        !          1261:        ThrowWidowGibReal (self, gibname, damage, type, startpos, false, 0, fade);
        !          1262: }
        !          1263: 
        !          1264: void ThrowWidowGibSized (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, int hitsound, qboolean fade)
        !          1265: {
        !          1266:        ThrowWidowGibReal (self, gibname, damage, type, startpos, true, hitsound, fade);
        !          1267: }
        !          1268: 
        !          1269: void ThrowWidowGibReal (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean sized, int hitsound, qboolean fade)
        !          1270: {
        !          1271:        edict_t *gib;
        !          1272:        vec3_t  vd;
        !          1273:        vec3_t  origin;
        !          1274:        vec3_t  size;
        !          1275:        float   vscale;
        !          1276: 
        !          1277:        if (!gibname)
        !          1278:                return;
        !          1279: 
        !          1280:        gib = G_Spawn();
        !          1281: 
        !          1282:        if (startpos)
        !          1283:                VectorCopy (startpos, gib->s.origin);
        !          1284:        else
        !          1285:        {
        !          1286:                VectorScale (self->size, 0.5, size);
        !          1287:                VectorAdd (self->absmin, size, origin);
        !          1288:                gib->s.origin[0] = origin[0] + crandom() * size[0];
        !          1289:                gib->s.origin[1] = origin[1] + crandom() * size[1];
        !          1290:                gib->s.origin[2] = origin[2] + crandom() * size[2];
        !          1291:        }
        !          1292: 
        !          1293:        gib->solid = SOLID_NOT;
        !          1294:        gib->s.effects |= EF_GIB;
        !          1295:        gib->flags |= FL_NO_KNOCKBACK;
        !          1296:        gib->takedamage = DAMAGE_YES;
        !          1297:        gib->die = gib_die;
        !          1298:        gib->s.renderfx |= RF_IR_VISIBLE;
        !          1299: 
        !          1300:        if (fade)
        !          1301:        {
        !          1302:                gib->think = G_FreeEdict;
        !          1303:                // sized gibs last longer
        !          1304:                if (sized)
        !          1305:                        gib->nextthink = level.time + 20 + random()*15;
        !          1306:                else
        !          1307:                        gib->nextthink = level.time + 10 + random()*10;
        !          1308:        }
        !          1309:        else
        !          1310:        {
        !          1311:                gib->think = G_FreeEdict;
        !          1312:                // sized gibs last longer
        !          1313:                if (sized)
        !          1314:                        gib->nextthink = level.time + 60 + random()*15;
        !          1315:                else
        !          1316:                        gib->nextthink = level.time + 25 + random()*10;
        !          1317:        }
        !          1318: 
        !          1319:        if (type == GIB_ORGANIC)
        !          1320:        {
        !          1321:                gib->movetype = MOVETYPE_TOSS;
        !          1322:                gib->touch = gib_touch;
        !          1323:                vscale = 0.5;
        !          1324:        }
        !          1325:        else
        !          1326:        {
        !          1327:                gib->movetype = MOVETYPE_BOUNCE;
        !          1328:                vscale = 1.0;
        !          1329:        }
        !          1330: 
        !          1331:        WidowVelocityForDamage (damage, vd);
        !          1332:        VectorMA (self->velocity, vscale, vd, gib->velocity);
        !          1333:        ClipGibVelocity (gib);
        !          1334: 
        !          1335:        gi.setmodel (gib, gibname);
        !          1336: 
        !          1337:        if (sized)
        !          1338:        {
        !          1339:                gib->plat2flags = hitsound;
        !          1340:                gib->solid = SOLID_BBOX;
        !          1341:                gib->avelocity[0] = random()*400;
        !          1342:                gib->avelocity[1] = random()*400;
        !          1343:                gib->avelocity[2] = random()*200;
        !          1344:                if (gib->velocity[2] < 0)
        !          1345:                        gib->velocity[2] *= -1;
        !          1346:                gib->velocity[2] = max((350 + (random()*100.0)), gib->velocity[2]);
        !          1347:                gib->gravity = 0.25;
        !          1348:                gib->touch = widow_gib_touch;
        !          1349:                gib->owner = self;
        !          1350:                if (gib->s.modelindex == gi.modelindex ("models/monsters/blackwidow2/gib2/tris.md2"))
        !          1351:                {
        !          1352:                        VectorSet (gib->mins, -10, -10, 0);
        !          1353:                        VectorSet (gib->maxs, 10, 10, 10);
        !          1354:                }
        !          1355:                else
        !          1356:                {
        !          1357:                        VectorSet (gib->mins, -5, -5, 0);
        !          1358:                        VectorSet (gib->maxs, 5, 5, 5);
        !          1359:                }
        !          1360:        }
        !          1361:        else
        !          1362:        {
        !          1363:                gib->velocity[0] *= 2;
        !          1364:                gib->velocity[1] *= 2;
        !          1365:                gib->avelocity[0] = random()*600;
        !          1366:                gib->avelocity[1] = random()*600;
        !          1367:                gib->avelocity[2] = random()*600;
        !          1368:        }
        !          1369: 
        !          1370: //     gib->think = G_FreeEdict;
        !          1371: //     gib->nextthink = level.time + 10 + random()*10;
        !          1372: 
        !          1373:        gi.linkentity (gib);
        !          1374: }
        !          1375: 
        !          1376: void BloodFountain (edict_t *self, int number, vec3_t startpos, int damage)
        !          1377: {
        !          1378:        int n;
        !          1379:        vec3_t  vd;
        !          1380:        vec3_t  origin, size, velocity;
        !          1381: 
        !          1382:        return;
        !          1383: 
        !          1384:        for (n= 0; n < number; n++)
        !          1385:        {
        !          1386:                if (startpos)
        !          1387:                        VectorCopy (startpos, origin);
        !          1388:                else
        !          1389:                {
        !          1390:                        VectorScale (self->size, 0.5, size);
        !          1391:                        VectorAdd (self->absmin, size, origin);
        !          1392:                        origin[0] = origin[0] + crandom() * size[0];
        !          1393:                        origin[1] = origin[1] + crandom() * size[1];
        !          1394:                        origin[2] = origin[2] + crandom() * size[2];
        !          1395:                }
        !          1396: 
        !          1397:                WidowVelocityForDamage (damage, vd);
        !          1398:                VectorMA (self->velocity, 1.0, vd, velocity);
        !          1399:                velocity[0] *= 2;
        !          1400:                velocity[1] *= 2;
        !          1401: 
        !          1402: //             gi.WriteByte (svc_temp_entity);
        !          1403: //             gi.WriteByte (TE_BLOOD_FOUNTAIN);
        !          1404: //             gi.WritePosition (origin);
        !          1405: //             gi.WritePosition (velocity);
        !          1406: //             gi.WriteShort (50);
        !          1407: //             gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1408:        }
        !          1409: }
        !          1410: 
        !          1411: void ThrowSmallStuff (edict_t *self, vec3_t point)
        !          1412: {
        !          1413:        int n;
        !          1414: 
        !          1415:        for (n= 0; n < 2; n++)
        !          1416:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, point, false);
        !          1417:        ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, point, false);
        !          1418:        ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 150, GIB_METALLIC, point, false);
        !          1419: 
        !          1420: }
        !          1421: 
        !          1422: void ThrowMoreStuff (edict_t *self, vec3_t point)
        !          1423: {
        !          1424:        int n;
        !          1425: 
        !          1426:        if (coop && coop->value)
        !          1427:        {
        !          1428:                ThrowSmallStuff (self, point);
        !          1429:                return;
        !          1430:        }
        !          1431: 
        !          1432:        for (n= 0; n < 2; n++)
        !          1433:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, point, false);
        !          1434:        for (n= 0; n < 2; n++)
        !          1435:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, point, false);
        !          1436:        for (n= 0; n < 3; n++)
        !          1437:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, point, false);
        !          1438: 
        !          1439: }
        !          1440: 
        !          1441: void WidowExplode (edict_t *self)
        !          1442: {
        !          1443:        vec3_t  org;
        !          1444:        int             n;
        !          1445: 
        !          1446:        self->think = WidowExplode;
        !          1447: //     gi.dprintf ("count = %d\n");
        !          1448: 
        !          1449: redo:
        !          1450:        VectorCopy (self->s.origin, org);
        !          1451:        org[2] += 24 + (rand()&15);
        !          1452:        if (self->count < 8)
        !          1453:                org[2] += 24 + (rand()&31);
        !          1454:        switch (self->count)
        !          1455:        {
        !          1456:        case 0:
        !          1457:                org[0] -= 24;
        !          1458:                org[1] -= 24;
        !          1459:                break;
        !          1460:        case 1:
        !          1461:                org[0] += 24;
        !          1462:                org[1] += 24;
        !          1463:                ThrowSmallStuff(self, org);
        !          1464:                break;
        !          1465:        case 2:
        !          1466:                org[0] += 24;
        !          1467:                org[1] -= 24;
        !          1468:                break;
        !          1469:        case 3:
        !          1470:                org[0] -= 24;
        !          1471:                org[1] += 24;
        !          1472:                ThrowMoreStuff(self, org);
        !          1473:                break;
        !          1474:        case 4:
        !          1475:                org[0] -= 48;
        !          1476:                org[1] -= 48;
        !          1477:                break;
        !          1478:        case 5:
        !          1479:                org[0] += 48;
        !          1480:                org[1] += 48;
        !          1481:                ThrowArm1 (self);
        !          1482:                break;
        !          1483:        case 6:
        !          1484:                org[0] -= 48;
        !          1485:                org[1] += 48;
        !          1486:                ThrowArm2 (self);
        !          1487:                break;
        !          1488:        case 7:
        !          1489:                org[0] += 48;
        !          1490:                org[1] -= 48;
        !          1491:                ThrowSmallStuff(self, org);
        !          1492:                break;
        !          1493:        case 8:
        !          1494:                org[0] += 18;
        !          1495:                org[1] += 18;
        !          1496:                org[2] = self->s.origin[2] + 48;
        !          1497:                ThrowMoreStuff(self, org);
        !          1498:                break;
        !          1499:        case 9:
        !          1500:                org[0] -= 18;
        !          1501:                org[1] += 18;
        !          1502:                org[2] = self->s.origin[2] + 48;
        !          1503:                break;
        !          1504:        case 10:
        !          1505:                org[0] += 18;
        !          1506:                org[1] -= 18;
        !          1507:                org[2] = self->s.origin[2] + 48;
        !          1508:                break;
        !          1509:        case 11:
        !          1510:                org[0] -= 18;
        !          1511:                org[1] -= 18;
        !          1512:                org[2] = self->s.origin[2] + 48;
        !          1513:                break;
        !          1514:        case 12:
        !          1515:                self->s.sound = 0;
        !          1516:                for (n= 0; n < 2; n++)
        !          1517:                        ThrowWidowGib (self, "models/objects/gibs/sm_meat/tris.md2", 400, GIB_ORGANIC);
        !          1518:                for (n= 0; n < 2; n++)
        !          1519:                        ThrowWidowGib (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC);
        !          1520:                for (n= 0; n < 2; n++)
        !          1521:                        ThrowWidowGib (self, "models/objects/gibs/sm_metal/tris.md2", 400, GIB_METALLIC);
        !          1522: //             ThrowGib (self, "models/objects/gibs/chest/tris.md2", 1000, GIB_ORGANIC);
        !          1523: //             ThrowHead (self, "models/objects/gibs/gear/tris.md2", 1000, GIB_METALLIC);
        !          1524:                self->deadflag = DEAD_DEAD;
        !          1525:                self->think = monster_think;
        !          1526:                self->nextthink = level.time + 0.1;
        !          1527:                self->monsterinfo.currentmove = &widow2_move_dead;
        !          1528:                return;
        !          1529:        }
        !          1530: 
        !          1531:        self->count++;
        !          1532:        if (self->count >=9 && self->count <=12)
        !          1533:        {
        !          1534:                gi.WriteByte (svc_temp_entity);
        !          1535:                gi.WriteByte (TE_EXPLOSION1_BIG);
        !          1536:                gi.WritePosition (org);
        !          1537:                gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1538:                goto redo;
        !          1539:        }
        !          1540:        // else
        !          1541:        gi.WriteByte (svc_temp_entity);
        !          1542:        if (self->count %2)
        !          1543:                gi.WriteByte (TE_EXPLOSION1);
        !          1544:        else
        !          1545:                gi.WriteByte (TE_EXPLOSION1_NP);
        !          1546:        gi.WritePosition (org);
        !          1547:        gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1548: 
        !          1549:        self->nextthink = level.time + 0.1;
        !          1550: }
        !          1551: 
        !          1552: void WidowExplosion1 (edict_t *self)
        !          1553: {
        !          1554:        int             n;
        !          1555:        vec3_t  f,r,u, startpoint;
        !          1556:        vec3_t  offset = {23.74, -37.67, 76.96};
        !          1557: 
        !          1558: //     gi.dprintf ("1\n");
        !          1559:        AngleVectors (self->s.angles, f, r, u);
        !          1560:        G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
        !          1561: 
        !          1562:        gi.WriteByte (svc_temp_entity);
        !          1563:        gi.WriteByte (TE_EXPLOSION1);
        !          1564:        gi.WritePosition (startpoint);
        !          1565:        gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1566:        
        !          1567:        for (n= 0; n < 2; n++)
        !          1568:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
        !          1569:        for (n= 0; n < 2; n++)
        !          1570:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
        !          1571:        for (n= 0; n < 2; n++)
        !          1572:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
        !          1573: }
        !          1574: 
        !          1575: void WidowExplosion2 (edict_t *self)
        !          1576: {
        !          1577:        int             n;
        !          1578:        vec3_t  f,r,u, startpoint;
        !          1579:        vec3_t  offset = {-20.49, 36.92, 73.52};
        !          1580: 
        !          1581: //     gi.dprintf ("2\n");
        !          1582: 
        !          1583:        AngleVectors (self->s.angles, f, r, u);
        !          1584:        G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
        !          1585: 
        !          1586:        gi.WriteByte (svc_temp_entity);
        !          1587:        gi.WriteByte (TE_EXPLOSION1);
        !          1588:        gi.WritePosition (startpoint);
        !          1589:        gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1590:        
        !          1591:        for (n= 0; n < 2; n++)
        !          1592:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
        !          1593:        for (n= 0; n < 2; n++)
        !          1594:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
        !          1595:        for (n= 0; n < 2; n++)
        !          1596:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
        !          1597: }
        !          1598: 
        !          1599: void WidowExplosion3 (edict_t *self)
        !          1600: {
        !          1601:        int             n;
        !          1602:        vec3_t  f,r,u, startpoint;
        !          1603:        vec3_t  offset = {2.11, 0.05, 92.20};
        !          1604: 
        !          1605: //     gi.dprintf ("3\n");
        !          1606: 
        !          1607:        AngleVectors (self->s.angles, f, r, u);
        !          1608:        G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
        !          1609: 
        !          1610:        gi.WriteByte (svc_temp_entity);
        !          1611:        gi.WriteByte (TE_EXPLOSION1);
        !          1612:        gi.WritePosition (startpoint);
        !          1613:        gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1614:        
        !          1615:        for (n= 0; n < 2; n++)
        !          1616:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
        !          1617:        for (n= 0; n < 2; n++)
        !          1618:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
        !          1619:        for (n= 0; n < 2; n++)
        !          1620:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
        !          1621: }
        !          1622: 
        !          1623: void WidowExplosion4 (edict_t *self)
        !          1624: {
        !          1625:        int             n;
        !          1626:        vec3_t  f,r,u, startpoint;
        !          1627:        vec3_t  offset = {-28.04, -35.57, -77.56};
        !          1628: 
        !          1629: //     gi.dprintf ("4\n");
        !          1630: 
        !          1631:        AngleVectors (self->s.angles, f, r, u);
        !          1632:        G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
        !          1633: 
        !          1634:        gi.WriteByte (svc_temp_entity);
        !          1635:        gi.WriteByte (TE_EXPLOSION1);
        !          1636:        gi.WritePosition (startpoint);
        !          1637:        gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1638:        
        !          1639:        for (n= 0; n < 2; n++)
        !          1640:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
        !          1641:        for (n= 0; n < 2; n++)
        !          1642:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
        !          1643:        for (n= 0; n < 2; n++)
        !          1644:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
        !          1645: }
        !          1646: 
        !          1647: void WidowExplosion5 (edict_t *self)
        !          1648: {
        !          1649:        int             n;
        !          1650:        vec3_t  f,r,u, startpoint;
        !          1651:        vec3_t  offset = {-20.11, -1.11, 40.76};
        !          1652: 
        !          1653: //     gi.dprintf ("5\n");
        !          1654: 
        !          1655:        AngleVectors (self->s.angles, f, r, u);
        !          1656:        G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
        !          1657: 
        !          1658:        gi.WriteByte (svc_temp_entity);
        !          1659:        gi.WriteByte (TE_EXPLOSION1);
        !          1660:        gi.WritePosition (startpoint);
        !          1661:        gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1662:        
        !          1663:        for (n= 0; n < 2; n++)
        !          1664:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
        !          1665:        for (n= 0; n < 2; n++)
        !          1666:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
        !          1667:        for (n= 0; n < 2; n++)
        !          1668:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
        !          1669: }
        !          1670: 
        !          1671: void WidowExplosion6 (edict_t *self)
        !          1672: {
        !          1673:        int             n;
        !          1674:        vec3_t  f,r,u, startpoint;
        !          1675:        vec3_t  offset = {-20.11, -1.11, 40.76};
        !          1676: 
        !          1677:        //gi.dprintf ("6\n");
        !          1678: 
        !          1679:        AngleVectors (self->s.angles, f, r, u);
        !          1680:        G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
        !          1681: 
        !          1682:        gi.WriteByte (svc_temp_entity);
        !          1683:        gi.WriteByte (TE_EXPLOSION1);
        !          1684:        gi.WritePosition (startpoint);
        !          1685:        gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1686:        
        !          1687:        for (n= 0; n < 2; n++)
        !          1688:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
        !          1689:        for (n= 0; n < 2; n++)
        !          1690:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
        !          1691:        for (n= 0; n < 2; n++)
        !          1692:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
        !          1693: }
        !          1694: 
        !          1695: void WidowExplosion7 (edict_t *self)
        !          1696: {
        !          1697:        int             n;
        !          1698:        vec3_t  f,r,u, startpoint;
        !          1699:        vec3_t  offset = {-20.11, -1.11, 40.76};
        !          1700: 
        !          1701:        //gi.dprintf ("7\n");
        !          1702: 
        !          1703:        AngleVectors (self->s.angles, f, r, u);
        !          1704:        G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
        !          1705: 
        !          1706:        gi.WriteByte (svc_temp_entity);
        !          1707:        gi.WriteByte (TE_EXPLOSION1);
        !          1708:        gi.WritePosition (startpoint);
        !          1709:        gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1710:        
        !          1711:        for (n= 0; n < 2; n++)
        !          1712:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
        !          1713:        for (n= 0; n < 2; n++)
        !          1714:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
        !          1715:        for (n= 0; n < 2; n++)
        !          1716:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
        !          1717: }
        !          1718: 
        !          1719: void WidowExplosionLeg (edict_t *self)
        !          1720: {
        !          1721:        int             n;
        !          1722:        vec3_t  f,r,u, startpoint;
        !          1723:        vec3_t  offset1 = {-31.89, -47.86, 67.02};
        !          1724:        vec3_t  offset2 = {-44.9, -82.14, 54.72};
        !          1725: 
        !          1726:        //gi.dprintf ("Leg\n");
        !          1727: 
        !          1728:        AngleVectors (self->s.angles, f, r, u);
        !          1729:        G_ProjectSource2 (self->s.origin, offset1, f, r, u, startpoint);
        !          1730: 
        !          1731:        gi.WriteByte (svc_temp_entity);
        !          1732:        gi.WriteByte (TE_EXPLOSION1_BIG);
        !          1733:        gi.WritePosition (startpoint);
        !          1734:        gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1735: 
        !          1736:        ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib2/tris.md2", 200, GIB_METALLIC, startpoint, 
        !          1737:                gi.soundindex ("misc/fhit3.wav"), false);
        !          1738:        ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
        !          1739:        for (n= 0; n < 2; n++)
        !          1740:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
        !          1741: 
        !          1742:        G_ProjectSource2 (self->s.origin, offset2, f, r, u, startpoint);
        !          1743: 
        !          1744:        gi.WriteByte (svc_temp_entity);
        !          1745:        gi.WriteByte (TE_EXPLOSION1);
        !          1746:        gi.WritePosition (startpoint);
        !          1747:        gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1748: 
        !          1749:        ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib1/tris.md2", 300, GIB_METALLIC, startpoint,
        !          1750:                gi.soundindex ("misc/fhit3.wav"), false);
        !          1751:        ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
        !          1752:        for (n= 0; n < 2; n++)
        !          1753:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
        !          1754: }
        !          1755: 
        !          1756: void ThrowArm1 (edict_t *self)
        !          1757: {
        !          1758:        int             n;
        !          1759:        vec3_t  f,r,u, startpoint;
        !          1760:        vec3_t  offset1 = {65.76, 17.52, 7.56};
        !          1761: 
        !          1762:        AngleVectors (self->s.angles, f, r, u);
        !          1763:        G_ProjectSource2 (self->s.origin, offset1, f, r, u, startpoint);
        !          1764: 
        !          1765:        gi.WriteByte (svc_temp_entity);
        !          1766:        gi.WriteByte (TE_EXPLOSION1_BIG);
        !          1767:        gi.WritePosition (startpoint);
        !          1768:        gi.multicast (self->s.origin, MULTICAST_ALL);
        !          1769: 
        !          1770:        for (n= 0; n < 2; n++)
        !          1771:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
        !          1772: }
        !          1773: 
        !          1774: void ThrowArm2 (edict_t *self)
        !          1775: {
        !          1776:        int             n;
        !          1777:        vec3_t  f,r,u, startpoint;
        !          1778:        vec3_t  offset1 = {65.76, 17.52, 7.56};
        !          1779: 
        !          1780:        AngleVectors (self->s.angles, f, r, u);
        !          1781:        G_ProjectSource2 (self->s.origin, offset1, f, r, u, startpoint);
        !          1782: 
        !          1783:        ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib4/tris.md2", 200, GIB_METALLIC, startpoint, 
        !          1784:                gi.soundindex ("misc/fhit3.wav"), false);
        !          1785:        ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
        !          1786:        for (n= 0; n < 2; n++)
        !          1787:                ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 100, GIB_METALLIC, startpoint, false);
        !          1788: }

unix.superglobalmegacorp.com

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