📄 g_phys.pas
字号:
{----------------------------------------------------------------------------}
{ }
{ File(s): g_phys.c }
{ Content: physics logic }
{ }
{ Initial conversion by : MathD (matheus@tilt.net }
{ Initial conversion on : 05-Feb-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. }
{ }
{----------------------------------------------------------------------------}
{ Updated on : 26-Jul-2002 }
{ Updated by : Scott Price }
{ }
{----------------------------------------------------------------------------}
unit g_phys;
interface
uses
g_local,
q_shared;
{pushmove objects do not obey gravity, and do not interact with each other or
trigger fields, but block normal movement and push normal objects when they move.
onground is set for toss objects when they come to a complete rest. it is set
for steping or walking objects
doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
corpses are SOLID_NOT and MOVETYPE_TOSS
crates are SOLID_BBOX and MOVETYPE_TOSS
walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
solid_edge items only clip against bsp models.}
function SV_TestEntityPosition(ent: edict_p): edict_p;
procedure SV_CheckVelocity (ent: edict_p);
function SV_RunThink (ent: edict_p): qboolean;
procedure SV_Impact(e1: edict_p; trace: trace_p);
function ClipVelocity(var in_, normal, out_: vec3_t; overbounce: single): integer;
function SV_FlyMove(ent: edict_p; time: single; mask: integer): integer;
procedure SV_AddGravity(ent: edict_p);
function SV_PushEntity(ent: edict_p; var push: vec3_t): trace_t;
function SV_Push(pusher: edict_p; var move_, amove: vec3_t): qboolean;
procedure SV_Physics_Pusher(ent: edict_p);
procedure SV_Physics_None(ent: edict_p);
procedure SV_Physics_Noclip(ent: edict_p);
procedure SV_Physics_Toss(ent: edict_p);
procedure SV_AddRotationalFriction(ent: edict_p);
procedure SV_Physics_Step(ent: edict_p);
procedure G_RunEntity(ent: edict_p);
implementation
uses g_main, game_add, g_utils, g_local_add, g_monster, m_move, GameUnit;
{/*
============
SV_TestEntityPosition
============
*/}
function SV_TestEntityPosition(ent: edict_p): edict_p;
var
trace: trace_t;
mask: integer;
begin
if ent^.clipMask <> 0 then
mask := ent^.clipMask
else
mask := MASK_SOLID;
trace := gi.trace(@ent^.s.origin, @ent^.mins, @ent^.maxs, @ent^.s.origin, ent, mask);
if (trace.startsolid) then
begin
Result := @g_edicts[0];
exit;
end;
result := nil;
end;
{/*
================
SV_CheckVelocity
================
*/}
procedure SV_CheckVelocity (ent: edict_p);
var
i: integer;
begin
//
// bound velocity
//
for i := 0 to 2 do
begin
if (ent^.velocity[i] > sv_maxvelocity^.value) then
ent^.velocity[i] := sv_maxvelocity^.value
else if (ent^.velocity[i] < (- sv_maxvelocity^.value)) then
ent^.velocity[i] := -sv_maxvelocity^.value;
end;
end;
{/*
=============
SV_RunThink
Runs thinking code for this frame if necessary
=============
*/}
function SV_RunThink (ent: edict_p): qboolean;
var
thinktime: single;
begin
thinktime := ent^.nextthink;
if (thinktime <= 0) then
begin
Result := True;
Exit;
end;
if (thinktime > level.time + 0.001) then
begin
Result := True;
Exit;
end;
ent^.nextthink := 0;
if (@ent^.think = nil) then
gi.error('NIL ent^.think');
ent^.think(ent);
result := false;
end;
{/*
==================
SV_Impact
Two entities have touched, so run their touch functions
==================
*/}
procedure SV_Impact(e1: edict_p; trace: trace_p);
var
e2: edict_p;
//backplane: cplane_p; <-- Commented out in the original
begin
e2 := trace^.ent;
if (@e1^.touch <> nil) and (e1^.solid <> SOLID_NOT) then
e1^.touch(e1, e2, @trace^.plane, trace^.surface);
if (@e2^.touch <> nil) and (e2^.solid <> SOLID_NOT) then
e2^.touch(e2, e1, nil, nil);
end;
{/*
==================
ClipVelocity
Slide off of the impacting object
returns the blocked flags (1 = floor, 2 = step / wall)
==================
*/}
const STOP_EPSILON = 0.1;
function ClipVelocity(var in_, normal, out_: vec3_t; overbounce: single): integer;
var
backoff, change: single;
i, blocked: integer;
begin
blocked := 0;
if (normal[2] > 0) then
blocked := blocked or 1; //floor
if (normal[2] = 0) then
blocked := blocked or 2; //step
backoff := DotProduct(in_, normal) * overbounce;
for i := 0 to 2 do
begin
change := normal[i] * backoff;
out_[i] := in_[i] - change;
if (out_[i] > -STOP_EPSILON) and (out_[i] < STOP_EPSILON) then
out_[i] := 0;
end;
Result := blocked;
end;
{/*
============
SV_FlyMove
The basic solid body movement clip that slides along multiple planes
Returns the clipflags if the velocity was modified (hit something solid)
1 = floor
2 = wall / step
4 = dead stop
============
*/}
const MAX_CLIP_PLANES = 5;
function SV_FlyMove(ent: edict_p; time: single; mask: integer): integer;
var
hit: edict_p;
bumpcount, numbumps: integer;
dir: vec3_t;
d: single;
numplanes: integer;
planes: array[0..MAX_CLIP_PLANES -1] of vec3_t;
primal_velocity, original_velocity, new_velocity: vec3_t;
i, j: integer;
trace: trace_t;
end_: vec3_t;
time_left: single;
blocked: integer;
begin
numbumps := 4;
blocked := 0;
VectorCopy(ent^.velocity, original_velocity);
VectorCopy(ent^.velocity, primal_velocity);
numplanes := 0;
time_left := time;
ent^.groundentity := nil;
for bumpcount := 0 to (numbumps - 1) do
begin
for i := 0 to 2 do
end_[i] := ent^.s.origin[i] + time_left * ent^.velocity[i];
trace := gi.trace(@ent^.s.origin, @ent^.mins, @ent^.maxs, @end_, ent, mask);
if (trace.allsolid) then
begin
//entity is trapped in another solid
VectorCopy(vec3_origin, ent^.velocity);
result := 3;
Exit;
end;
if (trace.fraction > 0) then
begin
//actually covered some distance
VectorCopy(trace.endpos, ent^.s.origin);
VectorCopy(ent^.velocity, original_velocity);
numplanes := 0;
end;
if (trace.fraction = 1) then
Break; //moved the entire distance
hit := trace.ent;
if (trace.plane.normal[2] > 0.7) then
begin
blocked := blocked or 1; //floor;
if (hit^.solid = SOLID_BSP) then
begin
ent^.groundentity := hit;
ent^.groundentity_linkcount := hit^.linkcount;
end;
end;
if trace.plane.normal[2] = 0 then
blocked := blocked or 2; //step
//
// run the impact function
//
SV_Impact(ent, @trace);
if not ent^.inuse then
Break;
time_left := time_left - (time_left * trace.fraction);
//cliped to another plane
if (numplanes >= MAX_CLIP_PLANES) then
begin
//this shouldn't really happen
VectorCopy(vec3_origin, ent^.velocity);
result := 3;
Exit;
end;
VectorCopy(trace.plane.normal, planes[numplanes]);
Inc(numplanes);
//
// modify original_velocity so it parallels all of the clip planes
//
for i := 0 to (numplanes - 1) do
begin
ClipVelocity(original_velocity, planes[i], new_velocity, 1);
for j := 0 to (numplanes - 1) do
begin
if (j <> i) and (VectorCompare(planes[i], planes[j]) = 0) then
begin
if (DotProduct(new_velocity, planes[j]) < 0) then
Break; //not ok
end;
end;
if j = numplanes then
Break;
end;
if (i <> numplanes) then
VectorCopy(new_velocity, ent^.velocity) //go along this plane
else
begin
if (numplanes <> 2) then begin //go along the crease
//gi.dprintf('clip velocity, numplanes = %i #13#10', numplanes);
VectorCopy(vec3_origin, ent^.velocity);
result := 7;
Exit;
end;
CrossProduct(planes[0], planes[1], dir);
d := DotProduct(dir, ent^.velocity);
VectorScale(dir, d, ent^.velocity);
end;
//
// if original velocity is against the original velocity, stop dead
// to avoid tiny occilations in sloping corners
//
if (DotProduct(ent^.velocity, primal_velocity) <= 0) then
begin
VectorCopy(vec3_origin, ent^.velocity);
result := blocked;
exit;
end;
end;
result := blocked;
end;
{/*
============
SV_AddGravity
============
*/}
procedure SV_AddGravity(ent: edict_p);
begin
ent^.velocity[2] := ent^.velocity[2] - (ent^.gravity * sv_gravity^.value * FRAMETIME);
end;
{/*
===============================================================================
PUSHMOVE
===============================================================================
*/`}
{/*
============
SV_PushEntity
Does not change the entities velocity at all
============
*/}
function SV_PushEntity(ent: edict_p; var push: vec3_t): trace_t;
label retry;
var
trace: trace_t;
start, end_: vec3_t;
mask: integer;
begin
VectorCopy(ent^.s.origin, start);
VectorAdd(start, push, end_);
retry:
if (ent^.clipmask <> 0) then
mask := ent^.clipmask
else
mask := MASK_SOLID;
trace := gi.trace(@start, @ent^.mins, @ent^.maxs, @end_, ent, mask);
VectorCopy(trace.endpos, ent^.s.origin);
gi.linkentity(ent);
if (trace.fraction <> 1.0) then
begin
SV_Impact(ent, @trace);
//if the pushed entity went away and the pusher is still there
if (not edict_p(trace.ent)^.inuse) and (ent^.inuse) then
begin
//move the pusher back and try again
VectorCopy(start, ent^.s.origin);
gi.linkentity(ent);
goto retry;
end;
end;
if ent^.inuse then
G_TouchTriggers(ent);
result := trace;
end;
{delphi-note: converted the C pushed_p variable to _pushed_p since}
{pushed_p is the typed pointer of a record}
type
pushed_t = packed record
ent: edict_p;
origin, angles: vec3_t;
deltayaw: single;
end;
pushed_p = ^pushed_t;
var
pushed: array[0..MAX_EDICTS -1] of pushed_t;
_pushed_p: pushed_p;
obstacle: edict_p;
{/*
============
SV_Push
Objects need to be moved back on a failed push,
otherwise riders would continue to slide.
============
*/}
function SV_Push(pusher: edict_p; var move_, amove: vec3_t): qboolean;
var
i, e: Integer;
check, block: edict_p;
mins, maxs: vec3_t;
p: pushed_p;
org, org2, move2, forward_, right, up: vec3_t;
temp: Single;
begin
// clamp the move to 1/8 units, so the position will
// be accurate for client side prediction
for i := 0 to 2 do
begin
temp := move_[i] * 8.0;
if (temp > 0.0) then
temp := temp + 0.5
else
temp := temp - 0.5;
move_[i] := 0.125 * Trunc(temp);
end;
// find the bounding box
for i := 0 to 2 do
begin
mins[i] := pusher^.absmin[i] + move_[i];
maxs[i] := pusher^.absmax[i] + move_[i];
end;
// we need this for pushing things later
VectorSubtract(vec3_origin, amove, org);
AngleVectors(org, @forward_, @right, @up);
// save the pusher's original position
_pushed_p^.ent := pusher;
VectorCopy(pusher^.s.origin, _pushed_p^.origin);
VectorCopy(pusher^.s.angles, _pushed_p^.angles);
if (pusher^.client <> Nil) then
_pushed_p^.deltayaw := pusher^.client^.ps.pmove.delta_angles[YAW];
Inc(_pushed_p);
// move the pusher to it's final position
VectorAdd(pusher^.s.origin, move_, pusher^.s.origin);
VectorAdd(pusher^.s.angles, amove, pusher^.s.angles);
gi.linkentity(pusher);
// see if any solid entities are inside the final position
check := @g_edicts[1];
for e := 1 to (globals.num_edicts - 1) do
begin
try
if (not check^.inuse) then
Continue;
if (check^.movetype = MOVETYPE_PUSH)
OR (check^.movetype = MOVETYPE_STOP)
OR (check^.movetype = MOVETYPE_NONE)
OR (check^.movetype = MOVETYPE_NOCLIP) then
Continue;
if (check^.area.prev = nil) then
Continue; // not linked in anywhere
// if the entity is standing on the pusher, it will definitely be moved
if (check^.groundentity <> pusher) then
begin
// see if the ent needs to be tested
if (check^.absmin[0] >= maxs[0])
OR (check^.absmin[1] >= maxs[1])
OR (check^.absmin[2] >= maxs[2])
OR (check^.absmax[0] <= mins[0])
OR (check^.absmax[1] <= mins[1])
OR (check^.absmax[2] <= mins[2]) then
Continue;
// see if the ent's bbox is inside the pusher's final position
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -