// vim: set ts=4 sw=4 tw=99 noet: // // AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). // Copyright (C) The AMX Mod X Development Team. // // This software is licensed under the GNU General Public License, version 3 or higher. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: // https://alliedmods.net/amxmodx-license // // Fakemeta Module // #include "fakemeta_amxx.h" /** Optimizations for Fakemeta. In the end we'll do this for other things too. */ static int g_offset_table[pev_absolute_end] = {-1}; #define DO_OFFSET(offs) g_offset_table[offs] = offsetof(entvars_t, offs) #define DO_OFFSET_R(named, real, offs) g_offset_table[named] = offsetof(entvars_t, real) + offs void initialze_offsets() { DO_OFFSET(fixangle); DO_OFFSET(modelindex); DO_OFFSET(viewmodel); DO_OFFSET(weaponmodel); DO_OFFSET(movetype); DO_OFFSET(solid); DO_OFFSET(skin); DO_OFFSET(body); DO_OFFSET(effects); DO_OFFSET(light_level); DO_OFFSET(sequence); DO_OFFSET(gaitsequence); DO_OFFSET(rendermode); DO_OFFSET(renderfx); DO_OFFSET(weapons); DO_OFFSET(deadflag); DO_OFFSET(button); DO_OFFSET(impulse); DO_OFFSET(spawnflags); DO_OFFSET(flags); DO_OFFSET(colormap); DO_OFFSET(team); DO_OFFSET(waterlevel); DO_OFFSET(watertype); DO_OFFSET(playerclass); DO_OFFSET(weaponanim); DO_OFFSET(pushmsec); DO_OFFSET(bInDuck); DO_OFFSET(flTimeStepSound); DO_OFFSET(flSwimTime); DO_OFFSET(flDuckTime); DO_OFFSET(iStepLeft); DO_OFFSET(gamestate); DO_OFFSET(oldbuttons); DO_OFFSET(groupinfo); DO_OFFSET(iuser1); DO_OFFSET(iuser2); DO_OFFSET(iuser3); DO_OFFSET(iuser4); DO_OFFSET(impacttime); DO_OFFSET(starttime); DO_OFFSET(idealpitch); DO_OFFSET(ideal_yaw); DO_OFFSET(pitch_speed); DO_OFFSET(yaw_speed); DO_OFFSET(ltime); DO_OFFSET(nextthink); DO_OFFSET(gravity); DO_OFFSET(friction); DO_OFFSET(frame); DO_OFFSET(animtime); DO_OFFSET(framerate); DO_OFFSET(scale); DO_OFFSET(renderamt); DO_OFFSET(health); DO_OFFSET(frags); DO_OFFSET(takedamage); DO_OFFSET(max_health); DO_OFFSET(teleport_time); DO_OFFSET(armortype); DO_OFFSET(armorvalue); DO_OFFSET(dmg_take); DO_OFFSET(dmg_save); DO_OFFSET(dmg); DO_OFFSET(dmgtime); DO_OFFSET(speed); DO_OFFSET(air_finished); DO_OFFSET(pain_finished); DO_OFFSET(radsuit_finished); DO_OFFSET(maxspeed); DO_OFFSET(fov); DO_OFFSET(flFallVelocity); DO_OFFSET(fuser1); DO_OFFSET(fuser2); DO_OFFSET(fuser3); DO_OFFSET(fuser4); DO_OFFSET(classname); DO_OFFSET(globalname); DO_OFFSET(model); DO_OFFSET(target); DO_OFFSET(targetname); DO_OFFSET(netname); DO_OFFSET(message); DO_OFFSET(noise); DO_OFFSET(noise1); DO_OFFSET(noise2); DO_OFFSET(noise3); DO_OFFSET(chain); DO_OFFSET(dmg_inflictor); DO_OFFSET(enemy); DO_OFFSET(aiment); DO_OFFSET(owner); DO_OFFSET(groundentity); DO_OFFSET(euser1); DO_OFFSET(euser2); DO_OFFSET(euser3); DO_OFFSET(euser4); DO_OFFSET(origin); DO_OFFSET(oldorigin); DO_OFFSET(velocity); DO_OFFSET(basevelocity); DO_OFFSET(clbasevelocity); DO_OFFSET(movedir); DO_OFFSET(angles); DO_OFFSET(avelocity); DO_OFFSET(v_angle); DO_OFFSET(endpos); DO_OFFSET(startpos); DO_OFFSET(absmin); DO_OFFSET(absmax); DO_OFFSET(mins); DO_OFFSET(maxs); DO_OFFSET(size); DO_OFFSET(rendercolor); DO_OFFSET(view_ofs); DO_OFFSET(vuser1); DO_OFFSET(vuser2); DO_OFFSET(vuser3); DO_OFFSET(vuser4); DO_OFFSET(punchangle); DO_OFFSET(controller); DO_OFFSET_R(controller_0, controller, 0); DO_OFFSET_R(controller_1, controller, 1); DO_OFFSET_R(controller_2, controller, 2); DO_OFFSET_R(controller_3, controller, 3); DO_OFFSET(blending); DO_OFFSET_R(blending_0, blending, 0); DO_OFFSET_R(blending_1, blending, 1); DO_OFFSET_R(pev_weaponmodel2, weaponmodel, 0); DO_OFFSET_R(pev_viewmodel2, viewmodel, 0); DO_OFFSET(pContainingEntity); } #define EDICT_OFFS(v,o) ((char *)v + o) // originally by mahnsawce static cell AMX_NATIVE_CALL amx_pev(AMX *amx,cell *params) { 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; } enum { Ret_Int = (1<<0), Ret_Float = (1<<1), Ret_Vec = (1<<2), Ret_ByteArray = (1<<3), Ret_String = (1<<4), Ret_Edict = (1<<5), Ret_Bytes2 = (1<<6), Ret_Bytes4 = (1<<7) }; union { int i; float f; byte b; string_t s; byte ba[4]; } rets; Vector vr; int ValType = 0; entvars_t *v = &(pEdict->v); //get primitive data types if (iSwitch > pev_int_start && iSwitch < pev_int_end) { rets.i = *(int *)EDICT_OFFS(v, offs); ValType = Ret_Int; } else if (iSwitch > pev_float_start && iSwitch < pev_float_end) { rets.f = *(float *)EDICT_OFFS(v, offs); ValType = Ret_Float; } else if (iSwitch > pev_vecarray_start && iSwitch < pev_vecarray_end) { vr = *(vec3_t *)EDICT_OFFS(v, offs); ValType = Ret_Vec; } else if (iSwitch > pev_bytearray_start && iSwitch < pev_bytearray_end) { if (iSwitch == controller) { rets.ba[0] = v->controller[0]; rets.ba[1] = v->controller[1]; rets.ba[2] = v->controller[2]; rets.ba[3] = v->controller[3]; ValType = Ret_Bytes4; } else { rets.ba[0] = v->blending[0]; rets.ba[1] = v->blending[1]; ValType = Ret_Bytes2; } } else if (iSwitch > pev_byte_start && iSwitch < pev_byte_end) { rets.b = *(byte *)EDICT_OFFS(v, offs); ValType = Ret_Int; } else if ( (iSwitch > pev_string_start && iSwitch < pev_string_end) || (iSwitch > pev_string2_begin && iSwitch < pev_string2_end) ) { rets.s = *(string_t *)EDICT_OFFS(v, offs); ValType = Ret_String; } else if ( (iSwitch > pev_edict_start && iSwitch < pev_edict_end) || (iSwitch > pev_edict2_start && iSwitch < pev_absolute_end) ) { edict_t *e = *(edict_t **)EDICT_OFFS(v, offs); rets.i = ENTINDEX(e); ValType = Ret_Int; ValType |= Ret_Edict; } size_t count = params[0] / sizeof(cell) - 2; if (count == 0) { //return an int if (ValType & Ret_Int) { return rets.i; } else if (ValType == Ret_Float) { return (cell)rets.f; } else if (ValType == Ret_String) { return (cell)rets.s; } else { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid return type"); return 0; } } else if (count == 1) { //return a byref float - usually cell *addr = MF_GetAmxAddr(amx, params[3]); if (ValType == Ret_Float) { *addr = amx_ftoc(rets.f); } else if (ValType == Ret_Int) { REAL f = (REAL)rets.i; *addr = amx_ftoc(f); } else if (ValType == Ret_Vec) { addr[0] = amx_ftoc(vr.x); addr[1] = amx_ftoc(vr.y); addr[2] = amx_ftoc(vr.z); } else if (ValType == Ret_Bytes2) { addr[0] = rets.ba[0]; addr[1] = rets.ba[1]; } else if (ValType == Ret_Bytes4) { addr[0] = rets.ba[0]; addr[1] = rets.ba[1]; addr[2] = rets.ba[2]; addr[3] = rets.ba[3]; } else { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid return type"); return 0; } return 1; } else if (count == 2) { cell size = *(MF_GetAmxAddr(amx, params[4])); if (ValType == Ret_String) { const char *str = STRING(rets.s); if (!str) str = ""; int num = MF_SetAmxString(amx, params[3], str, size); return num; } else if (ValType & Ret_Int) { char temp[32]; ke::SafeSprintf(temp, sizeof(temp), "%d", rets.i); return MF_SetAmxString(amx, params[3], temp, size); } else if (ValType == Ret_Float) { char temp[32]; ke::SafeSprintf(temp, sizeof(temp), "%f", rets.f); return MF_SetAmxString(amx, params[3], temp, size); } else if (ValType == Ret_Vec) { char temp[32]; ke::SafeSprintf(temp, sizeof(temp), "%f %f %f", vr.x, vr.y, vr.z); return MF_SetAmxString(amx, params[3], temp, size); } else if (ValType == Ret_Bytes2) { char temp[32]; ke::SafeSprintf(temp, sizeof(temp), "%d %d", rets.ba[0], rets.ba[1]); return MF_SetAmxString(amx, params[3], temp, size); } else if (ValType == Ret_Bytes4) { char temp[32]; ke::SafeSprintf(temp, sizeof(temp), "%d %d %d %d", rets.ba[0], rets.ba[1], rets.ba[2], rets.ba[3]); return MF_SetAmxString(amx, params[3], temp, size); } MF_LogError(amx, AMX_ERR_NATIVE, "Invalid return type"); } else if (count == 3) { cell size = *(MF_GetAmxAddr(amx, params[5])); if (ValType == Ret_String) { const char *str = STRING(rets.s); cell *addr = MF_GetAmxAddr(amx, params[3]); *addr = (cell)rets.s; if (!str) str = ""; int num = MF_SetAmxString(amx, params[4], str, size); return num; } MF_LogError(amx, AMX_ERR_NATIVE, "Invalid return type"); } //if we got here, something happened MF_LogError(amx, AMX_ERR_NATIVE, "Unknown pev index or return combination %d", iSwitch); return 0; } static cell AMX_NATIVE_CALL amx_set_pev(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; } cell *blah = MF_GetAmxAddr(amx,params[3]); entvars_t *v = &(pEdict->v); if (iSwitch > pev_int_start && iSwitch < pev_int_end) { *(int *)EDICT_OFFS(v, offs) = (int)*blah; } else if (iSwitch > pev_float_start && iSwitch < pev_float_end) { *(float *)EDICT_OFFS(v, offs) = (float)amx_ctof(blah[0]); } else if ( (iSwitch > pev_string_start && iSwitch < pev_string_end) || (iSwitch > pev_string2_begin && iSwitch < pev_string2_end) ) { int len; char *string = MF_GetAmxString(amx, params[3], 0, &len); string_t value = ALLOC_STRING(string); *(string_t *)EDICT_OFFS(v, offs) = value; } else if ( (iSwitch > pev_edict_start && iSwitch < pev_edict_end) || (iSwitch > pev_edict2_start && iSwitch < pev_absolute_end) ) { edict_t *e = INDEXENT2((int)*blah); *(edict_t **)EDICT_OFFS(v, offs) = e; } else if (iSwitch > pev_vecarray_start && iSwitch < pev_vecarray_end) { vec3_t vec; vec[0] = amx_ctof(blah[0]); vec[1] = amx_ctof(blah[1]); vec[2] = amx_ctof(blah[2]); *(vec3_t *)EDICT_OFFS(v, offs) = vec; } else if (iSwitch > pev_byte_start && iSwitch < pev_byte_end) { byte b = static_cast(blah[0]); *(byte *)EDICT_OFFS(v, offs) = b; } else if (iSwitch > pev_bytearray_start && iSwitch < pev_bytearray_end) { switch(iSwitch) { case controller: pEdict->v.controller[0]=blah[0]; pEdict->v.controller[1]=blah[1]; pEdict->v.controller[2]=blah[2]; pEdict->v.controller[3]=blah[3]; return 1; case blending: pEdict->v.blending[0]=blah[0]; pEdict->v.blending[1]=blah[1]; return 1; } } 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) { int idx = static_cast(params[1]); edict_t *e = INDEXENT2(idx); if (FNullEnt(e)) return 0; if (e->pvPrivateData) return 2; 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}, };