diff --git a/dlls/fakemeta/misc.cpp b/dlls/fakemeta/misc.cpp index 6273322c..1bae3c71 100644 --- a/dlls/fakemeta/misc.cpp +++ b/dlls/fakemeta/misc.cpp @@ -1,4 +1,5 @@ #include "fakemeta_amxx.h" +#include // HLSDK, for the animation natives static cell AMX_NATIVE_CALL copy_infokey_buffer(AMX *amx, cell *params) { @@ -7,7 +8,154 @@ static cell AMX_NATIVE_CALL copy_infokey_buffer(AMX *amx, cell *params) return MF_SetAmxString(amx, params[2], infobuffer, params[3]); } +int UTIL_stricmp(const char *s1, const char *s2) +{ + unsigned char c1, c2; + + for (;;) { + c1 = *s1++; + c2 = *s2++; + + if (!c1 || !c2) + break; + + if (c1 == c2) + continue; + + if ((c1 = tolower(c1)) != (c2 = tolower(c2))) + break; + } + return (int)c1 - (int)c2; +} + +// lookup_sequence(entid, "sequence name", &Float:framerate = 0.0, &bool:loops = false, &Float:groundspeed = 0.0); +static cell AMX_NATIVE_CALL lookup_sequence(AMX* amx, cell* params) +{ + int index = params[1]; + + CHECK_ENTITY(index); + + edict_t* ent = INDEXENT(index); + + studiohdr_t* pstudiohdr = static_cast(GET_MODEL_PTR(ent)); + + if (pstudiohdr == NULL) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Could not retrieve the model pointer from the entity provided."); + return 0; + } + + mstudioseqdesc_t* pseqdesc; + + pseqdesc = reinterpret_cast( + reinterpret_cast(pstudiohdr) + pstudiohdr->seqindex); + + char* label = MF_GetAmxString(amx, params[2], 0, NULL); + + for (int i = 0; i < pstudiohdr->numseq; i++) + { + if (UTIL_stricmp( pseqdesc[i].label, label ) == 0) + { + REAL* FrameRate = reinterpret_cast(MF_GetAmxAddr(amx, params[3])); + cell* Loops = MF_GetAmxAddr(amx, params[4]); + REAL* GroundSpeed = reinterpret_cast(MF_GetAmxAddr(amx, params[5])); + + // Taken from HLSDK: animation & animating.cpp + pseqdesc = &pseqdesc[i]; + *FrameRate = 256 * pseqdesc->fps / (pseqdesc->numframes - 1); + + *GroundSpeed = sqrt( pseqdesc->linearmovement[0]*pseqdesc->linearmovement[0]+ pseqdesc->linearmovement[1]*pseqdesc->linearmovement[1]+ pseqdesc->linearmovement[2]*pseqdesc->linearmovement[2] ); + *GroundSpeed = *GroundSpeed * pseqdesc->fps / (pseqdesc->numframes - 1); + + *Loops = pseqdesc->flags & STUDIO_LOOPING; + return i; + } + } + + return -1; + +}; +// Float:set_controller(entid, controllerid, Float:value); +static cell AMX_NATIVE_CALL set_controller(AMX* amx, cell* params) +{ +// From animation.cpp from the HLSDK +// SetController( void *pmodel, entvars_t *pev, int iController, float flValue ) + int entindex = params[1]; + CHECK_ENTITY(entindex); + edict_t* entity = INDEXENT(entindex); + + int iController = params[2]; + + if (iController < 0 || iController > 3) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid controller id passed. Expected 0 through 3, got %d.", iController); + return 0; + } + entvars_t* pev = &entity->v; + + float flValue = amx_ctof(params[3]); + + studiohdr_t* pstudiohdr = static_cast(GET_MODEL_PTR(entity)); + + if (! pstudiohdr) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Could not find the model pointer for the entity."); + return amx_ftoc(flValue); + } + + mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)pstudiohdr + pstudiohdr->bonecontrollerindex); + + int i = 0; + + // find first controller that matches the index + for (i = 0; i < pstudiohdr->numbonecontrollers; i++, pbonecontroller++) + { + if (pbonecontroller->index == iController) + break; + } + if (i >= pstudiohdr->numbonecontrollers) + return amx_ftoc(flValue); + + // wrap 0..360 if it's a rotational controller + + if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) + { + // ugly hack, invert value if end < start + if (pbonecontroller->end < pbonecontroller->start) + flValue = -flValue; + + // does the controller not wrap? + if (pbonecontroller->start + 359.0 >= pbonecontroller->end) + { + if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180) + flValue = flValue - 360; + if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180) + flValue = flValue + 360; + } + else + { + if (flValue > 360) + flValue = flValue - (int)(flValue / 360.0) * 360.0; + else if (flValue < 0) + flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0; + } + } + + int setting = static_cast(255 * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start)); + + if (setting < 0) setting = 0; + if (setting > 255) setting = 255; + pev->controller[iController] = setting; + + return amx_ftoc(setting * (1.0 / 255.0) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start); +} + + + AMX_NATIVE_INFO misc_natives[] = { { "copy_infokey_buffer", copy_infokey_buffer }, + { "lookup_sequence", lookup_sequence }, + { "set_controller", set_controller }, + {NULL, NULL}, }; diff --git a/dlls/fakemeta/pev.cpp b/dlls/fakemeta/pev.cpp index 44ad4807..cb314461 100755 --- a/dlls/fakemeta/pev.cpp +++ b/dlls/fakemeta/pev.cpp @@ -398,6 +398,45 @@ static cell AMX_NATIVE_CALL amx_set_pev(AMX *amx, cell *params) return 0; } +static cell AMX_NATIVE_CALL amx_set_pev_string(AMX *amx, cell *params) +{ + // index, pevdata + int index = params[1]; + CHECK_ENTITY(index); + edict_t *pEdict = INDEXENT2(index); + int iSwitch = params[2]; + + //onto normal cases - sanity check + if (iSwitch <= pev_string_start || iSwitch >= pev_absolute_end) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Undefined pev index: %d", iSwitch); + return 0; + } + + int offs = g_offset_table[iSwitch]; + + //sanity check #2 + if (offs == -1) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Undefined pev index: %d", iSwitch); + return 0; + } + + entvars_t *v = &(pEdict->v); + + if ( (iSwitch > pev_string_start && iSwitch < pev_string_end) + || (iSwitch > pev_string2_begin && iSwitch < pev_string2_end) ) + { + *(string_t *)EDICT_OFFS(v, offs) = params[3]; + } + else + { + MF_LogError(amx, AMX_ERR_NATIVE, "Non-string field passed to set_pev_string!"); + return 0; + } + + return 0; +} static cell AMX_NATIVE_CALL amx_pev_valid(AMX *amx, cell *params) { @@ -413,10 +452,20 @@ static cell AMX_NATIVE_CALL amx_pev_valid(AMX *amx, cell *params) return 1; } +static cell AMX_NATIVE_CALL amx_pev_serial(AMX* amx, cell* params) +{ + int id = static_cast(params[1]); + CHECK_ENTITY(id); + edict_t* ent = INDEXENT(id); + + return ent->serialnumber; +} AMX_NATIVE_INFO pev_natives[] = { { "pev", amx_pev }, { "set_pev", amx_set_pev }, + { "set_pev_string", amx_set_pev_string }, { "pev_valid", amx_pev_valid }, + { "pev_serial", amx_pev_serial }, {NULL, NULL}, }; diff --git a/plugins/include/fakemeta.inc b/plugins/include/fakemeta.inc index bf71a4ef..8deabc94 100755 --- a/plugins/include/fakemeta.inc +++ b/plugins/include/fakemeta.inc @@ -21,26 +21,72 @@ #pragma library fakemeta #endif -/* Returns entvar data from an entity Use the pev_* enum to specify which form of data you want returned. +/** + * Returns entvar data from an entity. Use the pev_* enum (in fakemeta_const.inc) to specify which data you want retrieved. * - * If retrieving strings, you may optionally get a pointer into the global string table. Depending on - * your situation, there are two ways to do this. - * 1: This simply gets the pointer. - * new ptr = pev(entid, pev_classname) - * 2: The pointer will be stored in ptr AND the actual string is retrieved. - * new ptr, classname[32] - * pev(entid, pev_classname, ptr, classname, 31) + * @note This function uses "read_data" style data syntax. It returns integer values, + * by-references float data, and sets a buffer for string data. + * + * @note If retrieving strings, you may optionally get a pointer into the global string table. Depending on + * your situation, there are two ways to do this. + * 1: This simply gets the pointer. + * new ptr = pev(entid, pev_classname) + * 2: The pointer will be stored in ptr AND the actual string is retrieved. + * new ptr, classname[32] + * pev(entid, pev_classname, ptr, classname, 31) + * + * @param _index The entity index to lookup. + * @param _value The pev field to lookup (look in fakemeta_const.inc) */ native pev(_index,_value,any:...); -/* Sets entvar data for an entity. Use the pev_* enum */ +/** + * Sets entvar data for an entity. Use the pev_* enum from fakemeta_const.inc for reference. + * + * @note Setting string data will automatically allocate a new string (via AllocString) + * If you have a string already allocated with your own call to AllocString, use + * set_pev_string_ptr instead. + * + * @param _index The entity index to set the value on. + * @param _value The pev field to set, (look in fakemeta_const.inc) + */ native set_pev(_index,_value,any:...); -/* returns 0 if ent is invalid, >0 if valid - * (1 == valid, 2 == valid+pvPrivateData valid) +/** + * Use this native to set a pev field to a string that is already allocated (via a function such + * as EngFunc_AllocString). + * + * @note If you specify _value as anything other than string fields, an error will be thrown. + * @note Pass 0 as the _string field to set it to an empty string. + * + * @param _index The entity index to set the value on. + * @param _value The pev field to set - MUST be a string field. + * @param _string The string handle, retrieved from places like AllocString. + */ +native set_pev_string(_index, _value, _string) + + +/** + * Checks the validity of an entity. + * + * @param entindex The entity id to check. + * + * @return 0 on invalid entity + * 1 on entity is valid + * 2 on entity is valid and it has private data (safe to use pdata natives on). */ native pev_valid(entindex); +/** + * Returns the serial number for each entity. The serial number is a unique identity + * generated when an entity is created. + * + * @param entindex The entity id. + * + * @return The serial number for the entity. + */ +native pev_serial(entindex); + /* Returns any global variable inside globalvars_t structure. Use the glb_* enum. * * When returning data from glb_pStringBase (the global string table), you may give a pointer into that table @@ -168,3 +214,26 @@ native set_pdata_string(entity, offset, const source[], realloc=2, linux=-5); // Copies the given infoBuffer pointer into out[] // An infoBuffer pointer is returned by EngFunc_GetInfoKeyBuffer native copy_infokey_buffer(infoBuffer, out[], maxlen); + + +/** + * Looks up the sequence for the entity. + * + * @param entity The entity id to lookup. + * @param name The sequence name to lookup, case insensitive. ("JUMP" would match "jump") + * @param framerate The framerate of the sequence, if found. + * @param loops Whether or not the sequence loops. + * @param groundspeed The groundspeed setting of the sequence. + * @return -1 on failed lookup, the sequence number on successful lookup. + */ +native lookup_sequence(entity, const name[], &Float:framerate = 0.0, &bool:loops = false, &Float:groundspeed = 0.0) + +/** + * Sets a bone controller with the specified value. + * + * @param entity The entity id to set the value on. + * @param controller Which controller to set (0 through 3). + * @param value The value to set it to. + * @return The percentage that the controller is extended (0.0 through 1.0) + */ +native Float:set_controller(entity, controller, Float:value) \ No newline at end of file