Custom Weapons API
SkillDev toolsGuide for implementing custom weapons using OOP-style patterns in AMX Mod X. Use when creating new weapons or extending existing ones with the Custom Weapons API.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Custom Weapons API skill
What this skill tells your AI
The instructions your AI receives, as published by hedgefog/amxx-modding-kit in .agents/skills/amxx-modding-kit/api/custom-weapons/SKILL.md and read by ahel’s review.
OOP-style weapon system for creating and extending weapons with custom behavior.
For complete API documentation, see README.md.
Naming Conventions
Member Constants
Use m_ prefix with Hungarian notation type prefix:
// Format: m_{Type}{Name}
new const m_flChargeTime[] = "flChargeTime"; // float - charge start time
new const m_bSilenced[] = "bSilenced"; // bool - silencer attached
new const m_iFireMode[] = "iFireMode"; // int - current fire mode
new const m_pTarget[] = "pTarget"; // pointer - locked target
Method Constants
Use PascalCase name only (no prefix):
new const SetPower[] = "SetPower";
new const ToggleSilencer[] = "ToggleSilencer";
new const StartCharge[] = "StartCharge";
Class Registration
public plugin_precache() {
// Precache resources BEFORE registration
precache_model(g_szModelV);
precache_model(g_szModelP);
precache_model(g_szModelW);
precache_sound(g_szShotSound);
// Register new weapon class
CW_RegisterClass(WEAPON_NAME);
// Or extend existing: CW_RegisterClass(WEAPON_NAME, "weapon_othercustomweapon");
// Implement base methods (override virtual methods)
CW_ImplementClassMethod(WEAPON_NAME, CW_Method_Create, "@Weapon_Create");
CW_ImplementClassMethod(WEAPON_NAME, CW_Method_Deploy, "@Weapon_Deploy");
CW_ImplementClassMethod(WEAPON_NAME, CW_Method_PrimaryAttack, "@Weapon_PrimaryAttack");
CW_ImplementClassMethod(WEAPON_NAME, CW_Method_Reload, "@Weapon_Reload");
// Register custom methods
CW_RegisterClassMethod(WEAPON_NAME, SetPower, "@Weapon_SetPower", CW_Type_Cell);
}
Custom weapons can only be inherited from other custom weapons.
Members Usage
Get Member
static szModel[64]; CW_GetMemberString(this, CW_Member_szModel, szModel, charsmax(szModel));
static Float:vecSpread[3]; CW_GetMemberVec(this, m_vecSpread, vecSpread);
new Float:flChargeTime = CW_GetMember(this, m_flChargeTime);
new iFireMode = CW_GetMember(this, m_iFireMode);
Set Member
CW_SetMemberString(this, CW_Member_szModel, g_szModelW);
CW_SetMemberVec(this, m_vecSpread, Float:{0.02, 0.02, 0.0});
CW_SetMember(this, m_flChargeTime, 0.0);
CW_SetMember(this, m_iFireMode, 1);
Conditions
When you need to get float member value in conditions or for other inline operations you should explicitly provide Float: tag to make sure compiler will use correct type.
if (Float:CW_GetMember(this, m_flNextUpdate) <= get_gametime()) {
// Do something
}
Method Implementation
Create Method (Constructor)
Set weapon properties. Never modify engine data here.
@Weapon_Create(const this) {
CW_CallBaseMethod();
// Built-in members
CW_SetMemberString(this, CW_Member_szModel, g_szModelW);
CW_SetMember(this, CW_Member_iId, WEAPON_ID);
CW_SetMember(this, CW_Member_iMaxClip, 30);
CW_SetMember(this, CW_Member_iSlot, WEAPON_SLOT);
CW_SetMember(this, CW_Member_iPosition, WEAPON_POSITION);
// Custom members
CW_SetMember(this, m_flChargeTime, 0.0);
CW_SetMember(this, m_bSilenced, false);
// ❌ WRONG: Don't use engine functions here
// set_pev(this, pev_dmg, 50.0);
}
Deploy Method
Handle weapon draw:
@Weapon_Deploy(const this) {
CW_CallBaseMethod();
// Use native method for default deploy behavior
CW_CallNativeMethod(this, CW_Method_DefaultDeploy, g_szModelV, g_szModelP, ANIM_DRAW, "rifle");
}
PrimaryAttack Method
Handle shooting:
@Weapon_PrimaryAttack(const this) {
CW_CallBaseMethod();
static Float:vecSpread[3]; vecSpread = Float:{0.02, 0.02, 0.0};
// DefaultShot returns true if shot was fired
if (CW_CallNativeMethod(this, CW_Method_DefaultShot, 25.0, 1.0, 0.1, vecSpread, 1)) {
static pPlayer; pPlayer = get_ent_data_entity(this, "CBasePlayerItem", "m_pPlayer");
emit_sound(pPlayer, CHAN_WEAPON, g_szShotSound, VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
}
}
Reload Method
Handle reloading:
@Weapon_Reload(const this) {
CW_CallBaseMethod();
if (CW_CallNativeMethod(this, CW_Method_DefaultReload, ANIM_RELOAD, 2.5)) {
static pPlayer; pPlayer = get_ent_data_entity(this, "CBasePlayerItem", "m_pPlayer");
emit_sound(pPlayer, CHAN_WEAPON, g_szReloadSound, VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
}
}
Think Method
Recurring logic with static variables for performance:
@Weapon_Think(const this) {
static pPlayer; pPlayer = get_ent_data_entity(this, "CBasePlayerItem", "m_pPlayer");
static Float:flChargeTime; flChargeTime = CW_GetMember(this, m_flChargeTime);
// Weapon logic...
CW_CallBaseMethod();
}
Creating Weapons
Give to Player
// Give weapon to player
new pWeapon = CW_Give(pPlayer, WEAPON_NAME);
if (pWeapon != FM_NULLENT) {
CW_SetMember(pWeapon, m_flChargeTime, 0.0);
}
Create Entity
// Create weapon entity (for world placement)
new pWeapon = CW_Create(WEAPON_NAME);
if (pWeapon != FM_NULLENT) {
engfunc(EngFunc_SetOrigin, pWeapon, vecOrigin);
dllfunc(DLLFunc_Spawn, pWeapon);
}
Check Player Has Weapon
if (CW_PlayerHasWeapon(pPlayer, WEAPON_NAME)) {
// Player has this weapon
}
// Get weapon entity from player inventory
new pWeapon = CW_PlayerFindWeapon(pPlayer, WEAPON_NAME);
if (pWeapon != FM_NULLENT) {
// Found weapon entity
}
Calling Methods
// Call custom method
CW_CallMethod(pWeapon, SetPower, 100.0);
CW_CallMethod(pWeapon, StartCharge);
// Call native method with parameters
CW_CallNativeMethod(pWeapon, CW_Method_DefaultDeploy, g_szModelV, g_szModelP, ANIM_DRAW, "rifle");
Calling Parent Methods
Always call CW_CallBaseMethod() to invoke parent implementation:
@Weapon_PrimaryAttack(const this) {
if (!CW_CallMethod(this, CanFire)) return;
CW_CallBaseMethod();
// Additional logic after parent method
PlayMuzzleFlash(this);
}
Getting Player from Weapon
Important: There is no CW_GetPlayer native. Use engine data access:
new pPlayer = get_ent_data_entity(this, "CBasePlayerItem", "m_pPlayer");
Weapon Hooks (External Plugins)
Listen to weapon events from other plugins:
public plugin_init() {
CW_RegisterClassNativeMethodHook(WEAPON_NAME, CW_Method_PrimaryAttack, "CWHook_Rifle_PrimaryAttack");
}
public CWHook_Rifle_PrimaryAttack(const this) {
// React to weapon fire...
return CW_HANDLED;
}
Hook callback naming: CWHook_{WeaponName}_{Method}
public CWHook_Rifle_PrimaryAttack(const this)
public CWHook_Shotgun_Reload(const this)
Ammo System
Register Ammo Type
public plugin_precache() {
CW_Ammo_Register("rifle_ammo", CSW_AK47, 90); // name, engine type, max amount
}
Give Ammo to Player
CW_GiveAmmo(pPlayer, "rifle_ammo", 30);
Best Practices Checklist
- Precache resources BEFORE
CW_RegisterClass - Use
Createfor member initialization only - Use
Deployfor setting view/player models - Check
FM_NULLENTafterCW_CreateorCW_Give - Use
CW_CallNativeMethodfor built-in behaviors - Get player with
get_ent_data_entity(this, "CBasePlayerItem", "m_pPlayer") - Use static variables in Think for performance
- Define member constants with
m_prefix and Hungarian notation - Define method constants with PascalCase only (no prefix)
Signals
- GitHub stars
- 32
- Forks
- 4
- Last commit
- Aug 2026
Advanced
- Catalog kind
- skill
- Gateway key
amxx-modding-kit-api-custom-weapons- Source
- github.com/hedgefog/amxx-modding-kit