mirror of
https://github.com/alliedmodders/amxmodx.git
synced 2024-12-27 07:15:37 +03:00
276 lines
6.2 KiB
C++
276 lines
6.2 KiB
C++
|
#include <string.h>
|
||
|
|
||
|
#include "../sdk/amxxmodule.h"
|
||
|
|
||
|
#include "../ns.h"
|
||
|
#include "../ns_const.h"
|
||
|
|
||
|
#include "../utilfunctions.h"
|
||
|
|
||
|
#include "../FastDelegate.h"
|
||
|
#include "../GameManager.h"
|
||
|
|
||
|
extern int IsValidBuilding[AVH_USER3_MAX + 1];
|
||
|
|
||
|
using namespace fastdelegate::detail;
|
||
|
|
||
|
|
||
|
void *GameRules=NULL;
|
||
|
|
||
|
|
||
|
mBOOL dlclose_handle_invalid; // Linking errors with metamod
|
||
|
|
||
|
// void AvHBaseBuildable::StartRecycle()
|
||
|
static void (GenericClass::*MFP_Recycle)();
|
||
|
|
||
|
// void AvHWeldable::AddBuildTime(float)
|
||
|
static void (GenericClass::*MFP_WeldFinished)(float);
|
||
|
|
||
|
// AvHGameRules *GetGameRules(void)
|
||
|
static void *(*FP_GetGameRules)();
|
||
|
|
||
|
|
||
|
char *FuncBase;
|
||
|
|
||
|
/**
|
||
|
* sizeof(void (detail::GenericClass::*fptr)())
|
||
|
* is 8 in GCC. Add an empty void * pointer at
|
||
|
* the end to compensate.
|
||
|
* Layout in GCC:
|
||
|
* union {
|
||
|
* void *address; // When this is an address it will always be positive
|
||
|
* int vtable_index; // When it is a vtable index it will always be odd = (vindex*2)+1
|
||
|
* };
|
||
|
* int delta;
|
||
|
* -
|
||
|
* Delta is the adjustment to the this pointer
|
||
|
* For my implementations I will only need it to 0
|
||
|
*/
|
||
|
#ifdef __GNUC__
|
||
|
template <typename OutType>
|
||
|
inline void set_mfp(OutType &out, void *in)
|
||
|
{
|
||
|
union
|
||
|
{
|
||
|
void *in[2];
|
||
|
OutType out;
|
||
|
} mfpu;
|
||
|
|
||
|
mfpu.in[0]=in;
|
||
|
mfpu.in[1]=NULL;
|
||
|
out=mfpu.out;
|
||
|
};
|
||
|
#else
|
||
|
template <typename OutType>
|
||
|
inline void set_mfp(OutType &out, void *in)
|
||
|
{
|
||
|
out=horrible_cast<OutType>(in);
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
void MFuncs_Initialize(void)
|
||
|
{
|
||
|
char FileName[256];
|
||
|
DLHANDLE DLLBase;
|
||
|
#ifdef __linux__
|
||
|
snprintf(FileName,sizeof(FileName)-1,"%s/dlls/ns_i386.so",MF_GetModname());
|
||
|
#else
|
||
|
snprintf(FileName,sizeof(FileName)-1,"%s\\dlls\\ns.dll",MF_GetModname());
|
||
|
#endif
|
||
|
|
||
|
DLLBase=DLOPEN(FileName);
|
||
|
FuncBase=(char *)DLSYM(DLLBase, MAKE_OFFSET(BASE));
|
||
|
DLCLOSE(DLLBase);
|
||
|
|
||
|
#define MFP(Offs) (((void *)(((char *)FuncBase)+MAKE_OFFSET(Offs))))
|
||
|
|
||
|
set_mfp(MFP_Recycle,MFP(MEMBER_RECYCLE));
|
||
|
|
||
|
set_mfp(MFP_WeldFinished,MFP(MEMBER_TRIGGER_WELDABLE));
|
||
|
|
||
|
// This is not a member function pointer, but use MFP since it
|
||
|
// uses the same address conversion as MFPs do
|
||
|
FP_GetGameRules=horrible_cast<void *(*)()>(MFP(GETGAMERULES));
|
||
|
};
|
||
|
|
||
|
static cell AMX_NATIVE_CALL ns_recycle(AMX *amx, cell *params)
|
||
|
{
|
||
|
CreateNonPlayerEdict(amx,params[1]);
|
||
|
|
||
|
if (Entity->free || Entity->pvPrivateData==NULL)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (Entity->v.iuser3 <= AVH_USER3_NONE || Entity->v.iuser3 >= AVH_USER3_MAX)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
if (IsValidBuilding[Entity->v.iuser3]!=1) // Not a marine structure?
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Make sure it's a marine building, undefined stuff happens on alien structures
|
||
|
(reinterpret_cast<GenericClass *>(Entity->pvPrivateData)->*(MFP_Recycle))();
|
||
|
|
||
|
|
||
|
return 1;
|
||
|
};
|
||
|
static cell AMX_NATIVE_CALL ns_finish_weldable(AMX *amx, cell *params)
|
||
|
{
|
||
|
CreateNonPlayerEdict(amx,params[1]);
|
||
|
|
||
|
if (Entity->free || Entity->pvPrivateData==NULL)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
// verify the classname since this will crash if it's the wrong class!
|
||
|
if (strcmp(STRING(Entity->v.classname),"avhweldable")!=0)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// First need to set the weldable to 100% complete
|
||
|
set_private_f(Entity,MAKE_OFFSET(WELD_DONE),get_private_f(Entity,MAKE_OFFSET(WELD_TIME)));
|
||
|
|
||
|
// Now make NS think the weldable has been welded again
|
||
|
// This has to call AvHWeldable::AddBuildTime(float)
|
||
|
// because AvHWeldable::TriggerFinished() does not work properly
|
||
|
(reinterpret_cast<GenericClass *>(Entity->pvPrivateData)->*(MFP_WeldFinished))(100.0);
|
||
|
|
||
|
|
||
|
return 1;
|
||
|
};
|
||
|
|
||
|
static cell AMX_NATIVE_CALL ns_get_teamres(AMX *amx, cell *params)
|
||
|
{
|
||
|
if (GameMan.IsCombat())
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
if (GameRules==NULL) // GameRules not initialized yet
|
||
|
{
|
||
|
GameRules=(*(FP_GetGameRules))();
|
||
|
}
|
||
|
if (GameRules==NULL) // Still null? Get out of here
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
switch(params[1])
|
||
|
{
|
||
|
case 1:
|
||
|
{
|
||
|
return amx_ftoc2(*(REAL *)((char *)GameRules+GAMERULES_TEAMA_RESOURCES));
|
||
|
}
|
||
|
case 2:
|
||
|
{
|
||
|
return amx_ftoc2(*(REAL *)((char *)GameRules+GAMERULES_TEAMB_RESOURCES));
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
MF_LogError(amx, AMX_ERR_NATIVE, "ns_get_teamres: Expected 1 for team a or 2 for team b, got %d", params[1]);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
static cell AMX_NATIVE_CALL ns_set_teamres(AMX *amx, cell *params)
|
||
|
{
|
||
|
if (GameMan.IsCombat())
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
if (GameRules==NULL) // GameRules not initialized yet
|
||
|
{
|
||
|
GameRules=(*(FP_GetGameRules))();
|
||
|
}
|
||
|
if (GameRules==NULL) // Still null? Get out of here
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
switch(params[1])
|
||
|
{
|
||
|
case 1:
|
||
|
{
|
||
|
*(REAL *)((char *)GameRules+GAMERULES_TEAMA_RESOURCES)=amx_ctof2(params[2]);
|
||
|
return 1;
|
||
|
}
|
||
|
case 2:
|
||
|
{
|
||
|
*(REAL *)((char *)GameRules+GAMERULES_TEAMB_RESOURCES)=amx_ctof2(params[2]);
|
||
|
return 1;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
MF_LogError(amx, AMX_ERR_NATIVE, "ns_set_teamres: Expected 1 for team a or 2 for team b, got %d", params[1]);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
static cell AMX_NATIVE_CALL ns_add_teamres(AMX *amx, cell *params)
|
||
|
{
|
||
|
if (GameMan.IsCombat())
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
if (GameRules==NULL) // GameRules not initialized yet
|
||
|
{
|
||
|
GameRules=(*(FP_GetGameRules))();
|
||
|
}
|
||
|
if (GameRules==NULL) // Still null? Get out of here
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
switch(params[1])
|
||
|
{
|
||
|
case 1:
|
||
|
{
|
||
|
return amx_ftoc2(*(REAL *)((char *)GameRules+GAMERULES_TEAMA_RESOURCES)+=amx_ctof2(params[2]));
|
||
|
}
|
||
|
case 2:
|
||
|
{
|
||
|
return amx_ftoc2(*(REAL *)((char *)GameRules+GAMERULES_TEAMB_RESOURCES)+=amx_ctof2(params[2]));
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
MF_LogError(amx, AMX_ERR_NATIVE, "ns_add_teamres: Expected 1 for team a or 2 for team b, got %d", params[1]);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef DEVELOPER_BUILD
|
||
|
static cell AMX_NATIVE_CALL findgameinfo(AMX *amx, cell *params)
|
||
|
{
|
||
|
void *Ret=(*(FP_GetGameRules))();
|
||
|
union
|
||
|
{
|
||
|
void *v;
|
||
|
int i;
|
||
|
}vi;
|
||
|
vi.v=Ret;
|
||
|
|
||
|
printf("GameRules=%d\n",vi.i);
|
||
|
return 1;
|
||
|
};
|
||
|
#endif
|
||
|
AMX_NATIVE_INFO memberfunc_natives[] = {
|
||
|
#ifdef DEVELOPER_BUILD
|
||
|
{ "getgameinfo", findgameinfo },
|
||
|
#endif
|
||
|
{ "ns_recycle", ns_recycle },
|
||
|
{ "ns_finish_weldable", ns_finish_weldable },
|
||
|
{ "ns_get_teamres", ns_get_teamres },
|
||
|
{ "ns_set_teamres", ns_set_teamres },
|
||
|
{ "ns_add_teamres", ns_add_teamres },
|
||
|
{ NULL, NULL }
|
||
|
};
|
||
|
|
||
|
void AddNatives_MemberFunc()
|
||
|
{
|
||
|
MF_AddNatives(memberfunc_natives);
|
||
|
};
|