// Copyright © 2016/2020 Vaqtincha
const MAX_SPAWNS = 64
const Float:MIN_SPAWN_RADIUS = 500.0
new const CSDM_SPAWN_DIR[] = "csdm/spawns" // default: addons/amxmodx/configs/csdm/spawns
#define PL_VERSION "0.1.4"
#include <amxmodx>
#include <fakemeta>
#include <reapi>
#if AMXX_VERSION_NUM < 183
#include <colorchat>
#endif
#define IsVectorZero(%1) (%1[X] == 0.0 && %1[Y] == 0.0 && %1[Z] == 0.0)
#define IsPlayer(%1) (1 <= %1 <= g_iMaxPlayers)
#define Vector(%1,%2,%3) (Float:{%1.0, %2.0, %3.0})
#define VECTOR_ZERO Vector(0, 0, 0)
#define FIND_ENT_IN_SPHERE(%1,%2,%3) engfunc(EngFunc_FindEntityInSphere, %1, %2, %3)
#define REMOVE_ENTITY(%1) engfunc(EngFunc_RemoveEntity, %1)
#define SET_ORIGIN(%1,%2) engfunc(EngFunc_SetOrigin, %1, %2)
#define SET_SIZE(%1,%2,%3) engfunc(EngFunc_SetSize, %1, %2, %3)
#define SET_MODEL(%1,%2) engfunc(EngFunc_SetModel, %1, %2)
const MENU_KEY_BITS = (MENU_KEY_1|MENU_KEY_2|MENU_KEY_3|MENU_KEY_4|MENU_KEY_5|MENU_KEY_6|MENU_KEY_7|MENU_KEY_9)
enum coord_e { Float:X, Float:Y, Float:Z }
enum { FAILED_CREATE, FILE_SAVED, FILE_DELETED }
enum { MODE_TEAMPLAY, MODE_DEATHMATCH }
enum spawn_s
{
Float:Origin[coord_e],
Float:VAngle[coord_e],
Float:Angles[coord_e],
TeamName:Team
}
enum player_s
{
Float:LastOrigin[coord_e],
Float:LastVAngle[coord_e],
Float:LastAngles[coord_e],
LastSpawn,
AimedEntity,
bool:FirstSpawn,
TeamName:LastTeam,
TeamName:CurTeam
}
// ======================= spawn editor settings =======================
const MAX_SEARCH_DISTANCE = 2500
const Float:ADD_Z_POSITION = 15.0
new const Float:g_flGravityValues[] = { 1.0, 0.5, 0.25, 0.15, 0.05 }
new const Float:g_vecColorTeam[any:TEAM_CT + 1][coord_e] =
{
{ 0.0, 250.0, 0.0 }, // TEAM_UNASSIGNED
{ 250.0, 0.0, 0.0 }, // TEAM_TERRORIST
{ 0.0, 0.0, 250.0 } // TEAM_CT
}
new const SOUND_SELECT[] = "common/menu2.wav"
new const SOUND_ERROR[] = "buttons/button2.wav"
new const SOUND_SUCCESS[] = "buttons/blip2.wav"
new const g_szModels[any:TEAM_CT + 1][] =
{
"models/player/vip/vip.mdl", // TEAM_UNASSIGNED
"models/player/leet/leet.mdl", // TEAM_TERRORIST
"models/player/gign/gign.mdl" // TEAM_CT
}
//======================================================================
new const g_szClassName[] = "view_spawn"
new const g_szMenuTitle[] = "SpawnEditor"
new any:g_aSpot[MAX_SPAWNS][spawn_s], any:g_aPlayerData[MAX_CLIENTS + 1][player_s]
new g_szSpawnDirectory[256], g_szSpawnFile[256], g_szMapName[32]
new g_iMenuID, bool:g_bEditSpawns, bool:g_bNotSaved
new g_iGravity, g_iMaxPlayers, HookChain:g_hGetPlayerSpawnSpot, HookChain:g_hUseEmpty
new g_iTotalPoints, g_iNum[any:TEAM_CT + 1], g_iSpawnMode = MODE_TEAMPLAY
public SetSpawnerStateApi(bool:bEnabled, const iNewMode)
{
SetSpawnerState(bEnabled, clamp(iNewMode, MODE_TEAMPLAY, MODE_DEATHMATCH))
return g_iTotalPoints
}
public plugin_precache()
{
for (new i = 0; i < sizeof(g_szModels); i++) {
precache_model(g_szModels[i]) // for custom models
}
precache_sound(SOUND_SELECT)
precache_sound(SOUND_ERROR)
precache_sound(SOUND_SUCCESS)
}
public plugin_init()
{
register_plugin("DeathMatch Spawn Manager", PL_VERSION, "Vaqtincha")
register_concmd("csdm_edit_spawns", "ConCmd_EditSpawns", ADMIN_MAP, "Edits spawn configuration")
register_clcmd("nightvision", "ClCmd_Nightvision")
register_menucmd((g_iMenuID = register_menuid(g_szMenuTitle)), MENU_KEY_BITS, "EditorMenuHandler")
DisableHookChain(g_hGetPlayerSpawnSpot = RegisterHookChain(RG_CSGameRules_GetPlayerSpawnSpot, "CSGameRules_GetPlayerSpawnSpot", .post = false))
DisableHookChain(g_hUseEmpty = RegisterHookChain(RG_CBasePlayer_UseEmpty, "CBasePlayer_UseEmpty", .post = false))
g_iMaxPlayers = get_maxplayers()
}
public plugin_cfg()
{
new iLen = get_localinfo("amxx_configsdir", g_szSpawnDirectory, charsmax(g_szSpawnDirectory))
formatex(g_szSpawnDirectory[iLen], charsmax(g_szSpawnDirectory) - iLen, "%s/%s", g_szSpawnDirectory[iLen], CSDM_SPAWN_DIR)
MakeDir(g_szSpawnDirectory)
get_mapname(g_szMapName, charsmax(g_szMapName))
formatex(g_szSpawnFile, charsmax(g_szSpawnFile), "%s/%s.spawns.cfg", g_szSpawnDirectory, g_szMapName)
LoadPoints()
}
public plugin_end()
{
if (g_bEditSpawns && g_bNotSaved) // autosave
{
MakeDir(g_szSpawnDirectory)
SavePoints()
}
}
public client_connect(pPlayer)
{
g_aPlayerData[pPlayer][FirstSpawn] = true
}
public client_putinserver(pPlayer)
{
g_aPlayerData[pPlayer][AimedEntity] = NULLENT
g_aPlayerData[pPlayer][LastTeam] = TEAM_UNASSIGNED
g_aPlayerData[pPlayer][LastSpawn] = 0
g_aPlayerData[pPlayer][FirstSpawn] = false
g_aPlayerData[pPlayer][LastOrigin][X] = g_aPlayerData[pPlayer][LastOrigin][Y] = g_aPlayerData[pPlayer][LastOrigin][Z] = 0.0
}
public ClCmd_Nightvision(const pPlayer, const level) {
return (!g_bEditSpawns || !is_user_alive(pPlayer) || !(get_user_flags(pPlayer) & level)) ? PLUGIN_CONTINUE : ShowEditorMenu(pPlayer)
}
public ConCmd_EditSpawns(const pPlayer, const level)
{
if (!is_user_alive(pPlayer) || !(get_user_flags(pPlayer) & level))
return PLUGIN_HANDLED
if (g_bEditSpawns)
{
if (g_bNotSaved && SavePoints() == FAILED_CREATE)
{
console_print(pPlayer, "[CSDM] Autosave is failed! Please try again...")
return ShowEditorMenu(pPlayer)
}
console_print(pPlayer, "[CSDM] Spawn editor disabled.")
IsViewingMenu(pPlayer, true)
RemoveAllSpotEntitys()
g_bEditSpawns = false
set_cvar_num("mp_round_infinite", 0)
set_cvar_num("mp_forcerespawn", 0)
set_entvar(pPlayer, var_gravity, 1.0)
// SetSpawnerState(false)
DisableHookChain(g_hUseEmpty)
return PLUGIN_HANDLED
}
console_print(pPlayer, "[CSDM] Spawn editor enabled.")
MakeAllSpotEntitys()
g_bEditSpawns = true
set_cvar_num("mp_round_infinite", 1)
set_cvar_num("mp_forcerespawn", 1)
set_entvar(pPlayer, var_gravity, g_flGravityValues[g_iGravity])
SetSpawnerState(true)
EnableHookChain(g_hUseEmpty)
return ShowEditorMenu(pPlayer)
}
public CBasePlayer_UseEmpty(const pPlayer)
{
if (IsViewingMenu(pPlayer))
{
EditorMenuHandler(pPlayer, 1)
return HC_SUPERCEDE
}
return HC_CONTINUE
}
public CSGameRules_GetPlayerSpawnSpot(const pPlayer)
{
if (g_iTotalPoints > 0 && !g_aPlayerData[pPlayer][FirstSpawn] && RandomSpawn(pPlayer))
{
SetHookChainReturn(ATYPE_INTEGER, pPlayer)
return HC_SUPERCEDE
}
return HC_CONTINUE
}
public ShowEditorMenu(const pPlayer)
{
new szMenu[512], bitKeys = (MENU_KEY_2|MENU_KEY_3|MENU_KEY_6|MENU_KEY_7)
new iLen = formatex(szMenu, charsmax(szMenu), "\yРедактор точек возрожд.^n^n")
new bool:bAddCancel = bool:(!IsVectorZero(g_aPlayerData[pPlayer][LastOrigin])), bool:bReached = bool:(g_iTotalPoints >= MAX_SPAWNS)
static const szTeamName[any:TEAM_CT + 1][] = { "ALL", "TT", "CT" }
if (bAddCancel)
{
bitKeys |= MENU_KEY_5
}
if (g_aPlayerData[pPlayer][AimedEntity] == NULLENT)
{
iLen += formatex(szMenu[iLen], charsmax(szMenu) - iLen,
"%s^n^n\
\y2. \wВыделить точку возрожд.^n\
\y3. \wВыбрать команду\r: \w[\y%s\w]^n\
\d4. Проверка точки возрожд.^n\
%s^n^n",
bReached ? "\d1. Добавить точку для возрожд.\w(\rМаксим. лимит точек\w)" : "\y1. \wДобавить новую точку для возрожд.",
szTeamName[g_aPlayerData[pPlayer][CurTeam]], bAddCancel ? "\y5. \wОтменить удаление" : "\d5. Удалить точку возрожд."
)
if (!bReached) {
bitKeys |= MENU_KEY_1
}
}
else
{
iLen += formatex(szMenu[iLen], charsmax(szMenu) - iLen,
"\y1. \wОбновить точку возрожд.^n^n\
\y2. \wСнять выделение с точки возрожд.^n\
\y3. \wВыбрать точку возрожд. для команд\r: \w[\y%s\w]^n\
\y4. \wПроверка точки возрожд.^n\
\y5. %s^n^n", szTeamName[get_entvar(g_aPlayerData[pPlayer][AimedEntity], var_team)],
bAddCancel ? "\wОтменить удаление" : "\rУдалить точку возрожд."
)
bitKeys |= (MENU_KEY_1|MENU_KEY_4|MENU_KEY_5)
}
iLen += formatex(szMenu[iLen], charsmax(szMenu) - iLen,
"\y6. \wПоказать статистику^n\
\y7. \wГравитация\r: \w[\y%0.2f\w]^n^n\
%s^n^n\
\wВсего точек\r: \w[\y%d\r/\y%d\w] \r(\wALL \y%d \r| \wTT \y%d \r| \wCT \y%d\r)", g_flGravityValues[g_iGravity],
g_bNotSaved ? "\y9. \wСохранить" : "\d9. Сохранить", g_iTotalPoints, MAX_SPAWNS, g_iNum[TEAM_UNASSIGNED], g_iNum[TEAM_TERRORIST], g_iNum[TEAM_CT]
)
show_menu(pPlayer, bitKeys |= g_bNotSaved ? MENU_KEY_9 : bitKeys, szMenu, .title = g_szMenuTitle)
return PLUGIN_HANDLED
}
public EditorMenuHandler(const pPlayer, iKey)
{
if (!g_bEditSpawns)
return PLUGIN_HANDLED
iKey++
switch (iKey)
{
case 1:
{
g_bNotSaved = bool:(g_aPlayerData[pPlayer][AimedEntity] == NULLENT ? AddSpawn(pPlayer) : MoveSpawn(pPlayer, g_aPlayerData[pPlayer][AimedEntity]))
g_aPlayerData[pPlayer][LastOrigin][X] = g_aPlayerData[pPlayer][LastOrigin][Y] = g_aPlayerData[pPlayer][LastOrigin][Z] = 0.0
}
case 2:
{
if (g_aPlayerData[pPlayer][AimedEntity] == NULLENT)
{
if (!SetAimedEntity(pPlayer)) {
client_print(pPlayer, print_center, "Spawn entity not found!")
}
else {
rg_send_audio(pPlayer, SOUND_SELECT)
}
}
else
{
ClearAimedEntity(pPlayer)
}
}
case 3:
{
if (g_aPlayerData[pPlayer][AimedEntity] == NULLENT)
{
if (++g_aPlayerData[pPlayer][CurTeam] > TEAM_CT) {
g_aPlayerData[pPlayer][CurTeam] = TEAM_UNASSIGNED
}
}
else
{
SetSpotTeam(g_aPlayerData[pPlayer][AimedEntity])
g_bNotSaved = true
}
}
case 4: TeleportToAimed(pPlayer, g_aPlayerData[pPlayer][AimedEntity])
case 5: g_bNotSaved = bool:(IsVectorZero(g_aPlayerData[pPlayer][LastOrigin]) ? DeleteSpawn(pPlayer, g_aPlayerData[pPlayer][AimedEntity]) : AddSpawn(pPlayer, true))
case 6:
{
new Float:vecOrigin[coord_e]
get_entvar(pPlayer, var_origin, vecOrigin)
client_print_color(pPlayer, print_team_grey, "Всего точек: ^4%d ^1(^3ALL ^4%d ^3TT ^4%d ^3CT ^4%d^1) Текущ. положение: ^3X ^4%0.f ^3Y ^4%0.f ^3Z ^4%0.f",
g_iTotalPoints, g_iNum[TEAM_UNASSIGNED], g_iNum[TEAM_TERRORIST], g_iNum[TEAM_CT], vecOrigin[X], vecOrigin[Y], vecOrigin[Z])
}
case 7:
{
if (++g_iGravity >= sizeof(g_flGravityValues)) {
g_iGravity = 0
}
set_entvar(pPlayer, var_gravity, g_flGravityValues[g_iGravity])
}
case 9:
{
static const szResultPrint[][] = {"Failed to create file!^rPlease try again", "Saved successfully", "File deleted"}
client_print(pPlayer, print_center, "%s", szResultPrint[SavePoints()])
}
}
return ShowEditorMenu(pPlayer)
}
bool:AddSpawn(const pPlayer, bool:bUndo = false)
{
new Float:vecOrigin[coord_e], Float:vecAngles[coord_e], Float:vecVAngles[coord_e], pEntity = NULLENT
if ((pEntity = CreateEntity()) == NULLENT)
return false
if (bUndo)
{
SetPosition(pEntity, g_aPlayerData[pPlayer][LastOrigin], g_aPlayerData[pPlayer][LastAngles], g_aPlayerData[pPlayer][LastVAngle])
set_entvar(pEntity, var_team, g_aPlayerData[pPlayer][LastTeam])
SET_MODEL(pEntity, g_szModels[g_aPlayerData[pPlayer][LastTeam]])
g_iNum[g_aPlayerData[pPlayer][LastTeam]]++
SetAimedEntity(pPlayer, pEntity, false)
}
else
{
GetPosition(pPlayer, vecOrigin, vecAngles, vecVAngles)
vecOrigin[Z] += ADD_Z_POSITION
if (!CheckFreeSpace(pPlayer, vecOrigin))
{
REMOVE_ENTITY(pEntity)
return false
}
g_iNum[g_aPlayerData[pPlayer][CurTeam]]++
SetPosition(pEntity, vecOrigin, vecAngles, vecVAngles)
set_entvar(pEntity, var_team, g_aPlayerData[pPlayer][CurTeam])
SET_MODEL(pEntity, g_szModels[g_aPlayerData[pPlayer][CurTeam]])
rg_send_audio(pPlayer, SOUND_SUCCESS)
}
g_iTotalPoints++
return true
}
bool:MoveSpawn(const pPlayer, const pEntity)
{
new Float:vecOrigin[coord_e], Float:vecAngles[coord_e], Float:vecVAngles[coord_e]
GetPosition(pPlayer, vecOrigin, vecAngles, vecVAngles)
vecOrigin[Z] += ADD_Z_POSITION
if (CheckFreeSpace(pPlayer, vecOrigin))
{
SetPosition(pEntity, vecOrigin, vecAngles, vecVAngles)
return true
}
return false
}
bool:DeleteSpawn(const pPlayer, const pEntity)
{
if (is_nullent(pEntity))
return false
GetPosition(pEntity, g_aPlayerData[pPlayer][LastOrigin], g_aPlayerData[pPlayer][LastAngles], g_aPlayerData[pPlayer][LastVAngle])
g_aPlayerData[pPlayer][LastTeam] = get_entvar(pEntity, var_team)
g_aPlayerData[pPlayer][AimedEntity] = NULLENT
REMOVE_ENTITY(pEntity)
g_iNum[g_aPlayerData[pPlayer][LastTeam]]--
g_iTotalPoints--
return true
}
TeleportToAimed(const pPlayer, const pEntity = NULLENT)
{
if (is_nullent(pEntity))
return
new Float:vecOrigin[coord_e], Float:vecAngles[coord_e], Float:vecVAngles[coord_e]
GetPosition(pEntity, vecOrigin, vecAngles, vecVAngles)
if (CheckFreeSpace(pPlayer, vecOrigin)) {
SetPlayerPosition(pPlayer, vecOrigin, vecVAngles)
}
}
LoadPoints()
{
new pFile
if (!(pFile = fopen(g_szSpawnFile, "rt")))
{
server_print("[CSDM] No spawn points file found ^"%s^"", g_szMapName)
return
}
new szDatas[128], szOrigin[coord_e][6], szTeam[3], szAngles[coord_e][6], szVAngles[coord_e][6]
while (!feof(pFile))
{
fgets(pFile, szDatas, charsmax(szDatas))
trim(szDatas)
if (!szDatas[0] || (szDatas[0] == '/' && szDatas[1] == '/'))
continue
if (parse(szDatas, szOrigin[X], 5, szOrigin[Y], 5, szOrigin[Z], 5, szAngles[X], 5, szAngles[Y], 5, szAngles[Z], 5,
szTeam, charsmax(szTeam), szVAngles[X], 5, szVAngles[Y], 5, szVAngles[Z], 5
) != 10)
{
continue // ignore invalid lines
}
if (g_iTotalPoints >= MAX_SPAWNS)
{
server_print("[CSDM] Max limit %d reached!", MAX_SPAWNS)
break
}
g_aSpot[g_iTotalPoints][Origin][X] = str_to_float(szOrigin[X])
g_aSpot[g_iTotalPoints][Origin][Y] = str_to_float(szOrigin[Y])
g_aSpot[g_iTotalPoints][Origin][Z] = str_to_float(szOrigin[Z])
// if (!IsHullVacant(g_aSpot[g_iTotalPoints][Origin], HULL_HUMAN))
// {
// server_print("[CSDM] Warning bad spawn detected at: [X: %0.f | Y: %0.f | Z: %0.f]", g_aSpot[g_iTotalPoints][Origin][X], g_aSpot[g_iTotalPoints][Origin][Y], g_aSpot[g_iTotalPoints][Origin][Z])
// continue
// }
g_aSpot[g_iTotalPoints][Angles][X] = str_to_float(szAngles[X])
g_aSpot[g_iTotalPoints][Angles][Y] = str_to_float(szAngles[Y])
// g_aSpot[g_iTotalPoints][Angles][Z] = str_to_float(szAngles[Z]) // not used
g_aSpot[g_iTotalPoints][VAngle][X] = str_to_float(szVAngles[X])
g_aSpot[g_iTotalPoints][VAngle][Y] = str_to_float(szVAngles[Y])
// g_aSpot[g_iTotalPoints][VAngle][Z] = str_to_float(szVAngles[Z]) // not used
g_aSpot[g_iTotalPoints][Team] = clamp(str_to_num(szTeam), any:TEAM_UNASSIGNED, any:TEAM_CT)
g_iNum[g_aSpot[g_iTotalPoints][Team]]++
g_iTotalPoints++
}
if (g_iTotalPoints > 0)
{
server_print("[CSDM] Map ^"%s^" total spawns: %d [ANY: %d | TT: %d | CT: %d]", g_szMapName,
g_iTotalPoints, g_iNum[TEAM_UNASSIGNED], g_iNum[TEAM_TERRORIST], g_iNum[TEAM_CT])
// SetSpawnerState(true, (!g_iNum[TEAM_TERRORIST] && !g_iNum[TEAM_CT]) ? MODE_DEATHMATCH : MODE_TEAMPLAY)
}
fclose(pFile)
}
SavePoints()
{
if (g_iTotalPoints <= 0)
{
delete_file(g_szSpawnFile)
SetSpawnerState(false)
return FILE_DELETED
}
new pFile, pEntity = NULLENT
if (!(pFile = fopen(g_szSpawnFile, "wt")))
{
MakeDir(g_szSpawnDirectory, false)
return FAILED_CREATE
}
fprintf(pFile, "// File generated by ^"CSDM Spawn Manager^" Version: %s^n// Total spawns: %d (ANY = %d, TT = %d, CT = %d)^n^n// Origin X:Y:Z, Angles X:Y:Z, Team, View Angles X:Y:Z^n^n",
PL_VERSION, g_iTotalPoints, g_iNum[TEAM_UNASSIGNED], g_iNum[TEAM_TERRORIST], g_iNum[TEAM_CT])
ClearAllArrays()
while ((pEntity = rg_find_ent_by_class(pEntity, g_szClassName)))
{
if (g_iTotalPoints >= MAX_SPAWNS)
{
server_print("[CSDM] Max limit %d reached!", MAX_SPAWNS)
break
}
GetPosition(pEntity, g_aSpot[g_iTotalPoints][Origin], g_aSpot[g_iTotalPoints][Angles], g_aSpot[g_iTotalPoints][VAngle])
if (IsVectorZero(g_aSpot[g_iTotalPoints][Origin]))
continue
g_aSpot[g_iTotalPoints][Team] = get_entvar(pEntity, var_team)
g_iNum[g_aSpot[g_iTotalPoints][Team]]++
fprintf(pFile, "%-6.f %-6.f %-6.f %-4.f %-5.f %-2.f %-2.1d %-4.f %-5.f %-1.f^n",
g_aSpot[g_iTotalPoints][Origin][X], g_aSpot[g_iTotalPoints][Origin][Y], g_aSpot[g_iTotalPoints][Origin][Z],
g_aSpot[g_iTotalPoints][Angles][X], g_aSpot[g_iTotalPoints][Angles][Y], g_aSpot[g_iTotalPoints][Angles][Z],
g_aSpot[g_iTotalPoints][Team],
g_aSpot[g_iTotalPoints][VAngle][X], g_aSpot[g_iTotalPoints][VAngle][Y], g_aSpot[g_iTotalPoints][VAngle][Z]
)
g_iTotalPoints++
}
// SetSpawnerState(true)
g_bNotSaved = false
fclose(pFile)
return FILE_SAVED
}
MakeAllSpotEntitys()
{
for (new i = 0, pEntity = NULLENT; i < g_iTotalPoints; i++)
{
if (!IsVectorZero(g_aSpot[i][Origin]) && (pEntity = CreateEntity()) != NULLENT)
{
SetPosition(pEntity, g_aSpot[i][Origin], g_aSpot[i][Angles], g_aSpot[i][VAngle])
set_entvar(pEntity, var_team, g_aSpot[i][Team])
SET_MODEL(pEntity, g_szModels[g_aSpot[i][Team]])
}
}
}
RemoveAllSpotEntitys()
{
new pEntity = NULLENT, pPlayer
for (pPlayer = 1; pPlayer < g_iMaxPlayers; pPlayer++)
{
g_aPlayerData[pPlayer][LastOrigin][X] = g_aPlayerData[pPlayer][LastOrigin][Y] = g_aPlayerData[pPlayer][LastOrigin][Z] = 0.0
g_aPlayerData[pPlayer][AimedEntity] = NULLENT
g_aPlayerData[pPlayer][LastTeam] = TEAM_UNASSIGNED
}
while ((pEntity = rg_find_ent_by_class(pEntity, g_szClassName))) {
REMOVE_ENTITY(pEntity)
}
}
CreateEntity()
{
new pEntity = rg_create_entity("info_target")
if (is_nullent(pEntity))
{
abort(AMX_ERR_GENERAL, "Failed to create entity!")
return NULLENT
}
set_entvar(pEntity, var_classname, g_szClassName)
set_entvar(pEntity, var_solid, SOLID_BBOX)
rg_animate_entity(pEntity, ACT_IDLE)
return pEntity
}
SetPlayerPosition(const pPlayer, const Float:vecOrigin[], const Float:vecAngles[])
{
const FORCE_VIEW_ANGLES = 1
SET_ORIGIN(pPlayer, vecOrigin)
set_entvar(pPlayer, var_velocity, VECTOR_ZERO)
// set_entvar(pPlayer, var_avelocity, VECTOR_ZERO)
set_entvar(pPlayer, var_v_angle, VECTOR_ZERO)
set_entvar(pPlayer, var_angles, vecAngles)
set_entvar(pPlayer, var_punchangle, VECTOR_ZERO)
set_entvar(pPlayer, var_fixangle, FORCE_VIEW_ANGLES)
}
SetPosition(const pEntity, const Float:vecOrigin[], const Float:vecAngles[], const Float:vecVAngles[])
{
SET_ORIGIN(pEntity, vecOrigin)
set_entvar(pEntity, var_angles, vecAngles)
set_entvar(pEntity, var_v_angle, vecVAngles) // temporary save
}
GetPosition(const pEntity, Float:vecOrigin[], Float:vecAngles[], Float:vecVAngles[])
{
get_entvar(pEntity, var_origin, vecOrigin)
get_entvar(pEntity, var_angles, vecAngles)
get_entvar(pEntity, var_v_angle, vecVAngles)
}
SetSpotTeam(const pEntity)
{
new TeamName:iOldTeam, TeamName:iNewTeam = iOldTeam = get_entvar(pEntity, var_team)
if (++iNewTeam > TEAM_CT) {
iNewTeam = TEAM_UNASSIGNED
}
g_iNum[iNewTeam]++
g_iNum[iOldTeam]--
set_entvar(pEntity, var_team, iNewTeam)
SET_MODEL(pEntity, g_szModels[iNewTeam])
rg_set_rendering(pEntity, kRenderFxGlowShell, g_vecColorTeam[iNewTeam], 10.0)
}
bool:SetAimedEntity(const pPlayer, pEntity = NULLENT, bool:bPrint = true)
{
if (pEntity > 0 || (pEntity = FindEntityByAim(pPlayer)) != NULLENT)
{
rg_animate_entity(pEntity, ACT_RUN, 1.0)
rg_set_rendering(pEntity, kRenderFxGlowShell, g_vecColorTeam[get_entvar(pEntity, var_team)], 10.0)
g_aPlayerData[pPlayer][AimedEntity] = pEntity
g_aPlayerData[pPlayer][LastOrigin][X] = g_aPlayerData[pPlayer][LastOrigin][Y] = g_aPlayerData[pPlayer][LastOrigin][Z] = 0.0
if (bPrint) {
client_print(pPlayer, print_center, "Aimed entity index %d", g_aPlayerData[pPlayer][AimedEntity])
}
return true
}
return false
}
ClearAimedEntity(const pPlayer)
{
rg_animate_entity(g_aPlayerData[pPlayer][AimedEntity], ACT_IDLE)
rg_set_rendering(g_aPlayerData[pPlayer][AimedEntity])
g_aPlayerData[pPlayer][AimedEntity] = NULLENT
}
ClearAllArrays()
{
for (new i = 0; i < MAX_SPAWNS; i++)
{
g_aSpot[i][Origin][X] = g_aSpot[i][Origin][Y] = g_aSpot[i][Origin][Z] = 0.0
g_aSpot[i][VAngle][X] = g_aSpot[i][VAngle][Y] = g_aSpot[i][VAngle][Z] = 0.0
g_aSpot[i][Angles][X] = g_aSpot[i][Angles][Y] = g_aSpot[i][Angles][Z] = 0.0
}
g_iNum[TEAM_UNASSIGNED] = g_iNum[TEAM_TERRORIST] = g_iNum[TEAM_CT] = 0
g_iTotalPoints = 0
}
bool:IsViewingMenu(const pPlayer, bool:Close = false)
{
new iMenuID, iKeys
get_user_menu(pPlayer, iMenuID, iKeys)
if (iMenuID == g_iMenuID)
{
if (Close)
{
// menu_cancel(pPlayer)
show_menu(pPlayer, 0, "^n", 0)
}
return true
}
return false
}
FindEntityByAim(const pPlayer)
{
new pEntity = NULLENT, iBody
SetEntitysSolid(true)
get_user_aiming(pPlayer, pEntity, iBody, MAX_SEARCH_DISTANCE)
SetEntitysSolid(false)
return (FClassnameIs(pEntity, g_szClassName)) ? pEntity : NULLENT
}
SetEntitysSolid(const bool:bSolid)
{
new pEntity = NULLENT
while ((pEntity = rg_find_ent_by_class(pEntity, g_szClassName)))
{
if (!bSolid)
SET_SIZE(pEntity, VECTOR_ZERO, VECTOR_ZERO)
else
SET_SIZE(pEntity, Vector(-16, -16, -36), Vector(16, 16, 36))
}
}
bool:RandomSpawn(const pPlayer)
{
new iRand, iAttempts, iLast = g_aPlayerData[pPlayer][LastSpawn], TeamName:iTeam = get_member(pPlayer, m_iTeam)
do
{
iAttempts++
iRand = random(g_iTotalPoints)
if (iRand != iLast && !IsVectorZero(g_aSpot[iRand][Origin]) && get_distance_fix(g_aSpot[iRand][Origin], g_aSpot[iLast][Origin]) > MIN_SPAWN_RADIUS)
{
if ((g_iSpawnMode == MODE_DEATHMATCH || g_aSpot[iRand][Team] == iTeam || g_aSpot[iRand][Team] == TEAM_UNASSIGNED)
&& !CheckDistance(pPlayer, g_aSpot[iRand][Origin])/* && IsHullVacant(g_aSpot[iRand][Origin], HULL_HUMAN, pPlayer) */)
{
SetPlayerPosition(pPlayer, g_aSpot[iRand][Origin], g_aSpot[iRand][VAngle])
g_aPlayerData[pPlayer][LastSpawn] = iRand
return true
}
}
} while (iAttempts <= g_iTotalPoints)
return false
}
bool:CheckDistance(const pPlayer, const Float:vecOrigin[])
{
new pEntity = NULLENT
while ((pEntity = FIND_ENT_IN_SPHERE(pEntity, vecOrigin, MIN_SPAWN_RADIUS)))
{
if (IsPlayer(pEntity) && pEntity != pPlayer && get_entvar(pEntity, var_deadflag) == DEAD_NO) {
return true
}
}
return false
}
bool:CheckFreeSpace(const pPlayer, const Float:vecOrigin[coord_e])
{
if (!IsHullVacant(vecOrigin, HULL_HUMAN, pPlayer))
{
client_print(pPlayer, print_center, "No free space!")
rg_send_audio(pPlayer, SOUND_ERROR)
return false
}
return true
}
SetSpawnerState(bool:bEnabled, const iNewMode = MODE_TEAMPLAY)
{
if (bEnabled && g_iTotalPoints > 0)
{
EnableHookChain(g_hGetPlayerSpawnSpot)
g_iSpawnMode = iNewMode
}
else
{
DisableHookChain(g_hGetPlayerSpawnSpot)
}
}
MakeDir(const szDirName[], bool:bPrint = true)
{
if (dir_exists(szDirName))
return
if (bPrint) {
server_print("[CSDM] Directory ^"%s^" not exist, will be created automatically.", szDirName)
}
if (mkdir(szDirName)) {
abort(AMX_ERR_GENERAL, "[CSDM] Failed to create directory ^"%s^"", szDirName)
}
}
// checks if a space is vacant, by VEN
stock bool:IsHullVacant(const Float:vecOrigin[], const iHullNumber, const pSkipEnt = 0)
{
const pTr = 0 // pGlobalTrace
engfunc(EngFunc_TraceHull, vecOrigin, vecOrigin, DONT_IGNORE_MONSTERS, iHullNumber, pSkipEnt, pTr)
return bool:(!get_tr2(pTr, TR_StartSolid) && !get_tr2(pTr, TR_AllSolid) && get_tr2(pTr, TR_InOpen))
}
stock rg_animate_entity(const pEntity, const Activity:iSequence, const Float:flFramerate = 0.0)
{
set_entvar(pEntity, var_sequence, iSequence)
set_entvar(pEntity, var_framerate, flFramerate)
}
stock rg_set_rendering(const pEntity, const fx = kRenderFxNone, const Float:flColor[] = {0.0, 0.0, 0.0}, const Float:iAmount = 0.0)
{
set_entvar(pEntity, var_renderfx, fx)
set_entvar(pEntity, var_rendercolor, flColor)
set_entvar(pEntity, var_renderamt, iAmount)
}
// FIX: "error 047: array sizes do not match, or destination array is too small"
stock Float:get_distance_fix(const Float:Origin1[], const Float:Origin2[])
{
new Float:fOrigin1[coord_e], Float:fOrigin2[coord_e]
fOrigin1[X] = Origin1[X]; fOrigin1[Y] = Origin1[Y]; fOrigin1[Z] = Origin1[Z]
fOrigin2[X] = Origin2[X]; fOrigin2[Y] = Origin2[Y]; fOrigin2[Z] = Origin2[Z]
return get_distance_f(fOrigin1, fOrigin2)
}