mirror of
https://github.com/alliedmodders/amxmodx.git
synced 2024-12-29 08:15:38 +03:00
70d822e17f
#554 (Revert "Fun: Make TraceLine a post forward") did not fully revert back TraceLine. RETURN_META(MRES_HANDLED); was added by #421 (Update Fun module) but still remains there.
527 lines
13 KiB
C++
527 lines
13 KiB
C++
// vim: set ts=4 sw=4 tw=99 noet:
|
|
//
|
|
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
|
|
// Copyright (C) The AMX Mod X Development Team.
|
|
//
|
|
// This software is licensed under the GNU General Public License, version 3 or higher.
|
|
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
|
// https://alliedmods.net/amxmodx-license
|
|
|
|
//
|
|
// Fun Module
|
|
//
|
|
|
|
#include "fun.h"
|
|
#include <HLTypeConversion.h>
|
|
|
|
HLTypeConversion TypeConversion;
|
|
CPlayers Players;
|
|
|
|
// native get_client_listen(receiver, sender)
|
|
static cell AMX_NATIVE_CALL get_client_listening(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_receiver, arg_sender };
|
|
|
|
CHECK_PLAYER(params[arg_receiver]);
|
|
CHECK_PLAYER(params[arg_sender]);
|
|
|
|
return GETCLIENTLISTENING(params[arg_receiver], params[arg_sender]);
|
|
}
|
|
|
|
// native set_client_listen(receiver, sender, listen)
|
|
static cell AMX_NATIVE_CALL set_client_listening(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_receiver, arg_sender, arg_listen };
|
|
|
|
CHECK_PLAYER(params[arg_receiver]);
|
|
CHECK_PLAYER(params[arg_sender]);
|
|
|
|
return SETCLIENTLISTENING(params[arg_receiver], params[arg_sender], params[arg_listen]);
|
|
}
|
|
|
|
// native set_user_godmode(index, godmode = 0)
|
|
static cell AMX_NATIVE_CALL set_user_godmode(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_user, arg_godmode };
|
|
|
|
CHECK_PLAYER(params[arg_user]);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_user]);
|
|
|
|
pPlayer->v.takedamage = params[arg_godmode] != 0 ? DAMAGE_NO : DAMAGE_AIM;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native get_user_godmode(index)
|
|
static cell AMX_NATIVE_CALL get_user_godmode(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_user };
|
|
|
|
CHECK_PLAYER(params[arg_user]);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_user]);
|
|
|
|
return pPlayer->v.takedamage == DAMAGE_NO;
|
|
}
|
|
|
|
// native give_item(index, const item[])
|
|
static cell AMX_NATIVE_CALL give_item(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index, arg_item };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
auto itemLength = 0;
|
|
const auto item = MF_GetAmxString(amx, params[arg_item], 1, &itemLength);
|
|
|
|
if (!itemLength
|
|
||(strncmp(item, "weapon_", 7) != 0
|
|
&& strncmp(item, "ammo_", 5) != 0
|
|
&& strncmp(item, "item_", 5) != 0
|
|
&& strncmp(item, "tf_weapon_", 10) != 0))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
auto pEntity = CREATE_NAMED_ENTITY(ALLOC_STRING(item));
|
|
|
|
if (FNullEnt(pEntity))
|
|
{
|
|
MF_LogError(amx, AMX_ERR_NATIVE, "Item \"%s\" failed to create", item);
|
|
return 0;
|
|
}
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
|
|
pEntity->v.origin = pPlayer->v.origin;
|
|
pEntity->v.spawnflags |= SF_NORESPAWN;
|
|
|
|
MDLL_Spawn(pEntity);
|
|
|
|
const auto oldSolid = pEntity->v.solid;
|
|
|
|
MDLL_Touch(pEntity, pPlayer);
|
|
|
|
if (pEntity->v.solid == oldSolid)
|
|
{
|
|
REMOVE_ENTITY(pEntity); // The function did not fail - we're just deleting the item
|
|
return -1;
|
|
}
|
|
|
|
return TypeConversion.edict_to_id(pEntity);
|
|
}
|
|
|
|
// native spawn(index)
|
|
static cell AMX_NATIVE_CALL spawn(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index };
|
|
|
|
CHECK_ENTITY(params[arg_index]);
|
|
|
|
const auto pEntity = TypeConversion.id_to_edict(params[arg_index]);
|
|
|
|
MDLL_Spawn(pEntity);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native set_user_health(index, health)
|
|
static cell AMX_NATIVE_CALL set_user_health(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index, arg_health };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
const auto health = float(params[arg_health]);
|
|
|
|
if (health > 0.0f)
|
|
{
|
|
pPlayer->v.health = health;
|
|
}
|
|
else
|
|
{
|
|
MDLL_ClientKill(pPlayer);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native set_user_frags(index, frags)
|
|
static cell AMX_NATIVE_CALL set_user_frags(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index, arg_frags };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
|
|
pPlayer->v.frags = float(params[arg_frags]);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native set_user_armor(index, armor)
|
|
static cell AMX_NATIVE_CALL set_user_armor(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index, arg_armor };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
|
|
pPlayer->v.armorvalue = float(params[arg_armor]);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native set_user_origin(index, const origin[3])
|
|
static cell AMX_NATIVE_CALL set_user_origin(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index, arg_origin };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
const auto pVector = MF_GetAmxAddr(amx, params[arg_origin]);
|
|
|
|
SET_SIZE(pPlayer, pPlayer->v.mins, pPlayer->v.maxs);
|
|
SET_ORIGIN(pPlayer, Vector(float(pVector[0]), float(pVector[1]), float(pVector[2])));
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native set_user_rendering(index, fx = kRenderFxNone, r = 0, g = 0, b = 0, render = kRenderNormal, amount = 0)
|
|
static cell AMX_NATIVE_CALL set_user_rendering(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index, arg_fx, arg_red, arg_green, arg_blue, arg_render, arg_amount };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
|
|
pPlayer->v.renderfx = params[arg_fx];
|
|
pPlayer->v.rendercolor = Vector(float(params[arg_red]), float(params[arg_green]), float(params[arg_blue]));
|
|
pPlayer->v.rendermode = params[arg_render];
|
|
pPlayer->v.renderamt = float(params[arg_amount]);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// get_user_rendering(index, &fx = kRenderFxNone, &r = 0, &g = 0, &b = 0, &render = kRenderNormal, &amount = 0);
|
|
static cell AMX_NATIVE_CALL get_user_rendering(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index, arg_fx, arg_red, arg_green, arg_blue, arg_render, arg_amount };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
|
|
*MF_GetAmxAddr(amx, params[arg_fx]) = pPlayer->v.renderfx;
|
|
*MF_GetAmxAddr(amx, params[arg_red]) = pPlayer->v.rendercolor[0];
|
|
*MF_GetAmxAddr(amx, params[arg_green]) = pPlayer->v.rendercolor[1];
|
|
*MF_GetAmxAddr(amx, params[arg_blue]) = pPlayer->v.rendercolor[2];
|
|
*MF_GetAmxAddr(amx, params[arg_render]) = pPlayer->v.rendermode;
|
|
*MF_GetAmxAddr(amx, params[arg_amount]) = pPlayer->v.renderamt;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native set_user_maxspeed(index, Float:speed = -1.0)
|
|
static cell AMX_NATIVE_CALL set_user_maxspeed(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index, arg_speed };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
const auto newSpeed = amx_ctof(params[arg_speed]);
|
|
|
|
SETCLIENTMAXSPEED(pPlayer, newSpeed);
|
|
pPlayer->v.maxspeed = newSpeed;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native Float:get_user_maxspeed(index)
|
|
static cell AMX_NATIVE_CALL get_user_maxspeed(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
|
|
return amx_ftoc(pPlayer->v.maxspeed);
|
|
}
|
|
|
|
// native set_user_gravity(index, Float:gravity = 1.0)
|
|
static cell AMX_NATIVE_CALL set_user_gravity(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index, arg_gravity };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
|
|
pPlayer->v.gravity = amx_ctof(params[arg_gravity]);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native Float:get_user_gravity(index)
|
|
static cell AMX_NATIVE_CALL get_user_gravity(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
|
|
return amx_ftoc(pPlayer->v.gravity);
|
|
}
|
|
|
|
// native set_user_hitzones(index = 0, target = 0, body = HITZONES_DEFAULT)
|
|
static cell AMX_NATIVE_CALL set_user_hitzones(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_attacker, arg_target, arg_hitzones };
|
|
|
|
const int attacker = params[arg_attacker];
|
|
const int target = params[arg_target];
|
|
const int hitzones = params[arg_hitzones];
|
|
|
|
if (attacker == 0 && target == 0)
|
|
{
|
|
Players.SetEveryoneBodyHits(hitzones);
|
|
}
|
|
else if (attacker == 0 && target != 0)
|
|
{
|
|
CHECK_PLAYER(target);
|
|
|
|
Players.SetAttackersBodyHits(target, hitzones);
|
|
}
|
|
else if (attacker != 0 && target == 0)
|
|
{
|
|
CHECK_PLAYER(attacker);
|
|
|
|
Players.SetTargetsBodyHits(attacker, hitzones);
|
|
}
|
|
else
|
|
{
|
|
CHECK_PLAYER(attacker);
|
|
CHECK_PLAYER(target);
|
|
|
|
Players.SetBodyHits(attacker, target, hitzones);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native get_user_hitzones(index, target)
|
|
static cell AMX_NATIVE_CALL get_user_hitzones(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_attacker, arg_target };
|
|
|
|
const auto attacker = params[arg_attacker];
|
|
|
|
CHECK_PLAYER(attacker);
|
|
|
|
const auto target = params[arg_target];
|
|
|
|
CHECK_PLAYER(target);
|
|
|
|
return Players[attacker].GetBodyHits(target);
|
|
}
|
|
|
|
// native set_user_noclip(index, noclip = 0)
|
|
static cell AMX_NATIVE_CALL set_user_noclip(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index, arg_noclip };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
|
|
pPlayer->v.movetype = params[arg_noclip] != 0 ? MOVETYPE_NOCLIP : MOVETYPE_WALK;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native get_user_noclip(index)
|
|
static cell AMX_NATIVE_CALL get_user_noclip(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index };
|
|
|
|
CHECK_PLAYER(params[arg_index]);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(params[arg_index]);
|
|
|
|
return pPlayer->v.movetype == MOVETYPE_NOCLIP;
|
|
}
|
|
|
|
// native set_user_footsteps(id, set = 1)
|
|
static cell AMX_NATIVE_CALL set_user_footsteps(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index, arg_footsteps };
|
|
|
|
const auto index = params[arg_index];
|
|
|
|
CHECK_PLAYER(index);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(index);
|
|
|
|
if (params[arg_footsteps] != 0)
|
|
{
|
|
pPlayer->v.flTimeStepSound = 999;
|
|
Players[index].SetSilentFootsteps(true);
|
|
|
|
g_pFunctionTable->pfnPlayerPreThink = PlayerPreThink;
|
|
}
|
|
else
|
|
{
|
|
pPlayer->v.flTimeStepSound = STANDARDTIMESTEPSOUND;
|
|
Players[index].SetSilentFootsteps(false);
|
|
|
|
if (g_pFunctionTable->pfnPlayerPreThink && !Players.HaveSilentFootsteps())
|
|
{
|
|
g_pFunctionTable->pfnPlayerPreThink = nullptr;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// native get_user_footsteps(index)
|
|
static cell AMX_NATIVE_CALL get_user_footsteps(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index };
|
|
|
|
const auto index = params[arg_index];
|
|
|
|
CHECK_PLAYER(index);
|
|
|
|
return Players[index].HasSilentFootsteps();
|
|
}
|
|
|
|
// native strip_user_weapons(index)
|
|
static cell AMX_NATIVE_CALL strip_user_weapons(AMX *amx, cell *params)
|
|
{
|
|
enum args { arg_count, arg_index };
|
|
|
|
const auto index = params[arg_index];
|
|
|
|
CHECK_PLAYER(index);
|
|
|
|
const auto pPlayer = TypeConversion.id_to_edict(index);
|
|
const auto pEntity = CREATE_NAMED_ENTITY(MAKE_STRING("player_weaponstrip"));
|
|
|
|
if (FNullEnt(pEntity))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
MDLL_Spawn(pEntity);
|
|
MDLL_Use(pEntity, pPlayer);
|
|
REMOVE_ENTITY(pEntity);
|
|
|
|
*reinterpret_cast<int *>(MF_PlayerPropAddr(index, Player_CurrentWeapon)) = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
AMX_NATIVE_INFO fun_Exports[] =
|
|
{
|
|
{ "get_client_listen" , get_client_listening },
|
|
{ "set_client_listen" , set_client_listening },
|
|
{ "set_user_godmode" , set_user_godmode },
|
|
{ "get_user_godmode" , get_user_godmode },
|
|
{ "set_user_health" , set_user_health },
|
|
{ "give_item" , give_item },
|
|
{ "spawn" , spawn },
|
|
{ "set_user_frags" , set_user_frags },
|
|
{ "set_user_armor" , set_user_armor },
|
|
{ "set_user_origin" , set_user_origin },
|
|
{ "set_user_rendering", set_user_rendering },
|
|
{ "get_user_rendering", get_user_rendering },
|
|
{ "set_user_maxspeed" , set_user_maxspeed },
|
|
{ "get_user_maxspeed" , get_user_maxspeed },
|
|
{ "set_user_gravity" , set_user_gravity },
|
|
{ "get_user_gravity" , get_user_gravity },
|
|
{ "get_user_footsteps", get_user_footsteps },
|
|
{ "set_user_hitzones" , set_user_hitzones },
|
|
{ "get_user_hitzones" , get_user_hitzones },
|
|
{ "set_user_noclip" , set_user_noclip },
|
|
{ "get_user_noclip" , get_user_noclip },
|
|
{ "set_user_footsteps", set_user_footsteps },
|
|
{ "strip_user_weapons", strip_user_weapons },
|
|
{ nullptr , nullptr }
|
|
};
|
|
|
|
|
|
void PlayerPreThink(edict_t *pEntity)
|
|
{
|
|
const auto index = TypeConversion.edict_to_id(pEntity);
|
|
|
|
if (Players[index].HasSilentFootsteps())
|
|
{
|
|
pEntity->v.flTimeStepSound = 999;
|
|
RETURN_META(MRES_HANDLED);
|
|
}
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
int ClientConnect(edict_t *pPlayer, const char *pszName, const char *pszAddress, char szRejectReason[128])
|
|
{
|
|
const auto index = TypeConversion.edict_to_id(pPlayer);
|
|
|
|
Players[index].Clear();
|
|
|
|
RETURN_META_VALUE(MRES_IGNORED, 0);
|
|
}
|
|
|
|
void TraceLine(const float *v1, const float *v2, int fNoMonsters, edict_t *shooter, TraceResult *ptr)
|
|
{
|
|
TRACE_LINE(v1, v2, fNoMonsters, shooter, ptr);
|
|
|
|
if (ptr->pHit && (ptr->pHit->v.flags & (FL_CLIENT | FL_FAKECLIENT))
|
|
&& shooter && (shooter->v.flags & (FL_CLIENT | FL_FAKECLIENT)) )
|
|
{
|
|
const auto shooterIndex = TypeConversion.edict_to_id(shooter);
|
|
const auto targetIndex = TypeConversion.edict_to_id(ptr->pHit);
|
|
|
|
if (!(Players[shooterIndex].GetBodyHits(targetIndex) & (1 << ptr->iHitgroup)))
|
|
{
|
|
ptr->flFraction = 1.0;
|
|
}
|
|
}
|
|
|
|
RETURN_META(MRES_SUPERCEDE);
|
|
}
|
|
|
|
|
|
void OnAmxxAttach()
|
|
{
|
|
MF_AddNatives(fun_Exports);
|
|
}
|
|
|
|
void OnPluginsLoaded()
|
|
{
|
|
Players.Clear();
|
|
|
|
TypeConversion.init();
|
|
|
|
g_pFunctionTable->pfnPlayerPreThink = nullptr;
|
|
g_pengfuncsTable_Post->pfnTraceLine = nullptr;
|
|
}
|
|
|
|
void ServerDeactivate()
|
|
{
|
|
g_pFunctionTable->pfnPlayerPreThink = nullptr;
|
|
g_pengfuncsTable_Post->pfnTraceLine = nullptr;
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|