void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { vec3_t v; edict_t *next; if (other->movetarget != self) return; if (other->enemy) return; #ifdef IML_Q2_EXTENSIONS if (!ai_movetarget_is_active(other)) return; #endif if (self->pathtarget) { char *savetarget; savetarget = self->target; self->target = self->pathtarget; G_UseTargets (self, other); self->target = savetarget; } if (self->target) next = G_PickTarget(self->target); else next = NULL; if ((next) && (next->spawnflags & PATH_CORNER_TELEPORT)) { VectorCopy (next->s.origin, v); v[2] += next->mins[2]; v[2] -= other->mins[2]; VectorCopy (v, other->s.origin); next = G_PickTarget(next->target); other->s.event = EV_OTHER_TELEPORT; } // 18 Aug 2004 - IML - madhura - path_corner enhancements // was: other->goalentity = other->movetarget = next; other->movetarget = next; other->goalentity = NULL; if (ai_movetarget_is_active(other)) other->goalentity = other->movetarget; if (self->wait) { other->monsterinfo.pausetime = level.time + self->wait; other->monsterinfo.stand (other); return; } // 18 Aug 2004 - IML - madhura - path_corner enhancements // was: if (!other->movetarget) if (!other->goalentity) { other->monsterinfo.pausetime = level.time + 100000000; other->monsterinfo.stand (other); } else { VectorSubtract (other->goalentity->s.origin, other->s.origin, v); other->ideal_yaw = vectoyaw (v); } }
void HintTestNext (edict_t *self, edict_t *hint) { edict_t *next=NULL; edict_t *e; vec3_t dir; self->monsterinfo.aiflags &= ~AI_HINT_TEST; if(self->goalentity == hint) self->goalentity = NULL; if(self->movetarget == hint) self->movetarget = NULL; if(self->monsterinfo.pathdir == 1) { if(hint->hint_chain) next = hint->hint_chain; else self->monsterinfo.pathdir = -1; } if(self->monsterinfo.pathdir == -1) { e = hint_chain_starts[hint->hint_chain_id]; while(e) { if(e->hint_chain == hint) { next = e; break; } e = e->hint_chain; } } if(!next) { self->monsterinfo.pathdir = 1; next = hint->hint_chain; } if(next) { self->hint_chain_id = next->hint_chain_id; VectorSubtract(next->s.origin, self->s.origin, dir); self->ideal_yaw = vectoyaw(dir); self->goalentity = self->movetarget = next; self->monsterinfo.pausetime = 0; self->monsterinfo.aiflags = AI_HINT_TEST; // run for it self->monsterinfo.run (self); gi.dprintf("%s (%s): Reached hint_path %s,\nsearching for hint_path %s at %s. %s\n", self->classname, (self->targetname ? self->targetname : "<noname>"), (hint->targetname ? hint->targetname : "<noname>"), (next->targetname ? next->targetname : "<noname>"), vtos(next->s.origin), (visible(self,next) ? "I see it." : "I don't see it.")); } else { self->monsterinfo.pausetime = level.time + 100000000; self->monsterinfo.stand (self); gi.dprintf("%s (%s): Error finding next/previous hint_path from %s at %s.\n", self->classname, (self->targetname ? self->targetname : "<noname>"), (hint->targetname ? hint->targetname : "<noname>"), vtos(hint->s.origin)); } }
void monster_start_go (edict_t *self) { vec3_t v; if (self->health <= 0) { if (self->max_health <= 0) InitiallyDead(self); return; } // Lazarus: move_origin for func_monitor if(!VectorLength(self->move_origin)) VectorSet(self->move_origin,0,0,self->viewheight); // check for target to point_combat and change to combattarget if (self->target) { qboolean notcombat; qboolean fixup; edict_t *target; target = NULL; notcombat = false; fixup = false; while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL) { if (strcmp(target->classname, "point_combat") == 0) { self->combattarget = self->target; fixup = true; } else { notcombat = true; } } if (notcombat && self->combattarget) gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin)); if (fixup) self->target = NULL; } // validate combattarget if (self->combattarget) { edict_t *target; target = NULL; while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL) { if (strcmp(target->classname, "point_combat") != 0) { gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n", self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2], self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1], (int)target->s.origin[2]); } } } if (self->target) { self->goalentity = self->movetarget = G_PickTarget(self->target); if (!self->movetarget) { gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin)); self->target = NULL; self->monsterinfo.pausetime = 100000000; self->monsterinfo.stand (self); } else if (strcmp (self->movetarget->classname, "path_corner") == 0) { // Lazarus: Don't wipe out target for trigger spawned monsters // that aren't triggered yet if( ! (self->spawnflags & SF_MONSTER_TRIGGER_SPAWN) ) { VectorSubtract (self->goalentity->s.origin, self->s.origin, v); self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v); self->monsterinfo.walk (self); self->target = NULL; } } else { self->goalentity = self->movetarget = NULL; self->monsterinfo.pausetime = 100000000; self->monsterinfo.stand (self); } } else { self->monsterinfo.pausetime = 100000000; self->monsterinfo.stand (self); } self->think = monster_think; self->nextthink = level.time + FRAMETIME; }
/* =========== 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; }
qboolean PM_AdjustAngleForWallRun( gentity_t *ent, usercmd_t *ucmd, qboolean doMove ) { if (( ent->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT || ent->client->ps.legsAnim == BOTH_WALL_RUN_LEFT ) && ent->client->ps.legsAnimTimer > 500 ) { //wall-running and not at end of anim //stick to wall, if there is one vec3_t rt, traceTo, mins = {ent->mins[0],ent->mins[1],0}, maxs = {ent->maxs[0],ent->maxs[1],24}, fwdAngles = {0, ent->client->ps.viewangles[YAW], 0}; trace_t trace; float dist, yawAdjust; AngleVectors( fwdAngles, NULL, rt, NULL ); if ( ent->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT ) { dist = 128; yawAdjust = -90; } else { dist = -128; yawAdjust = 90; } VectorMA( ent->currentOrigin, dist, rt, traceTo ); gi.trace( &trace, ent->currentOrigin, mins, maxs, traceTo, ent->s.number, ent->clipmask, (EG2_Collision)0, 0); if ( trace.fraction < 1.0f && trace.plane.normal[2] == 0.0f ) { //still a vertical wall there //FIXME: don't pull around 90 turns //FIXME: simulate stepping up steps here, somehow? if ( ent->s.number || !player_locked ) { if ( ent->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT ) { ucmd->rightmove = 127; } else { ucmd->rightmove = -127; } } if ( ucmd->upmove < 0 ) { ucmd->upmove = 0; } if ( ent->NPC ) { //invalid now VectorClear( ent->client->ps.moveDir ); } //make me face perpendicular to the wall ent->client->ps.viewangles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust; if ( ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD ) { //don't clamp angles when looking through a viewEntity SetClientViewAngle( ent, ent->client->ps.viewangles ); } ucmd->angles[YAW] = ANGLE2SHORT( ent->client->ps.viewangles[YAW] ) - ent->client->ps.delta_angles[YAW]; if ( ent->s.number || !player_locked ) { if ( doMove ) { //push me forward vec3_t fwd; float zVel = ent->client->ps.velocity[2]; if ( zVel > forceJumpStrength[FORCE_LEVEL_2]/2.0f ) { zVel = forceJumpStrength[FORCE_LEVEL_2]/2.0f; } if ( ent->client->ps.legsAnimTimer > 500 ) { //not at end of anim yet fwdAngles[YAW] = ent->client->ps.viewangles[YAW]; AngleVectors( fwdAngles, fwd, NULL, NULL ); //FIXME: or MA? float speed = 175; if ( ucmd->forwardmove < 0 ) { //slower speed = 100; } else if ( ucmd->forwardmove > 0 ) { speed = 250;//running speed } VectorScale( fwd, speed, ent->client->ps.velocity ); } ent->client->ps.velocity[2] = zVel;//preserve z velocity VectorMA( ent->client->ps.velocity, -128, trace.plane.normal, ent->client->ps.velocity ); //pull me toward the wall, too //VectorMA( ent->client->ps.velocity, dist, rt, ent->client->ps.velocity ); } } ucmd->forwardmove = 0; return qtrue; } else if ( doMove ) { //stop it if ( ent->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT ) { NPC_SetAnim( ent, SETANIM_BOTH, BOTH_WALL_RUN_RIGHT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } else if ( ent->client->ps.legsAnim == BOTH_WALL_RUN_LEFT ) { NPC_SetAnim( ent, SETANIM_BOTH, BOTH_WALL_RUN_LEFT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } } } return qfalse; }
void target_actor_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { vec3_t v; if (other->movetarget != self) return; if (other->enemy) return; other->goalentity = other->movetarget = NULL; if (self->message) { int n; edict_t *ent; for (n = 1; n <= game.maxclients; n++) { ent = &g_edicts[n]; if (!ent->inuse) continue; gi.cprintf (ent, PRINT_CHAT, "%s: %s\n", actor_names[(other - g_edicts)%MAX_ACTOR_NAMES], self->message); } } if (self->spawnflags & 1) //jump { other->velocity[0] = self->movedir[0] * self->speed; other->velocity[1] = self->movedir[1] * self->speed; if (other->groundentity) { other->groundentity = NULL; other->velocity[2] = self->movedir[2]; gi.sound(other, CHAN_VOICE, gi.soundindex("player/male/jump1.wav"), 1, ATTN_NORM, 0); } } if (self->spawnflags & 2) //shoot { } else if (self->spawnflags & 4) //attack { other->enemy = G_PickTarget(self->pathtarget); if (other->enemy) { other->goalentity = other->enemy; if (self->spawnflags & 32) other->monsterinfo.aiflags |= AI_BRUTAL; if (self->spawnflags & 16) { other->monsterinfo.aiflags |= AI_STAND_GROUND; actor_stand (other); } else { actor_run (other); } } } if (!(self->spawnflags & 6) && (self->pathtarget)) { char *savetarget; savetarget = self->target; self->target = self->pathtarget; G_UseTargets (self, other); self->target = savetarget; } other->movetarget = G_PickTarget(self->target); if (!other->goalentity) other->goalentity = other->movetarget; if (!other->movetarget && !other->enemy) { other->monsterinfo.pausetime = level.time + 100000000; other->monsterinfo.stand (other); } else if (other->movetarget == other->goalentity) { VectorSubtract (other->movetarget->s.origin, other->s.origin, v); other->ideal_yaw = vectoyaw (v); } }
qboolean Boss2_CheckAttack(edict_t *self) { vec3_t spot1, spot2; vec3_t temp; float chance; trace_t tr; int enemy_range; float enemy_yaw; if (!self) { return false; } if (self->enemy->health > 0) { /* see if any entities are in the way of the shot */ VectorCopy(self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy(self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; tr = gi.trace( spot1, NULL, NULL, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA); /* do we have a clear shot? */ if (tr.ent != self->enemy) { return false; } } enemy_range = range(self, self->enemy); VectorSubtract(self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); self->ideal_yaw = enemy_yaw; /* melee attack */ if (enemy_range == RANGE_MELEE) { if (self->monsterinfo.melee) { self->monsterinfo.attack_state = AS_MELEE; } else { self->monsterinfo.attack_state = AS_MISSILE; } return true; } /* missile attack */ if (!self->monsterinfo.attack) { return false; } if (level.time < self->monsterinfo.attack_finished) { return false; } if (enemy_range == RANGE_FAR) { return false; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { chance = 0.4; } else if (enemy_range == RANGE_MELEE) { chance = 0.8; } else if (enemy_range == RANGE_NEAR) { chance = 0.8; } else if (enemy_range == RANGE_MID) { chance = 0.8; } else { return false; } if (random() < chance) { self->monsterinfo.attack_state = AS_MISSILE; self->monsterinfo.attack_finished = level.time + 2 * random(); return true; } if (self->flags & FL_FLY) { if (random() < 0.3) { self->monsterinfo.attack_state = AS_SLIDING; } else { self->monsterinfo.attack_state = AS_STRAIGHT; } } return false; }
/* ====================== 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; } } }
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 path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { vec3_t v; edict_t *next; if (other->movetarget != self) return; if (other->enemy) return; if (self->pathtarget) { char *savetarget; savetarget = self->target; self->target = self->pathtarget; G_UseTargets (self, other); self->target = savetarget; } if (self->target) next = G_PickTarget(self->target); else next = NULL; if ((next) && (next->spawnflags & 1)) { VectorCopy (next->s.origin, v); v[2] += next->mins[2]; v[2] -= other->mins[2]; VectorCopy (v, other->s.origin); next = G_PickTarget(next->target); } other->goalentity = other->movetarget = next; if (self->wait) { if(other->goalentity) { VectorSubtract (other->goalentity->s.origin, other->s.origin, v); other->ideal_yaw = vectoyaw (v); } other->monsterinfo.pausetime = level.time + self->wait; other->monsterinfo.stand (other); return; } if (!other->movetarget) { other->monsterinfo.pausetime = level.time + 100000000; other->monsterinfo.stand (other); } else { VectorSubtract (other->goalentity->s.origin, other->s.origin, v); other->ideal_yaw = vectoyaw (v); } }
/* ================== 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; }
/* ============= ai_run The monster has an enemy it is trying to kill ============= */ void ai_run( float dist ) { vec3_t tmpv; if ( k_bloodfest ) { if ( (int)self->s.v.flags & FL_SWIM ) { dist *= 5; // let fish swim faster in bloodfest mode. } else if ( self->bloodfest_boss ) { dist *= 2; // let boss move faster } } movedist = dist; // see if the enemy is dead if ( ISDEAD( PROG_TO_EDICT( self->s.v.enemy ) ) || ( (int)PROG_TO_EDICT( self->s.v.enemy )->s.v.flags & FL_NOTARGET ) ) { self->s.v.enemy = EDICT_TO_PROG( world ); // FIXME: look all around for other targets if ( self->oldenemy && ISLIVE( self->oldenemy ) && !( (int)self->oldenemy->s.v.flags & FL_NOTARGET ) ) { self->s.v.enemy = EDICT_TO_PROG( self->oldenemy ); HuntTarget (); } else { if ( !self->movetarget || self->movetarget == world ) { if ( self->th_stand ) self->th_stand(); } else { if ( self->th_walk ) self->th_walk(); } return; } } self->show_hostile = g_globalvars.time + 1; // wake up other monsters // check knowledge of enemy enemy_vis = visible( PROG_TO_EDICT( self->s.v.enemy ) ); if ( enemy_vis ) self->search_time = g_globalvars.time + 5; // does not search for enemy next 5 seconds // look for other coop players if ( coop && self->search_time < g_globalvars.time ) { if ( FindTarget() ) { // this is fix for too frequent enemy sighting, required for bloodfest mode. if ( !visible( PROG_TO_EDICT( self->s.v.enemy ) ) ) { self->search_time = g_globalvars.time + 5; // does not search for enemy next 5 seconds } return; } } enemy_infront = infront( PROG_TO_EDICT( self->s.v.enemy ) ); enemy_range = range( PROG_TO_EDICT( self->s.v.enemy ) ); VectorSubtract( PROG_TO_EDICT( self->s.v.enemy )->s.v.origin, self->s.v.origin, tmpv); enemy_yaw = vectoyaw( tmpv ); if ( self->attack_state == AS_MISSILE ) { //dprint ("ai_run_missile\n"); ai_run_missile(); return; } if ( self->attack_state == AS_MELEE ) { //dprint ("ai_run_melee\n"); ai_run_melee(); return; } if ( CheckAnyAttack() ) return; // beginning an attack if ( self->attack_state == AS_SLIDING ) { ai_run_slide(); return; } // head straight in movetogoal( dist ); // done in C code... }
/* ============= ai_run The monster has an enemy it is trying to kill ============= */ void ai_run (edict_t *self, float dist) { vec3_t v; edict_t *tempgoal; edict_t *save; bool newcourse; edict_t *marker; float d1, d2; trace_t tr; vec3_t v_forward, v_right; float left, center, right; vec3_t left_target, right_target; // if we're going to a combat point, just proceed if (self->monsterinfo.aiflags & AI_COMBAT_POINT) { M_MoveToGoal (self, dist); return; } if (self->monsterinfo.aiflags & AI_SOUND_TARGET) { VectorSubtract (self->s.origin, self->enemy->s.origin, v); if (VectorLength(v) < 64) { self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND); self->monsterinfo.stand (self); return; } M_MoveToGoal (self, dist); if (!FindTarget (self)) return; } if (ai_checkattack (self, dist)) return; if (self->monsterinfo.attack_state == AS_SLIDING) { ai_run_slide (self, dist); return; } if (enemy_vis) { M_MoveToGoal (self, dist); self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting); self->monsterinfo.trail_time = level.time; return; } #ifdef AI_FIXME // coop will change to another enemy if visible if (coop->integer) { // FIXME: insane guys get mad with this, which causes crashes! if (FindTarget (self)) return; } #endif if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20000))) { M_MoveToGoal (self, dist); self->monsterinfo.search_time = 0; return; } save = self->goalentity; tempgoal = G_Spawn(); self->goalentity = tempgoal; newcourse = false; if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT)) { // just lost sight of the player, decide where to go first self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN); self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP); newcourse = true; } if (self->monsterinfo.aiflags & AI_PURSUE_NEXT) { self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT; // give ourself more time since we got this far self->monsterinfo.search_time = level.time + 5000; if (self->monsterinfo.aiflags & AI_PURSUE_TEMP) { self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP; marker = NULL; VectorCopy (self->monsterinfo.saved_goal, self->monsterinfo.last_sighting); newcourse = true; } else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN) { self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN; marker = G_PlayerTrail_PickFirst (self); } else { marker = G_PlayerTrail_PickNext (self); } if (marker) { VectorCopy (marker->s.origin, self->monsterinfo.last_sighting); self->monsterinfo.trail_time = marker->timeStamp; self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW]; newcourse = true; } } VectorSubtract (self->s.origin, self->monsterinfo.last_sighting, v); d1 = VectorLength(v); if (d1 <= dist) { self->monsterinfo.aiflags |= AI_PURSUE_NEXT; dist = d1; } VectorCopy (self->monsterinfo.last_sighting, self->goalentity->s.origin); if (newcourse) { G_Trace (&tr, self->s.origin, self->r.mins, self->r.maxs, self->monsterinfo.last_sighting, self, MASK_PLAYERSOLID); if (tr.fraction < 1) { VectorSubtract (self->goalentity->s.origin, self->s.origin, v); d1 = VectorLength(v); center = tr.fraction; d2 = d1 * ((center+1)/2); self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); AngleVectors(self->s.angles, v_forward, v_right, NULL); VectorSet(v, d2, -16, 0); G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target); G_Trace (&tr, self->s.origin, self->r.mins, self->r.maxs, left_target, self, MASK_PLAYERSOLID); left = tr.fraction; VectorSet(v, d2, 16, 0); G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target); G_Trace (&tr, self->s.origin, self->r.mins, self->r.maxs, right_target, self, MASK_PLAYERSOLID); right = tr.fraction; center = (d1*center)/d2; if (left >= center && left > right) { if (left < 1) { VectorSet(v, d2 * left * 0.5, -16, 0); G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target); } VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal); self->monsterinfo.aiflags |= AI_PURSUE_TEMP; VectorCopy (left_target, self->goalentity->s.origin); VectorCopy (left_target, self->monsterinfo.last_sighting); VectorSubtract (self->goalentity->s.origin, self->s.origin, v); self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); } else if (right >= center && right > left) { if (right < 1) { VectorSet(v, d2 * right * 0.5, 16, 0); G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target); } VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal); self->monsterinfo.aiflags |= AI_PURSUE_TEMP; VectorCopy (right_target, self->goalentity->s.origin); VectorCopy (right_target, self->monsterinfo.last_sighting); VectorSubtract (self->goalentity->s.origin, self->s.origin, v); self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); } } } M_MoveToGoal (self, dist); G_FreeEdict(tempgoal); if (self) self->goalentity = save; }
/* ============= ai_checkattack Decides if we're going to attack or do something else used by ai_run and ai_stand ============= */ bool ai_checkattack (edict_t *self, float dist) { vec3_t temp; bool hesDeadJim; // this causes monsters to run blindly to the combat point w/o firing if (self->goalentity) { if (self->monsterinfo.aiflags & AI_COMBAT_POINT) return false; if (self->monsterinfo.aiflags & AI_SOUND_TARGET) { if (level.time - self->enemy->teleport_time > 5000) { if (self->goalentity == self->enemy) { if (self->movetarget) self->goalentity = self->movetarget; else self->goalentity = NULL; } self->monsterinfo.aiflags &= ~AI_SOUND_TARGET; if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); } else { self->show_hostile = level.time + 1000; return false; } } } enemy_vis = false; // see if the enemy is dead hesDeadJim = false; if ((!self->enemy) || (!self->enemy->r.inuse)) { hesDeadJim = true; } else if (self->monsterinfo.aiflags & AI_MEDIC) { if (self->enemy->health > 0) { hesDeadJim = true; self->monsterinfo.aiflags &= ~AI_MEDIC; } } else { if (self->monsterinfo.aiflags & AI_BRUTAL) { if (self->enemy->health <= -80) hesDeadJim = true; } else { if (self->enemy->health <= 0) hesDeadJim = true; } } if (hesDeadJim) { self->enemy = NULL; // FIXME: look all around for other targets if (self->oldenemy && self->oldenemy->health > 0) { self->enemy = self->oldenemy; self->oldenemy = NULL; HuntTarget (self); } else { if (self->movetarget) { self->goalentity = self->movetarget; self->monsterinfo.walk (self); } else { // we need the pausetime otherwise the stand code // will just revert to walking with no target and // the monsters will wonder around aimlessly trying // to hunt the world entity self->monsterinfo.pausetime = level.time + 100000000; self->monsterinfo.stand (self); } return true; } } self->show_hostile = level.time + 1000; // wake up other monsters // check knowledge of enemy enemy_vis = G_Visible(self, self->enemy); if (enemy_vis) { self->monsterinfo.search_time = level.time + 5000; VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting); } // look for other coop players here // if (coop && self->monsterinfo.search_time < level.time) // { // if (FindTarget (self)) // return true; // } enemy_infront = G_InFront(self, self->enemy); enemy_range = range(self, self->enemy); VectorSubtract (self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); // JDC self->ideal_yaw = enemy_yaw; if (self->monsterinfo.attack_state == AS_MISSILE) { ai_run_missile (self); return true; } if (self->monsterinfo.attack_state == AS_MELEE) { ai_run_melee (self); return true; } // if enemy is not currently visible, we will never attack if (!enemy_vis) return false; return self->monsterinfo.checkattack (self); }
void medic_NextPatrolPoint (edict_t *self, edict_t *hint) { edict_t *next=NULL; edict_t *e; vec3_t dir; qboolean switch_paths=false; self->monsterinfo.aiflags &= ~AI_MEDIC_PATROL; // if(self->monsterinfo.aiflags & AI_MEDIC) // return; if(self->goalentity == hint) self->goalentity = NULL; if(self->movetarget == hint) self->movetarget = NULL; if (!(self->monsterinfo.aiflags & AI_MEDIC)) { if(medic_FindDeadMonster(self)) return; } if(self->monsterinfo.pathdir == 1) { if(hint->hint_chain) next = hint->hint_chain; else { self->monsterinfo.pathdir = -1; switch_paths = true; } } if(self->monsterinfo.pathdir == -1) { e = hint_chain_starts[hint->hint_chain_id]; while(e) { if(e->hint_chain == hint) { next = e; break; } e = e->hint_chain; } } if(!next) { self->monsterinfo.pathdir = 1; next = hint->hint_chain; switch_paths = true; } // If switch_paths is true, we reached the end of a hint_chain. Just for grins, // search for *another* visible hint_path chain and use it if it's reasonably close if(switch_paths && hint_chain_count > 1) { edict_t *e; edict_t *alternate=NULL; float dist; vec3_t dir; int i; float bestdistance=512; for(i=game.maxclients+1; i<globals.num_edicts; i++) { e = &g_edicts[i]; if(!e->inuse) continue; if(Q_strcasecmp(e->classname,"hint_path")) continue; if(next && (e->hint_chain_id == next->hint_chain_id)) continue; if(!visible(self,e)) continue; if(!canReach(self,e)) continue; VectorSubtract(e->s.origin,self->s.origin,dir); dist = VectorLength(dir); if(dist < bestdistance) { alternate = e; bestdistance = dist; } } if(alternate) next = alternate; } if(next) { self->hint_chain_id = next->hint_chain_id; VectorSubtract(next->s.origin, self->s.origin, dir); self->ideal_yaw = vectoyaw(dir); self->goalentity = self->movetarget = next; self->monsterinfo.pausetime = 0; self->monsterinfo.aiflags |= AI_MEDIC_PATROL; self->monsterinfo.aiflags &= ~(AI_SOUND_TARGET | AI_PURSUIT_LAST_SEEN | AI_PURSUE_NEXT | AI_PURSUE_TEMP); // run for it self->monsterinfo.run (self); } else { self->monsterinfo.pausetime = level.time + 100000000; self->monsterinfo.stand (self); } }
qboolean Makron_CheckAttack (edict_t *self) { vec3_t temp; float chance; //qboolean enemy_infront; int enemy_range; float enemy_yaw; if (!self->enemy) return false; /* if (self->enemy->health > 0) { // see if any entities are in the way of the shot VectorCopy (self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy (self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA); // do we have a clear shot? if (tr.ent != self->enemy) return false; } */ //enemy_infront = infront(self, self->enemy); enemy_range = range(self, self->enemy); VectorSubtract (self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); self->ideal_yaw = enemy_yaw; // melee attack if (enemy_range == RANGE_MELEE) { if (self->monsterinfo.melee) self->monsterinfo.attack_state = AS_MELEE; else self->monsterinfo.attack_state = AS_MISSILE; return true; } // missile attack if (!self->monsterinfo.attack) return false; if (level.time < self->monsterinfo.attack_finished) return false; // if (enemy_range == RANGE_FAR) // return false; if (self->monsterinfo.aiflags & AI_STAND_GROUND) { chance = 0.6; } else if (enemy_range == RANGE_MELEE) { chance = 0.85; } else if (enemy_range == RANGE_NEAR) { chance = 0.6; } else if (enemy_range == RANGE_MID) { chance = 0.4; } else { chance = 0.2; } if (random () < chance) { self->monsterinfo.attack_state = AS_MISSILE; self->monsterinfo.attack_finished = level.time + 0.3*random(); return true; } if (self->flags & FL_FLY) { if (random() < 0.3) self->monsterinfo.attack_state = AS_SLIDING; else self->monsterinfo.attack_state = AS_STRAIGHT; } return false; }
void medic_idle (edict_t *self) { if(!(self->spawnflags & SF_MONSTER_AMBUSH)) gi.sound (self, CHAN_VOICE, sound_idle1, 1, ATTN_IDLE, 0); if(self->monsterinfo.aiflags & AI_MEDIC) { // Then we must have reached this point after losing sight // of our patient. abortHeal(self,false); } if(medic_FindDeadMonster(self)) return; // If the map has hint_paths, AND the medic isn't at a HOLD point_combat, // AND the medic has previously called FoundTarget (trail_time set to // level.time), then look for hint_path chain and follow it, hopefully // to find monsters to resurrect if(self->monsterinfo.aiflags & AI_HINT_TEST) return; if (hint_chains_exist && !(self->monsterinfo.aiflags & AI_STAND_GROUND) && ((self->monsterinfo.trail_time > 0) || medic_test) ) { edict_t *e; edict_t *hint=NULL; float dist; vec3_t dir; int i; float bestdistance=99999; for(i=game.maxclients+1; i<globals.num_edicts; i++) { e = &g_edicts[i]; if(!e->inuse) continue; if(Q_strcasecmp(e->classname,"hint_path")) continue; if(!visible(self,e)) continue; if(!canReach(self,e)) continue; VectorSubtract(e->s.origin,self->s.origin,dir); dist = VectorLength(dir); if(dist < bestdistance) { hint = e; bestdistance = dist; } } if(hint) { self->hint_chain_id = hint->hint_chain_id; if(!self->monsterinfo.pathdir) self->monsterinfo.pathdir = 1; VectorSubtract(hint->s.origin, self->s.origin, dir); self->ideal_yaw = vectoyaw(dir); self->goalentity = self->movetarget = hint; self->monsterinfo.pausetime = 0; self->monsterinfo.aiflags |= AI_MEDIC_PATROL; self->monsterinfo.aiflags &= ~(AI_SOUND_TARGET | AI_PURSUIT_LAST_SEEN | AI_PURSUE_NEXT | AI_PURSUE_TEMP); // run for it self->monsterinfo.run (self); } } }
int HintTestStart(edict_t *self) { edict_t *e; edict_t *hint = NULL; float dist; vec3_t dir; int i; float bestdistance = 99999; if (!hint_chains_exist) { return 0; } for (i = game.maxclients + 1; i < globals.num_edicts; i++) { e = &g_edicts[i]; if (!e->inuse) { continue; } if (Q_strcasecmp(e->classname, "hint_path")) { continue; } if (!visible(self, e)) { continue; } if (!canReach(self, e)) { continue; } VectorSubtract(e->s.origin, self->s.origin, dir); dist = VectorLength(dir); if (dist < bestdistance) { hint = e; bestdistance = dist; } } if (hint) { self->hint_chain_id = hint->hint_chain_id; if (!self->monsterinfo.pathdir) { self->monsterinfo.pathdir = 1; } VectorSubtract(hint->s.origin, self->s.origin, dir); self->ideal_yaw = vectoyaw(dir); self->enemy = self->oldenemy = NULL; self->goalentity = self->movetarget = hint; self->monsterinfo.pausetime = 0; self->monsterinfo.aiflags = AI_HINT_TEST; // run for it self->monsterinfo.run(self); return 1; } else { return -1; } }
/* ------------------------- NAVNEW_SidestepBlocker ------------------------- */ qboolean NAVNEW_SidestepBlocker( gentity_t *self, gentity_t *blocker, vec3_t blocked_dir, float blocked_dist, vec3_t movedir, vec3_t right ) {//trace to sides of blocker and see if either is clear trace_t tr; vec3_t avoidAngles; vec3_t avoidRight_dir, avoidLeft_dir, block_pos, mins; float rightSucc, leftSucc, yaw, avoidRadius, arcAngle; VectorCopy( self->r.mins, mins ); mins[2] += STEPSIZE; //Get the blocked direction yaw = vectoyaw( blocked_dir ); //Get the avoid radius avoidRadius = sqrt( ( blocker->r.maxs[0] * blocker->r.maxs[0] ) + ( blocker->r.maxs[1] * blocker->r.maxs[1] ) ) + sqrt( ( self->r.maxs[0] * self->r.maxs[0] ) + ( self->r.maxs[1] * self->r.maxs[1] ) ); //See if we're inside our avoidance radius arcAngle = ( blocked_dist <= avoidRadius ) ? 135 : ( ( avoidRadius / blocked_dist ) * 90 ); /* float dot = DotProduct( blocked_dir, right ); //Go right on the first try if that works better if ( dot < 0.0f ) arcAngle *= -1; */ VectorClear( avoidAngles ); //need to stop it from ping-ponging, so we have a bit of a debounce time on which side you try if ( self->NPC->sideStepHoldTime > level.time ) { if ( self->NPC->lastSideStepSide == -1 )//left { arcAngle *= -1; }//else right avoidAngles[YAW] = AngleNormalize360( yaw + arcAngle ); AngleVectors( avoidAngles, movedir, NULL, NULL ); VectorMA( self->r.currentOrigin, blocked_dist, movedir, block_pos ); trap->Trace( &tr, self->r.currentOrigin, mins, self->r.maxs, block_pos, self->s.number, self->clipmask|CONTENTS_BOTCLIP, qfalse, 0, 0 ); return (tr.fraction==1.0&&!tr.allsolid&&!tr.startsolid); } //test right avoidAngles[YAW] = AngleNormalize360( yaw + arcAngle ); AngleVectors( avoidAngles, avoidRight_dir, NULL, NULL ); VectorMA( self->r.currentOrigin, blocked_dist, avoidRight_dir, block_pos ); trap->Trace( &tr, self->r.currentOrigin, mins, self->r.maxs, block_pos, self->s.number, self->clipmask|CONTENTS_BOTCLIP, qfalse, 0, 0 ); if ( !tr.allsolid && !tr.startsolid ) { if ( tr.fraction >= 1.0f ) {//all clear, go for it (favor the right if both are equal) VectorCopy( avoidRight_dir, movedir ); self->NPC->lastSideStepSide = 1; self->NPC->sideStepHoldTime = level.time + 2000; return qtrue; } rightSucc = tr.fraction; } else { rightSucc = 0.0f; } //now test left arcAngle *= -1; avoidAngles[YAW] = AngleNormalize360( yaw + arcAngle ); AngleVectors( avoidAngles, avoidLeft_dir, NULL, NULL ); VectorMA( self->r.currentOrigin, blocked_dist, avoidLeft_dir, block_pos ); trap->Trace( &tr, self->r.currentOrigin, mins, self->r.maxs, block_pos, self->s.number, self->clipmask|CONTENTS_BOTCLIP, qfalse, 0, 0 ); if ( !tr.allsolid && !tr.startsolid ) { if ( tr.fraction >= 1.0f ) {//all clear, go for it (right side would have already succeeded if as good as this) VectorCopy( avoidLeft_dir, movedir ); self->NPC->lastSideStepSide = -1; self->NPC->sideStepHoldTime = level.time + 2000; return qtrue; } leftSucc = tr.fraction; } else { leftSucc = 0.0f; } if ( leftSucc == 0.0f && rightSucc == 0.0f ) {//both sides failed return qfalse; } if ( rightSucc*blocked_dist >= avoidRadius || leftSucc*blocked_dist >= avoidRadius ) {//the traces hit something, but got a relatively good distance if ( rightSucc >= leftSucc ) {//favor the right, all things being equal VectorCopy( avoidRight_dir, movedir ); self->NPC->lastSideStepSide = 1; self->NPC->sideStepHoldTime = level.time + 2000; } else { VectorCopy( avoidLeft_dir, movedir ); self->NPC->lastSideStepSide = -1; self->NPC->sideStepHoldTime = level.time + 2000; } return qtrue; } //if neither are enough, we probably can't get around him return qfalse; }
qboolean Boss2_CheckAttack (edict_t *self) { vec3_t spot1, spot2; vec3_t temp; float chance; trace_t tr; qboolean enemy_infront; int enemy_range; float enemy_yaw; if (self->enemy->health > 0) { // see if any entities are in the way of the shot VectorCopy (self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy (self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA); // do we have a clear shot? if (tr.ent != self->enemy) { // PGM - we want them to go ahead and shoot at info_notnulls if they can. if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0) //PGM return false; } } enemy_infront = infront(self, self->enemy); enemy_range = range(self, self->enemy); VectorSubtract (self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); self->ideal_yaw = enemy_yaw; // melee attack if (enemy_range == RANGE_MELEE) { if (self->monsterinfo.melee) self->monsterinfo.attack_state = AS_MELEE; else self->monsterinfo.attack_state = AS_MISSILE; return true; } // missile attack if (!self->monsterinfo.attack) return false; if (level.time < self->monsterinfo.attack_finished) return false; if (enemy_range == RANGE_FAR) return false; if (self->monsterinfo.aiflags & AI_STAND_GROUND) { chance = 0.4; } else if (enemy_range == RANGE_MELEE) { chance = 0.8; } else if (enemy_range == RANGE_NEAR) { chance = 0.8; } else if (enemy_range == RANGE_MID) { chance = 0.8; } else { return false; } // PGM - go ahead and shoot every time if it's a info_notnull if ((random () < chance) || (self->enemy->solid == SOLID_NOT)) { self->monsterinfo.attack_state = AS_MISSILE; self->monsterinfo.attack_finished = level.time + 2*random(); return true; } if (self->flags & FL_FLY) { if (random() < 0.3) self->monsterinfo.attack_state = AS_SLIDING; else self->monsterinfo.attack_state = AS_STRAIGHT; } return false; }
void monster_start_go (edict_t *self) { vec3_t v; if (self->health <= 0) return; // check for target to combat_point and change to combattarget if (self->target) { qboolean notcombat; qboolean fixup; edict_t *target; target = NULL; notcombat = false; fixup = false; while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL) { if (strcmp(target->classname, "point_combat") == 0) { self->combattarget = self->target; fixup = true; } else { notcombat = true; } } if (notcombat && self->combattarget) gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin)); if (fixup) self->target = NULL; } // validate combattarget if (self->combattarget) { edict_t *target; target = NULL; while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL) { if (strcmp(target->classname, "point_combat") != 0) { gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n", self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2], self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1], (int)target->s.origin[2]); } } } if (self->target) { self->goalentity = self->movetarget = G_PickTarget(self->target); if (!self->movetarget) { gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin)); self->target = NULL; self->monsterinfo.pausetime = 100000000; self->monsterinfo.stand (self); } else if (strcmp (self->movetarget->classname, "path_corner") == 0) { VectorSubtract (self->goalentity->s.origin, self->s.origin, v); self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v); self->monsterinfo.walk (self); self->target = NULL; } else { self->goalentity = self->movetarget = NULL; self->monsterinfo.pausetime = 100000000; self->monsterinfo.stand (self); } } else { self->monsterinfo.pausetime = 100000000; self->monsterinfo.stand (self); } self->think = monster_think; self->nextthink = level.time + FRAMETIME; }
/* ================== */ #define RANGE_1_METER 39 /* 1 meter in map unit */ void CoopScoreboardMessage(edict_t * ent) { gclient_t *cl; edict_t *cl_ent; char string [1400]; int health , armor; int i , j, x, y; int dist , dir_h, dir_v; vec3_t v; float angle; char szArrow [8]; string[0] = '\0'; x = 160; y = 8; for (i = 0; i < game.maxclients; i++) { cl_ent = g_edicts + 1 + i; if (!cl_ent->inuse || game.clients[i].resp.spectator) continue; cl = &game.clients[i]; health = max(cl_ent->health, 0); health = (min(health, 100) / 5) * 5; /* bh0..5..10.. ..100 */ armor = entArmor(cl_ent); armor = max(armor, 0); armor = (min(armor, 100) / 5) * 5; /* ba0..5..10.. ..100 */ j = strlen(string); if (cl_ent == ent) { /* The player himself */ Com_sprintf(string + j, sizeof(string) - j, "xr -%i yt %i picn /players/%s_i.pcx " /* face */ "xr -%i yt %i string2 \"%s\" " /* name */ "xr -%i yt %i picn bh%i " /* health */ "yt %i picn ba%i " /* armor */ "xr -%i yt %i string2 \"%s\" ", /* ping */ x, y, Info_ValueForKey(cl_ent->client->pers.userinfo, "skin"), x - 34, y, cl_ent->client->pers.netname, x - 35, y + 8, health, y + 13, armor, x - 82, y + 20, va("(%dms)", min(cl->ping, 999))); } else { VectorSubtract(cl_ent->s.origin, ent->s.origin, v); dist = (int)(VectorLength(v) / RANGE_1_METER); /* direction_V : up, down or same height */ if (v[2] > 3 * RANGE_1_METER) dir_v = 1; else if (v[2] < -3 * RANGE_1_METER) dir_v = 2; else dir_v = 0; /* direction_H : left, right, front or back */ if (fabs(v[0]) < RANGE_1_METER && fabs(v[1]) < RANGE_1_METER) dir_h = 0; /* Too near, no direction */ else { angle = vectoyaw(v) - ent->s.angles[YAW]; if (angle < 0) angle += 360; else if (angle >= 360) angle -= 360; if (angle < 45) dir_h = 1; /* front */ else if (angle < 135) dir_h = 2; /* left */ else if (angle < 225) dir_h = 3; /* back */ else if (angle < 315) dir_h = 4; /* right */ else dir_h = 1; /* front */ } Com_sprintf(szArrow, sizeof(szArrow), "v%ih%i", dir_v, dir_h); Com_sprintf(string + j, sizeof(string) - j, "xr -%i yt %i picn /players/%s_i.pcx " /* face */ "xr -%i yt %i string \"%s\" " /* name */ "xr -%i yt %i picn bh%i " /* health */ "yt %i picn ba%i " /* armor */ "yt %i picn %s " /* directional arrow */ "xr -%i string \"%im (%dms)\" ", /* distance + ping */ x, y, Info_ValueForKey(cl_ent->client->pers.userinfo, "skin"), x - 34, y, cl_ent->client->pers.netname, x - 35, y + 8, health, y + 13, armor, y + 20, szArrow, x - 62, dist, min(cl->ping, 999)); } x += 160; } if (string[0]) { gi.WriteByte(svc_layout); gi.WriteString(string); } }