/* ====================== SV_StepDirection Turns to the movement direction, and walks the current distance if facing it. ====================== */ qboolean SV_StepDirection (edict_t *ent, float yaw, float dist) { vec3_t move, oldorigin; float delta; ent->ideal_yaw = yaw; M_ChangeYaw (ent); yaw = yaw*M_PI*2 / 360; move[0] = cos(yaw)*dist; move[1] = sin(yaw)*dist; move[2] = 0; VectorCopy (ent->s.origin, oldorigin); if (SV_movestep (ent, move, false)) { delta = ent->s.angles[YAW] - ent->ideal_yaw; if (delta > 45 && delta < 315) { // not turned far enough, so don't take the step VectorCopy (oldorigin, ent->s.origin); } gi.linkentity (ent); G_TouchTriggers (ent); return true; } gi.linkentity (ent); G_TouchTriggers (ent); return false; }
void body_think(edict_t *self) { float r; if (fabsf(self->ideal_yaw - anglemod(self->s.angles[YAW])) < 2) { if (self->timestamp < level.time) { r = random(); if (r < 0.10) { self->ideal_yaw = random() * 350.0; self->timestamp = level.time + 1; } } } else { M_ChangeYaw(self); } self->s.frame++; if (self->s.frame > FRAME_stand40) { self->s.frame = FRAME_stand01; } self->nextthink = level.time + 0.1; }
void fly_vertical (edict_t *self) { int i; vec3_t v; vec3_t forward, right, up; vec3_t start; vec3_t tempvec; VectorSubtract (self->goalentity->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); if (self->s.frame == FRAME_landing_58 || self->s.frame == FRAME_takeoff_16) { self->goalentity->nextthink = level.time + 0.1; self->goalentity->think = G_FreeEdict; self->monsterinfo.currentmove = &fixbot_move_stand; self->goalentity = self->enemy = NULL; } // kick up some particles VectorCopy (self->s.angles, tempvec); tempvec[PITCH] += 90; AngleVectors (tempvec, forward, right, up); VectorCopy (self->s.origin, start); for (i=0; i< 10; i++) blastoff (self, start, forward, 2, 1, TE_SHOTGUN, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD); // needs sound }
void mybrain_suxor (edict_t *self) { int damage, range, pull; vec3_t start, v, end; trace_t tr; if (!self->enemy) return; if (!self->enemy->inuse) return; self->lastsound = level.framenum; G_EntMidPoint(self->enemy, start); VectorSubtract(start, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); range = VectorLength(v); VectorNormalize(v); VectorMA(self->s.origin, 512, v, end); tr = gi.trace(self->s.origin, NULL, NULL, end, self, MASK_SHOT); if (G_EntExists(tr.ent) && (tr.ent == self->enemy)) { damage = BRAIN_INITIAL_TENTACLE_DMG + BRAIN_ADDON_TENTACLE_DMG*self->monsterinfo.level; pull = BRAIN_INITIAL_PULL + BRAIN_ADDON_PULL*self->monsterinfo.level; if (tr.ent->groundentity) pull *= 2; if (range > 64) T_Damage(tr.ent, self, self, v, tr.endpos, tr.plane.normal, 0, pull, 0, MOD_UNKNOWN); else T_Damage(tr.ent, self, self, v, tr.endpos, tr.plane.normal, damage, pull, 0, MOD_UNKNOWN); } }
void thing_think_pause(edict_t *self) { edict_t *monster; if(level.time > self->touch_debounce_time) { thing_think(self); return; } monster = self->target_ent; if(!monster || !monster->inuse) { G_FreeEdict(self); return; } if(has_valid_enemy(monster)) { vec3_t dir; vec3_t angles; if(visible(monster->enemy,monster)) { self->touch_debounce_time = 0; thing_think(self); return; } VectorSubtract(monster->enemy->s.origin,monster->s.origin,dir); VectorNormalize(dir); vectoangles(dir,angles); monster->ideal_yaw = angles[YAW]; M_ChangeYaw(monster); } self->nextthink = level.time + FRAMETIME; }
// NOTE: pos should be the final goal, because monster will stop moving after reaching it! void M_MoveToPosition (edict_t *ent, vec3_t pos, float dist) { vec3_t v; // stay in-place for medic healing if (ent->holdtime > level.time) return; // need to be touching the ground if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)) && !ent->waterlevel) { //gi.dprintf("not touching ground\n"); return; } // we are close enough if (distance(ent->s.origin, pos) < 32) { //gi.dprintf("close enough %.0f\n", distance(ent->s.origin, pos)); // look at the final position VectorSubtract(pos, ent->s.origin, v); VectorNormalize(v); ent->ideal_yaw = vectoyaw(v); M_ChangeYaw(ent); return; } // dont move so fast in the water if (!(ent->flags & (FL_FLY|FL_SWIM)) && (ent->waterlevel > 1)) dist *= 0.5; // if we can't take a step, try moving in another direction if (!SV_StepDirection (ent, ent->ideal_yaw, dist, true)) { //gi.dprintf("couldn't step\n"); // if the monster hasn't moved much, then increment // the number of frames it has been stuck if (distance(ent->s.origin, ent->monsterinfo.stuck_org) < 64) ent->monsterinfo.stuck_frames++; else ent->monsterinfo.stuck_frames = 0; // record current position for comparison VectorCopy(ent->s.origin, ent->monsterinfo.stuck_org); // attempt a course-correction if (ent->inuse && (level.time > ent->monsterinfo.bump_delay)) { //gi.dprintf("tried course correction\n"); //SV_NewChaseDir (ent, goal, dist); SV_NewChaseDir2(ent, pos, dist); ent->monsterinfo.bump_delay = level.time + FRAMETIME*GetRandom(3, 9); return; } } //else //gi.dprintf("step OK\n"); }
void minisentry_idle (edict_t *self) { float min_yaw, max_yaw; if (self->delay > level.time) return; if (self->delay == level.time) gi.sound(self, CHAN_AUTO, gi.soundindex("weapons/gunidle1.wav"), 0.5, ATTN_NORM, 0); // self->yaw_speed = 5; max_yaw = self->move_angles[YAW] + 45; min_yaw = self->move_angles[YAW] - 45; // validate angles if (max_yaw < 0) max_yaw += 360; else if (max_yaw > 360) max_yaw -= 360; if (min_yaw < 0) min_yaw += 360; else if (min_yaw > 360) min_yaw -= 360; // sentry scans side-to-side looking for targets if (self->style == SENTRY_IDLE_RIGHT) { self->ideal_yaw = max_yaw; M_ChangeYaw(self); if (self->s.angles[YAW] == self->ideal_yaw) { self->style = SENTRY_IDLE_LEFT; self->delay = level.time + 1.0; } } else { self->ideal_yaw = min_yaw; M_ChangeYaw(self); if (self->s.angles[YAW] == self->ideal_yaw) { self->style = SENTRY_IDLE_RIGHT; self->delay = level.time + 1.0; } } }
/* ============= ai_turn don't move, but turn towards ideal_yaw Distance is for slight position adjustments needed by the animations ============= */ void ai_turn (edict_t *self, float dist) { if (dist) M_walkmove (self, self->s.angles[YAW], dist); if (FindTarget (self)) return; M_ChangeYaw (self); }
/* ============= ai_run_missile Turn in place until within an angle to launch a missile attack ============= */ void ai_run_missile(edict_t *self) { self->ideal_yaw = enemy_yaw; M_ChangeYaw (self); if (FacingIdeal(self)) { self->monsterinfo.attack (self); self->monsterinfo.attack_state = AS_STRAIGHT; } }
/* ============= ai_charge Turns towards target and advances Use this call with a distnace of 0 to replace ai_face ============== */ void ai_charge (edict_t *self, float dist) { vec3_t v; VectorSubtract (self->enemy->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); if (dist) M_walkmove (self, self->s.angles[YAW], dist); }
void ai_move2 (edict_t *self, float dist) { vec3_t v; if (dist) M_walkmove (self, self->s.angles[YAW], dist); VectorSubtract (self->goalentity->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); };
void ai_facing (edict_t *self, float dist) { vec3_t v; if (infront (self, self->goalentity)) self->monsterinfo.currentmove = &fixbot_move_forward; else { VectorSubtract (self->goalentity->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); } };
/* ============= ai_stand Used for standing around and looking for players Distance is for slight position adjustments needed by the animations ============== */ void ai_stand (edict_t *self, float dist) { vec3_t v; if (dist) M_walkmove (self, self->s.angles[YAW], dist); if (self->monsterinfo.aiflags & AI_STAND_GROUND) { if (self->enemy) { VectorSubtract (self->enemy->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); if (self->s.angles[YAW] != self->ideal_yaw && self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) { self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); self->monsterinfo.run (self); } M_ChangeYaw (self); ai_checkattack (self, 0); } else FindTarget (self); return; } if (FindTarget (self)) return; if (level.time > self->monsterinfo.pausetime) { self->monsterinfo.walk (self); return; } if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && (level.time > self->monsterinfo.idle_time)) { if (self->monsterinfo.idle_time) { self->monsterinfo.idle (self); self->monsterinfo.idle_time = level.time + 15000 + random() * 15000; } else { self->monsterinfo.idle_time = level.time + random() * 15000; } } }
void Widow2TonguePull(edict_t *self) { vec3_t vec; vec3_t f, r, u; vec3_t start, end; if (!self) { return; } if ((!self->enemy) || (!self->enemy->inuse)) { self->monsterinfo.run(self); return; } AngleVectors(self->s.angles, f, r, u); G_ProjectSource2(self->s.origin, offsets[self->s.frame - FRAME_tongs01], f, r, u, start); VectorCopy(self->enemy->s.origin, end); if (!widow2_tongue_attack_ok(start, end, 256)) { return; } if (self->enemy->groundentity) { self->enemy->s.origin[2] += 1; self->enemy->groundentity = NULL; } VectorSubtract(self->s.origin, self->enemy->s.origin, vec); if (self->enemy->client) { VectorNormalize(vec); VectorMA(self->enemy->velocity, 1000, vec, self->enemy->velocity); } else { self->enemy->ideal_yaw = vectoyaw(vec); M_ChangeYaw(self->enemy); VectorScale(f, 1000, self->enemy->velocity); } }
void mybrain_jump_hold (edict_t *self) { vec3_t v; if (G_EntIsAlive(self->monsterinfo.attacker)) { // face the attacker VectorSubtract(self->monsterinfo.attacker->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw(self); } // check for landing or jump timeout if (self->waterlevel || self->groundentity || (level.time > self->monsterinfo.pausetime)) self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; else self->monsterinfo.aiflags |= AI_HOLD_FRAME; }
/* ============= ai_run_slide Strafe sideways, but stay at aproximately the same range ============= */ void ai_run_slide(edict_t *self, float distance) { float ofs; self->ideal_yaw = enemy_yaw; M_ChangeYaw (self); if (self->monsterinfo.lefty) ofs = 90; else ofs = -90; if (M_walkmove (self, self->ideal_yaw + ofs, distance)) return; self->monsterinfo.lefty = 1 - self->monsterinfo.lefty; M_walkmove (self, self->ideal_yaw - ofs, distance); }
void MakronHyperblaster (edict_t *self) { vec3_t v; vec3_t dir; vec3_t vec; vec3_t start; vec3_t forward, right; int flash_number; int damage; if (!self->enemy) return; if (self->radius_dmg) damage = 40 + 6 * self->monsterinfo.skill; else damage = 20 + 3 * self->monsterinfo.skill; flash_number = MZ2_MAKRON_BLASTER_1 + (self->s.frame - FRAME_attak405); AngleVectors (self->s.angles, forward, right, NULL); G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start); VectorSubtract (self->enemy->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); VectorMA(self->enemy->s.origin, 0.3, self->enemy->velocity, vec); // VectorCopy (self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract (vec, start, vec); vectoangles (vec, vec); dir[0] = vec[0]; if (self->s.frame <= FRAME_attak413) dir[1] = self->s.angles[1] - (1.5 - 0.01 * self->monsterinfo.skill) * (self->s.frame - FRAME_attak413); else dir[1] = self->s.angles[1] + (1.5 - 0.01 * self->monsterinfo.skill) * (self->s.frame - FRAME_attak421); dir[2] = 0; AngleVectors (dir, forward, NULL, NULL); monster_fire_blaster (self, start, forward, damage, 600 + 15 * self->monsterinfo.skill, MZ2_MAKRON_BLASTER_1, EF_BLASTER); }
void fly_vertical2 (edict_t *self) { vec3_t v; int len; VectorSubtract (self->goalentity->s.origin, self->s.origin, v); len = VectorLength (v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); if (len < 32) { self->goalentity->nextthink = level.time + 0.1; self->goalentity->think = G_FreeEdict; self->monsterinfo.currentmove = &fixbot_move_stand; self->goalentity = self->enemy = NULL; } // needs sound }
void minisentry_lockon (edict_t *self) { float max, temp; vec3_t angles, v; // curse causes minisentry to fail to lock-on to enemy if ((que_typeexists(self->curses, CURSE)) && random() <= 0.8) return; temp = self->yaw_speed; self->yaw_speed *= 3; VectorSubtract(self->enemy->s.origin, self->s.origin, v); vectoangles(v, angles); self->ideal_yaw = vectoyaw(v); M_ChangeYaw(self); self->yaw_speed = temp; // restore original yaw speed self->s.angles[PITCH] = angles[PITCH]; ValidateAngles(self->s.angles); // maximum pitch is 65 degrees in either direction if (self->enemy->s.origin[2] > self->s.origin[2]) // if the enemy is above { if (self->owner && self->owner->style == SENTRY_FLIPPED) max = 340; // allow 20 degrees up else max = 315; // allow 45 degrees up if (self->s.angles[PITCH] < max) self->s.angles[PITCH] = max; } else { if (self->owner && self->owner->style == SENTRY_FLIPPED) max = 45; // allow 45 degrees down else max = 20; // allow 20 degrees down if (self->s.angles[PITCH] > max) self->s.angles[PITCH] = max; } }
void mymedic_jump_hold (edict_t *self) { vec3_t v; if (G_EntExists(self->monsterinfo.attacker)) { // face the attacker VectorSubtract(self->monsterinfo.attacker->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw(self); } // check for landing or jump timeout if (self->groundentity || (level.time > self->monsterinfo.pausetime)) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; VectorClear(self->velocity); } else { // we're still in the air self->monsterinfo.aiflags |= AI_HOLD_FRAME; } }
/* ====================== SV_StepDirection Turns to the movement direction, and walks the current distance if facing it. ====================== */ qboolean SV_StepDirection (edict_t *ent, float yaw, float dist) { vec3_t move, oldorigin; float delta; if(!ent->inuse) return true; // PGM g_touchtrigger free problem ent->ideal_yaw = yaw; M_ChangeYaw (ent); yaw = yaw*M_PI*2 / 360; move[0] = cos(yaw)*dist; move[1] = sin(yaw)*dist; move[2] = 0; VectorCopy (ent->s.origin, oldorigin); if (SV_movestep (ent, move, false)) { ent->monsterinfo.aiflags &= ~AI_BLOCKED; if(!ent->inuse) return true; // PGM g_touchtrigger free problem delta = ent->s.angles[YAW] - ent->ideal_yaw; if (strncmp(ent->classname, "monster_widow", 13)) { if (delta > 45 && delta < 315) { // not turned far enough, so don't take the step VectorCopy (oldorigin, ent->s.origin); } } gi.linkentity (ent); G_TouchTriggers (ent); return true; } gi.linkentity (ent); G_TouchTriggers (ent); return false; }
void mybrain_jumpattack_hold (edict_t *self) { int value=0; // we are on ground if (self->groundentity) value = 1; // we are in the water or passed jump timeout value else if (self->waterlevel || level.time > self->monsterinfo.pausetime) value = 2; // discontinue holding this frame if (value) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; if (value == 1) gi.sound (self, CHAN_WEAPON, sound_thud, 1, ATTN_NORM, 0); } else { vec3_t v; // hold this frame self->monsterinfo.aiflags |= AI_HOLD_FRAME; // face the enemy if he's alive and visible if (G_EntIsAlive(self->enemy) && visible(self, self->enemy)) { VectorSubtract(self->enemy->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw(self); } } }
/* =========== FindTarget Self is currently not attacking anything, so try to find a target Returns TRUE if an enemy was sighted When a player fires a missile, the point of impact becomes a fakeplayer so that monsters that see the impact will respond as if they had seen the player. To avoid spending too much time, only a single client (or fakeclient) is checked each frame. This means multi player games will have slightly slower noticing monsters. ============ */ bool FindTarget (edict_t *self) { edict_t *client; bool heardit; bool noise; int r; if (self->monsterinfo.aiflags & AI_GOOD_GUY) { if (self->goalentity && self->goalentity->r.inuse && self->goalentity->classname) { if (strcmp(self->goalentity->classname, "target_actor") == 0) return false; } //FIXME look for monsters? return false; } // if we're going to a combat point, just proceed if (self->monsterinfo.aiflags & AI_COMBAT_POINT) return false; // if the first spawnflag bit is set, the monster will only wake up on // really seeing the player, not another monster getting angry or hearing // something // revised behavior so they will wake up if they "see" a player make a noise // but not weapon impact/explosion noises heardit = false; if ((level.sight_entity_framenum+1 >= level.framenum) && !(self->spawnflags & 1) ) { client = level.sight_entity; if (client->enemy == self->enemy) { return false; } } else if (level.sound_entity_framenum+1 >= level.framenum) { client = level.sound_entity; heardit = true; } else if (!(self->enemy) && (level.sound2_entity_framenum+1 >= level.framenum) && !(self->spawnflags & 1) ) { client = level.sound2_entity; heardit = true; } else { client = level.sight_client; if (!client) return false; // no clients to get mad at } // if the entity went away, forget it if (!client->r.inuse) return false; if (client == self->enemy) return true; // JDC false; noise = strcmp(client->classname, "player_noise") == 0; if (!noise && (client->r.svflags & SVF_NOCLIENT)) return false; if (client->r.client) { if (client->flags & FL_NOTARGET) return false; } else if (client->r.svflags & SVF_MONSTER) { if (!client->enemy) return false; if (client->enemy->flags & FL_NOTARGET) return false; } else if (heardit) { if (client->r.owner->flags & FL_NOTARGET) return false; } else return false; if (!heardit) { r = range (self, client); if (r == RANGE_FAR) return false; // this is where we would check invisibility // is client in an spot too dark to be seen? // if (client->light_level <= 5) // return false; if (!G_Visible (self, client)) { return false; } if (r == RANGE_NEAR) { if (client->show_hostile < level.time && !G_InFront (self, client)) { return false; } } else if (r == RANGE_MID) { if (!G_InFront (self, client)) { return false; } } self->enemy = client; if (!noise) { self->monsterinfo.aiflags &= ~AI_SOUND_TARGET; if (!self->enemy->r.client) { self->enemy = self->enemy->enemy; if (!self->enemy->r.client) { self->enemy = NULL; return false; } } } } else // heardit { vec3_t temp; if (self->spawnflags & 1) { if (!G_Visible (self, client)) return false; } VectorSubtract (client->s.origin, self->s.origin, temp); if (VectorLength(temp) > 1000) // too far to hear { return false; } // check area portals - if they are different and not connected then we can't hear it if (client->r.areanum != self->r.areanum) if (!trap_CM_AreasConnected(self->r.areanum, client->r.areanum)) return false; self->ideal_yaw = vectoyaw(temp); M_ChangeYaw (self); // hunt the sound for a bit; hopefully find the real player self->monsterinfo.aiflags |= AI_SOUND_TARGET; self->enemy = client; } // // got one // FoundTarget (self); if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight)) self->monsterinfo.sight (self, self->enemy); return true; }
void hunter_think(edict_t *self) { edict_t *owner; vec3_t dir, ang; if (!self) { return; } /* if we've exited the level, just remove ourselves. */ if (level.intermissiontime) { sphere_think_explode(self); return; } owner = self->owner; if (!owner && !(self->spawnflags & SPHERE_DOPPLEGANGER)) { G_FreeEdict(self); return; } if (owner) { self->ideal_yaw = owner->s.angles[YAW]; } else if (self->enemy) /* fired by doppleganger */ { VectorSubtract(self->enemy->s.origin, self->s.origin, dir); vectoangles2(dir, ang); self->ideal_yaw = ang[YAW]; } M_ChangeYaw(self); if (self->enemy) { sphere_chase(self, 0); /* deal with sam raimi cam */ if (owner && (owner->flags & FL_SAM_RAIMI)) { if (self->inuse) { owner->movetype = MOVETYPE_FLYMISSILE; LookAtKiller(owner, self, self->enemy); /* owner is flying with us, move him too */ owner->movetype = MOVETYPE_FLYMISSILE; owner->viewheight = self->s.origin[2] - owner->s.origin[2]; VectorCopy(self->s.origin, owner->s.origin); VectorCopy(self->velocity, owner->velocity); VectorClear(owner->mins); VectorClear(owner->maxs); gi.linkentity(owner); } else /* sphere timed out */ { VectorClear(owner->velocity); owner->movetype = MOVETYPE_NONE; gi.linkentity(owner); } } } else { sphere_fly(self); } if (self->inuse) { self->nextthink = level.time + 0.1; } }
/* ====================== M_MoveToGoal ====================== */ void M_MoveToGoal (edict_t *ent, float dist) { edict_t *goal; goal = ent->goalentity; if (ent->holdtime > level.time) // stay in-place for medic healing return; if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)) && !ent->waterlevel) { //gi.dprintf("not touching ground\n"); return; } // if the next step hits the enemy, return immediately if (ent->enemy && (ent->enemy->solid == SOLID_BBOX) && SV_CloseEnough (ent, ent->enemy, dist) ) //GHz START { vec3_t v; // we need to keep turning to avoid getting stuck doing nothing if (ent->goalentity && ent->goalentity->inuse) // 3.89 make sure the monster still has a goal! { VectorSubtract(ent->goalentity->s.origin, ent->s.origin, v); VectorNormalize(v); ent->ideal_yaw = vectoyaw(v); M_ChangeYaw(ent); } return; } //GHz END // dont move so fast in the water if (!(ent->flags & (FL_FLY|FL_SWIM)) && (ent->waterlevel > 1)) dist *= 0.5; // bump around... // if we can't take a step, try moving in another direction if (!SV_StepDirection (ent, ent->ideal_yaw, dist, true)) { //gi.dprintf("couldnt step\n"); // if the monster hasn't moved much, then increment // the number of frames it has been stuck if (distance(ent->s.origin, ent->monsterinfo.stuck_org) < 64) ent->monsterinfo.stuck_frames++; else ent->monsterinfo.stuck_frames = 0; // record current position for comparison VectorCopy(ent->s.origin, ent->monsterinfo.stuck_org); // attempt a course-correction if (ent->inuse && (level.time > ent->monsterinfo.bump_delay)) { //gi.dprintf("tried course correction %s\n", ent->goalentity?"true":"false"); SV_NewChaseDir (ent, goal, dist); ent->monsterinfo.bump_delay = level.time + FRAMETIME*GetRandom(2, 5); return; } } }
/* ====================== SV_StepDirection Turns to the movement direction, and walks the current distance if facing it. ====================== */ qboolean SV_StepDirection (edict_t *ent, float yaw, float dist, qboolean try_smallstep) { vec3_t move, oldorigin; //float delta; float old_dist; // dist = dist); //gi.dprintf("SV_StepDirection\n"); old_dist = dist; ent->ideal_yaw = yaw; M_ChangeYaw (ent); if (!dist || fabs(dist) < 1) return true; yaw = yaw*M_PI*2 / 360; VectorCopy (ent->s.origin, oldorigin); // loop until we can move successfully while ((int)dist != 0) { move[0] = cos(yaw)*dist; move[1] = sin(yaw)*dist; move[2] = 0; if (SV_movestep (ent, move, false)) { /* delta = ent->s.angles[YAW] - ent->ideal_yaw; if (delta > 45 && delta < 315) { // not turned far enough, so don't take the step VectorCopy (oldorigin, ent->s.origin); gi.dprintf("not turned far enough!\n"); } */ gi.linkentity (ent); G_TouchTriggers (ent); /* if (old_dist != dist) gi.dprintf("took a smaller step of %d instead of %d\n", (int)dist, (int)old_dist); else gi.dprintf("moved full distance\n"); */ return true; } if (!try_smallstep) break; if (dist > 0) dist--; else dist++; } gi.linkentity (ent); G_TouchTriggers (ent); //gi.dprintf("%d: %s can't walk forward!\n", level.framenum, V_GetMonsterName(ent)); return false; }
/* ================== AI_BeginAttack Returns true if OK to attack, false otherwise May start a new movement for the character, to straighten up, or avoid something/someone ================== */ qboolean AI_BeginAttack( edict_t *self ) { vec3_t vec; float dist; // Ridah, 7-5-99, If they've been ordered to attack us by a client, get mad at them also if (self->enemy && self->enemy->leader) AI_MakeEnemy( self, self->enemy->leader, 0 ); if (!self->enemy || (self->enemy->health <= 0)) { self->cast_info.currentmove = self->cast_info.move_stand; return false; } dist = VectorDistance( self->s.origin, self->enemy->s.origin ); // Ridah, 19-may-99, Grenade AI if (self->cast_info.aiflags & AI_GRENADE_GUY) { if (( self->s.origin[2] - self->enemy->s.origin[2] ) < -256) { if (self->cast_info.currentmove != self->cast_info.move_avoid_run) AI_EndAttack(self); // try find something else to do return false; } } if ( !(self->cast_info.aiflags & AI_SIDE_ATTACK)) // Ridah 7/5/99, fixes shooting with back to player (happens in SR1 courtyard sometimes) // && ( (dist > FACE_ANIM_DIST) // || (self->cast_info.aiflags & AI_FACE_ATTACK))) { if (!directly_infront(self, self->enemy)) { // we need to straighten up float dist; VectorSubtract( self->enemy->s.origin, self->s.origin, vec ); dist = VectorNormalize( vec ); self->ideal_yaw = vectoyaw( vec ); if ((dist > 384) && (self->maxs[2] == self->cast_info.standing_max_z)) { int side_result; side_result = AI_SideTrace( self, 48, 90, SIDE_RANDOM ); if (side_result && self->cast_info.move_lside_step) { if (side_result < 0) self->cast_info.currentmove = self->cast_info.move_lside_step; else self->cast_info.currentmove = self->cast_info.move_rside_step; } else { self->cast_info.currentmove = self->cast_info.move_avoid_walk; } return false; } else { M_ChangeYaw(self); } } } /* if ( (self->cast_info.move_avoid_reverse_run) && (self->cast_info.last_reverse < (level.time - 5)) && (dist < 64)) { // trace a line backwards, make sure we can move there if ( AI_YawTrace( self, 64, 180 ) ) { self->cast_info.currentmove = self->cast_info.move_avoid_reverse_run; self->cast_info.last_reverse = level.time; return false; } } */ if (!AI_ClearSight(self, self->enemy, true)) { // can't see them directly, will we hit another enemy? if so keep firing static vec3_t mins = { -8, -8, -4 }; static vec3_t maxs = { 8, 8, 4 }; vec3_t start, end; trace_t tr; cast_memory_t *tr_memory; VectorCopy( self->s.origin, start ); start[2] += self->viewheight - 8; VectorCopy( self->enemy->s.origin, end ); end[2] += self->enemy->viewheight - 8; tr = gi.trace( start, mins, maxs, end, self, MASK_PLAYERSOLID ); if (tr.fraction < 1) { if (tr.ent == world) { if (self->cast_info.currentmove != self->cast_info.move_avoid_run) AI_EndAttack(self); // try find something else to do return false; } else if (tr.ent->svflags & SVF_MONSTER || tr.ent->client) { tr_memory = level.global_cast_memory[self->character_index][tr.ent->character_index]; if (!tr_memory || !(tr_memory->flags & MEMORY_HOSTILE_ENEMY)) { if (self->cast_info.currentmove != self->cast_info.move_avoid_run) AI_EndAttack(self); // try find something else to do return false; } } } } if (self->dont_takecover_time == 99999) { // attack for at least 2 seconds if (!(self->cast_info.aiflags & AI_MELEE)) self->dont_takecover_time = level.time + 2 + random()*4; else self->dont_takecover_time = 0; } return true; }
// ==================== // ==================== 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; }
void Trap_Think(edict_t *ent) { edict_t *target = NULL; edict_t *best = NULL; vec3_t vec; int len, i; int oldlen = 8000; vec3_t forward, right, up; if (!ent) { return; } if (ent->timestamp < level.time) { BecomeExplosion1(ent); return; } ent->nextthink = level.time + 0.1; if (!ent->groundentity) { return; } /* ok lets do the blood effect */ if (ent->s.frame > 4) { if (ent->s.frame == 5) { if (ent->wait == 64) { gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/trapdown.wav"), 1, ATTN_IDLE, 0); } ent->wait -= 2; ent->delay += level.time; for (i = 0; i < 3; i++) { best = G_Spawn(); if (strcmp(ent->enemy->classname, "monster_gekk") == 0) { best->s.modelindex = gi.modelindex("models/objects/gekkgib/torso/tris.md2"); best->s.effects |= TE_GREENBLOOD; } else if (ent->mass > 200) { best->s.modelindex = gi.modelindex("models/objects/gibs/chest/tris.md2"); best->s.effects |= TE_BLOOD; } else { best->s.modelindex = gi.modelindex("models/objects/gibs/sm_meat/tris.md2"); best->s.effects |= TE_BLOOD; } AngleVectors(ent->s.angles, forward, right, up); RotatePointAroundVector(vec, up, right, ((360.0 / 3) * i) + ent->delay); VectorMA(vec, ent->wait / 2, vec, vec); VectorAdd(vec, ent->s.origin, vec); VectorAdd(vec, forward, best->s.origin); best->s.origin[2] = ent->s.origin[2] + ent->wait; VectorCopy(ent->s.angles, best->s.angles); best->solid = SOLID_NOT; best->s.effects |= EF_GIB; best->takedamage = DAMAGE_YES; best->movetype = MOVETYPE_TOSS; best->svflags |= SVF_MONSTER; best->deadflag = DEAD_DEAD; VectorClear(best->mins); VectorClear(best->maxs); best->watertype = gi.pointcontents(best->s.origin); if (best->watertype & MASK_WATER) { best->waterlevel = 1; } best->nextthink = level.time + 0.1; best->think = G_FreeEdict; gi.linkentity(best); } if (ent->wait < 19) { ent->s.frame++; } return; } ent->s.frame++; if (ent->s.frame == 8) { ent->nextthink = level.time + 1.0; ent->think = G_FreeEdict; best = G_Spawn(); SP_item_foodcube(best); VectorCopy(ent->s.origin, best->s.origin); best->s.origin[2] += 16; best->velocity[2] = 400; best->count = ent->mass; gi.linkentity(best); return; } return; } ent->s.effects &= ~EF_TRAP; if (ent->s.frame >= 4) { ent->s.effects |= EF_TRAP; VectorClear(ent->mins); VectorClear(ent->maxs); } if (ent->s.frame < 4) { ent->s.frame++; } while ((target = findradius(target, ent->s.origin, 256)) != NULL) { if (target == ent) { continue; } if (!(target->svflags & SVF_MONSTER) && !target->client) { continue; } if (target->health <= 0) { continue; } if (!visible(ent, target)) { continue; } if (!best) { best = target; continue; } VectorSubtract(ent->s.origin, target->s.origin, vec); len = VectorLength(vec); if (len < oldlen) { oldlen = len; best = target; } } /* pull the enemy in */ if (best) { vec3_t forward; if (best->groundentity) { best->s.origin[2] += 1; best->groundentity = NULL; } VectorSubtract(ent->s.origin, best->s.origin, vec); len = VectorLength(vec); if (best->client) { VectorNormalize(vec); VectorMA(best->velocity, 250, vec, best->velocity); } else { best->ideal_yaw = vectoyaw(vec); M_ChangeYaw(best); AngleVectors(best->s.angles, forward, NULL, NULL); VectorScale(forward, 256, best->velocity); } gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/trapsuck.wav"), 1, ATTN_IDLE, 0); if (len < 32) { if (best->mass < 400) { T_Damage(best, ent, ent->owner, vec3_origin, best->s.origin, vec3_origin, 100000, 1, 0, MOD_TRAP); ent->enemy = best; ent->wait = 64; VectorCopy(ent->s.origin, ent->s.old_origin); ent->timestamp = level.time + 30; if (deathmatch->value) { ent->mass = best->mass / 4; } else { ent->mass = best->mass / 10; } /* ok spawn the food cube */ ent->s.frame = 5; } else { BecomeExplosion1(ent); return; } } } }
// ================= // ================= void hunter_think (edict_t *self) { edict_t *owner; vec3_t dir, ang; // if we've exited the level, just remove ourselves. if (level.intermissiontime) { sphere_think_explode(self); return; } owner = self->owner; if(!owner && !(self->spawnflags & SPHERE_DOPPLEGANGER)) { // gi.dprintf("think: no owner\n"); G_FreeEdict(self); return; } if(owner) self->ideal_yaw = owner->s.angles[YAW]; else if(self->enemy) // fired by doppleganger { VectorSubtract(self->enemy->s.origin, self->s.origin, dir); vectoangles2(dir, ang); self->ideal_yaw = ang[YAW]; } M_ChangeYaw(self); // if(level.time - self->timestamp > 1) // { // gi.sound (self, CHAN_VOICE, gi.soundindex ("powerup/hsphere.wav"), 0.5, ATTN_NORM, 0); // self->timestamp = level.time; // } if(self->enemy) { sphere_chase (self, 0); // deal with sam raimi cam if(owner && (owner->flags & FL_SAM_RAIMI)) { if(self->inuse) { owner->movetype = MOVETYPE_FLYMISSILE; // VectorCopy(self->s.angles, owner->s.angles); // VectorCopy(self->s.angles, owner->client->v_angle); LookAtKiller (owner, self, self->enemy); // owner->viewheight = 22; // owner->client->v_angle[YAW]+=5; // owner is flying with us, move him too owner->movetype = MOVETYPE_FLYMISSILE; owner->viewheight = self->s.origin[2] - owner->s.origin[2]; // VectorCopy(self->s.angles, owner->s.angles); // VectorCopy(self->s.angles, owner->client->v_angle); VectorCopy(self->s.origin, owner->s.origin); VectorCopy(self->velocity, owner->velocity); VectorClear(owner->mins); VectorClear(owner->maxs); gi.linkentity(owner); } else // sphere timed out { VectorClear(owner->velocity); owner->movetype = MOVETYPE_NONE; gi.linkentity(owner); } } } else { // self->ideal_yaw+=3; // M_ChangeYaw (self); sphere_fly (self); } if(self->inuse) self->nextthink = level.time + 0.1; }