new const PluginName[ ] = "[AMXX] Addon: Floating Damage";
new const PluginVersion[ ] = "1.0.6";
new const PluginAuthor[ ] = "Yoshioka Haruki";
new const PluginPrefix[ ] = "Floating Damager";
/* ~ [ Includes ] ~ */
#include <amxmodx>
#include <fakemeta_util>
#include <xs>
/**
* If ur server can't use Re modules, just disable (with //) or delete this line
*/
#include <reapi>
/**
* Don't touch this 😡
*/
#if !defined _reapi_included
#include <hamsandwich>
#include <non_reapi_support>
#endif
/* ~ [ Plugin Settings ] ~ */
const MAX_BODY_PARTS = 4;
const MAX_SUBMODELS = 11;
const ASCII_ZERO = 48;
new const MAX_BODY_SUBMODELS[ MAX_BODY_PARTS ] = {
MAX_SUBMODELS, ...
};
const Float: DamagerTestDistance = 512.0;
new const Float: DamagerTestDamages[ 2 ] = {
// Default, HeadShot
1234.0, 5678.0
};
/**
* Whether to enable Damager for new players.
* If you turn off this function, you need to go to the menu and turn it on yourself
*/
#define EnableDamagerForNewPlayers
/**
* With the settings enabled, while Damager is flying up,
* it will turn behind the player
*/
#define EnableDamagerRotation
/* ~ [ Entity: Floating Damager ] ~ */
new const EntityDamagerReference[ ] = "info_target";
new const EntityDamagerClassName[ ] = "ent_floating_damage";
new const EntityDamagerModel[ ] = "models/x_re/floating_damage.mdl";
const Float: EntityDamagerNextThink = 0.05;
const Float: EntityDamagerAnimFrameRate = 0.33;
const EntityDamagerSkinsCount = 10;
/* ~ [ Params ] ~ */
#if AMXX_VERSION_NUM <= 182
new MaxClients;
#endif
#if !defined _reapi_included
new gl_iszAllocString_Damager;
#endif
new gl_bitsUserDamagerEnabled;
new gl_iUserDamagerSkin[ MAX_PLAYERS + 1 ];
/* ~ [ Macroses ] ~ */
#if !defined Vector3
#define Vector3(%0) Float: %0[ 3 ]
#endif
#if AMXX_VERSION_NUM <= 183
#define MAX_MENU_LENGTH 512
#endif
#define BIT_PLAYER(%0) ( BIT( %0 - 1 ) )
#define BIT_ADD(%0,%1) ( %0 |= %1 )
#define BIT_SUB(%0,%1) ( %0 &= ~%1 )
#define BIT_VALID(%0,%1) ( %0 & %1 )
#define BIT_INVERT(%0,%1) ( %0 ^= %1 )
#define IsUserValid(%0) bool: ( 0 < %0 <= MaxClients )
#define UserDamagerEnabled(%0) ( BIT_VALID( gl_bitsUserDamagerEnabled, BIT_PLAYER( %0 ) ) ? true : false )
#define SetFormatex(%0,%1,%2) ( %1 = formatex( %0, charsmax( %0 ), %2 ) )
#define AddFormatex(%0,%1,%2) ( %1 += formatex( %0[ %1 ], charsmax( %0 ) - %1, %2 ) )
// https://dev-cs.ru/threads/222/post-76443
#define register_cmd_list(%0,%1,%2) for ( new i; i < sizeof %1; i++ ) register_%0( %1[ i ], %2 )
#define var_start_velocity var_vuser1
/* ~ [ AMX Mod X ] ~ */
public plugin_natives( )
{
register_native( "get_user_damager_status", "native_get_user_damager_status" );
register_native( "set_user_damager_status", "native_set_user_damager_status" );
register_native( "get_user_damager_skin", "native_get_user_damager_skin" );
register_native( "set_user_damager_skin", "native_set_user_damager_skin" );
}
public plugin_precache( )
{
/* -> Precache Models <- */
engfunc( EngFunc_PrecacheModel, EntityDamagerModel );
#if !defined _reapi_included
/* -> Alloc String <- */
gl_iszAllocString_Damager = engfunc( EngFunc_AllocString, EntityDamagerClassName );
#endif
}
public plugin_init( )
{
register_plugin( PluginName, PluginVersion, PluginAuthor );
#if defined _reapi_included
/* -> ReGameDLL <- */
RegisterHookChain( RG_CBasePlayer_TakeDamage, "CBasePlayer__TakeDamage_Post", true );
#else
/* -> HamSandwich: Player <- */
RegisterHam( Ham_TakeDamage, "player", "CBasePlayer__TakeDamage_Post", true );
/* -> HamSandwich: Entity <- */
RegisterHam( Ham_Think, EntityDamagerReference, "CEntity__Think_Post", true );
#endif
/* -> Lang Files <- */
register_dictionary( "floating_damage.txt" );
/* -> Register Commands <- */
new DamagerCommands[ ][ ] = {
"damager", "say /damager", "say damager", "say_team /damager", "say_team damager"
};
register_cmd_list(clcmd, DamagerCommands, "ClientCommand__DamagerMenu" );
/* -> Register Menus <- */
register_menucmd( register_menuid( "MenuDamager_Show" ), ( MENU_KEY_0|MENU_KEY_1|MENU_KEY_2|MENU_KEY_3 ), "MenuDamager_Handler" );
/* -> Other <- */
#if AMXX_VERSION_NUM <= 182
#if defined _reapi_included
MaxClients = get_member_game( m_nMaxPlayers );
#else
MaxClients = get_maxplayers( );
#endif
#endif
}
public plugin_cfg( )
{
#if AMXX_VERSION_NUM <= 182
register_cvar( "Floating_Damage_Version", PluginVersion, ( FCVAR_SERVER | FCVAR_SPONLY | FCVAR_UNLOGGED ) );
#else
create_cvar( "Floating_Damage_Version", PluginVersion, ( FCVAR_SERVER | FCVAR_SPONLY | FCVAR_UNLOGGED ) );
#endif
}
public client_putinserver( pPlayer )
{
#if defined EnableDamagerForNewPlayers
BIT_ADD( gl_bitsUserDamagerEnabled, BIT_PLAYER( pPlayer ) );
#endif
#if !defined _reapi_included
set_entvar( pPlayer, var_groupinfo, get_entvar( pPlayer, var_groupinfo ) | ( BIT( 0 )|BIT( pPlayer ) ) );
#endif
}
#if AMXX_VERSION_NUM < 183
public client_disconnect( pPlayer )
#else
public client_disconnected( pPlayer )
#endif
BIT_SUB( gl_bitsUserDamagerEnabled, BIT_PLAYER( pPlayer ) );
public ClientCommand__DamagerMenu( const pPlayer )
{
MenuDamager_Show( pPlayer );
return PLUGIN_HANDLED;
}
/* ~ [ ReGameDLL ] ~ */
public CBasePlayer__TakeDamage_Post( const pVictim, const pInflictor, const pAttacker, const Float: flDamage )
{
if ( !is_user_alive( pAttacker ) || pVictim == pAttacker || !UserDamagerEnabled( pAttacker ) )
return;
if ( get_member( pVictim, m_iTeam ) == get_member( pAttacker, m_iTeam ) )
return;
new Vector3( vecOrigin ); get_entvar( pVictim, var_origin, vecOrigin );
CDamager__SpawnEntity( pAttacker, pVictim, vecOrigin, flDamage, bool: ( get_member( pVictim, m_LastHitGroup ) == HIT_HEAD ) );
}
#if !defined _reapi_included
/* ~ [ HamSandwich ] ~ */
public CEntity__Think_Post( const pEntity )
{
if ( is_nullent( pEntity ) )
return;
if ( get_entvar( pEntity, var_impulse ) == gl_iszAllocString_Damager )
CDamager__Think( pEntity );
}
#endif
/* ~ [ Menus ] ~ */
public MenuDamager_Show( const pPlayer )
{
if ( !is_user_connected( pPlayer ) )
return;
new szMenu[ MAX_MENU_LENGTH ], iLen;
SetFormatex( szMenu, iLen, "%L^n^n", LANG_PLAYER, "ML_Damager_Menu_Title" );
new bitsKeys = MENU_KEY_0|MENU_KEY_1|MENU_KEY_2|MENU_KEY_3;
AddFormatex( szMenu, iLen, "\r1. \w%L: %L^n^n", LANG_PLAYER, "ML_Damager_Menu_Status", LANG_PLAYER, UserDamagerEnabled( pPlayer ) ? "ML_Damager_Status_On" : "ML_Damager_Status_Off" );
AddFormatex( szMenu, iLen, "\r2. \w%L: \y%i^n", LANG_PLAYER, "ML_Damager_Menu_Skin", gl_iUserDamagerSkin[ pPlayer ] + 1 );
if ( is_user_alive( pPlayer ) )
AddFormatex( szMenu, iLen, "\r3. \w%L^n", LANG_PLAYER, "ML_Damager_Menu_Show" );
else
BIT_SUB( bitsKeys, MENU_KEY_3 );
AddFormatex( szMenu, iLen, "^n\r0. \w%L", LANG_PLAYER, "ML_Damager_Menu_Exit" );
set_member( pPlayer, m_iMenu, Menu_OFF );
show_menu( pPlayer, bitsKeys, szMenu, -1, "MenuDamager_Show" );
}
public MenuDamager_Handler( const pPlayer, const iMenuKey )
{
if ( !is_user_connected( pPlayer ) || iMenuKey == 9 )
{
UTIL_DestroyMenu( pPlayer );
return;
}
switch ( iMenuKey )
{
case 0: {
BIT_INVERT( gl_bitsUserDamagerEnabled, BIT_PLAYER( pPlayer ) );
}
case 1: {
if ( ++gl_iUserDamagerSkin[ pPlayer ] && gl_iUserDamagerSkin[ pPlayer ] >= EntityDamagerSkinsCount )
gl_iUserDamagerSkin[ pPlayer ] = 0;
}
case 2: {
new Vector3( vecEyeLevel ); UTIL_GetEyePosition( pPlayer, vecEyeLevel );
new Vector3( vecEndPos ); UTIL_GetVectorAiming( pPlayer, vecEndPos );
xs_vec_add_scaled( vecEyeLevel, vecEndPos, DamagerTestDistance, vecEndPos );
engfunc( EngFunc_TraceLine, vecEyeLevel, vecEndPos, DONT_IGNORE_MONSTERS, pPlayer, 0 );
get_tr2( 0, TR_vecEndPos, vecEndPos );
for ( new i = 0; i < 2; i++ )
CDamager__SpawnEntity( pPlayer, NULLENT, vecEndPos, DamagerTestDamages[ i ], bool: i );
}
}
MenuDamager_Show( pPlayer );
}
/* ~ [ Other ] ~ */
public CDamager__SpawnEntity( const pPlayer, const pVictim, Vector3( vecOrigin ), Float: flDamage, const bool: bHeadShot )
{
new iDamagerSkin = gl_iUserDamagerSkin[ pPlayer ];
if ( iDamagerSkin != 0 ) iDamagerSkin *= 2;
if ( bHeadShot ) iDamagerSkin += 1;
new pEntity = CDamager__FindActiveEntity( pPlayer, pVictim );
if ( pEntity != NULLENT )
{
// Update origin
if ( pVictim != NULLENT )
CDamager__GetPosition( pPlayer, pVictim, vecOrigin, vecOrigin );
engfunc( EngFunc_SetOrigin, pEntity, vecOrigin );
// Update velocity
get_entvar( pEntity, var_start_velocity, vecOrigin );
set_entvar( pEntity, var_velocity, vecOrigin );
// Update angles
get_entvar( pPlayer, var_v_angle, vecOrigin );
vecOrigin[ 1 ] -= 180.0;
set_entvar( pEntity, var_angles, vecOrigin );
// Update total damage
#if !defined _reapi_included
new Float: flDamageSave; get_entvar( pEntity, var_dmg_save, flDamageSave );
flDamage += flDamageSave;
#else
flDamage += Float: get_entvar( pEntity, var_dmg_save );
#endif
set_entvar( pEntity, var_dmg_save, flDamage );
set_entvar( pEntity, var_sequence, 0 );
set_entvar( pEntity, var_renderamt, 255.0 );
set_entvar( pEntity, var_nextthink, get_gametime( ) + 0.3 );
set_entvar( pEntity, var_skin, iDamagerSkin );
set_entvar( pEntity, var_body, CDamager__PrepareBody( flDamage ) );
return pEntity;
}
pEntity = rg_create_entity( EntityDamagerReference );
if ( is_nullent( pEntity ) )
return NULLENT;
if ( pVictim != NULLENT )
CDamager__GetPosition( pPlayer, pVictim, vecOrigin, vecOrigin );
else
{
vecOrigin[ 0 ] += random_float( -32.0, 32.0 );
vecOrigin[ 1 ] += random_float( -32.0, 32.0 );
vecOrigin[ 2 ] += 16.0;
}
new Vector3( vecTemp ); xs_vec_copy( vecOrigin, vecTemp );
new Vector3( vecVelocity ); vecTemp[ 2 ] += 64.0;
UTIL_GetSpeedVector( vecOrigin, vecTemp, _, 1.0, vecVelocity );
set_entvar( pEntity, var_velocity, vecVelocity );
engfunc( EngFunc_SetOrigin, pEntity, vecOrigin );
engfunc( EngFunc_SetModel, pEntity, EntityDamagerModel );
#if !defined _reapi_included
set_entvar( pEntity, var_impulse, gl_iszAllocString_Damager );
#endif
set_entvar( pEntity, var_classname, EntityDamagerClassName );
set_entvar( pEntity, var_solid, SOLID_NOT );
set_entvar( pEntity, var_movetype, MOVETYPE_NOCLIP );
set_entvar( pEntity, var_owner, pPlayer );
set_entvar( pEntity, var_playerclass, pVictim );
set_entvar( pEntity, var_skin, iDamagerSkin );
set_entvar( pEntity, var_body, CDamager__PrepareBody( flDamage ) );
// Cache vars
set_entvar( pEntity, var_start_velocity, vecVelocity );
set_entvar( pEntity, var_dmg_save, flDamage );
#if defined _reapi_included
set_entvar( pEntity, var_effects, get_entvar( pEntity, var_effects ) | EF_OWNER_VISIBILITY );
#else
set_entvar( pEntity, var_groupinfo, BIT( pPlayer ) );
#endif
get_entvar( pPlayer, var_v_angle, vecTemp );
vecTemp[ 1 ] -= 180.0;
set_entvar( pEntity, var_angles, vecTemp );
#if defined _reapi_included
SetThink( pEntity, "CDamager__Think" );
#endif
set_entvar( pEntity, var_nextthink, get_gametime( ) + 0.3 );
UTIL_SetEntityRendering( pEntity, _, _, kRenderTransAdd, 255.0 );
return pEntity;
}
public CDamager__Think( const pEntity )
{
static pOwner; pOwner = get_entvar( pEntity, var_owner );
#if defined EnableDamagerRotation
static Vector3( vecAngles ); get_entvar( pOwner, var_v_angle, vecAngles );
vecAngles[ 1 ] -= 180.0;
set_entvar( pEntity, var_angles, vecAngles );
#endif
if ( get_entvar( pEntity, var_sequence ) != 1 )
UTIL_SetEntityAnim( pEntity, 1, .flFrameRate = Float: EntityDamagerAnimFrameRate );
static Float: flRenderAmt; get_entvar( pEntity, var_renderamt, flRenderAmt );
if ( ( flRenderAmt -= 15.0 ) && flRenderAmt <= 15.0 )
{
UTIL_KillEntity( pEntity );
return;
}
set_entvar( pEntity, var_renderamt, flRenderAmt );
set_entvar( pEntity, var_nextthink, get_gametime( ) + Float: EntityDamagerNextThink );
}
public CDamager__FindActiveEntity( const pPlayer, const pVictim )
{
if ( !IsUserValid( pVictim ) )
return NULLENT;
new pEntity = MaxClients;
while ( ( pEntity = fm_find_ent_by_class( pEntity, EntityDamagerClassName ) ) > 0 )
{
if ( get_entvar( pEntity, var_owner ) != pPlayer )
continue;
if ( get_entvar( pEntity, var_playerclass ) != pVictim )
continue;
return pEntity;
}
return NULLENT;
}
public CDamager__GetPosition( const pPlayer, const pVictim, Vector3( vecVictimOrigin ), Vector3( vecOut ) )
{
// by Docaner
new Vector3( vecAttackerOrigin );
get_entvar( pPlayer, var_origin, vecAttackerOrigin );
new Vector3( vecDirection );
xs_vec_sub( vecAttackerOrigin, vecVictimOrigin, vecDirection );
xs_vec_normalize( vecDirection, vecDirection );
new Vector3( vecViewOfs );
get_entvar( pVictim, var_view_ofs, vecViewOfs );
vecViewOfs[ 2 ] += 30.0;
xs_vec_add( vecVictimOrigin, vecViewOfs, vecOut );
xs_vec_add_scaled( vecOut, vecDirection, 20.0, vecOut );
}
public CDamager__PrepareBody( Float: flDamage )
{
flDamage = floatmin( flDamage, 9999.0 );
new szDamage[ MAX_BODY_PARTS + 1 ], aParts[ MAX_BODY_PARTS ]
for ( new i, j = num_to_str( floatround( flDamage ), szDamage, charsmax( szDamage ) ); i < j; i++ )
aParts[ i ] = ++szDamage[ i ] - ASCII_ZERO;
return CalculateModelBodyArr( aParts, MAX_BODY_SUBMODELS, MAX_BODY_PARTS );
}
/* ~ [ Natives ] ~ */
public bool: native_get_user_damager_status( const iPlugin, const iParams )
{
enum { arg_player = 1 };
new pPlayer = get_param( arg_player );
if ( !is_user_connected( pPlayer ) )
{
log_error( AMX_ERR_NATIVE, "[%s] Invalid Player (%i)", PluginPrefix, pPlayer );
return false;
}
return bool: UserDamagerEnabled( pPlayer );
}
public bool: native_set_user_damager_status( const iPlugin, const iParams )
{
enum { arg_player = 1, arg_value };
new pPlayer = get_param( arg_player );
if ( !is_user_connected( pPlayer ) )
{
log_error( AMX_ERR_NATIVE, "[%s] Invalid Player (%i)", PluginPrefix, pPlayer );
return false;
}
new bool: bValue = bool: get_param( arg_value );
bValue ? BIT_ADD( gl_bitsUserDamagerEnabled, BIT_PLAYER( pPlayer ) ) : BIT_SUB( gl_bitsUserDamagerEnabled, BIT_PLAYER( pPlayer ) );
return true;
}
public native_get_user_damager_skin( const iPlugin, const iParams )
{
enum { arg_player = 1 };
new pPlayer = get_param( arg_player );
if ( !is_user_connected( pPlayer ) )
{
log_error( AMX_ERR_NATIVE, "[%s] Invalid Player (%i)", PluginPrefix, pPlayer );
return -1;
}
return gl_iUserDamagerSkin[ pPlayer ];
}
public bool: native_set_user_damager_skin( const iPlugin, const iParams )
{
enum { arg_player = 1, arg_value };
new pPlayer = get_param( arg_player );
if ( !is_user_connected( pPlayer ) )
{
log_error( AMX_ERR_NATIVE, "[%s] Invalid Player (%i)", PluginPrefix, pPlayer );
return false;
}
gl_iUserDamagerSkin[ pPlayer ] = clamp( get_param( arg_value ), 0, ( EntityDamagerSkinsCount - 1 ) );
return true;
}
/* ~ [ Stocks ] ~ */
// https://dev-cs.ru/threads/222/page-7#post-77015
stock CalculateModelBodyArr( const parts[ ], const sizes[ ], const count )
{
static bodyInt32, temp, it, tempCount; bodyInt32 = 0; tempCount = count;
while ( tempCount-- )
{
if ( sizes[ tempCount ] == 1 )
continue;
temp = parts[ tempCount ];
for ( it = 0; it < tempCount; it++ )
temp *= sizes[it];
bodyInt32 += temp;
}
return bodyInt32;
}
/* -> Destroy Menu <- */
stock UTIL_DestroyMenu( const pPlayer )
{
show_menu( pPlayer, 0, "^n", 1 );
menu_cancel( pPlayer );
}
/* -> Destroy Entity <- */
stock UTIL_KillEntity( const pEntity )
{
set_entvar( pEntity, var_flags, FL_KILLME );
set_entvar( pEntity, var_nextthink, get_gametime( ) );
}
/* -> Set Entity Animation <- */
stock UTIL_SetEntityAnim( const pEntity, const iSequence = 0, const Float: flFrame = 0.0, const Float: flFrameRate = 1.0 )
{
set_entvar( pEntity, var_frame, flFrame );
set_entvar( pEntity, var_framerate, flFrameRate );
set_entvar( pEntity, var_animtime, get_gametime( ) );
set_entvar( pEntity, var_sequence, iSequence );
}
/* -> Set Entity Rendering <- */
stock UTIL_SetEntityRendering( const pEntity, const iRenderFx = kRenderFxNone, const Float: flRenderColor[ 3 ] = { 255.0, 255.0, 255.0 }, const iRenderMode = kRenderNormal, const Float: flRenderAmount = 16.0 )
{
set_entvar( pEntity, var_renderfx, iRenderFx );
set_entvar( pEntity, var_rendercolor, flRenderColor );
set_entvar( pEntity, var_rendermode, iRenderMode );
set_entvar( pEntity, var_renderamt, flRenderAmount );
}
/* -> Get speed Vector to 2 points <- */
stock UTIL_GetSpeedVector( const Vector3( vecStartOrigin ), const Vector3( vecEndOrigin ), Float: flSpeed = 0.0, Float: flTime = 1.0, Vector3( vecVelocity ) )
{
if ( !flSpeed )
flSpeed = xs_vec_distance( vecStartOrigin, vecEndOrigin ) / flTime;
else flSpeed /= flTime;
xs_vec_sub( vecEndOrigin, vecStartOrigin, vecVelocity );
xs_vec_normalize( vecVelocity, vecVelocity );
xs_vec_mul_scalar( vecVelocity, flSpeed, vecVelocity );
}
/* -> Get player eye position <- */
stock UTIL_GetEyePosition( const pPlayer, Vector3( vecEyeLevel ) )
{
static Vector3( vecOrigin ); get_entvar( pPlayer, var_origin, vecOrigin );
static Vector3( vecViewOfs ); get_entvar( pPlayer, var_view_ofs, vecViewOfs );
xs_vec_add( vecOrigin, vecViewOfs, vecEyeLevel );
}
/* -> Get player aiming <- */
stock UTIL_GetVectorAiming( const pPlayer, Vector3( vecAiming ) )
{
static Vector3( vecViewAngle ); get_entvar( pPlayer, var_v_angle, vecViewAngle );
static Vector3( vecPunchAngle ); get_entvar( pPlayer, var_punchangle, vecPunchAngle );
xs_vec_add( vecViewAngle, vecPunchAngle, vecViewAngle );
angle_vector( vecViewAngle, ANGLEVECTOR_FORWARD, vecAiming );
}