📄 g_func.pas
字号:
{----------------------------------------------------------------------------}
{ }
{ File(s): g_func.pas }
{ Content: Quake2\Game-CTF\ Server commands }
{ }
{ Initial conversion by : you_known - you_known@163.com }
{ Initial conversion on : 03-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: }
{----------------------------------------------------------------------------}
{ * Still dependent (to compile correctly) on: }
{ }
{----------------------------------------------------------------------------}
{ * TODO: }
{ }
{----------------------------------------------------------------------------}
unit g_func;
interface
uses g_local,SysUtils;
type pedict_t = ^edict_t;
pcplane_t = ^cplane_t;
pcsurface_t = ^csurface_t;
pmoveinfo = ^moveinfo;
qboolean = boolean;
(*
=========================================================
PLATS
movement options:
linear
smooth start, hard stop
smooth start, smooth stop
start
end
acceleration
speed
deceleration
begin sound
end sound
target fired when reaching end
wait at end
object characteristics that use move segments
---------------------------------------------
movetype_push, or movetype_stop
action when touched
action when blocked
action when used
disabled?
auto trigger spawning
=========================================================
*)
const PLAT_LOW_TRIGGER = 1;
STATE_TOP = 0;
STATE_BOTTOM = 1;
STATE_UP = 2;
STATE_DOWN = 3;
DOOR_START_OPEN = 1;
DOOR_REVERSE = 2;
DOOR_CRUSHER = 4;
DOOR_NOMONSTER = 8;
DOOR_TOGGLE = 32;
DOOR_X_AXIS = 64;
DOOR_Y_AXIS =128;
procedure Think_AccelMove (ent:pedict_t);
procedure plat_go_down (ent:pedict_t);
procedure door_go_down (self:pedict_t);
procedure train_next (self:pedict_t);
procedure door_secret_move1 (self:pedict_t);
procedure door_secret_move2 (self:pedict_t);
procedure door_secret_move3 (self:pedict_t);
procedure door_secret_move4 (self:pedict_t);
procedure door_secret_move5 (self:pedict_t);
procedure door_secret_move6 (self:pedict_t);
procedure door_secret_done (self:pedict_t);
implementation
//
// Support routines for movement (changes in origin using velocity)
//
procedure Move_Done (ent:pedict_t);
begin
VectorClear (ent^.velocity);
ent^.moveinfo.endfunc (ent);
end;
procedure Move_Final (ent:pedict_t);
begin
if (ent^.moveinfo.remaining_distance = 0) then
begin
Move_Done (ent);
exit;
end;
VectorScale (ent^.moveinfo.dir, ent^.moveinfo.remaining_distance / FRAMETIME, ent^.velocity);
ent^.think := Move_Done;
ent^.nextthink := level.time + FRAMETIME;
end;
procedure Move_Begin (ent:pedict_t);
var
frames:single;
begin
if ((ent^.moveinfo.speed * FRAMETIME) >= ent^.moveinfo.remaining_distance) then
begin
Move_Final (ent);
exit;
end;
VectorScale (ent^.moveinfo.dir, ent^.moveinfo.speed, ent^.velocity);
frames := floor((ent^.moveinfo.remaining_distance / ent^.moveinfo.speed) / FRAMETIME);
ent^.moveinfo.remaining_distance -:= frames * ent^.moveinfo.speed * FRAMETIME;
ent^.nextthink := level.time + (frames * FRAMETIME);
ent^.think := Move_Final;
end;
procedure Move_Calc (ent:pedict_t, vec3_t dest, procedure(*func)(edict_t**));
begin
VectorClear (ent^.velocity);
VectorSubtract (dest, ent^.s.origin, ent^.moveinfo.dir);
ent^.moveinfo.remaining_distance := VectorNormalize (ent^.moveinfo.dir);
ent^.moveinfo.endfunc := func;
if (ent^.moveinfo.speed = ent^.moveinfo.accel andand ent^.moveinfo.speed = ent^.moveinfo.decel) then
begin
if (level.current_entity = (if (ent^.flags and FL_TEAMSLAVE)=1 then ent^.teammaster else ent)) then
begin
Move_Begin (ent);
end
else
begin
ent^.nextthink := level.time + FRAMETIME;
ent^.think := Move_Begin;
end;
end
else
begin
// accelerative
ent^.moveinfo.current_speed := 0;
ent^.think := Think_AccelMove;
ent^.nextthink := level.time + FRAMETIME;
end;
end;
//
// Support routines for angular movement (changes in angle using avelocity)
//
procedure AngleMove_Done (ent:pedict_t);
begin
VectorClear (ent^.avelocity);
ent^.moveinfo.endfunc (ent);
end;
procedure AngleMove_Final (ent:pedict_t);
var
move:vec3_t;
begin
if (ent^.moveinfo.state = STATE_UP) then
VectorSubtract (ent^.moveinfo.end_angles, ent^.s.angles, move)
else
VectorSubtract (ent^.moveinfo.start_angles, ent^.s.angles, move);
if (VectorCompare (move, vec3_origin)) then
begin
AngleMove_Done (ent);
exit;
end;
VectorScale (move, 1.0/FRAMETIME, ent^.avelocity);
ent^.think := AngleMove_Done;
ent^.nextthink := level.time + FRAMETIME;
end;
procedure AngleMove_Begin (ent:pedict_t);
var
destdelta:vec3_t;
len:single;
traveltime:single;
frames:single;
begin
// set destdelta to the vector needed to move
if (ent^.moveinfo.state = STATE_UP) then
VectorSubtract (ent^.moveinfo.end_angles, ent^.s.angles, destdelta)
else
VectorSubtract (ent^.moveinfo.start_angles, ent^.s.angles, destdelta);
// calculate length of vector
len := VectorLength (destdelta);
// divide by speed to get time to reach dest
traveltime := len / ent^.moveinfo.speed;
if (traveltime < FRAMETIME) then
begin
AngleMove_Final (ent);
exit;
end;
frames := floor(traveltime / FRAMETIME);
// scale the destdelta vector by the time spent traveling to get velocity
VectorScale (destdelta, 1.0 / traveltime, ent^.avelocity);
// set nextthink to trigger a think when dest is reached
ent^.nextthink := level.time + frames * FRAMETIME;
ent^.think := AngleMove_Final;
end;
procedure AngleMove_Calc (ent:pedict_t, procedure(*func)(edict_t**));
begin
VectorClear (ent^.avelocity);
ent^.moveinfo.endfunc := func;
if (level.current_entity = (if (ent^.flags and FL_TEAMSLAVE)=1 then ent^.teammaster else ent)) then
begin
AngleMove_Begin (ent);
end
else
begin
ent^.nextthink := level.time + FRAMETIME;
ent^.think := AngleMove_Begin;
end;
end;
(*
==============
Think_AccelMove
The team has completed a frame of movement, so
change the speed for the next frame
==============
*)
const AccelerationDistance(target, rate)=(target * ((target / rate) + 1) / 2);
procedure plat_CalcAcceleratedMove(moveinfo:pmoveinfo_t);
var
accel_dist:single;
decel_dist:single;
f:single;
begin
moveinfo^.move_speed := moveinfo^.speed;
if (moveinfo^.remaining_distance < moveinfo^.accel) then
begin
moveinfo^.current_speed := moveinfo^.remaining_distance;
exit;
end;
accel_dist := AccelerationDistance (moveinfo^.speed, moveinfo^.accel);
decel_dist := AccelerationDistance (moveinfo^.speed, moveinfo^.decel);
if ((moveinfo^.remaining_distance - accel_dist - decel_dist) < 0) then
begin
f := (moveinfo^.accel + moveinfo^.decel) / (moveinfo^.accel * moveinfo^.decel);
moveinfo^.move_speed := (-2 + sqrt(4 - 4 * f * (-2 * moveinfo^.remaining_distance))) / (2 * f);
decel_dist := AccelerationDistance (moveinfo^.move_speed, moveinfo^.decel);
end;
moveinfo^.decel_distance := decel_dist;
end;
procedure plat_Accelerate (moveinfo:pmoveinfo_t);
var
p1_distance:single;
p2_distance:single;
distance:single;
old_speed:single;
p1_speed:single;
begin
// are we decelerating?
if (moveinfo^.remaining_distance <= moveinfo^.decel_distance) then
begin
if (moveinfo^.remaining_distance < moveinfo^.decel_distance) then
begin
if (moveinfo^.next_speed) then
begin
moveinfo^.current_speed := moveinfo^.next_speed;
moveinfo^.next_speed := 0;
exit;
end;
if (moveinfo^.current_speed > moveinfo^.decel) then
moveinfo^.current_speed := moveinfo^.current_speed - moveinfo^.decel;
end;
exit;
end;
// are we at full speed and need to start decelerating during this move?
if (moveinfo^.current_speed = moveinfo^.move_speed) then
if ((moveinfo^.remaining_distance - moveinfo^.current_speed) < moveinfo^.decel_distance) then
begin
p1_distance := moveinfo^.remaining_distance - moveinfo^.decel_distance;
p2_distance := moveinfo^.move_speed * (1.0 - (p1_distance / moveinfo^.move_speed));
distance := p1_distance + p2_distance;
moveinfo^.current_speed := moveinfo^.move_speed;
moveinfo^.next_speed := moveinfo^.move_speed - moveinfo^.decel * (p2_distance / distance);
exit;
end;
// are we accelerating?
if (moveinfo^.current_speed < moveinfo^.speed) then
begin
old_speed := moveinfo^.current_speed;
// figure simple acceleration up to move_speed
moveinfo^.current_speed := moveinfo^.current_speed + moveinfo^.accel;
if (moveinfo^.current_speed > moveinfo^.speed) then
moveinfo^.current_speed := moveinfo^.speed;
// are we accelerating throughout this entire move?
if ((moveinfo^.remaining_distance - moveinfo^.current_speed) >= moveinfo^.decel_distance) then
exit;
// during this move we will accelrate from current_speed to move_speed
// and cross over the decel_distance; figure the average speed for the
// entire move
p1_distance := moveinfo^.remaining_distance - moveinfo^.decel_distance;
p1_speed := (old_speed + moveinfo^.move_speed) / 2.0;
p2_distance := moveinfo^.move_speed * (1.0 - (p1_distance / p1_speed));
distance := p1_distance + p2_distance;
moveinfo^.current_speed := (p1_speed * (p1_distance / distance)) + (moveinfo^.move_speed * (p2_distance / distance));
moveinfo^.next_speed := moveinfo^.move_speed - moveinfo^.decel * (p2_distance / distance);
exit;
end;
// we are at constant velocity (move_speed)
exit;
end;
procedure Think_AccelMove (ent:pedict_t);
begin
ent^.moveinfo.remaining_distance := ent^.moveinfo.remaining_distance - ent^.moveinfo.current_speed;
if (ent^.moveinfo.current_speed = 0) then // starting or blocked
plat_CalcAcceleratedMove(andent^.moveinfo);
plat_Accelerate (andent^.moveinfo);
// will the entire move complete on next frame?
if (ent^.moveinfo.remaining_distance <:= ent^.moveinfo.current_speed) then
begin
Move_Final (ent);
exit;
end;
VectorScale (ent^.moveinfo.dir, ent^.moveinfo.current_speed*10, ent^.velocity);
ent^.nextthink := level.time + FRAMETIME;
ent^.think := Think_AccelMove;
end;
procedure plat_hit_top (ent:pedict_t);
begin
if (not (ent^.flags and FL_TEAMSLAVE)) then
begin
if (ent^.moveinfo.sound_end) then
gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent^.moveinfo.sound_end, 1, ATTN_STATIC, 0);
ent^.s.sound := 0;
end;
ent^.moveinfo.state := STATE_TOP;
ent^.think := plat_go_down;
ent^.nextthink := level.time + 3;
end;
procedure plat_hit_bottom (ent:pedict_t);
begin
if (not (ent^.flags and FL_TEAMSLAVE)) then
begin;
if (ent^.moveinfo.sound_end) then
gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent^.moveinfo.sound_end, 1, ATTN_STATIC, 0);
ent^.s.sound := 0;
end;
ent^.moveinfo.state := STATE_BOTTOM;
end;
procedure plat_go_down (ent:pedict_t);
begin
if (not (ent^.flags and FL_TEAMSLAVE)) then
begin
if (ent^.moveinfo.sound_start) then
gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent^.moveinfo.sound_start, 1, ATTN_STATIC, 0);
ent^.s.sound := ent^.moveinfo.sound_middle;
end;
ent^.moveinfo.state := STATE_DOWN;
Move_Calc (ent, ent^.moveinfo.end_origin, plat_hit_bottom);
end;
procedure plat_go_up (ent:pedict_t);
begin
if (not (ent^.flags and FL_TEAMSLAVE)) then
begin
if (ent^.moveinfo.sound_start) then
gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent^.moveinfo.sound_start, 1, ATTN_STATIC, 0);
ent^.s.sound := ent^.moveinfo.sound_middle;
end;
ent^.moveinfo.state := STATE_UP;
Move_Calc (ent, ent^.moveinfo.start_origin, plat_hit_top);
end;
procedure plat_blocked (self:pedict_t; other:pedict_t);
begin
if (not (other^.svflags and SVF_MONSTER) andand (not other^.client) ) then
begin
// give it a chance to go away on it's own terms (like gibs)
T_Damage (other, self, self, vec3_origin, other^.s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
// if it's still there, nuke it
if (other) then
BecomeExplosion1 (other);
exit;
end;
T_Damage (other, self, self, vec3_origin, other^.s.origin, vec3_origin, self^.dmg, 1, 0, MOD_CRUSH);
if (self^.moveinfo.state = STATE_UP) then
plat_go_down (self)
else if (self^.moveinfo.state = STATE_DOWN) then
plat_go_up (self);
end;
procedure Use_Plat (ent:pedict_t; other:pedict_t;activator:pedict_t);
begin
if (ent^.think) then
exit; // already down
plat_go_down (ent);
end;
procedure Touch_Plat_Center (ent:pedict_t; other:pedict_t; plane:pcplane_t; surf:pcsurface_t);
begin
if (not other^.client) then
exit;
if (other^.health <= 0) then
exit;
ent := ent^.enemy; // now point at the plat, not the trigger
if (ent^.moveinfo.state = STATE_BOTTOM) then
plat_go_up (ent)
else if (ent^.moveinfo.state :=:= STATE_TOP) then
ent^.nextthink := level.time + 1; // the player is still on the plat, so delay going down
end;
procedure plat_spawn_inside_trigger (ent:pedict_t);
var
trigger:pedict_t;
tmin, tmax:vec3_t;
begin
//
// middle trigger
//
trigger := G_Spawn();
trigger^.touch := Touch_Plat_Center;
trigger^.movetype := MOVETYPE_NONE;
trigger^.solid := SOLID_TRIGGER;
trigger^.enemy := ent;
tmin[0] := ent^.mins[0] + 25;
tmin[1] := ent^.mins[1] + 25;
tmin[2] := ent^.mins[2];
tmax[0] := ent^.maxs[0] - 25;
tmax[1] := ent^.maxs[1] - 25;
tmax[2] := ent^.maxs[2] + 8;
tmin[2] := tmax[2] - (ent^.pos1[2] - ent^.pos2[2] + st.lip);
if (ent^.spawnflags and PLAT_LOW_TRIGGER) then
tmax[2] := tmin[2] + 8;
if (tmax[0] - tmin[0] <= 0) then
begin
tmin[0] := (ent^.mins[0] + ent^.maxs[0]) *0.5;
tmax[0] := tmin[0] + 1;
end;
if (tmax[1] - tmin[1] <= 0) then
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -