Skip to content

Commit 6e4c6d0

Browse files
committed
add future readme
1 parent d334f5d commit 6e4c6d0

File tree

1 file changed

+212
-0
lines changed

1 file changed

+212
-0
lines changed
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
Here is the improved documentation with grammar corrections and enhanced clarity:
2+
3+
# Custom Entities API
4+
5+
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.
6+
7+
## Implementing a Custom Entity
8+
9+
### Registering a New Entity Class
10+
11+
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`.
12+
13+
Let's create a `key` item entity:
14+
15+
```cpp
16+
#include <amxmodx>
17+
#include <fakemeta>
18+
#include <api_custom_entities>
19+
20+
public plugin_precache() {
21+
CE_RegisterClass("item_key", CEPreset_Item);
22+
}
23+
```
24+
25+
In this example, the `CEPreset_Item` preset class is used to implement the item. It inherits logic for items such as pickup methods.
26+
27+
### Setting Entity Properties
28+
29+
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 properties:
30+
31+
```cpp
32+
public plugin_precache() {
33+
// Precaching key model
34+
precache_model("models/w_security.mdl");
35+
36+
CE_RegisterClass("item_key", CEPreset_Item);
37+
38+
CE_ImplementClassMethod("item_key", CEMethod_Allocate, "@KeyItem_Allocate");
39+
}
40+
41+
@KeyItem_Allocate(const this) {
42+
CE_CallBaseMethod(); // Calling the base Allocate method
43+
44+
CE_SetMemberString(this, CE_MEMBER_MODEL, "models/w_security.mdl");
45+
CE_SetMemberVec(this, CE_MEMBER_MINS, Float:{-8.0, -8.0, 0.0});
46+
CE_SetMemberVec(this, CE_MEMBER_MAXS, Float:{8.0, 8.0, 8.0});
47+
}
48+
```
49+
50+
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.
51+
52+
> Caution: When calling CE_CallBaseMethod, you need to pass all method arguments to ensure the base method receives the necessary context for its operations.
53+
54+
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.
55+
56+
### Writing Logic for the Entity
57+
58+
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.
59+
60+
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`:
61+
62+
```cpp
63+
new g_rgbPlayerHasKey[MAX_PLAYERS + 1];
64+
65+
public plugin_precache() {
66+
CE_RegisterClass("item_key", CEPreset_Item);
67+
68+
CE_ImplementClassMethod("item_key", CEMethod_Allocate, "@KeyItem_Allocate");
69+
CE_ImplementClassMethod("item_key", CEMethod_CanPickup, "@KeyItem_CanPickup");
70+
CE_ImplementClassMethod("item_key", CEMethod_Pickup, "@KeyItem_Pickup");
71+
}
72+
73+
@KeyItem_Allocate(const this) { ... }
74+
75+
@KeyItem_CanPickup(const this, const pPlayer) {
76+
// Base implementation returns false if the item is not on the ground
77+
if (!CE_CallBaseMethod(pPlayer)) return false;
78+
79+
// Can't pick up if already holding a key
80+
if (g_rgbPlayerHasKey[pPlayer]) return false;
81+
82+
return true;
83+
}
84+
85+
@KeyItem_Pickup(const this, const pPlayer) {
86+
CE_CallBaseMethod(pPlayer);
87+
88+
client_print(pPlayer, print_center, "You have found a key!");
89+
90+
g_rgbPlayerHasKey[pPlayer] = true;
91+
}
92+
```
93+
94+
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.
95+
96+
### Custom Properties
97+
98+
If you want to implement different key types, you can use custom members. Let's update our logic and improve the code:
99+
100+
```cpp
101+
#include <amxmodx>
102+
#include <fakemeta>
103+
104+
#include <api_custom_entities>
105+
106+
#define ENTITY_CLASSNAME "item_key"
107+
108+
#define m_iType "iType"
109+
110+
enum KeyType {
111+
KeyType_Red = 0,
112+
KeyType_Yellow,
113+
KeyType_Green,
114+
KeyType_Blue
115+
};
116+
117+
new const KEY_NAMES[KeyType][] = { "red", "yellow", "green", "blue" };
118+
119+
new const Float:KEY_COLORS_F[KeyType][3] = {
120+
{255.0, 0.0, 0.0},
121+
{255.0, 255.0, 0.0},
122+
{0.0, 255.0, 0.0},
123+
{0.0, 0.0, 255.0},
124+
};
125+
126+
new const g_szModel[] = "models/w_security.mdl";
127+
128+
new bool:g_rgbPlayerHasKey[MAX_PLAYERS + 1][KeyType];
129+
130+
public plugin_precache() {
131+
precache_model(g_szModel);
132+
133+
CE_RegisterClass(ENTITY_CLASSNAME, CEPreset_Item);
134+
135+
CE_ImplementClassMethod(ENTITY_CLASSNAME, CEMethod_Allocate, "@KeyItem_Allocate");
136+
CE_ImplementClassMethod(ENTITY_CLASSNAME, CEMethod_Spawn, "@KeyItem_Spawn");
137+
CE_ImplementClassMethod(ENTITY_CLASSNAME, CEMethod_CanPickup, "@KeyItem_CanPickup");
138+
CE_ImplementClassMethod(ENTITY_CLASSNAME, CEMethod_Pickup, "@KeyItem_Pickup");
139+
140+
// Bind the "type" entity key to the "m_iType" entity member
141+
CE_RegisterClassKeyMemberBinding(ENTITY_CLASSNAME, "type", m_iType, CEMemberType_Cell);
142+
}
143+
144+
@KeyItem_Allocate(const this) {
145+
CE_CallBaseMethod();
146+
147+
CE_SetMemberString(this, CE_MEMBER_MODEL, g_szModel);
148+
CE_SetMemberVec(this, CE_MEMBER_MINS, Float:{-8.0, -8.0, 0.0});
149+
CE_SetMemberVec(this, CE_MEMBER_MAXS, Float:{8.0, 8.0, 8.0});
150+
151+
CE_SetMember(this, m_iType, KeyType_Red); // Default key type
152+
}
153+
154+
@KeyItem_Spawn(const this) {
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)