Introduction:
I've seen many users ask how to create dynamic classes or items with sub-plugins like in Zombie Plague.
This tutorial will teach you just how to do that.
This tutorial is not for new users to AMXX scripting!
In this tutorial, you will need knowledge of:
For this tutorial, I'm going to use a "shop" for an example.
It will show a menu on spawn which contains the items.
Integration File (.inc):
Let's call it shop.inc:
Code:
#if defined _shop_included
#endinput
#endif
#define _shop_included
That code will make sure if included more than once in a single plugin, it won't duplicate its contents.
First, you will need a custom library for this.
Let's call it "shop"
Code:
#if AMXX_VERSION_NUM >= 175
#pragma reqlib shop
#if !defined AMXMODX_NOAUTOLOAD
#pragma loadlib shop
#endif
#else
#pragma library shop
#endif
Next, you will need 2 functions that will integrate your plugins:
- A function executed in the sub-plugin that sends data to the main plugin so the item is recognized.
- A forward executed from the main plugin and is hooked in the sub-plugin to know when the item is selected.
If the sub-plugin is going to be an item, then you will need to pass all the information about the item into the function.
To keep it simple, we will only need:
So, here is how our function would look.
Code:
native shop_item_add( const szName[ ], const iCost );
The function should return a unique item index so it can be separate from other plugins.
In the forward when the player selects the item, it will need to pass the player's index and the unique item index.
So, here is how the forward would look.
Code:
forward shop_item_selected( iPlayer, iItemIndex );
Here is our complete shop.inc file:
Code:
#if defined _shop_included
#endinput
#endif
#define _shop_included
#if AMXX_VERSION_NUM >= 175
#pragma reqlib shop
#if !defined AMXMODX_NOAUTOLOAD
#pragma loadlib shop
#endif
#else
#pragma library shop
#endif
native shop_item_add( const szName[ ], const iCost );
forward shop_item_selected( iPlayer, iItemIndex );
Main Plugin:
I commented the plugin very well.
If you have knowledge that was listed above, you should be able to understand everything with just the comments.
Code:
#include < amxmodx >
#include < cstrike >
#include < hamsandwich >
// create the structure for each item
// we need the item name and cost
enum _:ItemData
{
ItemName[ 32 ],
ItemCost
};
// create a dynamic array to hold all the items
new Array:g_aItems;
// this will tell how many are in the array instead of using ArraySize()
new g_iTotalItems;
// create a forward for when player selects an item
new g_hSelectItemForward;
public plugin_init( )
{
// register player spawn to show the shop menu
RegisterHam( Ham_Spawn, "player", "FwdPlayerSpawnPost", 1 );
// create our array with the size of the item structure
g_aItems = ArrayCreate( ItemData );
// create our forward
g_hSelectItemForward = CreateMultiForward( "shop_item_selected", ET_IGNORE, FP_CELL, FP_CELL );
}
public plugin_natives( )
{
// register our custom library
register_library( "shop" );
// create a native to allow other plugins to add items
register_native( "shop_item_add", "_item_add" );
}
public _item_add( iPlugin, iParams )
{
// create an array to hold our item data
new eItemData[ ItemData ];
// get item name from function
get_string( 1, eItemData[ ItemName ], charsmax( eItemData[ ItemName ] ) );
// get item cost from function
eItemData[ ItemCost ] = get_param( 2 );
// add item to array and increase size
ArrayPushArray( g_aItems, eItemData );
g_iTotalItems++;
// return the index of this item in the array
// this creates the unique item index
return ( g_iTotalItems - 1 );
}
public FwdPlayerSpawnPost( iPlayer )
{
// check if player spawned properly
if( is_user_alive( iPlayer ) )
{
// show the shop menu starting with the first page
// don't understand this function call?
// <a href="http://forums.alliedmods.net/showthread.php?t=90480" target="_blank" rel="nofollow noopener">http://forums.alliedmods.net/showthread.php?t=90480</a>
ShowShopMenu( iPlayer, .iPage = 0 );
}
}
ShowShopMenu( iPlayer, iPage )
{
// check if there are no items
if( !g_iTotalItems )
{
return;
}
// clamp page to valid range of pages
iPage = clamp( iPage, 0, ( g_iTotalItems - 1 ) / 7 );
// create menu
new hMenu = menu_create( "Shop Menu", "MenuShop" );
// used to display item in the menu
new eItemData[ ItemData ];
new szItem[ 64 ];
// used for array index to menu
new szNum[ 3 ];
// loop through each item
for( new i = 0; i < g_iTotalItems; i++ )
{
// get item data from array
ArrayGetArray( g_aItems, i, eItemData );
// format item for menu
formatex( szItem, charsmax( szItem ), "%s\R\y%i", eItemData[ ItemName ], eItemData[ ItemCost ] );
// pass array index to menu to find information about it later
num_to_str( i, szNum, charsmax( szNum ) );
// add item to menu
menu_additem( hMenu, szItem, szNum );
}
// display menu to player
menu_display( iPlayer, hMenu, iPage );
}
public MenuShop( iPlayer, hMenu, iItem )
{
if( iItem == MENU_EXIT )
{
menu_destroy( hMenu );
return;
}
new iAccess, szNum[ 3 ], hCallback;
menu_item_getinfo( hMenu, iItem, iAccess, szNum, charsmax( szNum ), _, _, hCallback );
menu_destroy( hMenu );
// get item index from menu
new iItemIndex = str_to_num( szNum );
// get item data from array
new eItemData[ ItemData ];
ArrayGetArray( g_aItems, iItemIndex, eItemData );
// get player money and subtract the cost
new iMoney = cs_get_user_money( iPlayer ) - eItemData[ ItemCost ];
// result money will be < 0 if not enough money
if( iMoney < 0 )
{
// notify player
client_print( iPlayer, print_chat, "* You need $%i more to buy this item!", ( iMoney * -1 ) );
}
else
{
// set player money
cs_set_user_money( iPlayer, iMoney );
// notify plugins that the player bought this item
new iReturn;
ExecuteForward( g_hSelectItemForward, iReturn, iPlayer, iItemIndex );
}
ShowShopMenu( iPlayer, .iPage = ( iItemIndex / 7 ) );
}
Sub-Plugin:
Code:
#include < amxmodx >
#include < fun >
#include < shop >
// save item index
new g_iItemIndex;
public plugin_init( )
{
// register item with main plugin and store item index
g_iItemIndex = shop_item_add( "Low Gravity", 2000 );
}
// catch when items are bought
public shop_item_selected( iPlayer, iItemIndex )
{
// check if bought item is the one from this plugin
if( g_iItemIndex == iItemIndex )
{
// set gravity
set_user_gravity( iPlayer, 0.25 );
}
}
___________________________
Here is how it would be done with specific callback functions.
Introduction:
I've seen many users ask how to create dynamic classes or items with sub-plugins like in Zombie Plague.
This tutorial will teach you just how to do that.
This tutorial is not for new users to AMXX scripting!
In this tutorial, you will need knowledge of:
For this tutorial, I'm going to use a "shop" for an example.
It will show a menu on spawn which contains the items.
Integration File (.inc):
Let's call it shop.inc:
Code:
#if defined _shop_included
#endinput
#endif
#define _shop_included
That code will make sure if included more than once in a single plugin, it won't duplicate its contents.
First, you will need a custom library for this.
Let's call it "shop"
Code:
#if AMXX_VERSION_NUM >= 175
#pragma reqlib shop
#if !defined AMXMODX_NOAUTOLOAD
#pragma loadlib shop
#endif
#else
#pragma library shop
#endif
Next, you will need 2 functions that will integrate your plugins:
- A function executed in the sub-plugin that sends data to the main plugin so the item is recognized.
- A forward executed from the main plugin and is hooked in the sub-plugin to know when the item is selected.
If the sub-plugin is going to be an item, then you will need to pass all the information about the item into the function.
To keep it simple, we will only need:
- item name
- item cost
- callback function
So, here is how our function would look.
Code:
native shop_item_add( const szName[ ], const iCost, const szHandler[ ] );
The function should uses a callback handler so it can be separate from other plugins.
Here is our complete shop.inc file:
Code:
#if defined _shop_included
#endinput
#endif
#define _shop_included
#if AMXX_VERSION_NUM >= 175
#pragma reqlib shop
#if !defined AMXMODX_NOAUTOLOAD
#pragma loadlib shop
#endif
#else
#pragma library shop
#endif
native shop_item_add( const szName[ ], const iCost, const szHandler[ ] );
Main Plugin:
I commented the plugin very well.
If you have knowledge that was listed above, you should be able to understand everything with just the comments.
Code:
#include < amxmodx >
#include < cstrike >
#include < hamsandwich >
// create the structure for each item
// we need the item name, cost, plugin adding it, and the callback function
enum _:ItemData
{
ItemName[ 32 ],
ItemCost,
ItemPlugin,
ItemFuncID
};
// create a dynamic array to hold all the items
new Array:g_aItems;
// this will tell how many are in the array instead of using ArraySize()
new g_iTotalItems;
public plugin_init( )
{
// register player spawn to show the shop menu
RegisterHam( Ham_Spawn, "player", "FwdPlayerSpawnPost", 1 );
// create our array with the size of the item structure
g_aItems = ArrayCreate( ItemData );
}
public plugin_natives( )
{
// register our custom library
register_library( "shop" );
// create a native to allow other plugins to add items
register_native( "shop_item_add", "_item_add" );
}
public _item_add( iPlugin, iParams )
{
// create an array to hold our item data
new eItemData[ ItemData ];
// get item name from function
get_string( 1, eItemData[ ItemName ], charsmax( eItemData[ ItemName ] ) );
// get item cost from function
eItemData[ ItemCost ] = get_param( 2 );
// save the plugin adding the item
eItemData[ ItemPlugin ] = iPlugin;
// get item callback function
new szHandler[ 32 ];
get_string( 3, szHandler, charsmax( szHandler ) );
eItemData[ ItemFuncID ] = get_func_id( szHandler, iPlugin );
// add item to array and increase size
ArrayPushArray( g_aItems, eItemData );
g_iTotalItems++;
// return the index of this item in the array
// this creates the unique item index
return ( g_iTotalItems - 1 );
}
public FwdPlayerSpawnPost( iPlayer )
{
// check if player spawned properly
if( is_user_alive( iPlayer ) )
{
// show the shop menu starting with the first page
// don't understand this function call?
// <a href="http://forums.alliedmods.net/showthread.php?t=90480" target="_blank" rel="nofollow noopener">http://forums.alliedmods.net/showthread.php?t=90480</a>
ShowShopMenu( iPlayer, .iPage = 0 );
}
}
ShowShopMenu( iPlayer, iPage )
{
// check if there are no items
if( !g_iTotalItems )
{
return;
}
// clamp page to valid range of pages
iPage = clamp( iPage, 0, ( g_iTotalItems - 1 ) / 7 );
// create menu
new hMenu = menu_create( "Shop Menu", "MenuShop" );
// used to display item in the menu
new eItemData[ ItemData ];
new szItem[ 64 ];
// used for array index to menu
new szNum[ 3 ];
// loop through each item
for( new i = 0; i < g_iTotalItems; i++ )
{
// get item data from array
ArrayGetArray( g_aItems, i, eItemData );
// format item for menu
formatex( szItem, charsmax( szItem ), "%s\R\y%i", eItemData[ ItemName ], eItemData[ ItemCost ] );
// pass array index to menu to find information about it later
num_to_str( i, szNum, charsmax( szNum ) );
// add item to menu
menu_additem( hMenu, szItem, szNum );
}
// display menu to player
menu_display( iPlayer, hMenu, iPage );
}
public MenuShop( iPlayer, hMenu, iItem )
{
if( iItem == MENU_EXIT )
{
menu_destroy( hMenu );
return;
}
new iAccess, szNum[ 3 ], hCallback;
menu_item_getinfo( hMenu, iItem, iAccess, szNum, charsmax( szNum ), _, _, hCallback );
menu_destroy( hMenu );
// get item index from menu
new iItemIndex = str_to_num( szNum );
// get item data from array
new eItemData[ ItemData ];
ArrayGetArray( g_aItems, iItemIndex, eItemData );
// get player money and subtract the cost
new iMoney = cs_get_user_money( iPlayer ) - eItemData[ ItemCost ];
// result money will be < 0 if not enough money
if( iMoney < 0 )
{
// notify player
client_print( iPlayer, print_chat, "* You need $%i more to buy this item!", ( iMoney * -1 ) );
}
else
{
// set player money
cs_set_user_money( iPlayer, iMoney );
// notify the plugin that the player bought this item
callfunc_begin_i( eItemData[ ItemFuncID ], eItemData[ ItemPlugin ] );
callfunc_push_int( iPlayer );
callfunc_end( );
}
ShowShopMenu( iPlayer, .iPage = ( iItemIndex / 7 ) );
}
Sub-Plugin:
Code:
#include < amxmodx >
#include < fun >
#include < shop >
public plugin_init( )
{
// register item with main plugin and store item index
shop_item_add( "Low Gravity", 2000, "ShopLowGravity" );
}
// player bought low gravity
public ShopLowGravity( iPlayer )
{
// set gravity
set_user_gravity( iPlayer, 0.25 );
}
___________________________
Last Notes:
Don't post here asking how to fix your mod.
Feel free to post if you don't understand something about this or if you have suggestions to better this information.
__________________