📄 g_func.pas
字号:
(*
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)
{******************************************************
Name: g_func.pas
Translator: you_known
Description: a part of Quake 2 - Visual C to Delphi
Conversion
Date: 2002-02-03 16:43
E-mail: you_known@163.com
******************************************************}
unit g_func;
interface
uses
q_shared,
GameUnit,
g_local,
g_combat,
game_add,
g_local_add,
SysUtils;
(*
=========================================================
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: edict_p); cdecl;
procedure plat_go_down (ent:edict_p); cdecl;
procedure door_go_down (self:edict_p); cdecl;
procedure train_next (self:edict_p); cdecl;
procedure train_use (self:edict_p; other:edict_p; activator:edict_p); cdecl;
procedure func_train_find (self:edict_p); cdecl;
procedure door_secret_move1 (self:edict_p); cdecl;
procedure door_secret_move2 (self:edict_p); cdecl;
procedure door_secret_move3 (self:edict_p); cdecl;
procedure door_secret_move4 (self:edict_p); cdecl;
procedure door_secret_move5 (self:edict_p); cdecl;
procedure door_secret_move6 (self:edict_p); cdecl;
procedure door_secret_done (self:edict_p); cdecl;
procedure SP_func_plat (ent:edict_p); cdecl;
procedure SP_func_button (ent:edict_p); cdecl;
procedure SP_func_door_rotating (ent:edict_p); cdecl;
procedure SP_func_rotating (ent:edict_p); cdecl;
procedure SP_func_door (ent:edict_p); cdecl;
procedure SP_func_water (self:edict_p); cdecl;
procedure SP_func_train (self:edict_p); cdecl;
procedure SP_func_timer (self:edict_p); cdecl;
procedure SP_func_conveyor (self:edict_p); cdecl;
procedure SP_func_door_secret (ent:edict_p); cdecl;
procedure SP_func_killbox (ent:edict_p); cdecl;
procedure SP_trigger_elevator (self:edict_p); cdecl;
implementation
uses
g_misc,
g_utils,
g_save,
g_main,
q_shared_add;
//
// Support routines for movement (changes in origin using velocity)
//
procedure Move_Done (ent:edict_p); cdecl;
begin
VectorClear (ent^.velocity);
ent^.moveinfo.endfunc (ent);
end;
procedure Move_Final (ent:edict_p); cdecl;
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:edict_p); cdecl;
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 := ent^.moveinfo.remaining_distance - frames * ent^.moveinfo.speed * FRAMETIME;
ent^.nextthink := level.time + (frames * FRAMETIME);
ent^.think := Move_Final;
end;
procedure Move_Calc (ent: edict_p; const dest: vec3_t; Func : Proc_edit_s);
var
tempEdict : edict_p;
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) and (ent^.moveinfo.speed = ent^.moveinfo.decel) then
begin
if (ent^.flags and FL_TEAMSLAVE) <> 0 then
tempEdict := ent^.teammaster
else
tempEdict := ent;
if (level.current_entity = tempEdict) 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:edict_p); cdecl;
begin
VectorClear (ent^.avelocity);
ent^.moveinfo.endfunc (ent);
end;
procedure AngleMove_Final (ent:edict_p); cdecl;
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) <> 0) 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:edict_p); cdecl;
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:edict_p; func : Proc_edit_s);
var
tempEdict : edict_p;
begin
VectorClear (ent^.avelocity);
ent^.moveinfo.endfunc := func;
if (ent^.flags and FL_TEAMSLAVE) <> 0 then
tempEdict := ent^.teammaster
else
tempEdict := ent;
if (level.current_entity = tempEdict) 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
==============
*)
function AccelerationDistance(target, rate : double):double;
begin
Result := (target * ((target / rate) + 1) / 2);
end;
procedure plat_CalcAcceleratedMove(moveinfo:moveinfo_p);
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:moveinfo_p);
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 <> 0) 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:edict_p);
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(@ent^.moveinfo);
plat_Accelerate (@ent^.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:edict_p); cdecl;
begin
if ((ent^.flags and FL_TEAMSLAVE) = 0) then
begin
if (ent^.moveinfo.sound_end <> 0) 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:edict_p); cdecl;
begin
if ((ent^.flags and FL_TEAMSLAVE) = 0) then
begin;
if (ent^.moveinfo.sound_end <> 0) 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:edict_p);
begin
if ((ent^.flags and FL_TEAMSLAVE) = 0) then
begin
if (ent^.moveinfo.sound_start <> 0) 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:edict_p);
begin
if ((ent^.flags and FL_TEAMSLAVE) = 0) then
begin
if (ent^.moveinfo.sound_start <> 0) 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:edict_p; other:edict_p); cdecl;
begin
if ((other^.svflags and SVF_MONSTER = 0) and (other^.client = nil)) 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 assigned(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:edict_p; other:edict_p;activator:edict_p); cdecl;
begin
if assigned(ent^.think) then
exit; // already down
plat_go_down (ent);
end;
procedure Touch_Plat_Center (ent:edict_p; other:edict_p; plane:cplane_p; surf:csurface_p); cdecl;
begin
if (other^.client = nil) 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:edict_p);
var
trigger:edict_p;
tmin, tmax:vec3_t;
begin
//
// middle trigger
//
trigger := G_Spawn();
trigger^.touch := Touch_Plat_Center;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -