void stalker_pain (edict_t *self, edict_t *other, float kick, int damage) { if (self->deadflag == DEAD_DEAD) return; if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (skill->value == 3) return; // no pain anims in nightmare // if (self->monsterinfo.aiflags & AI_DODGING) // monster_done_dodge (self); if (self->groundentity == NULL) return; // if we're reactivating or false dying, ignore the pain. if (self->monsterinfo.currentmove == &stalker_move_false_death_end || self->monsterinfo.currentmove == &stalker_move_false_death_start ) return; if (self->monsterinfo.currentmove == &stalker_move_false_death) { stalker_reactivate(self); return; } if ((self->health > 0) && (self->health < (self->max_health / 4))) { if(random() < (0.2 * skill->value)) { if( !STALKER_ON_CEILING(self) || stalker_ok_to_transition(self) ) { // gi.dprintf("starting false death sequence\n"); stalker_false_death_start(self); return; } } } if (level.time < self->pain_debounce_time) return; self->pain_debounce_time = level.time + 3; // gi.dprintf("stalker_pain\n"); if (damage > 10) // don't react unless the damage was significant { // stalker should dodge jump periodically to help avoid damage. if(self->groundentity && (random() < 0.5)) stalker_dodge_jump(self); else self->monsterinfo.currentmove = &stalker_move_pain; gi.sound (self, CHAN_WEAPON, sound_pain, 1, ATTN_NORM, 0); } }
//=================== // stalker_jump_straightup //=================== void stalker_jump_straightup (edict_t *self) { if (self->deadflag == DEAD_DEAD) return; if(STALKER_ON_CEILING(self)) { if(stalker_ok_to_transition(self)) { // gi.dprintf("falling off ceiling %d\n", self->health); self->gravityVector[2] = -1; self->s.angles[2] += 180.0; if(self->s.angles[2] > 360.0) self->s.angles[2] -= 360.0; self->groundentity = NULL; } } else if(self->groundentity) // make sure we're standing on SOMETHING... { self->velocity[0] += ((random() * 10) - 5); self->velocity[1] += ((random() * 10) - 5); self->velocity[2] += -400 * self->gravityVector[2]; if(stalker_ok_to_transition(self)) { // gi.dprintf("falling TO ceiling %d\n", self->health); self->gravityVector[2] = 1; self->s.angles[2] = 180.0; self->groundentity = NULL; } } }
void stalker_jump_straightup(edict_t *self) { if (!self) { return; } if (self->deadflag == DEAD_DEAD) { return; } if (STALKER_ON_CEILING(self)) { if (stalker_ok_to_transition(self)) { self->gravityVector[2] = -1; self->s.angles[2] += 180.0; if (self->s.angles[2] > 360.0) { self->s.angles[2] -= 360.0; } self->groundentity = NULL; } } else if (self->groundentity) /* make sure we're standing on SOMETHING... */ { self->velocity[0] += ((random() * 10) - 5); self->velocity[1] += ((random() * 10) - 5); self->velocity[2] += -400 * self->gravityVector[2]; if (stalker_ok_to_transition(self)) { self->gravityVector[2] = 1; self->s.angles[2] = 180.0; self->groundentity = NULL; } } }
// ==================== // ==================== int stalker_do_pounce(edict_t *self, vec3_t dest) { vec3_t forward, right; vec3_t dist; vec_t length; vec3_t jumpAngles; vec3_t jumpLZ; float velocity = 400.1; trace_t trace; int preferHighJump; // don't pounce when we're on the ceiling if(STALKER_ON_CEILING(self)) return false; if(!stalker_check_lz (self, self->enemy, dest)) return false; VectorSubtract(dest, self->s.origin, dist); // make sure we're pointing in that direction 15deg margin of error. vectoangles2 (dist, jumpAngles); if(abs(jumpAngles[YAW] - self->s.angles[YAW]) > 45) return false; // not facing the player... self->ideal_yaw = jumpAngles[YAW]; M_ChangeYaw(self); length = VectorLength(dist); if(length > 450) return false; // can't jump that far... VectorCopy(dest, jumpLZ); preferHighJump = 0; // if we're having to jump up a distance, jump a little too high to compensate. if(dist[2] >= 32.0) { preferHighJump = 1; jumpLZ[2] += 32; } trace = gi.trace (self->s.origin, vec3_origin, vec3_origin, dest, self, MASK_MONSTERSOLID); if((trace.fraction < 1) && (trace.ent != self->enemy)) { // gi.dprintf("prefer high jump angle\n"); preferHighJump = 1; } // find a valid angle/velocity combination while(velocity <= 800) { calcJumpAngle(self->s.origin, jumpLZ, velocity, jumpAngles); if((!_isnan(jumpAngles[0])) || (!_isnan(jumpAngles[1]))) break; velocity+=200; }; if(!preferHighJump && (!_isnan(jumpAngles[0])) ) { AngleVectors (self->s.angles, forward, right, NULL); VectorNormalize ( forward ) ; VectorScale( forward, velocity * cos(DEG2RAD(jumpAngles[0])), self->velocity); self->velocity[2] = velocity * sin(DEG2RAD(jumpAngles[0])) + (0.5 * sv_gravity->value * FRAMETIME); // gi.dprintf(" pouncing! %0.1f,%0.1f (%0.1f) --> %0.1f, %0.1f, %0.1f\n", // jumpAngles[0], jumpAngles[1], jumpAngles[0], // self->velocity[0], self->velocity[1], self->velocity[2]); return 1; } if(!_isnan(jumpAngles[1])) { AngleVectors (self->s.angles, forward, right, NULL); VectorNormalize ( forward ) ; VectorScale( forward, velocity * cos(DEG2RAD(jumpAngles[1])), self->velocity); self->velocity[2] = velocity * sin(DEG2RAD(jumpAngles[1])) + (0.5 * sv_gravity->value * FRAMETIME); // gi.dprintf(" pouncing! %0.1f,%0.1f (%0.1f) --> %0.1f, %0.1f, %0.1f\n", // jumpAngles[0], jumpAngles[1], jumpAngles[1], // self->velocity[0], self->velocity[1], self->velocity[2]); return 1; } // gi.dprintf(" nan\n"); return 0; }
//========================= //========================= qboolean stalker_ok_to_transition (edict_t *self) { trace_t trace; vec3_t pt, start; float max_dist; float margin; float end_height; if(STALKER_ON_CEILING(self)) { max_dist = -384; margin = self->mins[2] - 8; } else { // her stalkers are just better if (self->monsterinfo.aiflags & AI_SPAWNED_WIDOW) max_dist = 256; else max_dist = 180; margin = self->maxs[2] + 8; } VectorCopy(self->s.origin, pt); pt[2] += max_dist; trace = gi.trace (self->s.origin, self->mins, self->maxs, pt, self, MASK_MONSTERSOLID); if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world)) { if(STALKER_ON_CEILING(self)) { if(trace.plane.normal[2] < 0.9) return false; } else { if(trace.plane.normal[2] > -0.9) return false; } } // gi.dprintf("stalker_check_pt: main check ok\n"); end_height = trace.endpos[2]; // check the four corners, tracing only to the endpoint of the center trace (vertically). pt[0] = self->absmin[0]; pt[1] = self->absmin[1]; pt[2] = trace.endpos[2] + margin; // give a little margin of error to allow slight inclines VectorCopy(pt, start); start[2] = self->s.origin[2]; trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID); if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world)) { // gi.dprintf("stalker_check_pt: absmin/absmin failed\n"); return false; } if(abs(end_height + margin - trace.endpos[2]) > 8) return false; pt[0] = self->absmax[0]; pt[1] = self->absmin[1]; VectorCopy(pt, start); start[2] = self->s.origin[2]; trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID); if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world)) { // gi.dprintf("stalker_check_pt: absmax/absmin failed\n"); return false; } if(abs(end_height + margin - trace.endpos[2]) > 8) return false; pt[0] = self->absmax[0]; pt[1] = self->absmax[1]; VectorCopy(pt, start); start[2] = self->s.origin[2]; trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID); if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world)) { // gi.dprintf("stalker_check_pt: absmax/absmax failed\n"); return false; } if(abs(end_height + margin - trace.endpos[2]) > 8) return false; pt[0] = self->absmin[0]; pt[1] = self->absmax[1]; VectorCopy(pt, start); start[2] = self->s.origin[2]; trace = gi.trace( start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID); if(trace.fraction == 1.0 || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world)) { // gi.dprintf("stalker_check_pt: absmin/absmax failed\n"); return false; } if(abs(end_height + margin - trace.endpos[2]) > 8) return false; return true; }
void stalker_pain(edict_t *self, edict_t *other /* unused */, float kick, int damage) { if (!self) { return; } if (self->deadflag == DEAD_DEAD) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (skill->value == 3) { return; /* no pain anims in nightmare */ } if (self->groundentity == NULL) { return; } /* if we're reactivating or false dying, ignore the pain. */ if ((self->monsterinfo.currentmove == &stalker_move_false_death_end) || (self->monsterinfo.currentmove == &stalker_move_false_death_start)) { return; } if (self->monsterinfo.currentmove == &stalker_move_false_death) { stalker_reactivate(self); return; } if ((self->health > 0) && (self->health < (self->max_health / 4))) { if (random() < (0.2 * skill->value)) { if (!STALKER_ON_CEILING(self) || stalker_ok_to_transition(self)) { stalker_false_death_start(self); return; } } } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; if (damage > 10) /* don't react unless the damage was significant */ { /* stalker should dodge jump periodically to help avoid damage. */ if (self->groundentity && (random() < 0.5)) { stalker_dodge_jump(self); } else { self->monsterinfo.currentmove = &stalker_move_pain; } gi.sound(self, CHAN_WEAPON, sound_pain, 1, ATTN_NORM, 0); } }
qboolean stalker_ok_to_transition(edict_t *self) { trace_t trace; vec3_t pt, start; float max_dist; float margin; float end_height; if (!self) { return false; } if (STALKER_ON_CEILING(self)) { max_dist = -384; margin = self->mins[2] - 8; } else { // ace - (because, reasons) make give them some randomness (and FIXME: add another way to differentiate Widow spawns) max_dist = 180 + (rand() & 76); /* her stalkers are just better */ /* if (self->monsterinfo.aiflags & AI_SPAWNED_WIDOW) { max_dist = 256; } else { max_dist = 180; }*/ margin = self->maxs[2] + 8; } VectorCopy(self->s.origin, pt); pt[2] += max_dist; trace = gi.trace(self->s.origin, self->mins, self->maxs, pt, self, MASK_MONSTERSOLID); if ((trace.fraction == 1.0) || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world)) { if (STALKER_ON_CEILING(self)) { if (trace.plane.normal[2] < 0.9) { return false; } } else { if (trace.plane.normal[2] > -0.9) { return false; } } } end_height = trace.endpos[2]; /* check the four corners, tracing only to the endpoint of the center trace (vertically). */ pt[0] = self->absmin[0]; pt[1] = self->absmin[1]; pt[2] = trace.endpos[2] + margin; /* give a little margin of error to allow slight inclines */ VectorCopy(pt, start); start[2] = self->s.origin[2]; trace = gi.trace(start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID); if ((trace.fraction == 1.0) || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world)) { return false; } if (fabsf(end_height + margin - trace.endpos[2]) > 8) { return false; } pt[0] = self->absmax[0]; pt[1] = self->absmin[1]; VectorCopy(pt, start); start[2] = self->s.origin[2]; trace = gi.trace(start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID); if ((trace.fraction == 1.0) || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world)) { return false; } if (fabsf(end_height + margin - trace.endpos[2]) > 8) { return false; } pt[0] = self->absmax[0]; pt[1] = self->absmax[1]; VectorCopy(pt, start); start[2] = self->s.origin[2]; trace = gi.trace(start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID); if ((trace.fraction == 1.0) || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world)) { return false; } if (fabsf(end_height + margin - trace.endpos[2]) > 8) { return false; } pt[0] = self->absmin[0]; pt[1] = self->absmax[1]; VectorCopy(pt, start); start[2] = self->s.origin[2]; trace = gi.trace(start, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID); if ((trace.fraction == 1.0) || !(trace.contents & CONTENTS_SOLID) || (trace.ent != world)) { return false; } if (fabsf(end_height + margin - trace.endpos[2]) > 8) { return false; } return true; }