2019-08-31 19:18:15 -07:00
|
|
|
/*
|
2022-07-07 09:10:14 -07:00
|
|
|
* Copyright (c) 2016-2022 Vera Visions LLC.
|
2019-08-31 19:18:15 -07:00
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
|
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
2022-07-07 09:10:14 -07:00
|
|
|
*/
|
2019-01-18 20:50:25 -08:00
|
|
|
|
2023-03-18 17:49:12 -07:00
|
|
|
static int g_ent_spawned;
|
2023-10-22 23:30:31 -07:00
|
|
|
.bool gotData;
|
2023-03-18 17:49:12 -07:00
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called once every single tic on the server. */
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
StartFrame(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2021-04-06 03:22:32 -07:00
|
|
|
PMove_StartFrame();
|
|
|
|
|
2021-02-14 12:05:10 -08:00
|
|
|
/* For entity parenting to work, we need to go through and run on every
|
2021-10-19 16:18:36 -07:00
|
|
|
* this method on every NSEntity class */
|
2020-10-22 07:30:37 -07:00
|
|
|
for (entity a = world; (a = findfloat(a, ::identity, 1));) {
|
2021-10-19 16:18:36 -07:00
|
|
|
NSEntity ent = (NSEntity)a;
|
2020-01-15 20:43:12 -08:00
|
|
|
ent.ParentUpdate();
|
|
|
|
}
|
|
|
|
|
2021-02-14 12:05:10 -08:00
|
|
|
if (g_ents_initialized)
|
|
|
|
g_grMode.FrameStart();
|
|
|
|
|
2020-03-25 05:58:19 -07:00
|
|
|
Vote_Frame();
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called when the client-slot is being prepared for a player.
|
2021-01-06 04:58:37 -08:00
|
|
|
The client may not fully spawn into the world (yet), as they're still
|
|
|
|
loading or receiving packets.
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
The `self` global is the connecting client in question.
|
2021-01-06 04:58:37 -08:00
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
2021-05-28 01:26:42 -07:00
|
|
|
ClientConnect(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2020-11-23 12:39:40 -08:00
|
|
|
int playercount = 0;
|
2023-10-22 23:30:31 -07:00
|
|
|
self.gotData = false;
|
2020-11-23 12:39:40 -08:00
|
|
|
|
2022-12-28 16:47:44 -08:00
|
|
|
/* don't carry over team settings from a previous session */
|
2022-03-02 09:18:38 -08:00
|
|
|
forceinfokey(self, "*team", "0");
|
|
|
|
|
2022-12-28 16:47:44 -08:00
|
|
|
/* bot needs special init */
|
2021-01-06 06:54:17 -08:00
|
|
|
#ifdef BOT_INCLUDED
|
|
|
|
if (clienttype(self) == CLIENTTYPE_BOT) {
|
Add a whole bunch of new bot features, including bot profiles.
Developers: the internal class `bot` is now `NSBot`, and an entityDef
of the old name will be instantiated instead.
Override defs/bot.def in your mod and make it use any custom spawnclass
you wish. Now games don't have to override `addbot` or `bot_add` inside
the multiplayer game rules.
There's also more console commands. Clients now have access to:
addBot, killAllBots, killClass [classname], killMovables, trigger [targetname], input [entnum] [input] [data], listBotProfiles, listTargets, teleport [targetname], teleportToClass [classname], respawnEntities, spawn
2024-01-26 19:22:14 -08:00
|
|
|
/* from now on we're of type NSBot */
|
|
|
|
EntityDef_SpawnClassname("bot");
|
2021-01-06 06:54:17 -08:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
|
2020-10-30 03:28:59 -07:00
|
|
|
/* make sure you never change the classname. ever. */
|
|
|
|
if (self.classname != "player") {
|
|
|
|
spawnfunc_player();
|
|
|
|
}
|
|
|
|
|
2021-02-14 12:05:10 -08:00
|
|
|
if (g_ents_initialized)
|
2022-05-11 12:49:04 -07:00
|
|
|
g_grMode.PlayerConnect((NSClientPlayer)self);
|
2021-02-14 12:05:10 -08:00
|
|
|
|
2020-11-23 12:39:40 -08:00
|
|
|
for (entity a = world; (a = find(a, ::classname, "player"));)
|
|
|
|
playercount++;
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called when a player leaves the server. At the end of the function the
|
|
|
|
client slot referred to by the `self` global will be cleared.
|
2021-01-06 04:58:37 -08:00
|
|
|
This means the fields will still be accessible inside of this function.
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
ClientDisconnect(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2021-02-14 12:05:10 -08:00
|
|
|
if (g_ents_initialized)
|
2022-05-11 12:49:04 -07:00
|
|
|
g_grMode.PlayerDisconnect((NSClientPlayer)self);
|
2022-04-26 11:22:12 -07:00
|
|
|
|
|
|
|
/* this will hide/remove the player from other clients */
|
|
|
|
player pl = (player)self;
|
2024-03-04 18:51:07 -08:00
|
|
|
pl.SetSolid(SOLID_NOT);
|
|
|
|
pl.SetMovetype(MOVETYPE_NONE);
|
|
|
|
pl.SetModelindex(0);
|
|
|
|
pl.SetHealth(0);
|
|
|
|
pl.SetTakedamage(DAMAGE_NO);
|
|
|
|
pl.SetTeam(0);
|
2022-12-30 13:53:42 -08:00
|
|
|
pl.Disappear();
|
2024-03-02 00:40:08 -08:00
|
|
|
pl.classname = "";
|
|
|
|
pl.flags = 0;
|
2024-03-04 19:45:33 -08:00
|
|
|
pl.deaths = 0;
|
|
|
|
pl.frags = 0;
|
|
|
|
pl.score = 0;
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called by the `kill` console command.
|
|
|
|
The `self` global is the client issuing the command.
|
2021-01-06 04:58:37 -08:00
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
ClientKill(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2021-02-14 12:05:10 -08:00
|
|
|
if (g_ents_initialized)
|
2022-05-11 12:49:04 -07:00
|
|
|
g_grMode.PlayerKill((NSClientPlayer)self);
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** This is run every frame on every spectator.
|
|
|
|
The `self` global refers to one of any given amount of spectator.
|
2021-01-06 04:58:37 -08:00
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
SpectatorThink(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2021-03-23 23:50:30 -07:00
|
|
|
if (self.classname == "spectator") {
|
2022-05-11 12:49:04 -07:00
|
|
|
NSClientSpectator spec = (NSClientSpectator)self;
|
2021-03-23 23:50:30 -07:00
|
|
|
spec.PreFrame();
|
|
|
|
spec.PostFrame();
|
|
|
|
return;
|
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
2021-01-06 04:58:37 -08:00
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called when a NSClientSpectator joins the server.
|
|
|
|
The `self` global is the connecting NSClientSpectator in question.
|
2021-01-06 04:58:37 -08:00
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
SpectatorConnect(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2022-05-11 12:49:04 -07:00
|
|
|
spawnfunc_NSClientSpectator();
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
2021-01-06 04:58:37 -08:00
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called when a NSClientSpectator leaves the server.
|
|
|
|
The `self` global is the leaving NSClientSpectator in question.
|
2021-01-06 04:58:37 -08:00
|
|
|
Attributes cleared when this function is done executing.
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
SpectatorDisconnect(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called when a player enters the game, having fully connected and loaded into
|
2021-01-06 04:58:37 -08:00
|
|
|
the session.
|
2022-10-10 12:30:31 -07:00
|
|
|
The `self` global is the player in question.
|
2021-01-06 04:58:37 -08:00
|
|
|
The 'parmX' globals are also populated with any data carried over from
|
|
|
|
past levels for the player in question.
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
PutClientInServer(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2021-02-14 12:05:10 -08:00
|
|
|
if (g_ents_initialized)
|
2022-05-11 12:49:04 -07:00
|
|
|
g_grMode.PlayerSpawn((NSClientPlayer)self);
|
2021-02-14 12:05:10 -08:00
|
|
|
|
2023-09-28 18:40:06 -07:00
|
|
|
/* handle transitions */
|
2024-03-04 18:51:07 -08:00
|
|
|
if (FileExists("data/trans.dat")) {
|
2024-03-02 00:40:08 -08:00
|
|
|
|
|
|
|
for (entity a = world; (a = findfloat(a, ::identity, 1));) {
|
|
|
|
NSEntity levelEnt = (NSEntity)a;
|
|
|
|
levelEnt.TransitionComplete();
|
|
|
|
}
|
|
|
|
|
2023-09-28 18:40:06 -07:00
|
|
|
trigger_transition::LoadTransition();
|
|
|
|
}
|
|
|
|
|
2022-05-11 12:49:04 -07:00
|
|
|
Plugin_PlayerEntered((NSClientPlayer)self);
|
2024-03-04 19:45:33 -08:00
|
|
|
|
|
|
|
if (g_grMode.IsMultiplayer() == true && self.deaths <= 0) {
|
|
|
|
bprint(PRINT_HIGH, sprintf("%s^d entered the game.\n", self.netname));
|
|
|
|
}
|
2020-09-07 18:29:10 -07:00
|
|
|
|
|
|
|
/* activate all game_playerspawn entities */
|
2020-09-08 13:49:35 -07:00
|
|
|
for (entity a = world; (a = find(a, ::targetname, "game_playerspawn"));) {
|
2021-10-19 16:18:36 -07:00
|
|
|
NSEntity t = (NSEntity)a;
|
2020-09-07 18:29:10 -07:00
|
|
|
|
2020-09-08 13:49:35 -07:00
|
|
|
if (t.Trigger)
|
2020-09-07 18:29:10 -07:00
|
|
|
t.Trigger(self, TRIG_TOGGLE);
|
|
|
|
}
|
2022-12-14 16:51:16 -08:00
|
|
|
|
2022-12-28 16:47:44 -08:00
|
|
|
/* the game and its triggers start when the player is ready to see it */
|
2022-12-14 16:51:16 -08:00
|
|
|
trigger_auto_trigger();
|
2024-02-21 13:41:18 -08:00
|
|
|
|
|
|
|
if (g_grMode.InIntermission() == true) {
|
|
|
|
g_grMode.IntermissionToPlayer((NSClientPlayer)self);
|
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Run before game physics have taken place.
|
|
|
|
The `self` global refers to a single client, as this function is called
|
2021-01-06 04:58:37 -08:00
|
|
|
times the amount of players in a given game.
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
PlayerPreThink(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2021-03-23 23:50:30 -07:00
|
|
|
if (self.classname == "spectator") {
|
2022-05-11 12:49:04 -07:00
|
|
|
//NSClientSpectator spec = (NSClientSpectator)self;
|
2021-03-23 23:50:30 -07:00
|
|
|
//spec.PreFrame();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-16 08:34:04 -08:00
|
|
|
if (self.classname != "player") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-25 22:27:34 -08:00
|
|
|
#ifdef BOT_INCLUDED
|
|
|
|
if (clienttype(self) == CLIENTTYPE_BOT) {
|
Add a whole bunch of new bot features, including bot profiles.
Developers: the internal class `bot` is now `NSBot`, and an entityDef
of the old name will be instantiated instead.
Override defs/bot.def in your mod and make it use any custom spawnclass
you wish. Now games don't have to override `addbot` or `bot_add` inside
the multiplayer game rules.
There's also more console commands. Clients now have access to:
addBot, killAllBots, killClass [classname], killMovables, trigger [targetname], input [entnum] [input] [data], listBotProfiles, listTargets, teleport [targetname], teleportToClass [classname], respawnEntities, spawn
2024-01-26 19:22:14 -08:00
|
|
|
((NSBot)self).PreFrame();
|
2020-12-25 22:27:34 -08:00
|
|
|
}
|
|
|
|
#endif
|
2021-01-16 08:34:04 -08:00
|
|
|
|
2021-02-14 12:05:10 -08:00
|
|
|
if (g_ents_initialized)
|
2022-05-11 12:49:04 -07:00
|
|
|
g_grMode.PlayerPreFrame((NSClientPlayer)self);
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Run after game physics have taken place.
|
|
|
|
The `self` global refers to a single client, as this function is called
|
2021-01-06 04:58:37 -08:00
|
|
|
times the amount of players in a given game.
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
PlayerPostThink(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2021-03-23 23:50:30 -07:00
|
|
|
if (self.classname == "spectator") {
|
|
|
|
SpectatorThink();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-16 08:34:04 -08:00
|
|
|
if (self.classname != "player") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-25 22:27:34 -08:00
|
|
|
#ifdef BOT_INCLUDED
|
|
|
|
if (clienttype(self) == CLIENTTYPE_BOT) {
|
Add a whole bunch of new bot features, including bot profiles.
Developers: the internal class `bot` is now `NSBot`, and an entityDef
of the old name will be instantiated instead.
Override defs/bot.def in your mod and make it use any custom spawnclass
you wish. Now games don't have to override `addbot` or `bot_add` inside
the multiplayer game rules.
There's also more console commands. Clients now have access to:
addBot, killAllBots, killClass [classname], killMovables, trigger [targetname], input [entnum] [input] [data], listBotProfiles, listTargets, teleport [targetname], teleportToClass [classname], respawnEntities, spawn
2024-01-26 19:22:14 -08:00
|
|
|
((NSBot)self).PostFrame();
|
2020-12-25 22:27:34 -08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-02-27 17:31:27 -08:00
|
|
|
if (g_ents_initialized) {
|
|
|
|
player pl = (player)self;
|
2022-05-11 12:49:04 -07:00
|
|
|
g_grMode.PlayerPostFrame((NSClientPlayer)self);
|
2021-02-27 17:31:27 -08:00
|
|
|
pl.EvaluateEntity();
|
2023-06-15 11:42:01 -07:00
|
|
|
forceinfokey(pl, "*score", ftos(pl.score));
|
2021-02-27 17:31:27 -08:00
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called when we spawn in a new map (both single and multiplayer) with no level
|
2021-01-06 04:58:37 -08:00
|
|
|
change ever having taken place.
|
2022-10-10 12:30:31 -07:00
|
|
|
The `self` global does not refer to anything.
|
2021-01-06 04:58:37 -08:00
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
SetNewParms(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2024-03-02 00:40:08 -08:00
|
|
|
InitPrint("Setting New Level Parameters");
|
2021-02-14 12:05:10 -08:00
|
|
|
|
|
|
|
if (g_ents_initialized)
|
|
|
|
g_grMode.LevelNewParms();
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called whenever a single-player level change is about to happen, carrying
|
2021-01-06 04:58:37 -08:00
|
|
|
over data from one level to the next. This is not called with the 'map' command.
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
The `self` global refers to a client that's partaking in the level-change.
|
2021-01-06 04:58:37 -08:00
|
|
|
Make sure we're saving important fields/attributes in the 'parmX' globals
|
|
|
|
allocated for every client.
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
SetChangeParms(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2024-03-02 00:40:08 -08:00
|
|
|
InitPrint("Setting Level-Change Parameters");
|
2021-02-14 12:05:10 -08:00
|
|
|
|
|
|
|
if (g_ents_initialized)
|
2022-05-11 12:49:04 -07:00
|
|
|
g_grMode.LevelChangeParms((NSClientPlayer)self);
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Run whenever an input packet by a client has been received.
|
2021-01-06 04:58:37 -08:00
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
The `self` global is the entity having sent the input packet,
|
2021-01-06 04:58:37 -08:00
|
|
|
with the input_X globals being set to the appropriate data.
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
SV_RunClientCommand(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2022-12-28 16:47:44 -08:00
|
|
|
NSClient cl = (NSClient)self;
|
2020-12-22 22:53:43 -08:00
|
|
|
|
2023-01-23 00:12:35 -08:00
|
|
|
if (self.classname != "player" && self.classname != "spectator")
|
|
|
|
return;
|
|
|
|
|
2023-08-07 12:56:17 -07:00
|
|
|
CheatersLament((NSClientPlayer)cl, input_angles, input_buttons, input_timelength);
|
2023-11-18 21:16:52 -08:00
|
|
|
self.v_angle = input_angles;
|
2023-08-07 12:56:17 -07:00
|
|
|
|
2019-08-14 02:43:43 -07:00
|
|
|
if (!Plugin_RunClientCommand()) {
|
2022-12-22 16:54:11 -08:00
|
|
|
/* TODO */
|
|
|
|
}
|
|
|
|
|
2024-01-08 14:58:45 -08:00
|
|
|
/* FIXME: before a splitscreen player disconnects, it often sends
|
|
|
|
one last SV_RunClientCommand which causes an error if we don't
|
|
|
|
check these two methods here */
|
|
|
|
if (cl.SharedInputFrame)
|
|
|
|
cl.SharedInputFrame();
|
|
|
|
if (cl.ServerInputFrame)
|
|
|
|
cl.ServerInputFrame();
|
2023-10-22 23:30:31 -07:00
|
|
|
|
|
|
|
if (self.gotData == false) {
|
|
|
|
BreakModel_SendClientData(self);
|
|
|
|
self.gotData = true;
|
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Any 'cmd' from the client get sent here and handled.
|
2021-01-06 04:58:37 -08:00
|
|
|
Unlike ConsoleCommmand() if you want to let the server engine
|
|
|
|
take over, you need to pass the string 'cmd' over via clientcommand().
|
|
|
|
|
|
|
|
Notable examples of client cmd's involve the chat system.
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
SV_ParseClientCommand(string cmd)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2020-11-06 04:04:22 -08:00
|
|
|
string newcmd = Plugin_ParseClientCommand(cmd);
|
2022-12-22 11:53:15 -08:00
|
|
|
int argc;
|
2020-11-06 04:04:22 -08:00
|
|
|
|
2022-12-22 11:53:15 -08:00
|
|
|
/* give the game-mode a chance to override us */
|
|
|
|
if (g_ents_initialized)
|
|
|
|
if (g_grMode.ClientCommand((NSClient)self, cmd) == true)
|
|
|
|
return;
|
2021-03-23 23:50:30 -07:00
|
|
|
|
2022-12-22 11:53:15 -08:00
|
|
|
argc = tokenize(cmd);
|
2021-03-23 23:50:30 -07:00
|
|
|
|
|
|
|
switch (argv(0)) {
|
2022-12-22 11:53:15 -08:00
|
|
|
case "say":
|
|
|
|
if (argc == 2)
|
2023-02-14 12:54:06 -08:00
|
|
|
g_grMode.ChatMessageAll((NSClient)self, argv(1));
|
2022-12-22 11:53:15 -08:00
|
|
|
else
|
2023-02-14 12:54:06 -08:00
|
|
|
g_grMode.ChatMessageAll((NSClient)self, substring(cmd, 5, -2));
|
2022-12-22 11:53:15 -08:00
|
|
|
break;
|
|
|
|
case "say_team":
|
|
|
|
if (argc == 2)
|
2023-02-14 12:54:06 -08:00
|
|
|
g_grMode.ChatMessageTeam((NSClient)self, argv(1));
|
2022-12-22 11:53:15 -08:00
|
|
|
else
|
2023-02-14 12:54:06 -08:00
|
|
|
g_grMode.ChatMessageTeam((NSClient)self, substring(cmd, 10, -2));
|
2022-12-22 11:53:15 -08:00
|
|
|
break;
|
2021-03-23 23:50:30 -07:00
|
|
|
case "spectate":
|
|
|
|
if (self.classname != "player")
|
|
|
|
break;
|
2024-01-08 14:58:45 -08:00
|
|
|
|
|
|
|
player pl = (player)self;
|
|
|
|
pl.MakeSpectator();
|
2021-03-23 23:50:30 -07:00
|
|
|
break;
|
|
|
|
case "play":
|
|
|
|
if (self.classname != "spectator")
|
|
|
|
break;
|
|
|
|
spawnfunc_player();
|
|
|
|
PutClientInServer();
|
2021-09-09 10:03:26 -07:00
|
|
|
break;
|
|
|
|
case "setpos":
|
|
|
|
if (cvar("sv_cheats") == 1) {
|
|
|
|
setorigin(self, stov(argv(1)));
|
|
|
|
}
|
2022-12-22 16:29:02 -08:00
|
|
|
break;
|
2022-12-22 16:54:11 -08:00
|
|
|
case "timeleft":
|
|
|
|
string msg;
|
|
|
|
string timestring;
|
|
|
|
float timeleft;
|
|
|
|
timeleft = cvar("timelimit") - (time / 60);
|
|
|
|
timestring = Util_TimeToString(timeleft);
|
|
|
|
msg = sprintf("we have %s minutes remaining", timestring);
|
|
|
|
bprint(PRINT_CHAT, msg);
|
|
|
|
break;
|
2022-12-22 16:29:02 -08:00
|
|
|
default:
|
|
|
|
clientcommand(self, cmd);
|
2021-03-23 23:50:30 -07:00
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called when the QC module gets loaded.
|
|
|
|
No entities exist yet and we are unable to allocate any in here.
|
|
|
|
So avoid calling spawn() related functions here. */
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
init(float prevprogs)
|
2020-04-13 18:12:09 -07:00
|
|
|
{
|
2024-03-02 00:40:08 -08:00
|
|
|
InitPrint("Initializing Server Game");
|
|
|
|
NSLog("Built: %s %s", __DATE__, __TIME__);
|
|
|
|
NSLog("QCC: %s", __QCCVER__);
|
2023-03-18 17:49:12 -07:00
|
|
|
|
2020-04-13 18:12:09 -07:00
|
|
|
Plugin_Init();
|
2022-10-15 22:43:32 -07:00
|
|
|
|
2023-07-15 09:32:21 -07:00
|
|
|
Constants_Init();
|
2022-10-15 22:43:32 -07:00
|
|
|
Sound_Init();
|
2021-09-21 11:33:09 -07:00
|
|
|
PropData_Init();
|
2021-10-15 09:06:04 -07:00
|
|
|
SurfData_Init();
|
2022-10-27 20:03:03 -07:00
|
|
|
DecalGroups_Init();
|
2023-11-18 21:16:52 -08:00
|
|
|
|
|
|
|
/* DO NOT EVER CHANGE THESE. */
|
|
|
|
cvar_set("r_meshpitch", "1");
|
2020-04-13 18:12:09 -07:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called inside initents() to make sure the entities have their Respawn()
|
2021-01-06 04:58:37 -08:00
|
|
|
method called at the beginning of them having spawned.
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
init_respawn(void)
|
2020-04-13 18:12:09 -07:00
|
|
|
{
|
2023-03-18 17:49:12 -07:00
|
|
|
int endspawn = 0;
|
2021-02-14 12:05:10 -08:00
|
|
|
|
|
|
|
if (g_ents_initialized)
|
|
|
|
g_grMode.InitPostEnts();
|
2020-04-14 07:19:25 -07:00
|
|
|
|
2023-03-18 17:49:12 -07:00
|
|
|
/* of all the map entities that we wanted to spawn, how many are left? */
|
|
|
|
for (entity a = world; (a = findfloat(a, ::_mapspawned, true));) {
|
|
|
|
endspawn++;
|
2020-04-13 18:12:09 -07:00
|
|
|
}
|
2023-03-18 17:49:12 -07:00
|
|
|
|
2024-03-02 00:40:08 -08:00
|
|
|
NSLog("...%i entities spawned (%i inhibited)", g_ent_spawned, g_ent_spawned - endspawn);
|
|
|
|
|
|
|
|
InitEnd();
|
2023-09-28 18:40:06 -07:00
|
|
|
|
2024-03-04 18:51:07 -08:00
|
|
|
Nodes_Init();
|
|
|
|
|
2020-04-13 18:12:09 -07:00
|
|
|
remove(self);
|
|
|
|
}
|
|
|
|
|
2022-08-10 14:24:06 -07:00
|
|
|
entity g_respawntimer;
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called by the engine when we're ready to spawn entities.
|
|
|
|
Before this, we are not able to spawn, touch or allocate any entity slots.
|
2021-01-06 04:58:37 -08:00
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
initents(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2020-07-09 01:42:34 -07:00
|
|
|
/* sound shader init */
|
2022-10-15 20:32:40 -07:00
|
|
|
Materials_Init();
|
2023-03-25 16:28:06 -07:00
|
|
|
MOTD_Init();
|
2019-01-20 18:00:14 -08:00
|
|
|
PMove_Init();
|
2019-03-06 05:11:23 -08:00
|
|
|
|
2023-03-13 23:52:41 -07:00
|
|
|
/** compat... */
|
|
|
|
|
2023-11-17 18:54:46 -08:00
|
|
|
Sound_Precache("Player.GaspLight");
|
|
|
|
Sound_Precache("Player.GaspHeavy");
|
2024-01-15 15:45:48 -08:00
|
|
|
Sound_Precache("Player.WaterEnter");
|
|
|
|
Sound_Precache("Player.WaterExit");
|
2023-10-22 23:30:31 -07:00
|
|
|
Sound_Precache("Player.Death");
|
|
|
|
Sound_Precache("Player.Pain");
|
|
|
|
Sound_Precache("Player.Wade");
|
|
|
|
Sound_Precache("Player.Swim");
|
2024-01-15 15:45:48 -08:00
|
|
|
Sound_Precache("Player.DenyWeaponSelection");
|
2023-10-22 23:30:31 -07:00
|
|
|
Sound_Precache("Player.WeaponSelected");
|
2023-11-17 18:54:46 -08:00
|
|
|
Sound_Precache("Player.FallDamage");
|
|
|
|
Sound_Precache("Player.LightFall");
|
2023-10-22 23:30:31 -07:00
|
|
|
|
2021-03-27 01:09:10 -07:00
|
|
|
Sound_Precache("damage_bullet.hit");
|
2023-11-17 18:54:46 -08:00
|
|
|
Sound_Precache("SprayCan.Paint");
|
2023-04-15 18:11:43 -07:00
|
|
|
Sound_Precache("step_ladder.left");
|
|
|
|
Sound_Precache("step_ladder.right");
|
2021-03-12 19:24:53 -08:00
|
|
|
|
2023-09-29 16:10:21 -07:00
|
|
|
if (!g_grMode) {
|
2023-03-23 17:04:34 -07:00
|
|
|
Game_InitRules();
|
2023-09-29 16:10:21 -07:00
|
|
|
forceinfokey(world, "mode", g_grMode.Title());
|
|
|
|
}
|
2023-03-23 17:04:34 -07:00
|
|
|
|
2019-03-06 05:11:23 -08:00
|
|
|
Game_Worldspawn();
|
|
|
|
Decals_Init();
|
2020-03-26 03:24:33 -07:00
|
|
|
Sentences_Init();
|
|
|
|
|
2020-07-09 01:42:34 -07:00
|
|
|
/* TODO: Make sure every entity calls Respawn inside the constructor, then
|
|
|
|
* remove this */
|
2022-08-10 14:24:06 -07:00
|
|
|
g_respawntimer = spawn();
|
|
|
|
g_respawntimer.think = init_respawn;
|
|
|
|
g_respawntimer.nextthink = time + 0.1f;
|
2020-09-27 05:25:10 -07:00
|
|
|
|
|
|
|
/* menu background lock */
|
|
|
|
if (cvar("sv_background") == 1) {
|
|
|
|
forceinfokey(world, "background", "1");
|
|
|
|
localcmd("sv_background 0\n");
|
|
|
|
} else {
|
|
|
|
forceinfokey(world, "background", "0");
|
|
|
|
}
|
2021-01-23 03:56:22 -08:00
|
|
|
|
|
|
|
/* the maxclients serverinfo key? yeah, that one lies to the client. so
|
|
|
|
* let's add our own that we can actually trust. */
|
|
|
|
forceinfokey(world, "sv_playerslots", cvar_string("sv_playerslots"));
|
2021-01-24 11:26:10 -08:00
|
|
|
|
|
|
|
Plugin_InitEnts();
|
2021-02-08 04:35:15 -08:00
|
|
|
Mapcycle_Init();
|
2021-02-13 14:08:58 -08:00
|
|
|
Vote_Init();
|
2022-01-10 23:48:14 -08:00
|
|
|
ChangeTarget_Activate();
|
2021-02-14 12:05:10 -08:00
|
|
|
|
|
|
|
g_ents_initialized = TRUE;
|
2022-12-28 15:15:30 -08:00
|
|
|
|
|
|
|
/* engine hacks for dedicated servers */
|
2024-03-02 00:40:08 -08:00
|
|
|
cvar_set("s_nominaldistance", "1536");
|
2023-05-01 06:58:54 -07:00
|
|
|
|
|
|
|
/* other engine hacks */
|
|
|
|
cvar_set("sv_nqplayerphysics", "0");
|
Add a whole bunch of new bot features, including bot profiles.
Developers: the internal class `bot` is now `NSBot`, and an entityDef
of the old name will be instantiated instead.
Override defs/bot.def in your mod and make it use any custom spawnclass
you wish. Now games don't have to override `addbot` or `bot_add` inside
the multiplayer game rules.
There's also more console commands. Clients now have access to:
addBot, killAllBots, killClass [classname], killMovables, trigger [targetname], input [entnum] [input] [data], listBotProfiles, listTargets, teleport [targetname], teleportToClass [classname], respawnEntities, spawn
2024-01-26 19:22:14 -08:00
|
|
|
|
|
|
|
#ifdef BOT_INCLUDED
|
|
|
|
BotLib_Init();
|
|
|
|
#endif
|
2019-03-06 05:11:23 -08:00
|
|
|
}
|
2019-03-01 14:35:28 -08:00
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Any command executed on the server (either tty, rcon or `sv`) gets
|
2021-01-06 04:58:37 -08:00
|
|
|
sent here first.
|
|
|
|
|
|
|
|
When returning FALSE the server will interpret the command.
|
2022-10-10 12:30:31 -07:00
|
|
|
Returning TRUE will mark the command as 'resolved' and the engine
|
|
|
|
will not attempt handling it.
|
|
|
|
|
|
|
|
The client-side equivalent is `CSQC_ConsoleCommand` (src/client/entry.qc)
|
2021-01-06 04:58:37 -08:00
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
float
|
|
|
|
ConsoleCmd(string cmd)
|
2019-01-20 18:00:14 -08:00
|
|
|
{
|
2020-12-22 22:53:43 -08:00
|
|
|
player pl;
|
|
|
|
|
|
|
|
/* some sv commands can only be executed by a player in-world */
|
|
|
|
if ( !self ) {
|
|
|
|
for ( other = world; ( other = find( other, classname, "player" ) ); ) {
|
|
|
|
if ( clienttype( other ) == CLIENTTYPE_REAL ) {
|
|
|
|
self = other;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-23 23:50:30 -07:00
|
|
|
|
|
|
|
if (!self) {
|
|
|
|
for ( other = world; ( other = find( other, classname, "spectator" ) ); ) {
|
|
|
|
if ( clienttype( other ) == CLIENTTYPE_REAL ) {
|
|
|
|
self = other;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-22 22:53:43 -08:00
|
|
|
pl = (player)self;
|
2020-10-06 05:42:46 -07:00
|
|
|
|
|
|
|
/* give the game-mode a chance to override us */
|
2021-02-14 12:05:10 -08:00
|
|
|
if (g_ents_initialized)
|
|
|
|
if (g_grMode.ConsoleCommand(pl, cmd) == TRUE)
|
2021-05-10 02:33:31 -07:00
|
|
|
return (1);
|
2020-10-06 05:42:46 -07:00
|
|
|
|
|
|
|
/* time to handle commands that apply to all games */
|
|
|
|
tokenize(cmd);
|
|
|
|
switch (argv(0)) {
|
Add a whole bunch of new bot features, including bot profiles.
Developers: the internal class `bot` is now `NSBot`, and an entityDef
of the old name will be instantiated instead.
Override defs/bot.def in your mod and make it use any custom spawnclass
you wish. Now games don't have to override `addbot` or `bot_add` inside
the multiplayer game rules.
There's also more console commands. Clients now have access to:
addBot, killAllBots, killClass [classname], killMovables, trigger [targetname], input [entnum] [input] [data], listBotProfiles, listTargets, teleport [targetname], teleportToClass [classname], respawnEntities, spawn
2024-01-26 19:22:14 -08:00
|
|
|
case "addBot":
|
2024-03-02 00:40:08 -08:00
|
|
|
string botProfile = strtolower(argv(1));
|
|
|
|
float teamValue = stof(argv(2));
|
|
|
|
float spawnDelay = stof(argv(3));
|
|
|
|
string newName = argv(4);
|
|
|
|
Bot_AddBot_f(botProfile, teamValue, spawnDelay, newName);
|
Add a whole bunch of new bot features, including bot profiles.
Developers: the internal class `bot` is now `NSBot`, and an entityDef
of the old name will be instantiated instead.
Override defs/bot.def in your mod and make it use any custom spawnclass
you wish. Now games don't have to override `addbot` or `bot_add` inside
the multiplayer game rules.
There's also more console commands. Clients now have access to:
addBot, killAllBots, killClass [classname], killMovables, trigger [targetname], input [entnum] [input] [data], listBotProfiles, listTargets, teleport [targetname], teleportToClass [classname], respawnEntities, spawn
2024-01-26 19:22:14 -08:00
|
|
|
break;
|
|
|
|
case "killAllBots":
|
|
|
|
Bot_KillAllBots();
|
|
|
|
break;
|
|
|
|
case "resetAllBotsGoals":
|
|
|
|
Bot_ResetAllBotsGoals();
|
|
|
|
break;
|
|
|
|
case "listBotProfiles":
|
|
|
|
Bot_ListBotProfiles_f();
|
|
|
|
break;
|
|
|
|
case "killClass":
|
|
|
|
string targetClass;
|
|
|
|
targetClass = argv(1);
|
|
|
|
|
|
|
|
if (targetClass)
|
|
|
|
for (entity a = world; (a = find(a, ::classname, targetClass));) {
|
|
|
|
NSEntity t = (NSEntity)a;
|
|
|
|
t.Destroy();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "killMovables":
|
|
|
|
for (entity a = world; (a = findfloat(a, ::movetype, MOVETYPE_PHYSICS));) {
|
|
|
|
NSEntity t = (NSEntity)a;
|
|
|
|
t.Destroy();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "trigger":
|
2020-10-06 05:42:46 -07:00
|
|
|
string targ;
|
|
|
|
targ = argv(1);
|
|
|
|
|
|
|
|
if (targ)
|
Add a whole bunch of new bot features, including bot profiles.
Developers: the internal class `bot` is now `NSBot`, and an entityDef
of the old name will be instantiated instead.
Override defs/bot.def in your mod and make it use any custom spawnclass
you wish. Now games don't have to override `addbot` or `bot_add` inside
the multiplayer game rules.
There's also more console commands. Clients now have access to:
addBot, killAllBots, killClass [classname], killMovables, trigger [targetname], input [entnum] [input] [data], listBotProfiles, listTargets, teleport [targetname], teleportToClass [classname], respawnEntities, spawn
2024-01-26 19:22:14 -08:00
|
|
|
for (entity a = world; (a = find(a, ::targetname, targ));) {
|
2021-10-19 16:18:36 -07:00
|
|
|
NSEntity t = (NSEntity)a;
|
2020-10-06 05:42:46 -07:00
|
|
|
|
|
|
|
if (t.Trigger)
|
|
|
|
t.Trigger(self, TRIG_TOGGLE);
|
|
|
|
}
|
|
|
|
break;
|
2023-09-25 16:12:32 -07:00
|
|
|
case "input":
|
|
|
|
float entNum = stof(argv(1));
|
|
|
|
string inputName = argv(2);
|
|
|
|
string inputData = argv(3);
|
|
|
|
NSEntity inputTarget = (NSEntity)edict_num(entNum);
|
|
|
|
|
|
|
|
if (inputTarget) {
|
|
|
|
inputTarget.Input(self, inputName, inputData);
|
|
|
|
print(sprintf("Sending input to %d, %S: %S\n", entNum, inputName, inputData));
|
|
|
|
}
|
|
|
|
break;
|
Add a whole bunch of new bot features, including bot profiles.
Developers: the internal class `bot` is now `NSBot`, and an entityDef
of the old name will be instantiated instead.
Override defs/bot.def in your mod and make it use any custom spawnclass
you wish. Now games don't have to override `addbot` or `bot_add` inside
the multiplayer game rules.
There's also more console commands. Clients now have access to:
addBot, killAllBots, killClass [classname], killMovables, trigger [targetname], input [entnum] [input] [data], listBotProfiles, listTargets, teleport [targetname], teleportToClass [classname], respawnEntities, spawn
2024-01-26 19:22:14 -08:00
|
|
|
case "listTargets":
|
|
|
|
for (entity a = world; (a = findfloat(a, ::identity, 1));) {
|
|
|
|
if (a.targetname) {
|
|
|
|
print(sprintf("%d: %s (%s)\n", num_for_edict(a), a.targetname, a.classname));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "teleport":
|
|
|
|
static entity targetFinder;
|
|
|
|
targetFinder = find(targetFinder, ::targetname, argv(1));
|
|
|
|
|
|
|
|
/* try at least one more time to skip world */
|
|
|
|
if (!targetFinder)
|
|
|
|
targetFinder = find(targetFinder, ::targetname, argv(1));
|
|
|
|
|
|
|
|
if (targetFinder)
|
|
|
|
setorigin(pl, targetFinder.origin);
|
|
|
|
break;
|
|
|
|
case "teleportToClass":
|
2022-07-25 09:27:19 -07:00
|
|
|
static entity finder;
|
|
|
|
finder = find(finder, ::classname, argv(1));
|
|
|
|
|
Add a whole bunch of new bot features, including bot profiles.
Developers: the internal class `bot` is now `NSBot`, and an entityDef
of the old name will be instantiated instead.
Override defs/bot.def in your mod and make it use any custom spawnclass
you wish. Now games don't have to override `addbot` or `bot_add` inside
the multiplayer game rules.
There's also more console commands. Clients now have access to:
addBot, killAllBots, killClass [classname], killMovables, trigger [targetname], input [entnum] [input] [data], listBotProfiles, listTargets, teleport [targetname], teleportToClass [classname], respawnEntities, spawn
2024-01-26 19:22:14 -08:00
|
|
|
/* try at least one more time to skip world */
|
|
|
|
if (!finder)
|
|
|
|
finder = find(finder, ::classname, argv(1));
|
|
|
|
|
2022-07-25 09:27:19 -07:00
|
|
|
if (finder)
|
|
|
|
setorigin(pl, finder.origin);
|
|
|
|
break;
|
2024-03-02 00:40:08 -08:00
|
|
|
case "renetworkEntities":
|
|
|
|
for (entity a = world; (a = findfloat(a, ::identity, 1));) {
|
|
|
|
NSEntity ent = (NSEntity)a;
|
|
|
|
ent.SendFlags = -1;
|
|
|
|
}
|
|
|
|
break;
|
Add a whole bunch of new bot features, including bot profiles.
Developers: the internal class `bot` is now `NSBot`, and an entityDef
of the old name will be instantiated instead.
Override defs/bot.def in your mod and make it use any custom spawnclass
you wish. Now games don't have to override `addbot` or `bot_add` inside
the multiplayer game rules.
There's also more console commands. Clients now have access to:
addBot, killAllBots, killClass [classname], killMovables, trigger [targetname], input [entnum] [input] [data], listBotProfiles, listTargets, teleport [targetname], teleportToClass [classname], respawnEntities, spawn
2024-01-26 19:22:14 -08:00
|
|
|
case "respawnEntities":
|
2021-02-14 12:05:10 -08:00
|
|
|
for (entity a = world; (a = findfloat(a, ::identity, 1));) {
|
2021-10-19 16:18:36 -07:00
|
|
|
NSEntity ent = (NSEntity)a;
|
2021-02-14 12:05:10 -08:00
|
|
|
ent.Respawn();
|
|
|
|
}
|
2022-07-17 20:45:50 -07:00
|
|
|
break;
|
|
|
|
case "spawn":
|
2023-09-27 13:01:30 -07:00
|
|
|
NSEntity unit = EntityDef_CreateClassname(argv(1));
|
|
|
|
makevectors(pl.v_angle);
|
|
|
|
traceline(pl.origin, pl.origin + (v_forward * 1024), MOVE_NORMAL, pl);
|
2022-07-17 20:45:50 -07:00
|
|
|
setorigin(unit, trace_endpos);
|
2021-02-14 12:05:10 -08:00
|
|
|
break;
|
2020-12-22 22:53:43 -08:00
|
|
|
#ifdef BOT_INCLUDED
|
2020-12-25 22:27:34 -08:00
|
|
|
case "way":
|
|
|
|
Way_Cmd();
|
2020-12-22 22:53:43 -08:00
|
|
|
break;
|
|
|
|
#endif
|
2023-11-17 18:54:46 -08:00
|
|
|
case "listSoundDef":
|
|
|
|
Sound_DebugList();
|
|
|
|
break;
|
2020-10-06 05:42:46 -07:00
|
|
|
default:
|
2021-05-10 02:33:31 -07:00
|
|
|
return (0);
|
2020-10-06 05:42:46 -07:00
|
|
|
}
|
2021-05-10 02:33:31 -07:00
|
|
|
return (1);
|
2019-01-20 18:00:14 -08:00
|
|
|
}
|
2020-09-27 05:25:10 -07:00
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Returns TRUE if the server can pause the server when the 'pause' command
|
2021-01-06 04:58:37 -08:00
|
|
|
is being executed.
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
float
|
|
|
|
SV_ShouldPause(float newstatus)
|
2020-09-27 05:25:10 -07:00
|
|
|
{
|
2021-01-06 03:38:35 -08:00
|
|
|
if (serverkeyfloat("background") == 1)
|
2021-05-10 02:33:31 -07:00
|
|
|
return (0);
|
2021-01-06 03:38:35 -08:00
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
if (cvar("pausable") == 1)
|
2021-05-10 02:33:31 -07:00
|
|
|
return (1);
|
2021-01-06 04:58:37 -08:00
|
|
|
|
2021-01-06 03:38:35 -08:00
|
|
|
if (cvar("sv_playerslots") > 1)
|
2021-05-10 02:33:31 -07:00
|
|
|
return (0);
|
2020-09-27 05:25:10 -07:00
|
|
|
|
|
|
|
return newstatus;
|
|
|
|
}
|
2021-05-28 01:26:42 -07:00
|
|
|
|
2022-08-10 14:24:06 -07:00
|
|
|
//#define REEDICT 1
|
2022-10-10 12:30:31 -07:00
|
|
|
|
|
|
|
/** Called by the engine when we're loading a savegame file.
|
|
|
|
|
|
|
|
This deals with the de and re-allocation of all map entities from
|
|
|
|
the passed file handle.
|
|
|
|
*/
|
2021-05-28 01:26:42 -07:00
|
|
|
void
|
2022-01-06 19:27:08 -08:00
|
|
|
SV_PerformLoad(float fh, float entcount, float playerslots)
|
2021-05-28 01:26:42 -07:00
|
|
|
{
|
2022-08-10 14:24:06 -07:00
|
|
|
entity e = world;
|
2021-05-28 01:26:42 -07:00
|
|
|
entity eold;
|
|
|
|
string l;
|
2022-08-10 14:24:06 -07:00
|
|
|
float n = 0;
|
2021-11-09 08:33:16 -08:00
|
|
|
NSEntity loadent = __NULL__;
|
2022-08-10 14:24:06 -07:00
|
|
|
int num_loaded = 0i;
|
2021-05-28 01:26:42 -07:00
|
|
|
|
2024-03-02 00:40:08 -08:00
|
|
|
InitPrint("Loading Existing Save");
|
2022-08-10 14:24:06 -07:00
|
|
|
g_isloading = true;
|
|
|
|
|
2023-03-23 17:04:34 -07:00
|
|
|
#if 0
|
2022-08-10 14:24:06 -07:00
|
|
|
/* mark anything else for deletion */
|
|
|
|
while ((e=nextent(e))) {
|
|
|
|
if (edict_num(1) != e) {
|
|
|
|
if (e.identity != 0) {
|
|
|
|
e.think = Util_Destroy;
|
|
|
|
e.nextthink = time + 0.05f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-23 17:04:34 -07:00
|
|
|
#endif
|
2021-10-21 14:30:42 -07:00
|
|
|
|
2021-10-22 12:20:22 -07:00
|
|
|
#ifdef REEDICT
|
2021-05-28 01:26:42 -07:00
|
|
|
while ((e=nextent(e))) {
|
|
|
|
if (edict_num(1) != e)
|
2024-03-04 19:55:30 -08:00
|
|
|
if (e.identity) {
|
|
|
|
NSEntity toRemove = (NSEntity)e;
|
2024-03-04 18:51:07 -08:00
|
|
|
toRemove.Destroy();
|
|
|
|
} else {
|
|
|
|
remove(e);
|
|
|
|
}
|
2021-05-28 01:26:42 -07:00
|
|
|
}
|
2021-10-22 12:20:22 -07:00
|
|
|
#else
|
2021-11-09 08:33:16 -08:00
|
|
|
e = world;
|
2021-10-22 12:20:22 -07:00
|
|
|
#endif
|
2021-05-28 01:26:42 -07:00
|
|
|
|
2021-10-21 14:30:42 -07:00
|
|
|
/* read line per line of our file handle */
|
2021-05-28 01:26:42 -07:00
|
|
|
while ((l=fgets(fh))) {
|
2021-10-22 12:20:22 -07:00
|
|
|
float args = tokenize_console(l);
|
2021-05-28 01:26:42 -07:00
|
|
|
|
|
|
|
if (!args)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (argv(0) == "ENTITY") {
|
|
|
|
string cname;
|
2022-08-10 14:24:06 -07:00
|
|
|
cname = argv(2);
|
2021-10-22 12:20:22 -07:00
|
|
|
#ifndef REEDICT
|
2021-05-28 01:26:42 -07:00
|
|
|
n = stof(argv(1));
|
|
|
|
e = edict_num(n);
|
2024-03-02 00:40:08 -08:00
|
|
|
NSLog("Creating %s (edict %d)", cname, n);
|
2021-10-22 12:20:22 -07:00
|
|
|
#else
|
|
|
|
entity e = spawn();
|
|
|
|
#endif
|
2021-10-22 11:51:51 -07:00
|
|
|
loadent = __NULL__;
|
2021-05-28 01:26:42 -07:00
|
|
|
|
|
|
|
__fullspawndata = fgets(fh);
|
|
|
|
|
2023-07-07 23:43:12 -07:00
|
|
|
eold = self;
|
|
|
|
self = e;
|
2021-05-28 01:26:42 -07:00
|
|
|
|
2023-07-07 23:43:12 -07:00
|
|
|
/* check for entityDef first */
|
|
|
|
if (EntityDef_SpawnClassname(cname)) {
|
|
|
|
e.classname = cname;
|
|
|
|
loadent = (NSEntity)e;
|
|
|
|
self = eold;
|
|
|
|
} else if (isfunction(strcat("spawnfunc_", cname))) {
|
|
|
|
/* call the constructor if one is present, init the default fields */
|
|
|
|
e.classname = cname;
|
2021-05-28 01:26:42 -07:00
|
|
|
callfunction(strcat("spawnfunc_", cname));
|
|
|
|
e.classname = cname;
|
2021-10-22 11:51:51 -07:00
|
|
|
loadent = (NSEntity)e;
|
2021-05-28 01:26:42 -07:00
|
|
|
self = eold;
|
|
|
|
} else {
|
2024-03-02 00:40:08 -08:00
|
|
|
NSError("Could not spawn %s", cname);
|
2024-03-04 19:55:30 -08:00
|
|
|
if (e.identity) {
|
|
|
|
NSEntity toRemove = (NSEntity)e;
|
2024-03-04 18:51:07 -08:00
|
|
|
toRemove.Destroy();
|
|
|
|
} else {
|
|
|
|
remove(e);
|
|
|
|
}
|
2023-07-07 23:43:12 -07:00
|
|
|
self = eold;
|
2021-05-28 01:26:42 -07:00
|
|
|
continue;
|
|
|
|
}
|
2021-10-21 14:30:42 -07:00
|
|
|
} else if (argv(0) == "{") {
|
|
|
|
} else if (argv(0) == "}") {
|
2021-10-22 12:20:22 -07:00
|
|
|
if (loadent) {
|
2022-03-31 00:30:15 -07:00
|
|
|
loadent.RestoreComplete();
|
2022-08-10 14:24:06 -07:00
|
|
|
num_loaded++;
|
2024-03-02 00:40:08 -08:00
|
|
|
NSLog("completed %s (edict %d)", loadent.classname, n);
|
2021-10-22 12:20:22 -07:00
|
|
|
loadent = __NULL__;
|
|
|
|
}
|
|
|
|
} else {
|
2021-10-22 11:51:51 -07:00
|
|
|
if (loadent) {
|
|
|
|
int c = tokenize(l);
|
|
|
|
|
2021-10-22 12:20:22 -07:00
|
|
|
if (c == 2) {
|
2021-10-22 11:51:51 -07:00
|
|
|
loadent.Restore(argv(0), argv(1));
|
2022-08-10 14:24:06 -07:00
|
|
|
//print(sprintf("%s %s\n", argv(0), argv(1)));
|
2021-10-22 12:20:22 -07:00
|
|
|
}
|
2021-10-22 11:51:51 -07:00
|
|
|
}
|
2021-05-28 01:26:42 -07:00
|
|
|
}
|
|
|
|
}
|
2022-08-10 14:24:06 -07:00
|
|
|
|
2024-03-02 00:40:08 -08:00
|
|
|
NSLog("...loaded %i entities", num_loaded);
|
2022-08-10 14:24:06 -07:00
|
|
|
g_isloading = false;
|
2021-05-28 01:26:42 -07:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called when we are saving our game. We only get passed a file handle
|
|
|
|
to work with and dump entity data as well as some global info directly
|
|
|
|
into it.
|
|
|
|
*/
|
2021-05-28 01:26:42 -07:00
|
|
|
void
|
2022-01-06 19:27:08 -08:00
|
|
|
SV_PerformSave(float fh, float entcount, float playerslots)
|
2021-05-28 01:26:42 -07:00
|
|
|
{
|
|
|
|
float i = 0;
|
2022-08-10 14:24:06 -07:00
|
|
|
int num_saved = 0i;
|
2021-05-28 01:26:42 -07:00
|
|
|
entity e;
|
|
|
|
|
2024-03-02 00:40:08 -08:00
|
|
|
InitPrint("Performing Save");
|
2022-02-23 01:17:05 -08:00
|
|
|
|
2022-01-06 19:27:08 -08:00
|
|
|
for (i = 0; i < entcount; i++) {
|
2021-10-21 14:30:42 -07:00
|
|
|
NSEntity willsave;
|
2021-05-28 01:26:42 -07:00
|
|
|
e = edict_num(i);
|
|
|
|
|
|
|
|
if (e==world && i)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (wasfreed(e))
|
|
|
|
continue;
|
|
|
|
|
2021-10-21 14:30:42 -07:00
|
|
|
if (e.identity == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
willsave = (NSEntity)e;
|
|
|
|
fputs(fh, sprintf("ENTITY \"%d\" %S\n", i, willsave.classname));
|
2021-10-22 11:51:51 -07:00
|
|
|
fputs(fh, "{\n");
|
2021-10-21 14:30:42 -07:00
|
|
|
willsave.Save(fh);
|
|
|
|
fputs(fh, "}\n");
|
2022-08-10 14:24:06 -07:00
|
|
|
num_saved++;
|
2021-05-28 01:26:42 -07:00
|
|
|
}
|
|
|
|
fclose(fh);
|
2024-03-02 00:40:08 -08:00
|
|
|
NSLog("saved %i entities", num_saved);
|
2021-06-16 02:52:36 -07:00
|
|
|
}
|
|
|
|
|
2022-10-10 12:30:31 -07:00
|
|
|
/** Called by the engine to check with us if a spawn function exists.
|
|
|
|
|
|
|
|
The `self` global refers to an already allocated entity, which we have
|
|
|
|
to remove in case we won't initialize it.
|
|
|
|
*/
|
2021-06-16 02:52:36 -07:00
|
|
|
void
|
|
|
|
CheckSpawn(void() spawnfunc)
|
|
|
|
{
|
2024-03-02 00:40:08 -08:00
|
|
|
string desiredClass = self.classname;
|
2023-04-22 02:37:17 -07:00
|
|
|
|
2024-03-02 00:40:08 -08:00
|
|
|
if (MapTweak_EntitySpawn(self)) {
|
|
|
|
self._mapspawned = true;
|
|
|
|
self.classname = desiredClass;
|
|
|
|
} else if (EntityDef_SpawnClassname(desiredClass)) {
|
|
|
|
self._mapspawned = true;
|
|
|
|
} else if (spawnfunc) {
|
2021-06-16 02:52:36 -07:00
|
|
|
spawnfunc();
|
2024-03-02 00:40:08 -08:00
|
|
|
self.classname = desiredClass;
|
2022-04-06 16:21:07 -07:00
|
|
|
self._mapspawned = true;
|
2023-03-18 17:49:12 -07:00
|
|
|
g_ent_spawned++;
|
2022-05-04 21:31:37 -07:00
|
|
|
} else {
|
2024-03-02 00:40:08 -08:00
|
|
|
NSError("Unable to spawn %s", desiredClass);
|
2021-06-16 02:56:28 -07:00
|
|
|
remove(self);
|
2022-05-04 21:31:37 -07:00
|
|
|
}
|
2023-01-06 13:23:48 -08:00
|
|
|
|
|
|
|
/* check if this entity was meant to spawn on the client-side only */
|
|
|
|
if (self.identity) {
|
|
|
|
NSEntity ent = (NSEntity)self;
|
|
|
|
|
2023-03-18 17:49:12 -07:00
|
|
|
if (ent.CanSpawn(false) == false) {
|
2023-01-06 13:23:48 -08:00
|
|
|
ent.Destroy();
|
2023-03-18 17:49:12 -07:00
|
|
|
g_ent_spawned--;
|
|
|
|
}
|
2023-01-06 13:23:48 -08:00
|
|
|
}
|
2024-03-02 00:40:08 -08:00
|
|
|
}
|