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

1.1     ! root        1: /*
        !             2: ==============================================================================
        !             3: 
        !             4: TURRET
        !             5: 
        !             6: ==============================================================================
        !             7: */
        !             8: 
        !             9: #include "g_local.h"
        !            10: #include "m_turret.h"
        !            11: 
        !            12: #define SPAWN_BLASTER                  0x0008
        !            13: #define SPAWN_MACHINEGUN               0x0010
        !            14: #define SPAWN_ROCKET                   0x0020
        !            15: #define SPAWN_HEATBEAM                 0x0040
        !            16: #define SPAWN_WEAPONCHOICE             0x0078
        !            17: #define SPAWN_INSTANT_WEAPON   0x0050
        !            18: #define SPAWN_WALL_UNIT                        0x0080
        !            19: 
        !            20: extern qboolean FindTarget (edict_t *self);
        !            21: 
        !            22: void turret_run (edict_t *self);
        !            23: void TurretAim (edict_t *self);
        !            24: void turret_sight (edict_t *self, edict_t *other);
        !            25: void turret_search (edict_t *self);
        !            26: void turret_stand (edict_t *self);
        !            27: void turret_wake (edict_t *self);
        !            28: void turret_ready_gun (edict_t *self);
        !            29: void turret_run (edict_t *self);
        !            30: 
        !            31: void turret_attack (edict_t *self);
        !            32: mmove_t turret_move_fire;
        !            33: mmove_t turret_move_fire_blind;
        !            34: 
        !            35: 
        !            36: void TurretAim(edict_t *self)
        !            37: {
        !            38:        vec3_t  end, dir;
        !            39:        vec3_t  ang;
        !            40:        float   move, idealPitch, idealYaw, current, speed;
        !            41:        int             orientation;
        !            42: 
        !            43: // gi.dprintf("turret_aim: %d %d\n", self->s.frame, self->monsterinfo.nextframe);
        !            44: 
        !            45:        if(!self->enemy || self->enemy == world)
        !            46:        {
        !            47:                if(!FindTarget (self))
        !            48:                        return;
        !            49:        }
        !            50: 
        !            51:        // if turret is still in inactive mode, ready the gun, but don't aim
        !            52:        if(self->s.frame < FRAME_active01)
        !            53:        {
        !            54:                turret_ready_gun(self);
        !            55:                return;
        !            56:        }
        !            57:        // if turret is still readying, don't aim.
        !            58:        if(self->s.frame < FRAME_run01)
        !            59:                return;
        !            60: 
        !            61:        // PMM - blindfire aiming here
        !            62:        if (self->monsterinfo.currentmove == &turret_move_fire_blind)
        !            63:        {
        !            64:                VectorCopy(self->monsterinfo.blind_fire_target, end);
        !            65:                if (self->enemy->s.origin[2] < self->monsterinfo.blind_fire_target[2])
        !            66:                        end[2] += self->enemy->viewheight + 10;
        !            67:                else
        !            68:                        end[2] += self->enemy->mins[2] - 10;
        !            69:        }
        !            70:        else
        !            71:        {
        !            72:                VectorCopy(self->enemy->s.origin, end);
        !            73:                if (self->enemy->client)
        !            74:                        end[2] += self->enemy->viewheight;
        !            75:        }
        !            76: 
        !            77:        VectorSubtract(end, self->s.origin, dir);
        !            78:        vectoangles2(dir, ang);
        !            79: 
        !            80:        //
        !            81:        // Clamp first
        !            82:        //
        !            83: 
        !            84:        idealPitch = ang[PITCH];
        !            85:        idealYaw = ang[YAW];
        !            86: 
        !            87:        orientation = self->offset[1];
        !            88:        switch(orientation)
        !            89:        {
        !            90:                case -1:                        // up           pitch: 0 to 90
        !            91:                        if(idealPitch < -90)
        !            92:                                idealPitch += 360;
        !            93:                        if(idealPitch > -5)
        !            94:                                idealPitch = -5;
        !            95:                        break;
        !            96:                case -2:                        // down         pitch: -180 to -360
        !            97:                        if(idealPitch > -90)
        !            98:                                idealPitch -= 360;
        !            99:                        if(idealPitch < -355)
        !           100:                                idealPitch = -355;
        !           101:                        else if(idealPitch > -185)
        !           102:                                idealPitch = -185;
        !           103:                        break;
        !           104:                case 0:                         // +X           pitch: 0 to -90, -270 to -360 (or 0 to 90)
        !           105: //gi.dprintf("idealpitch %0.1f  idealyaw %0.1f\n", idealPitch, idealYaw);
        !           106:                        if(idealPitch < -180)
        !           107:                                idealPitch += 360;
        !           108: 
        !           109:                        if(idealPitch > 85)
        !           110:                                idealPitch = 85;
        !           111:                        else if(idealPitch < -85)
        !           112:                                idealPitch = -85;
        !           113: 
        !           114: //gi.dprintf("idealpitch %0.1f  idealyaw %0.1f\n", idealPitch, idealYaw);
        !           115:                                                        //                      yaw: 270 to 360, 0 to 90
        !           116:                                                        //                      yaw: -90 to 90 (270-360 == -90-0)
        !           117:                        if(idealYaw > 180)
        !           118:                                idealYaw -= 360;
        !           119:                        if(idealYaw > 85)
        !           120:                                idealYaw = 85;
        !           121:                        else if(idealYaw < -85)
        !           122:                                idealYaw = -85;
        !           123: //gi.dprintf("idealpitch %0.1f  idealyaw %0.1f\n", idealPitch, idealYaw);
        !           124:                        break;
        !           125:                case 90:                        // +Y   pitch: 0 to 90, -270 to -360 (or 0 to 90)
        !           126:                        if(idealPitch < -180)
        !           127:                                idealPitch += 360;
        !           128: 
        !           129:                        if(idealPitch > 85)
        !           130:                                idealPitch = 85;
        !           131:                        else if(idealPitch < -85)
        !           132:                                idealPitch = -85;
        !           133: 
        !           134:                                                        //                      yaw: 0 to 180
        !           135:                        if(idealYaw > 270)
        !           136:                                idealYaw -= 360;
        !           137:                        if(idealYaw > 175)      idealYaw = 175;
        !           138:                        else if(idealYaw < 5)   idealYaw = 5;
        !           139: 
        !           140:                        break;
        !           141:                case 180:                       // -X   pitch: 0 to 90, -270 to -360 (or 0 to 90)
        !           142:                        if(idealPitch < -180)
        !           143:                                idealPitch += 360;
        !           144: 
        !           145:                        if(idealPitch > 85)
        !           146:                                idealPitch = 85;
        !           147:                        else if(idealPitch < -85)
        !           148:                                idealPitch = -85;
        !           149: 
        !           150:                                                        //                      yaw: 90 to 270
        !           151:                        if(idealYaw > 265)      idealYaw = 265;
        !           152:                        else if(idealYaw < 95)  idealYaw = 95;
        !           153: 
        !           154:                        break;
        !           155:                case 270:                       // -Y   pitch: 0 to 90, -270 to -360 (or 0 to 90)
        !           156:                        if(idealPitch < -180)
        !           157:                                idealPitch += 360;
        !           158: 
        !           159:                        if(idealPitch > 85)
        !           160:                                idealPitch = 85;
        !           161:                        else if(idealPitch < -85)
        !           162:                                idealPitch = -85;
        !           163: 
        !           164:                                                        //                      yaw: 180 to 360
        !           165:                        if(idealYaw < 90)
        !           166:                                idealYaw += 360;
        !           167:                        if(idealYaw > 355)      idealYaw = 355;
        !           168:                        else if(idealYaw < 185) idealYaw = 185;
        !           169:                        break;
        !           170:        }
        !           171: 
        !           172:        //
        !           173:        // adjust pitch
        !           174:        //
        !           175:        current = self->s.angles[PITCH];
        !           176:        speed = self->yaw_speed;
        !           177: 
        !           178:        if(idealPitch != current)
        !           179:        {
        !           180:                move = idealPitch - current;
        !           181: 
        !           182:                while(move >= 360)
        !           183:                        move -= 360;
        !           184:                if (move >= 90)
        !           185:                {
        !           186:                        move = move - 360;
        !           187:                }
        !           188: 
        !           189:                while(move <= -360)
        !           190:                        move += 360;
        !           191:                if (move <= -90)
        !           192:                {
        !           193:                        move = move + 360;
        !           194:                }
        !           195: 
        !           196:                if (move > 0)
        !           197:                {
        !           198:                        if (move > speed)
        !           199:                                move = speed;
        !           200:                }
        !           201:                else
        !           202:                {
        !           203:                        if (move < -speed)
        !           204:                                move = -speed;
        !           205:                }
        !           206: 
        !           207:                self->s.angles[PITCH] = anglemod (current + move);
        !           208:        }
        !           209: 
        !           210:        //
        !           211:        // adjust yaw
        !           212:        //
        !           213:        current = self->s.angles[YAW];
        !           214:        speed = self->yaw_speed;
        !           215: 
        !           216:        if(idealYaw != current)
        !           217:        {
        !           218:                move = idealYaw - current;
        !           219: 
        !           220: //             while(move >= 360)
        !           221: //                     move -= 360;
        !           222:                if (move >= 180)
        !           223:                {
        !           224:                        move = move - 360;
        !           225:                }
        !           226: 
        !           227: //             while(move <= -360)
        !           228: //                     move += 360;
        !           229:                if (move <= -180)
        !           230:                {
        !           231:                        move = move + 360;
        !           232:                }
        !           233: 
        !           234:                if (move > 0)
        !           235:                {
        !           236:                        if (move > speed)
        !           237:                                move = speed;
        !           238:                }
        !           239:                else
        !           240:                {
        !           241:                        if (move < -speed)
        !           242:                                move = -speed;
        !           243:                }
        !           244: 
        !           245:                self->s.angles[YAW] = anglemod (current + move);
        !           246:        }
        !           247: 
        !           248: }
        !           249: 
        !           250: void turret_sight (edict_t *self, edict_t *other)
        !           251: {
        !           252: }
        !           253: 
        !           254: void turret_search (edict_t *self)
        !           255: {
        !           256: }
        !           257: 
        !           258: mframe_t turret_frames_stand [] =
        !           259: {
        !           260:        ai_stand, 0, NULL,
        !           261:        ai_stand, 0, NULL
        !           262: };
        !           263: mmove_t turret_move_stand = {FRAME_stand01, FRAME_stand02, turret_frames_stand, NULL};
        !           264: 
        !           265: void turret_stand (edict_t *self)
        !           266: {
        !           267: //gi.dprintf("turret_stand\n");
        !           268:        self->monsterinfo.currentmove = &turret_move_stand;
        !           269: }
        !           270: 
        !           271: mframe_t turret_frames_ready_gun [] =
        !           272: {
        !           273:        ai_stand, 0, NULL,
        !           274:        ai_stand, 0, NULL,
        !           275:        ai_stand, 0, NULL,
        !           276: 
        !           277:        ai_stand, 0, NULL,
        !           278:        ai_stand, 0, NULL,
        !           279:        ai_stand, 0, NULL,
        !           280:        
        !           281:        ai_stand, 0, NULL
        !           282: };
        !           283: mmove_t turret_move_ready_gun = { FRAME_active01, FRAME_run01, turret_frames_ready_gun, turret_run };
        !           284: 
        !           285: void turret_ready_gun (edict_t *self)
        !           286: {
        !           287:        self->monsterinfo.currentmove = &turret_move_ready_gun;
        !           288: }
        !           289: 
        !           290: mframe_t turret_frames_seek [] =
        !           291: {
        !           292:        ai_walk, 0, TurretAim,
        !           293:        ai_walk, 0, TurretAim
        !           294: };
        !           295: mmove_t turret_move_seek = {FRAME_run01, FRAME_run02, turret_frames_seek, NULL};
        !           296: 
        !           297: void turret_walk (edict_t *self)
        !           298: {
        !           299:        if(self->s.frame < FRAME_run01)
        !           300:                turret_ready_gun(self);
        !           301:        else
        !           302:                self->monsterinfo.currentmove = &turret_move_seek;
        !           303: }
        !           304: 
        !           305: 
        !           306: mframe_t turret_frames_run [] =
        !           307: {
        !           308:        ai_run, 0, TurretAim,
        !           309:        ai_run, 0, TurretAim
        !           310: };
        !           311: mmove_t turret_move_run = {FRAME_run01, FRAME_run02, turret_frames_run, turret_run};
        !           312: 
        !           313: void turret_run (edict_t *self)
        !           314: {
        !           315:        if(self->s.frame < FRAME_run01)
        !           316:                turret_ready_gun(self);
        !           317:        else
        !           318:                self->monsterinfo.currentmove = &turret_move_run;
        !           319: }
        !           320: 
        !           321: // **********************
        !           322: //  ATTACK
        !           323: // **********************
        !           324: 
        !           325: #define TURRET_BULLET_DAMAGE   4
        !           326: #define TURRET_HEAT_DAMAGE             4
        !           327: 
        !           328: void TurretFire (edict_t *self)
        !           329: {
        !           330:        vec3_t  forward;
        !           331:        vec3_t  start, end, dir;
        !           332:        float   time, dist, chance;
        !           333:        trace_t trace;
        !           334:        int             rocketSpeed;
        !           335: 
        !           336:        TurretAim(self);
        !           337: 
        !           338:        if(!self->enemy || !self->enemy->inuse)
        !           339:                return;
        !           340: 
        !           341:        VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
        !           342:        VectorNormalize(dir);
        !           343:        AngleVectors(self->s.angles, forward, NULL, NULL);
        !           344:        chance = DotProduct(dir, forward);
        !           345:        if(chance < 0.98)
        !           346:        {
        !           347: //             gi.dprintf("off-angle\n");
        !           348:                return;
        !           349:        }
        !           350: 
        !           351:        chance = random();
        !           352: 
        !           353:        // rockets fire less often than the others do.
        !           354:        if (self->spawnflags & SPAWN_ROCKET)
        !           355:        {
        !           356:                chance = chance * 3;
        !           357: 
        !           358:                rocketSpeed = 550;
        !           359:                if (skill->value == 2)
        !           360:                {
        !           361:                        rocketSpeed += 200 * random();
        !           362:                }
        !           363:                else if (skill->value == 3)
        !           364:                {
        !           365:                        rocketSpeed += 100 + (200 * random());
        !           366:                }
        !           367:        }
        !           368:        else if (self->spawnflags & SPAWN_BLASTER)
        !           369:        {
        !           370:                chance = chance * 2;
        !           371:        }
        !           372:        
        !           373:        // up the fire chance 20% per skill level.
        !           374:        chance = chance - (0.2 * skill->value);
        !           375: 
        !           376:        if(/*chance < 0.5 && */visible(self, self->enemy))
        !           377:        {
        !           378:                VectorCopy(self->s.origin, start);
        !           379:                VectorCopy(self->enemy->s.origin, end);
        !           380:                
        !           381:                // aim for the head.
        !           382:                if ((self->enemy) && (self->enemy->client))
        !           383:                        end[2]+=self->enemy->viewheight;
        !           384:                else
        !           385:                        end[2]+=22;
        !           386: 
        !           387:                VectorSubtract(end, start, dir);
        !           388:                dist = VectorLength(dir);
        !           389:                
        !           390:                // check for predictive fire if distance less than 512
        !           391:                if(!(self->spawnflags & SPAWN_INSTANT_WEAPON) && (dist<512))
        !           392:                {
        !           393:                        chance = random();
        !           394:                        // ramp chance. easy - 50%, avg - 60%, hard - 70%, nightmare - 80%
        !           395:                        chance += (3 - skill->value) * 0.1;
        !           396:                        if(chance < 0.8)
        !           397:                        {
        !           398:                                // lead the target....
        !           399:                                time = dist / 1000;
        !           400:                                VectorMA(end, time, self->enemy->velocity, end);
        !           401:                                VectorSubtract(end, start, dir);
        !           402:                        }
        !           403:                }
        !           404: 
        !           405:                VectorNormalize(dir);
        !           406:                trace = gi.trace(start, vec3_origin, vec3_origin, end, self, MASK_SHOT);
        !           407:                if(trace.ent == self->enemy || trace.ent == world)
        !           408:                {
        !           409:                        if(self->spawnflags & SPAWN_BLASTER)
        !           410:                                monster_fire_blaster(self, start, dir, 20, 1000, MZ2_TURRET_BLASTER, EF_BLASTER);
        !           411:                        else if(self->spawnflags & SPAWN_MACHINEGUN)
        !           412:                                monster_fire_bullet (self, start, dir, TURRET_BULLET_DAMAGE, 0, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_TURRET_MACHINEGUN);
        !           413:                        else if(self->spawnflags & SPAWN_ROCKET)
        !           414:                        {
        !           415:                                if(dist * trace.fraction > 72)
        !           416:                                        monster_fire_rocket (self, start, dir, 50, rocketSpeed, MZ2_TURRET_ROCKET);
        !           417:                        }
        !           418:                }       
        !           419:        }
        !           420: }
        !           421: 
        !           422: // PMM
        !           423: void TurretFireBlind (edict_t *self)
        !           424: {
        !           425:        vec3_t  forward;
        !           426:        vec3_t  start, end, dir;
        !           427:        float   dist, chance;
        !           428:        int             rocketSpeed;
        !           429: 
        !           430:        TurretAim(self);
        !           431: 
        !           432:        if(!self->enemy || !self->enemy->inuse)
        !           433:                return;
        !           434: 
        !           435:        VectorSubtract(self->monsterinfo.blind_fire_target, self->s.origin, dir);
        !           436:        VectorNormalize(dir);
        !           437:        AngleVectors(self->s.angles, forward, NULL, NULL);
        !           438:        chance = DotProduct(dir, forward);
        !           439:        if(chance < 0.98)
        !           440:        {
        !           441: //             gi.dprintf("off-angle\n");
        !           442:                return;
        !           443:        }
        !           444: 
        !           445:        if (self->spawnflags & SPAWN_ROCKET)
        !           446:        {
        !           447:                rocketSpeed = 550;
        !           448:                if (skill->value == 2)
        !           449:                {
        !           450:                        rocketSpeed += 200 * random();
        !           451:                }
        !           452:                else if (skill->value == 3)
        !           453:                {
        !           454:                        rocketSpeed += 100 + (200 * random());
        !           455:                }
        !           456:        }
        !           457: 
        !           458:        VectorCopy(self->s.origin, start);
        !           459:        VectorCopy(self->monsterinfo.blind_fire_target, end);
        !           460:                
        !           461:        if (self->enemy->s.origin[2] < self->monsterinfo.blind_fire_target[2])
        !           462:                end[2] += self->enemy->viewheight + 10;
        !           463:        else
        !           464:                end[2] += self->enemy->mins[2] - 10;
        !           465: 
        !           466:        VectorSubtract(end, start, dir);
        !           467:        dist = VectorLength(dir);
        !           468:                
        !           469:        VectorNormalize(dir);
        !           470: 
        !           471:        if(self->spawnflags & SPAWN_BLASTER)
        !           472:                monster_fire_blaster(self, start, dir, 20, 1000, MZ2_TURRET_BLASTER, EF_BLASTER);
        !           473:        else if(self->spawnflags & SPAWN_ROCKET)
        !           474:                monster_fire_rocket (self, start, dir, 50, rocketSpeed, MZ2_TURRET_ROCKET);
        !           475: }
        !           476: //pmm
        !           477: 
        !           478: mframe_t turret_frames_fire [] =
        !           479: {
        !           480:        ai_run,   0, TurretFire,
        !           481:        ai_run,   0, TurretAim,
        !           482:        ai_run,   0, TurretAim,
        !           483:        ai_run,   0, TurretAim
        !           484: };
        !           485: mmove_t turret_move_fire = {FRAME_pow01, FRAME_pow04, turret_frames_fire, turret_run};
        !           486: 
        !           487: //PMM
        !           488: 
        !           489: // the blind frames need to aim first
        !           490: mframe_t turret_frames_fire_blind [] =
        !           491: {
        !           492:        ai_run,   0, TurretAim,
        !           493:        ai_run,   0, TurretAim,
        !           494:        ai_run,   0, TurretAim,
        !           495:        ai_run,   0, TurretFireBlind
        !           496: };
        !           497: mmove_t turret_move_fire_blind = {FRAME_pow01, FRAME_pow04, turret_frames_fire_blind, turret_run};
        !           498: //pmm
        !           499: 
        !           500: void turret_attack(edict_t *self)
        !           501: {
        !           502:        float r, chance;
        !           503: 
        !           504:        if(self->s.frame < FRAME_run01)
        !           505:                turret_ready_gun(self);
        !           506:        // PMM
        !           507:        else if (self->monsterinfo.attack_state != AS_BLIND)
        !           508:        {
        !           509:                self->monsterinfo.nextframe = FRAME_pow01;
        !           510:                self->monsterinfo.currentmove = &turret_move_fire;
        !           511:        }
        !           512:        else
        !           513:        {
        !           514:                // setup shot probabilities
        !           515:                if (self->monsterinfo.blind_fire_delay < 1.0)
        !           516:                        chance = 1.0;
        !           517:                else if (self->monsterinfo.blind_fire_delay < 7.5)
        !           518:                        chance = 0.4;
        !           519:                else
        !           520:                        chance = 0.1;
        !           521: 
        !           522:                r = random();
        !           523: 
        !           524:                if ((g_showlogic) && (g_showlogic->value))
        !           525:                        gi.dprintf ("chance = %2.2f, roll = %2.2f\n", chance, r);
        !           526: 
        !           527:                // minimum of 3 seconds, plus 0-4, after the shots are done - total time should be max less than 7.5
        !           528:                self->monsterinfo.blind_fire_delay += 0.4 + 3.0 + random()*4.0;
        !           529:                // don't shoot at the origin
        !           530:                if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin))
        !           531:                        return;
        !           532: 
        !           533:                // don't shoot if the dice say not to
        !           534:                if (r > chance)
        !           535:                {
        !           536:                        if ((g_showlogic) && (g_showlogic->value))
        !           537:                                gi.dprintf ("blindfire - NO SHOT\n");
        !           538:                        return;
        !           539:                }
        !           540: 
        !           541:                self->monsterinfo.nextframe = FRAME_pow01;
        !           542:                self->monsterinfo.currentmove = &turret_move_fire_blind;
        !           543:        }
        !           544:        // pmm
        !           545: }
        !           546: 
        !           547: // **********************
        !           548: //  PAIN
        !           549: // **********************
        !           550: 
        !           551: void turret_pain (edict_t *self, edict_t *other, float kick, int damage)
        !           552: {
        !           553:        return;
        !           554: }
        !           555: 
        !           556: // **********************
        !           557: //  DEATH
        !           558: // **********************
        !           559: 
        !           560: void turret_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
        !           561: {
        !           562:        vec3_t          forward;
        !           563:        vec3_t          start;
        !           564:        edict_t         *base;
        !           565: 
        !           566:        gi.WriteByte (svc_temp_entity);
        !           567:        gi.WriteByte (TE_PLAIN_EXPLOSION);
        !           568:        gi.WritePosition (self->s.origin);
        !           569:        gi.multicast (self->s.origin, MULTICAST_PHS);
        !           570: 
        !           571:        AngleVectors(self->s.angles, forward, NULL, NULL);
        !           572:        VectorMA(self->s.origin, 1, forward, start);
        !           573: 
        !           574:        ThrowDebris (self, "models/objects/debris1/tris.md2", 1, start);
        !           575:        ThrowDebris (self, "models/objects/debris1/tris.md2", 2, start);
        !           576:        ThrowDebris (self, "models/objects/debris1/tris.md2", 1, start);
        !           577:        ThrowDebris (self, "models/objects/debris1/tris.md2", 2, start);
        !           578: 
        !           579:        if(self->teamchain)
        !           580:        {
        !           581:                base = self->teamchain;
        !           582:                base->solid = SOLID_BBOX;
        !           583:                base->takedamage = DAMAGE_NO;
        !           584:                base->movetype = MOVETYPE_NONE;
        !           585:                gi.linkentity (base);
        !           586:        }
        !           587: 
        !           588:        if(self->target)
        !           589:        {
        !           590:                if(self->enemy && self->enemy->inuse)
        !           591:                        G_UseTargets (self, self->enemy);
        !           592:                else
        !           593:                        G_UseTargets (self, self);
        !           594:        }
        !           595: 
        !           596:        G_FreeEdict(self);
        !           597: }
        !           598: 
        !           599: // **********************
        !           600: //  WALL SPAWN
        !           601: // **********************
        !           602: 
        !           603: void turret_wall_spawn (edict_t *turret)
        !           604: {
        !           605:        edict_t         *ent;
        !           606:        int                     angle;
        !           607: 
        !           608:        ent = G_Spawn();
        !           609:        VectorCopy(turret->s.origin, ent->s.origin);
        !           610:        VectorCopy(turret->s.angles, ent->s.angles);
        !           611:        
        !           612:        angle = ent->s.angles[1];
        !           613:        if(ent->s.angles[0] == 90)
        !           614:                angle = -1;
        !           615:        else if(ent->s.angles[0] == 270)
        !           616:                angle = -2;
        !           617:        switch (angle)
        !           618:        {
        !           619:                case -1:
        !           620:                        VectorSet(ent->mins, -16, -16, -8);
        !           621:                        VectorSet(ent->maxs, 16, 16, 0);
        !           622:                        break;
        !           623:                case -2:
        !           624:                        VectorSet(ent->mins, -16, -16, 0);
        !           625:                        VectorSet(ent->maxs, 16, 16, 8);
        !           626:                        break;
        !           627:                case 0:
        !           628:                        VectorSet(ent->mins, -8, -16, -16);
        !           629:                        VectorSet(ent->maxs, 0, 16, 16);
        !           630:                        break;
        !           631:                case 90:
        !           632:                        VectorSet(ent->mins, -16, -8, -16);
        !           633:                        VectorSet(ent->maxs, 16, 0, 16);
        !           634:                        break;
        !           635:                case 180:
        !           636:                        VectorSet(ent->mins, 0, -16, -16);
        !           637:                        VectorSet(ent->maxs, 8, 16, 16);
        !           638:                        break;
        !           639:                case 270:
        !           640:                        VectorSet(ent->mins, -16, 0, -16);
        !           641:                        VectorSet(ent->maxs, 16, 8, 16);
        !           642:                        break;
        !           643: 
        !           644:        }
        !           645: 
        !           646:        ent->movetype = MOVETYPE_PUSH;
        !           647:        ent->solid = SOLID_NOT;
        !           648: 
        !           649:        ent->teammaster = turret;
        !           650:        turret->teammaster = turret;
        !           651:        turret->teamchain = ent;
        !           652:        ent->teamchain = NULL;
        !           653:        ent->flags |= FL_TEAMSLAVE;
        !           654:        ent->owner = turret;
        !           655: 
        !           656:        ent->s.modelindex = gi.modelindex("models/monsters/turretbase/tris.md2");
        !           657: 
        !           658:        gi.linkentity (ent);
        !           659: }
        !           660: 
        !           661: void turret_wake (edict_t *self)
        !           662: {
        !           663:        // the wall section will call this when it stops moving.
        !           664:        // just return without doing anything. easiest way to have a null function.
        !           665:        if(self->flags & FL_TEAMSLAVE)
        !           666:        {
        !           667:                return;
        !           668:        }
        !           669: 
        !           670:        self->monsterinfo.stand = turret_stand;
        !           671:        self->monsterinfo.walk = turret_walk;
        !           672:        self->monsterinfo.run = turret_run;
        !           673:        self->monsterinfo.dodge = NULL;
        !           674:        self->monsterinfo.attack = turret_attack;
        !           675:        self->monsterinfo.melee = NULL;
        !           676:        self->monsterinfo.sight = turret_sight;
        !           677:        self->monsterinfo.search = turret_search;
        !           678:        self->monsterinfo.currentmove = &turret_move_stand;
        !           679:        self->takedamage = DAMAGE_AIM;
        !           680:        self->movetype = MOVETYPE_NONE;
        !           681:        // prevent counting twice
        !           682:        self->monsterinfo.aiflags |= AI_DO_NOT_COUNT;
        !           683: 
        !           684:        gi.linkentity (self);
        !           685: 
        !           686:        stationarymonster_start (self);
        !           687:        
        !           688:        if(self->spawnflags & SPAWN_MACHINEGUN)
        !           689:        {
        !           690:                self->s.skinnum = 1;
        !           691:        }
        !           692:        else if(self->spawnflags & SPAWN_ROCKET)
        !           693:        {
        !           694:                self->s.skinnum = 2;
        !           695:        }
        !           696: 
        !           697:        // but we do want the death to count
        !           698:        self->monsterinfo.aiflags &= ~AI_DO_NOT_COUNT;
        !           699: }
        !           700: 
        !           701: extern void Move_Calc (edict_t *ent, vec3_t dest, void(*func)(edict_t*));
        !           702: 
        !           703: void turret_activate (edict_t *self, edict_t *other, edict_t *activator)
        !           704: {
        !           705:        vec3_t          endpos;
        !           706:        vec3_t          forward;
        !           707:        edict_t         *base;
        !           708: 
        !           709:        self->movetype = MOVETYPE_PUSH;
        !           710:        if(!self->speed)
        !           711:                self->speed = 15;
        !           712:        self->moveinfo.speed = self->speed;
        !           713:        self->moveinfo.accel = self->speed;
        !           714:        self->moveinfo.decel = self->speed;
        !           715: 
        !           716:        if(self->s.angles[0] == 270)
        !           717:        {
        !           718:                VectorSet (forward, 0,0,1);
        !           719:        }
        !           720:        else if(self->s.angles[0] == 90)
        !           721:        {
        !           722:                VectorSet (forward, 0,0,-1);
        !           723:        }
        !           724:        else if(self->s.angles[1] == 0)
        !           725:        {
        !           726:                VectorSet (forward, 1,0,0);
        !           727:        }
        !           728:        else if(self->s.angles[1] == 90)
        !           729:        {
        !           730:                VectorSet (forward, 0,1,0);
        !           731:        }
        !           732:        else if(self->s.angles[1] == 180)
        !           733:        {
        !           734:                VectorSet (forward, -1,0,0);
        !           735:        }
        !           736:        else if(self->s.angles[1] == 270)
        !           737:        {
        !           738:                VectorSet (forward, 0,-1,0);
        !           739:        }
        !           740:        
        !           741:        // start up the turret
        !           742:        VectorMA(self->s.origin, 32, forward, endpos);
        !           743:        Move_Calc(self, endpos, turret_wake);
        !           744: 
        !           745:        base = self->teamchain;
        !           746:        if(base)
        !           747:        {
        !           748:                base->movetype = MOVETYPE_PUSH;
        !           749:                base->speed = self->speed;
        !           750:                base->moveinfo.speed = base->speed;
        !           751:                base->moveinfo.accel = base->speed;
        !           752:                base->moveinfo.decel = base->speed;
        !           753: 
        !           754:                // start up the wall section
        !           755:                VectorMA(self->teamchain->s.origin, 32, forward, endpos);
        !           756:                Move_Calc(self->teamchain, endpos, turret_wake);
        !           757:        }
        !           758: 
        !           759:        gi.sound (self, CHAN_VOICE, gi.soundindex ("world/dr_short.wav"), 1, ATTN_NORM, 0);
        !           760: }
        !           761: 
        !           762: // PMM
        !           763: // checkattack .. ignore range, just attack if available
        !           764: qboolean turret_checkattack (edict_t *self)
        !           765: {
        !           766:        vec3_t  spot1, spot2;
        !           767:        float   chance, nexttime;
        !           768:        trace_t tr;
        !           769:        int             enemy_range;
        !           770: 
        !           771:        if (self->enemy->health > 0)
        !           772:        {
        !           773:        // see if any entities are in the way of the shot
        !           774:                VectorCopy (self->s.origin, spot1);
        !           775:                spot1[2] += self->viewheight;
        !           776:                VectorCopy (self->enemy->s.origin, spot2);
        !           777:                spot2[2] += self->enemy->viewheight;
        !           778: 
        !           779:                tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WINDOW);
        !           780: 
        !           781:                // do we have a clear shot?
        !           782:                if (tr.ent != self->enemy)
        !           783:                {       
        !           784:                        // PGM - we want them to go ahead and shoot at info_notnulls if they can.
        !           785:                        if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0)                //PGM
        !           786:                        {
        !           787:                                // PMM - if we can't see our target, and we're not blocked by a monster, go into blind fire if available
        !           788:                                if ((!(tr.ent->svflags & SVF_MONSTER)) && (!visible(self, self->enemy)))
        !           789:                                {
        !           790:                                        if ((self->monsterinfo.blindfire) && (self->monsterinfo.blind_fire_delay <= 10.0))
        !           791:                                        {
        !           792:                                                if (level.time < self->monsterinfo.attack_finished)
        !           793:                                                {
        !           794:                                                        return false;
        !           795:                                                }
        !           796:                                                if (level.time < (self->monsterinfo.trail_time + self->monsterinfo.blind_fire_delay))
        !           797:                                                {
        !           798:                                                        // wait for our time
        !           799:                                                        return false;
        !           800:                                                }
        !           801:                                                else
        !           802:                                                {
        !           803:                                                        // make sure we're not going to shoot something we don't want to shoot
        !           804:                                                        tr = gi.trace (spot1, NULL, NULL, self->monsterinfo.blind_fire_target, self, CONTENTS_MONSTER);
        !           805:                                                        if (tr.allsolid || tr.startsolid || ((tr.fraction < 1.0) && (tr.ent != self->enemy)))
        !           806:                                                        {
        !           807:                                                                if ((g_showlogic) && (g_showlogic->value))
        !           808:                                                                        gi.dprintf ("blindfire blocked\n");
        !           809:                                                                return false;
        !           810:                                                        }
        !           811: 
        !           812:                                                        self->monsterinfo.attack_state = AS_BLIND;
        !           813:                                                        self->monsterinfo.attack_finished = level.time + 0.5 + 2*random();
        !           814:                                                        return true;
        !           815:                                                }
        !           816:                                        }
        !           817:                                }
        !           818:                                // pmm
        !           819:                                return false;
        !           820:                        }
        !           821:                }
        !           822:        }
        !           823:        
        !           824:        if (level.time < self->monsterinfo.attack_finished)
        !           825:                return false;
        !           826: 
        !           827:        enemy_range = range(self, self->enemy);
        !           828: 
        !           829:        if (enemy_range == RANGE_MELEE)
        !           830:        {
        !           831:                // don't always melee in easy mode
        !           832:                if (skill->value == 0 && (rand()&3) )
        !           833:                        return false;
        !           834:                self->monsterinfo.attack_state = AS_MISSILE;
        !           835:                return true;
        !           836:        }
        !           837:        
        !           838:        if (self->spawnflags & SPAWN_ROCKET)
        !           839:        {
        !           840:                chance = 0.10;
        !           841:                nexttime = (1.8 - (0.2 * skill->value));
        !           842:        }
        !           843:        else if(self->spawnflags & SPAWN_BLASTER)
        !           844:        {
        !           845:                chance = 0.35;
        !           846:                nexttime = (1.2 - (0.2 * skill->value));
        !           847:        }
        !           848:        else
        !           849:        {
        !           850:                chance = 0.50;
        !           851:                nexttime = (0.8 - (0.1 * skill->value));
        !           852:        }
        !           853: 
        !           854:        if (skill->value == 0)
        !           855:                chance *= 0.5;
        !           856:        else if (skill->value > 1)
        !           857:                chance *= 2;
        !           858: 
        !           859:        // PGM - go ahead and shoot every time if it's a info_notnull
        !           860:        // PMM - added visibility check
        !           861:        if ( ((random () < chance) && (visible(self, self->enemy))) || (self->enemy->solid == SOLID_NOT))
        !           862:        {
        !           863:                self->monsterinfo.attack_state = AS_MISSILE;
        !           864: //             self->monsterinfo.attack_finished = level.time + 0.3 + 2*random();
        !           865:                self->monsterinfo.attack_finished = level.time + nexttime;
        !           866:                return true;
        !           867:        }
        !           868: 
        !           869:        self->monsterinfo.attack_state = AS_STRAIGHT;
        !           870: 
        !           871:        return false;
        !           872: }
        !           873: 
        !           874: 
        !           875: // **********************
        !           876: //  SPAWN
        !           877: // **********************
        !           878: 
        !           879: /*QUAKED monster_turret (1 .5 0) (-16 -16 -16) (16 16 16) Ambush Trigger_Spawn Sight Blaster MachineGun Rocket Heatbeam WallUnit
        !           880: 
        !           881: The automated defense turret that mounts on walls. 
        !           882: Check the weapon you want it to use: blaster, machinegun, rocket, heatbeam.
        !           883: Default weapon is blaster.
        !           884: When activated, wall units move 32 units in the direction they're facing.
        !           885: */
        !           886: void SP_monster_turret (edict_t *self)
        !           887: {
        !           888:        int             angle;
        !           889: 
        !           890:        if (deathmatch->value)
        !           891:        {
        !           892:                G_FreeEdict (self);
        !           893:                return;
        !           894:        }
        !           895: 
        !           896:        // VERSIONING
        !           897:        if (g_showlogic && g_showlogic->value)
        !           898:                gi.dprintf ("%s\n", ROGUE_VERSION_STRING);
        !           899: 
        !           900:        self->plat2flags = ROGUE_VERSION_ID;
        !           901:        // versions
        !           902: 
        !           903:        // pre-caches
        !           904:        gi.soundindex ("world/dr_short.wav");
        !           905:        gi.modelindex ("models/objects/debris1/tris.md2");
        !           906: 
        !           907:        self->s.modelindex = gi.modelindex("models/monsters/turret/tris.md2");
        !           908: 
        !           909:        VectorSet (self->mins, -12, -12, -12);
        !           910:        VectorSet (self->maxs, 12, 12, 12);
        !           911:        self->movetype = MOVETYPE_NONE;
        !           912:        self->solid = SOLID_BBOX;
        !           913: 
        !           914:        self->health = 240;
        !           915:        self->gib_health = -100;
        !           916:        self->mass = 250;
        !           917:        self->yaw_speed = 45;
        !           918: 
        !           919:        self->flags |= FL_MECHANICAL;
        !           920: 
        !           921:        self->pain = turret_pain;
        !           922:        self->die = turret_die;
        !           923: 
        !           924:        // map designer didn't specify weapon type. set it now.
        !           925:        if(!(self->spawnflags & SPAWN_WEAPONCHOICE))
        !           926:        {
        !           927:                self->spawnflags |= SPAWN_BLASTER;
        !           928: //             self->spawnflags |= SPAWN_MACHINEGUN;
        !           929: //             self->spawnflags |= SPAWN_ROCKET;
        !           930: //             self->spawnflags |= SPAWN_HEATBEAM;
        !           931:        }
        !           932: 
        !           933:        if(self->spawnflags & SPAWN_HEATBEAM)
        !           934:        {
        !           935:                self->spawnflags &= ~SPAWN_HEATBEAM;
        !           936:                self->spawnflags |= SPAWN_BLASTER;
        !           937:        }
        !           938: 
        !           939:        if(!(self->spawnflags & SPAWN_WALL_UNIT))
        !           940:        {
        !           941:                self->monsterinfo.stand = turret_stand;
        !           942:                self->monsterinfo.walk = turret_walk;
        !           943:                self->monsterinfo.run = turret_run;
        !           944:                self->monsterinfo.dodge = NULL;
        !           945:                self->monsterinfo.attack = turret_attack;
        !           946:                self->monsterinfo.melee = NULL;
        !           947:                self->monsterinfo.sight = turret_sight;
        !           948:                self->monsterinfo.search = turret_search;
        !           949:                self->monsterinfo.currentmove = &turret_move_stand;
        !           950:        }
        !           951: 
        !           952:        // PMM
        !           953:        self->monsterinfo.checkattack = turret_checkattack;
        !           954: 
        !           955:        self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
        !           956:        self->monsterinfo.scale = MODEL_SCALE;
        !           957:        self->gravity = 0;
        !           958: 
        !           959:        VectorCopy(self->s.angles, self->offset);
        !           960:        angle=(int)self->s.angles[1];
        !           961:        switch(angle)
        !           962:        {
        !           963:                case -1:                                        // up
        !           964:                        self->s.angles[0] = 270;
        !           965:                        self->s.angles[1] = 0;
        !           966:                        self->s.origin[2] += 2;
        !           967:                        break;
        !           968:                case -2:                                        // down
        !           969:                        self->s.angles[0] = 90;
        !           970:                        self->s.angles[1] = 0;
        !           971:                        self->s.origin[2] -= 2;
        !           972:                        break;
        !           973:                case 0:
        !           974:                        self->s.origin[0] += 2;
        !           975:                        break;
        !           976:                case 90:
        !           977:                        self->s.origin[1] += 2;
        !           978:                        break;
        !           979:                case 180:
        !           980:                        self->s.origin[0] -= 2;
        !           981:                        break;
        !           982:                case 270:
        !           983:                        self->s.origin[1] -= 2;
        !           984:                        break;
        !           985:                default:
        !           986:                        break;
        !           987:        }
        !           988: 
        !           989:        gi.linkentity (self);
        !           990: 
        !           991: 
        !           992:        if(self->spawnflags & SPAWN_WALL_UNIT)
        !           993:        {
        !           994:                if(!self->targetname)
        !           995:                {
        !           996:                        gi.dprintf("Wall Unit Turret without targetname! %s\n", vtos(self->s.origin));
        !           997:                        G_FreeEdict(self);
        !           998:                        return;
        !           999:                }
        !          1000: 
        !          1001:                self->takedamage = DAMAGE_NO;
        !          1002:                self->use = turret_activate;
        !          1003:                turret_wall_spawn(self);
        !          1004:                if ((!(self->monsterinfo.aiflags & AI_GOOD_GUY)) && (!(self->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
        !          1005:                        level.total_monsters++;
        !          1006: 
        !          1007:        }
        !          1008:        else
        !          1009:        {
        !          1010:                stationarymonster_start (self);
        !          1011:        }
        !          1012: 
        !          1013:        if(self->spawnflags & SPAWN_MACHINEGUN)
        !          1014:        {
        !          1015:                gi.soundindex ("infantry/infatck1.wav");
        !          1016:                self->s.skinnum = 1;
        !          1017:        }
        !          1018:        else if(self->spawnflags & SPAWN_ROCKET)
        !          1019:        {
        !          1020:                gi.soundindex ("weapons/rockfly.wav");
        !          1021:                gi.modelindex ("models/objects/rocket/tris.md2");
        !          1022:                gi.soundindex ("chick/chkatck2.wav");
        !          1023:                self->s.skinnum = 2;
        !          1024:        }
        !          1025:        else
        !          1026:        {
        !          1027:                if (!(self->spawnflags & SPAWN_BLASTER))
        !          1028:                {
        !          1029:                        if ((g_showlogic) && (g_showlogic->value))
        !          1030:                                gi.dprintf ("Unknown spawn flags for turret.  Defaulting to blaster.\n");
        !          1031:                        self->spawnflags |= SPAWN_BLASTER;
        !          1032:                }
        !          1033:                gi.modelindex ("models/objects/laser/tris.md2");
        !          1034:                gi.soundindex ("misc/lasfly.wav");
        !          1035:                gi.soundindex ("soldier/solatck2.wav");
        !          1036:        }
        !          1037:        
        !          1038:        // PMM  - turrets don't get mad at monsters, and visa versa
        !          1039:        self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
        !          1040:        // PMM - blindfire
        !          1041:        if(self->spawnflags & (SPAWN_ROCKET|SPAWN_BLASTER))
        !          1042:                self->monsterinfo.blindfire = true;
        !          1043: }

unix.superglobalmegacorp.com

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