📄 p_client.pas
字号:
begin
// grab a body que and cycle to the next one
body := @g_edicts^[trunc(maxclients^.Value) + level.body_que + 1];
level.body_que := (level.body_que + 1) MOD BODY_QUEUE_SIZE;
// FIXME: send an effect on the removed body
gi.unlinkentity (ent);
gi.unlinkentity (body);
body^.s := ent^.s;
body^.s.number := (Cardinal(body) - Cardinal(g_edicts)) div SizeOf(edict_t);
body^.svflags := ent^.svflags;
VectorCopy (ent^.mins, body^.mins);
VectorCopy (ent^.maxs, body^.maxs);
VectorCopy (ent^.absmin, body^.absmin);
VectorCopy (ent^.absmax, body^.absmax);
VectorCopy (ent^.size, body^.size);
body^.solid := ent^.solid;
body^.clipmask := ent^.clipmask;
body^.owner := ent^.owner;
body^.movetype := ent^.movetype;
body^.die := body_die;
body^.takedamage := DAMAGE_YES;
gi.linkentity (body);
end;
procedure respawn (self : edict_p);
begin
if (deathmatch^.value <> 0) OR (coop^.value <> 0) then
begin
// spectator's don't leave bodies
if (self^.movetype <> MOVETYPE_NOCLIP) then
CopyToBodyQue (self);
self^.svflags := self^.svflags AND (NOT SVF_NOCLIENT);
PutClientInServer (self);
// add a teleportation effect
self^.s.event := EV_PLAYER_TELEPORT;
// hold in place briefly
self^.client^.ps.pmove.pm_flags := PMF_TIME_TELEPORT;
self^.client^.ps.pmove.pm_time := 14;
self^.client^.respawn_time := level.time;
Exit;
end;
// restart the entire server
gi.AddCommandString ('menu_loadgame'#10);
end;
{$IFNDEF CTF} //onlyGAME (noneCTF)
{*
* only called when pers.spectator changes
* note that resp.spectator should be the opposite of pers.spectator here
*}
procedure spectator_respawn (ent : edict_p); //imp
var
i, numspec : integer;
value : PChar;
begin
// if the user wants to become a spectator, make sure he doesn't
// exceed max_spectators
if (ent^.client^.pers.spectator) then
begin
value := Info_ValueForKey (ent^.client^.pers.userinfo, 'spectator');
if ( (spectator_password^.string_ <> nil) AND
(strcmp(spectator_password^.string_, 'none') <> 0) AND
(strcmp(spectator_password^.string_, value) <> 0)) then
begin
gi.cprintf(ent, PRINT_HIGH, 'Spectator password incorrect.'#10);
ent^.client^.pers.spectator := false;
gi.WriteByte (svc_stufftext);
gi.WriteString ('spectator 0'#10);
gi.unicast(ent, true);
Exit;
end;
// count spectators
// for (i = 1, numspec = 0; i <= maxclients->value; i++)
numspec := 0;
for i := 1 to trunc(maxclients^.Value) do
if (g_edicts^[i].inuse) AND (g_edicts^[i].client^.pers.spectator) then
Inc(numspec);
if (numspec >= maxspectators^.value) then
begin
gi.cprintf(ent, PRINT_HIGH, 'Server spectator limit is full.');
ent^.client^.pers.spectator := false;
// reset his spectator var
gi.WriteByte (svc_stufftext);
gi.WriteString ('spectator 0'#10);
gi.unicast(ent, true);
Exit;
end;
end
else
begin
// he was a spectator and wants to join the game
// he must have the right password
value := Info_ValueForKey (ent^.client^.pers.userinfo, 'password');
{ if ( (password^.string_^ <> #0) AND // <<-- Truer to Original Conversion }
if ( (password^.string_ <> nil) AND
(strcmp(password^.string_, 'none') <> 0) AND
(strcmp(password^.string_, value) <> 0)) then
begin
gi.cprintf(ent, PRINT_HIGH, 'Password incorrect.'#10);
ent^.client^.pers.spectator := true;
gi.WriteByte (svc_stufftext);
gi.WriteString ('spectator 1'#10);
gi.unicast(ent, true);
Exit;
end;
end;
// clear score on respawn
// ent->client->resp.score = ent->client->pers.score = 0;
ent^.client^.pers.score := 0;
ent.client.resp.score := ent^.client^.pers.score;
ent^.svflags := ent^.svflags AND (NOT SVF_NOCLIENT);
PutClientInServer (ent);
// add a teleportation effect
if (NOT ent^.client^.pers.spectator) then
begin
// send effect
gi.WriteByte (svc_muzzleflash);
gi.WriteShort ((Cardinal(ent)-Cardinal(g_edicts)) div SizeOf(edict_t));
gi.WriteByte (MZ_LOGIN);
gi.multicast (@ent^.s.origin, MULTICAST_PVS);
// hold in place briefly
ent^.client^.ps.pmove.pm_flags := PMF_TIME_TELEPORT;
ent^.client^.ps.pmove.pm_time := 14;
end;
ent^.client^.respawn_time := level.time;
if (ent^.client^.pers.spectator) then
gi.bprintf (PRINT_HIGH, '%s has moved to the sidelines'#10, ent^.client^.pers.netname)
else
gi.bprintf (PRINT_HIGH, '%s joined the game'#10, ent^.client^.pers.netname);
end;
{$ENDIF}
//==============================================================
{*
===========
PutClientInServer
Called when a player connects to a server or respawns in
a deathmatch.
============
*}
procedure PutClientInServer (ent : edict_p);
const
mins : vec3_t = (-16, -16, -24);
maxs : vec3_t = ( 16, 16, 32);
var
index : integer;
spawn_origin,
spawn_angles : vec3_t;
client : gclient_p;
i : integer;
saved : client_persistant_t;
resp : client_respawn_t;
userinfo : array[0..MAX_INFO_STRING-1] of char;
begin
// find a spawn point
// do it before setting health back up, so farthest
// ranging doesn't count this client
SelectSpawnPoint (ent, spawn_origin, spawn_angles);
index := (Cardinal(ent) - Cardinal(g_edicts)) div SizeOf(edict_t) - 1;
client := ent^.client;
// deathmatch wipes most client data every spawn
if (deathmatch^.value <> 0) then
begin
resp := client^.resp;
memcpy (@userinfo, @client^.pers.userinfo, SizeOf(userinfo));
InitClientPersistant (client);
ClientUserinfoChanged (ent, userinfo);
end
else if (coop^.value <> 0) then
begin
resp := client^.resp;
memcpy (@userinfo, @client^.pers.userinfo, SizeOf(userinfo));
{$IFDEF CTF}
for n:=0 to MAX_ITEMS-1 do
if ((itemlist[n].flags AND IT_KEY) <> 0) then
resp.coop_respawn.inventory[n] := client.pers.inventory[n];
{$ELSE}
// this is kind of ugly, but it's how we want to handle keys in coop
(*idsoft
// for (n = 0; n < game.num_items; n++)
// {
// if (itemlist[n].flags & IT_KEY)
// resp.coop_respawn.inventory[n] = client->pers.inventory[n];
// }
*)
resp.coop_respawn.game_helpchanged := client^.pers.game_helpchanged;
resp.coop_respawn.helpchanged := client^.pers.helpchanged;
{$ENDIF}
client^.pers := resp.coop_respawn;
ClientUserinfoChanged (ent, userinfo);
if (resp.score > client^.pers.score) then
client^.pers.score := resp.score;
end
else
FillChar(resp, SizeOf(resp), 0);
// clear everything but the persistant data
saved := client^.pers;
FillChar(client^, SizeOf(gclient_t), 0);
client^.pers := saved;
if (client^.pers.health <= 0) then
InitClientPersistant(client);
client^.resp := resp;
// copy some data from the client to the entity
FetchClientEntData (ent);
// clear entity values
ent^.groundentity := Nil;
ent^.client := @gclient_a(game.clients)[index];
ent^.takedamage := DAMAGE_AIM;
ent^.movetype := MOVETYPE_WALK;
ent^.viewheight := 22;
ent^.inuse := true;
ent^.classname := 'player';
ent^.mass := 200;
ent^.solid := SOLID_BBOX;
ent^.deadflag := DEAD_NO;
ent^.air_finished := level.time + 12;
ent^.clipmask := MASK_PLAYERSOLID;
ent^.model := 'players/male/tris.md2';
ent^.pain := player_pain;
ent^.die := player_die;
ent^.waterlevel := 0;
ent^.watertype := 0;
ent^.flags := ent^.flags AND (NOT FL_NO_KNOCKBACK);
ent^.svflags := ent^.svflags AND (NOT SVF_DEADMONSTER);
VectorCopy (mins, ent^.mins);
VectorCopy (maxs, ent^.maxs);
VectorClear (ent^.velocity);
// clear playerstate values
FillChar(ent^.client^.ps, SizeOf(client^.ps), 0);
client^.ps.pmove.origin[0] := trunc(spawn_origin[0]*8);
client^.ps.pmove.origin[1] := trunc(spawn_origin[1]*8);
client^.ps.pmove.origin[2] := trunc(spawn_origin[2]*8);
{$IFDEF CTF} //onlyCTF
//ZOID
client^.ps.pmove.pm_flags := client^.ps.pmove.pm_flags AND (NOT PMF_NO_PREDICTION);
//ZOID
{$ENDIF}
if (deathmatch^.Value <> 0) AND ((trunc(dmflags^.Value) and DF_FIXED_FOV) <> 0) then
begin
client^.ps.fov := 90
end
else
begin
client^.ps.fov := atoi(Info_ValueForKey(client^.pers.userinfo, 'fov'));
if (client^.ps.fov < 1) then
client^.ps.fov := 90
else if (client^.ps.fov > 160) then
client^.ps.fov := 160;
end;
client^.ps.gunindex := gi.modelindex(client^.pers.weapon^.view_model);
// clear entity state values
ent^.s.effects := 0;
ent^.s.modelindex := 255; // will use the skin specified model
ent^.s.modelindex2 := 255; // custom gun model
// sknum is player num and weapon number
// weapon number will be added in changeweapon
ent^.s.skinnum := (Cardinal(ent) - Cardinal(g_edicts)) div SizeOf(edict_t) - 1;
ent^.s.frame := 0;
VectorCopy (spawn_origin, ent^.s.origin);
ent^.s.origin[2] := ent^.s.origin[2] +1; // make sure off ground
VectorCopy (ent^.s.origin, ent^.s.old_origin);
// set the delta angle
for i := 0 to 2 do
client^.ps.pmove.delta_angles[i] := ANGLE2SHORT(spawn_angles[i] - client^.resp.cmd_angles[i]);
ent^.s.angles[PITCH] := 0;
ent^.s.angles[YAW] := spawn_angles[YAW];
ent^.s.angles[ROLL] := 0;
VectorCopy (ent^.s.angles, client^.ps.viewangles);
VectorCopy (ent^.s.angles, client^.v_angle);
{$IFDEF CTF}
(*Y//ZOID
if (CTFStartClient(ent)) then
Exit;
//ZOID*)
{$ELSE}
// spawn a spectator
if (client^.pers.spectator) then
begin
client^.chase_target := Nil;
client^.resp.spectator := true;
ent^.movetype := MOVETYPE_NOCLIP;
ent^.solid := SOLID_NOT;
ent^.svflags := ent^.svflags OR SVF_NOCLIENT;
ent^.client^.ps.gunindex := 0;
gi.linkentity (ent);
Exit;
end
else
client^.resp.spectator := false;
{$ENDIF}
if (not KillBox (ent)) then
begin
// could't spawn in?
end;
gi.linkentity (ent);
// force the current weapon up
client^.newweapon := client^.pers.weapon;
ChangeWeapon (ent);
end;
{*
=====================
ClientBeginDeathmatch
A client has just connected to the server in
deathmatch mode, so clear everything out before starting them.
=====================
*}
procedure ClientBeginDeathmatch (ent : edict_p);
begin
G_InitEdict (ent);
InitClientResp (ent^.client);
// locate ent at a spawn point
PutClientInServer (ent);
if (level.intermissiontime <> 0) then
begin
MoveClientToIntermission (ent);
end
else
begin
// send effect
gi.WriteByte (svc_muzzleflash);
gi.WriteShort ((Cardinal(ent) - Cardinal(g_edicts)) div SizeOf(edict_t));
gi.WriteByte (MZ_LOGIN);
gi.multicast (@ent^.s.origin, MULTICAST_PVS);
end;
gi.bprintf (PRINT_HIGH, '%s entered the game'#10, ent^.client^.pers.netname);
// make sure all view stuff is valid
ClientEndServerFrame (ent);
end;
{*
===========
ClientBegin
called when a client has finished connecting, and is ready
to be placed into the game. This will happen every level load.
============
*}
procedure ClientBegin (ent : edict_p); //g_main
var
i : integer;
begin
ent^.client := @gclient_a(game.clients)[((Cardinal(ent) - Cardinal(g_edicts)) div sizeof(edict_t)- 1)];
if (deathmatch^.value <> 0) then
begin
ClientBeginDeathmatch (ent);
Exit;
end;
// if there is already a body waiting for us (a loadgame), just
// take it, otherwise spawn one from scratch
if (ent^.inuse = true) then
begin
// the client has cleared the client side viewangles upon
// connecting to the server, which is different than the
// state when the game is saved, so we need to compensate
// with deltaangles
for i := 0 to 2 do
ent^.client^.ps.pmove.delta_angles[i] := ANGLE2SHORT(ent^.client^.ps.viewangles[i]);
end
else
begin
// a spawn point will completely reinitialize the entity
// except for the persistant data that was initialized at
// ClientConnect() time
G_InitEdict (ent);
ent^.classname := 'player';
InitClientResp (ent^.client);
PutClientInServer (ent);
end;
if (level.intermissiontime <> 0) then
begin
MoveClientToIntermission (ent)
end
else
begin
// send effect if in a multiplayer game
if (game.maxclients > 1) then
begin
gi.WriteByte (svc_muzzleflash);
gi.WriteShort (Cardinal(ent)-Cardinal(g_edicts) div SizeOf(edict_t));
gi.WriteByte (MZ_LOGIN);
gi.multicast (@ent^.s.origin, MULTICAST_PVS);
gi.bprintf (PRINT_HIGH, '%s entered the game'#10, ent^.client^.pers.netname);
end;
end;
// make sure all view stuff is valid
ClientEndServerFrame (ent);
end;
{*
===========
ClientUserInfoChanged
called whenever the player updates a userinfo variable.
The game can override any of the settings in place
(forcing skins or names, etc) before copying it off.
============
*}
procedure ClientUserinfoChanged (ent : edict_p; userinfo : PChar); //g_main
var
s : PChar;
playernum : integer;
begin
// check for malformed or illegal info strings
if (not Info_Validate(userinfo)) then
strcpy (userinfo, '\name\badinfo\skin\male/grunt');
// set name
s := Info_ValueForKey (userinfo, 'name');
strncpy (ent^.client^.pers.netname, s, sizeof(ent^.client^.pers.netname)-1);
{$IFNDEF CTF} //onlyGAME (noneCTF)
// set spectator
s := Info_ValueForKey (userinfo, 'spectator');
// spectators are only supported in deathmatch
{ 2003-05-08-SP: Alternative more Literal Conversion in case chosen option is under doubt
if (deathmatch^.value <> 0) and (s^ <> #0) and (strcmp(s, '0') <> 0) then }
if (deathmatch^.value <> 0) and (s <> nil) and (strcmp(s, '0') <> 0) then
ent^.client^.pers.spectator := true
else
ent^.client^.pers.spectator := false;
{$ENDIF}
// set skin
s := Info_ValueForKey (userinfo, 'skin');
playernum := ((Cardinal(ent) - Cardinal(g_edicts)) div SizeOf(edict_t)) - 1;
// combine name and skin into a configstring
{$IFDEF CTF} //onlyCTF
(*Y//ZOID
if (ctf.value)
then CTFAssignSkin(ent, s)
else
//ZOID*)
{$ENDIF}
gi.configstring (CS_PLAYERSKINS+playernum, va('%s\%s', [ent^.client^.pers.netname, s]));
// fov
if (deathmatch^.value <> 0) AND ((trunc(dmflags^.Value) and DF_FIXED_FOV) <> 0) then
begin
ent^.client^.ps.fov := 90
end
else
begin
ent^.client^.ps.fov := atoi(Info_ValueForKey(userinfo, 'fov'));
if (ent^.client^.ps.fov < 1) then
ent^.client^.ps.fov := 90
else if (ent^.client^.ps.fov > 160) then
ent^.client^.ps.fov := 160;
end;
// handedness
s := Info_ValueForKey (userinfo, 'hand');
if (strlen(s) <> 0) then
ent^.client^.pers.hand := atoi(s);
// save off the userinfo in case we want to check something later
strncpy (ent^.client^.pers.userinfo, userinfo, sizeof(ent^.client^.pers.userinfo)-1);
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -