⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 g_ai.pas

📁 雷神之锤2(Quake2)Delphi源码
💻 PAS
📖 第 1 页 / 共 3 页
字号:

  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 + -