📄 g_ai.pas
字号:
self^.movetarget := G_PickTarget(self^.combattarget);
self^.goalentity := self^.movetarget;
if (NOT self^.movetarget) then begin
self^.movetarget := self^.enemy;
self^.goalentity := self^.movetarget;
HuntTarget(self);
gi.dprintf('%s at %s, combattarget %s not found'#10, self^.classname,
vtos(self^.s.origin), self^.combattarget);
Exit;
end;
{ clear out our combattarget, these are a one shot deal }
self^.combattarget := NULL;
{ TODO: Translate the Line Below: }
self^.monsterinfo.aiflags := self^.monsterinfo.aiflags OR AI_COMBAT_POINT;
{ clear the targetname, that point is ours! }
self^.movetarget^.targetname := NULL;
self^.monsterinfo.pausetime := 0;
{ run for it }
self^.monsterinfo.run(self);
end;
(* ===========
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.
============ *)
function FindTarget(self: Pedict_t): qboolean;
var
client: Pedict_t;
heardit: qboolean;
r: Integer;
temp: vec3_t;
begin
if ((self^.monsterinfo.aiflags AND AI_GOOD_GUY) <> 0) then begin
{ TODO: Translate the Line Below: }
if ((self^.goalentity <> Nil) AND self^.goalentity^.inuse AND (self^.goalentity^.classname <> Nil)) then begin
if (StrComp(self^.goalentity^.classname, 'target_actor') = 0) then
Result := False;
end;
//FIXME look for monsters?
Result := False;
end;
{ if we're going to a combat point, just proceed }
if (self^.monsterinfo.aiflags AND AI_COMBAT_POINT) then
Result := 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;
{ TODO: Translate the Line Below: }
// ORIGINAL: if ((level.sight_entity_framenum >= (level.framenum - 1)) && !(self^.spawnflags AND 1) ) then begin
if ((level.sight_entity_framenum >= (level.framenum - 1)) AND ((self^.spawnflags AND 1) = 0)) then begin
client := level.sight_entity;
if (client^.enemy = self^.enemy) then begin
Result := False;
Exit;
end;
end else if (level.sound_entity_framenum >= (level.framenum - 1)) then begin
client := level.sound_entity;
heardit := True;
end
{ TODO: Translate the Line Below: }
// ORIGINAL: else if (!(self->enemy) && (level.sound2_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) ) then begin
else if ((self^.enemy = Nil) AND (level.sound2_entity_framenum >= (level.framenum - 1)) AND ((self^.spawnflags AND 1) = 0)) then begin
client := level.sound2_entity;
heardit := True;
end else begin
client := level.sight_client;
if (NOT client) then begin
{ no clients to get mad at }
Result := False;
Exit;
end;
end;
{ if the entity went away, forget it }
if (NOT client^.inuse) then begin
Result := False;
Exit;
end;
if (client = self^.enemy) then begin
Result := True; // JDC false;
Exit;
end;
if (client^.client) then begin
if ((client^.flags AND FL_NOTARGET) <> 0) then begin
Result := False;
Exit;
end;
end else if ((client^.svflags AND SVF_MONSTER) <> 0) then begin
if (NOT client^.enemy) then begin
Result := False;
Exit;
end;
if ((client^.enemy^.flags AND FL_NOTARGET) <> 0) then begin
Result := False;
Exit;
end;
end else if (heardit) then begin
if ((client^.owner^.flags AND FL_NOTARGET) <> 0) then begin
Result := False;
Exit;
end;
end else begin
Result := False;
Exit;
end;
if (NOT heardit) then begin
r := range(self, client);
if (r = RANGE_FAR) then begin
Result := False;
Exit;
end;
{ this is where we would check invisibility }
{ is client in an spot too dark to be seen? }
if (client^.light_level <= 5) then begin
Result := False;
Exit;
end;
if (NOT visible(self, client)) then begin
Result := False;
Exit;
end;
if (r = RANGE_NEAR) then begin
if (client^.show_hostile < level.time && !infront(self, client)) then begin
Result := False;
Exit;
end;
end else if (r == RANGE_MID) then begin
if (NOT infront(self, client)) then begin
Result := False;
Exit;
end;
end;
self^.enemy := client;
if (StrComp(self^.enemy^.classname, 'player_noise') <> 0) then begin
{ TODO: Translate the Line Below: }
self^.monsterinfo.aiflags := self^.monsterinfo.aiflags AND (NOT AI_SOUND_TARGET);
if (NOT self^.enemy^.client) then begin
self^.enemy := self^.enemy^.enemy;
if (NOT self^.enemy^.client) then begin
self^.enemy := NULL;
Result := False;
Exit;
end;
end;
end;
end
else // heardit
begin
if ((self^.spawnflags AND 1) <> 0) then begin
if (NOT visible (self, client)) then begin
Result := False;
Exit;
end;
end else begin
if (NOT gi.inPHS(self^.s.origin, client^.s.origin)) then begin
Result := False;
Exit;
end;
end;
VectorSubtract(client^.s.origin, self^.s.origin, temp);
if (VectorLength(temp) > 1000) then begin
{ too far to hear }
Result := False;
Exit;
end;
{ check area portals - if they are different and not connected
then we can't hear it }
if (client^.areanum <> self^.areanum) then
if (NOT gi.AreasConnected(self^.areanum, client^.areanum)) then begin
Result := False;
Exit;
end;
self^.ideal_yaw := vectoyaw(temp);
M_ChangeYaw(self);
{ hunt the sound for a bit; hopefully find the real player }
{ TODO: Translate the Line Below: }
self^.monsterinfo.aiflags := self^.monsterinfo.aiflags OR AI_SOUND_TARGET;
self^.enemy := client;
end;
//
// got one
//
FoundTarget(self);
{ TODO: Translate the Line Below: }
// ORIGINAL: if (NOT ((self^.monsterinfo.aiflags AND AI_SOUND_TARGET) <> 0) && (self^.monsterinfo.sight)) then begin
if (((self^.monsterinfo.aiflags AND AI_SOUND_TARGET) = 0) AND (self^.monsterinfo.sight <> Nil)) then
self^.monsterinfo.sight(self, self^.enemy);
Result := True;
end;
(* ============
FacingIdeal
============ *)
function FacingIdeal(self: Pedict_t): qboolean;
var
delta: Single;
begin
delta := anglemod(self^.s.angles[YAW] - self^.ideal_yaw);
{ TODO: Translate the Line Below: }
if ((delta > 45) AND (delta < 315)) then begin
Result := False;
Exit;
end;
Result := True;
end;
function M_CheckAttack(self: Pedict_t): qboolean;
var
spot1, spot2: vec3_t;
chance: Single;
tr: trace_t;
begin
if (self^.enemy^.health > 0) then begin
{ see if any entities are in the way of the shot }
VectorCopy(self^.s.origin, spot1);
//spot1[2] += self->viewheight;
spot1[2] := spot1[2] + self^.viewheight;
VectorCopy(self^.enemy^.s.origin, spot2);
//spot2[2] += self->enemy->viewheight;
spot2[2] := spot2[2] + self^.enemy^.viewheight;
tr := gi.trace(spot1, NULL, NULL, spot2, self, CONTENTS_SOLID OR
CONTENTS_MONSTER OR CONTENTS_SLIME OR CONTENTS_LAVA OR CONTENTS_WINDOW);
{ do we have a clear shot? }
if (tr.ent <> self^.enemy) then begin
Result := False;
Exit;
end;
end;
{ melee attack }
if (enemy_range = RANGE_MELEE) then begin
{ don't always melee in easy mode }
{ TODO: Translate the Line Below: }
if ((skill^.value = 0) AND ((rand() AND 3) <> 0)) then begin
Result := False;
Exit;
end;
if (self^.monsterinfo.melee) then
self^.monsterinfo.attack_state := AS_MELEE
else
self^.monsterinfo.attack_state := AS_MISSILE;
Result := True;
Exit;
end;
{ missile attack }
if (NOT self^.monsterinfo.attack) then begin
Result := False;
Exit;
end;
if (level.time < self^.monsterinfo.attack_finished) then begin
Result := False;
Exit;
end;
if (enemy_range = RANGE_FAR) then begin
Result := False;
Exit;
end;
if ((self^.monsterinfo.aiflags AND AI_STAND_GROUND) <> 0) then
chance := 0.4
else if (enemy_range = RANGE_MELEE) then
chance = 0.2
else if (enemy_range = RANGE_NEAR) then
chance = 0.1
else if (enemy_range = RANGE_MID) then
chance = 0.02
else begin
Result := False;
Exit;
end;
if (skill^.value = 0) then
{ TODO: Translate the Line Below: }
chance := (chance * 0.5)
else if (skill^.value >= 2) then
{ TODO: Translate the Line Below: }
chance := (chance * 2);
if (random() < chance) then begin
self^.monsterinfo.attack_state := AS_MISSILE;
self^.monsterinfo.attack_finished := level.time + (2 * random());
Result := True;
Exit;
end;
if (self^.flags AND FL_FLY) then begin
if (random() < 0.3) then
self^.monsterinfo.attack_state := AS_SLIDING
else
self^.monsterinfo.attack_state := AS_STRAIGHT;
end;
Result := False;
end;
(* =============
ai_run_melee
Turn and close until within an angle to launch a melee attack
============= *)
procedure ai_run_melee(self: Pedict_t);
begin
self^.ideal_yaw := enemy_yaw;
M_ChangeYaw(self);
if (FacingIdeal(self)) then begin
self^.monsterinfo.melee(self);
self^.monsterinfo.attack_state := AS_STRAIGHT;
end;
end;
(* =============
ai_run_missile
Turn in place until within an angle to launch a missile attack
============= *)
procedure ai_run_missile(self: Pedict_t);
begin
self^.ideal_yaw := enemy_yaw;
M_ChangeYaw(self);
if (FacingIdeal(self)) then begin
self^.monsterinfo.attack(self);
self^.monsterinfo.attack_state := AS_STRAIGHT;
end;
end;
(* =============
ai_run_slide
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -