📄 g_ai.pas
字号:
{----------------------------------------------------------------------------}
{ }
{ File(s): g_ai.c }
{ Content: }
{ }
{ Initial conversion by : Scott Price }
{ Initial conversion on : 09-Jan-2002 }
{ }
{ This File contains part of convertion of Quake2 source to ObjectPascal. }
{ More information about this project can be found at: }
{ http://www.sulaco.co.za/quake2/ }
{ }
{ Copyright (C) 1997-2001 Id Software, Inc. }
{ }
{ This program is free software; you can redistribute it and/or }
{ modify it under the terms of the GNU General Public License }
{ as published by the Free Software Foundation; either version 2 }
{ of the License, or (at your option) any later version. }
{ }
{ This program is distributed in the hope that it will be useful, }
{ but WITHOUT ANY WARRANTY; without even the implied warranty of }
{ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. }
{ }
{ See the GNU General Public License for more details. }
{ }
{ NOTE!! This is same file than in CTF folder with same name }
{----------------------------------------------------------------------------}
unit g_ai;
interface
uses
g_local;
function FindTarget(self: Pedict_t): qboolean;
//extern cvar_t *maxclients;
function ai_checkattack(self: Pedict_t; dist: Single): qboolean;
var
enemy_vis: qboolean;
enemy_infront: qboolean;
enemy_range: Integer;
enemy_yaw: Single;
implementation
uses
m_move;
(* =================
AI_SetSightClient
Called once each frame to set level.sight_client to the
player to be checked for in findtarget.
If all clients sare either dead or in notarget, sight_client
will be null.
In coop games, sight_client will cycle between the clients.
================= *)
procedure AI_SetSightClient;
var
ent: Pedict_t;
start, check: Integer;
begin
if (level.sight_client = nil) then
start := 1
else
start := (Cardinal(level.sight_client) - Cardinal(g_edicts)) div sizeof(edict_t);
check := start;
while (True) do begin
Inc(check);
if (check > game.maxclients) then
check := 1;
ent := @g_edicts[check];
{ TODO: Translate the Following Line: }
// ORIGINAL: if (ent->inuse && ent->health > 0 && !(ent->flags & FL_NOTARGET) )
if (ent^.inuse AND (ent^.health > 0) AND ((ent^.flags AND FL_NOTARGET) = 0)) then begin
level.sight_client := ent;
Exit; // got one
end;
if (check = start) then begin
level.sight_client := nil;
Exit; // nobody to see
end;
end;
end;
(* =============
ai_move
Move the specified distance at current facing.
This replaces the QC functions: ai_forward, ai_back, ai_pain, and ai_painforward
============== *)
procedure ai_move(self: Pedict_t; dist: Single);
begin
M_walkmove(self, self^.s.angles[YAW], dist);
end;
(* =============
ai_stand
Used for standing around and looking for players
Distance is for slight position adjustments needed by the animations
============== *)
procedure ai_stand(self: Pedict_t; dist: Single);
var
v: vec3_t;
begin
if (dist <> 0) then
M_walkmove(self, self^.s.angles[YAW], dist);
if ((self^.monsterinfo.aiflags AND AI_STAND_GROUND) <> 0) then begin
if (self^.enemy <> 0) then begin
VectorSubtract(self^.enemy^.s.origin, self^.s.origin, v);
self^.ideal_yaw := vectoyaw(v);
{ TODO: Translate the Line Below: }
// ORIGINAL: if (self->s.angles[YAW] <> self->ideal_yaw && self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) then
if ((self^.s.angles[YAW] <> self^.ideal_yaw) AND ((self^.monsterinfo.aiflags AND AI_TEMP_STAND_GROUND) <> 0)) then
begin
{ TODO: Translate the Line Below: }
// ORIGINAL: self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
self^.monsterinfo.aiflags := self^.monsterinfo.aiflags AND (NOT (AI_STAND_GROUND OR AI_TEMP_STAND_GROUND));
self^.monsterinfo.run(self);
end;
M_ChangeYaw(self);
ai_checkattack(self, 0);
end else
FindTarget(self);
Exit;
end;
if (FindTarget(self)) then
Exit;
if (level.time > self->monsterinfo.pausetime) then begin
self^.monsterinfo.walk(self);
Exit;
end;
{ TODO: Translate the Line Below: }
// ORIGINAL: if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && (level.time > self->monsterinfo.idle_time))
if (((self^.spawnflags AND 1) = 0) AND (self^.monsterinfo.idle) AND (level.time > self^.monsterinfo.idle_time)) then
begin
{ TODO: Translate the Line Below: }
if (self^.monsterinfo.idle_time) then begin
self^.monsterinfo.idle(self);
self^.monsterinfo.idle_time := level.time + 15 + random() * 15;
end else begin
self^.monsterinfo.idle_time := level.time + random() * 15;
end;
end;
end;
(* =============
ai_walk
The monster is walking it's beat
============= *)
procedure ai_walk(self: Pedict_t, dist: Single);
begin
M_MoveToGoal(self, dist);
// check for noticing a player
if FindTarget(self) then
Exit;
{ TODO: Translate the Line Below: }
// ORIGINAL: if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time)) then begin
if ((self^.monsterinfo.search) AND (level.time > self^.monsterinfo.idle_time)) then begin
{ TODO: Translate the Line Below: }
if (self^.monsterinfo.idle_time) begin
self^.monsterinfo.search(self);
self^.monsterinfo.idle_time := level.time + 15 + random() * 15;
end else begin
self^.monsterinfo.idle_time := level.time + random() * 15;
end;
end;
end;
(* =============
ai_charge
Turns towards target and advances
Use this call with a distnace of 0 to replace ai_face
============== *)
procedure ai_charge(self: Pedict_t; dist: Single);
var
v: vec3_t;
begin
VectorSubtract(self^.enemy^.s.origin, self^.s.origin, v);
self^.ideal_yaw := vectoyaw(v);
M_ChangeYaw(self);
if (dist <> 0) then
M_walkmove(self, self^.s.angles[YAW], dist);
end;
(* =============
ai_turn
don't move, but turn towards ideal_yaw
Distance is for slight position adjustments needed by the animations
============= *)
procedure ai_turn(self: Pedict_t; dist: Single);
begin
if (dist <> 0) then
M_walkmove(self, self^s.angles[YAW], dist);
if FindTarget(self) then
Exit;
M_ChangeYaw(self);
end;
(*
.enemy
Will be world if not currently angry at anyone.
.movetarget
The next path spot to walk toward. If .enemy, ignore .movetarget.
When an enemy is killed, the monster will try to return to it's path.
.hunt_time
Set to time + something when the player is in sight, but movement straight for
him is blocked. This causes the monster to use wall following code for
movement direction instead of sighting on the player.
.ideal_yaw
A yaw angle of the intended direction, which will be turned towards at up
to 45 deg / state. If the enemy is in view and hunt_time is not active,
this will be the exact line towards the enemy.
.pausetime
A monster will leave it's stand state and head towards it's .movetarget when
time > .pausetime.
walkmove(angle, speed) primitive is all or nothing *)
(*
=============
range
returns the range catagorization of an entity reletive to self
0 melee range, will become hostile even if back is turned
1 visibility and infront, or visibility and show hostile
2 infront and show hostile
3 only triggered by damage
=============
*)
function range(self: Pedict_t; other: Pedict_t): Integer;
//function range(self, other: Pedict_t): Integer;
var
v: vec3_t;
len: Single;
begin
VectorSubtract(self^.s.origin, other^.s.origin, v);
len := VectorLength(v);
if (len < MELEE_DISTANCE) then begin
Result := RANGE_MELEE;
Exit;
end;
if (len < 500) then begin
Result := RANGE_NEAR;
Exit;
end;
if (len < 1000) then begin
Result := RANGE_MID;
Exit;
end;
Result := RANGE_FAR;
end;
(* =============
visible
returns 1 if the entity is visible to self, even if not infront ()
============= *)
function visible(self: Pedict_t; other: Pedict_t): qboolean;
//function visible(self, other: Pedict_t): qboolean;
var
spot1: vec3_t;
spot2: vec3_t;
trace: trace_t;
begin
VectorCopy(self^.s.origin, spot1);
//spot1[2] += self->viewheight; <-- TODO: Check this Translation
spot1[2] := (spot1[2] + self^.viewheight);
VectorCopy(other^.s.origin, spot2);
//spot2[2] += other->viewheight; <-- TODO: Check this Translation
spot2[2] := (spot2[2] + other^.viewheight);
trace := gi.trace(spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE);
if (trace.fraction = 1.0) then begin
Result := True;
Exit;
end;
Result := False;
end;
(* =============
infront
returns 1 if the entity is in front (in sight) of self
============= *)
function infront(self: Pedict_t; other: Pedict_t): qboolean;
//function infront(self, other: Pedict_t): qboolean;
var
vec: vec3_t;
dot: Single;
forward_: vec3_t;
begin
AngleVectors(self^.s.angles, forward_, NULL, NULL);
VectorSubtract(other^.s.origin, self^.s.origin, vec);
VectorNormalize(vec);
dot := DotProduct(vec, forward_);
if (dot > 0.3) then begin
Result := True;
Exit;
end;
Result := False;
end;
//============================================================================
procedure HuntTarget(self: Pedict_t);
var
vec: vec3_t;
begin
self^.goalentity := self^.enemy;
if ((self^.monsterinfo.aiflags AND AI_STAND_GROUND) <> 0) then
self^.monsterinfo.stand(self);
else
self^.monsterinfo.run(self);
VectorSubtract(self^.enemy^.s.origin, self^.s.origin, vec);
self^.ideal_yaw := vectoyaw(vec);
{ wait a while before first attack }
if ((self^.monsterinfo.aiflags AND AI_STAND_GROUND) = 0) then
AttackFinished(self, 1);
end;
procedure FoundTarget(self: Pedict_t);
begin
{ let other monsters see this monster for a while }
if (self^.enemy^.client) then begin
level.sight_entity := self;
level.sight_entity_framenum := level.framenum;
level.sight_entity^.light_level := 128;
end;
{ wake up other monsters }
self^.show_hostile := level.time + 1;
VectorCopy(self^.enemy^.s.origin, self^.monsterinfo.last_sighting);
self^.monsterinfo.trail_time := level.time;
if (NOT self^.combattarget) then begin
HuntTarget(self);
Exit;
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -