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