#include <amxmodx>
#include <fakemeta>
#include <hamsandwich>
#include <reapi>
#include <xs>
enum RadiusDamage_Func {
RadiusDamage_Static,
RadiusDamage_Linear,
RadiusDamage_Sqr,
RadiusDamage_Sqrt,
RadiusDamage_Sin,
RadiusDamage_Tan,
RadiusDamage_Arcsin,
}
enum RadiusDamage_InnerRadiusBehavior {
RadiusDamage_OffsetX,
RadiusDamage_OffsetFx,
RadiusDamage_Overlay,
}
public plugin_init() {
register_clcmd("radiusdmg", "@Cmd_RadiusDmg");
}
@Cmd_RadiusDmg(const UserId) {
new Float:fvOrigin[3];
get_entvar(UserId, var_origin, fvOrigin);
new RadiusDamage_Func:iFunc = RadiusDamage_Linear;
if (read_argc() >= 2) {
iFunc = RadiusDamage_Func:read_argv_int(1);
}
RadiusDamage(
fvOrigin, UserId, UserId, 1000.0, 500.0,
.fInnerRadius=100.0, .iInnerRadiusBehavior=RadiusDamage_OffsetX,
.iFunc=iFunc, .fFuncVar=0.0,
.iIgnoreEntity=UserId,
.bCalcFromHitbox=true,
.bThroughWalls=true
);
}
RadiusDamage(
const Float:fvOrigin[3],
const InflictorId,
const AttackerId,
const Float:fBaseDamage,
const Float:fRadius,
const iClassIgnore = CLASS_NONE,
const bitsDamageType = DMG_GENERIC,
const RadiusDamage_Func:iFunc = RadiusDamage_Linear,
const Float:fFuncVar = 0.0,
const Float:fInnerRadius = 0.0,
const RadiusDamage_InnerRadiusBehavior:iInnerRadiusBehavior = RadiusDamage_OffsetX,
const iIgnoreEntity = 0,
const bool:bCalcFromHitbox = false,
const bool:bThroughWalls = false
) {
new EntId = -1;
new const Float:fInnerRadiusPercent = fInnerRadius / fRadius;
while ((EntId = engfunc(EngFunc_FindEntityInSphere, EntId, fvOrigin, fRadius)) != 0) {
if (EntId == iIgnoreEntity) {
continue;
}
if (get_entvar(EntId, var_takedamage) == DAMAGE_NO) {
continue;
}
if (iClassIgnore && ExecuteHamB(Ham_Classify, EntId) == iClassIgnore) {
continue;
}
if (FClassnameIs(EntId, "player") && !rg_is_player_can_takedamage(EntId, AttackerId)) {
continue;
}
new Float:fvEntOrigin[3];
RadiusDamage_GetEntCenter(EntId, fvEntOrigin);
new iTrace = create_tr2();
engfunc(EngFunc_TraceLine, fvOrigin, fvEntOrigin, DONT_IGNORE_MONSTERS, FM_NULLENT, iTrace);
new Float:fFraction;
get_tr2(iTrace, TR_flFraction, fFraction);
new iTracedEnt = get_tr2(iTrace, TR_pHit);
if (!bThroughWalls && iTracedEnt != EntId && fFraction != 1.0) {
continue;
}
if (bCalcFromHitbox) {
get_tr2(iTrace, TR_vecEndPos, fvEntOrigin);
}
new Float:fDistance = xs_vec_distance(fvOrigin, fvEntOrigin);
new Float:fDmgMult;
if (iInnerRadiusBehavior == RadiusDamage_Overlay && fDistance <= fInnerRadius) {
fDmgMult = 1.0;
} else {
new Float:fDistancePercent;
if (iInnerRadiusBehavior == RadiusDamage_OffsetX) {
fDistancePercent = 1.0 - floatclamp(((fDistance - fInnerRadius) / (fRadius - fInnerRadius)), 0.0, 1.0);
} else {
fDistancePercent = 1.0 - floatmin((fDistance / fRadius), 1.0);
}
fDmgMult = RadiusDamage_CalcDamage(iFunc, fDistancePercent, fFuncVar);
if (iInnerRadiusBehavior == RadiusDamage_OffsetFx) {
fDmgMult = floatmin(fDmgMult + fInnerRadiusPercent, 1.0);
}
}
if (fFraction == 1.0 || iTracedEnt != EntId) {
ExecuteHamB(Ham_TakeDamage, EntId, InflictorId, AttackerId, fBaseDamage * fDmgMult, bitsDamageType);
} else {
new Float:fvTraceDirection[3];
xs_vec_sub(fvEntOrigin, fvOrigin, fvTraceDirection);
xs_vec_normalize(fvTraceDirection, fvTraceDirection);
rg_multidmg_clear();
ExecuteHamB(Ham_TraceAttack, EntId, InflictorId, fBaseDamage * fDmgMult, fvTraceDirection, iTrace, bitsDamageType);
rg_multidmg_apply(InflictorId, AttackerId);
}
free_tr2(iTrace);
}
}
RadiusDamage_GetEntCenter(const EntId, Float:fvCenter[3]) {
if (FClassnameIs(EntId, "func_breakable")) {
new Float:fvAbsMin[3], Float:fvSize[3];
get_entvar(EntId, var_absmin, fvAbsMin);
get_entvar(EntId, var_size, fvSize);
xs_vec_div_scalar(fvSize, 2.0, fvSize);
xs_vec_add(fvAbsMin, fvSize, fvCenter);
} else {
get_entvar(EntId, var_origin, fvCenter);
}
}
Float:RadiusDamage_CalcDamage(const RadiusDamage_Func:iFunc, const Float:fX, const Float:fFuncVar = 0.0) {
new Float:fVar = fFuncVar;
if (fVar == 0.0) {
switch (iFunc) {
case RadiusDamage_Sqr, RadiusDamage_Sqrt:
fVar = 2.0;
case RadiusDamage_Linear, RadiusDamage_Static:
fVar = 1.0;
}
}
new Float:fRes;
switch (iFunc) {
case RadiusDamage_Static:
fRes = fVar;
case RadiusDamage_Linear:
fRes = fX * fVar;
case RadiusDamage_Sqr:
fRes = floatpower(fX, fVar);
case RadiusDamage_Sqrt:
fRes = floatpower(fX, 1 / fVar);
case RadiusDamage_Sin:
fRes = floatsin(fX * 1.57, radian);
case RadiusDamage_Tan:
fRes = floattan(fX * 0.7854, radian);
case RadiusDamage_Arcsin:
fRes = (floatasin((fX - 0.5) * 2, radian) + 1.5) / 3;
}
return floatclamp(fRes, 0.0, 1.0);
}