Skip to content

Commit f4d5846

Browse files
committed
add future version
1 parent d334f5d commit f4d5846

File tree

4 files changed

+2744
-0
lines changed

4 files changed

+2744
-0
lines changed
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# Custom Entities API
2+
3+
The Custom Entities API provides a flexible framework for managing and creating custom entities. This API allows developers to register, spawn, manipulate, and interact with custom entities, defining their behavior through hooks and methods.
4+
5+
## Implementing a Custom Entity
6+
7+
### 📚 Registering a New Entity Class
8+
9+
To implement a custom entity, the first thing you need to do is register a new entity class using the `CE_RegisterClass` native function. This can be done in the `plugin_precache` function, allowing you to place your entities directly on the map using the registered class as the `classname`.
10+
11+
Let's create a `key` item entity:
12+
13+
```cpp
14+
#include <amxmodx>
15+
#include <fakemeta>
16+
#include <api_custom_entities>
17+
18+
public plugin_precache() {
19+
CE_RegisterClass("item_key", CEPreset_Item);
20+
}
21+
```
22+
23+
In this example, the `CEPreset_Item` preset class is used to implement the item. It inherits logic for items such as pickup methods.
24+
25+
### ⚙️ Setting Entity Members
26+
27+
The entity currently lacks a model and size, so let's provide them by implementing the `Allocate` method for the entity to supply all the necessary members:
28+
29+
```cpp
30+
public plugin_precache() {
31+
// Precaching key model
32+
precache_model("models/w_security.mdl");
33+
34+
CE_RegisterClass("item_key", CEPreset_Item);
35+
36+
CE_ImplementClassMethod("item_key", CEMethod_Allocate, "@KeyItem_Allocate");
37+
}
38+
39+
@KeyItem_Allocate(const this) {
40+
CE_CallBaseMethod(); // Calling the base Allocate method
41+
42+
CE_SetMemberString(this, CE_MEMBER_MODEL, "models/w_security.mdl");
43+
CE_SetMemberVec(this, CE_MEMBER_MINS, Float:{-8.0, -8.0, 0.0});
44+
CE_SetMemberVec(this, CE_MEMBER_MAXS, Float:{8.0, 8.0, 8.0});
45+
}
46+
```
47+
48+
In the implementation of the `Allocate` method, the `CE_CallBaseMethod()` call allows us to invoke the base `Allocate` method of the `CEPreset_Item` preset class, allowing it to handle its own allocation logic before executing custom logic. Make sure to include this call in every implemented or overridden method unless you need to fully rewrite the implementation.
49+
50+
> Caution: When calling CE_CallBaseMethod, you need to pass all method arguments to ensure the base method receives the necessary context for its operations.
51+
52+
Natives like `CE_SetMemberString` and `CE_SetMemberVec` are used to set members/properties for the entity instance. Constants such as `CE_MEMBER_*` are used to specify the property names that will set the model each time the entity is spawned or its variables are reset. For example, `CE_MEMBER_MODEL` sets `pev->model` of the entity every respawn. Similarly, `CE_MEMBER_MINS` and `CE_MEMBER_MAXS` specify the entity's bounding box.
53+
54+
### 💡 Writing Logic for the Entity
55+
56+
Our `item_key` entity is functional, allowing you to place the entity with the classname `item_key` on your map. It will spawn in the game and can be picked up.
57+
58+
However, we still need to add some logic to the entity, as it currently does not perform any specific actions. Let's implement the `Pickup` and `CanPickup` methods in the same way we implemented `Allocate`:
59+
60+
```cpp
61+
new g_rgbPlayerHasKey[MAX_PLAYERS + 1];
62+
63+
public plugin_precache() {
64+
CE_RegisterClass("item_key", CEPreset_Item);
65+
66+
CE_ImplementClassMethod("item_key", CEMethod_Allocate, "@KeyItem_Allocate");
67+
CE_ImplementClassMethod("item_key", CEMethod_CanPickup, "@KeyItem_CanPickup");
68+
CE_ImplementClassMethod("item_key", CEMethod_Pickup, "@KeyItem_Pickup");
69+
}
70+
71+
@KeyItem_Allocate(const this) { ... }
72+
73+
@KeyItem_CanPickup(const this, const pPlayer) {
74+
// Base implementation returns false if the item is not on the ground
75+
if (!CE_CallBaseMethod(pPlayer)) return false;
76+
77+
// Can't pick up if already holding a key
78+
if (g_rgbPlayerHasKey[pPlayer]) return false;
79+
80+
return true;
81+
}
82+
83+
@KeyItem_Pickup(const this, const pPlayer) {
84+
CE_CallBaseMethod(pPlayer);
85+
86+
client_print(pPlayer, print_center, "You have found a key!");
87+
88+
g_rgbPlayerHasKey[pPlayer] = true;
89+
}
90+
```
91+
92+
This simple implementation will display the text `"You have found a key!"` to the player who picks up the key and mark that the player has picked up a key.
93+
94+
### 🧩 Custom Members
95+
96+
If you want to implement different key types, you can use custom members. Let's update our logic and improve the code:
97+
98+
```cpp
99+
#include <amxmodx>
100+
#include <fakemeta>
101+
102+
#include <api_custom_entities>
103+
104+
#define ENTITY_CLASSNAME "item_key"
105+
106+
#define m_iType "iType"
107+
108+
enum KeyType {
109+
KeyType_Red = 0,
110+
KeyType_Yellow,
111+
KeyType_Green,
112+
KeyType_Blue
113+
};
114+
115+
new const KEY_NAMES[KeyType][] = { "red", "yellow", "green", "blue" };
116+
117+
new const Float:KEY_COLORS_F[KeyType][3] = {
118+
{255.0, 0.0, 0.0},
119+
{255.0, 255.0, 0.0},
120+
{0.0, 255.0, 0.0},
121+
{0.0, 0.0, 255.0},
122+
};
123+
124+
new const g_szModel[] = "models/w_security.mdl";
125+
126+
new bool:g_rgbPlayerHasKey[MAX_PLAYERS + 1][KeyType];
127+
128+
public plugin_precache() {
129+
precache_model(g_szModel);
130+
131+
CE_RegisterClass(ENTITY_CLASSNAME, CEPreset_Item);
132+
133+
CE_ImplementClassMethod(ENTITY_CLASSNAME, CEMethod_Allocate, "@KeyItem_Allocate");
134+
CE_ImplementClassMethod(ENTITY_CLASSNAME, CEMethod_Spawn, "@KeyItem_Spawn");
135+
CE_ImplementClassMethod(ENTITY_CLASSNAME, CEMethod_CanPickup, "@KeyItem_CanPickup");
136+
CE_ImplementClassMethod(ENTITY_CLASSNAME, CEMethod_Pickup, "@KeyItem_Pickup");
137+
138+
// Bind the "type" entity key to the "m_iType" entity member
139+
CE_RegisterClassKeyMemberBinding(ENTITY_CLASSNAME, "type", m_iType, CEMemberType_Cell);
140+
}
141+
142+
@KeyItem_Allocate(const this) {
143+
CE_CallBaseMethod();
144+
145+
CE_SetMemberString(this, CE_MEMBER_MODEL, g_szModel);
146+
CE_SetMemberVec(this, CE_MEMBER_MINS, Float:{-8.0, -8.0, 0.0});
147+
CE_SetMemberVec(this, CE_MEMBER_MAXS, Float:{8.0, 8.0, 8.0});
148+
149+
CE_SetMember(this, m_iType, KeyType_Red); // Default key type
150+
}
151+
152+
@KeyItem_Spawn(const this) {
153+
CE_CallBaseMethod();
154+
155+
new KeyType:iType = CE_GetMember(this, m_iType);
156+
157+
// Adding rendering effect based on key type
158+
set_pev(this, pev_renderfx, kRenderFxGlowShell);
159+
set_pev(this, pev_renderamt, 1.0);
160+
set_pev(this, pev_rendercolor, KEY_COLORS_F[iType]);
161+
}
162+
163+
@KeyItem_CanPickup(const this, const pPlayer) {
164+
if (!CE_CallBaseMethod(pPlayer)) return false;
165+
166+
new KeyType:iType = CE_GetMember(this, m_iType);
167+
168+
if (g_rgbPlayerHasKey[pPlayer][iType]) return false;
169+
170+
return true;
171+
}
172+
173+
@KeyItem_Pickup(const this, const pPlayer) {
174+
CE_CallBaseMethod(pPlayer);
175+
176+
new KeyType:iType = CE_GetMember(this, m_iType);
177+
178+
client_print(pPlayer, print_center, "You have found a %s key!", KEY_NAMES[iType]);
179+
180+
g_rgbPlayerHasKey[pPlayer][iType] = true;
181+
}
182+
```
183+
184+
Here, we added `KeyType` constants to represent different key types and implemented the `Spawn` method to set rendering effects based on the key type.
185+
186+
You may have noticed the constant `m_iType`, which is a string constant used for the custom member we work with using `CE_GetMember` and `CE_SetMember` natives. We also use `CE_RegisterClassKeyMemberBinding` to bind this member to the entity key `type`, allowing us to change the key type by setting the `type` key-value on the map.
187+
188+
### 🕵️‍♂️ Testing and Debugging
189+
190+
> What if we don't have a map yet to test it? Is there another way to spawn our entity?
191+
192+
Yes, there are a few ways to do it!
193+
194+
#### Spawning an Entity Using the Console
195+
196+
You can spawn an entity using the console command `ce_spawn <classname> [...members]`. The `<classname>` parameter is the `classname` of the registered entity, and `[...members]` are optional parameters to set before spawning. Let's spawn a `"Green"` key:
197+
198+
```cpp
199+
ce_spawn "item_key" "iType" 3
200+
```
201+
202+
### Spawning an Entity with Code
203+
204+
You can also create the entity using the `CE_Create` native function and then call the engine `Spawn` function on it:
205+
206+
```cpp
207+
new pKey = CE_Create("item_key", vecOrigin);
208+
209+
if (pKey != FM_NULLENT) {
210+
dllfunc(DLLFunc_Spawn, pKey);
211+
}
212+
```

0 commit comments

Comments
 (0)