Hamsandwich Guide: RegisterHam and Ham_TakeDamage

September 24, 2025 Daemon666 7 min read 287 преглеждания

Ham Sandwich (module hamsandwich) hooks the virtual methods of Half-Life/CS entities — the C++ functions the game DLL calls internally to spawn a player, apply damage, or handle a kill. Unlike a network event, a Ham hook runs inside the call, so you can read the real arguments, change them, and cancel the whole thing. It works on both stock CS and ReGameDLL, which makes it the portable way to alter core gameplay. Ham_TakeDamage is the one you will use most, so this guide builds around it.

1. Registering a hook

RegisterHam takes the function to hook, the entity classname whose method you want, your callback, and a post flag (0 = pre, 1 = post). Register in plugin_init():

#include <amxmodx>
#include <hamsandwich>

public plugin_init()
{
    register_plugin("Ham Demo", "1.0", "CSB")

    RegisterHam(Ham_TakeDamage, "player", "fw_take_damage_pre", 0)
    RegisterHam(Ham_Spawn,      "player", "fw_spawn_post", 1)
    RegisterHam(Ham_Killed,     "player", "fw_killed_pre", 0)
}

The classname "player" hooks the method on every player entity. You can hook other entities the same way — "weapon_ak47", "func_breakable" — as long as the function exists on that class.

2. Ham_TakeDamage: reading the parameters

The TakeDamage forward hands you the victim, the inflictor entity, the attacker, the damage amount, and the damage-type bits:

public fw_take_damage_pre(victim, inflictor, attacker, Float:damage, damagebits)
{
    // ignore world/self damage
    if (!is_user_connected(attacker) || victim == attacker)
        return HAM_IGNORED

    new vname[32], aname[32]
    get_user_name(victim, vname, charsmax(vname))
    get_user_name(attacker, aname, charsmax(aname))

    server_print("%s is hitting %s for %.0f", aname, vname, damage)
    return HAM_IGNORED
}

damagebits is a bitfield of DMG_* flags (bullet, blast, fall, etc.), useful for distinguishing a knife from a grenade from fall damage.

3. Modifying and blocking

To change a parameter, write it back with the SetHamParam* natives — the argument number matches the parameter position. Damage is parameter 4:

public fw_take_damage_pre(victim, inflictor, attacker, Float:damage, damagebits)
{
    if (!is_user_connected(attacker))
        return HAM_IGNORED

    // double all player damage
    SetHamParamFloat(4, damage * 2.0)
    return HAM_HANDLED
}

To cancel the call outright — total damage immunity, spawn protection — return HAM_SUPERCEDE. That prevents the original function from running at all:

public fw_take_damage_pre(victim, inflictor, attacker, Float:damage, damagebits)
{
    if (is_spawn_protected(victim))
        return HAM_SUPERCEDE      // take no damage

    return HAM_IGNORED
}

The return values: HAM_IGNORED (do nothing special), HAM_HANDLED (you changed a param, keep going), HAM_SUPERCEDE (block the original), and HAM_OVERRIDE (block and substitute your own return value, set with SetHamReturn*).

4. Pre versus post

A pre hook (flag 0) runs before the original function — the only place you can modify parameters or supersede the call. A post hook (flag 1) runs after, when the effect has already happened. Use post for Ham_Spawn when you want to hand out weapons or set health after the game has reset the player:

public fw_spawn_post(id)
{
    if (!is_user_alive(id))
        return HAM_IGNORED

    set_pev(id, pev_health, 150.0)   // give bonus HP on spawn
    return HAM_IGNORED
}

Setting health in a pre spawn hook would be pointless — the game overwrites it during the spawn. Match the hook direction to when the state you care about actually exists.

5. Calling the function yourself

ExecuteHamB lets you invoke a Ham function manually — for example, to kill a player through the real code path so death handling runs correctly:

// deal 1000 damage to slay a player properly
ExecuteHamB(Ham_TakeDamage, id, 0, id, 1000.0, DMG_GENERIC)

Common errors

  • Server crashes on load — wrong classname, or hooking a function the class does not have. Ham_TakeDamage on "player" is safe; a typo in the classname is not.
  • Damage change ignored — you set the param in a post hook. Parameters can only be modified in a pre hook, before the original runs.
  • Superceding TakeDamage breaks other plugins — if another plugin also hooks it and expects the call to proceed, your HAM_SUPERCEDE hides the event from it. Prefer setting damage to 0.0 and returning HAM_HANDLED when you only mean to null the damage, not the whole call.
  • run time error after the hook — you used an invalid entity index (attacker 0 is the world). Guard with is_user_connected before touching player data. See run-time error 4.

Verification

Load the plugin, take a hit in-game, and confirm the server_print reports the expected damage. For a modifier, check that the scoreboard/health reflects the change — double damage should drop a full-HP player in half the usual hits. For HAM_SUPERCEDE immunity, stand in fire and confirm health never drops. When you need named members and a cleaner API on a ReGameDLL server, ReAPI hooks the same functions with readable arguments; for lighter needs, compare all three hook types.

Сътрудници: Daemon666 ✦
Сподели: