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