From bd9fe1f487d9a0d52a4a6ae6fef38e131945e0ba Mon Sep 17 00:00:00 2001 From: Giegue Date: Mon, 6 Mar 2023 20:02:04 -0300 Subject: [PATCH] Handle entvar keyvalues. --- README.md | 2 - extra/README.md | 2 + extra/base/README.md | 25 +++++ extra/base/bin/glb_dispatchuse.amxx | Bin 0 -> 1547 bytes extra/base/src/glb_dispatchuse.sma | 24 ++++ src/dlls/cmbase.cpp | 15 +++ src/dlls/cmbase.h | 2 +- src/dlls/dllapi.cpp | 47 +++++++- src/dlls/util.cpp | 163 ++++++++++++++++++++++++++++ 9 files changed, 274 insertions(+), 6 deletions(-) create mode 100644 extra/base/README.md create mode 100644 extra/base/bin/glb_dispatchuse.amxx create mode 100644 extra/base/src/glb_dispatchuse.sma diff --git a/README.md b/README.md index d93b501..c8da562 100644 --- a/README.md +++ b/README.md @@ -158,8 +158,6 @@ I'm aware that the plugin is far from perfect, and there are a few things that n - Rarely, Stukabats will become unable to fly towards their target, standing in air doing seemingly nothing. Cause of bug unknown. -- Entvars are not recognized, so anything that is a pevfield that is not used by monstermod is unusable. - There are probably more issues that aren't listed here, I'll attempt to fix them as I find them. Of course, any bug report is welcome. If reporting a bug, try to explain step by step how the bug ocurred. The easier it is to replicate a bug, the easier it is to locate it and fix it. ## Milestones diff --git a/extra/README.md b/extra/README.md index 5df6f0e..aa681a4 100644 --- a/extra/README.md +++ b/extra/README.md @@ -4,4 +4,6 @@ This folder contains auxiliary tools used to enhance the compatibility and/or in As these are auxiliary, they aren't needed to enjoy MonsterMod. If you want an extra boost, feel free to install these. +Plugins from the `base` folder should come first, then the ones from the appropiate mod folder. + Once all milestones are completed, the possibility of removing the need for external plugins will be considered. Until then, this has to stay. diff --git a/extra/base/README.md b/extra/base/README.md new file mode 100644 index 0000000..4e3384d --- /dev/null +++ b/extra/base/README.md @@ -0,0 +1,25 @@ +## Base Plugins (All Mods) + +Auxiliary Tools to use MonsterMod in any GoldSrc game. + +These are base plugins that should be installed first before adding any mod-specific plugin. + +### AMX Mod X Plugins + +#### -A note about AMXX plugins- + +All plugins in this section require AMXX 1.9.0 or greater, they will not work on 1.8.2 or older. + +Compiled plugins are provided in the `bin` folder for your convenience. However, if you prefer to build the plugins yourself, the source code of all AMXX plugins are located in the `src` folder for compilation. + +#### GoldSrc --> MonsterMod Use Dispatcher + +Because MonsterMod entities are, -quite literally-, just a func_wall with its own logic, trying to trigger any MonsterMod entity from the outside will fail, as it will attempt to use the logic of game's "func_wall" instead of our own. + +To add salt to injury, Metamod is incapable of hooking use functions, as the pfnUse/DispatchUse methods provided by the API has been deprecated, and no longer work. Leaving AMXX's HamSandwich as the only module that can hook an entity's Use() function. + +In normal circunstances, you do not need this plugin. But let's say you have a turret monster, disabled by default, and you gave it a targetname. Even with a name, triggering this turret regardless of the method used will not activate it. + +This plugin redirects the game's func_wall Use() function to MonsterMod, connecting the missing piece and allowing you to trigger MonsterMod entities with your shiny func_button. + +I can't believe I've done this. *incomprehensible screams* diff --git a/extra/base/bin/glb_dispatchuse.amxx b/extra/base/bin/glb_dispatchuse.amxx new file mode 100644 index 0000000000000000000000000000000000000000..4d912151f45f71dfacc2b807711c721ccc8a8554 GIT binary patch literal 1547 zcmV+m2K4z@SWQ6y0|5l{1pol24FCXeMgRa90001ZoSj!oY#c=pt+AbCLmV)~1Ooxg z*BD~2oy2i~h+yyf2exC`4u5IX-tFBTGN0A-jN?@}aEU|^2OtvS2oebK5dy&>2M#&p zfDl*2B~l{J5fLYHKnky?Yu4j+6bK`|ecjd7RsE^%>aA}jy7Xr**G4&_HloWNM0LpO zlSJ3>9U`Iy;A6&F;1W=T?(bmqqCbEiE&OQU>tya>?q`06d5-y2W{0`Ke2V!)=1;&F z7JY3Qe`mq%M9-ZE525}uco_UEcm#YAd=PvEe1wXQs*9*pJEkPrc11+OwQIt&Wf%q_ z)!n%2__pIa5mjYmS7N_pJ7p>f*GpETkqSMFo>%Y#t+cV?K>YikKdzrIiPsjHW*2in2`CI10%$;m&E8FO58GDvHEW;PL{v>lZ+t|i7dTua|TUz!a zUAuG+oxCm=+DxBQz#irv=5FRL=G4{!9$ydJ+|D-lu>T#rc0-xE#r5lb(|`axV3POE z)q4g=AY{EMU<{Z8CV&Ah9|h)tC19D`7J;XMUf>v@*p{vr*@u?Z*|+vp0%|Qjhk0$& zu}^Vdty2YJF6*ztb)Rk7zzMeRGUz|}&yJVaPTVqwN7=^Blq+m+6ajPv`9`0*UD(}2 z$?oXL$kzf-=HG~2NBH@oR3#Bsg~|(W%?rwF1H*$)4Gs-dooHm==bIQ7EnPV}HklzWOZ8JXK0^b4t1-y&Q zk=RYT`vZDffKDaJZE4O%Q;c+z2#g;+&ko2#DOBL|nrlb|=@lp<$UL`be(o7!}Sl*aeQ29p94(RzbE z+}ZR^&3Pu>z?tmWv7xaY&DnfO%{Xh{tiyTK+{(EPi>ZgO`PGoTc9P#B4>S9Y(5J?t zl^}2t*9#VZl%}GB-&=%D5f#WYFkDFb(cP$J~6rE6F z`w@EG5b4HPjf;@(MC~Oh>VaQyg;I&>-P%zc1>ULZFZqm*xr! zBE$oap3vn*sbV)mnnigciLJdfGe5&B15{WQ;kb?$g;Y>Nr4|IoDNj?vlZC*qI8~&F z%+qf;^Z46G`f<5m{~FT=bWtx?vYaEEUWW%;--n~R!?gC8ejPX%Ll@uASU{R;69hDTUI%4Z3j)-&MqR-XrtY)89wGUHt2{)b_Jy xZs +#include +#include + +public plugin_init() +{ + register_plugin( "GAME-MONSTER: Use Dispatcher", "1.0", "Giegue" ); + + RegisterHam( Ham_Use, "func_wall", "DispatchUse" ); +} + +public DispatchUse( entity, caller, activator, useType, Float:value ) +{ + // all monstermod entities have this set + if ( entity_get_edict( entity, EV_ENT_euser4 ) ) + { + server_cmd( "_use %i %i %i %i %f", entity, caller, activator, useType, value ); + return HAM_SUPERCEDE; + } + + return HAM_IGNORED; +} diff --git a/src/dlls/cmbase.cpp b/src/dlls/cmbase.cpp index 644a062..e97cc96 100644 --- a/src/dlls/cmbase.cpp +++ b/src/dlls/cmbase.cpp @@ -17,6 +17,8 @@ #include "cmbase.h" #include "decals.h" +void EntvarsKeyvalue( entvars_t *pev, KeyValueData *pkvd ); + extern Vector VecBModelOrigin( entvars_t* pevBModel ); extern DLL_GLOBAL Vector g_vecAttackDir; @@ -103,6 +105,19 @@ edict_t *CMBaseEntity::CreateEntity(char *classname) return pent; } +// process entvar keyvalue +void CMBaseEntity :: KeyValue( KeyValueData* pkvd ) +{ + if ( !pev || !pkvd ) + return; + + if ( pkvd->fHandled ) + { + // only handled data contain readable strings + EntvarsKeyvalue( pev, pkvd ); + } +} + // give health int CMBaseEntity :: TakeHealth( float flHealth, int bitsDamageType ) { diff --git a/src/dlls/cmbase.h b/src/dlls/cmbase.h index 6f5d1f0..824ae1c 100644 --- a/src/dlls/cmbase.h +++ b/src/dlls/cmbase.h @@ -134,7 +134,7 @@ public: // initialization functions virtual void Spawn( void ) { return; } virtual void Precache( void ) { return; } - virtual void KeyValue( KeyValueData* pkvd) { pkvd->fHandled = FALSE; } + virtual void KeyValue( KeyValueData* pkvd ); virtual int ObjectCaps( void ) { return FCAP_ACROSS_TRANSITION; } virtual void Activate( void ) {} diff --git a/src/dlls/dllapi.cpp b/src/dlls/dllapi.cpp index e81a07f..572a000 100644 --- a/src/dlls/dllapi.cpp +++ b/src/dlls/dllapi.cpp @@ -1327,6 +1327,46 @@ void mmDispatchTouch( edict_t *pentTouched, edict_t *pentOther ) RETURN_META(MRES_IGNORED); } +// pfnUse has been deprecated so the only way to trigger a monstermod +// entity from the outside is to do it manually. ARRGHH! -Giegue +void mmDispatchUse( void ) +{ + if ( CMD_ARGC() >= 6 ) // the command itself is an argument, we need 5. so argc == 6 + { + edict_t *entity = INDEXENT( atoi( CMD_ARGV( 1 ) ) ); + edict_t *caller = INDEXENT( atoi( CMD_ARGV( 2 ) ) ); + edict_t *activator = INDEXENT( atoi( CMD_ARGV( 3 ) ) ); + USE_TYPE useType = USE_TYPE( atoi( CMD_ARGV( 4 ) ) ); + float flValue = atof( CMD_ARGV( 5 ) ); + + // nevermind the unoptimization that this brings... >C + for (int index=0; index < monster_ents_used; index++) + { + if ((entity != NULL) && (entity == monsters[index].monster_pent)) + { + if ( FNullEnt( caller ) ) caller = NULL; + if ( FNullEnt( activator ) ) activator = NULL; + + monsters[index].pMonster->Use( caller, activator, useType, flValue ); + return; + } + } + } +} + +void mmDispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd ) +{ + for (int index=0; index < monster_ents_used; index++) + { + if ((pentKeyvalue != NULL) && (pentKeyvalue == monsters[index].monster_pent)) + { + monsters[index].pMonster->KeyValue( pkvd ); + RETURN_META(MRES_SUPERCEDE); + } + } + + RETURN_META(MRES_IGNORED); +} void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) { @@ -1371,6 +1411,7 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) (g_engfuncs.pfnAddServerCommand)("monster", MonsterCommand); (g_engfuncs.pfnAddServerCommand)("node_viewer", SpawnViewerCommand); + (g_engfuncs.pfnAddServerCommand)("_use", mmDispatchUse); for (index = 0; monster_types[index].name[0]; index++) { @@ -1531,10 +1572,10 @@ static DLL_FUNCTIONS gFunctionTable = mmGameDLLInit, //! pfnGameInit() Initialize the game (one-time call after loading of game .dll) mmDispatchSpawn, //! pfnSpawn() mmDispatchThink, //! pfnThink - NULL, // pfnUse + NULL, // pfnUse [DEPRECATED] mmDispatchTouch, //! pfnTouch NULL, // pfnBlocked - NULL, // pfnKeyValue + mmDispatchKeyValue, //! pfnKeyValue NULL, // pfnSave NULL, // pfnRestore NULL, // pfnSetAbsBox @@ -1631,7 +1672,7 @@ static DLL_FUNCTIONS gFunctionTable_Post = NULL, // pfnGameInit() Initialize the game (one-time call after loading of game .dll) NULL, // pfnSpawn() mmDispatchThink_Post, //! pfnThink - NULL, // pfnUse + NULL, // pfnUse [DEPRECATED] NULL, // pfnTouch NULL, // pfnBlocked NULL, // pfnKeyValue diff --git a/src/dlls/util.cpp b/src/dlls/util.cpp index c6382ab..c1a0d18 100644 --- a/src/dlls/util.cpp +++ b/src/dlls/util.cpp @@ -226,6 +226,120 @@ UTIL_GroupTrace::~UTIL_GroupTrace( void ) } +TYPEDESCRIPTION gEntvarsDescription[] = +{ + DEFINE_ENTITY_FIELD( classname, FIELD_STRING ), + DEFINE_ENTITY_GLOBAL_FIELD( globalname, FIELD_STRING ), + + DEFINE_ENTITY_FIELD( origin, FIELD_POSITION_VECTOR ), + DEFINE_ENTITY_FIELD( oldorigin, FIELD_POSITION_VECTOR ), + DEFINE_ENTITY_FIELD( velocity, FIELD_VECTOR ), + DEFINE_ENTITY_FIELD( basevelocity, FIELD_VECTOR ), + DEFINE_ENTITY_FIELD( movedir, FIELD_VECTOR ), + + DEFINE_ENTITY_FIELD( angles, FIELD_VECTOR ), + DEFINE_ENTITY_FIELD( avelocity, FIELD_VECTOR ), + DEFINE_ENTITY_FIELD( punchangle, FIELD_VECTOR ), + DEFINE_ENTITY_FIELD( v_angle, FIELD_VECTOR ), + DEFINE_ENTITY_FIELD( fixangle, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( idealpitch, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( pitch_speed, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( ideal_yaw, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( yaw_speed, FIELD_FLOAT ), + + DEFINE_ENTITY_FIELD( modelindex, FIELD_INTEGER ), + DEFINE_ENTITY_GLOBAL_FIELD( model, FIELD_MODELNAME ), + + DEFINE_ENTITY_FIELD( viewmodel, FIELD_MODELNAME ), + DEFINE_ENTITY_FIELD( weaponmodel, FIELD_MODELNAME ), + + DEFINE_ENTITY_FIELD( absmin, FIELD_POSITION_VECTOR ), + DEFINE_ENTITY_FIELD( absmax, FIELD_POSITION_VECTOR ), + DEFINE_ENTITY_GLOBAL_FIELD( mins, FIELD_VECTOR ), + DEFINE_ENTITY_GLOBAL_FIELD( maxs, FIELD_VECTOR ), + DEFINE_ENTITY_GLOBAL_FIELD( size, FIELD_VECTOR ), + + DEFINE_ENTITY_FIELD( ltime, FIELD_TIME ), + DEFINE_ENTITY_FIELD( nextthink, FIELD_TIME ), + + DEFINE_ENTITY_FIELD( solid, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( movetype, FIELD_INTEGER ), + + DEFINE_ENTITY_FIELD( skin, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( body, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( effects, FIELD_INTEGER ), + + DEFINE_ENTITY_FIELD( gravity, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( friction, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( light_level, FIELD_FLOAT ), + + DEFINE_ENTITY_FIELD( frame, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( scale, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( sequence, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( animtime, FIELD_TIME ), + DEFINE_ENTITY_FIELD( framerate, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( controller, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( blending, FIELD_INTEGER ), + + DEFINE_ENTITY_FIELD( rendermode, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( renderamt, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( rendercolor, FIELD_VECTOR ), + DEFINE_ENTITY_FIELD( renderfx, FIELD_INTEGER ), + + DEFINE_ENTITY_FIELD( health, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( frags, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( weapons, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( takedamage, FIELD_FLOAT ), + + DEFINE_ENTITY_FIELD( deadflag, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( view_ofs, FIELD_VECTOR ), + DEFINE_ENTITY_FIELD( button, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( impulse, FIELD_INTEGER ), + + DEFINE_ENTITY_FIELD( chain, FIELD_EDICT ), + DEFINE_ENTITY_FIELD( dmg_inflictor, FIELD_EDICT ), + DEFINE_ENTITY_FIELD( enemy, FIELD_EDICT ), + DEFINE_ENTITY_FIELD( aiment, FIELD_EDICT ), + DEFINE_ENTITY_FIELD( owner, FIELD_EDICT ), + DEFINE_ENTITY_FIELD( groundentity, FIELD_EDICT ), + + DEFINE_ENTITY_FIELD( spawnflags, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( flags, FIELD_FLOAT ), + + DEFINE_ENTITY_FIELD( colormap, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( team, FIELD_INTEGER ), + + DEFINE_ENTITY_FIELD( max_health, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( teleport_time, FIELD_TIME ), + DEFINE_ENTITY_FIELD( armortype, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( armorvalue, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( waterlevel, FIELD_INTEGER ), + DEFINE_ENTITY_FIELD( watertype, FIELD_INTEGER ), + + // Having these fields be local to the individual levels makes it easier to test those levels individually. + DEFINE_ENTITY_GLOBAL_FIELD( target, FIELD_STRING ), + DEFINE_ENTITY_GLOBAL_FIELD( targetname, FIELD_STRING ), + DEFINE_ENTITY_FIELD( netname, FIELD_STRING ), + DEFINE_ENTITY_FIELD( message, FIELD_STRING ), + + DEFINE_ENTITY_FIELD( dmg_take, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( dmg_save, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( dmg, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( dmgtime, FIELD_TIME ), + + DEFINE_ENTITY_FIELD( noise, FIELD_SOUNDNAME ), + DEFINE_ENTITY_FIELD( noise1, FIELD_SOUNDNAME ), + DEFINE_ENTITY_FIELD( noise2, FIELD_SOUNDNAME ), + DEFINE_ENTITY_FIELD( noise3, FIELD_SOUNDNAME ), + DEFINE_ENTITY_FIELD( speed, FIELD_FLOAT ), + DEFINE_ENTITY_FIELD( air_finished, FIELD_TIME ), + DEFINE_ENTITY_FIELD( pain_finished, FIELD_TIME ), + DEFINE_ENTITY_FIELD( radsuit_finished, FIELD_TIME ), +}; + +#define ENTVARS_COUNT (sizeof(gEntvarsDescription)/sizeof(gEntvarsDescription[0])) + + #ifdef DEBUG edict_t *DBG_EntOfVars( const entvars_t *pev ) { @@ -1512,6 +1626,55 @@ void UTIL_StripToken( const char *pKey, char *pDest ) } +void EntvarsKeyvalue( entvars_t *pev, KeyValueData *pkvd ) +{ + int i; + TYPEDESCRIPTION *pField; + + for ( i = 0; i < ENTVARS_COUNT; i++ ) + { + pField = &gEntvarsDescription[i]; + + if ( !stricmp( pField->fieldName, pkvd->szKeyName ) ) + { + switch( pField->fieldType ) + { + case FIELD_MODELNAME: + case FIELD_SOUNDNAME: + case FIELD_STRING: + (*(int *)((char *)pev + pField->fieldOffset)) = ALLOC_STRING( pkvd->szValue ); + break; + + case FIELD_TIME: + case FIELD_FLOAT: + (*(float *)((char *)pev + pField->fieldOffset)) = atof( pkvd->szValue ); + break; + + case FIELD_INTEGER: + (*(int *)((char *)pev + pField->fieldOffset)) = atoi( pkvd->szValue ); + break; + + case FIELD_POSITION_VECTOR: + case FIELD_VECTOR: + UTIL_StringToVector( (float *)((char *)pev + pField->fieldOffset), pkvd->szValue ); + break; + + default: + case FIELD_EVARS: + case FIELD_CLASSPTR: + case FIELD_EDICT: + case FIELD_ENTITY: + case FIELD_POINTER: + ALERT( at_error, "Bad field in entity!!\n" ); + break; + } + pkvd->fHandled = TRUE; + return; + } + } +} + + Vector VecBModelOrigin( entvars_t* pevBModel ) { return pevBModel->absmin + ( pevBModel->size * 0.5 );