2019-08-31 19:18:15 -07:00
|
|
|
/*
|
2021-01-06 04:58:37 -08:00
|
|
|
* Copyright (c) 2016-2021 Marco Hladik <marco@icculus.org>
|
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.
|
|
|
|
*/
|
2019-01-18 20:50:25 -08:00
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
StartFrame
|
2019-01-18 20:50:25 -08:00
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
Called once every single frame.
|
|
|
|
=================
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
ClientConnect
|
|
|
|
|
|
|
|
Called when the client-slot is being prepared for a player.
|
|
|
|
The client may not fully spawn into the world (yet), as they're still
|
|
|
|
loading or receiving packets.
|
|
|
|
|
|
|
|
The 'self' global is the connecting client in question.
|
|
|
|
=================
|
|
|
|
*/
|
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;
|
|
|
|
|
2021-01-06 06:54:17 -08:00
|
|
|
#ifdef BOT_INCLUDED
|
|
|
|
if (clienttype(self) == CLIENTTYPE_BOT) {
|
|
|
|
spawnfunc_bot();
|
|
|
|
} 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)
|
|
|
|
g_grMode.PlayerConnect((base_player)self);
|
|
|
|
|
2020-11-23 12:39:40 -08:00
|
|
|
for (entity a = world; (a = find(a, ::classname, "player"));)
|
|
|
|
playercount++;
|
|
|
|
|
2021-02-14 12:05:10 -08:00
|
|
|
/* we're the only one. respawn all entities */
|
2020-11-23 12:39:40 -08:00
|
|
|
if (playercount == 1) {
|
|
|
|
for (entity a = world; (a = findfloat(a, ::identity, 1));) {
|
2021-10-19 16:18:36 -07:00
|
|
|
NSEntity caw = (NSEntity)a;
|
2020-11-23 12:39:40 -08:00
|
|
|
caw.Respawn();
|
|
|
|
}
|
|
|
|
Nodes_Init();
|
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
ClientDisconnect
|
|
|
|
|
|
|
|
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.
|
|
|
|
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)
|
|
|
|
g_grMode.PlayerDisconnect((base_player)self);
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
ClientKill
|
|
|
|
|
|
|
|
Called by the 'kill' command.
|
|
|
|
The 'self' global is the client issueing the command.
|
|
|
|
=================
|
|
|
|
*/
|
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)
|
|
|
|
g_grMode.PlayerKill((base_player)self);
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
SpectatorThink
|
|
|
|
|
|
|
|
Run every frame on every spectator.
|
|
|
|
The 'self' global refers to one of any given amount of spectators.
|
|
|
|
=================
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
SpectatorThink(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2019-01-20 18:00:14 -08:00
|
|
|
Game_SpectatorThink();
|
2021-03-23 23:50:30 -07:00
|
|
|
|
|
|
|
if (self.classname == "spectator") {
|
|
|
|
spectator spec = (spectator)self;
|
|
|
|
spec.PreFrame();
|
|
|
|
spec.PostFrame();
|
|
|
|
return;
|
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
2021-01-06 04:58:37 -08:00
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
SpectatorConnect
|
|
|
|
|
|
|
|
Called when a spectator joins the server.
|
|
|
|
The 'self' global is the connecting spectator in question.
|
|
|
|
=================
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
SpectatorConnect(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2019-01-20 18:00:14 -08:00
|
|
|
Game_SpectatorConnect();
|
2021-03-23 23:50:30 -07:00
|
|
|
spawnfunc_spectator();
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
2021-01-06 04:58:37 -08:00
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
SpectatorDisconnect
|
|
|
|
|
|
|
|
Called when a spectator leaves the server.
|
|
|
|
The 'self' global is the leaving spectator in question.
|
|
|
|
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
|
|
|
{
|
2019-01-20 18:00:14 -08:00
|
|
|
Game_SpectatorDisconnect();
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
PutClientInServer
|
|
|
|
|
|
|
|
Called when a player enters the game, having fully connected and loaded into
|
|
|
|
the session.
|
|
|
|
The 'self' global is the player in question.
|
|
|
|
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)
|
|
|
|
g_grMode.PlayerSpawn((base_player)self);
|
|
|
|
|
|
|
|
Plugin_PlayerEntered((base_player)self);
|
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);
|
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
PlayerPreThink
|
|
|
|
|
|
|
|
Run before physics have taken place.
|
|
|
|
The 'self' global refers to a single client, as this function is called
|
|
|
|
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") {
|
|
|
|
//spectator spec = (spectator)self;
|
|
|
|
//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) {
|
|
|
|
((bot)self).PreFrame();
|
|
|
|
}
|
|
|
|
#endif
|
2021-01-16 08:34:04 -08:00
|
|
|
|
2021-02-14 12:05:10 -08:00
|
|
|
if (g_ents_initialized)
|
|
|
|
g_grMode.PlayerPreFrame((base_player)self);
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
PlayerPostThink
|
|
|
|
|
|
|
|
Run after physics have taken place.
|
|
|
|
The 'self' global refers to a single client, as this function is called
|
|
|
|
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) {
|
|
|
|
((bot)self).PostFrame();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-02-27 17:31:27 -08:00
|
|
|
if (g_ents_initialized) {
|
|
|
|
player pl = (player)self;
|
2021-02-14 12:05:10 -08:00
|
|
|
g_grMode.PlayerPostFrame((base_player)self);
|
2021-02-27 17:31:27 -08:00
|
|
|
pl.EvaluateEntity();
|
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
SetNewParms
|
|
|
|
|
|
|
|
Called when we spawn in a new map (both single and multiplayer) with no level
|
|
|
|
change ever having taken place.
|
|
|
|
The 'self' global does not refer to anything.
|
|
|
|
=================
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
SetNewParms(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2020-04-19 03:02:05 -07:00
|
|
|
iprint("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
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
SetChangeParms
|
|
|
|
|
|
|
|
Called whenever a single-player level change is about to happen, carrying
|
|
|
|
over data from one level to the next. This is not called with the 'map' command.
|
|
|
|
|
|
|
|
The 'self' global refers to a client that's partaking in the level-change.
|
|
|
|
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
|
|
|
{
|
2020-04-19 03:02:05 -07:00
|
|
|
iprint("Setting Level-Change Parameters");
|
2021-02-14 12:05:10 -08:00
|
|
|
|
|
|
|
if (g_ents_initialized)
|
|
|
|
g_grMode.LevelChangeParms((base_player)self);
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
SV_RunClientCommand
|
|
|
|
|
|
|
|
Run whenever an input packet by a client has been received.
|
|
|
|
|
|
|
|
The 'self' global is the entity having sent the input packet,
|
|
|
|
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
|
|
|
{
|
2021-03-23 23:50:30 -07:00
|
|
|
if (self.classname == "spectator") {
|
|
|
|
spectator spec = (spectator)self;
|
|
|
|
spec.RunClientCommand();
|
|
|
|
}
|
|
|
|
|
2021-01-16 08:34:04 -08:00
|
|
|
if (self.classname != "player") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-22 22:53:43 -08:00
|
|
|
#ifdef BOT_INCLUDED
|
2021-01-06 04:58:37 -08:00
|
|
|
/* wait a few seconds, as we may not have been spawned yet */
|
2020-12-22 22:53:43 -08:00
|
|
|
if (clienttype(self) == CLIENTTYPE_BOT) {
|
|
|
|
((bot)self).RunAI();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-08-14 02:43:43 -07:00
|
|
|
if (!Plugin_RunClientCommand()) {
|
|
|
|
Game_RunClientCommand();
|
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
SV_ParseClientCommand
|
|
|
|
|
|
|
|
Any 'cmd' from the client get sent here and handled.
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (newcmd == __NULL__)
|
2019-08-14 02:43:43 -07:00
|
|
|
Game_ParseClientCommand(cmd);
|
2020-11-06 04:04:22 -08:00
|
|
|
else
|
|
|
|
Game_ParseClientCommand(newcmd);
|
2021-03-23 23:50:30 -07:00
|
|
|
|
|
|
|
tokenize(cmd);
|
|
|
|
|
|
|
|
switch (argv(0)) {
|
|
|
|
case "spectate":
|
|
|
|
if (self.classname != "player")
|
|
|
|
break;
|
|
|
|
ClientKill();
|
|
|
|
spawnfunc_spectator();
|
|
|
|
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)));
|
|
|
|
}
|
2021-03-23 23:50:30 -07:00
|
|
|
break;
|
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
init
|
|
|
|
|
|
|
|
Called when the QC module gets loaded. No entities exist yet.
|
|
|
|
=================
|
|
|
|
*/
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
init(float prevprogs)
|
2020-04-13 18:12:09 -07:00
|
|
|
{
|
2020-04-19 03:02:05 -07:00
|
|
|
iprint("Initializing Server-Module");
|
2020-04-13 18:12:09 -07:00
|
|
|
Plugin_Init();
|
2021-09-21 11:33:09 -07:00
|
|
|
PropData_Init();
|
2021-10-15 09:06:04 -07:00
|
|
|
SurfData_Init();
|
2020-04-13 18:12:09 -07:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
init_respawn
|
|
|
|
|
|
|
|
Called inside initents() to make sure the entities have their Respawn()
|
|
|
|
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
|
|
|
{
|
2020-04-19 03:02:05 -07:00
|
|
|
iprint("Respawning Entities");
|
2021-02-14 12:05:10 -08:00
|
|
|
|
|
|
|
if (g_ents_initialized)
|
|
|
|
g_grMode.InitPostEnts();
|
2020-04-14 07:19:25 -07:00
|
|
|
|
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-04-13 18:12:09 -07:00
|
|
|
ent.Respawn();
|
|
|
|
}
|
|
|
|
remove(self);
|
|
|
|
}
|
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
initents
|
|
|
|
|
|
|
|
???
|
|
|
|
=================
|
|
|
|
*/
|
2021-02-22 08:27:22 -08:00
|
|
|
.string skyname;
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
initents(void)
|
2019-01-18 20:50:25 -08:00
|
|
|
{
|
2020-04-19 03:02:05 -07:00
|
|
|
iprint("Initializing Entities");
|
|
|
|
|
2020-07-09 01:42:34 -07:00
|
|
|
/* sound shader init */
|
2020-04-12 10:40:09 -07:00
|
|
|
Sound_Init();
|
2019-01-18 20:50:25 -08:00
|
|
|
|
2021-04-06 01:43:38 -07:00
|
|
|
/* only bother doing so on Half-Life BSP */
|
2020-12-12 15:56:11 -08:00
|
|
|
if (serverkeyfloat("*bspversion") == BSPVER_HL) {
|
2021-04-06 01:43:38 -07:00
|
|
|
HLMaterials_Init();
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
|
|
|
|
2019-01-20 18:00:14 -08:00
|
|
|
PMove_Init();
|
2019-03-06 05:11:23 -08:00
|
|
|
|
2020-07-09 01:42:34 -07:00
|
|
|
/* TODO: turn these effects into sound shaders */
|
2019-03-01 14:35:28 -08:00
|
|
|
precache_sound("weapons/explode3.wav");
|
|
|
|
precache_sound("weapons/explode4.wav");
|
|
|
|
precache_sound("weapons/explode5.wav");
|
|
|
|
precache_sound("debris/glass1.wav");
|
|
|
|
precache_sound("debris/glass2.wav");
|
|
|
|
precache_sound("debris/glass3.wav");
|
|
|
|
precache_sound("debris/wood1.wav");
|
|
|
|
precache_sound("debris/wood2.wav");
|
|
|
|
precache_sound("debris/wood3.wav");
|
|
|
|
precache_sound("debris/metal1.wav");
|
|
|
|
precache_sound("debris/metal2.wav");
|
|
|
|
precache_sound("debris/metal3.wav");
|
|
|
|
precache_sound("debris/flesh1.wav");
|
|
|
|
precache_sound("debris/flesh2.wav");
|
|
|
|
precache_sound("debris/flesh3.wav");
|
|
|
|
precache_sound("debris/flesh5.wav");
|
|
|
|
precache_sound("debris/flesh6.wav");
|
|
|
|
precache_sound("debris/flesh7.wav");
|
|
|
|
precache_sound("debris/concrete1.wav");
|
|
|
|
precache_sound("debris/concrete2.wav");
|
|
|
|
precache_sound("debris/concrete3.wav");
|
|
|
|
|
2020-10-30 05:29:37 -07:00
|
|
|
Footsteps_Init();
|
2020-04-03 03:12:11 -07:00
|
|
|
|
2019-08-20 08:53:57 -07:00
|
|
|
precache_sound("player/pl_fallpain3.wav");
|
2019-03-01 14:35:28 -08:00
|
|
|
precache_sound("items/9mmclip1.wav");
|
|
|
|
precache_sound("items/gunpickup2.wav");
|
|
|
|
precache_sound("common/wpn_select.wav");
|
|
|
|
precache_sound("common/wpn_denyselect.wav");
|
|
|
|
precache_sound("player/sprayer.wav");
|
|
|
|
precache_sound("items/flashlight1.wav");
|
2019-09-06 23:39:28 -07:00
|
|
|
precache_sound("common/null.wav");
|
|
|
|
|
2021-03-12 19:24:53 -08:00
|
|
|
Sound_Precache("player.gasplight");
|
|
|
|
Sound_Precache("player.gaspheavy");
|
|
|
|
Sound_Precache("player.waterenter");
|
|
|
|
Sound_Precache("player.waterexit");
|
2021-03-27 01:09:10 -07:00
|
|
|
Sound_Precache("damage_bullet.hit");
|
2021-03-12 19:24:53 -08:00
|
|
|
|
2020-04-13 18:12:09 -07:00
|
|
|
Game_InitRules();
|
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 */
|
2020-03-08 01:59:46 -08:00
|
|
|
entity respawntimer = spawn();
|
|
|
|
respawntimer.think = init_respawn;
|
|
|
|
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();
|
2021-02-14 12:05:10 -08:00
|
|
|
|
|
|
|
g_ents_initialized = TRUE;
|
2019-03-06 05:11:23 -08:00
|
|
|
}
|
2019-03-01 14:35:28 -08:00
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
worldspawn
|
|
|
|
|
|
|
|
The first entity spawn function. You want to make sure to put anything in here
|
|
|
|
that'll affect subsequent initialization of map entities.
|
|
|
|
|
|
|
|
Any find() or similar function will not find any entity but 'world',
|
|
|
|
as they do not exist yet. Keep this in mind.
|
|
|
|
=================
|
|
|
|
*/
|
2020-10-28 00:16:50 -07:00
|
|
|
var int autocvar_sv_levelexec = 1;
|
2021-05-28 01:26:42 -07:00
|
|
|
|
2021-02-14 12:05:10 -08:00
|
|
|
void
|
|
|
|
worldspawn(void)
|
2019-03-06 05:11:23 -08:00
|
|
|
{
|
2020-04-19 03:02:05 -07:00
|
|
|
iprint("Initializing World");
|
2019-03-01 14:35:28 -08:00
|
|
|
lightstyle(0, "m");
|
|
|
|
lightstyle(1, "mmnmmommommnonmmonqnmmo");
|
|
|
|
lightstyle(2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba");
|
|
|
|
lightstyle(3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg");
|
|
|
|
lightstyle(4, "mamamamamama");
|
2020-10-18 06:19:56 -07:00
|
|
|
lightstyle(5, "jklmnopqrstuvwxyzyxwvutsrqponmlkj");
|
2019-03-01 14:35:28 -08:00
|
|
|
lightstyle(6, "nmonqnmomnmomomno");
|
|
|
|
lightstyle(7, "mmmaaaabcdefgmmmmaaaammmaamm");
|
|
|
|
lightstyle(8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa");
|
|
|
|
lightstyle(9, "aaaaaaaazzzzzzzz");
|
|
|
|
lightstyle(10, "mmamammmmammamamaaamammma");
|
|
|
|
lightstyle(11, "abcdefghijklmnopqrrqponmlkjihgfedcba");
|
2020-07-24 16:58:12 -07:00
|
|
|
Skill_Init();
|
2020-10-28 00:16:50 -07:00
|
|
|
|
2021-06-01 03:40:53 -07:00
|
|
|
precache_model("models/error.vvm");
|
|
|
|
|
2020-10-28 00:16:50 -07:00
|
|
|
if (autocvar_sv_levelexec)
|
|
|
|
readcmd(sprintf("exec maps/%s.cfg\n", mapname));
|
2021-02-22 20:55:57 -08:00
|
|
|
|
2021-04-22 03:14:56 -07:00
|
|
|
/* we need to flush this, so that any leftover serverinfo
|
|
|
|
* in the server-config gets overwritten */
|
|
|
|
forceinfokey(world, "skyname", "");
|
|
|
|
|
2021-02-22 20:55:57 -08:00
|
|
|
/* Set the default sky */
|
|
|
|
if (serverkeyfloat("*bspversion") == BSPVER_HL) {
|
|
|
|
if (!self.skyname) {
|
|
|
|
self.skyname = "desert";
|
|
|
|
}
|
|
|
|
forceinfokey(world, "skyname", self.skyname);
|
|
|
|
}
|
2019-01-18 20:50:25 -08:00
|
|
|
}
|
2019-01-20 18:00:14 -08:00
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
ConsoleCmd
|
|
|
|
|
|
|
|
Any command executed on the server (either tty, rcon or 'sv') gets
|
|
|
|
sent here first.
|
|
|
|
|
|
|
|
When returning FALSE the server will interpret the command.
|
|
|
|
Returning TRUE will mark the command as 'resolved'.
|
|
|
|
=================
|
|
|
|
*/
|
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)) {
|
|
|
|
case "trigger_ent":
|
|
|
|
string targ;
|
|
|
|
targ = argv(1);
|
|
|
|
|
|
|
|
if (targ)
|
|
|
|
for (entity a = world; (a = find(a, ::targetname, argv(1)));) {
|
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;
|
2021-02-14 12:05:10 -08:00
|
|
|
case "respawn_ents":
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
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
|
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
|
|
|
|
2021-01-06 04:58:37 -08:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
SV_ShouldPause
|
|
|
|
|
|
|
|
Returns TRUE if the server should pause the game-logic when the 'pause' command
|
|
|
|
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
|
|
|
|
|
|
|
void
|
|
|
|
SV_PerformLoad(float fh)
|
|
|
|
{
|
|
|
|
entity e;
|
|
|
|
entity eold;
|
|
|
|
string l;
|
|
|
|
float n;
|
|
|
|
e = world;
|
|
|
|
|
2021-10-21 14:30:42 -07:00
|
|
|
int inentity;
|
|
|
|
int inworld;
|
|
|
|
|
2021-05-28 01:26:42 -07:00
|
|
|
/*
|
|
|
|
while ((e=nextent(e))) {
|
|
|
|
if (edict_num(1) != e)
|
|
|
|
remove(e);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if 1
|
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))) {
|
|
|
|
float braced = FALSE;
|
|
|
|
float args = tokenize(l);
|
|
|
|
|
|
|
|
if (!args)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (argv(0) == "ENTITY") {
|
|
|
|
string cname;
|
|
|
|
n = stof(argv(1));
|
|
|
|
cname = argv(2);
|
|
|
|
e = edict_num(n);
|
|
|
|
|
|
|
|
__fullspawndata = fgets(fh);
|
|
|
|
|
|
|
|
print(sprintf("Try spawning %s\n", cname));
|
|
|
|
/* call the constructor if one is present, init the default fields */
|
|
|
|
if (isfunction(strcat("spawnfunc_", cname))) {
|
2021-10-21 14:30:42 -07:00
|
|
|
NSEntity willload;
|
2021-05-28 01:26:42 -07:00
|
|
|
e.classname = cname;
|
|
|
|
print(sprintf("I'm actually spawning %s\n", cname));
|
|
|
|
|
2021-10-21 14:30:42 -07:00
|
|
|
eold = self;
|
2021-05-28 01:26:42 -07:00
|
|
|
self = e;
|
|
|
|
callfunction(strcat("spawnfunc_", cname));
|
|
|
|
e.classname = cname;
|
|
|
|
self = eold;
|
2021-10-21 14:30:42 -07:00
|
|
|
inentity = TRUE;
|
2021-05-28 01:26:42 -07:00
|
|
|
} else {
|
|
|
|
print(sprintf("Could not spawn %s\n", cname));
|
|
|
|
remove(e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else if (argv(1) == "GLOBAL") {
|
|
|
|
// TODO
|
2021-10-21 14:30:42 -07:00
|
|
|
} else if (argv(0) == "{") {
|
|
|
|
if (inentity)
|
|
|
|
|
|
|
|
willload = (NSEntity)e;
|
|
|
|
willload.Restore(fh);
|
|
|
|
} else if (argv(0) == "}") {
|
|
|
|
|
2021-05-28 01:26:42 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SV_PerformSave(float fh, float numents)
|
|
|
|
{
|
|
|
|
float i = 0;
|
|
|
|
entity e;
|
|
|
|
|
|
|
|
for (i = 0; i < numents; 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));
|
|
|
|
fputs(fh, "{ ");
|
|
|
|
willsave.Save(fh);
|
|
|
|
fputs(fh, "}\n");
|
2021-05-28 01:26:42 -07:00
|
|
|
}
|
|
|
|
fclose(fh);
|
2021-06-16 02:52:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CheckSpawn(void() spawnfunc)
|
|
|
|
{
|
|
|
|
if (spawnfunc)
|
|
|
|
spawnfunc();
|
|
|
|
else
|
2021-06-16 02:56:28 -07:00
|
|
|
remove(self);
|
2021-06-16 02:52:36 -07:00
|
|
|
}
|