AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Code Snippets/Tutorials (https://forums.alliedmods.net/forumdisplay.php?f=83)
-   -   Dynamic Items in Menu and Plugin API (https://forums.alliedmods.net/showthread.php?t=123445)

Exolent[jNr] 04-05-2010 12:44

Dynamic Items in Menu and Plugin API
 
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:
  1. A function executed in the sub-plugin that sends data to the main plugin so the item is recognized.
  2. 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

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:
  1. A function executed in the sub-plugin that sends data to the main plugin so the item is recognized.
  2. 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.

Seta00 04-05-2010 14:10

Re: Dynamic Items in Menu and Plugin API
 
Nice! A guy requested me this last week, but I haven't got time to do this, so thank you Exolent.

Exolent[jNr] 04-05-2010 14:22

Re: Dynamic Items in Menu and Plugin API
 
I'm glad you find it useful. :)

fezh 04-05-2010 18:02

Re: Dynamic Items in Menu and Plugin API
 
Nice :up:

wrecked_ 04-05-2010 23:04

Re: Dynamic Items in Menu and Plugin API
 
I had a few people ask me how this type of thing would work (natives to interact with the base plugin) and they were using ZP as an example, which confused them. A lot. This is certainly a better example.

Awesome job. :lol:

abdul-rehman 04-06-2010 05:34

Re: Dynamic Items in Menu and Plugin API
 
Coool i needed this for my ZP New Modes

Seta00 04-06-2010 11:05

Re: Dynamic Items in Menu and Plugin API
 
@Exolent
How about making specific forwards for each menu item?
Code:
shop_add_item("Durgs", 1337, "MyCoolAndShinyHandler"); public MyCoolAndShinyHandler() {     // la la la la }

Exolent[jNr] 04-06-2010 12:44

Re: Dynamic Items in Menu and Plugin API
 
Quote:

Originally Posted by Seta00 (Post 1141306)
@Exolent
How about making specific forwards for each menu item?
Code:
shop_add_item("Durgs", 1337, "MyCoolAndShinyHandler"); public MyCoolAndShinyHandler() {     // la la la la }

The purpose was to show how it would be done like Zombie Plague.
I can add more features to this like what you're suggesting, but people can figure those features out themselves.
They normally have problems just figuring out how to do this basic API.

padilha007 04-14-2010 11:38

Re: Dynamic Items in Menu and Plugin API
 
Quote:

// format item for menu
formatex( szItem, charsmax(szItem), "%s\R\y%i", eItemData[ ItemName ], eItemData[ ItemCost ] );

drekes 04-14-2010 13:17

Re: Dynamic Items in Menu and Plugin API
 
nice tutorial.
I just don't understand the #pragma thing.

And don't we include the shop in the plugin? Can you explain that?


All times are GMT -4. The time now is 23:23.

Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.