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

1.1       root        1: /*
                      2: ==============================================================================
                      3: 
                      4: stalker
                      5: 
                      6: ==============================================================================
                      7: */
                      8: 
                      9: #include "g_local.h"
                     10: #include "m_stalker.h"
                     11: #include <float.h>
                     12: 
                     13: static int     sound_pain1;
                     14: static int     sound_pain2;
                     15: static int     sound_die;
                     16: static int     sound_launch;
                     17: static int     sound_sight;
                     18: static int  sound_punch_hit;
                     19: 
                     20: int stalker_do_pounce(edict_t *self, vec3_t dest);
                     21: void stalker_stand (edict_t *self);
                     22: void stalker_run (edict_t *self);
                     23: void stalker_walk (edict_t *self);
                     24: void stalker_jump (edict_t *self);
                     25: void stalker_dodge_jump (edict_t *self);
                     26: void stalker_swing_check_l (edict_t *self);
                     27: void stalker_swing_check_r (edict_t *self);
                     28: void stalker_swing_attack (edict_t *self);
                     29: void stalker_jump_straightup (edict_t *self);
                     30: void stalker_jump_wait_land (edict_t *self);
                     31: void stalker_false_death (edict_t *self);
                     32: void stalker_false_death_start (edict_t *self);
                     33: qboolean stalker_ok_to_transition (edict_t *self);
                     34: 
                     35: #define STALKER_ON_CEILING(ent)  ( ent->gravityVector[2] > 0 ? 1 : 0 )
                     36: 
                     37: //extern qboolean SV_StepDirection (edict_t *ent, float yaw, float dist);
                     38: extern qboolean SV_PointCloseEnough (edict_t *ent, vec3_t goal, float dist);
                     39: extern void drawbbox(edict_t *self);
                     40: 
                     41: //=========================
                     42: //=========================
                     43: qboolean stalker_ok_to_transition (edict_t *self)
                     44: {
                     45:        trace_t         trace;
                     46:        vec3_t          pt, start;
                     47:        float           max_dist;
                     48:        float           margin;
                     49:        float           end_height;
                     50: 
                     51:        if(STALKER_ON_CEILING(self))
                     52:        {
                     53:                max_dist = -384;
                     54:                margin = self->mins[2] - 8;
                     55:        }       
                     56:        else
                     57:        {
                     58:                max_dist = 180;
                     59:                margin = self->maxs[2] + 8;
                     60:        }
                     61: 
                     62:        VectorCopy(self->s.origin, pt);
                     63:        pt[2] += max_dist;
                     64:        trace = gi.trace (self->s.origin, self->mins, self->maxs, pt, self, MASK_MONSTERSOLID);
                     65:        if(trace.fraction == 1.0 || 
                     66:           !(trace.contents & CONTENTS_SOLID) ||
                     67:           (trace.ent != world))
                     68:        {
                     69:                if(STALKER_ON_CEILING(self))
                     70:                {
                     71:                        if(trace.plane.normal[2] < 0.9)
                     72:                                return false;
                     73:                }
                     74:                else
                     75:                {
                     76:                        if(trace.plane.normal[2] > -0.9)
                     77:                                return false;
                     78:                }
                     79:        }
                     80: //     gi.dprintf("stalker_check_pt: main check ok\n");
                     81: 
                     82:        end_height = trace.endpos[2];
                     83: 
                     84:        // check the four corners, tracing only to the endpoint of the center trace (vertically).
                     85:        pt[0] = self->absmin[0];
                     86:        pt[1] = self->absmin[1];
                     87:        pt[2] = trace.endpos[2] + margin;       // give a little margin of error to allow slight inclines
                     88:        VectorCopy(pt, start);
                     89:        start[2] = self->s.origin[2];
                     90:        trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID);
                     91:        if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world))
                     92:        {
                     93: //             gi.dprintf("stalker_check_pt: absmin/absmin failed\n");
                     94:                return false;
                     95:        }
                     96:        if(abs(end_height + margin - trace.endpos[2]) > 8)
                     97:                return false;
                     98: 
                     99:        pt[0] = self->absmax[0];
                    100:        pt[1] = self->absmin[1];
                    101:        VectorCopy(pt, start);
                    102:        start[2] = self->s.origin[2];
                    103:        trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID);
                    104:        if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world))
                    105:        {
                    106: //             gi.dprintf("stalker_check_pt: absmax/absmin failed\n");
                    107:                return false;
                    108:        }
                    109:        if(abs(end_height + margin - trace.endpos[2]) > 8)
                    110:                return false;
                    111: 
                    112:        pt[0] = self->absmax[0];
                    113:        pt[1] = self->absmax[1];
                    114:        VectorCopy(pt, start);
                    115:        start[2] = self->s.origin[2];
                    116:        trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID);
                    117:        if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world))
                    118:        {
                    119: //             gi.dprintf("stalker_check_pt: absmax/absmax failed\n");
                    120:                return false;
                    121:        }
                    122:        if(abs(end_height + margin - trace.endpos[2]) > 8)
                    123:                return false;
                    124: 
                    125:        pt[0] = self->absmin[0];
                    126:        pt[1] = self->absmax[1];
                    127:        VectorCopy(pt, start);
                    128:        start[2] = self->s.origin[2];
                    129:        trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID);
                    130:        if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world))
                    131:        {
                    132: //             gi.dprintf("stalker_check_pt: absmin/absmax failed\n");
                    133:                return false;
                    134:        }
                    135:        if(abs(end_height + margin - trace.endpos[2]) > 8)
                    136:                return false;
                    137: 
                    138:        return true;
                    139: }
                    140: 
                    141: //=========================
                    142: //=========================
                    143: void stalker_sight (edict_t *self, edict_t *other)
                    144: {
                    145:        gi.sound (self, CHAN_WEAPON, sound_sight, 1, ATTN_NORM, 0);
                    146: }
                    147: 
                    148: // ******************
                    149: // IDLE
                    150: // ******************
                    151: 
                    152: mframe_t stalker_frames_idle [] =
                    153: {
                    154:        ai_stand, 0, NULL,
                    155:        ai_stand, 0, NULL,
                    156:        ai_stand, 0, NULL,
                    157:        ai_stand, 0, NULL,
                    158:        ai_stand, 0, NULL,
                    159: 
                    160:        ai_stand, 0, NULL,
                    161:        ai_stand, 0, NULL,
                    162:        ai_stand, 0, NULL,
                    163:        ai_stand, 0, NULL,
                    164:        ai_stand, 0, NULL,
                    165: 
                    166:        ai_stand, 0, NULL,
                    167:        ai_stand, 0, NULL,
                    168:        ai_stand, 0, NULL,
                    169:        ai_stand, 0, NULL,
                    170:        ai_stand, 0, NULL,
                    171: 
                    172:        ai_stand, 0, NULL,
                    173:        ai_stand, 0, NULL,
                    174:        ai_stand, 0, NULL,
                    175:        ai_stand, 0, NULL,
                    176:        ai_stand, 0, NULL,
                    177: 
                    178:        ai_stand, 0, NULL
                    179: };
                    180: mmove_t stalker_move_idle = {FRAME_idle01, FRAME_idle21, stalker_frames_idle, stalker_stand};
                    181: 
                    182: void stalker_idle (edict_t *self)
                    183: { 
                    184:        self->monsterinfo.currentmove = &stalker_move_idle;
                    185: }
                    186: 
                    187: // ******************
                    188: // STAND
                    189: // ******************
                    190: 
                    191: mframe_t stalker_frames_stand [] =
                    192: {
                    193:        ai_stand, 0, NULL,
                    194:        ai_stand, 0, NULL,
                    195:        ai_stand, 0, NULL,
                    196:        ai_stand, 0, NULL,
                    197:        ai_stand, 0, NULL,
                    198: 
                    199:        ai_stand, 0, NULL,
                    200:        ai_stand, 0, NULL,
                    201:        ai_stand, 0, NULL,
                    202:        ai_stand, 0, NULL,
                    203:        ai_stand, 0, NULL,
                    204: 
                    205:        ai_stand, 0, NULL,
                    206:        ai_stand, 0, NULL,
                    207:        ai_stand, 0, NULL,
                    208:        ai_stand, 0, NULL,
                    209:        ai_stand, 0, NULL,
                    210: 
                    211:        ai_stand, 0, NULL,
                    212:        ai_stand, 0, NULL,
                    213:        ai_stand, 0, NULL,
                    214:        ai_stand, 0, NULL,
                    215:        ai_stand, 0, NULL,
                    216: 
                    217:        ai_stand, 0, NULL
                    218: };
                    219: mmove_t        stalker_move_stand = {FRAME_idle01, FRAME_idle21, stalker_frames_stand, stalker_stand};
                    220: 
                    221: void stalker_stand (edict_t *self)
                    222: {
                    223:        self->monsterinfo.currentmove = &stalker_move_stand;
                    224: }
                    225: 
                    226: // ******************
                    227: // RUN
                    228: // ******************
                    229: 
                    230: mframe_t stalker_frames_run [] =
                    231: {
                    232:        ai_run, 13, NULL,
                    233:        ai_run, 17, NULL,
                    234:        ai_run, 21, NULL,
                    235:        ai_run, 18, NULL
                    236: 
                    237: /*     ai_run, 15, NULL,
                    238:        ai_run, 20, NULL,
                    239:        ai_run, 18, NULL,
                    240:        ai_run, 14, NULL*/
                    241: };
                    242: mmove_t stalker_move_run = {FRAME_run01, FRAME_run04, stalker_frames_run, NULL};
                    243: 
                    244: void stalker_run (edict_t *self)
                    245: {
                    246: //     gi.dprintf("stalker_run %5.1f\n", level.time);
                    247:        if (self->monsterinfo.aiflags & AI_STAND_GROUND)
                    248:                self->monsterinfo.currentmove = &stalker_move_stand;
                    249:        else
                    250:                self->monsterinfo.currentmove = &stalker_move_run;
                    251: }
                    252: 
                    253: // ******************
                    254: // WALK
                    255: // ******************
                    256: 
                    257: mframe_t stalker_frames_walk [] =
                    258: {
                    259:        ai_walk, 4, NULL,
                    260:        ai_walk, 6, NULL,
                    261:        ai_walk, 8, NULL,
                    262:        ai_walk, 5, NULL,
                    263: 
                    264:        ai_walk, 4, NULL,
                    265:        ai_walk, 6, NULL,
                    266:        ai_walk, 8, NULL,
                    267:        ai_walk, 4, NULL
                    268: };
                    269: mmove_t stalker_move_walk = {FRAME_walk01, FRAME_walk08, stalker_frames_walk, stalker_walk};
                    270: 
                    271: void stalker_walk (edict_t *self)
                    272: {
                    273: //     gi.dprintf("stalker_walk\n");
                    274:        self->monsterinfo.currentmove = &stalker_move_walk;
                    275: }
                    276: 
                    277: // ******************
                    278: // false death
                    279: // ******************
                    280: mframe_t stalker_frames_reactivate [] = 
                    281: {
                    282:        ai_move, 0, NULL,
                    283:        ai_move, 0, NULL,
                    284:        ai_move, 0, NULL,
                    285:        ai_move, 0, NULL
                    286: };
                    287: mmove_t stalker_move_false_death_end = { FRAME_reactive01, FRAME_reactive04, stalker_frames_reactivate, stalker_run };
                    288: 
                    289: void stalker_reactivate (edict_t *self)
                    290: {
                    291:        self->monsterinfo.aiflags &= ~AI_STAND_GROUND;
                    292:        self->monsterinfo.currentmove = &stalker_move_false_death_end;
                    293: }
                    294: 
                    295: void stalker_heal (edict_t *self)
                    296: {
                    297:        if(skill->value == 2)
                    298:                self->health+=2;
                    299:        else if(skill->value == 3)
                    300:                self->health+=3;
                    301:        else
                    302:                self->health++;
                    303: 
                    304: //     gi.dprintf("stalker_heal: %d\n", self->health);
                    305: 
                    306:        if(self->health > (self->max_health/2))
                    307:                self->s.skinnum = 0;
                    308: 
                    309:        if(self->health >= self->max_health)
                    310:        {
                    311:                self->health = self->max_health;
                    312:                stalker_reactivate(self);
                    313:        }
                    314: }
                    315: 
                    316: mframe_t stalker_frames_false_death [] =
                    317: {
                    318:        ai_move, 0, stalker_heal,
                    319:        ai_move, 0, stalker_heal,
                    320:        ai_move, 0, stalker_heal,
                    321:        ai_move, 0, stalker_heal,
                    322:        ai_move, 0, stalker_heal,
                    323: 
                    324:        ai_move, 0, stalker_heal,
                    325:        ai_move, 0, stalker_heal,
                    326:        ai_move, 0, stalker_heal,
                    327:        ai_move, 0, stalker_heal,
                    328:        ai_move, 0, stalker_heal
                    329: };
                    330: mmove_t stalker_move_false_death = {FRAME_twitch01, FRAME_twitch10, stalker_frames_false_death, stalker_false_death};
                    331: 
                    332: void stalker_false_death (edict_t *self)
                    333: {
                    334:        self->monsterinfo.currentmove = &stalker_move_false_death;
                    335: }
                    336: 
                    337: mframe_t stalker_frames_false_death_start [] =
                    338: {
                    339:        ai_move, 0, NULL,
                    340:        ai_move, 0, NULL,
                    341:        ai_move, 0, NULL,
                    342:        ai_move, 0, NULL,
                    343:        ai_move, 0, NULL,
                    344: 
                    345:        ai_move, 0, NULL,
                    346:        ai_move, 0, NULL,
                    347:        ai_move, 0, NULL,
                    348:        ai_move, 0, NULL,
                    349: };
                    350: mmove_t stalker_move_false_death_start = {FRAME_death01, FRAME_death09, stalker_frames_false_death_start, stalker_false_death};
                    351: 
                    352: void stalker_false_death_start (edict_t *self)
                    353: {
                    354:        self->s.angles[2] = 0;
                    355:        VectorSet(self->gravityVector, 0, 0, -1);
                    356: 
                    357:        self->monsterinfo.aiflags |= AI_STAND_GROUND;
                    358:        self->monsterinfo.currentmove = &stalker_move_false_death_start;
                    359: }
                    360: 
                    361: 
                    362: // ******************
                    363: // PAIN
                    364: // ******************
                    365: 
                    366: mframe_t stalker_frames_pain [] =
                    367: {
                    368:        ai_move, 0, NULL,
                    369:        ai_move, 0, NULL,
                    370:        ai_move, 0,     NULL,
                    371:        ai_move, 0, NULL
                    372: };
                    373: mmove_t stalker_move_pain = {FRAME_pain01, FRAME_pain04, stalker_frames_pain, stalker_run};
                    374: 
                    375: void stalker_pain (edict_t *self, edict_t *other, float kick, int damage)
                    376: {
                    377:        if (self->deadflag == DEAD_DEAD)
                    378:                return;
                    379: 
                    380:        if (self->health < (self->max_health / 2)) 
                    381:        {
                    382:                self->s.skinnum = 1;
                    383:        }
                    384: 
                    385:        if (skill->value == 3)
                    386:                return;         // no pain anims in nightmare
                    387: 
                    388: //     if (self->monsterinfo.aiflags & AI_DODGING)
                    389: //             monster_done_dodge (self);
                    390: 
                    391:        if (self->groundentity == NULL)
                    392:                return;
                    393: 
                    394:        // if we're reactivating or false dying, ignore the pain.
                    395:        if (self->monsterinfo.currentmove == &stalker_move_false_death_end ||
                    396:                self->monsterinfo.currentmove == &stalker_move_false_death_start )
                    397:                return;
                    398: 
                    399:        if (self->monsterinfo.currentmove == &stalker_move_false_death)
                    400:        {
                    401:                stalker_reactivate(self);
                    402:                return;
                    403:        }
                    404: 
                    405:        if ((self->health > 0) && (self->health < (self->max_health / 4)))
                    406:        {
                    407:                if(random() < (0.2 * skill->value))
                    408:                {
                    409:                        if( !STALKER_ON_CEILING(self) || stalker_ok_to_transition(self) )
                    410:                        {
                    411: //                             gi.dprintf("starting false death sequence\n");
                    412:                                stalker_false_death_start(self);
                    413:                                return;
                    414:                        }
                    415:                }       
                    416:        }
                    417: 
                    418:        if (level.time < self->pain_debounce_time)
                    419:                return;
                    420: 
                    421:        self->pain_debounce_time = level.time + 3;
                    422: 
                    423: //     gi.dprintf("stalker_pain\n");
                    424:        if (damage > 10)                // don't react unless the damage was significant
                    425:        {
                    426:                // stalker should dodge jump periodically to help avoid damage.
                    427:                if(self->groundentity && (random() < 0.5))
                    428:                        stalker_dodge_jump(self);
                    429:                else
                    430:                        self->monsterinfo.currentmove = &stalker_move_pain;
                    431: 
                    432:                if (random() < 0.5)
                    433:                        gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
                    434:                else
                    435:                        gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
                    436:        }
                    437: }
                    438: 
                    439: 
                    440: // ******************
                    441: // STALKER ATTACK
                    442: // ******************
                    443: 
                    444: //extern qboolean infront (edict_t *self, edict_t *other);
                    445: 
                    446: void stalker_shoot_attack (edict_t *self)
                    447: {
                    448:        vec3_t  offset, start, f, r, dir;
                    449:        vec3_t  end;
                    450:        float   time, dist;
                    451:        trace_t trace;
                    452: 
                    453:        if(!has_valid_enemy(self))
                    454:                return;
                    455: 
                    456:        if(self->groundentity && random() < 0.33)
                    457:        {
                    458:                VectorSubtract (self->enemy->s.origin, self->s.origin, dir);
                    459:                dist = VectorLength (dir);
                    460: 
                    461:                if((dist > 256) || (random() < 0.5))
                    462:                        stalker_do_pounce(self, self->enemy->s.origin);
                    463:                else
                    464:                        stalker_jump_straightup (self);
                    465:        }
                    466: 
                    467:        // FIXME -- keep this but use a custom one
                    468: //     if (!infront(self, self->enemy))
                    469: //             return;
                    470: 
                    471:        AngleVectors (self->s.angles, f, r, NULL);
                    472:        VectorSet (offset, 24, 0, 6);
                    473:        G_ProjectSource (self->s.origin, offset, f, r, start);
                    474: 
                    475:        VectorSubtract(self->enemy->s.origin, start, dir);
                    476:        if(random() < (0.20 + 0.1 * skill->value))
                    477:        {
                    478:                dist = VectorLength(dir);
                    479:                time = dist / 1000;
                    480:                VectorMA(self->enemy->s.origin, time, self->enemy->velocity, end);
                    481:                VectorSubtract(end, start, dir);
                    482:        }
                    483:        else
                    484:                VectorCopy(self->enemy->s.origin, end);
                    485: 
                    486:        trace = gi.trace(start, vec3_origin, vec3_origin, end, self, MASK_SHOT);
                    487:        if(trace.ent == self->enemy || trace.ent == world)
                    488:                monster_fire_blaster2(self, start, dir, 15, 800, MZ2_STALKER_BLASTER, EF_BLASTER);
                    489: //     else
                    490: //             gi.dprintf("blocked by entity %s\n", trace.ent->classname);
                    491: }
                    492: 
                    493: void stalker_shoot_attack2 (edict_t *self)
                    494: {
                    495: //     if (random() < (0.4+(float)skill->value))
                    496: //             stalker_shoot_attack (self);
                    497: 
                    498:        if (random() < (0.4 + (0.1 * (float)skill->value)))
                    499:                stalker_shoot_attack (self);
                    500: }
                    501: 
                    502: mframe_t stalker_frames_shoot [] =
                    503: {
                    504:        ai_charge, 13, NULL,
                    505:        ai_charge, 17, stalker_shoot_attack,
                    506:        ai_charge, 21, NULL,
                    507:        ai_charge, 18, stalker_shoot_attack2
                    508: };
                    509: mmove_t stalker_move_shoot = {FRAME_run01, FRAME_run04, stalker_frames_shoot, stalker_run};
                    510: 
                    511: void stalker_attack_ranged (edict_t *self)
                    512: {
                    513:        if(!has_valid_enemy(self))
                    514:                return;
                    515: 
                    516:        // PMM - circle strafe stuff
                    517:        if (random() > (1.0 - (0.5/(float)(skill->value))))
                    518:        {
                    519:                self->monsterinfo.attack_state = AS_STRAIGHT;
                    520:        }
                    521:        else
                    522:        {
                    523:                if (random () <= 0.5) // switch directions
                    524:                        self->monsterinfo.lefty = 1 - self->monsterinfo.lefty;
                    525:                self->monsterinfo.attack_state = AS_SLIDING;
                    526:        }
                    527:        self->monsterinfo.currentmove = &stalker_move_shoot;
                    528: }
                    529: 
                    530: // ******************
                    531: // close combat
                    532: // ******************
                    533: 
                    534: void stalker_swing_attack (edict_t *self)
                    535: {
                    536:        vec3_t  aim;
                    537: 
                    538:        VectorSet (aim, MELEE_DISTANCE, 0, 0);
                    539:        if (fire_hit (self, aim, (5 + (rand() % 5)), 50))
                    540:                gi.sound (self, CHAN_WEAPON, sound_punch_hit, 1, ATTN_NORM, 0);
                    541: }
                    542: 
                    543: mframe_t stalker_frames_swing_l [] =
                    544: {
                    545:        ai_charge, 2, NULL,
                    546:        ai_charge, 4, NULL,
                    547:        ai_charge, 6, NULL,
                    548:        ai_charge, 10, NULL,
                    549: 
                    550:        ai_charge, 5, stalker_swing_attack,
                    551:        ai_charge, 5, NULL,
                    552:        ai_charge, 5, NULL,
                    553:        ai_charge, 5, NULL  // stalker_swing_check_l
                    554: };
                    555: mmove_t stalker_move_swing_l = {FRAME_attack01, FRAME_attack08, stalker_frames_swing_l, stalker_run};
                    556: 
                    557: mframe_t stalker_frames_swing_r [] =
                    558: {
                    559:        ai_charge, 4, NULL,
                    560:        ai_charge, 6, NULL,
                    561:        ai_charge, 6, stalker_swing_attack,
                    562:        ai_charge, 10, NULL,
                    563:        ai_charge, 5, NULL      // stalker_swing_check_r
                    564: };
                    565: mmove_t stalker_move_swing_r = {FRAME_attack11, FRAME_attack15, stalker_frames_swing_r, stalker_run};
                    566: 
                    567: void stalker_attack_melee (edict_t *self)
                    568: {
                    569:        if(!has_valid_enemy(self))
                    570:                return;
                    571: 
                    572:        if(random() < 0.5)
                    573:        {
                    574:                self->monsterinfo.currentmove = &stalker_move_swing_l;
                    575:        }
                    576:        else
                    577:        {
                    578:                self->monsterinfo.currentmove = &stalker_move_swing_r;
                    579:        }
                    580: }
                    581: 
                    582: 
                    583: // ******************
                    584: // POUNCE
                    585: // ******************
                    586: 
                    587: #define PI 3.14159
                    588: #define RAD2DEG(x)     (x * (float)180.0 / (float)PI)
                    589: #define DEG2RAD(x)     (x * (float)PI / (float)180.0)
                    590: #define FAUX_GRAVITY   800.0
                    591: 
                    592: // ====================
                    593: // ====================
                    594: void calcJumpAngle(vec3_t start, vec3_t end, float velocity, vec3_t angles)
                    595: {
                    596:        float   distV, distH;
                    597:        float   one, cosU;
                    598:        float   l, U;
                    599:        vec3_t  dist;
                    600: 
                    601:        VectorSubtract(end, start, dist);
                    602:        distH = (float)sqrt(dist[0]*dist[0] + dist[1]*dist[1]);
                    603:        distV = dist[2];
                    604:        if(distV < 0)
                    605:                distV = 0 - distV;
                    606: 
                    607:        if(distV)
                    608:        {
                    609:                l = (float) sqrt(distH*distH + distV*distV);
                    610:                U = (float) atan(distV / distH);
                    611:                if(dist[2] > 0)
                    612:                        U = (float)0.0 - U;
                    613: 
                    614:                angles[2] = 0.0;
                    615: 
                    616:                cosU = (float)cos(U);
                    617:                one = l * FAUX_GRAVITY * (cosU * cosU);
                    618:                one = one / (velocity * velocity);
                    619:                one = one - (float)sin(U);
                    620:        //      one = ((l * FAUX_GRAVITY * (cosU * cosU)) / (velocity * velocity)) - (float)sin(U);
                    621:                angles[0] = (float)asin(one);
                    622:                if(_isnan(angles[0]))
                    623:                        angles[2] = 1.0;
                    624:                angles[1] = (float)PI - angles[0];
                    625:                if(_isnan(angles[1]))
                    626:                        angles[2] = 1.0;
                    627: 
                    628:                angles[0] = RAD2DEG ( (angles[0] - U) / 2.0 );
                    629:                angles[1] = RAD2DEG ( (angles[1] - U) / 2.0 );
                    630:        }
                    631:        else
                    632:        {
                    633:                l = (float) sqrt(distH*distH + distV*distV);
                    634: 
                    635:                angles[2] = 0.0;
                    636: 
                    637:                one = l * FAUX_GRAVITY;
                    638:                one = one / (velocity * velocity);
                    639:                angles[0] = (float)asin(one);
                    640:                if(_isnan(angles[0]))
                    641:                        angles[2] = 1.0;
                    642:                angles[1] = (float)PI - angles[0];
                    643:                if(_isnan(angles[1]))
                    644:                        angles[2] = 1.0;
                    645: 
                    646:                angles[0] = RAD2DEG ( (angles[0]) / 2.0 );
                    647:                angles[1] = RAD2DEG ( (angles[1]) / 2.0 );
                    648:        }
                    649: }
                    650: 
                    651: // ====================
                    652: // ====================
                    653: int stalker_check_lz (edict_t *self, edict_t *target, vec3_t dest)
                    654: {
                    655:        vec3_t  jumpLZ;
                    656: 
                    657:        if( (gi.pointcontents (dest) & MASK_WATER) || (target->waterlevel))
                    658:        {
                    659: //             gi.dprintf ("you won't make me jump in water!\n");
                    660:                return false;
                    661:        }
                    662: 
                    663:        if( !target->groundentity )
                    664:        {
                    665: //             gi.dprintf( "I'll wait until you land..\n");
                    666:                return false;
                    667:        }
                    668: 
                    669:        // check under the player's four corners
                    670:        // if they're not solid, bail.
                    671:        jumpLZ[0] = self->enemy->mins[0];
                    672:        jumpLZ[1] = self->enemy->mins[1];
                    673:        jumpLZ[2] = self->enemy->mins[2] - 0.25;
                    674:        if( !(gi.pointcontents (jumpLZ) & MASK_SOLID) )
                    675:                return false;
                    676: 
                    677:        jumpLZ[0] = self->enemy->maxs[0];
                    678:        jumpLZ[1] = self->enemy->mins[1];
                    679:        if( !(gi.pointcontents (jumpLZ) & MASK_SOLID) )
                    680:                return false;
                    681: 
                    682:        jumpLZ[0] = self->enemy->maxs[0];
                    683:        jumpLZ[1] = self->enemy->maxs[1];
                    684:        if( !(gi.pointcontents (jumpLZ) & MASK_SOLID) )
                    685:                return false;
                    686: 
                    687:        jumpLZ[0] = self->enemy->mins[0];
                    688:        jumpLZ[1] = self->enemy->maxs[1];
                    689:        if( !(gi.pointcontents (jumpLZ) & MASK_SOLID) )
                    690:                return false;
                    691: 
                    692:        return true;
                    693: }
                    694: 
                    695: // ====================
                    696: // ====================
                    697: int stalker_do_pounce(edict_t *self, vec3_t dest)
                    698: {
                    699:        vec3_t  forward, right;
                    700:        vec3_t  dist;
                    701:        vec_t   length;
                    702:        vec3_t  jumpAngles;
                    703:        vec3_t  jumpLZ;
                    704:        float   velocity = 400.1;
                    705:        trace_t trace;
                    706:        int             preferHighJump;
                    707: 
                    708:        // don't pounce when we're on the ceiling
                    709:        if(STALKER_ON_CEILING(self))
                    710:                return false;
                    711: 
                    712:        if(!stalker_check_lz (self, self->enemy, dest))
                    713:                return false;
                    714: 
                    715:        VectorSubtract(dest, self->s.origin, dist);
                    716:        
                    717:        // make sure we're pointing in that direction 15deg margin of error.
                    718:        vectoangles2 (dist, jumpAngles);
                    719:        if(abs(jumpAngles[YAW] - self->s.angles[YAW]) > 45)
                    720:                return false;                   // not facing the player...
                    721: 
                    722:        self->ideal_yaw = jumpAngles[YAW];
                    723:        M_ChangeYaw(self);
                    724: 
                    725:        length = VectorLength(dist);
                    726:        if(length > 450)
                    727:                return false;                   // can't jump that far...
                    728: 
                    729:        VectorCopy(dest, jumpLZ);
                    730: 
                    731:        preferHighJump = 0;
                    732: 
                    733:        // if we're having to jump up a distance, jump a little too high to compensate.
                    734:        if(dist[2] >= 32.0)
                    735:        {
                    736:                preferHighJump = 1;
                    737:                jumpLZ[2] += 32;
                    738:        }
                    739: 
                    740:        trace = gi.trace (self->s.origin, vec3_origin, vec3_origin, dest, self, MASK_MONSTERSOLID);
                    741:        if((trace.fraction < 1) && (trace.ent != self->enemy))
                    742:        {
                    743: //             gi.dprintf("prefer high jump angle\n");
                    744:                preferHighJump = 1; 
                    745:        }
                    746: 
                    747:        // find a valid angle/velocity combination
                    748:        while(velocity <= 800)
                    749:        {
                    750:                calcJumpAngle(self->s.origin, jumpLZ, velocity, jumpAngles);
                    751:                if((!_isnan(jumpAngles[0]))  || (!_isnan(jumpAngles[1])))
                    752:                        break;
                    753:                
                    754:                velocity+=200;
                    755:        };
                    756: 
                    757:        if(!preferHighJump && (!_isnan(jumpAngles[0])) )
                    758:        {
                    759:                AngleVectors (self->s.angles, forward, right, NULL);
                    760:                VectorNormalize ( forward ) ;
                    761: 
                    762:                VectorScale( forward, velocity * cos(DEG2RAD(jumpAngles[0])), self->velocity);
                    763:                self->velocity[2] = velocity * sin(DEG2RAD(jumpAngles[0])) + (0.5 * sv_gravity->value * FRAMETIME);
                    764: //             gi.dprintf("  pouncing! %0.1f,%0.1f (%0.1f)  --> %0.1f, %0.1f, %0.1f\n", 
                    765: //                             jumpAngles[0], jumpAngles[1], jumpAngles[0],
                    766: //                             self->velocity[0], self->velocity[1], self->velocity[2]);
                    767:                return 1;
                    768:        }
                    769: 
                    770:        if(!_isnan(jumpAngles[1]))
                    771:        {
                    772:                AngleVectors (self->s.angles, forward, right, NULL);
                    773:                VectorNormalize ( forward ) ;
                    774: 
                    775:                VectorScale( forward, velocity * cos(DEG2RAD(jumpAngles[1])), self->velocity);
                    776:                self->velocity[2] = velocity * sin(DEG2RAD(jumpAngles[1])) + (0.5 * sv_gravity->value * FRAMETIME);
                    777: //             gi.dprintf("  pouncing! %0.1f,%0.1f (%0.1f)  --> %0.1f, %0.1f, %0.1f\n", 
                    778: //                             jumpAngles[0], jumpAngles[1], jumpAngles[1],
                    779: //                             self->velocity[0], self->velocity[1], self->velocity[2]);
                    780:                return 1;
                    781:        }
                    782: 
                    783: //     gi.dprintf("  nan\n");
                    784:        return 0;
                    785: }
                    786: 
                    787: // ******************
                    788: // DODGE
                    789: // ******************
                    790: 
                    791: //===================
                    792: // stalker_jump_straightup
                    793: //===================
                    794: void stalker_jump_straightup (edict_t *self)
                    795: {
                    796:        if (self->deadflag == DEAD_DEAD)
                    797:                return;
                    798: 
                    799:        if(STALKER_ON_CEILING(self))
                    800:        {
                    801:                if(stalker_ok_to_transition(self))
                    802:                {
                    803: //                     gi.dprintf("falling off ceiling %d\n", self->health);
                    804:                        self->gravityVector[2] = -1;
                    805:                        self->s.angles[2] += 180.0;
                    806:                        if(self->s.angles[2] > 360.0)
                    807:                                self->s.angles[2] -= 360.0;
                    808:                        self->groundentity = NULL;
                    809:                }
                    810:        }
                    811:        else if(self->groundentity)     // make sure we're standing on SOMETHING...
                    812:        {
                    813:                self->velocity[0] += ((random() * 10) - 5);
                    814:                self->velocity[1] += ((random() * 10) - 5);
                    815:                self->velocity[2] += -400 * self->gravityVector[2];
                    816:                if(stalker_ok_to_transition(self))
                    817:                {
                    818: //                     gi.dprintf("falling TO ceiling %d\n", self->health);
                    819:                        self->gravityVector[2] = 1;
                    820:                        self->s.angles[2] = 180.0;
                    821:                        self->groundentity = NULL;
                    822:                }
                    823:        }
                    824: }
                    825: 
                    826: mframe_t stalker_frames_jump_straightup [] =
                    827: {
                    828:        ai_move, 1,  stalker_jump_straightup,
                    829:        ai_move, 1,  stalker_jump_wait_land,
                    830:        ai_move, -1, NULL,
                    831:        ai_move, -1, NULL
                    832: };
                    833: 
                    834: mmove_t        stalker_move_jump_straightup = {FRAME_jump04, FRAME_jump07, stalker_frames_jump_straightup, stalker_run};
                    835: 
                    836: //===================
                    837: // stalker_dodge_jump - abstraction so pain function can trigger a dodge jump too without
                    838: //             faking the inputs to stalker_dodge
                    839: //===================
                    840: void stalker_dodge_jump (edict_t *self)
                    841: {
                    842:        self->monsterinfo.currentmove = &stalker_move_jump_straightup;
                    843: }
                    844: 
                    845: mframe_t stalker_frames_dodge_run [] =
                    846: {
                    847:        ai_run, 13, NULL,
                    848:        ai_run, 17, NULL,
                    849:        ai_run, 21, NULL,
                    850:        ai_run, 18, monster_done_dodge
                    851: };
                    852: mmove_t stalker_move_dodge_run = {FRAME_run01, FRAME_run04, stalker_frames_dodge_run, NULL};
                    853: 
                    854: void stalker_dodge (edict_t *self, edict_t *attacker, float eta, trace_t *tr)
                    855: {
                    856:        if (!self->groundentity || self->health <= 0)
                    857:                return;
                    858: 
                    859:        if (!self->enemy)
                    860:        {
                    861:                self->enemy = attacker;
                    862:                FoundTarget(self);
                    863:                return;
                    864:        }
                    865:        
                    866:        // PMM - don't bother if it's going to hit anyway; fix for weird in-your-face etas (I was
                    867:        // seeing numbers like 13 and 14)
                    868:        if ((eta < 0.1) || (eta > 5))
                    869:                return;
                    870: 
                    871:        // this will override the foundtarget call of stalker_run
                    872:        stalker_dodge_jump(self);
                    873: }
                    874: 
                    875: 
                    876: // ******************
                    877: // Jump onto / off of things
                    878: // ******************
                    879: 
                    880: //===================
                    881: //===================
                    882: void stalker_jump_down (edict_t *self)
                    883: {
                    884:        vec3_t  forward,up;
                    885: 
                    886:        AngleVectors (self->s.angles, forward, NULL, up);
                    887:        VectorMA(self->velocity, 100, forward, self->velocity);
                    888:        VectorMA(self->velocity, 300, up, self->velocity);
                    889: }
                    890: 
                    891: //===================
                    892: //===================
                    893: void stalker_jump_up (edict_t *self)
                    894: {
                    895:        vec3_t  forward,up;
                    896: 
                    897:        AngleVectors (self->s.angles, forward, NULL, up);
                    898:        VectorMA(self->velocity, 200, forward, self->velocity);
                    899:        VectorMA(self->velocity, 450, up, self->velocity);
                    900: }
                    901: 
                    902: //===================
                    903: //===================
                    904: void stalker_jump_wait_land (edict_t *self)
                    905: {
                    906:        if ((random() < (0.3 + (0.1*(float)(skill->value)))) && (level.time >= self->monsterinfo.attack_finished))
                    907:        {
                    908:                self->monsterinfo.attack_finished = level.time + 0.3;
                    909:                stalker_shoot_attack(self);
                    910:        }
                    911: 
                    912:        if(self->groundentity == NULL)
                    913:        {
                    914:                self->gravity = 1.3;
                    915:                self->monsterinfo.nextframe = self->s.frame;
                    916:        }
                    917:        else 
                    918:        {
                    919:                self->gravity = 1;
                    920:                self->monsterinfo.nextframe = self->s.frame + 1;
                    921:        }
                    922: }
                    923: 
                    924: mframe_t stalker_frames_jump_up [] =
                    925: {
                    926:        ai_move, -8, NULL,
                    927:        ai_move, -8, NULL,
                    928:        ai_move, -8, NULL,
                    929:        ai_move, -8, NULL,
                    930: 
                    931:        ai_move, 0, stalker_jump_up,
                    932:        ai_move, 0, stalker_jump_wait_land,
                    933:        ai_move, 0, NULL
                    934: };
                    935: mmove_t stalker_move_jump_up = { FRAME_jump01, FRAME_jump07, stalker_frames_jump_up, stalker_run };
                    936: 
                    937: mframe_t stalker_frames_jump_down [] =
                    938: {
                    939:        ai_move, 0, NULL,
                    940:        ai_move, 0, NULL,
                    941:        ai_move, 0, NULL,
                    942:        ai_move, 0, NULL,
                    943:        
                    944:        ai_move, 0, stalker_jump_down,
                    945:        ai_move, 0, stalker_jump_wait_land,
                    946:        ai_move, 0, NULL
                    947: };
                    948: mmove_t stalker_move_jump_down = { FRAME_jump01, FRAME_jump07, stalker_frames_jump_down, stalker_run };
                    949: 
                    950: //============
                    951: // stalker_jump - this is only used for jumping onto or off of things. for dodge jumping,
                    952: //             use stalker_dodge_jump
                    953: //============
                    954: void stalker_jump (edict_t *self)
                    955: {
                    956:        if(!self->enemy)
                    957:                return;
                    958: 
                    959:        if(self->enemy->s.origin[2] >= self->s.origin[2])
                    960:        {
                    961: //             gi.dprintf("stalker_jump_up\n");
                    962:                self->monsterinfo.currentmove = &stalker_move_jump_up;
                    963:        }
                    964:        else
                    965:        {
                    966: //             gi.dprintf("stalker_jump_down\n");
                    967:                self->monsterinfo.currentmove = &stalker_move_jump_down;
                    968:        }
                    969: }
                    970: 
                    971: 
                    972: // ******************
                    973: // Blocked
                    974: // ******************
                    975: 
                    976: qboolean stalker_blocked (edict_t *self, float dist)
                    977: {
                    978:        qboolean        onCeiling;
                    979: 
                    980: //     gi.dprintf("stalker_blocked\n");
                    981:        if(!has_valid_enemy(self))
                    982:                return false;
                    983: 
                    984:        onCeiling = false;
                    985:        if(self->gravityVector[2] > 0)
                    986:                onCeiling = true;
                    987: 
                    988:        if(!onCeiling)
                    989:        {
                    990:                if(blocked_checkshot(self, 0.25 + (0.05 * skill->value) ))
                    991:                {
                    992: //                     gi.dprintf("blocked: shooting\n");
                    993:                        return true;
                    994:                }
                    995: 
                    996:                if(visible (self, self->enemy))
                    997:                {
                    998: //                     gi.dprintf("blocked: jumping at player!\n");
                    999:                        stalker_do_pounce(self, self->enemy->s.origin);
                   1000:                        return true;
                   1001:                }
                   1002: 
                   1003:                if(blocked_checkjump (self, dist, 256, 68))
                   1004:                {
                   1005: //                     gi.dprintf("blocked: jumping up/down\n");
                   1006:                        stalker_jump (self);
                   1007:                        return true;
                   1008:                }
                   1009: 
                   1010:                if(blocked_checkplat (self, dist))
                   1011:                        return true;
                   1012:        }
                   1013:        else
                   1014:        {
                   1015:                if(blocked_checkshot(self, 0.25 + (0.05 * skill->value) ))
                   1016:                {
                   1017: //                     gi.dprintf("blocked: shooting\n");
                   1018:                        return true;
                   1019:                }       
                   1020:                else if(stalker_ok_to_transition(self))
                   1021:                {
                   1022:                        self->gravityVector[2] = -1;
                   1023:                        self->s.angles[2] += 180.0;
                   1024:                        if(self->s.angles[2] > 360.0)
                   1025:                                self->s.angles[2] -= 360.0;
                   1026:                        self->groundentity = NULL;
                   1027:                        
                   1028: //                     gi.dprintf("falling off ceiling\n");
                   1029:                        return true;
                   1030:                }
                   1031: //             else
                   1032: //                     gi.dprintf("Not OK to fall!\n");
                   1033:        }
                   1034: 
                   1035:        return false;
                   1036: }
                   1037: 
                   1038: // ******************
                   1039: // Death
                   1040: // ******************
                   1041: 
                   1042: void stalker_dead (edict_t *self)
                   1043: {
                   1044:        VectorSet (self->mins, -28, -28, -18);
                   1045:        VectorSet (self->maxs, 28, 28, -4);
                   1046:        self->movetype = MOVETYPE_TOSS;
                   1047:        self->svflags |= SVF_DEADMONSTER;
                   1048:        self->nextthink = 0;
                   1049:        gi.linkentity (self);
                   1050: //     drawbbox(self);
                   1051: }
                   1052: 
                   1053: mframe_t stalker_frames_death [] =
                   1054: {
                   1055:        ai_move, 0,      NULL,
                   1056:        ai_move, -5,     NULL,
                   1057:        ai_move, -10,    NULL,
                   1058:        ai_move, -20,    NULL,
                   1059:        
                   1060:        ai_move, -10,    NULL,
                   1061:        ai_move, -10,    NULL,
                   1062:        ai_move, -5,     NULL,
                   1063:        ai_move, -5,     NULL,
                   1064: 
                   1065:        ai_move, 0,      NULL
                   1066: };
                   1067: mmove_t stalker_move_death = {FRAME_death01, FRAME_death09, stalker_frames_death, stalker_dead};
                   1068: 
                   1069: void stalker_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
                   1070: {
                   1071:        int             n;
                   1072: 
                   1073: //     gi.dprintf("stalker_die: %d\n", self->health);
                   1074: 
                   1075: // dude bit it, make him fall!
                   1076:        self->movetype = MOVETYPE_TOSS;
                   1077:        self->s.angles[2] = 0;
                   1078:        VectorSet(self->gravityVector, 0, 0, -1);
                   1079: 
                   1080: // check for gib
                   1081:        if (self->health <= self->gib_health)
                   1082:        {
                   1083:                gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
                   1084:                for (n= 0; n < 2; n++)
                   1085:                        ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
                   1086:                for (n= 0; n < 4; n++)
                   1087:                        ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
                   1088:                ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
                   1089:                self->deadflag = DEAD_DEAD;
                   1090:                return;
                   1091:        }
                   1092: 
                   1093:        if (self->deadflag == DEAD_DEAD)
                   1094:                return;
                   1095: 
                   1096: // regular death
                   1097:        gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
                   1098:        self->deadflag = DEAD_DEAD;
                   1099:        self->takedamage = DAMAGE_YES;
                   1100:        self->monsterinfo.currentmove = &stalker_move_death;
                   1101: }
                   1102: 
                   1103: 
                   1104: // ******************
                   1105: // SPAWN
                   1106: // ******************
                   1107: 
                   1108: /*QUAKED monster_stalker (1 .5 0) (-28 -28 -18) (28 28 18) Ambush Trigger_Spawn Sight OnRoof
                   1109: Spider Monster
                   1110: 
                   1111:   ONROOF - Monster starts sticking to the roof.
                   1112: */
                   1113: void SP_monster_stalker (edict_t *self)
                   1114: {
                   1115:        if (deathmatch->value)
                   1116:        {
                   1117:                G_FreeEdict (self);
                   1118:                return;
                   1119:        }
                   1120: 
                   1121:        // FIXME - I want my own sounds!
                   1122:        sound_pain1 = gi.soundindex ("parasite/parpain1.wav");  
                   1123:        sound_pain2 = gi.soundindex ("parasite/parpain2.wav");  
                   1124:        sound_die = gi.soundindex ("parasite/pardeth1.wav");    
                   1125:        sound_launch = gi.soundindex("parasite/paratck1.wav");
                   1126:        sound_sight = gi.soundindex("parasite/parsght1.wav");
                   1127:        sound_punch_hit = gi.soundindex ("infantry/melee2.wav");
                   1128: 
                   1129:        // PMM - precache bolt2
                   1130:        gi.modelindex ("models/proj/laser2/tris.md2");
                   1131: 
                   1132:        self->s.modelindex = gi.modelindex ("models/monsters/stalker/tris.md2");
                   1133:        VectorSet (self->mins, -28, -28, -18);
                   1134:        VectorSet (self->maxs, 28, 28, 18);
                   1135:        self->movetype = MOVETYPE_STEP;
                   1136:        self->solid = SOLID_BBOX;
                   1137: 
                   1138:        self->health = 250;
                   1139:        self->gib_health = -50;         // FIXME 
                   1140:        self->mass = 250;
                   1141: 
                   1142:        self->pain = stalker_pain;
                   1143:        self->die = stalker_die;
                   1144: 
                   1145:        self->monsterinfo.stand = stalker_stand;
                   1146:        self->monsterinfo.walk = stalker_walk;
                   1147:        self->monsterinfo.run = stalker_run;
                   1148:        self->monsterinfo.attack = stalker_attack_ranged;
                   1149:        self->monsterinfo.sight = stalker_sight;
                   1150:        self->monsterinfo.idle = stalker_idle;
                   1151:        self->monsterinfo.dodge = stalker_dodge;
                   1152:        self->monsterinfo.blocked = stalker_blocked;
                   1153:        self->monsterinfo.melee = stalker_attack_melee;
                   1154: 
                   1155:        gi.linkentity (self);
                   1156: 
                   1157:        self->monsterinfo.currentmove = &stalker_move_stand;    
                   1158:        self->monsterinfo.scale = MODEL_SCALE;
                   1159: 
                   1160:        self->monsterinfo.aiflags |= AI_WALK_WALLS;
                   1161: 
                   1162:        if(self->spawnflags & 8)
                   1163:        {
                   1164:                self->s.angles[2] = 180;
                   1165:                self->gravityVector[2] = 1;
                   1166:        }
                   1167: 
                   1168:        walkmonster_start (self);
                   1169: }

unix.superglobalmegacorp.com

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