Created
March 6, 2018 19:01
-
-
Save rlabrecque/11eb4a7e9c5c50ad39957857e56dbba6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*======================================== | |
** Multiplayer Bunnyhops: Source by DaFox & petsku | |
** Compatible with: Counter-Strike Source | |
** Thanks to Ian (Juan) Cammarata & #sourcemod | |
**======================================*/ | |
#define VERSION "1.0.0.4" | |
#define MAX_BHOPBLOCKS 1024 //max. number of door/button based bhop blocks handled in a map | |
#define BLOCK_TELEPORT 0.15 //how long can players stand on the block before getting teleported | |
#define BLOCK_COOLDOWN 1.0 //when can they touch the same block again without getting teleported | |
#define COLOR_DOOR { 0,200,0,255 } //rgba value to color door blocks if cvar is enabled | |
#define COLOR_BUTTON { 200,0,200,255 } //rgba value to color button blocks if cvar is enabled | |
#include <sourcemod> | |
#include <sdktools> | |
#include <sdkhooks> | |
public Plugin:myinfo = { | |
name = "Multiplayer Bunnyhops: Source", | |
author = "DaFox & petsku", | |
description = "Allows players to jump on bhop maps, without the blocks being triggered but push the player off if they fail to bhop", | |
version = VERSION, | |
url = "http://www.google.com/" | |
} | |
//============================================================================================= | |
//============================================================================================= | |
#define SF_BUTTON_DONTMOVE (1<<0) //dont move when fired | |
#define SF_BUTTON_TOUCH_ACTIVATES (1<<8) //button fires when touched | |
#define SF_DOOR_PTOUCH (1<<10) //player touch opens | |
new bool:g_bLateLoaded = false | |
new bool:g_bMapEnding = false | |
new g_iBhopDoorList[MAX_BHOPBLOCKS] | |
new g_iBhopDoorTeleList[MAX_BHOPBLOCKS] | |
new g_iBhopDoorCount | |
new g_iBhopButtonList[MAX_BHOPBLOCKS] | |
new g_iBhopButtonTeleList[MAX_BHOPBLOCKS] | |
new g_iBhopButtonCount | |
new g_iOffs_clrRender = -1 | |
new g_iOffs_vecOrigin = -1 | |
new g_iOffs_vecMins = -1 | |
new g_iOffs_vecMaxs = -1 | |
new g_iDoorOffs_vecPosition1 = -1 | |
new g_iDoorOffs_vecPosition2 = -1 | |
new g_iDoorOffs_flSpeed = -1 | |
new g_iDoorOffs_spawnflags = -1 | |
new g_iDoorOffs_NoiseMoving = -1 | |
new g_iDoorOffs_sLockedSound = -1 | |
new g_iDoorOffs_bLocked = -1 | |
new g_iButtonOffs_vecPosition1 = -1 | |
new g_iButtonOffs_vecPosition2 = -1 | |
new g_iButtonOffs_flSpeed = -1 | |
new g_iButtonOffs_spawnflags = -1 | |
new Handle:g_hCvar_Enable = INVALID_HANDLE | |
new Handle:g_hCvar_Color = INVALID_HANDLE | |
new Handle:g_hSDK_Touch = INVALID_HANDLE | |
//============================================================================================= | |
//============================================================================================= | |
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) { | |
g_bLateLoaded = late | |
return APLRes_Success | |
} | |
public OnPluginStart() { | |
new Handle:hGameConf = LoadGameConfigFile("sdkhooks.games") | |
if(hGameConf == INVALID_HANDLE) { | |
SetFailState("GameConfigFile sdkhooks.games was not found") | |
return | |
} | |
StartPrepSDKCall(SDKCall_Entity) | |
PrepSDKCall_SetFromConf(hGameConf,SDKConf_Virtual,"Touch") | |
PrepSDKCall_AddParameter(SDKType_CBaseEntity,SDKPass_Pointer) | |
g_hSDK_Touch = EndPrepSDKCall() | |
CloseHandle(hGameConf) | |
if(g_hSDK_Touch == INVALID_HANDLE) { | |
SetFailState("Unable to prepare virtual function CBaseEntity::Touch") | |
return | |
} | |
CreateConVar("mpbhops_version",VERSION,"Multiplayer Bunnyhops: Source",FCVAR_PLUGIN|FCVAR_NOTIFY) | |
g_hCvar_Enable = CreateConVar("mpbhops_enable","1","Enable/disable Multiplayer Bunnyhops: Source",FCVAR_PLUGIN|FCVAR_NOTIFY) | |
g_hCvar_Color = CreateConVar("mpbhops_color","0","If enabled, marks hooked bhop blocks with colors",FCVAR_PLUGIN|FCVAR_NOTIFY) | |
HookConVarChange(g_hCvar_Enable,ConVarChanged_Enable) | |
HookConVarChange(g_hCvar_Color,ConVarChanged_Color) | |
HookEvent("round_start",Event_RoundStart,EventHookMode_PostNoCopy) | |
g_iOffs_clrRender = FindSendPropInfo("CBaseEntity","m_clrRender") | |
g_iOffs_vecOrigin = FindSendPropInfo("CBaseEntity","m_vecOrigin") | |
g_iOffs_vecMins = FindSendPropInfo("CBaseEntity","m_vecMins") | |
g_iOffs_vecMaxs = FindSendPropInfo("CBaseEntity","m_vecMaxs") | |
if(g_bLateLoaded) { | |
OnPluginPauseChange(false) | |
} | |
} | |
public OnMapEnd() { //DF 2012 Jan 20 - I don't know what the fuck is going on here, but I'm too afraid to touch it. It's likely just cruft that should be removed. | |
g_bMapEnding = true | |
SetConVarBool(g_hCvar_Enable,GetConVarBool(g_hCvar_Enable)) //DF 2012 Jan 20 - This used to always set it to true, Causing the plugin to turn on | |
g_bMapEnding = false // automatically at map change, even if it was disabled. | |
} | |
public OnPluginPauseChange(bool:pause) { | |
if(pause) { | |
OnPluginEnd() | |
} | |
else if(GetConVarBool(g_hCvar_Enable)) { | |
g_iBhopDoorCount = 0 | |
g_iBhopButtonCount = 0 | |
FindBhopBlocks() | |
if(!g_iBhopDoorCount && !g_iBhopButtonCount) { | |
SetConVarBool(g_hCvar_Enable,false) | |
} | |
} | |
} | |
public OnPluginEnd() { | |
AlterBhopBlocks(true) | |
g_iBhopDoorCount = 0 | |
g_iBhopButtonCount = 0 | |
} | |
//============================================================================================= | |
//============================================================================================= | |
public Event_RoundStart(Handle:event,const String:name[],bool:dontBroadcast) { | |
OnPluginPauseChange(false) | |
} | |
public Entity_Touch(bhop,client) { | |
if(0 < client <= MaxClients) { | |
static Float:flPunishTime[MAXPLAYERS + 1], iLastBlock[MAXPLAYERS + 1] = { -1,... } | |
new Float:time = GetEngineTime(), Float:diff = time - flPunishTime[client] | |
if(iLastBlock[client] != bhop || diff > BLOCK_COOLDOWN) { | |
iLastBlock[client] = bhop | |
flPunishTime[client] = time + BLOCK_TELEPORT | |
} | |
else if(diff > BLOCK_TELEPORT) { | |
decl i | |
new tele = -1, ent = iLastBlock[client] | |
iLastBlock[client] = -1 | |
for(i = 0; i < g_iBhopDoorCount; i++) { | |
if(ent == g_iBhopDoorList[i]) { | |
tele = g_iBhopDoorTeleList[i] | |
break | |
} | |
} | |
if(tele == -1) { | |
for(i = 0; i < g_iBhopButtonCount; i++) { | |
if(ent == g_iBhopButtonList[i]) { | |
tele = g_iBhopButtonTeleList[i] | |
break | |
} | |
} | |
} | |
if(tele != -1 && IsValidEntity(tele)) { | |
SDKCall(g_hSDK_Touch,tele,client) | |
} | |
} | |
} | |
} | |
public ConVarChanged_Enable(Handle:convar,const String:oldValue[],const String:newValue[]) { | |
if(g_bMapEnding) { | |
return | |
} | |
new bool:bEnabled = StringToInt(newValue) ? true : false | |
if(bEnabled != (StringToInt(oldValue) ? true : false)) { | |
OnPluginPauseChange(!bEnabled) | |
} | |
} | |
public ConVarChanged_Color(Handle:convar,const String:oldValue[],const String:newValue[]) { | |
if(!GetConVarBool(g_hCvar_Enable)) { | |
return | |
} | |
new bool:bEnabled = StringToInt(newValue) ? true : false | |
if(bEnabled != (StringToInt(oldValue) ? true : false)) { | |
ColorBlocks(!bEnabled) | |
} | |
} | |
//============================================================================================= | |
//============================================================================================= | |
FindBhopBlocks() { | |
decl Float:startpos[3], Float:endpos[3], Float:mins[3], Float:maxs[3], tele | |
new ent = -1 | |
while((ent = FindEntityByClassname(ent,"func_door")) != -1) { | |
if(g_iDoorOffs_vecPosition1 == -1) { | |
g_iDoorOffs_vecPosition1 = FindDataMapOffs(ent,"m_vecPosition1") | |
g_iDoorOffs_vecPosition2 = FindDataMapOffs(ent,"m_vecPosition2") | |
g_iDoorOffs_flSpeed = FindDataMapOffs(ent,"m_flSpeed") | |
g_iDoorOffs_spawnflags = FindDataMapOffs(ent,"m_spawnflags") | |
g_iDoorOffs_NoiseMoving = FindDataMapOffs(ent,"m_NoiseMoving") | |
g_iDoorOffs_sLockedSound = FindDataMapOffs(ent,"m_ls.sLockedSound") | |
g_iDoorOffs_bLocked = FindDataMapOffs(ent,"m_bLocked") | |
} | |
GetEntDataVector(ent,g_iDoorOffs_vecPosition1,startpos) | |
GetEntDataVector(ent,g_iDoorOffs_vecPosition2,endpos) | |
if(startpos[2] > endpos[2]) { | |
GetEntDataVector(ent,g_iOffs_vecMins,mins) | |
GetEntDataVector(ent,g_iOffs_vecMaxs,maxs) | |
startpos[0] += (mins[0] + maxs[0]) * 0.5 | |
startpos[1] += (mins[1] + maxs[1]) * 0.5 | |
startpos[2] += maxs[2] | |
if((tele = CustomTraceForTeleports(startpos,endpos[2] + maxs[2])) != -1) { | |
g_iBhopDoorList[g_iBhopDoorCount] = ent | |
g_iBhopDoorTeleList[g_iBhopDoorCount] = tele | |
if(++g_iBhopDoorCount == sizeof g_iBhopDoorList) { | |
break | |
} | |
} | |
} | |
} | |
ent = -1 | |
while((ent = FindEntityByClassname(ent,"func_button")) != -1) { | |
if(g_iButtonOffs_vecPosition1 == -1) { | |
g_iButtonOffs_vecPosition1 = FindDataMapOffs(ent,"m_vecPosition1") | |
g_iButtonOffs_vecPosition2 = FindDataMapOffs(ent,"m_vecPosition2") | |
g_iButtonOffs_flSpeed = FindDataMapOffs(ent,"m_flSpeed") | |
g_iButtonOffs_spawnflags = FindDataMapOffs(ent,"m_spawnflags") | |
} | |
GetEntDataVector(ent,g_iButtonOffs_vecPosition1,startpos) | |
GetEntDataVector(ent,g_iButtonOffs_vecPosition2,endpos) | |
if(startpos[2] > endpos[2] && (GetEntData(ent,g_iButtonOffs_spawnflags,4) & SF_BUTTON_TOUCH_ACTIVATES)) { | |
GetEntDataVector(ent,g_iOffs_vecMins,mins) | |
GetEntDataVector(ent,g_iOffs_vecMaxs,maxs) | |
startpos[0] += (mins[0] + maxs[0]) * 0.5 | |
startpos[1] += (mins[1] + maxs[1]) * 0.5 | |
startpos[2] += maxs[2] | |
if((tele = CustomTraceForTeleports(startpos,endpos[2] + maxs[2])) != -1) { | |
g_iBhopButtonList[g_iBhopButtonCount] = ent | |
g_iBhopButtonTeleList[g_iBhopButtonCount] = tele | |
if(++g_iBhopButtonCount == sizeof g_iBhopButtonList) { | |
break | |
} | |
} | |
} | |
} | |
AlterBhopBlocks(false) | |
} | |
AlterBhopBlocks(bool:bRevertChanges) { | |
static Float:vecDoorPosition2[sizeof g_iBhopDoorList][3] | |
static Float:flDoorSpeed[sizeof g_iBhopDoorList] | |
static iDoorSpawnflags[sizeof g_iBhopDoorList] | |
static bool:bDoorLocked[sizeof g_iBhopDoorList] | |
static Float:vecButtonPosition2[sizeof g_iBhopButtonList][3] | |
static Float:flButtonSpeed[sizeof g_iBhopButtonList] | |
static iButtonSpawnflags[sizeof g_iBhopButtonList] | |
decl ent, i | |
if(bRevertChanges) { | |
for(i = 0; i < g_iBhopDoorCount; i++) { | |
ent = g_iBhopDoorList[i] | |
if(IsValidEntity(ent)) { | |
SetEntDataVector(ent,g_iDoorOffs_vecPosition2,vecDoorPosition2[i]) | |
SetEntDataFloat(ent,g_iDoorOffs_flSpeed,flDoorSpeed[i]) | |
SetEntData(ent,g_iDoorOffs_spawnflags,iDoorSpawnflags[i],4) | |
if(!bDoorLocked[i]) { | |
AcceptEntityInput(ent,"Unlock") | |
} | |
SDKUnhook(ent,SDKHook_Touch,Entity_Touch) | |
} | |
} | |
for(i = 0; i < g_iBhopButtonCount; i++) { | |
ent = g_iBhopButtonList[i] | |
if(IsValidEntity(ent)) { | |
SetEntDataVector(ent,g_iButtonOffs_vecPosition2,vecButtonPosition2[i]) | |
SetEntDataFloat(ent,g_iButtonOffs_flSpeed,flButtonSpeed[i]) | |
SetEntData(ent,g_iButtonOffs_spawnflags,iButtonSpawnflags[i],4) | |
SDKUnhook(ent,SDKHook_Touch,Entity_Touch) | |
} | |
} | |
} | |
else { //note: This only gets called directly after finding the blocks, so the entities are valid. | |
decl Float:startpos[3] | |
for(i = 0; i < g_iBhopDoorCount; i++) { | |
ent = g_iBhopDoorList[i] | |
GetEntDataVector(ent,g_iDoorOffs_vecPosition2,vecDoorPosition2[i]) | |
flDoorSpeed[i] = GetEntDataFloat(ent,g_iDoorOffs_flSpeed) | |
iDoorSpawnflags[i] = GetEntData(ent,g_iDoorOffs_spawnflags,4) | |
bDoorLocked[i] = GetEntData(ent,g_iDoorOffs_bLocked,1) ? true : false | |
GetEntDataVector(ent,g_iDoorOffs_vecPosition1,startpos) | |
SetEntDataVector(ent,g_iDoorOffs_vecPosition2,startpos) | |
SetEntDataFloat(ent,g_iDoorOffs_flSpeed,0.0) | |
SetEntData(ent,g_iDoorOffs_spawnflags,SF_DOOR_PTOUCH,4) | |
AcceptEntityInput(ent,"Lock") | |
SetEntData(ent,g_iDoorOffs_sLockedSound,GetEntData(ent,g_iDoorOffs_NoiseMoving,4),4) | |
SDKHook(ent,SDKHook_Touch,Entity_Touch) | |
} | |
for(i = 0; i < g_iBhopButtonCount; i++) { | |
ent = g_iBhopButtonList[i] | |
GetEntDataVector(ent,g_iButtonOffs_vecPosition2,vecButtonPosition2[i]) | |
flButtonSpeed[i] = GetEntDataFloat(ent,g_iButtonOffs_flSpeed) | |
iButtonSpawnflags[i] = GetEntData(ent,g_iButtonOffs_spawnflags,4) | |
GetEntDataVector(ent,g_iButtonOffs_vecPosition1,startpos) | |
SetEntDataVector(ent,g_iButtonOffs_vecPosition2,startpos) | |
SetEntDataFloat(ent,g_iButtonOffs_flSpeed,0.0) | |
SetEntData(ent,g_iButtonOffs_spawnflags,SF_BUTTON_DONTMOVE|SF_BUTTON_TOUCH_ACTIVATES,4) | |
SDKHook(ent,SDKHook_Touch,Entity_Touch) | |
} | |
} | |
if(GetConVarBool(g_hCvar_Color)) { | |
ColorBlocks(bRevertChanges) | |
} | |
} | |
ColorBlocks(bool:bRevertChanges) { | |
static iDoorClrRender[sizeof g_iBhopDoorList][4] | |
static iButtonClrRender[sizeof g_iBhopButtonList][4] | |
decl ent, i | |
if(bRevertChanges) { | |
for(i = 0; i < g_iBhopDoorCount; i++) { | |
ent = g_iBhopDoorList[i] | |
if(IsValidEntity(ent)) { | |
SetEntDataArray(ent,g_iOffs_clrRender,iDoorClrRender[i],sizeof iDoorClrRender[],1,true) | |
} | |
} | |
for(i = 0; i < g_iBhopButtonCount; i++) { | |
ent = g_iBhopButtonList[i] | |
if(IsValidEntity(ent)) { | |
SetEntDataArray(ent,g_iOffs_clrRender,iButtonClrRender[i],sizeof iButtonClrRender[],1,true) | |
} | |
} | |
} | |
else { | |
for(i = 0; i < g_iBhopDoorCount; i++) { | |
ent = g_iBhopDoorList[i] | |
if(IsValidEntity(ent)) { | |
GetEntDataArray(ent,g_iOffs_clrRender,iDoorClrRender[i],sizeof iDoorClrRender[],1) | |
SetEntDataArray(ent,g_iOffs_clrRender,COLOR_DOOR,4,1,true) | |
} | |
} | |
for(i = 0; i < g_iBhopButtonCount; i++) { | |
ent = g_iBhopButtonList[i] | |
if(IsValidEntity(ent)) { | |
GetEntDataArray(ent,g_iOffs_clrRender,iButtonClrRender[i],sizeof iButtonClrRender[],1) | |
SetEntDataArray(ent,g_iOffs_clrRender,COLOR_BUTTON,4,1,true) | |
} | |
} | |
} | |
} | |
CustomTraceForTeleports(const Float:startpos[3],Float:endheight,Float:step=1.0) { | |
decl teleports[512] | |
new tpcount, ent = -1 | |
while((ent = FindEntityByClassname(ent,"trigger_teleport")) != -1 && tpcount != sizeof teleports) { | |
teleports[tpcount++] = ent | |
} | |
decl Float:mins[3], Float:maxs[3], Float:origin[3], i | |
origin[0] = startpos[0] | |
origin[1] = startpos[1] | |
origin[2] = startpos[2] | |
do { | |
for(i = 0; i < tpcount; i++) { | |
ent = teleports[i] | |
GetAbsBoundingBox(ent,mins,maxs) | |
if(mins[0] <= origin[0] <= maxs[0] && mins[1] <= origin[1] <= maxs[1] && mins[2] <= origin[2] <= maxs[2]) { | |
return ent | |
} | |
} | |
origin[2] -= step | |
} while(origin[2] >= endheight) | |
return -1 | |
} | |
GetAbsBoundingBox(ent,Float:mins[3],Float:maxs[3]) { | |
decl Float:origin[3] | |
GetEntDataVector(ent,g_iOffs_vecOrigin,origin) | |
GetEntDataVector(ent,g_iOffs_vecMins,mins) | |
GetEntDataVector(ent,g_iOffs_vecMaxs,maxs) | |
mins[0] += origin[0] | |
mins[1] += origin[1] | |
mins[2] += origin[2] | |
maxs[0] += origin[0] | |
maxs[1] += origin[1] | |
maxs[2] += origin[2] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment