488 lines
11 KiB
C++
Executable File
488 lines
11 KiB
C++
Executable File
#include "ns.h"
|
|
|
|
|
|
#include <sdk_util.h> //useful almost everywhere
|
|
#include <usercmd.h>
|
|
#include <entity_state.h>
|
|
|
|
CSpawn ns_spawnpoints;
|
|
CPlayer g_player[33];
|
|
edict_t *player_edicts[33];
|
|
|
|
BOOL CheckForPublic(const char *publicname);
|
|
|
|
int gmsgHudText2=0;
|
|
int ChangeclassForward = -1;
|
|
int BuiltForward = -1;
|
|
int SpawnForward = -1;
|
|
int TeamForward = -1;
|
|
// Index of last entity hooked in CreateNamedEntity
|
|
int iCreateEntityIndex;
|
|
BOOL iscombat;
|
|
int gmsgScoreInfo=0;
|
|
|
|
// Module is attaching to AMXX
|
|
void OnAmxxAttach()
|
|
{
|
|
MF_AddNatives(ns_misc_natives);
|
|
MF_AddNatives(ns_pdata_natives);
|
|
}
|
|
|
|
// All plugins have loaded (probably around Spawning worldspawn..
|
|
void OnPluginsLoaded()
|
|
{
|
|
|
|
gmsgHudText2 = GET_USER_MSG_ID(&Plugin_info,"HudText2",NULL);
|
|
// Check the map name and see if it's combat or not.
|
|
iscombat=FALSE;
|
|
char mapname[255];
|
|
strcpy(mapname,STRING(gpGlobals->mapname));
|
|
if ((mapname[0]=='c' || mapname[0]=='C') && (mapname[1]=='o' || mapname[1]=='O') && mapname[2]=='_')
|
|
iscombat=TRUE;
|
|
|
|
ChangeclassForward = MF_RegisterForward("client_changeclass", ET_IGNORE, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
|
|
// No sense in this if it's combat..
|
|
if (!iscombat) {
|
|
if (CheckForPublic("client_built")) {
|
|
BuiltForward = MF_RegisterForward("client_built", ET_IGNORE, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
|
|
g_pengfuncsTable_Post->pfnAlertMessage=AlertMessage_Post;
|
|
g_pengfuncsTable->pfnCreateNamedEntity=CreateNamedEntity;
|
|
} else {
|
|
g_pengfuncsTable_Post->pfnAlertMessage=NULL;
|
|
g_pengfuncsTable->pfnCreateNamedEntity=NULL;
|
|
}
|
|
} else {
|
|
// no need for these hooks in co
|
|
g_pengfuncsTable_Post->pfnAlertMessage=NULL;
|
|
g_pengfuncsTable->pfnCreateNamedEntity=NULL;
|
|
}
|
|
SpawnForward = MF_RegisterForward("client_spawn",ET_IGNORE,FP_CELL/*id*/,FP_DONE);
|
|
TeamForward = MF_RegisterForward("client_changeteam",ET_IGNORE,FP_CELL/*id*/,FP_CELL/*new team*/,FP_CELL/*old team*/,FP_DONE);
|
|
}
|
|
|
|
|
|
|
|
int DispatchSpawn(edict_t *pEntity)
|
|
{
|
|
// Everything starting up:
|
|
// - Reset CPlayer classes
|
|
// - Reset CSpawn classes
|
|
if (FStrEq(STRING(pEntity->v.classname),"worldspawn"))
|
|
{
|
|
int i;
|
|
for (i=0;i<=32;i++)
|
|
{
|
|
CPlayer *player = GET_PLAYER_I(i);
|
|
player->Reset();
|
|
}
|
|
ns_spawnpoints.clear();
|
|
}
|
|
else if (FStrEq(STRING(pEntity->v.classname),"info_player_start"))
|
|
{
|
|
// Mark down the ready room spawn point.
|
|
ns_spawnpoints.put(0,pEntity->v.origin);
|
|
}
|
|
else if (FStrEq(STRING(pEntity->v.classname),"info_team_start"))
|
|
{
|
|
// Mark down the team based spawn point.
|
|
ns_spawnpoints.put(pEntity->v.team,pEntity->v.origin);
|
|
}
|
|
RETURN_META_VALUE(MRES_IGNORED, 0);
|
|
}
|
|
|
|
|
|
void ServerActivate(edict_t *pEdictList, int edictCount, int clientMax)
|
|
{
|
|
// Mark down proper edicts (fixes INDEXENT() bug).
|
|
// Reset CPlayer classes (again?)
|
|
for(int i = 1; i <= gpGlobals->maxClients;i++)
|
|
{
|
|
player_edicts[i]=pEdictList + i;
|
|
CPlayer *player = GET_PLAYER_I(i);
|
|
player->edict=pEdictList + i;
|
|
//player->index=i;
|
|
player->pev=&player->edict->v;
|
|
player->oldimpulse=0;
|
|
player->Reset();
|
|
player->connected=false;
|
|
}
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
void PlayerPreThink(edict_t *pEntity)
|
|
{
|
|
CPlayer *player = GET_PLAYER_E(pEntity);
|
|
player->PreThink();
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
void PlayerPreThink_Post(edict_t *pEntity)
|
|
{
|
|
CPlayer *player = GET_PLAYER_E(pEntity);
|
|
player->PreThink_Post();
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
void PlayerPostThink_Post(edict_t *pEntity)
|
|
{
|
|
CPlayer *player = GET_PLAYER_E(pEntity);
|
|
player->PostThink_Post();
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
|
|
// Parse log messages here for any desired information (structure_built, etc.)
|
|
// The following logs are needed:
|
|
// name<CID><AUTHID><TEAM> triggered "structure_built" (type "type") -- client_built
|
|
// name<CID><AUTHID><TEAM> changed role to "class" -- client_changeclass
|
|
void AlertMessage_Post(ALERT_TYPE atype, char *szFmt, ...)
|
|
{
|
|
if (atype != at_logged)
|
|
RETURN_META(MRES_IGNORED);
|
|
va_list LogArg;
|
|
char *sz, *message;
|
|
const char *b;
|
|
char szParm[5][128];
|
|
int argc,len;
|
|
va_start(LogArg, szFmt);
|
|
sz = va_arg(LogArg, char *);
|
|
va_end(LogArg);
|
|
message = sz;
|
|
b=message;
|
|
argc=0;
|
|
// Parse the damn message
|
|
while (*b && *b!='\0' && argc<5)
|
|
{
|
|
|
|
len=0;
|
|
if (*b == '"')
|
|
{
|
|
b++; // Skip over the "
|
|
while (*b && *b != '"' && len < 127)
|
|
{
|
|
szParm[argc][len]=*b;
|
|
b++;
|
|
len++;
|
|
}
|
|
//*szParm='\0';
|
|
szParm[argc][len]='\0';
|
|
if (*b && *b == '"' && *b+1 != '\0' && *b+2 != '\0')
|
|
{
|
|
b+=2;
|
|
argc++;
|
|
}
|
|
else
|
|
{
|
|
argc++;
|
|
break;
|
|
}
|
|
}
|
|
else if (*b == '(')
|
|
{
|
|
b++; // Skip over the (
|
|
while (*b && *b != ')' && len < 127)
|
|
{
|
|
szParm[argc][len]=*b;
|
|
b++;
|
|
len++;
|
|
}
|
|
szParm[argc][len]='\0';
|
|
if (*b && *b == ')' && *b+1 != '\0' && *b+2 != '\0')
|
|
{
|
|
b+=2;
|
|
argc++;
|
|
}
|
|
else
|
|
{
|
|
argc++;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (*b && *b != '"' && *b != '(' && len < 127)
|
|
{
|
|
szParm[argc][len]=*b;
|
|
b++;
|
|
len++;
|
|
}
|
|
szParm[argc][len]='\0';
|
|
if (*b != '"' && *b != '(' && *b != '\0' && *b+1 != '\0' && *b+2 != '\0')
|
|
{
|
|
b+=2;
|
|
argc++;
|
|
}
|
|
else
|
|
{
|
|
argc++;
|
|
if (*b == '\0')
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
/*
|
|
if (argc == 3) // changed role to = 3 long
|
|
{
|
|
if (FStrEq((const char *)szParm[1],"changed role to "))
|
|
{
|
|
int index=LogToIndex(szParm[0]);
|
|
if (!index)
|
|
RETURN_META(MRES_IGNORED);
|
|
CPlayer *player = GET_PLAYER_I(index);
|
|
int iImpulse=0;
|
|
int iClass=1;
|
|
if (INDEXENT2(index)->v.team != 0)
|
|
{
|
|
if (FStrEq((const char *)szParm[2],"gestate"))
|
|
{
|
|
iImpulse = player->oldpev.impulse;
|
|
}
|
|
if (FStrEq((const char *)szParm[2],"none"))
|
|
iClass=0;
|
|
if (iClass > 0)
|
|
{
|
|
ns2amx_changeclass.execute(index,player->oldpev.iuser3,player->pev->iuser3,iImpulse);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else */
|
|
if (argc == 4) // structure_built / structure_destroyed are 4 long
|
|
{
|
|
//"NAME<CID><AUTH><TEAM>" triggered "structure_built" (type "TYPE")
|
|
if (FStrEq((const char *)szParm[2],"structure_built"))
|
|
{
|
|
int index=LogToIndex(szParm[0]);
|
|
if (!index)
|
|
RETURN_META(MRES_IGNORED);
|
|
CPlayer *player = GET_PLAYER_I(index);
|
|
|
|
int iForward=0;
|
|
int iType=player->pev->impulse;
|
|
if (FStrEq((const char *)szParm[3],"type \"team_hive\""))
|
|
{
|
|
iForward=2;
|
|
iCreateEntityIndex=Find_Building_Hive();
|
|
}
|
|
else if (FStrEq((const char *)szParm[3],"type \"offensechamber\""))
|
|
{
|
|
iForward=2;
|
|
}
|
|
else if (FStrEq((const char *)szParm[3],"type \"movementchamber\""))
|
|
{
|
|
iForward=2;
|
|
}
|
|
else if (FStrEq((const char *)szParm[3],"type \"sensorychamber\""))
|
|
{
|
|
iForward=2;
|
|
}
|
|
else if (FStrEq((const char *)szParm[3],"type \"defensechamber\""))
|
|
{
|
|
iForward=2;
|
|
}
|
|
else if (FStrEq((const char *)szParm[3],"type \"alienresourcetower\""))
|
|
{
|
|
iForward=2;
|
|
}
|
|
else
|
|
{
|
|
iForward = 1;
|
|
}
|
|
// ns2amx_built.execute(index,iCreateEntityIndex,iForward,iType);
|
|
if (BuiltForward != -1) {
|
|
MF_ExecuteForward(BuiltForward, index, FStrEq((const char *)szParm[3],"type \"weapon_mine\"") ? 0 : iCreateEntityIndex, iForward, iType);
|
|
}
|
|
iCreateEntityIndex=0;
|
|
}
|
|
}
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
// We hook newly created entities here.
|
|
// This is where we check for client_built created entities.
|
|
edict_t* CreateNamedEntity(int className)
|
|
{
|
|
if (iscombat)
|
|
RETURN_META_VALUE(MRES_IGNORED,0);
|
|
edict_t *pEntity;
|
|
// Incase another plugin supercedes/overrides, use their returned value here.
|
|
// (Untested).
|
|
if (gpMetaGlobals->status >= MRES_OVERRIDE)
|
|
{
|
|
pEntity=META_RESULT_OVERRIDE_RET(edict_t *);
|
|
iCreateEntityIndex=ENTINDEX(pEntity);
|
|
RETURN_META_VALUE(MRES_IGNORED, false);
|
|
}
|
|
else
|
|
{
|
|
pEntity=CREATE_NAMED_ENTITY(className);
|
|
if (!FNullEnt(pEntity))
|
|
{
|
|
iCreateEntityIndex=ENTINDEX(pEntity);
|
|
RETURN_META_VALUE(MRES_SUPERCEDE,pEntity);
|
|
}
|
|
RETURN_META_VALUE(MRES_SUPERCEDE,pEntity);
|
|
}
|
|
RETURN_META_VALUE(MRES_IGNORED,false);
|
|
}
|
|
|
|
// Map is changing/server is shutting down.
|
|
// We do all cleanup routines here, since, as noted in metamod's dllapi
|
|
// ServerDeactivate is the very last function called before the server loads up a new map.
|
|
void ServerDeactivate(void)
|
|
{
|
|
for (int i=1;i<=gpGlobals->maxClients;i++)
|
|
{
|
|
CPlayer *player = GET_PLAYER_I(i);
|
|
if (player->connected)
|
|
player->Disconnect();
|
|
}
|
|
ns_spawnpoints.clear();
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
// Reset player data here..
|
|
qboolean ClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ])
|
|
{
|
|
// Client's connecting. Freshen up his save data, and mark him as being connected.
|
|
CPlayer *player = GET_PLAYER_E(pEntity);
|
|
player->Connect();
|
|
RETURN_META_VALUE(MRES_HANDLED,0);
|
|
}
|
|
void ClientDisconnect(edict_t *pEntity)
|
|
{
|
|
// Client is disconnecting, clear all his saved information.
|
|
CPlayer *player = GET_PLAYER_E(pEntity);
|
|
player->Disconnect();
|
|
RETURN_META(MRES_HANDLED);
|
|
}
|
|
|
|
// NS resets pev->fov every single frame, but this is called right before the data is sent to the client.
|
|
// Reset FOV if we need to.
|
|
void UpdateClientData( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd )
|
|
{
|
|
edict_t *pEntity = (edict_t*)ent;
|
|
CPlayer *player = GET_PLAYER_E(pEntity);
|
|
if (player->foved)
|
|
pEntity->v.fov = player->fov;
|
|
|
|
RETURN_META(MRES_HANDLED);
|
|
|
|
}
|
|
int LogToIndex(char logline[128])
|
|
{
|
|
char *cname;
|
|
// Format of log line:
|
|
// name<CID><auth><team>
|
|
// We need to find their ID from their name...
|
|
int x,y=0;
|
|
char cindex[64];
|
|
// first we find the location of the start of the index
|
|
// Name can contain <>'s, so we go from the end up.
|
|
for (x=strlen(logline);x>=0;x--)
|
|
{
|
|
if (logline[x]=='<')
|
|
{
|
|
y++;
|
|
if (y==3)
|
|
{
|
|
y=x;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// We found the end of the name, now copy the rest down.
|
|
y--;
|
|
x=0;
|
|
while (x<=y)
|
|
{
|
|
cindex[x]=logline[x];
|
|
x++;
|
|
}
|
|
cindex[x]='\0';
|
|
// Now we have their name, now cycle through all players to find which index it is
|
|
for (x=1;x<=gpGlobals->maxClients;x++)
|
|
{
|
|
cname=strdup(cindex);
|
|
if (!FNullEnt(INDEXENT2(x)))
|
|
{
|
|
if (FStrEq(cname,STRING(INDEXENT2(x)->v.netname)))
|
|
{
|
|
return x;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
int Find_Building_Hive(void)
|
|
{
|
|
edict_t *pEntity=NULL;
|
|
while ((pEntity = UTIL_FindEntityByString(pEntity,"classname","team_hive")))
|
|
{
|
|
if (pEntity->v.health > 0 && pEntity->v.solid > 0 && pEntity->v.fuser1 < 1000)
|
|
{
|
|
return ENTINDEX(pEntity);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
int AMX_MAKE_STRING(AMX *oPlugin, cell tParam, int &iLength)
|
|
{
|
|
char *szNewValue = MF_GetAmxString(oPlugin, tParam, 0, &iLength);
|
|
return ALLOC_STRING(szNewValue);
|
|
}
|
|
|
|
// Makes a char pointer out of an AMX cell.
|
|
char *AMX_GET_STRING(AMX *oPlugin, cell tParam, int &iLength)
|
|
{
|
|
char *szNewValue = MF_GetAmxString(oPlugin, tParam, 0, &iLength);
|
|
return (char*)STRING(ALLOC_STRING(szNewValue));
|
|
}
|
|
edict_t *UTIL_PlayerByIndexE( int playerIndex )
|
|
{
|
|
|
|
if ( playerIndex > 0 && playerIndex <= gpGlobals->maxClients )
|
|
{
|
|
edict_t *pPlayerEdict = INDEXENT2( playerIndex );
|
|
if ( pPlayerEdict && !pPlayerEdict->free )
|
|
{
|
|
return pPlayerEdict;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
edict_t *UTIL_FindEntityByString(edict_t *pentStart, const char *szKeyword, const char *szValue)
|
|
{
|
|
edict_t *pentEntity;
|
|
pentEntity=FIND_ENTITY_BY_STRING(pentStart, szKeyword, szValue);
|
|
if(!FNullEnt(pentEntity))
|
|
return pentEntity;
|
|
return NULL;
|
|
}
|
|
|
|
BOOL CheckForPublic(const char *publicname)
|
|
{
|
|
AMX* amx;
|
|
char blah[64];
|
|
strncpy(blah,publicname,63);
|
|
int iFunctionIndex;
|
|
int i=0;
|
|
// Loop through all running scripts
|
|
while((amx=MF_GetScriptAmx(i++))!=NULL)
|
|
{
|
|
// Scan for public
|
|
if (MF_AmxFindPublic(amx, blah, &iFunctionIndex) == AMX_ERR_NONE)
|
|
{
|
|
// Public was found.
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE; // no public found in any loaded script
|
|
}
|