From ca720c8c83beeb34c3eee98b09e4a5fa0c837b46 Mon Sep 17 00:00:00 2001 From: Pavol Marko Date: Sat, 13 Nov 2004 10:20:12 +0000 Subject: [PATCH] XS Library --- plugins/include/xs.inc | 1237 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1237 insertions(+) create mode 100755 plugins/include/xs.inc diff --git a/plugins/include/xs.inc b/plugins/include/xs.inc new file mode 100755 index 00000000..e665db3e --- /dev/null +++ b/plugins/include/xs.inc @@ -0,0 +1,1237 @@ +/* XS Library +* for AMX and AMXX +* +* Copyright (C) 2004 Pavol "PM" Marko +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +* Version 0.1 +* +* +* MACROS THAT YOU CAN DEFINE BEFORE INCLUDING XS.INC: +* XS_FLEQ_TOLERANCE: +* Tolerance that is used for XS_FLEQ float nearly-equal comparisions +* DEFAULT: 0.000005 +* XS_DEBUG +* Turn debug logging on +* DEFAULT: 0 +* XS_LOGBUFFER_SIZE +* Buffer size for logging +* DEFAULT: 512 +* XS_TASK_MAXPARAMS +* Maximal parameter count for managed tasks +* DEFAULT: 8 +* XS_TASK_MAXPARAMSIZE +* Maximal size of string parameter for tasks +* Has to be power of 2 and has to be >= 8 +* DEFAULT: 512 +* XS_TASK_MANAGEDIDS +* Number of managed IDs for tasks. +* DEFAULT: 2048 +* XS_REPLACEBUF_SIZE +* DEFAULT: 3072 +* +* +* NOTES: +* On AMX, VexdUM is required for some math functions +* +* xs__ / XS__ (2 underscores) stuff is meant to be intern +* +* untested: never tested +* half-tested: succesfully used in other applications; not extensively tested in xs though +* tested: fully tested +* +* If you have any useful functions / ideas for functions, please tell me. +*/ + +// **** CONFIG CHECK + +#if !defined XS_FLEQ_TOLERANCE + #define XS_FLEQ_TOLERANCE 0.000005 +#endif + +#if !defined XS_DEBUG + #define XS_DEBUG 0 +#endif + +#if !defined XS_LOGBUFFER_SIZE + #define XS_LOGBUFFER_SIZE 512 +#endif + +#if !defined XS_TASK_MAXPARAMS + #define XS_TASK_MAXPARAMS 8 +#endif + +#if !defined XS_TASK_MAXPARAMSIZE + #define XS_TASK_MAXPARAMSIZE 512 +#endif + +#if !defined XS_TASK_MANAGEDIDS + #define XS_TASK_MANAGEDIDS 2048 +#endif + +#if !defined XS_REPLACEBUF_SIZE + #define XS_REPLACEBUF_SIZE 3072 +#endif + +// **** Detect platform +#define XS_AMX 0 +#define XS_AMXX 1 + +#if defined _amxmodx_included + #define XS_PLATFORM XS_AMXX +#endif + +#if defined _amxmod_included && !defined _amxmodx_included + #define XS_PLATFORM XS_AMX +#endif + +#if !defined XS_PLATFORM + // Could not detect platform. + // Make sure you include amxmodx.inc or amxmod.inc before including xs.inc + #assert 0 + #endinput +#endif + +// Turn on for release +#define XS__LIBRELEASE 1 + +#if XS__LIBRELEASE + #define XS_LIBFUNC_ATTRIB stock +#else + #define XS_LIBFUNC_ATTRIB +#endif +#if XS__LIBRELEASE + #define XS_LIBVAR_ATTRIB stock +#else + #define XS_LIBVAR_ATTRIB new +#endif + + +// Hardcore +#pragma semicolon 1 +/****** DEBUGGING / LOGING FUNCTIONS ******/ + enum xs_logtypes + { + xs_debug, + xs_message, + xs_warning, + xs_error, + xs_fatalerror, + xs__assertionfailed, + + // must come last + xs_logtypes_count + } + + XS_LIBVAR_ATTRIB const xs__logtypenames[xs_logtypes_count][] = {"DEBUG", "", "WARNING", "ERROR", "FATAL ERROR", "DEBUG ASSERTION FAILED"}; + + // tested + XS_LIBFUNC_ATTRIB xs_log(xs_logtypes:logtype, {Float,_}:...) + { + // WARNING: Don't try to use assert in here; it uses this func + + // Don't log debug if not in debug mode + #if !XS_DEBUG + if (logtype == xs_debug) + return; + #endif + + new buffer[XS_LOGBUFFER_SIZE+1]; + buffer[XS_LOGBUFFER_SIZE]=0; + format_args(buffer, XS_LOGBUFFER_SIZE, 1 /* go from SECOND argument*/); + new bool:addLogTypeName = strlen(xs__logtypenames[logtype]) ? true : false; + + #if XS_PLATFORM == XS_AMX + new plugname[32]; + new dummy[1]; + get_plugin(-1, plugname, 31, dummy, 0, dummy, 0, dummy, 0, dummy, 0, dummy[0]); + // log into HL Logs + log_message("[AMX][%s]: %s%s%s", plugname, addLogTypeName ? xs__logtypenames[logtype] : "", + addLogTypeName ? ": " : "", buffer); + #else // assume AMXX + + // Use AMXX's logging system + log_amx("%s%s%s", addLogTypeName ? xs__logtypenames[logtype] : "", + addLogTypeName ? ": " : "", buffer); + #endif + } + + // Assertion + // tested + XS_LIBFUNC_ATTRIB xs_assertfunc({Float,_}:exp, const desc[]) + { + // Check exp + if (exp) + return 1; // ok + + // not ok + + // print info + xs_log(xs__assertionfailed, "%s", desc); + + return 0; + } + #define xs_assert(%1,%2) if (!xs_assertfunc(%1,%2)) xs__global_null /= xs__global_null + + + // Assertion; only in debug mode + // untested; logical flow says it should work + #if XS_DEBUG + #define xs_assert_dbg(%1,%2) if (!xs_assertfunc(%1,%2)) xs__global_null /= xs__global_null + #else + #define xs_assert_dbg(%1,%2) + #endif + + new xs__global_null = 0; + +/****** MATH FUNCTIONS ******/ + + /****** BASIC STUFF ******/ + + #if XS_PLATFORM == XS_AMX + enum anglemode + { + radian = 0, + degrees, + grades + } + #endif + + // Returns -1 if num is negative, 0 if num is 0, 1 if num is positive + // tested + XS_LIBFUNC_ATTRIB xs_sign(num) + { + return (num < 0) ? -1 : ((num == 0) ? 0 : 1); + } + + // Returns -1 if num is negative, 0 if num is 0, 1 if num is positive + // tested + XS_LIBFUNC_ATTRIB xs_fsign(Float:num) + { + return (num < 0.0) ? -1 : ((num == 0.0) ? 0 : 1); + } + + // Returns absolute value + // tested + XS_LIBFUNC_ATTRIB xs_abs(num) + { + return (num < 0) ? -num : num; + } + + // is power of 2? (== can be expressed as 1<= ((%2) - XS_FLEQ_TOLERANCE))) + + // 1/sqrt + // tested + XS_LIBFUNC_ATTRIB Float:xs_rsqrt(Float:x) + { + #if XS_PLATFORM == XS_AMX + // store half + new Float:xhalf = x * 0.5; + + // compute initial guess + new i = _:x; + i = 0x5f375a84 - (i >> 1); + x = Float:i; + + // refine 3 times + x = x * (1.5 - xhalf * x * x); + x = x * (1.5 - xhalf * x * x); + x = x * (1.5 - xhalf * x * x); + + return x; + #else + return 1.0 / floatsqroot(x); + #endif + } + + // sqrt + // tested + XS_LIBFUNC_ATTRIB Float:xs_sqrt(Float:x) + { + #if XS_PLATFORM == XS_AMX + // 1.0 / rsqrt should still be faster than loop-using-approximation-methods + return 1.0 / xs_rsqrt(x); + #else + return floatsqroot(x); + #endif + } + + // These functions generate errors if you use the macros with wrong parameter count. + stock Float:xs_fabs(Float:pa) + { + #pragma unused pa + new rawr = you_need_one_param_for_fabs; + rawr = warning_below_shows_line_number; + #pragma unused rawr + } + stock Float:xs_asin(Float:pa,Float:pb) + { + #pragma unused pa,pb + new rawr = you_need_two_params_for_asin; + rawr = warning_below_shows_line_number; + #pragma unused rawr + } + stock Float:xs_sin(Float:pa,Float:pb) + { + #pragma unused pa,pb + new rawr = you_need_two_params_for_sin; + #pragma unused rawr + } + stock Float:xs_acos(Float:pa,Float:pb) + { + #pragma unused pa,pb + new rawr = you_need_two_params_for_acos; + rawr = warning_below_shows_line_number; + #pragma unused rawr + } + stock Float:xs_cos(Float:pa,Float:pb) + { + #pragma unused pa,pb + new rawr = you_need_two_params_for_cos; + rawr = warning_below_shows_line_number; + #pragma unused rawr + } + stock Float:xs_atan(Float:pa,Float:pb) + { + #pragma unused pa,pb + new rawr = you_need_two_params_for_atan; + rawr = warning_below_shows_line_number; + #pragma unused rawr + } + stock Float:xs_atan2(Float:pa,Float:pb) + { + #pragma unused pa,pb + new rawr = you_need_two_params_for_atan2; + rawr = warning_below_shows_line_number; + #pragma unused rawr + } + stock Float:xs_tan(Float:pa, Float:pb) + { + #pragma unused pa,pb + new rawr = you_need_two_params_for_tan; + rawr = warning_below_shows_line_number; + #pragma unused rawr + } + + #if XS_PLATFORM == XS_AMX + #pragma semicolon 0 + #include + #pragma semicolon 1 + // We need stocks to provide radian / degrees / grades functionality + + XS_LIBFUNC_ATTRIB Float:xs__2rad(Float:x, anglemode:mod) + { + switch (mod) + { + case radian: + return x; + case degrees: + return xs_deg2rad(x); + case grades: + return xs_gra2rad(x); + default: + xs_assert(0, "xs_asin, xs_sin, xs_acos, xs_cos, xs_atan, xs_atan2 or xs_tan called with invalid mod param"); + } + + return 0.0; // compiler warning + } + + #define xs_fabs(%1) fabs(%1) + #define xs_asin(%1,%2) asin(xs__2rad(%1, %2)) + #define xs_sin(%1,%2) sin(xs__2rad(%1, %2)) + #define xs_acos(%1,%2) acos(xs__2rad(%1, %2)) + #define xs_cos(%1,%2) cos(xs__2rad(%1, %2)) + #define xs_atan(%1,%2) atan(xs__2rad(%1, %2)) + #define xs_atan2(%1,%2) atan2(xs__2rad(%1, %2)) + #define xs_tan(%1,%2) tan(xs__2rad(%1, %2)) + #else + #define xs_fabs(%1) floatabs(%1) + #define xs_asin(%1,%2) floatasin(%1, %2) + #define xs_sin(%1,%2) floatsin(%1, %2) + #define xs_acos(%1,%2) floatacos(%1, %2) + #define xs_cos(%1,%2) floatcos(%1, %2) + #define xs_atan(%1,%2) floatatan(%1, %2) + #define xs_atan2(%1,%2) floatatan2(%1, %2) + #define xs_tan(%1,%2) floattan(%1, %2) + #endif + + /****** RANDOM NUMBERS ******/ + // This routine comes from the book "Inner Loops" by Rick Booth, Addison-Wesley + // (ISBN 0-201-47960-5). This is a "multiplicative congruential random number + // generator" that has been extended to 31-bits + + XS_LIBVAR_ATTRIB xs__internalseed=0x546875; + + #define XS__IL_RMULT 1103515245 + + // tested + XS_LIBFUNC_ATTRIB xs_seed(seed) + { + xs__internalseed = seed; + } + + // tested + XS_LIBFUNC_ATTRIB xs_irand() + { + new lo, hi, ll, lh, hh, hl; + new result; + + lo = xs__internalseed & 0xffff; + hi = xs__internalseed >> 16; + xs__internalseed = xs__internalseed * XS__IL_RMULT + 12345; + ll = lo * (XS__IL_RMULT & 0xffff); + lh = lo * (XS__IL_RMULT >> 16 ); + hl = hi * (XS__IL_RMULT & 0xffff); + hh = hi * (XS__IL_RMULT >> 16 ); + result = xs_abs(((ll + 12345) >> 16) + lh + hl + (hh << 16)); + return result; + } + + // tested + XS_LIBFUNC_ATTRIB Float:xs_frand() + { + return float(xs_irand()) / float(xs_get_maxnum()); // -1/2 should be the biggest possible positive number + } + + // tested + XS_LIBFUNC_ATTRIB xs_irand_range(pmin, pmax) + { + xs_assert_dbg(pmax - pmin >= 0, "xs_irand_range: pmin > pmax"); + new i = pmin + floatround(xs_frand() * float(pmax - pmin)); + if (i > pmax) + i = pmax; + return i; + } + + /****** VECTORS & PLANES ******/ + + // *** vectors + + // Set vec components to values + // tested + XS_LIBFUNC_ATTRIB xs_vec_set(Float:vec[], Float:x, Float:y, Float:z) + { + vec[0] = x; + vec[1] = y; + vec[2] = z; + } + + // Add vec + // tested + XS_LIBFUNC_ATTRIB xs_vec_add(const Float:in1[], const Float:in2[], Float:out[]) + { + out[0] = in1[0] + in2[0]; + out[1] = in1[1] + in2[1]; + out[2] = in1[2] + in2[2]; + } + + // Subtract vec + // untested, but should work + XS_LIBFUNC_ATTRIB xs_vec_sub(const Float:in1[], const Float:in2[], Float:out[]) + { + out[0] = in1[0] - in2[0]; + out[1] = in1[1] - in2[1]; + out[2] = in1[2] - in2[2]; + } + + // Are vectors equal? + // untested, but should work + XS_LIBFUNC_ATTRIB bool:xs_vec_equal(const Float:vec1[], const Float:vec2[]) + { + return (vec1[0] == vec2[0]) && (vec1[1] == vec2[1]) && (vec1[2] == vec2[2]); + } + + // Are vectors nearly equal? + // tested + XS_LIBFUNC_ATTRIB bool:xs_vec_nearlyequal(const Float:vec1[], const Float:vec2[]) + { + return XS_FLEQ(vec1[0], vec2[0]) && XS_FLEQ(vec1[1], vec2[1]) && XS_FLEQ(vec1[2], vec2[2]); + } + + // multiply vector by scalar + // tested + XS_LIBFUNC_ATTRIB xs_vec_mul_scalar(const Float:vec[], Float:scalar, Float:out[]) + { + out[0] = vec[0] * scalar; + out[1] = vec[1] * scalar; + out[2] = vec[2] * scalar; + } + + // divide vector by scalar + // untested, but should work + XS_LIBFUNC_ATTRIB xs_vec_div_scalar(const Float:vec[], Float:scalar, Float:out[]) + { + new Float:__tmp = 1.0 / scalar; + out[0] = vec[0] * __tmp; + out[1] = vec[1] * __tmp; + out[2] = vec[2] * __tmp; + } + + // Compute vector length + // tested + XS_LIBFUNC_ATTRIB Float:xs_vec_len(const Float:vec[]) + { + return xs_sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); + } + + // Normalize vec + // tested + XS_LIBFUNC_ATTRIB xs_vec_normalize(const Float:vec[], Float:out[]) + { + new Float:invlen = xs_rsqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); + out[0] = vec[0] * invlen; + out[1] = vec[1] * invlen; + out[2] = vec[2] * invlen; + } + + // Store the cross product of vec1 and vec2 in out + // tested + XS_LIBFUNC_ATTRIB xs_vec_cross(const Float:vec1[], const Float:vec2[], Float:out[]) + { + out[0] = vec1[1]*vec2[2] - vec1[2]*vec2[1]; + out[1] = vec1[2]*vec2[0] - vec1[0]*vec2[2]; + out[2] = vec1[0]*vec2[1] - vec1[1]*vec2[0]; + } + + // Compute vec1 dot vec2 + // tested + XS_LIBFUNC_ATTRIB Float:xs_vec_dot(const Float:vec1[], const Float:vec2[]) + { + return vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]; + } + + // Negate vec into out + // untested, but should work + XS_LIBFUNC_ATTRIB xs_vec_neg(const Float:vec[], Float:out[]) + { + out[0] = -vec[0]; + out[1] = -vec[1]; + out[2] = -vec[2]; + } + + // Copy vec + // untested, but should work + XS_LIBFUNC_ATTRIB xs_vec_copy(const Float:vecIn[], Float:vecOut[]) + { + vecOut[0] = vecIn[0]; + vecOut[1] = vecIn[1]; + vecOut[2] = vecIn[2]; + } + + // Compute angle between vec1 and vec2 + // tested + XS_LIBFUNC_ATTRIB Float:xs_vec_angle(const Float:vec1[], const Float:vec2[]) + { + return xs_rad2deg(xs_acos(xs_vec_dot(vec1, vec2), radian)); + } + + // Reflect vec about normal + // untested + XS_LIBFUNC_ATTRIB xs_vec_reflect(const Float:vec[], const Float:normal[], Float:out[]) + { + // normalize(vec) - (normal * 2.0 * (tmp . normal)) * length(vec) + + new Float:tmp1[3]; + xs_vec_normalize(vec, tmp1); + + // tmp1 - (normal * 2.0 * (tmp . normal)) * length(vec) + + new Float:tmp2[3]; + xs_vec_mul_scalar(normal, 2.0, tmp2); + xs_vec_mul_scalar(tmp2, xs_vec_dot(tmp1, normal), tmp2); + + // tmp1 - tmp2 * length(vec) + xs_vec_mul_scalar(tmp2, xs_vec_len(vec), tmp2); + + // tmp1 - tmp2 + xs_vec_sub(tmp1, tmp2, out); + } + + // *** planes + + // normal + #define XS_PLANE_A 0 + #define XS_PLANE_B 1 + #define XS_PLANE_C 2 + // plane shift distance + #define XS_PLANE_D 3 + + + // Set a plane to specific values + // tested + XS_LIBFUNC_ATTRIB xs_plane_set(Float:plane[], Float:a, Float:b, Float:c, Float:d) + { + plane[XS_PLANE_A] = a; + plane[XS_PLANE_B] = b; + plane[XS_PLANE_C] = c; + plane[XS_PLANE_D] = d; + } + + // Construct a plane out of 3 points + // tested + XS_LIBFUNC_ATTRIB xs_plane_3p(Float:plane[], const Float:p1[], const Float:p2[], const Float:p3[]) + { + new Float:normalA[3], Float:normalB[3]; + + // normalA = Normalize(p3 - p1); + normalA[0] = p3[0] - p1[0]; + normalA[1] = p3[1] - p1[1]; + normalA[2] = p3[2] - p1[2]; + xs_vec_normalize(normalA, normalA); + + // normalB = Normalize(p3 - p2); + normalB[0] = p3[0] - p2[0]; + normalB[1] = p3[1] - p2[1]; + normalB[2] = p3[2] - p2[2]; + xs_vec_normalize(normalB, normalB); + + // plane normal = Normalize(normalA cross normalB) + xs_vec_cross(normalA, normalB, plane); + xs_vec_normalize(plane, plane); + + // plane shift distance = (-p1) dot plane normal + new Float:__tmp[3]; + xs_vec_neg(plane, __tmp); + plane[XS_PLANE_D] = xs_vec_dot(__tmp, p1); + + } + + // untested, but should work + XS_LIBFUNC_ATTRIB bool:xs_plane_equal(const Float:plane1[], const Float:plane2[]) + { + if ( (plane1[0] == plane2[0]) && + (plane1[1] == plane2[1]) && + (plane1[2] == plane2[2]) && + (plane1[3] == plane2[3])) + return true; + return false; + } + + // untested, but should work + XS_LIBFUNC_ATTRIB bool:xs_plane_nearlyequal(const Float:plane1[], const Float:plane2[]) + { + if ( XS_FLEQ(plane1[0], plane2[0]) && + XS_FLEQ(plane1[1], plane2[1]) && + XS_FLEQ(plane1[2], plane2[2]) && + XS_FLEQ(plane1[3], plane2[3])) + return true; + return false; + } + + // Compute distance between plane and point + // tested + XS_LIBFUNC_ATTRIB Float:xs_plane_dst2point(const Float:plane[], const Float:point[]) + { + // return normal dot point + D + return xs_vec_dot(plane, point) + plane[XS_PLANE_D]; + } + + // Checks whether plane intersects with the ray starting and rayStart and going to rayDir direction. + // If yes, returns true and sets out to the intersection point + // Otherwise, returns false + // tested + XS_LIBFUNC_ATTRIB bool:xs_plane_rayintersect(const Float:plane[], const Float:rayStart[], const Float:rayDir[], Float:out[]) + { + new Float:a = xs_vec_dot(plane, rayDir); + + if (a == 0.0) + return false; // ray is parallel to plane + + // if (distance plane<->(rayStart + rayDir) > distance plane<->rayStart) and both have the same sign, the ray + // goes away from the plane + new Float:rsplusrd[3]; + xs_vec_add(rayStart, rayDir, rsplusrd); + new Float:dst1 = xs_plane_dst2point(plane, rsplusrd); + new Float:dst2 = xs_plane_dst2point(plane, rayStart); + if (xs_fabs(dst1) > xs_fabs(dst2) && xs_fsign(dst1) == xs_fsign(dst2)) + return false; + + + // out = rayStart - rayDir * ((distance plane<->rayStart) / a) + new Float:__tmp[3]; + xs_vec_mul_scalar(rayDir, xs_plane_dst2point(plane, rayStart) / a, __tmp); + // out = rayStart - tmp + xs_vec_sub(rayStart, __tmp, out); + + return true; + } + + // Is point on plane? + // tested + XS_LIBFUNC_ATTRIB bool:xs_point_onplane(const Float:plane[], const Float:point[]) + { + return XS_FLEQ(xs_plane_dst2point(plane, point), 0.0); + } + + // Project point on plane + // tested + XS_LIBFUNC_ATTRIB xs_projpoint_onplane(const Float:plane[], const Float:point[], Float:out[]) + { + new Float:__tmp[3]; + // out = point - (plane normal * distance point<->plane) + xs_vec_copy(plane, __tmp); + xs_vec_mul_scalar(__tmp, xs_plane_dst2point(plane, point), __tmp); + xs_vec_sub(point, __tmp, out); + } + + // Copy plane + // untested, but should work + XS_LIBFUNC_ATTRIB xs_plane_copy(const Float:planeIn[], Float:planeOut[]) + { + planeOut[0] = planeIn[0]; + planeOut[1] = planeIn[1]; + planeOut[2] = planeIn[2]; + planeOut[3] = planeIn[3]; + } + + /****** HL ENGINE SPECIFIC STUFF ******/ + // Compute forward, right and up vector from angles + // half-tested + + // angle indexes + #define XS_PITCH 0 // up / down + #define XS_YAW 1 // left / right + #define XS_ROLL 2 // fall over + + XS_LIBFUNC_ATTRIB xs_anglevectors(const Float:angles[3], Float:fwd[3], Float:right[3], Float:up[3]) + { + // sin (s) and cos (c) for yaw (y), pitch (p) and roll (r) + new Float:sr, Float:sp, Float:sy, Float:cr, Float:cp, Float:cy; + + sy = xs_sin(angles[XS_YAW], degrees); + cy = xs_cos(angles[XS_YAW], degrees); + sp = xs_sin(angles[XS_PITCH], degrees); + cp = xs_cos(angles[XS_PITCH], degrees); + sr = xs_sin(angles[XS_ROLL], degrees); + cr = xs_cos(angles[XS_ROLL], degrees); + + fwd[0] = cp*cy; + fwd[1] = cp*sy; + fwd[2] = -sp; + + right[0] = (-1*sr*sp*cy + -1*cr*-sy); + right[1] = (-1*sr*sp*sy + -1*cr*cy); + right[2] = -1*sr*cp; + + up[0] = (cr*sp*cy + -sr*-sy); + up[1] = (cr*sp*sy + -sr*cy); + up[2] = cr*cp; + } +/****** STRING FUNCS *******/ + + // tested + XS_LIBFUNC_ATTRIB xs_strchr(const str[], chr) + { + for (new i = 0; str[i] != 0; ++i) + { + if (str[i] == chr) + return i; + } + return -1; + } + + // by JGHG, adapted + // removes charstotrim number of charactes from stringtotrim's + // - beginning if fromleft is true + // - end if fromleft is false + // tested + XS_LIBFUNC_ATTRIB xs_strtrim(stringtotrim[], charstotrim, bool:fromleft = true) + { + if (charstotrim <= 0) + return; + + if (fromleft) + { + new maxlen = strlen(stringtotrim); + if (charstotrim > maxlen) + charstotrim = maxlen; + + // In format, input and output regions can overlap + format(stringtotrim, maxlen, "%s", stringtotrim[charstotrim]); + } + else + { + new maxlen = strlen(stringtotrim) - charstotrim; + if (maxlen < 0) + maxlen = 0; + + // In format, input and output regions can overlap + format(stringtotrim, maxlen, "%s", stringtotrim); + } + } + + // by xeroblood, adapted + // copies characters from oldmsg to newmsg, starting at start and ending at end (_includes_ end). + // terminates newmsg with 0 + // if outlen is positive, it specifies the maximal number of characters to be copied. + // otherwise, assumes that newmsg is at least end-start+1 characters long. + // tested + XS_LIBFUNC_ATTRIB xs_strmid(const oldmsg[], newmsg[], start, end, outlen=-1) + { + new len = strlen(oldmsg); + + if(start < 0) + start = 0; + + ++end; // Include end + + if(end <= start || end > len) + end = len; + + new j = 0, i = start; + for(; (i < end) && (outlen--);) + newmsg[j++] = oldmsg[i++]; + + newmsg[j] = 0; + } + + // by xeroblood, adapted + // maxelems: maximal number of elements in output, elemsize: maximal size of one element + // tested + XS_LIBFUNC_ATTRIB xs_explode(const input[], output[][], delimiter, maxelems, elemsize) + { + new nIdx = 0; + new nLen = 0; + + new copied = 0; + while(nLen < strlen(input) && nIdx < maxelems) + { + copied = copyc(output[nIdx++], elemsize, input[nLen], delimiter); + if (copied == elemsize) + { + // maybe it got force-stopped because of maxsize + // so check whether we have to skip something + if (input[nLen + copied] != delimiter && input[nLen + copied] != 0) + { + new found = xs_strchr(input[nLen + copied], delimiter); + if (found == -1) + break; + copied += found; + } + } + + nLen += copied + 1; // +1: skip delimiter + } + return nIdx; + } + + // returns number of cells written. + XS_LIBFUNC_ATTRIB xs_implode(output[], outsize, delimiter, const input[][], elemsnum) + { + new pos = 0; + new copied; + for (new i = 0; i < elemsnum; ++i) + { + copied = copy(output[pos], outsize - pos, input[i]); + pos += copied; + if (pos >= outsize) + return outsize; + // append delimiter + output[pos] = delimiter; + ++pos; + // last check + if (pos >= outsize) + return outsize; + } + + output[--pos] = 0; // The last char would be delimiter, so skip it. + return pos; + } + + + XS_LIBVAR_ATTRIB xs__replace_buf[XS_REPLACEBUF_SIZE]; + // Replace all occurencies of what in text with with + // Returns number of (also partially if trimmed by len) replaced items. + XS_LIBFUNC_ATTRIB xs_replace(text[], len, const what[], const with[]) + { + new occur = 0; + new i = 0; + new bufPos = 0; + new replaceLen = strlen(with); + new whatLen = strlen(what); + for (; text[i]; ++i) + { + if (text[i] == what[0]) + { + new posInWhat=0; + new j; + for (j = i; j-i < replaceLen && text[j]; ++j, ++posInWhat) + { + if (text[j] != what[posInWhat]) + break; + } + if (whatLen == posInWhat) + { + for (new i2 = 0; i2 < replaceLen && bufPos < XS_REPLACEBUF_SIZE; ++i2) + xs__replace_buf[bufPos++] = with[i2]; + i = j - 1; + ++occur; + if (bufPos >= XS_REPLACEBUF_SIZE) + return occur; + continue; + } + } + if (bufPos >= XS_REPLACEBUF_SIZE) + return occur; + xs__replace_buf[bufPos++] = text[i]; + } + xs__replace_buf[bufPos] = 0; + copy(text, len, xs__replace_buf); + return occur; + } + + // replaces all occurencies of what in text with with + // Returns number of replaced items. + XS_LIBFUNC_ATTRIB xs_replace_char(text[], len, what, with) + { + // let the xs_replace function do the work + new arr[4]; + arr[0] = what; + arr[1] = 0; + arr[2] = with; + arr[3] = 0; + + return xs_replace(text, len, arr[0], arr[2]); + } + + #if XS_PLATFORM == XS_AMX + // message_begin checking for AMX + xs__hook_message_begin(dest, msg_type, origin[3]={0,0,0}, player = 0) + { + xs_assert(xs_is_msg_valid(msg_type), "message_begin called with bogus message type"); + return message_begin(dest, msg_type, origin, player); + } + + #define message_begin xs__hook_message_begin + #endif +/****** MISC FUNCS *******/ + // sets namestr to name of the command identified by cid + // half-tested + XS_LIBFUNC_ATTRIB xs_concmd_name(cid, namestr[], namelen) + { + new dummy1; + new dummy2[1]; + get_concmd(cid, namestr, namelen, dummy1, dummy2, 0, 0); + } + + // Checks whether there are at least num free visible slots + // half-tested + XS_LIBFUNC_ATTRIB bool:xs_freevisibleslots(num) + { + new maxplayers = get_cvar_num("sv_visiblemaxplayers"); + if (maxplayers <= 0) + maxplayers = get_maxplayers(); + + return (get_playersnum(1) <= maxplayers-num) ? true : false; + } + + // Returns biggest possible positive number + XS_LIBVAR_ATTRIB xs__maxnum = 0; + // tested + XS_LIBFUNC_ATTRIB xs_get_maxnum() + { + if (!xs__maxnum) + { + // build it + xs__maxnum = ((1 << (cellbits - 2)) - 1 ) | (1 << (cellbits - 2)); + /* + new bits = get_cellsize() * 8 - 1; + for (new i = 0; i < bits; ++i) + xs__maxnum |= 1 << i; + */ + } + return xs__maxnum; + } + + // tested + XS_LIBFUNC_ATTRIB xs_get_minnum() + { + return xs_get_maxnum() + 1; + } + + + // *** The following two functions were created by Damaged Soul. + + // Max messages reserved by engine (DO NOT MODIFY) + #define XS__MAX_ENGINE_MESSAGES 63 + // Max possible messages for mod, is 255 really the limit? + #define XS__MAX_POSSIBLE_MESSAGES 255 + + // Returns max number of messages for mod + XS_LIBFUNC_ATTRIB xs_get_maxmessages() + { + new name[32]; + + for (new i = XS__MAX_ENGINE_MESSAGES + 1; i <= XS__MAX_POSSIBLE_MESSAGES; i++) + if (!get_user_msgname(i, name, 31)) + return i - 1; + + return XS__MAX_ENGINE_MESSAGES; + } + + // Returns true if msgid is a valid message + XS_LIBFUNC_ATTRIB bool:xs_is_msg_valid(msgid) + { + new name[32]; + new retval = get_user_msgname(msgid, name, 31); + + if (msgid < 1 || (msgid > XS__MAX_ENGINE_MESSAGES && !retval)) + return false; + + return true; + } + +/****** MANAGED TASKS ******/ + + // ***** managed task ids + XS_LIBFUNC_ATTRIB xs_find_freetaskid() + { + for (new i = 1; i <= XS_TASK_MANAGEDIDS; ++i) + { + if (!task_exists(i)) + return i; + } + return -1; + } + + // ***** managed tasks + enum xs_paramtypes + { + xs_invalid = 0, + xs_int, + xs_float, + xs_string + } + + // new task + XS_LIBVAR_ATTRIB xs__TaskParam[ 1 + // number of parameters + XS_TASK_MAXPARAMS + // parameter types + (XS_TASK_MAXPARAMSIZE char) * XS_TASK_MAXPARAMS]; // space for len + value + + XS_LIBVAR_ATTRIB Float:xs__TaskInterval = 0.0; + XS_LIBVAR_ATTRIB xs__TaskFlags[5]; + XS_LIBVAR_ATTRIB xs__TaskFunc[48]; + XS_LIBVAR_ATTRIB xs__TaskId; + XS_LIBVAR_ATTRIB xs__TaskRepeat; + + #define xs__TaskParamCount xs__TaskParam[0] + #define xs__TaskParamType[%1] xs__TaskParam[1 + %1] + + #define xs__TaskParamValue[%1] xs__TaskParam[1 + XS_TASK_MAXPARAMS + (%1 * (XS_TASK_MAXPARAMSIZE char))] + + + // incoming task + XS_LIBVAR_ATTRIB xs__ITaskParam[ 1 + // number of parameters + XS_TASK_MAXPARAMS + // parameter types + (XS_TASK_MAXPARAMSIZE char) * XS_TASK_MAXPARAMS]; // space for len + value + XS_LIBVAR_ATTRIB xs__ITaskId; + + #define xs__ITaskParamCount xs__ITaskParam[0] + #define xs__ITaskParamType[%1] xs__ITaskParam[1 + %1] + + #define xs__ITaskParamValue[%1] xs__ITaskParam[1 + XS_TASK_MAXPARAMS + (%1 * (XS_TASK_MAXPARAMSIZE char))] + + // tested + XS_LIBFUNC_ATTRIB xs_task_begin(Float:interval, const func[], id = 0, const flags[] = "", repeat = 0) + { + xs_assert(xs__TaskInterval == 0.0, "New xs_task_begin called before xs_task_end"); + + xs__TaskInterval = interval; + if (xs__TaskInterval < 0.1) + xs__TaskInterval = 0.1; + + copy(xs__TaskFunc, 47, func); + xs__TaskId = id; + copy(xs__TaskFlags, 4, flags); + xs__TaskRepeat = repeat; + + xs__TaskParamCount = 0; + } + + // tested + XS_LIBFUNC_ATTRIB xs_task_pushint(value, bool:__isfl=false /*internal use only*/) + { + xs_assert(xs__TaskInterval, "xs_task_push* called without xs_task_begin"); + if (xs__TaskParamCount >= XS_TASK_MAXPARAMS) + return 0; + + xs__TaskParamType[xs__TaskParamCount] = __isfl ? xs_float : xs_int; + xs__TaskParamValue[xs__TaskParamCount] = value; + + ++xs__TaskParamCount; + return 1; + } + + // tested + XS_LIBFUNC_ATTRIB xs_task_pushfl(Float:value) + { + return xs_task_pushint(_:value, true); + } + + // tested + XS_LIBFUNC_ATTRIB xs_task_pushstr(const value[]) + { + xs_assert(xs__TaskInterval, "xs_task_push* called without xs_task_begin"); + if (xs__TaskParamCount >= XS_TASK_MAXPARAMS) + return 0; + + xs__TaskParamType[xs__TaskParamCount] = xs_string; + strpack(xs__TaskParamValue[xs__TaskParamCount], value); + ++xs__TaskParamCount; + return 1; + } + + // tested + XS_LIBFUNC_ATTRIB xs_task_end() + { + xs_assert(xs__TaskInterval, "xs_task_end called without xs_task_begin"); + + // find a task id if needed + if (xs__TaskId == -1) + { + xs__TaskId = xs_find_freetaskid(); + if (xs__TaskId == -1) + { + // not found + xs__TaskInterval = 0.0; + return -1; + } + } + + set_task(xs__TaskInterval, xs__TaskFunc, xs__TaskId, xs__TaskParam, + 1 + xs__TaskParamCount * (XS_TASK_MAXPARAMSIZE char), xs__TaskFlags, xs__TaskRepeat); + + xs__TaskInterval = 0.0; + + return xs__TaskId; + } + + + // tested + #define XS_MAKE_TASKFUNC(%1) public %1(const _xs__taskparam[], _xs__taskid) if(xs__task_setup(_xs__taskparam, _xs__taskid)) + + // tested + XS_LIBFUNC_ATTRIB xs__task_setup(const param[], taskid) + { + xs__ITaskId = taskid; + new len = 1 + param[0] * (XS_TASK_MAXPARAMSIZE char); + for (new i = 0; i < len; ++i) + xs__ITaskParam[i] = param[i]; + return 1; + } + + // tested + XS_LIBFUNC_ATTRIB xs_task_readid() + { + return xs__ITaskId; + } + + // tested + XS_LIBFUNC_ATTRIB xs_task_paramcount() + { + return xs__ITaskParamCount; + } + + // tested + XS_LIBFUNC_ATTRIB xs_paramtypes:xs_task_paramtype(paramid) + { + if (paramid < 0 || paramid >= xs__ITaskParamCount) + return xs_invalid; + + return xs_paramtypes:xs__ITaskParamType[paramid]; + } + + // tested + XS_LIBFUNC_ATTRIB xs_task_paramint(paramid) + { + if (paramid < 0 || paramid >= xs__ITaskParamCount) + return 0; + if (xs__ITaskParamType[paramid] != _:xs_int) + return 0; + + return xs__ITaskParamValue[paramid]; + } + + // tested + XS_LIBFUNC_ATTRIB Float:xs_task_paramfl(paramid) + { + if (paramid < 0 || paramid >= xs__ITaskParamCount) + return 0.0; + if (xs__ITaskParamType[paramid] != _:xs_float) + return 0.0; + + return Float:xs__ITaskParamValue[paramid]; + } + + // tested + XS_LIBFUNC_ATTRIB xs_task_paramstr(paramid, out[], maxlen) + { + #pragma unused maxlen + + if (paramid < 0 || paramid >= xs__ITaskParamCount) + return 0; + if (xs__ITaskParamType[paramid] != _:xs_string) + return 0; + + strunpack(out, xs__ITaskParamValue[paramid]); + return 1; + } + +#pragma semicolon 0 \ No newline at end of file