.agents/skills/amxx-modding-kit/api/player-roles/SKILL.md
Guide for Player Roles API usage implementing OOP-style player role management with inheritance, groups, and custom methods.
npx skillsauth add hedgefog/amxx-modding-kit amxx-modding-kit-api-player-rolesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
OOP-style system for defining, assigning, and managing player roles with inheritance, groups, and custom methods/members.
For complete API documentation, see README.md.
Use #define with ROLE_ and ROLE_GROUP_ prefixes:
#define ROLE_SURVIVOR "survivor"
#define ROLE_ZOMBIE "zombie"
#define ROLE_FAST_ZOMBIE "fast_zombie"
#define ROLE_GROUP_SPECIES "species"
#define ROLE_GROUP_CLASS "class"
new const m_flPower[] = "flPower";
new const m_szVariant[] = "szVariant";
new const Growl[] = "Growl";
new const GetSpeed[] = "GetSpeed";
public plugin_precache() {
// Register standalone roles
PlayerRole_Register(ROLE_SURVIVOR);
PlayerRole_Register(ROLE_ZOMBIE);
}
public plugin_precache() {
// Base zombie role
PlayerRole_Register(ROLE_ZOMBIE);
// Specialized zombies inherit from base
PlayerRole_Register(ROLE_FAST_ZOMBIE, ROLE_ZOMBIE);
PlayerRole_Register(ROLE_TANK_ZOMBIE, ROLE_ZOMBIE);
PlayerRole_Register(ROLE_POISON_ZOMBIE, ROLE_ZOMBIE);
}
public plugin_precache() {
PlayerRole_Register(ROLE_ZOMBIE);
// Implement native methods
PlayerRole_ImplementMethod(ROLE_ZOMBIE, PlayerRole_Method_Assign, "@Zombie_Assign");
PlayerRole_ImplementMethod(ROLE_ZOMBIE, PlayerRole_Method_Unassign, "@Zombie_Unassign");
// Register custom methods
PlayerRole_RegisterMethod(ROLE_ZOMBIE, Growl, "@Zombie_Growl");
PlayerRole_RegisterVirtualMethod(ROLE_ZOMBIE, GetSpeed, "@Zombie_GetSpeed");
}
@Zombie_Assign(const pPlayer) {
// Call base method if inheriting
PlayerRole_This_CallBaseMethod();
// Initialize role members
PlayerRole_This_SetMember(m_flPower, 100.0);
PlayerRole_This_SetMemberString(m_szVariant, "standard");
// Apply role effects
PlayerModel_Set(pPlayer, g_szZombieModel);
PlayerModel_Update(pPlayer);
client_print(pPlayer, print_chat, "You are now a zombie!");
}
@Zombie_Unassign(const pPlayer) {
PlayerRole_This_CallBaseMethod();
// Remove role effects
PlayerModel_Reset(pPlayer);
client_print(pPlayer, print_chat, "You are no longer a zombie.");
}
@Zombie_Growl(const pPlayer) {
emit_sound(pPlayer, CHAN_VOICE, g_szGrowlSound, VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
client_print(pPlayer, print_center, "Grrrrrrr!");
}
Float:@Zombie_GetSpeed(const pPlayer) {
return 280.0;
}
// Assign role to player
PlayerRole_Player_AssignRole(pPlayer, ROLE_ZOMBIE);
// Unassign role
PlayerRole_Player_UnassignRole(pPlayer, ROLE_ZOMBIE);
// Unassign all roles
PlayerRole_Player_UnassignRoles(pPlayer);
Groups enforce mutual exclusivity - only one role per group:
// Assign role in specific group
PlayerRole_Player_AssignRole(pPlayer, ROLE_ZOMBIE, ROLE_GROUP_SPECIES);
// Later, assigning different role in same group auto-unassigns previous
PlayerRole_Player_AssignRole(pPlayer, ROLE_SURVIVOR, ROLE_GROUP_SPECIES);
// Zombie is automatically unassigned
// Unassign all roles in a group
PlayerRole_Player_UnassignRoleGroup(pPlayer, ROLE_GROUP_SPECIES);
Roles with same parent are mutually exclusive:
// Assign FastZombie (inherits from Zombie)
PlayerRole_Player_AssignRole(pPlayer, ROLE_FAST_ZOMBIE);
// Assign TankZombie - FastZombie is automatically unassigned
// (both inherit from Zombie)
PlayerRole_Player_AssignRole(pPlayer, ROLE_TANK_ZOMBIE);
// Check if player has role (includes inheritance)
if (PlayerRole_Player_HasRole(pPlayer, ROLE_ZOMBIE)) {
// Player has Zombie, FastZombie, TankZombie, etc.
}
// Check exact role (no inheritance check)
if (PlayerRole_Player_HasExactRole(pPlayer, ROLE_FAST_ZOMBIE)) {
// Player has exactly FastZombie
}
if (PlayerRole_Player_HasRoleGroup(pPlayer, ROLE_GROUP_SPECIES)) {
new szRole[PLAYER_ROLE_MAX_LENGTH];
PlayerRole_Player_GetRoleByGroup(pPlayer, ROLE_GROUP_SPECIES, szRole, charsmax(szRole));
client_print(pPlayer, print_chat, "Your species role: %s", szRole);
}
// From within role method (uses This_ variant)
@Zombie_Assign(const pPlayer) {
PlayerRole_This_SetMember(m_flPower, 100.0);
PlayerRole_This_SetMemberString(m_szVariant, "alpha");
}
// From outside role method
PlayerRole_Player_SetMember(pPlayer, ROLE_ZOMBIE, m_flPower, 150.0);
PlayerRole_Player_SetMemberString(pPlayer, ROLE_ZOMBIE, m_szVariant, "beta");
// From within role method
new Float:flPower = PlayerRole_This_GetMember(m_flPower);
// From outside
new Float:flPower = PlayerRole_Player_GetMember(pPlayer, ROLE_ZOMBIE, m_flPower);
new szVariant[32];
PlayerRole_Player_GetMemberString(pPlayer, ROLE_ZOMBIE, m_szVariant, szVariant, charsmax(szVariant));
// Call method if player has role
if (PlayerRole_Player_HasRole(pPlayer, ROLE_ZOMBIE)) {
PlayerRole_Player_CallMethod(pPlayer, ROLE_ZOMBIE, Growl);
new Float:flSpeed = PlayerRole_Player_CallMethod(pPlayer, ROLE_ZOMBIE, GetSpeed);
}
@FastZombie_Assign(const pPlayer) {
// Call parent (Zombie) Assign method first
PlayerRole_This_CallBaseMethod();
// Then add FastZombie-specific logic
PlayerRole_This_SetMember(m_flSpeedBonus, 50.0);
}
public HamHook_Player_Spawn_Post(const pPlayer) {
if (!is_user_alive(pPlayer)) return HAM_IGNORED;
// Assign role based on team
switch (get_user_team(pPlayer)) {
case 1: PlayerRole_Player_AssignRole(pPlayer, ROLE_SURVIVOR, ROLE_GROUP_SPECIES);
case 2: PlayerRole_Player_AssignRole(pPlayer, ROLE_ZOMBIE, ROLE_GROUP_SPECIES);
}
return HAM_HANDLED;
}
public HamHook_Player_GetMaxSpeed(const pPlayer) {
if (PlayerRole_Player_HasRole(pPlayer, ROLE_ZOMBIE)) {
new Float:flSpeed = PlayerRole_Player_CallMethod(pPlayer, ROLE_ZOMBIE, GetSpeed);
SetHamReturnFloat(flSpeed);
return HAM_OVERRIDE;
}
return HAM_IGNORED;
}
public client_disconnected(pPlayer) {
PlayerRole_Player_UnassignRoles(pPlayer);
}
#pragma semicolon 1
#include <amxmodx>
#include <api_player_roles>
/*--------------------------------[ Constants ]--------------------------------*/
#define ROLE "Test"
#define GetMaxSpeed "GetMaxSpeed"
#define GetMaxHealth "GetMaxHealth"
/*--------------------------------[ Plugin Initialization ]--------------------------------*/
public plugin_precache() {
PlayerRole_Register(ROLE);
PlayerRole_ImplementMethod(ROLE, PlayerRole_Method_Assign, "@Role_Assign");
PlayerRole_ImplementMethod(ROLE, PlayerRole_Method_Unassign, "@Role_Unassign");
PlayerRole_RegisterVirtualMethod(ROLE, GetMaxSpeed, "@Role_GetMaxSpeed");
PlayerRole_RegisterVirtualMethod(ROLE, GetMaxHealth, "@Role_GetMaxHealth");
}
public plugin_init() {
register_plugin("[Role] Test", "1.0.0", "Author");
}
/*--------------------------------[ Methods ]--------------------------------*/
@Role_Assign(const pPlayer) {}
@Role_Unassign(const pPlayer) {}
Float:@Role_GetMaxSpeed(const pPlayer) {
return 250.0;
}
Float:@Role_GetMaxHealth(const pPlayer) {
return 100.0;
}
plugin_precacheThis_ variants inside role methodsPlayerRole_This_CallBaseMethod() in inherited roles#define ROLE_ prefixtools
Best practices for organizing large AMX Mod X projects with multiple plugins sharing entities, weapons, events, and other definitions. Use when creating mod-scale projects that need consistent namespace patterns and shared constants.
development
Guide for Waypoint Markers API creating 3D waypoint sprites visible through walls with per-player visibility.
development
Guide for States API usage implementing state machines with transitions, guards, and lifecycle hooks.
development
Guide for Shops API usage creating in-game shops with items, custom balance systems, and access control.