Parent Directory | Revision Log
Revision 1 - (view) (download)
1 : | ian | 1 | /* XS Library |
2 : | * for AMX and AMXX | ||
3 : | * | ||
4 : | * Copyright (C) 2004 Pavol "PM" Marko | ||
5 : | * | ||
6 : | * This program is free software; you can redistribute it and/or modify it | ||
7 : | * under the terms of the GNU General Public License as published by the | ||
8 : | * Free Software Foundation; either version 2 of the License, or (at | ||
9 : | * your option) any later version. | ||
10 : | * | ||
11 : | * This program is distributed in the hope that it will be useful, but | ||
12 : | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 : | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 : | * General Public License for more details. | ||
15 : | * | ||
16 : | * You should have received a copy of the GNU General Public License | ||
17 : | * along with this program; if not, write to the Free Software Foundation, | ||
18 : | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 : | * | ||
20 : | * In addition, as a special exception, the author gives permission to | ||
21 : | * link the code of this program with the Half-Life Game Engine ("HL | ||
22 : | * Engine") and Modified Game Libraries ("MODs") developed by Valve, | ||
23 : | * L.L.C ("Valve"). You must obey the GNU General Public License in all | ||
24 : | * respects for all of the code used other than the HL Engine and MODs | ||
25 : | * from Valve. If you modify this file, you may extend this exception | ||
26 : | * to your version of the file, but you are not obligated to do so. If | ||
27 : | * you do not wish to do so, delete this exception statement from your | ||
28 : | * version. | ||
29 : | * | ||
30 : | * Version 0.1 | ||
31 : | * | ||
32 : | * | ||
33 : | * MACROS THAT YOU CAN DEFINE BEFORE INCLUDING XS.INC: | ||
34 : | * XS_FLEQ_TOLERANCE: | ||
35 : | * Tolerance that is used for XS_FLEQ float nearly-equal comparisions | ||
36 : | * DEFAULT: 0.000005 | ||
37 : | * XS_DEBUG | ||
38 : | * Turn debug logging on | ||
39 : | * DEFAULT: 0 | ||
40 : | * XS_LOGBUFFER_SIZE | ||
41 : | * Buffer size for logging | ||
42 : | * DEFAULT: 512 | ||
43 : | * XS_TASK_MAXPARAMS | ||
44 : | * Maximal parameter count for managed tasks | ||
45 : | * DEFAULT: 8 | ||
46 : | * XS_TASK_MAXPARAMSIZE | ||
47 : | * Maximal size of string parameter for tasks | ||
48 : | * Has to be power of 2 and has to be >= 8 | ||
49 : | * DEFAULT: 512 | ||
50 : | * XS_TASK_MANAGEDIDS | ||
51 : | * Number of managed IDs for tasks. | ||
52 : | * DEFAULT: 2048 | ||
53 : | * XS_REPLACEBUF_SIZE | ||
54 : | * DEFAULT: 3072 | ||
55 : | * | ||
56 : | * | ||
57 : | * NOTES: | ||
58 : | * On AMX, VexdUM is required for some math functions | ||
59 : | * | ||
60 : | * xs__ / XS__ (2 underscores) stuff is meant to be intern | ||
61 : | * | ||
62 : | * untested: never tested | ||
63 : | * half-tested: succesfully used in other applications; not extensively tested in xs though | ||
64 : | * tested: fully tested | ||
65 : | * | ||
66 : | * If you have any useful functions / ideas for functions, please tell me. | ||
67 : | */ | ||
68 : | |||
69 : | #if defined _xs_included | ||
70 : | #endinput | ||
71 : | #endif | ||
72 : | #define _xs_included | ||
73 : | |||
74 : | // **** CONFIG CHECK | ||
75 : | |||
76 : | #if !defined XS_FLEQ_TOLERANCE | ||
77 : | #define XS_FLEQ_TOLERANCE 0.000005 | ||
78 : | #endif | ||
79 : | |||
80 : | #if !defined XS_DEBUG | ||
81 : | #define XS_DEBUG 0 | ||
82 : | #endif | ||
83 : | |||
84 : | #if !defined XS_LOGBUFFER_SIZE | ||
85 : | #define XS_LOGBUFFER_SIZE 512 | ||
86 : | #endif | ||
87 : | |||
88 : | #if !defined XS_TASK_MAXPARAMS | ||
89 : | #define XS_TASK_MAXPARAMS 8 | ||
90 : | #endif | ||
91 : | |||
92 : | #if !defined XS_TASK_MAXPARAMSIZE | ||
93 : | #define XS_TASK_MAXPARAMSIZE 512 | ||
94 : | #endif | ||
95 : | |||
96 : | #if !defined XS_TASK_MANAGEDIDS | ||
97 : | #define XS_TASK_MANAGEDIDS 2048 | ||
98 : | #endif | ||
99 : | |||
100 : | #if !defined XS_REPLACEBUF_SIZE | ||
101 : | #define XS_REPLACEBUF_SIZE 3072 | ||
102 : | #endif | ||
103 : | |||
104 : | // **** Detect platform | ||
105 : | #define XS_AMX 0 | ||
106 : | #define XS_AMXX 1 | ||
107 : | |||
108 : | #if defined _amxmodx_included | ||
109 : | #define XS_PLATFORM XS_AMXX | ||
110 : | #endif | ||
111 : | |||
112 : | #if defined _amxmod_included && !defined _amxmodx_included | ||
113 : | #define XS_PLATFORM XS_AMX | ||
114 : | #endif | ||
115 : | |||
116 : | #if !defined XS_PLATFORM | ||
117 : | // Could not detect platform. | ||
118 : | // Make sure you include amxmodx.inc or amxmod.inc before including xs.inc | ||
119 : | #assert 0 | ||
120 : | #endinput | ||
121 : | #endif | ||
122 : | |||
123 : | // Turn on for release | ||
124 : | #define XS__LIBRELEASE 1 | ||
125 : | |||
126 : | #if XS__LIBRELEASE | ||
127 : | #define XS_LIBFUNC_ATTRIB stock | ||
128 : | #else | ||
129 : | #define XS_LIBFUNC_ATTRIB | ||
130 : | #endif | ||
131 : | #if XS__LIBRELEASE | ||
132 : | #define XS_LIBVAR_ATTRIB stock | ||
133 : | #else | ||
134 : | #define XS_LIBVAR_ATTRIB new | ||
135 : | #endif | ||
136 : | |||
137 : | |||
138 : | // Hardcore | ||
139 : | #pragma semicolon 1 | ||
140 : | /****** DEBUGGING / LOGING FUNCTIONS ******/ | ||
141 : | enum xs_logtypes | ||
142 : | { | ||
143 : | xs_debug, | ||
144 : | xs_message, | ||
145 : | xs_warning, | ||
146 : | xs_error, | ||
147 : | xs_fatalerror, | ||
148 : | xs__assertionfailed, | ||
149 : | |||
150 : | // must come last | ||
151 : | xs_logtypes_count | ||
152 : | } | ||
153 : | |||
154 : | XS_LIBVAR_ATTRIB const xs__logtypenames[xs_logtypes_count][] = {"DEBUG", "", "WARNING", "ERROR", "FATAL ERROR", "DEBUG ASSERTION FAILED"}; | ||
155 : | |||
156 : | // tested | ||
157 : | XS_LIBFUNC_ATTRIB xs_log(xs_logtypes:logtype, {Float,_}:...) | ||
158 : | { | ||
159 : | // WARNING: Don't try to use assert in here; it uses this func | ||
160 : | |||
161 : | // Don't log debug if not in debug mode | ||
162 : | #if !XS_DEBUG | ||
163 : | if (logtype == xs_debug) | ||
164 : | return; | ||
165 : | #endif | ||
166 : | |||
167 : | new buffer[XS_LOGBUFFER_SIZE+1]; | ||
168 : | buffer[XS_LOGBUFFER_SIZE]=0; | ||
169 : | format_args(buffer, XS_LOGBUFFER_SIZE, 1 /* go from SECOND argument*/); | ||
170 : | new bool:addLogTypeName = strlen(xs__logtypenames[logtype]) ? true : false; | ||
171 : | |||
172 : | #if XS_PLATFORM == XS_AMX | ||
173 : | new plugname[32]; | ||
174 : | new dummy[1]; | ||
175 : | get_plugin(-1, plugname, 31, dummy, 0, dummy, 0, dummy, 0, dummy, 0, dummy[0]); | ||
176 : | // log into HL Logs | ||
177 : | log_message("[AMX][%s]: %s%s%s", plugname, addLogTypeName ? xs__logtypenames[logtype] : "", | ||
178 : | addLogTypeName ? ": " : "", buffer); | ||
179 : | #else // assume AMXX | ||
180 : | |||
181 : | // Use AMXX's logging system | ||
182 : | log_amx("%s%s%s", addLogTypeName ? xs__logtypenames[logtype] : "", | ||
183 : | addLogTypeName ? ": " : "", buffer); | ||
184 : | #endif | ||
185 : | } | ||
186 : | |||
187 : | // Assertion | ||
188 : | // tested | ||
189 : | XS_LIBFUNC_ATTRIB xs_assertfunc({Float,_}:exp, const desc[]) | ||
190 : | { | ||
191 : | // Check exp | ||
192 : | if (exp) | ||
193 : | return 1; // ok | ||
194 : | |||
195 : | // not ok | ||
196 : | |||
197 : | // print info | ||
198 : | xs_log(xs__assertionfailed, "%s", desc); | ||
199 : | |||
200 : | return 0; | ||
201 : | } | ||
202 : | #define xs_assert(%1,%2) if (!xs_assertfunc(%1,%2)) xs__global_null /= xs__global_null | ||
203 : | |||
204 : | |||
205 : | // Assertion; only in debug mode | ||
206 : | // untested; logical flow says it should work | ||
207 : | #if XS_DEBUG | ||
208 : | #define xs_assert_dbg(%1,%2) if (!xs_assertfunc(%1,%2)) xs__global_null /= xs__global_null | ||
209 : | #else | ||
210 : | #define xs_assert_dbg(%1,%2) | ||
211 : | #endif | ||
212 : | |||
213 : | new xs__global_null = 0; | ||
214 : | |||
215 : | /****** MATH FUNCTIONS ******/ | ||
216 : | |||
217 : | /****** BASIC STUFF ******/ | ||
218 : | |||
219 : | #if XS_PLATFORM == XS_AMX | ||
220 : | enum anglemode | ||
221 : | { | ||
222 : | radian = 0, | ||
223 : | degrees, | ||
224 : | grades | ||
225 : | } | ||
226 : | #endif | ||
227 : | |||
228 : | // Returns -1 if num is negative, 0 if num is 0, 1 if num is positive | ||
229 : | // tested | ||
230 : | XS_LIBFUNC_ATTRIB xs_sign(num) | ||
231 : | { | ||
232 : | return (num < 0) ? -1 : ((num == 0) ? 0 : 1); | ||
233 : | } | ||
234 : | |||
235 : | // Returns -1 if num is negative, 0 if num is 0, 1 if num is positive | ||
236 : | // tested | ||
237 : | XS_LIBFUNC_ATTRIB xs_fsign(Float:num) | ||
238 : | { | ||
239 : | return (num < 0.0) ? -1 : ((num == 0.0) ? 0 : 1); | ||
240 : | } | ||
241 : | |||
242 : | // Returns absolute value | ||
243 : | // tested | ||
244 : | XS_LIBFUNC_ATTRIB xs_abs(num) | ||
245 : | { | ||
246 : | return (num < 0) ? -num : num; | ||
247 : | } | ||
248 : | |||
249 : | // is power of 2? (== can be expressed as 1<<i) | ||
250 : | // tested | ||
251 : | XS_LIBFUNC_ATTRIB xs_is_2power(x) | ||
252 : | { | ||
253 : | return (x!=0) && ((x&(x-1))==0); | ||
254 : | } | ||
255 : | |||
256 : | // degrees to radians | ||
257 : | // tested | ||
258 : | XS_LIBFUNC_ATTRIB Float:xs_deg2rad(Float:x) | ||
259 : | { | ||
260 : | return x * 0.017453292519943; | ||
261 : | } | ||
262 : | |||
263 : | // tested | ||
264 : | XS_LIBFUNC_ATTRIB Float:xs_rad2deg(Float:x) | ||
265 : | { | ||
266 : | return x * 57.29577951308232; | ||
267 : | } | ||
268 : | |||
269 : | // untested, should work though | ||
270 : | XS_LIBFUNC_ATTRIB Float:xs_gra2rad(Float:x) | ||
271 : | { | ||
272 : | return x * 0.015707963267948; | ||
273 : | } | ||
274 : | |||
275 : | // untested, should work though | ||
276 : | XS_LIBFUNC_ATTRIB Float:xs_rad2gra(Float:x) | ||
277 : | { | ||
278 : | return x * 63.66197723675813; | ||
279 : | } | ||
280 : | |||
281 : | // Only works when there is no whitespace between %1, the comma and %2... | ||
282 : | // tested | ||
283 : | #define XS_FLEQ(%1,%2) (((%1) <= ((%2) + XS_FLEQ_TOLERANCE)) && ((%1) >= ((%2) - XS_FLEQ_TOLERANCE))) | ||
284 : | |||
285 : | // 1/sqrt | ||
286 : | // tested | ||
287 : | XS_LIBFUNC_ATTRIB Float:xs_rsqrt(Float:x) | ||
288 : | { | ||
289 : | #if XS_PLATFORM == XS_AMX | ||
290 : | // store half | ||
291 : | new Float:xhalf = x * 0.5; | ||
292 : | |||
293 : | // compute initial guess | ||
294 : | new i = _:x; | ||
295 : | i = 0x5f375a84 - (i >> 1); | ||
296 : | x = Float:i; | ||
297 : | |||
298 : | // refine 3 times | ||
299 : | x = x * (1.5 - xhalf * x * x); | ||
300 : | x = x * (1.5 - xhalf * x * x); | ||
301 : | x = x * (1.5 - xhalf * x * x); | ||
302 : | |||
303 : | return x; | ||
304 : | #else | ||
305 : | return 1.0 / floatsqroot(x); | ||
306 : | #endif | ||
307 : | } | ||
308 : | |||
309 : | // sqrt | ||
310 : | // tested | ||
311 : | XS_LIBFUNC_ATTRIB Float:xs_sqrt(Float:x) | ||
312 : | { | ||
313 : | #if XS_PLATFORM == XS_AMX | ||
314 : | // 1.0 / rsqrt should still be faster than loop-using-approximation-methods | ||
315 : | return 1.0 / xs_rsqrt(x); | ||
316 : | #else | ||
317 : | return floatsqroot(x); | ||
318 : | #endif | ||
319 : | } | ||
320 : | |||
321 : | // These functions generate errors if you use the macros with wrong parameter count. | ||
322 : | stock Float:xs_fabs(Float:pa) | ||
323 : | { | ||
324 : | #pragma unused pa | ||
325 : | new rawr = you_need_one_param_for_fabs; | ||
326 : | rawr = warning_below_shows_line_number; | ||
327 : | #pragma unused rawr | ||
328 : | } | ||
329 : | stock Float:xs_asin(Float:pa,Float:pb) | ||
330 : | { | ||
331 : | #pragma unused pa,pb | ||
332 : | new rawr = you_need_two_params_for_asin; | ||
333 : | rawr = warning_below_shows_line_number; | ||
334 : | #pragma unused rawr | ||
335 : | } | ||
336 : | stock Float:xs_sin(Float:pa,Float:pb) | ||
337 : | { | ||
338 : | #pragma unused pa,pb | ||
339 : | new rawr = you_need_two_params_for_sin; | ||
340 : | #pragma unused rawr | ||
341 : | } | ||
342 : | stock Float:xs_acos(Float:pa,Float:pb) | ||
343 : | { | ||
344 : | #pragma unused pa,pb | ||
345 : | new rawr = you_need_two_params_for_acos; | ||
346 : | rawr = warning_below_shows_line_number; | ||
347 : | #pragma unused rawr | ||
348 : | } | ||
349 : | stock Float:xs_cos(Float:pa,Float:pb) | ||
350 : | { | ||
351 : | #pragma unused pa,pb | ||
352 : | new rawr = you_need_two_params_for_cos; | ||
353 : | rawr = warning_below_shows_line_number; | ||
354 : | #pragma unused rawr | ||
355 : | } | ||
356 : | stock Float:xs_atan(Float:pa,Float:pb) | ||
357 : | { | ||
358 : | #pragma unused pa,pb | ||
359 : | new rawr = you_need_two_params_for_atan; | ||
360 : | rawr = warning_below_shows_line_number; | ||
361 : | #pragma unused rawr | ||
362 : | } | ||
363 : | stock Float:xs_atan2(Float:pa,Float:pb) | ||
364 : | { | ||
365 : | #pragma unused pa,pb | ||
366 : | new rawr = you_need_two_params_for_atan2; | ||
367 : | rawr = warning_below_shows_line_number; | ||
368 : | #pragma unused rawr | ||
369 : | } | ||
370 : | stock Float:xs_tan(Float:pa, Float:pb) | ||
371 : | { | ||
372 : | #pragma unused pa,pb | ||
373 : | new rawr = you_need_two_params_for_tan; | ||
374 : | rawr = warning_below_shows_line_number; | ||
375 : | #pragma unused rawr | ||
376 : | } | ||
377 : | |||
378 : | #if XS_PLATFORM == XS_AMX | ||
379 : | #pragma semicolon 0 | ||
380 : | #include <VexdUM> | ||
381 : | #pragma semicolon 1 | ||
382 : | // We need stocks to provide radian / degrees / grades functionality | ||
383 : | |||
384 : | XS_LIBFUNC_ATTRIB Float:xs__2rad(Float:x, anglemode:mod) | ||
385 : | { | ||
386 : | switch (mod) | ||
387 : | { | ||
388 : | case radian: | ||
389 : | return x; | ||
390 : | case degrees: | ||
391 : | return xs_deg2rad(x); | ||
392 : | case grades: | ||
393 : | return xs_gra2rad(x); | ||
394 : | default: | ||
395 : | xs_assert(0, "xs_asin, xs_sin, xs_acos, xs_cos, xs_atan, xs_atan2 or xs_tan called with invalid mod param"); | ||
396 : | } | ||
397 : | |||
398 : | return 0.0; // compiler warning | ||
399 : | } | ||
400 : | |||
401 : | #define xs_fabs(%1) fabs(%1) | ||
402 : | #define xs_asin(%1,%2) asin(xs__2rad(%1, %2)) | ||
403 : | #define xs_sin(%1,%2) sin(xs__2rad(%1, %2)) | ||
404 : | #define xs_acos(%1,%2) acos(xs__2rad(%1, %2)) | ||
405 : | #define xs_cos(%1,%2) cos(xs__2rad(%1, %2)) | ||
406 : | #define xs_atan(%1,%2) atan(xs__2rad(%1, %2)) | ||
407 : | #define xs_atan2(%1,%2) atan2(xs__2rad(%1, %2)) | ||
408 : | #define xs_tan(%1,%2) tan(xs__2rad(%1, %2)) | ||
409 : | #else | ||
410 : | #define xs_fabs(%1) floatabs(%1) | ||
411 : | #define xs_asin(%1,%2) floatasin(%1, %2) | ||
412 : | #define xs_sin(%1,%2) floatsin(%1, %2) | ||
413 : | #define xs_acos(%1,%2) floatacos(%1, %2) | ||
414 : | #define xs_cos(%1,%2) floatcos(%1, %2) | ||
415 : | #define xs_atan(%1,%2) floatatan(%1, %2) | ||
416 : | #define xs_atan2(%1,%2) floatatan2(%1, %2) | ||
417 : | #define xs_tan(%1,%2) floattan(%1, %2) | ||
418 : | #endif | ||
419 : | |||
420 : | /****** RANDOM NUMBERS ******/ | ||
421 : | // This routine comes from the book "Inner Loops" by Rick Booth, Addison-Wesley | ||
422 : | // (ISBN 0-201-47960-5). This is a "multiplicative congruential random number | ||
423 : | // generator" that has been extended to 31-bits | ||
424 : | |||
425 : | XS_LIBVAR_ATTRIB xs__internalseed=0x546875; | ||
426 : | |||
427 : | #define XS__IL_RMULT 1103515245 | ||
428 : | |||
429 : | // tested | ||
430 : | XS_LIBFUNC_ATTRIB xs_seed(seed) | ||
431 : | { | ||
432 : | xs__internalseed = seed; | ||
433 : | } | ||
434 : | |||
435 : | // tested | ||
436 : | XS_LIBFUNC_ATTRIB xs_irand() | ||
437 : | { | ||
438 : | new lo, hi, ll, lh, hh, hl; | ||
439 : | new result; | ||
440 : | |||
441 : | lo = xs__internalseed & 0xffff; | ||
442 : | hi = xs__internalseed >> 16; | ||
443 : | xs__internalseed = xs__internalseed * XS__IL_RMULT + 12345; | ||
444 : | ll = lo * (XS__IL_RMULT & 0xffff); | ||
445 : | lh = lo * (XS__IL_RMULT >> 16 ); | ||
446 : | hl = hi * (XS__IL_RMULT & 0xffff); | ||
447 : | hh = hi * (XS__IL_RMULT >> 16 ); | ||
448 : | result = xs_abs(((ll + 12345) >> 16) + lh + hl + (hh << 16)); | ||
449 : | return result; | ||
450 : | } | ||
451 : | |||
452 : | // tested | ||
453 : | XS_LIBFUNC_ATTRIB Float:xs_frand() | ||
454 : | { | ||
455 : | return float(xs_irand()) / float(xs_get_maxnum()); // -1/2 should be the biggest possible positive number | ||
456 : | } | ||
457 : | |||
458 : | // tested | ||
459 : | XS_LIBFUNC_ATTRIB xs_irand_range(pmin, pmax) | ||
460 : | { | ||
461 : | xs_assert_dbg(pmax - pmin >= 0, "xs_irand_range: pmin > pmax"); | ||
462 : | new i = pmin + floatround(xs_frand() * float(pmax - pmin)); | ||
463 : | if (i > pmax) | ||
464 : | i = pmax; | ||
465 : | return i; | ||
466 : | } | ||
467 : | |||
468 : | /****** VECTORS & PLANES ******/ | ||
469 : | |||
470 : | // *** vectors | ||
471 : | |||
472 : | // Set vec components to values | ||
473 : | // tested | ||
474 : | XS_LIBFUNC_ATTRIB xs_vec_set(Float:vec[], Float:x, Float:y, Float:z) | ||
475 : | { | ||
476 : | vec[0] = x; | ||
477 : | vec[1] = y; | ||
478 : | vec[2] = z; | ||
479 : | } | ||
480 : | |||
481 : | // Add vec | ||
482 : | // tested | ||
483 : | XS_LIBFUNC_ATTRIB xs_vec_add(const Float:in1[], const Float:in2[], Float:out[]) | ||
484 : | { | ||
485 : | out[0] = in1[0] + in2[0]; | ||
486 : | out[1] = in1[1] + in2[1]; | ||
487 : | out[2] = in1[2] + in2[2]; | ||
488 : | } | ||
489 : | |||
490 : | // Subtract vec | ||
491 : | // untested, but should work | ||
492 : | XS_LIBFUNC_ATTRIB xs_vec_sub(const Float:in1[], const Float:in2[], Float:out[]) | ||
493 : | { | ||
494 : | out[0] = in1[0] - in2[0]; | ||
495 : | out[1] = in1[1] - in2[1]; | ||
496 : | out[2] = in1[2] - in2[2]; | ||
497 : | } | ||
498 : | |||
499 : | // Are vectors equal? | ||
500 : | // untested, but should work | ||
501 : | XS_LIBFUNC_ATTRIB bool:xs_vec_equal(const Float:vec1[], const Float:vec2[]) | ||
502 : | { | ||
503 : | return (vec1[0] == vec2[0]) && (vec1[1] == vec2[1]) && (vec1[2] == vec2[2]); | ||
504 : | } | ||
505 : | |||
506 : | // Are vectors nearly equal? | ||
507 : | // tested | ||
508 : | XS_LIBFUNC_ATTRIB bool:xs_vec_nearlyequal(const Float:vec1[], const Float:vec2[]) | ||
509 : | { | ||
510 : | return XS_FLEQ(vec1[0], vec2[0]) && XS_FLEQ(vec1[1], vec2[1]) && XS_FLEQ(vec1[2], vec2[2]); | ||
511 : | } | ||
512 : | |||
513 : | // multiply vector by scalar | ||
514 : | // tested | ||
515 : | XS_LIBFUNC_ATTRIB xs_vec_mul_scalar(const Float:vec[], Float:scalar, Float:out[]) | ||
516 : | { | ||
517 : | out[0] = vec[0] * scalar; | ||
518 : | out[1] = vec[1] * scalar; | ||
519 : | out[2] = vec[2] * scalar; | ||
520 : | } | ||
521 : | |||
522 : | // divide vector by scalar | ||
523 : | // untested, but should work | ||
524 : | XS_LIBFUNC_ATTRIB xs_vec_div_scalar(const Float:vec[], Float:scalar, Float:out[]) | ||
525 : | { | ||
526 : | new Float:__tmp = 1.0 / scalar; | ||
527 : | out[0] = vec[0] * __tmp; | ||
528 : | out[1] = vec[1] * __tmp; | ||
529 : | out[2] = vec[2] * __tmp; | ||
530 : | } | ||
531 : | |||
532 : | // Compute vector length | ||
533 : | // tested | ||
534 : | XS_LIBFUNC_ATTRIB Float:xs_vec_len(const Float:vec[]) | ||
535 : | { | ||
536 : | return xs_sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); | ||
537 : | } | ||
538 : | |||
539 : | // Normalize vec | ||
540 : | // tested | ||
541 : | XS_LIBFUNC_ATTRIB xs_vec_normalize(const Float:vec[], Float:out[]) | ||
542 : | { | ||
543 : | new Float:invlen = xs_rsqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); | ||
544 : | out[0] = vec[0] * invlen; | ||
545 : | out[1] = vec[1] * invlen; | ||
546 : | out[2] = vec[2] * invlen; | ||
547 : | } | ||
548 : | |||
549 : | // Store the cross product of vec1 and vec2 in out | ||
550 : | // tested | ||
551 : | XS_LIBFUNC_ATTRIB xs_vec_cross(const Float:vec1[], const Float:vec2[], Float:out[]) | ||
552 : | { | ||
553 : | out[0] = vec1[1]*vec2[2] - vec1[2]*vec2[1]; | ||
554 : | out[1] = vec1[2]*vec2[0] - vec1[0]*vec2[2]; | ||
555 : | out[2] = vec1[0]*vec2[1] - vec1[1]*vec2[0]; | ||
556 : | } | ||
557 : | |||
558 : | // Compute vec1 dot vec2 | ||
559 : | // tested | ||
560 : | XS_LIBFUNC_ATTRIB Float:xs_vec_dot(const Float:vec1[], const Float:vec2[]) | ||
561 : | { | ||
562 : | return vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]; | ||
563 : | } | ||
564 : | |||
565 : | // Negate vec into out | ||
566 : | // untested, but should work | ||
567 : | XS_LIBFUNC_ATTRIB xs_vec_neg(const Float:vec[], Float:out[]) | ||
568 : | { | ||
569 : | out[0] = -vec[0]; | ||
570 : | out[1] = -vec[1]; | ||
571 : | out[2] = -vec[2]; | ||
572 : | } | ||
573 : | |||
574 : | // Copy vec | ||
575 : | // untested, but should work | ||
576 : | XS_LIBFUNC_ATTRIB xs_vec_copy(const Float:vecIn[], Float:vecOut[]) | ||
577 : | { | ||
578 : | vecOut[0] = vecIn[0]; | ||
579 : | vecOut[1] = vecIn[1]; | ||
580 : | vecOut[2] = vecIn[2]; | ||
581 : | } | ||
582 : | |||
583 : | // Compute angle between vec1 and vec2 | ||
584 : | // tested | ||
585 : | XS_LIBFUNC_ATTRIB Float:xs_vec_angle(const Float:vec1[], const Float:vec2[]) | ||
586 : | { | ||
587 : | return xs_rad2deg(xs_acos(xs_vec_dot(vec1, vec2), radian)); | ||
588 : | } | ||
589 : | |||
590 : | // Reflect vec about normal | ||
591 : | // untested | ||
592 : | XS_LIBFUNC_ATTRIB xs_vec_reflect(const Float:vec[], const Float:normal[], Float:out[]) | ||
593 : | { | ||
594 : | // normalize(vec) - (normal * 2.0 * (tmp . normal)) * length(vec) | ||
595 : | |||
596 : | new Float:tmp1[3]; | ||
597 : | xs_vec_normalize(vec, tmp1); | ||
598 : | |||
599 : | // tmp1 - (normal * 2.0 * (tmp . normal)) * length(vec) | ||
600 : | |||
601 : | new Float:tmp2[3]; | ||
602 : | xs_vec_mul_scalar(normal, 2.0, tmp2); | ||
603 : | xs_vec_mul_scalar(tmp2, xs_vec_dot(tmp1, normal), tmp2); | ||
604 : | |||
605 : | // tmp1 - tmp2 * length(vec) | ||
606 : | xs_vec_mul_scalar(tmp2, xs_vec_len(vec), tmp2); | ||
607 : | |||
608 : | // tmp1 - tmp2 | ||
609 : | xs_vec_sub(tmp1, tmp2, out); | ||
610 : | } | ||
611 : | |||
612 : | // Turn a 3D vector into a 2D vector | ||
613 : | XS_LIBFUNC_ATTRIB xs_vec_make2d(const Float:vec[3], Float:out[2]) | ||
614 : | { | ||
615 : | out[0] = vec[0]; | ||
616 : | out[1] = vec[1]; | ||
617 : | } | ||
618 : | |||
619 : | // *** planes | ||
620 : | |||
621 : | // normal | ||
622 : | #define XS_PLANE_A 0 | ||
623 : | #define XS_PLANE_B 1 | ||
624 : | #define XS_PLANE_C 2 | ||
625 : | // plane shift distance | ||
626 : | #define XS_PLANE_D 3 | ||
627 : | |||
628 : | |||
629 : | // Set a plane to specific values | ||
630 : | // tested | ||
631 : | XS_LIBFUNC_ATTRIB xs_plane_set(Float:plane[], Float:a, Float:b, Float:c, Float:d) | ||
632 : | { | ||
633 : | plane[XS_PLANE_A] = a; | ||
634 : | plane[XS_PLANE_B] = b; | ||
635 : | plane[XS_PLANE_C] = c; | ||
636 : | plane[XS_PLANE_D] = d; | ||
637 : | } | ||
638 : | |||
639 : | // Construct a plane out of 3 points | ||
640 : | // tested | ||
641 : | XS_LIBFUNC_ATTRIB xs_plane_3p(Float:plane[], const Float:p1[], const Float:p2[], const Float:p3[]) | ||
642 : | { | ||
643 : | new Float:normalA[3], Float:normalB[3]; | ||
644 : | |||
645 : | // normalA = Normalize(p3 - p1); | ||
646 : | normalA[0] = p3[0] - p1[0]; | ||
647 : | normalA[1] = p3[1] - p1[1]; | ||
648 : | normalA[2] = p3[2] - p1[2]; | ||
649 : | xs_vec_normalize(normalA, normalA); | ||
650 : | |||
651 : | // normalB = Normalize(p3 - p2); | ||
652 : | normalB[0] = p3[0] - p2[0]; | ||
653 : | normalB[1] = p3[1] - p2[1]; | ||
654 : | normalB[2] = p3[2] - p2[2]; | ||
655 : | xs_vec_normalize(normalB, normalB); | ||
656 : | |||
657 : | // plane normal = Normalize(normalA cross normalB) | ||
658 : | xs_vec_cross(normalA, normalB, plane); | ||
659 : | xs_vec_normalize(plane, plane); | ||
660 : | |||
661 : | // plane shift distance = (-p1) dot plane normal | ||
662 : | new Float:__tmp[3]; | ||
663 : | xs_vec_neg(plane, __tmp); | ||
664 : | plane[XS_PLANE_D] = xs_vec_dot(__tmp, p1); | ||
665 : | |||
666 : | } | ||
667 : | |||
668 : | // untested, but should work | ||
669 : | XS_LIBFUNC_ATTRIB bool:xs_plane_equal(const Float:plane1[], const Float:plane2[]) | ||
670 : | { | ||
671 : | if ( (plane1[0] == plane2[0]) && | ||
672 : | (plane1[1] == plane2[1]) && | ||
673 : | (plane1[2] == plane2[2]) && | ||
674 : | (plane1[3] == plane2[3])) | ||
675 : | return true; | ||
676 : | return false; | ||
677 : | } | ||
678 : | |||
679 : | // untested, but should work | ||
680 : | XS_LIBFUNC_ATTRIB bool:xs_plane_nearlyequal(const Float:plane1[], const Float:plane2[]) | ||
681 : | { | ||
682 : | if ( XS_FLEQ(plane1[0], plane2[0]) && | ||
683 : | XS_FLEQ(plane1[1], plane2[1]) && | ||
684 : | XS_FLEQ(plane1[2], plane2[2]) && | ||
685 : | XS_FLEQ(plane1[3], plane2[3])) | ||
686 : | return true; | ||
687 : | return false; | ||
688 : | } | ||
689 : | |||
690 : | // Compute distance between plane and point | ||
691 : | // tested | ||
692 : | XS_LIBFUNC_ATTRIB Float:xs_plane_dst2point(const Float:plane[], const Float:point[]) | ||
693 : | { | ||
694 : | // return normal dot point + D | ||
695 : | return xs_vec_dot(plane, point) + plane[XS_PLANE_D]; | ||
696 : | } | ||
697 : | |||
698 : | // Checks whether plane intersects with the ray starting and rayStart and going to rayDir direction. | ||
699 : | // If yes, returns true and sets out to the intersection point | ||
700 : | // Otherwise, returns false | ||
701 : | // tested | ||
702 : | XS_LIBFUNC_ATTRIB bool:xs_plane_rayintersect(const Float:plane[], const Float:rayStart[], const Float:rayDir[], Float:out[]) | ||
703 : | { | ||
704 : | new Float:a = xs_vec_dot(plane, rayDir); | ||
705 : | |||
706 : | if (a == 0.0) | ||
707 : | return false; // ray is parallel to plane | ||
708 : | |||
709 : | // if (distance plane<->(rayStart + rayDir) > distance plane<->rayStart) and both have the same sign, the ray | ||
710 : | // goes away from the plane | ||
711 : | new Float:rsplusrd[3]; | ||
712 : | xs_vec_add(rayStart, rayDir, rsplusrd); | ||
713 : | new Float:dst1 = xs_plane_dst2point(plane, rsplusrd); | ||
714 : | new Float:dst2 = xs_plane_dst2point(plane, rayStart); | ||
715 : | if (xs_fabs(dst1) > xs_fabs(dst2) && xs_fsign(dst1) == xs_fsign(dst2)) | ||
716 : | return false; | ||
717 : | |||
718 : | |||
719 : | // out = rayStart - rayDir * ((distance plane<->rayStart) / a) | ||
720 : | new Float:__tmp[3]; | ||
721 : | xs_vec_mul_scalar(rayDir, xs_plane_dst2point(plane, rayStart) / a, __tmp); | ||
722 : | // out = rayStart - tmp | ||
723 : | xs_vec_sub(rayStart, __tmp, out); | ||
724 : | |||
725 : | return true; | ||
726 : | } | ||
727 : | |||
728 : | // Is point on plane? | ||
729 : | // tested | ||
730 : | XS_LIBFUNC_ATTRIB bool:xs_point_onplane(const Float:plane[], const Float:point[]) | ||
731 : | { | ||
732 : | return XS_FLEQ(xs_plane_dst2point(plane, point), 0.0); | ||
733 : | } | ||
734 : | |||
735 : | // Project point on plane | ||
736 : | // tested | ||
737 : | XS_LIBFUNC_ATTRIB xs_projpoint_onplane(const Float:plane[], const Float:point[], Float:out[]) | ||
738 : | { | ||
739 : | new Float:__tmp[3]; | ||
740 : | // out = point - (plane normal * distance point<->plane) | ||
741 : | xs_vec_copy(plane, __tmp); | ||
742 : | xs_vec_mul_scalar(__tmp, xs_plane_dst2point(plane, point), __tmp); | ||
743 : | xs_vec_sub(point, __tmp, out); | ||
744 : | } | ||
745 : | |||
746 : | // Copy plane | ||
747 : | // untested, but should work | ||
748 : | XS_LIBFUNC_ATTRIB xs_plane_copy(const Float:planeIn[], Float:planeOut[]) | ||
749 : | { | ||
750 : | planeOut[0] = planeIn[0]; | ||
751 : | planeOut[1] = planeIn[1]; | ||
752 : | planeOut[2] = planeIn[2]; | ||
753 : | planeOut[3] = planeIn[3]; | ||
754 : | } | ||
755 : | |||
756 : | /****** HL ENGINE SPECIFIC STUFF ******/ | ||
757 : | // Compute forward, right and up vector from angles | ||
758 : | // half-tested | ||
759 : | |||
760 : | // angle indexes | ||
761 : | #define XS_PITCH 0 // up / down | ||
762 : | #define XS_YAW 1 // left / right | ||
763 : | #define XS_ROLL 2 // fall over | ||
764 : | |||
765 : | XS_LIBFUNC_ATTRIB xs_anglevectors(const Float:angles[3], Float:fwd[3], Float:right[3], Float:up[3]) | ||
766 : | { | ||
767 : | // sin (s) and cos (c) for yaw (y), pitch (p) and roll (r) | ||
768 : | new Float:sr, Float:sp, Float:sy, Float:cr, Float:cp, Float:cy; | ||
769 : | |||
770 : | sy = xs_sin(angles[XS_YAW], degrees); | ||
771 : | cy = xs_cos(angles[XS_YAW], degrees); | ||
772 : | sp = xs_sin(angles[XS_PITCH], degrees); | ||
773 : | cp = xs_cos(angles[XS_PITCH], degrees); | ||
774 : | sr = xs_sin(angles[XS_ROLL], degrees); | ||
775 : | cr = xs_cos(angles[XS_ROLL], degrees); | ||
776 : | |||
777 : | fwd[0] = cp*cy; | ||
778 : | fwd[1] = cp*sy; | ||
779 : | fwd[2] = -sp; | ||
780 : | |||
781 : | right[0] = (-1*sr*sp*cy + -1*cr*-sy); | ||
782 : | right[1] = (-1*sr*sp*sy + -1*cr*cy); | ||
783 : | right[2] = -1*sr*cp; | ||
784 : | |||
785 : | up[0] = (cr*sp*cy + -sr*-sy); | ||
786 : | up[1] = (cr*sp*sy + -sr*cy); | ||
787 : | up[2] = cr*cp; | ||
788 : | } | ||
789 : | /****** STRING FUNCS *******/ | ||
790 : | |||
791 : | // tested | ||
792 : | XS_LIBFUNC_ATTRIB xs_strchr(const str[], chr) | ||
793 : | { | ||
794 : | for (new i = 0; str[i] != 0; ++i) | ||
795 : | { | ||
796 : | if (str[i] == chr) | ||
797 : | return i; | ||
798 : | } | ||
799 : | return -1; | ||
800 : | } | ||
801 : | |||
802 : | // by JGHG, adapted | ||
803 : | // removes charstotrim number of charactes from stringtotrim's | ||
804 : | // - beginning if fromleft is true | ||
805 : | // - end if fromleft is false | ||
806 : | // tested | ||
807 : | XS_LIBFUNC_ATTRIB xs_strtrim(stringtotrim[], charstotrim, bool:fromleft = true) | ||
808 : | { | ||
809 : | if (charstotrim <= 0) | ||
810 : | return; | ||
811 : | |||
812 : | if (fromleft) | ||
813 : | { | ||
814 : | new maxlen = strlen(stringtotrim); | ||
815 : | if (charstotrim > maxlen) | ||
816 : | charstotrim = maxlen; | ||
817 : | |||
818 : | // In format, input and output regions can overlap | ||
819 : | format(stringtotrim, maxlen, "%s", stringtotrim[charstotrim]); | ||
820 : | } | ||
821 : | else | ||
822 : | { | ||
823 : | new maxlen = strlen(stringtotrim) - charstotrim; | ||
824 : | if (maxlen < 0) | ||
825 : | maxlen = 0; | ||
826 : | |||
827 : | // In format, input and output regions can overlap | ||
828 : | format(stringtotrim, maxlen, "%s", stringtotrim); | ||
829 : | } | ||
830 : | } | ||
831 : | |||
832 : | // by xeroblood, adapted | ||
833 : | // copies characters from oldmsg to newmsg, starting at start and ending at end (_includes_ end). | ||
834 : | // terminates newmsg with 0 | ||
835 : | // if outlen is positive, it specifies the maximal number of characters to be copied. | ||
836 : | // otherwise, assumes that newmsg is at least end-start+1 characters long. | ||
837 : | // tested | ||
838 : | XS_LIBFUNC_ATTRIB xs_strmid(const oldmsg[], newmsg[], start, end, outlen=-1) | ||
839 : | { | ||
840 : | new len = strlen(oldmsg); | ||
841 : | |||
842 : | if(start < 0) | ||
843 : | start = 0; | ||
844 : | |||
845 : | ++end; // Include end | ||
846 : | |||
847 : | if(end <= start || end > len) | ||
848 : | end = len; | ||
849 : | |||
850 : | new j = 0, i = start; | ||
851 : | for(; (i < end) && (outlen--);) | ||
852 : | newmsg[j++] = oldmsg[i++]; | ||
853 : | |||
854 : | newmsg[j] = 0; | ||
855 : | } | ||
856 : | |||
857 : | // by xeroblood, adapted | ||
858 : | // maxelems: maximal number of elements in output, elemsize: maximal size of one element | ||
859 : | // tested | ||
860 : | XS_LIBFUNC_ATTRIB xs_explode(const input[], output[][], delimiter, maxelems, elemsize) | ||
861 : | { | ||
862 : | new nIdx = 0; | ||
863 : | new nLen = 0; | ||
864 : | |||
865 : | new copied = 0; | ||
866 : | while(nLen < strlen(input) && nIdx < maxelems) | ||
867 : | { | ||
868 : | copied = copyc(output[nIdx++], elemsize, input[nLen], delimiter); | ||
869 : | if (copied == elemsize) | ||
870 : | { | ||
871 : | // maybe it got force-stopped because of maxsize | ||
872 : | // so check whether we have to skip something | ||
873 : | if (input[nLen + copied] != delimiter && input[nLen + copied] != 0) | ||
874 : | { | ||
875 : | new found = xs_strchr(input[nLen + copied], delimiter); | ||
876 : | if (found == -1) | ||
877 : | break; | ||
878 : | copied += found; | ||
879 : | } | ||
880 : | } | ||
881 : | |||
882 : | nLen += copied + 1; // +1: skip delimiter | ||
883 : | } | ||
884 : | return nIdx; | ||
885 : | } | ||
886 : | |||
887 : | // returns number of cells written. | ||
888 : | XS_LIBFUNC_ATTRIB xs_implode(output[], outsize, delimiter, const input[][], elemsnum) | ||
889 : | { | ||
890 : | new pos = 0; | ||
891 : | new copied; | ||
892 : | for (new i = 0; i < elemsnum; ++i) | ||
893 : | { | ||
894 : | copied = copy(output[pos], outsize - pos, input[i]); | ||
895 : | pos += copied; | ||
896 : | if (pos >= outsize) | ||
897 : | return outsize; | ||
898 : | // append delimiter | ||
899 : | output[pos] = delimiter; | ||
900 : | ++pos; | ||
901 : | // last check | ||
902 : | if (pos >= outsize) | ||
903 : | return outsize; | ||
904 : | } | ||
905 : | |||
906 : | output[--pos] = 0; // The last char would be delimiter, so skip it. | ||
907 : | return pos; | ||
908 : | } | ||
909 : | |||
910 : | |||
911 : | XS_LIBVAR_ATTRIB xs__replace_buf[XS_REPLACEBUF_SIZE]; | ||
912 : | // Replace all occurencies of what in text with with | ||
913 : | // Returns number of (also partially if trimmed by len) replaced items. | ||
914 : | XS_LIBFUNC_ATTRIB xs_replace(text[], len, const what[], const with[]) | ||
915 : | { | ||
916 : | new occur = 0; | ||
917 : | new i = 0; | ||
918 : | new bufPos = 0; | ||
919 : | new replaceLen = strlen(with); | ||
920 : | new whatLen = strlen(what); | ||
921 : | for (; text[i]; ++i) | ||
922 : | { | ||
923 : | if (text[i] == what[0]) | ||
924 : | { | ||
925 : | new posInWhat=0; | ||
926 : | new j; | ||
927 : | for (j = i; j-i < replaceLen && text[j]; ++j, ++posInWhat) | ||
928 : | { | ||
929 : | if (text[j] != what[posInWhat]) | ||
930 : | break; | ||
931 : | } | ||
932 : | if (whatLen == posInWhat) | ||
933 : | { | ||
934 : | for (new i2 = 0; i2 < replaceLen && bufPos < XS_REPLACEBUF_SIZE; ++i2) | ||
935 : | xs__replace_buf[bufPos++] = with[i2]; | ||
936 : | i = j - 1; | ||
937 : | ++occur; | ||
938 : | if (bufPos >= XS_REPLACEBUF_SIZE) | ||
939 : | return occur; | ||
940 : | continue; | ||
941 : | } | ||
942 : | } | ||
943 : | if (bufPos >= XS_REPLACEBUF_SIZE) | ||
944 : | return occur; | ||
945 : | xs__replace_buf[bufPos++] = text[i]; | ||
946 : | } | ||
947 : | xs__replace_buf[bufPos] = 0; | ||
948 : | copy(text, len, xs__replace_buf); | ||
949 : | return occur; | ||
950 : | } | ||
951 : | |||
952 : | // replaces all occurencies of what in text with with | ||
953 : | // Returns number of replaced items. | ||
954 : | XS_LIBFUNC_ATTRIB xs_replace_char(text[], len, what, with) | ||
955 : | { | ||
956 : | // let the xs_replace function do the work | ||
957 : | new arr[4]; | ||
958 : | arr[0] = what; | ||
959 : | arr[1] = 0; | ||
960 : | arr[2] = with; | ||
961 : | arr[3] = 0; | ||
962 : | |||
963 : | return xs_replace(text, len, arr[0], arr[2]); | ||
964 : | } | ||
965 : | |||
966 : | #if XS_PLATFORM == XS_AMX | ||
967 : | // message_begin checking for AMX | ||
968 : | xs__hook_message_begin(dest, msg_type, origin[3]={0,0,0}, player = 0) | ||
969 : | { | ||
970 : | xs_assert(xs_is_msg_valid(msg_type), "message_begin called with bogus message type"); | ||
971 : | return message_begin(dest, msg_type, origin, player); | ||
972 : | } | ||
973 : | |||
974 : | #define message_begin xs__hook_message_begin | ||
975 : | #endif | ||
976 : | /****** MISC FUNCS *******/ | ||
977 : | // sets namestr to name of the command identified by cid | ||
978 : | // half-tested | ||
979 : | XS_LIBFUNC_ATTRIB xs_concmd_name(cid, namestr[], namelen) | ||
980 : | { | ||
981 : | new dummy1; | ||
982 : | new dummy2[1]; | ||
983 : | get_concmd(cid, namestr, namelen, dummy1, dummy2, 0, 0); | ||
984 : | } | ||
985 : | |||
986 : | // Checks whether there are at least num free visible slots | ||
987 : | // half-tested | ||
988 : | XS_LIBFUNC_ATTRIB bool:xs_freevisibleslots(num) | ||
989 : | { | ||
990 : | new maxplayers = get_cvar_num("sv_visiblemaxplayers"); | ||
991 : | if (maxplayers <= 0) | ||
992 : | maxplayers = get_maxplayers(); | ||
993 : | |||
994 : | return (get_playersnum(1) <= maxplayers-num) ? true : false; | ||
995 : | } | ||
996 : | |||
997 : | // Returns biggest possible positive number | ||
998 : | XS_LIBVAR_ATTRIB xs__maxnum = 0; | ||
999 : | // tested | ||
1000 : | XS_LIBFUNC_ATTRIB xs_get_maxnum() | ||
1001 : | { | ||
1002 : | if (!xs__maxnum) | ||
1003 : | { | ||
1004 : | // build it | ||
1005 : | xs__maxnum = ((1 << (cellbits - 2)) - 1 ) | (1 << (cellbits - 2)); | ||
1006 : | /* | ||
1007 : | new bits = get_cellsize() * 8 - 1; | ||
1008 : | for (new i = 0; i < bits; ++i) | ||
1009 : | xs__maxnum |= 1 << i; | ||
1010 : | */ | ||
1011 : | } | ||
1012 : | return xs__maxnum; | ||
1013 : | } | ||
1014 : | |||
1015 : | // tested | ||
1016 : | XS_LIBFUNC_ATTRIB xs_get_minnum() | ||
1017 : | { | ||
1018 : | return xs_get_maxnum() + 1; | ||
1019 : | } | ||
1020 : | |||
1021 : | |||
1022 : | // *** The following two functions were created by Damaged Soul. | ||
1023 : | |||
1024 : | // Max messages reserved by engine (DO NOT MODIFY) | ||
1025 : | #define XS__MAX_ENGINE_MESSAGES 63 | ||
1026 : | // Max possible messages for mod, is 255 really the limit? | ||
1027 : | #define XS__MAX_POSSIBLE_MESSAGES 255 | ||
1028 : | |||
1029 : | // Returns max number of messages for mod | ||
1030 : | XS_LIBFUNC_ATTRIB xs_get_maxmessages() | ||
1031 : | { | ||
1032 : | new name[2]; | ||
1033 : | |||
1034 : | for (new i = XS__MAX_ENGINE_MESSAGES + 1; i <= XS__MAX_POSSIBLE_MESSAGES; i++) | ||
1035 : | if (!get_user_msgname(i, name, 1)) | ||
1036 : | return i - 1; | ||
1037 : | |||
1038 : | return XS__MAX_POSSIBLE_MESSAGES; | ||
1039 : | } | ||
1040 : | |||
1041 : | // Returns true if msgid is a valid message | ||
1042 : | XS_LIBFUNC_ATTRIB bool:xs_is_msg_valid(msgid) | ||
1043 : | { | ||
1044 : | new name[2]; | ||
1045 : | new retval = get_user_msgname(msgid, name, 1); | ||
1046 : | |||
1047 : | if (msgid < 1 || (msgid > XS__MAX_ENGINE_MESSAGES && !retval)) | ||
1048 : | return false; | ||
1049 : | |||
1050 : | return true; | ||
1051 : | } | ||
1052 : | |||
1053 : | /****** MANAGED TASKS ******/ | ||
1054 : | |||
1055 : | // ***** managed task ids | ||
1056 : | XS_LIBFUNC_ATTRIB xs_find_freetaskid() | ||
1057 : | { | ||
1058 : | for (new i = 1; i <= XS_TASK_MANAGEDIDS; ++i) | ||
1059 : | { | ||
1060 : | if (!task_exists(i)) | ||
1061 : | return i; | ||
1062 : | } | ||
1063 : | return -1; | ||
1064 : | } | ||
1065 : | |||
1066 : | // ***** managed tasks | ||
1067 : | enum xs_paramtypes | ||
1068 : | { | ||
1069 : | xs_invalid = 0, | ||
1070 : | xs_int, | ||
1071 : | xs_float, | ||
1072 : | xs_string | ||
1073 : | } | ||
1074 : | |||
1075 : | // new task | ||
1076 : | XS_LIBVAR_ATTRIB xs__TaskParam[ 1 + // number of parameters | ||
1077 : | XS_TASK_MAXPARAMS + // parameter types | ||
1078 : | (XS_TASK_MAXPARAMSIZE char) * XS_TASK_MAXPARAMS]; // space for len + value | ||
1079 : | |||
1080 : | XS_LIBVAR_ATTRIB Float:xs__TaskInterval = 0.0; | ||
1081 : | XS_LIBVAR_ATTRIB xs__TaskFlags[5]; | ||
1082 : | XS_LIBVAR_ATTRIB xs__TaskFunc[48]; | ||
1083 : | XS_LIBVAR_ATTRIB xs__TaskId; | ||
1084 : | XS_LIBVAR_ATTRIB xs__TaskRepeat; | ||
1085 : | |||
1086 : | #define xs__TaskParamCount xs__TaskParam[0] | ||
1087 : | #define xs__TaskParamType[%1] xs__TaskParam[1 + %1] | ||
1088 : | |||
1089 : | #define xs__TaskParamValue[%1] xs__TaskParam[1 + XS_TASK_MAXPARAMS + (%1 * (XS_TASK_MAXPARAMSIZE char))] | ||
1090 : | |||
1091 : | |||
1092 : | // incoming task | ||
1093 : | XS_LIBVAR_ATTRIB xs__ITaskParam[ 1 + // number of parameters | ||
1094 : | XS_TASK_MAXPARAMS + // parameter types | ||
1095 : | (XS_TASK_MAXPARAMSIZE char) * XS_TASK_MAXPARAMS]; // space for len + value | ||
1096 : | XS_LIBVAR_ATTRIB xs__ITaskId; | ||
1097 : | |||
1098 : | #define xs__ITaskParamCount xs__ITaskParam[0] | ||
1099 : | #define xs__ITaskParamType[%1] xs__ITaskParam[1 + %1] | ||
1100 : | |||
1101 : | #define xs__ITaskParamValue[%1] xs__ITaskParam[1 + XS_TASK_MAXPARAMS + (%1 * (XS_TASK_MAXPARAMSIZE char))] | ||
1102 : | |||
1103 : | // tested | ||
1104 : | XS_LIBFUNC_ATTRIB xs_task_begin(Float:interval, const func[], id = 0, const flags[] = "", repeat = 0) | ||
1105 : | { | ||
1106 : | xs_assert(xs__TaskInterval == 0.0, "New xs_task_begin called before xs_task_end"); | ||
1107 : | |||
1108 : | xs__TaskInterval = interval; | ||
1109 : | if (xs__TaskInterval < 0.1) | ||
1110 : | xs__TaskInterval = 0.1; | ||
1111 : | |||
1112 : | copy(xs__TaskFunc, 47, func); | ||
1113 : | xs__TaskId = id; | ||
1114 : | copy(xs__TaskFlags, 4, flags); | ||
1115 : | xs__TaskRepeat = repeat; | ||
1116 : | |||
1117 : | xs__TaskParamCount = 0; | ||
1118 : | } | ||
1119 : | |||
1120 : | // tested | ||
1121 : | XS_LIBFUNC_ATTRIB xs_task_pushint(value, bool:__isfl=false /*internal use only*/) | ||
1122 : | { | ||
1123 : | xs_assert(xs__TaskInterval, "xs_task_push* called without xs_task_begin"); | ||
1124 : | if (xs__TaskParamCount >= XS_TASK_MAXPARAMS) | ||
1125 : | return 0; | ||
1126 : | |||
1127 : | xs__TaskParamType[xs__TaskParamCount] = __isfl ? xs_float : xs_int; | ||
1128 : | xs__TaskParamValue[xs__TaskParamCount] = value; | ||
1129 : | |||
1130 : | ++xs__TaskParamCount; | ||
1131 : | return 1; | ||
1132 : | } | ||
1133 : | |||
1134 : | // tested | ||
1135 : | XS_LIBFUNC_ATTRIB xs_task_pushfl(Float:value) | ||
1136 : | { | ||
1137 : | return xs_task_pushint(_:value, true); | ||
1138 : | } | ||
1139 : | |||
1140 : | // tested | ||
1141 : | XS_LIBFUNC_ATTRIB xs_task_pushstr(const value[]) | ||
1142 : | { | ||
1143 : | xs_assert(xs__TaskInterval, "xs_task_push* called without xs_task_begin"); | ||
1144 : | if (xs__TaskParamCount >= XS_TASK_MAXPARAMS) | ||
1145 : | return 0; | ||
1146 : | |||
1147 : | xs__TaskParamType[xs__TaskParamCount] = xs_string; | ||
1148 : | strpack(xs__TaskParamValue[xs__TaskParamCount], value); | ||
1149 : | ++xs__TaskParamCount; | ||
1150 : | return 1; | ||
1151 : | } | ||
1152 : | |||
1153 : | // tested | ||
1154 : | XS_LIBFUNC_ATTRIB xs_task_end() | ||
1155 : | { | ||
1156 : | xs_assert(xs__TaskInterval, "xs_task_end called without xs_task_begin"); | ||
1157 : | |||
1158 : | // find a task id if needed | ||
1159 : | if (xs__TaskId == -1) | ||
1160 : | { | ||
1161 : | xs__TaskId = xs_find_freetaskid(); | ||
1162 : | if (xs__TaskId == -1) | ||
1163 : | { | ||
1164 : | // not found | ||
1165 : | xs__TaskInterval = 0.0; | ||
1166 : | return -1; | ||
1167 : | } | ||
1168 : | } | ||
1169 : | |||
1170 : | set_task(xs__TaskInterval, xs__TaskFunc, xs__TaskId, xs__TaskParam, | ||
1171 : | 1 + xs__TaskParamCount * (XS_TASK_MAXPARAMSIZE char), xs__TaskFlags, xs__TaskRepeat); | ||
1172 : | |||
1173 : | xs__TaskInterval = 0.0; | ||
1174 : | |||
1175 : | return xs__TaskId; | ||
1176 : | } | ||
1177 : | |||
1178 : | |||
1179 : | // tested | ||
1180 : | #define XS_MAKE_TASKFUNC(%1) public %1(const _xs__taskparam[], _xs__taskid) if(xs__task_setup(_xs__taskparam, _xs__taskid)) | ||
1181 : | |||
1182 : | // tested | ||
1183 : | XS_LIBFUNC_ATTRIB xs__task_setup(const param[], taskid) | ||
1184 : | { | ||
1185 : | xs__ITaskId = taskid; | ||
1186 : | new len = 1 + param[0] * (XS_TASK_MAXPARAMSIZE char); | ||
1187 : | for (new i = 0; i < len; ++i) | ||
1188 : | xs__ITaskParam[i] = param[i]; | ||
1189 : | return 1; | ||
1190 : | } | ||
1191 : | |||
1192 : | // tested | ||
1193 : | XS_LIBFUNC_ATTRIB xs_task_readid() | ||
1194 : | { | ||
1195 : | return xs__ITaskId; | ||
1196 : | } | ||
1197 : | |||
1198 : | // tested | ||
1199 : | XS_LIBFUNC_ATTRIB xs_task_paramcount() | ||
1200 : | { | ||
1201 : | return xs__ITaskParamCount; | ||
1202 : | } | ||
1203 : | |||
1204 : | // tested | ||
1205 : | XS_LIBFUNC_ATTRIB xs_paramtypes:xs_task_paramtype(paramid) | ||
1206 : | { | ||
1207 : | if (paramid < 0 || paramid >= xs__ITaskParamCount) | ||
1208 : | return xs_invalid; | ||
1209 : | |||
1210 : | return xs_paramtypes:xs__ITaskParamType[paramid]; | ||
1211 : | } | ||
1212 : | |||
1213 : | // tested | ||
1214 : | XS_LIBFUNC_ATTRIB xs_task_paramint(paramid) | ||
1215 : | { | ||
1216 : | if (paramid < 0 || paramid >= xs__ITaskParamCount) | ||
1217 : | return 0; | ||
1218 : | if (xs__ITaskParamType[paramid] != _:xs_int) | ||
1219 : | return 0; | ||
1220 : | |||
1221 : | return xs__ITaskParamValue[paramid]; | ||
1222 : | } | ||
1223 : | |||
1224 : | // tested | ||
1225 : | XS_LIBFUNC_ATTRIB Float:xs_task_paramfl(paramid) | ||
1226 : | { | ||
1227 : | if (paramid < 0 || paramid >= xs__ITaskParamCount) | ||
1228 : | return 0.0; | ||
1229 : | if (xs__ITaskParamType[paramid] != _:xs_float) | ||
1230 : | return 0.0; | ||
1231 : | |||
1232 : | return Float:xs__ITaskParamValue[paramid]; | ||
1233 : | } | ||
1234 : | |||
1235 : | // tested | ||
1236 : | XS_LIBFUNC_ATTRIB xs_task_paramstr(paramid, out[], maxlen) | ||
1237 : | { | ||
1238 : | #pragma unused maxlen | ||
1239 : | |||
1240 : | if (paramid < 0 || paramid >= xs__ITaskParamCount) | ||
1241 : | return 0; | ||
1242 : | if (xs__ITaskParamType[paramid] != _:xs_string) | ||
1243 : | return 0; | ||
1244 : | |||
1245 : | strunpack(out, xs__ITaskParamValue[paramid]); | ||
1246 : | return 1; | ||
1247 : | } | ||
1248 : | |||
1249 : | #pragma semicolon 0 |
Contact | ViewVC Help |
Powered by ViewVC 1.0.4 |