@nodemod/core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/dist/core/cmd.d.ts +148 -0
- package/dist/core/cmd.d.ts.map +1 -0
- package/dist/core/cmd.js +177 -0
- package/dist/core/cmd.js.map +1 -0
- package/dist/core/menu.d.ts +300 -0
- package/dist/core/menu.d.ts.map +1 -0
- package/dist/core/menu.js +449 -0
- package/dist/core/menu.js.map +1 -0
- package/dist/core/msg.d.ts +300 -0
- package/dist/core/msg.d.ts.map +1 -0
- package/dist/core/msg.js +374 -0
- package/dist/core/msg.js.map +1 -0
- package/dist/core/resource.d.ts +137 -0
- package/dist/core/resource.d.ts.map +1 -0
- package/dist/core/resource.js +171 -0
- package/dist/core/resource.js.map +1 -0
- package/dist/core/sound.d.ts +262 -0
- package/dist/core/sound.d.ts.map +1 -0
- package/dist/core/sound.js +300 -0
- package/dist/core/sound.js.map +1 -0
- package/dist/enhanced/entity.d.ts +263 -0
- package/dist/enhanced/entity.d.ts.map +1 -0
- package/dist/enhanced/entity.js +447 -0
- package/dist/enhanced/entity.js.map +1 -0
- package/dist/enhanced/events.d.ts +257 -0
- package/dist/enhanced/events.d.ts.map +1 -0
- package/dist/enhanced/events.js +350 -0
- package/dist/enhanced/events.js.map +1 -0
- package/dist/enhanced/player.d.ts +272 -0
- package/dist/enhanced/player.d.ts.map +1 -0
- package/dist/enhanced/player.js +389 -0
- package/dist/enhanced/player.js.map +1 -0
- package/dist/enhanced/trace.d.ts +198 -0
- package/dist/enhanced/trace.d.ts.map +1 -0
- package/dist/enhanced/trace.js +311 -0
- package/dist/enhanced/trace.js.map +1 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +120 -0
- package/dist/index.js.map +1 -0
- package/dist/native/cvar.d.ts +49 -0
- package/dist/native/cvar.d.ts.map +1 -0
- package/dist/native/cvar.js +169 -0
- package/dist/native/cvar.js.map +1 -0
- package/dist/native/file.d.ts +221 -0
- package/dist/native/file.d.ts.map +1 -0
- package/dist/native/file.js +353 -0
- package/dist/native/file.js.map +1 -0
- package/dist/types/dll.d.ts +109 -0
- package/dist/types/engine.d.ts +319 -0
- package/dist/types/enums.d.ts +434 -0
- package/dist/types/events.d.ts +2432 -0
- package/dist/types/index.d.ts +38 -0
- package/dist/types/structures.d.ts +1144 -0
- package/dist/utils/util.d.ts +202 -0
- package/dist/utils/util.d.ts.map +1 -0
- package/dist/utils/util.js +318 -0
- package/dist/utils/util.js.map +1 -0
- package/package.json +167 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SoundChannel = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Audio channel constants for categorizing sounds.
|
|
6
|
+
*/
|
|
7
|
+
exports.SoundChannel = {
|
|
8
|
+
/** Automatically select appropriate channel */
|
|
9
|
+
auto: 0,
|
|
10
|
+
/** Weapon sounds */
|
|
11
|
+
weapon: 1,
|
|
12
|
+
/** Voice communications */
|
|
13
|
+
voice: 2,
|
|
14
|
+
/** Item interaction sounds */
|
|
15
|
+
item: 3,
|
|
16
|
+
/** Body/movement sounds */
|
|
17
|
+
body: 4,
|
|
18
|
+
/** Streaming audio */
|
|
19
|
+
stream: 5,
|
|
20
|
+
/** Static/ambient sounds */
|
|
21
|
+
static: 6,
|
|
22
|
+
/** Network voice communication base */
|
|
23
|
+
networkVoiceBase: 7,
|
|
24
|
+
/** Network voice communication end */
|
|
25
|
+
networkVoiceEnd: 500
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Enhanced sound system providing comprehensive audio functionality for the game server.
|
|
29
|
+
* Handles sound emission, validation, and management with improved defaults and error handling.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* // Play a weapon sound
|
|
34
|
+
* nodemodCore.sound.emitSound({
|
|
35
|
+
* entity: player,
|
|
36
|
+
* channel: SoundChannel.weapon,
|
|
37
|
+
* sound: 'weapons/ak47/ak47-1.wav',
|
|
38
|
+
* volume: 0.8,
|
|
39
|
+
* pitch: 110
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* // Broadcast ambient sound to all players
|
|
43
|
+
* nodemodCore.sound.broadcastSound('ambient/music/track1.wav', {
|
|
44
|
+
* volume: 0.5,
|
|
45
|
+
* attenuation: 0.5
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* // Play client-side sound command
|
|
49
|
+
* nodemodCore.sound.emitClientSound(player, 'ui/buttonclick');
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
class NodemodSound {
|
|
53
|
+
/** Utility service for entity operations */
|
|
54
|
+
util;
|
|
55
|
+
/**
|
|
56
|
+
* Creates a new NodemodSound instance.
|
|
57
|
+
*
|
|
58
|
+
* @param utilService - Utility service for entity operations
|
|
59
|
+
*/
|
|
60
|
+
constructor(utilService) {
|
|
61
|
+
this.util = utilService;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Emits a sound with enhanced validation and default values.
|
|
65
|
+
* Provides better parameter validation and clamping than the basic engine function.
|
|
66
|
+
*
|
|
67
|
+
* @param options - Sound emission configuration
|
|
68
|
+
* @returns True if sound was emitted successfully, false otherwise
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* // Basic sound emission
|
|
73
|
+
* nodemodCore.sound.emitSound({
|
|
74
|
+
* sound: 'weapons/ak47/ak47-1.wav'
|
|
75
|
+
* });
|
|
76
|
+
*
|
|
77
|
+
* // Advanced sound with all options
|
|
78
|
+
* nodemodCore.sound.emitSound({
|
|
79
|
+
* entity: player,
|
|
80
|
+
* channel: SoundChannel.weapon,
|
|
81
|
+
* sound: 'weapons/ak47/ak47-1.wav',
|
|
82
|
+
* volume: 0.8,
|
|
83
|
+
* attenuation: 1.5,
|
|
84
|
+
* flags: 0, // Normal playback
|
|
85
|
+
* pitch: 110
|
|
86
|
+
* });
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
emitSound(options) {
|
|
90
|
+
const { entity = 0, channel = exports.SoundChannel.auto, sound, volume = 1.0, attenuation = 1.0, flags = 0, pitch = 100 } = options;
|
|
91
|
+
if (!sound) {
|
|
92
|
+
console.error('NodemodSound.emitSound: sound parameter is required');
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const entityObj = entity ? this.util.forceEntityObject(entity) : nodemod.eng.pEntityOfEntIndex(0);
|
|
96
|
+
nodemod.eng.emitSound(entityObj, channel, sound, Math.max(0, Math.min(1, volume)), // Clamp volume 0-1
|
|
97
|
+
Math.max(0, attenuation), flags, Math.max(1, Math.min(255, pitch)) // Clamp pitch 1-255
|
|
98
|
+
);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Sends a client-side sound command to a specific player.
|
|
103
|
+
* Uses the client's `spk` command to play sounds locally.
|
|
104
|
+
*
|
|
105
|
+
* @param entity - Target player entity or index
|
|
106
|
+
* @param soundName - Name of the sound file (with or without extension)
|
|
107
|
+
* @param extension - File extension to append if not present in soundName
|
|
108
|
+
* @returns True if command was sent successfully, false otherwise
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* // Play UI sound to player
|
|
113
|
+
* nodemodCore.sound.emitClientSound(player, 'ui/buttonclick');
|
|
114
|
+
*
|
|
115
|
+
* // Play custom sound with specific extension
|
|
116
|
+
* nodemodCore.sound.emitClientSound(player, 'custom/notification', 'mp3');
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
emitClientSound(entity, soundName, extension = 'wav') {
|
|
120
|
+
const entityObj = this.util.forceEntityObject(entity);
|
|
121
|
+
if (!entityObj) {
|
|
122
|
+
console.error('NodemodSound.emitClientSound: invalid entity');
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const soundPath = soundName.includes('.') ? soundName : `${soundName}.${extension}`;
|
|
126
|
+
nodemod.eng.clientCommand(entityObj, `spk sound/${soundPath}\n`);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Emits an ambient sound at a specific 3D position.
|
|
131
|
+
* Useful for environmental sounds, explosions, or location-based audio.
|
|
132
|
+
*
|
|
133
|
+
* @param position - 3D coordinates [x, y, z] where the sound originates
|
|
134
|
+
* @param sound - Path to the sound file
|
|
135
|
+
* @param volume - Volume level (0.0 to 1.0)
|
|
136
|
+
* @param attenuation - How quickly sound fades with distance
|
|
137
|
+
* @param flags - Sound transmission flags
|
|
138
|
+
* @param pitch - Pitch adjustment (1-255, 100 = normal)
|
|
139
|
+
* @returns True if sound was emitted successfully, false otherwise
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* // Explosion sound at coordinates
|
|
144
|
+
* nodemodCore.sound.emitAmbientSound(
|
|
145
|
+
* [100, 200, 50],
|
|
146
|
+
* 'weapons/explode1.wav',
|
|
147
|
+
* 0.9,
|
|
148
|
+
* 2.0
|
|
149
|
+
* );
|
|
150
|
+
*
|
|
151
|
+
* // Ambient environmental sound
|
|
152
|
+
* nodemodCore.sound.emitAmbientSound(
|
|
153
|
+
* [0, 0, 0],
|
|
154
|
+
* 'ambient/wind/wind1.wav',
|
|
155
|
+
* 0.3,
|
|
156
|
+
* 0.5,
|
|
157
|
+
* nodemod.SND.SPAWNING
|
|
158
|
+
* );
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
emitAmbientSound(position, sound, volume = 1.0, attenuation = 1.0, flags = 0, pitch = 100) {
|
|
162
|
+
if (!sound || !Array.isArray(position) || position.length < 3) {
|
|
163
|
+
console.error('NodemodSound.emitAmbientSound: invalid parameters');
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
nodemod.eng.emitAmbientSound(null, // entity parameter - using null for ambient sounds
|
|
167
|
+
position, sound, Math.max(0, Math.min(1, volume)), Math.max(0, attenuation), flags, Math.max(1, Math.min(255, pitch)));
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Broadcasts a sound to all players on the server.
|
|
172
|
+
* Convenient wrapper for playing sounds that everyone should hear.
|
|
173
|
+
*
|
|
174
|
+
* @param sound - Path to the sound file
|
|
175
|
+
* @param options - Optional sound configuration overrides
|
|
176
|
+
* @returns True if sound was broadcast successfully, false otherwise
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* // Simple server announcement sound
|
|
181
|
+
* nodemodCore.sound.broadcastSound('admin/announcement.wav');
|
|
182
|
+
*
|
|
183
|
+
* // Round start music with custom settings
|
|
184
|
+
* nodemodCore.sound.broadcastSound('music/roundstart.wav', {
|
|
185
|
+
* volume: 0.7,
|
|
186
|
+
* channel: SoundChannel.stream,
|
|
187
|
+
* attenuation: 0.1
|
|
188
|
+
* });
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
broadcastSound(sound, options = {}) {
|
|
192
|
+
const soundOptions = {
|
|
193
|
+
entity: 0, // World entity
|
|
194
|
+
channel: exports.SoundChannel.auto,
|
|
195
|
+
sound,
|
|
196
|
+
volume: 1.0,
|
|
197
|
+
attenuation: 1.0,
|
|
198
|
+
flags: 0,
|
|
199
|
+
pitch: 100,
|
|
200
|
+
...options
|
|
201
|
+
};
|
|
202
|
+
return this.emitSound(soundOptions);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Attempts to stop a sound on a specific entity.
|
|
206
|
+
* Uses a workaround by playing a very quiet sound since direct stop may not be available.
|
|
207
|
+
*
|
|
208
|
+
* @param entity - Target entity or entity index
|
|
209
|
+
* @param channel - Audio channel to stop
|
|
210
|
+
* @param sound - Specific sound to stop (empty for all sounds on channel)
|
|
211
|
+
* @returns True if stop attempt was successful, false otherwise
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```typescript
|
|
215
|
+
* // Stop all sounds on entity
|
|
216
|
+
* nodemodCore.sound.stopSound(player);
|
|
217
|
+
*
|
|
218
|
+
* // Stop weapon channel sounds
|
|
219
|
+
* nodemodCore.sound.stopSound(player, SoundChannel.weapon);
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
stopSound(entity, channel = exports.SoundChannel.auto, sound = '') {
|
|
223
|
+
const entityObj = this.util.forceEntityObject(entity);
|
|
224
|
+
if (!entityObj)
|
|
225
|
+
return false;
|
|
226
|
+
// Fallback: emit very quiet sound to "override" since stopSound may not be available
|
|
227
|
+
return this.emitSound({
|
|
228
|
+
entity: entityObj,
|
|
229
|
+
channel,
|
|
230
|
+
sound: sound || 'common/null.wav',
|
|
231
|
+
volume: 0.001,
|
|
232
|
+
pitch: 1
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Validates whether a sound file path is valid.
|
|
237
|
+
* Checks for supported file extensions and proper format.
|
|
238
|
+
*
|
|
239
|
+
* @param soundPath - Path to the sound file to validate
|
|
240
|
+
* @returns True if the sound path is valid, false otherwise
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```typescript
|
|
244
|
+
* // Valid sound files
|
|
245
|
+
* console.log(nodemodCore.sound.isValidSound('weapons/ak47-1.wav')); // true
|
|
246
|
+
* console.log(nodemodCore.sound.isValidSound('music/track.mp3')); // true
|
|
247
|
+
*
|
|
248
|
+
* // Invalid sound files
|
|
249
|
+
* console.log(nodemodCore.sound.isValidSound('invalid.txt')); // false
|
|
250
|
+
* console.log(nodemodCore.sound.isValidSound('')); // false
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
isValidSound(soundPath) {
|
|
254
|
+
if (!soundPath || typeof soundPath !== 'string')
|
|
255
|
+
return false;
|
|
256
|
+
const validExtensions = ['wav', 'mp3'];
|
|
257
|
+
const extension = soundPath.split('.').pop()?.toLowerCase();
|
|
258
|
+
return extension ? validExtensions.includes(extension) : false;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Emits multiple sounds in batch with individual result tracking.
|
|
262
|
+
* Useful for playing multiple sounds simultaneously or handling bulk operations.
|
|
263
|
+
*
|
|
264
|
+
* @param soundConfigs - Array of sound configurations to emit
|
|
265
|
+
* @returns Array of results indicating success/failure for each sound
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```typescript
|
|
269
|
+
* const sounds = [
|
|
270
|
+
* { sound: 'weapons/ak47/ak47-1.wav', entity: player1, volume: 0.8 },
|
|
271
|
+
* { sound: 'weapons/deagle/deagle-1.wav', entity: player2, volume: 0.9 },
|
|
272
|
+
* { sound: 'items/ammopickup2.wav', volume: 0.5 }
|
|
273
|
+
* ];
|
|
274
|
+
*
|
|
275
|
+
* const results = nodemodCore.sound.emitMultipleSounds(sounds);
|
|
276
|
+
* results.forEach((result, i) => {
|
|
277
|
+
* if (result.success) {
|
|
278
|
+
* console.log(`Sound ${i} played successfully`);
|
|
279
|
+
* } else {
|
|
280
|
+
* console.log(`Sound ${i} failed: ${result.error}`);
|
|
281
|
+
* }
|
|
282
|
+
* });
|
|
283
|
+
* ```
|
|
284
|
+
*/
|
|
285
|
+
emitMultipleSounds(soundConfigs) {
|
|
286
|
+
const results = [];
|
|
287
|
+
soundConfigs.forEach((config, index) => {
|
|
288
|
+
try {
|
|
289
|
+
const result = this.emitSound(config);
|
|
290
|
+
results.push({ index, success: result, config });
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
results.push({ index, success: false, error: error instanceof Error ? error.message : String(error), config });
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
return results;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
exports.default = NodemodSound;
|
|
300
|
+
//# sourceMappingURL=sound.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sound.js","sourceRoot":"","sources":["../../src/core/sound.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import type NodemodUtil from '../utils/util';
|
|
2
|
+
/**
|
|
3
|
+
* Extended entity interface that allows dynamic property access.
|
|
4
|
+
* Useful for setting custom entity properties not defined in the base interface.
|
|
5
|
+
*/
|
|
6
|
+
export interface DynamicEntity extends nodemod.Entity {
|
|
7
|
+
/** Allow dynamic property access */
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Criteria for filtering entities in search operations.
|
|
12
|
+
*/
|
|
13
|
+
export interface EntityCriteria {
|
|
14
|
+
/** Filter by entity class name */
|
|
15
|
+
className?: string;
|
|
16
|
+
/** Filter by entity target name */
|
|
17
|
+
targetName?: string;
|
|
18
|
+
/** Filter by entity target */
|
|
19
|
+
target?: string;
|
|
20
|
+
/** Filter by model path */
|
|
21
|
+
model?: string;
|
|
22
|
+
/** Filter by health value */
|
|
23
|
+
health?: number;
|
|
24
|
+
/** Filter by entity flags (bitwise AND check) */
|
|
25
|
+
flags?: number;
|
|
26
|
+
/** Filter by spawn flags (bitwise AND check) */
|
|
27
|
+
spawnflags?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Enhanced entity wrapper providing convenient property access and utility methods.
|
|
31
|
+
* Extends nodemod.Entity directly for full compatibility with nodemod.eng functions.
|
|
32
|
+
*/
|
|
33
|
+
export interface EntityWrapper extends nodemod.Entity {
|
|
34
|
+
/** Remove the entity from the world */
|
|
35
|
+
remove(): void;
|
|
36
|
+
/** Make entity static (non-moving) */
|
|
37
|
+
makeStatic(): void;
|
|
38
|
+
/** Set entity bounding box size */
|
|
39
|
+
setSize(mins: number[], maxs: number[]): void;
|
|
40
|
+
/** Drop entity to floor and return drop distance */
|
|
41
|
+
dropToFloor(): number;
|
|
42
|
+
/** Check if entity is on the floor */
|
|
43
|
+
isOnFloor(): number;
|
|
44
|
+
/** Emit a sound from this entity */
|
|
45
|
+
emitSound(channel: number, sound: string, volume?: number, attenuation?: number, flags?: number, pitch?: number): void;
|
|
46
|
+
/** Get distance to another entity */
|
|
47
|
+
getDistance(target: EntityWrapper | nodemod.Entity): number;
|
|
48
|
+
/** Get illumination level at entity position */
|
|
49
|
+
getIllum(): number;
|
|
50
|
+
/** Set callback for when this entity is touched */
|
|
51
|
+
onTouch(callback: (other: EntityWrapper | null) => void): void;
|
|
52
|
+
/** Set callback for when this entity is used */
|
|
53
|
+
onUse(callback: (other: EntityWrapper | null) => void): void;
|
|
54
|
+
use(): void;
|
|
55
|
+
touch(other: nodemod.Entity): void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Comprehensive entity management system providing enhanced entity creation, searching, and manipulation.
|
|
59
|
+
* Wraps raw nodemod entities with convenient property accessors and utility methods.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // Find all players
|
|
64
|
+
* const players = nodemodCore.entity.find({ className: 'player' });
|
|
65
|
+
* players.forEach(player => {
|
|
66
|
+
* console.log(`Player at: ${player.origin}`);
|
|
67
|
+
* });
|
|
68
|
+
*
|
|
69
|
+
* // Create a light entity
|
|
70
|
+
* const light = nodemodCore.entity.createLight([100, 200, 300], '_light', 500);
|
|
71
|
+
* if (light) {
|
|
72
|
+
* light.targetName = 'my_light';
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* // Find entities near a position
|
|
76
|
+
* const nearby = nodemodCore.entity.findInSphere([0, 0, 0], 512, 'weapon_ak47');
|
|
77
|
+
* console.log(`Found ${nearby.length} AK47s nearby`);
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export default class NodemodEntity {
|
|
81
|
+
/** Utility service for entity operations */
|
|
82
|
+
private util;
|
|
83
|
+
/**
|
|
84
|
+
* Creates a new NodemodEntity instance.
|
|
85
|
+
*
|
|
86
|
+
* @param utilService - Utility service for entity operations
|
|
87
|
+
*/
|
|
88
|
+
constructor(utilService: NodemodUtil);
|
|
89
|
+
/**
|
|
90
|
+
* Creates a new entity with optional class name.
|
|
91
|
+
*
|
|
92
|
+
* @param className - Entity class name (e.g., 'info_player_start', 'weapon_ak47')
|
|
93
|
+
* @returns EntityWrapper for the created entity, or null if creation failed
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* // Create a generic entity
|
|
98
|
+
* const entity = nodemodCore.entity.create();
|
|
99
|
+
*
|
|
100
|
+
* // Create a specific entity class
|
|
101
|
+
* const weapon = nodemodCore.entity.create('weapon_ak47');
|
|
102
|
+
* if (weapon) {
|
|
103
|
+
* weapon.origin = [100, 200, 300];
|
|
104
|
+
* }
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
create(className?: string | null): EntityWrapper | null;
|
|
108
|
+
/**
|
|
109
|
+
* Wraps a raw nodemod entity with enhanced functionality while preserving nodemod.eng compatibility.
|
|
110
|
+
* The entity object itself is returned with additional methods attached, ensuring full compatibility
|
|
111
|
+
* with core nodemod.eng functions.
|
|
112
|
+
*
|
|
113
|
+
* @param entity - The raw nodemod entity to wrap
|
|
114
|
+
* @returns EntityWrapper with enhanced functionality, or null if entity is invalid
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* const rawEntity = nodemod.eng.pEntityOfEntIndex(1);
|
|
119
|
+
* const wrapped = nodemodCore.entity.wrap(rawEntity);
|
|
120
|
+
* if (wrapped) {
|
|
121
|
+
* console.log(`Entity class: ${wrapped.classname}`);
|
|
122
|
+
* wrapped.health = 100;
|
|
123
|
+
* wrapped.emitSound(0, 'items/healthkit.wav');
|
|
124
|
+
* // Still compatible with nodemod.eng functions:
|
|
125
|
+
* nodemod.eng.setOrigin(wrapped, [0, 0, 0]);
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
wrap(entity: nodemod.Entity): EntityWrapper | null;
|
|
130
|
+
/**
|
|
131
|
+
* Finds entities matching the specified criteria.
|
|
132
|
+
*
|
|
133
|
+
* @param criteria - Search criteria to filter entities
|
|
134
|
+
* @returns Array of EntityWrapper objects matching the criteria
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* // Find all players
|
|
139
|
+
* const players = nodemodCore.entity.find({ className: 'player' });
|
|
140
|
+
*
|
|
141
|
+
* // Find entities with specific target name
|
|
142
|
+
* const targets = nodemodCore.entity.find({ targetName: 'button_secret' });
|
|
143
|
+
*
|
|
144
|
+
* // Find entities with specific flags
|
|
145
|
+
* const onGroundEntities = nodemodCore.entity.find({
|
|
146
|
+
* flags: nodemod.FL.ONGROUND
|
|
147
|
+
* });
|
|
148
|
+
*
|
|
149
|
+
* // Combine multiple criteria
|
|
150
|
+
* const healthyPlayers = nodemodCore.entity.find({
|
|
151
|
+
* className: 'player',
|
|
152
|
+
* health: 100
|
|
153
|
+
* });
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
find(criteria: EntityCriteria): EntityWrapper[];
|
|
157
|
+
/**
|
|
158
|
+
* Finds the first entity matching the specified criteria.
|
|
159
|
+
* Convenience method for when you only need one result.
|
|
160
|
+
*
|
|
161
|
+
* @param criteria - Search criteria to filter entities
|
|
162
|
+
* @returns First EntityWrapper matching criteria, or null if none found
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* // Find first player
|
|
167
|
+
* const player = nodemodCore.entity.findOne({ className: 'player' });
|
|
168
|
+
* if (player) {
|
|
169
|
+
* console.log(`Player health: ${player.health}`);
|
|
170
|
+
* }
|
|
171
|
+
*
|
|
172
|
+
* // Find specific target
|
|
173
|
+
* const secretButton = nodemodCore.entity.findOne({
|
|
174
|
+
* targetName: 'secret_button'
|
|
175
|
+
* });
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
findOne(criteria: EntityCriteria): EntityWrapper | null;
|
|
179
|
+
/**
|
|
180
|
+
* Finds entities within a spherical radius of a position.
|
|
181
|
+
* Uses actual entity bounds, not just origin points - works correctly with brush entities.
|
|
182
|
+
*
|
|
183
|
+
* @param origin - Center position [x, y, z] to search from
|
|
184
|
+
* @param radius - Search radius in world units
|
|
185
|
+
* @param className - Optional filter by entity class name
|
|
186
|
+
* @returns Array of EntityWrapper objects within the sphere
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```typescript
|
|
190
|
+
* // Find all entities near player
|
|
191
|
+
* const playerPos = [100, 200, 300];
|
|
192
|
+
* const nearbyEntities = nodemodCore.entity.findInSphere(playerPos, 512);
|
|
193
|
+
*
|
|
194
|
+
* // Find specific entity types nearby
|
|
195
|
+
* const nearbyWeapons = nodemodCore.entity.findInSphere(
|
|
196
|
+
* playerPos,
|
|
197
|
+
* 256,
|
|
198
|
+
* 'weapon_ak47'
|
|
199
|
+
* );
|
|
200
|
+
*
|
|
201
|
+
* // Find entities in explosion radius
|
|
202
|
+
* const explosionPos = [0, 0, 0];
|
|
203
|
+
* const affected = nodemodCore.entity.findInSphere(explosionPos, 200);
|
|
204
|
+
* affected.forEach(entity => {
|
|
205
|
+
* // Apply damage logic
|
|
206
|
+
* });
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
findInSphere(origin: number[], radius: number, className?: string | null): EntityWrapper[];
|
|
210
|
+
getById(id: number): EntityWrapper | null;
|
|
211
|
+
getAll(): EntityWrapper[];
|
|
212
|
+
private matchesCriteria;
|
|
213
|
+
/**
|
|
214
|
+
* Creates a light entity at the specified position.
|
|
215
|
+
*
|
|
216
|
+
* @param origin - Position [x, y, z] for the light
|
|
217
|
+
* @param color - Light color property name (default: '_light')
|
|
218
|
+
* @param brightness - Light brightness value (default: 300)
|
|
219
|
+
* @returns EntityWrapper for the created light, or null if creation failed
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```typescript
|
|
223
|
+
* // Create standard light
|
|
224
|
+
* const light = nodemodCore.entity.createLight([100, 200, 300]);
|
|
225
|
+
*
|
|
226
|
+
* // Create colored light
|
|
227
|
+
* const redLight = nodemodCore.entity.createLight([0, 0, 0], '_light', 500);
|
|
228
|
+
* if (redLight) {
|
|
229
|
+
* (redLight as any)._color = '255 0 0'; // Red color
|
|
230
|
+
* }
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
createLight(origin: number[], color?: string, brightness?: number): EntityWrapper | null;
|
|
234
|
+
createInfo(className: string, origin: number[], angles?: number[]): EntityWrapper | null;
|
|
235
|
+
createTrigger(className: string, origin: number[], size?: number[]): EntityWrapper | null;
|
|
236
|
+
/**
|
|
237
|
+
* Removes all entities matching the specified criteria.
|
|
238
|
+
*
|
|
239
|
+
* @param criteria - Search criteria to filter entities for removal
|
|
240
|
+
* @returns Number of entities removed
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```typescript
|
|
244
|
+
* // Remove all weapons from the map
|
|
245
|
+
* const removed = nodemodCore.entity.removeAll({
|
|
246
|
+
* className: 'weapon_ak47'
|
|
247
|
+
* });
|
|
248
|
+
* console.log(`Removed ${removed} AK47s`);
|
|
249
|
+
*
|
|
250
|
+
* // Remove entities with specific target name
|
|
251
|
+
* nodemodCore.entity.removeAll({
|
|
252
|
+
* targetName: 'cleanup_target'
|
|
253
|
+
* });
|
|
254
|
+
*
|
|
255
|
+
* // Remove all low-health entities
|
|
256
|
+
* nodemodCore.entity.removeAll({
|
|
257
|
+
* health: 1
|
|
258
|
+
* });
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
removeAll(criteria: EntityCriteria): number;
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/enhanced/entity.ts"],"names":[],"mappings":"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"}
|