📄 g_phys.pas
字号:
if (SV_TestEntityPosition(check) = nil) then
Continue;
end;
if ((pusher^.movetype = MOVETYPE_PUSH) OR (check^.groundentity = pusher)) then
begin
// move this entity
_pushed_p^.ent := check;
VectorCopy(check^.s.origin, _pushed_p^.origin);
VectorCopy(check^.s.angles, _pushed_p^.angles);
Inc(_pushed_p);
// try moving the contacted entity
VectorAdd(check^.s.origin, move_, check^.s.origin);
if (check^.client <> nil) then
begin // FIXME: doesn't rotate monsters?
check^.client^.ps.pmove.delta_angles[YAW] := check^.client^.ps.pmove.delta_angles[YAW] + trunc(amove[YAW]);
end;
// figure movement due to the pusher's amove
VectorSubtract(check^.s.origin, pusher^.s.origin, org);
org2[0] := DotProduct(org, forward_);
org2[1] := -DotProduct(org, right);
org2[2] := DotProduct(org, up);
VectorSubtract(org2, org, move2);
VectorAdd(check^.s.origin, move2, check^.s.origin);
// may have pushed them off an edge
if (check^.groundentity <> pusher) then
check^.groundentity := nil;
block := SV_TestEntityPosition(check);
if (block = nil) then
begin // pushed ok
gi.linkentity(check);
// impact?
Continue;
end;
// if it is ok to leave in the old position, do it
// this is only relevent for riding entities, not pushed
// FIXME: this doesn't acount for rotation
VectorSubtract(check^.s.origin, move_, check^.s.origin);
block := SV_TestEntityPosition(check);
if (block = nil) then
begin
Dec(_pushed_p);
Continue;
end;
end;
// save off the obstacle so we can call the block function
obstacle := check;
// move back any entities we already moved
// go backwards, so if the same entity was pushed
// twice, it goes back to the original position
p := Pointer(Cardinal(_pushed_p) - 1 * SizeOf(pushed_t));
while (Cardinal(p) >= Cardinal(@pushed)) do
begin
VectorCopy(p^.origin, p^.ent^.s.origin);
VectorCopy(p^.angles, p^.ent^.s.angles);
if (p^.ent^.client <> nil) then
begin
p^.ent^.client^.ps.pmove.delta_angles[YAW] := trunc(p^.deltayaw);
end;
gi.linkentity(p^.ent);
p := Pointer(Cardinal(p) - 1 * SizeOf(pushed_t));
end;
Result := False;
Exit;
finally
{ All Expression Two items get evaluated at the End of any loop }
Inc(check);
{ Inc(e) <-- Shouldnt' be needed as should already be incremented }
end;
end;
//FIXME: is there a better way to handle this?
// see if anything we moved has touched a trigger
p := Pointer(Cardinal(_pushed_p) - 1 * SizeOf(pushed_t));
while (Cardinal(p) >= Cardinal(@pushed)) do
begin
G_TouchTriggers(p^.ent);
p := Pointer(Cardinal(p) - 1 * SizeOf(pushed_t));
end;
Result := True;
end;
{/*
================
SV_Physics_Pusher
Bmodel objects don't interact with each other, but
push all box objects
================
*/}
procedure SV_Physics_Pusher(ent: edict_p);
var
move_, amove: vec3_t;
part, mv: edict_p;
begin
// if not a team captain, so movement will be handled elsewhere
if (ent^.flags AND FL_TEAMSLAVE) <> 0 then
Exit;
// make sure all team slaves can move before commiting
// any moves or calling any think functions
// if the move is blocked, all moved objects will be backed out
//retry:
_pushed_p := @pushed[0];
{ ORIGINAL LINE: for (part = ent ; part ; part=part->teamchain) }
{ NOTE(SP): While (part <> nil) might be better - but logic similar in either case.
- Whilst originally implemented with a repeat until and using a try..finally block
to ensure the next pointer assignment occered, this also inadvertently moved the
pointer forwards, even in the case of the call to Brea. As such, changed the
approach to use a while loop, and removed the try..finally for performance. }
part := ent;
while (part <> nil) do
begin
if (part^.velocity[0] <> 0) OR (part^.velocity[1] <> 0) OR (part^.velocity[2] <> 0) OR
(part^.avelocity[0] <> 0) OR (part^.avelocity[1] <> 0) OR (part^.avelocity[2] <> 0) then
begin // object is moving
VectorScale(part^.velocity, FRAMETIME, move_);
VectorScale(part^.avelocity, FRAMETIME, amove);
if (NOT SV_Push(part, move_, amove)) then
Break; // move was blocked
end;
part := part^.teamchain;
end;
if (Cardinal(_pushed_p) > Cardinal(@pushed[MAX_EDICTS - 1])) then //was pushed[MAX_EDICTS]
gi.error({ERR_FATAL,} '_pushed_p > @pushed[MAX_EDICTS], memory corrupted');
if (part <> nil) then
begin
// the move failed, bump all nextthink times and back out moves
mv := ent;
while (mv <> nil) do
begin
if (mv^.nextthink > 0) then
mv^.nextthink := mv^.nextthink + FRAMETIME;
mv := mv^.teamchain;
end;
// if the pusher has a "blocked" function, call it
// otherwise, just stay in place until the obstacle is gone
if (@part^.blocked <> nil) then
part^.blocked(part, obstacle);
(*{$ifdef 0}
// if the pushed entity went away and the pusher is still there
if (NOT obstacle^.inuse) AND part^.inuse) then
goto retry;
{$endif}*)
end
else
begin
// the move succeeded, so call all think functions
part := ent;
while (part <> nil) do
begin
SV_RunThink(part);
part := part^.teamchain
end;
end;
end;
//==================================================================
{/*
=============
SV_Physics_None
Non moving objects can only think
=============
*/}
procedure SV_Physics_None(ent: edict_p);
begin
// regular thinking
SV_RunThink(ent);
end;
{/*
=============
SV_Physics_Noclip
A moving object that doesn't obey physics
=============
*/}
procedure SV_Physics_Noclip(ent: edict_p);
begin
// regular thinking
if not SV_RunThink(ent) then
Exit;
VectorMA(ent^.s.angles, FRAMETIME, ent^.avelocity, ent^.s.angles);
VectorMA(ent^.s.origin, FRAMETIME, ent^.velocity, ent^.s.origin);
gi.linkentity(ent);
end;
{/*
==============================================================================
TOSS / BOUNCE
==============================================================================
*/}
{/*
=============
SV_Physics_Toss
Toss, bounce, and fly movement. When onground, do nothing.
=============
*/}
procedure SV_Physics_Toss(ent: edict_p);
var
trace: trace_t;
move_: vec3_t;
backoff: single;
slave: edict_p;
wasinwater, isinwater: qboolean;
old_origin: vec3_t;
begin
// regular thinking
SV_RunThink(ent);
// if not a team captain, so movement will be handled elsewhere
if (ent^.flags and FL_TEAMSLAVE) <> 0 then
Exit;
if (ent^.velocity[2] > 0) then
ent^.groundentity := nil;
// check for the groundentity going away
if (ent^.groundentity <> nil) then
if not ent^.groundentity^.inuse then
ent^.groundentity := nil;
// if onground, return without moving
if (ent^.groundentity <> nil) then
Exit;
VectorCopy(ent^.s.origin, old_origin);
SV_CheckVelocity(ent);
// add gravity
if (ent^.movetype <> MOVETYPE_FLY) and (ent^.movetype <> MOVETYPE_FLYMISSILE) then
SV_AddGravity(ent);
// move angles
VectorMA(ent^.s.angles, FRAMETIME, ent^.avelocity, ent^.s.angles);
// move origin
VectorScale(ent^.velocity, FRAMETIME, move_);
trace := SV_PushEntity(ent, move_);
if not ent^.inuse then
Exit;
if (trace.fraction < 1) then
begin
if (ent^.movetype = MOVETYPE_BOUNCE) then
backoff := 1.5
else
backoff := 1;
ClipVelocity(ent^.velocity, trace.plane.normal, ent^.velocity, backoff);
// stop if on ground
if (trace.plane.normal[2] > 0.7) then
begin
if (ent^.velocity[2] < 60) or (ent^.movetype <> MOVETYPE_BOUNCE) then
begin
ent^.groundentity := trace.ent;
ent^.groundentity_linkcount := edict_p(trace.ent)^.linkcount;
VectorCopy(vec3_origin, ent^.velocity);
VectorCopy(vec3_origin, ent^.avelocity);
end;
end;
{ The following was originally commented out, but converted still :) }
//if (ent^.touch <> nil) then
// ent^.touch(ent, trace.ent, @trace.plane, trace.surface);
end;
//check for water transition
wasinwater := (ent^.watertype and MASK_WATER) <> 0;
ent^.watertype := gi.pointcontents(ent^.s.origin);
isinwater := ent^.watertype and MASK_WATER <> 0;
if (isinwater) then
ent^.waterlevel := 1
else
ent^.waterlevel := 0;
if (not wasinwater) and isinwater then
gi.positioned_sound(@old_origin, @g_edicts[0], CHAN_AUTO, gi.soundindex('misc/h2ohit1.wav'), 1, 1, 0)
else if wasinwater and (not isinwater) then
gi.positioned_sound(@ent^.s.origin, @g_edicts[0], CHAN_AUTO, gi.soundindex('misc/h2ohit1.wav'), 1, 1, 0);
//move teamslaves
{for (slave = ent->teamchain; slave; slave = slave->teamchain)}
{ Or replace with while (slave <> nil) do loop }
slave := ent^.teamchain;
while (slave <> nil) do
begin
VectorCopy(ent^.s.origin, slave^.s.origin);
gi.linkentity(slave);
slave := slave^.teamchain;
end;
end;
{/*
===============================================================================
STEPPING MOVEMENT
===============================================================================
*/}
{/*
=============
SV_Physics_Step
Monsters freefall when they don't have a ground entity, otherwise
all movement is done with discrete steps.
This is also used for objects that have become still on the ground, but
will fall if the floor is pulled out from under them.
FIXME: is this true?
=============
*/}
//FIXME: hacked in for E3 demo
const
sv_stopspeed = 100;
sv_friction = 6;
sv_waterfriction = 1;
procedure SV_AddRotationalFriction(ent: edict_p);
var
n: integer;
adjustment: single;
begin
VectorMA(ent^.s.angles, FRAMETIME, ent^.avelocity, ent^.s.angles);
adjustment := FRAMETIME * sv_stopspeed * sv_friction;
for n := 0 to 2 do
begin
if (ent^.avelocity[n] > 0) then
begin
ent^.avelocity[n] := ent^.avelocity[n] - adjustment;
if (ent^.avelocity[n] < 0) then
ent^.avelocity[n] := 0;
end
else
begin
ent^.avelocity[n] := ent^.avelocity[n] + adjustment;
if (ent^.avelocity[n] > 0) then
ent^.avelocity[n] := 0;
end;
end;
end;
procedure SV_Physics_Step(ent: edict_p);
var
wasonground, hitsound: qboolean;
vel: vec3_p;
speed, newspeed, control, friction: Single;
groundentity: edict_p;
mask: Integer;
begin
hitsound := False;
// airborn monsters should always check for ground
if (ent^.groundentity = nil) then
M_CheckGround(ent);
groundentity := ent^.groundentity;
SV_CheckVelocity(ent);
if (groundentity <> nil) then
wasonground := True
else
wasonground := False;
if (ent^.avelocity[0] <> 0) OR (ent^.avelocity[1] <> 0) OR (ent^.avelocity[2] <> 0) then
SV_AddRotationalFriction(ent);
// add gravity except:
// flying monsters
// swimming monsters who are in the water
if (NOT wasonground) then
if ((ent^.flags AND FL_FLY) = 0) then
if NOT (((ent^.flags AND FL_SWIM) <> 0) AND (ent^.waterlevel > 2)) then
begin
if (ent^.velocity[2] < (sv_gravity^.value * -0.1)) then
hitsound := True;
if (ent^.waterlevel = 0) then
SV_AddGravity(ent);
end;
// friction for flying monsters that have been given vertical velocity
if ((ent^.flags AND FL_FLY) <> 0) AND (ent^.velocity[2] <> 0) then
begin
speed := fabs(ent^.velocity[2]);
if (speed < sv_stopspeed) then
control := sv_stopspeed
else
control := speed;
friction := (sv_friction / 3);
newspeed := speed - (FRAMETIME * control * friction);
if (newspeed < 0) then
newspeed := 0;
newspeed := newspeed / speed;
ent^.velocity[2] := ent^.velocity[2] * newspeed;
end;
// friction for flying monsters that have been given vertical velocity
if ((ent^.flags AND FL_SWIM) <> 0) AND (ent^.velocity[2] <> 0) then
begin
speed := fabs(ent^.velocity[2]);
if (speed < sv_stopspeed) then
control := sv_stopspeed
else
control := speed;
newspeed := speed - (FRAMETIME * control * sv_waterfriction * ent^.waterlevel);
if (newspeed < 0) then
newspeed := 0;
newspeed := newspeed / speed;
ent^.velocity[2] := ent^.velocity[2] * newspeed;
end;
if (ent^.velocity[2] <> 0) OR (ent^.velocity[1] <> 0) OR (ent^.velocity[0] <> 0) then
begin
// apply friction
// let dead monsters who aren't completely onground slide
if (wasonground) OR ((ent^.flags AND (FL_SWIM OR FL_FLY)) <> 0) then
if NOT ((ent^.health <= 0.0) AND (NOT M_CheckBottom(ent))) then
begin
vel := @ent^.velocity;
speed := sqrt(vel[0] * vel[0] + vel[1] * vel[1]);
if (speed <> 0) then
begin
friction := sv_friction;
if (speed < sv_stopspeed) then
control := sv_stopspeed
else
control := speed;
newspeed := speed - FRAMETIME * control * friction;
if (newspeed < 0) then
newspeed := 0;
newspeed := newspeed / speed;
vel[0] := vel[0] * newspeed;
vel[1] := vel[1] * newspeed;
end;
end;
if (ent^.svflags AND SVF_MONSTER) <> 0 then
mask := MASK_MONSTERSOLID
else
mask := MASK_SOLID;
SV_FlyMove(ent, FRAMETIME, mask);
gi.linkentity(ent);
G_TouchTriggers(ent);
if (NOT ent^.inuse) then
Exit;
if (ent^.groundentity <> nil) then
if (NOT wasonground) then
if (hitsound) then
gi.sound(ent, 0, gi.soundindex('world/land.wav'), 1, 1, 0);
end;
// regular thinking
SV_RunThink(ent);
end;
//============================================================================
{/*
================
G_RunEntity
================
*/}
procedure G_RunEntity(ent: edict_p);
begin
if (@ent^.prethink <> nil) then
ent^.prethink(ent);
case Integer(ent^.movetype) of
Ord(MOVETYPE_PUSH),
Ord(MOVETYPE_STOP):
begin
SV_Physics_Pusher(ent);
//break;
end;
Ord(MOVETYPE_NONE):
begin
SV_Physics_None(ent);
//break;
end;
Ord(MOVETYPE_NOCLIP):
begin
SV_Physics_Noclip(ent);
//break;
end;
Ord(MOVETYPE_STEP):
begin
SV_Physics_Step(ent);
//break;
end;
Ord(MOVETYPE_TOSS),
Ord(MOVETYPE_BOUNCE),
Ord(MOVETYPE_FLY),
Ord(MOVETYPE_FLYMISSILE):
begin
SV_Physics_Toss(ent);
//break;
end;
else
gi.error('SV_Physics: bad movetype %i', Integer(ent^.movetype));
end;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -