From 28d76b41e5c97ed848159fdf07f64e676edac5ab Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Tue, 1 Mar 2016 02:19:19 +0100 Subject: [PATCH] Add natives to force a camera shot Override the autodirector and tell him what to show. Fixpoint camera or chasing a player for now. --- extension.cpp | 3 ++ extension.h | 6 ++- forwards.h | 58 +++++++++++----------- hltvdirectorwrapper.cpp | 40 +++++++++++++++ hltvdirectorwrapper.h | 46 ++++++++++++++++++ natives.cpp | 100 ++++++++++++++++++++++++++++++++++++-- smsdk_config.h | 2 +- sourcetv_test.sp | 58 +++++++++++++++++++++- sourcetvmanager.games.txt | 40 ++++++++++++++- sourcetvmanager.inc | 13 ++++- 10 files changed, 325 insertions(+), 41 deletions(-) create mode 100644 hltvdirectorwrapper.cpp create mode 100644 hltvdirectorwrapper.h diff --git a/extension.cpp b/extension.cpp index 50f1031..0ba2daa 100644 --- a/extension.cpp +++ b/extension.cpp @@ -40,6 +40,7 @@ void *old_host_client = nullptr; bool g_HostClientOverridden = false; IGameEventManager2 *gameevents = nullptr; +CGlobalVars *gpGlobals; IBinTools *bintools = nullptr; ISDKTools *sdktools = nullptr; @@ -135,6 +136,8 @@ bool SourceTVManager::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxle GET_V_IFACE_CURRENT(GetServerFactory, hltvdirector, IHLTVDirector, INTERFACEVERSION_HLTVDIRECTOR); GET_V_IFACE_CURRENT(GetEngineFactory, gameevents, IGameEventManager2, INTERFACEVERSION_GAMEEVENTSMANAGER2); + gpGlobals = ismm->GetCGlobals(); + return true; } diff --git a/extension.h b/extension.h index 58d2487..e7521f5 100644 --- a/extension.h +++ b/extension.h @@ -1,8 +1,8 @@ /** * vim: set ts=4 : * ============================================================================= - * SourceMod Sample Extension - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * SourceMod SourceTV Manager Extension + * Copyright (C) 2004-2016 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -40,6 +40,7 @@ #include "smsdk_ext.h" #include #include +#include "hltvdirectorwrapper.h" #include "ihltvdirector.h" #include "ihltv.h" #include "iserver.h" @@ -148,6 +149,7 @@ extern ISDKTools *sdktools; extern IGameConfig *g_pGameConf; extern IServer *iserver; +extern CGlobalVars *gpGlobals; extern IGameEventManager2 *gameevents; extern IHLTVDirector *hltvdirector; diff --git a/forwards.h b/forwards.h index ba529b4..2214344 100644 --- a/forwards.h +++ b/forwards.h @@ -1,33 +1,33 @@ /** -* vim: set ts=4 : -* ============================================================================= -* SourceMod Sample Extension -* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. -* ============================================================================= -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the GNU General Public License, version 3.0, as published by the -* Free Software Foundation. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -* details. -* -* You should have received a copy of the GNU General Public License along with -* this program. If not, see . -* -* As a special exception, AlliedModders LLC gives you permission to link the -* code of this program (as well as its derivative works) to "Half-Life 2," the -* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software -* by the Valve Corporation. You must obey the GNU General Public License in -* all respects for all other code used. Additionally, AlliedModders LLC grants -* this exception to all derivative works. AlliedModders LLC defines further -* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), -* or . -* -* Version: $Id$ -*/ + * vim: set ts=4 : + * ============================================================================= + * SourceMod SourceTV Manager Extension + * Copyright (C) 2004-2016 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ #ifndef _INCLUDE_SOURCEMOD_EXTENSION_FORWARDS_H_ #define _INCLUDE_SOURCEMOD_EXTENSION_FORWARDS_H_ diff --git a/hltvdirectorwrapper.cpp b/hltvdirectorwrapper.cpp new file mode 100644 index 0000000..7eae26f --- /dev/null +++ b/hltvdirectorwrapper.cpp @@ -0,0 +1,40 @@ +#include "hltvdirectorwrapper.h" + +HLTVDirectorWrapper g_HLTVDirectorWrapper; + +void HLTVDirectorWrapper::SetPVSEntity(int index) +{ + static int offset = -1; + if (offset == -1 && !g_pGameConf->GetOffset("CHLTVDirector::m_iPVSEntity", &offset)) + { + smutils->LogError(myself, "Failed to get CHLTVDirector::m_iPVSEntity offset."); + return; + } + + *(int *)((intptr_t)hltvdirector + offset) = index; +} + +void HLTVDirectorWrapper::SetPVSOrigin(Vector pos) +{ + static int offset = -1; + if (offset == -1 && !g_pGameConf->GetOffset("CHLTVDirector::m_vPVSOrigin", &offset)) + { + smutils->LogError(myself, "Failed to get CHLTVDirector::m_vPVSOrigin offset."); + return; + } + + Vector *m_vPVSOrigin = (Vector *)((intptr_t)hltvdirector + offset); + *m_vPVSOrigin = pos; +} + +void HLTVDirectorWrapper::SetNextThinkTick(int tick) +{ + static int offset = -1; + if (offset == -1 && !g_pGameConf->GetOffset("CHLTVDirector::m_nNextShotTick", &offset)) + { + smutils->LogError(myself, "Failed to get CHLTVDirector::m_nNextShotTick offset."); + return; + } + + *(int *)((intptr_t)hltvdirector + offset) = tick; +} \ No newline at end of file diff --git a/hltvdirectorwrapper.h b/hltvdirectorwrapper.h new file mode 100644 index 0000000..515be98 --- /dev/null +++ b/hltvdirectorwrapper.h @@ -0,0 +1,46 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod SourceTV Manager Extension + * Copyright (C) 2004-2016 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_HLTVDIRECTOR_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_HLTVDIRECTOR_H_ + +#include "extension.h" + +class HLTVDirectorWrapper { +public: + void SetPVSEntity(int index); + void SetPVSOrigin(Vector pos); + void SetNextThinkTick(int tick); +}; + +extern HLTVDirectorWrapper g_HLTVDirectorWrapper; + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_HLTVDIRECTOR_H_ \ No newline at end of file diff --git a/natives.cpp b/natives.cpp index 1298ab9..4645ca2 100644 --- a/natives.cpp +++ b/natives.cpp @@ -31,6 +31,9 @@ #include "extension.h" +#define TICK_INTERVAL (gpGlobals->interval_per_tick) +#define TIME_TO_TICKS( dt ) ( (int)( 0.5f + (float)(dt) / TICK_INTERVAL ) ) + extern const sp_nativeinfo_t sourcetv_natives[]; // native SourceTV_GetHLTVServerCount(); @@ -172,7 +175,7 @@ static cell_t Native_BroadcastConsoleMessage(IPluginContext *pContext, const cel if (!pBroadcastPrintf) { int offset = -1; - if (!g_pGameConf->GetOffset("BroadcastPrintf", &offset) || offset == -1) + if (!g_pGameConf->GetOffset("CBaseServer::BroadcastPrintf", &offset) || offset == -1) { pContext->ReportError("Failed to get CBaseServer::BroadcastPrintf offset."); return 0; @@ -231,8 +234,8 @@ static cell_t Native_GetViewEntity(IPluginContext *pContext, const cell_t *param return hltvdirector->GetPVSEntity(); } -// native SourceTV_GetViewCoordinates(); -static cell_t Native_GetViewCoordinates(IPluginContext *pContext, const cell_t *params) +// native SourceTV_GetViewOrigin(); +static cell_t Native_GetViewOrigin(IPluginContext *pContext, const cell_t *params) { Vector pvs = hltvdirector->GetPVSOrigin(); @@ -244,6 +247,93 @@ static cell_t Native_GetViewCoordinates(IPluginContext *pContext, const cell_t * return 0; } +// native bool:SourceTV_ForceFixedCameraShot(Float:pos[], Float:angle[], iTarget, Float:fov, Float:fDuration); +static cell_t Native_ForceFixedCameraShot(IPluginContext *pContext, const cell_t *params) +{ + if (hltvserver == nullptr) + return 0; + + // Validate entities. + if (params[3] != 0 && !gamehelpers->ReferenceToEntity(params[3])) + { + pContext->ReportError("Invalid target (%d - %d)", gamehelpers->ReferenceToIndex(params[3]), params[3]); + return 0; + } + + cell_t *pos; + pContext->LocalToPhysAddr(params[1], &pos); + + cell_t *angle; + pContext->LocalToPhysAddr(params[2], &angle); + + // Update director state like it would do itself. + g_HLTVDirectorWrapper.SetPVSEntity(0); + Vector vPos(sp_ctof(pos[0]), sp_ctof(pos[1]), sp_ctof(pos[2])); + g_HLTVDirectorWrapper.SetPVSOrigin(vPos); + + IGameEvent *shot = gameevents->CreateEvent("hltv_fixed", true); + if (!shot) + return 0; + + shot->SetInt("posx", vPos.x); + shot->SetInt("posy", vPos.y); + shot->SetInt("posz", vPos.z); + shot->SetInt("theta", sp_ctof(angle[0])); + shot->SetInt("phi", sp_ctof(angle[1])); + shot->SetInt("target", params[3] ? gamehelpers->ReferenceToIndex(params[3]) : 0); + shot->SetFloat("fov", sp_ctof(params[4])); + + hltvserver->BroadcastEvent(shot); + gameevents->FreeEvent(shot); + + // Prevent auto director from changing shots until we allow it to again. + g_HLTVDirectorWrapper.SetNextThinkTick(hltvdirector->GetDirectorTick() + TIME_TO_TICKS(sp_ctof(params[5]))); + + return 1; +} + +// native bool:SourceTV_ForceChaseCameraShot(iTarget1, iTarget2, distance, phi, theta, bool:bInEye, Float:fDuration); +static cell_t Native_ForceChaseCameraShot(IPluginContext *pContext, const cell_t *params) +{ + if (hltvserver == nullptr) + return 0; + + // Validate entities. + if (!gamehelpers->ReferenceToEntity(params[1])) + { + pContext->ReportError("Invalid target1 (%d - %d)", gamehelpers->ReferenceToIndex(params[1]), params[1]); + return 0; + } + + if (params[2] != 0 && !gamehelpers->ReferenceToEntity(params[2])) + { + pContext->ReportError("Invalid target2 (%d - %d)", gamehelpers->ReferenceToIndex(params[2]), params[2]); + return 0; + } + + IGameEvent *shot = gameevents->CreateEvent("hltv_chase", true); + if (!shot) + return 0; + + shot->SetInt("target1", gamehelpers->ReferenceToIndex(params[1])); + shot->SetInt("target2", params[2] ? gamehelpers->ReferenceToIndex(params[2]) : 0); + shot->SetInt("distance", params[3]); + shot->SetInt("phi", params[4]); // hi/low + shot->SetInt("theta", params[5]); // left/right + shot->SetInt("ineye", params[6] ? 1 : 0); + + // Update director state + g_HLTVDirectorWrapper.SetPVSEntity(gamehelpers->ReferenceToIndex(params[1])); + + hltvserver->BroadcastEvent(shot); + gameevents->FreeEvent(shot); + + // Prevent auto director from changing shots until we allow it to again. + g_HLTVDirectorWrapper.SetNextThinkTick(hltvdirector->GetDirectorTick() + TIME_TO_TICKS(sp_ctof(params[7]))); + + return 1; +} + // native bool:SourceTV_IsRecording(); static cell_t Native_IsRecording(IPluginContext *pContext, const cell_t *params) { @@ -499,7 +589,9 @@ const sp_nativeinfo_t sourcetv_natives[] = { "SourceTV_BroadcastHintMessage", Native_BroadcastHintMessage }, { "SourceTV_BroadcastConsoleMessage", Native_BroadcastConsoleMessage }, { "SourceTV_GetViewEntity", Native_GetViewEntity }, - { "SourceTV_GetViewCoordinates", Native_GetViewCoordinates }, + { "SourceTV_GetViewOrigin", Native_GetViewOrigin }, + { "SourceTV_ForceFixedCameraShot", Native_ForceFixedCameraShot }, + { "SourceTV_ForceChaseCameraShot", Native_ForceChaseCameraShot }, { "SourceTV_StartRecording", Native_StartRecording }, { "SourceTV_StopRecording", Native_StopRecording }, { "SourceTV_IsRecording", Native_IsRecording }, diff --git a/smsdk_config.h b/smsdk_config.h index 7ff3527..4d3fcd2 100644 --- a/smsdk_config.h +++ b/smsdk_config.h @@ -65,7 +65,7 @@ //#define SMEXT_ENABLE_DBMANAGER #define SMEXT_ENABLE_GAMECONF //#define SMEXT_ENABLE_MEMUTILS -//#define SMEXT_ENABLE_GAMEHELPERS +#define SMEXT_ENABLE_GAMEHELPERS //#define SMEXT_ENABLE_TIMERSYS //#define SMEXT_ENABLE_THREADER #define SMEXT_ENABLE_LIBSYS diff --git a/sourcetv_test.sp b/sourcetv_test.sp index 1aa5d8c..8ba68f7 100644 --- a/sourcetv_test.sp +++ b/sourcetv_test.sp @@ -1,17 +1,24 @@ -#undef REQUIRE_EXTENSIONS +//#undef REQUIRE_EXTENSIONS #include "sourcetvmanager" public OnPluginStart() { + LoadTranslations("common.phrases"); + RegConsoleCmd("sm_servercount", Cmd_GetServerCount); RegConsoleCmd("sm_selectserver", Cmd_SelectServer); RegConsoleCmd("sm_getselectedserver", Cmd_GetSelectedServer); RegConsoleCmd("sm_getbotindex", Cmd_GetBotIndex); + RegConsoleCmd("sm_getbroadcasttick", Cmd_GetBroadcastTick); RegConsoleCmd("sm_localstats", Cmd_Localstats); RegConsoleCmd("sm_getdelay", Cmd_GetDelay); RegConsoleCmd("sm_spectators", Cmd_Spectators); RegConsoleCmd("sm_spechintmsg", Cmd_SendHintMessage); RegConsoleCmd("sm_specmsg", Cmd_SendMessage); + RegConsoleCmd("sm_getviewentity", Cmd_GetViewEntity); + RegConsoleCmd("sm_getvieworigin", Cmd_GetViewOrigin); + RegConsoleCmd("sm_forcechasecam", Cmd_ForceChaseCameraShot); + //RegConsoleCmd("sm_forcefixedcam", Cmd_ForceFixedCameraShot); RegConsoleCmd("sm_startrecording", Cmd_StartRecording); RegConsoleCmd("sm_stoprecording", Cmd_StopRecording); RegConsoleCmd("sm_isrecording", Cmd_IsRecording); @@ -68,6 +75,12 @@ public Action:Cmd_GetBotIndex(client, args) return Plugin_Handled; } +public Action:Cmd_GetBroadcastTick(client, args) +{ + ReplyToCommand(client, "SourceTV broadcast tick: %d", SourceTV_GetBroadcastTick()); + return Plugin_Handled; +} + public Action:Cmd_Localstats(client, args) { new proxies, slots, specs; @@ -135,6 +148,49 @@ public Action:Cmd_SendMessage(client, args) return Plugin_Handled; } +public Action:Cmd_GetViewEntity(client, args) +{ + ReplyToCommand(client, "SourceTV view entity: %d", SourceTV_GetViewEntity()); + return Plugin_Handled; +} + +public Action:Cmd_GetViewOrigin(client, args) +{ + new Float:pos[3]; + SourceTV_GetViewOrigin(pos); + ReplyToCommand(client, "SourceTV view origin: %f %f %f", pos[0], pos[1], pos[2]); + return Plugin_Handled; +} + +public Action:Cmd_ForceChaseCameraShot(client, args) +{ + if (args < 1) + { + ReplyToCommand(client, "Usage: sm_startrecording "); + return Plugin_Handled; + } + + new String:sTarget[PLATFORM_MAX_PATH]; + GetCmdArg(1, sTarget, sizeof(sTarget)); + StripQuotes(sTarget); + new iTarget = FindTarget(client, sTarget, false, false); + if (iTarget == -1) + return Plugin_Handled; + + new bool:bInEye; + if (args >= 2) + { + new String:sInEye[16]; + GetCmdArg(2, sInEye, sizeof(sInEye)); + StripQuotes(sInEye); + bInEye = sInEye[0] == '1'; + } + + SourceTV_ForceChaseCameraShot(iTarget, 0, 96, -20, (GetRandomFloat()>0.5)?30:-30, bInEye, 20.0); + ReplyToCommand(client, "SourceTV forcing camera shot on %N.", iTarget); + return Plugin_Handled; +} + public Action:Cmd_StartRecording(client, args) { if (args < 1) diff --git a/sourcetvmanager.games.txt b/sourcetvmanager.games.txt index 2956809..c423ad6 100644 --- a/sourcetvmanager.games.txt +++ b/sourcetvmanager.games.txt @@ -19,7 +19,7 @@ } "Offsets" { - "BroadcastPrintf" + "CBaseServer::BroadcastPrintf" { "windows" "39" "linux" "40" @@ -30,6 +30,24 @@ "windows" "19600" "linux" "20496" } + + "CHLTVDirector::m_iPVSEntity" + { + "windows" "32" + "linux" "32" + } + + "CHLTVDirector::m_vPVSOrigin" + { + "windows" "36" + "linux" "36" + } + + "CHLTVDirector::m_nNextShotTick" + { + "windows" "52" + "linux" "52" + } } "Signatures" { @@ -61,7 +79,7 @@ } "Offsets" { - "BroadcastPrintf" + "CBaseServer::BroadcastPrintf" { "windows" "35" "linux" "36" @@ -72,6 +90,24 @@ "windows" "19192" "linux" "19424" // -416 } + + "CHLTVDirector::m_iPVSEntity" + { + "windows" "16" + "linux" "16" + } + + "CHLTVDirector::m_vPVSOrigin" + { + "windows" "20" + "linux" "20" + } + + "CHLTVDirector::m_nNextShotTick" + { + "windows" "40" + "linux" "40" + } } "Signatures" { diff --git a/sourcetvmanager.inc b/sourcetvmanager.inc index cab89f2..8fea1ee 100644 --- a/sourcetvmanager.inc +++ b/sourcetvmanager.inc @@ -3,6 +3,11 @@ #endif #define _stvmngr_included +// Some guidelines from the SDK about director camera shot durations +#define MIN_SHOT_LENGTH 4.0 // minimum time of a cut (seconds) +#define MAX_SHOT_LENGTH 8.0 // maximum time of a cut (seconds) +#define DEF_SHOT_LENGTH 6.0 // average time of a cut (seconds) + native SourceTV_GetHLTVServerCount(); native SourceTV_SelectHLTVServer(instance); native SourceTV_GetSelectedHLTVServer(); @@ -15,7 +20,9 @@ native bool:SourceTV_BroadcastConsoleMessage(const String:format[], any:...); // get current view entity (PVS), 0 if coords are used native SourceTV_GetViewEntity(); // get current PVS origin -native SourceTV_GetViewCoordinates(Float:view[3]); +native SourceTV_GetViewOrigin(Float:view[3]); +native bool:SourceTV_ForceFixedCameraShot(Float:pos[], Float:angle[], iTarget, Float:fov, Float:fDuration = DEF_SHOT_LENGTH); +native bool:SourceTV_ForceChaseCameraShot(iTarget1, iTarget2, distance, phi, theta, bool:bInEye, Float:fDuration = DEF_SHOT_LENGTH); native bool:SourceTV_StartRecording(const String:sFilename[]); native bool:SourceTV_StopRecording(); @@ -66,7 +73,9 @@ public __ext_stvmngr_SetNTVOptional() MarkNativeAsOptional("SourceTV_BroadcastHintMessage"); MarkNativeAsOptional("SourceTV_BroadcastConsoleMessage"); MarkNativeAsOptional("SourceTV_GetViewEntity"); - MarkNativeAsOptional("SourceTV_GetViewCoordinates"); + MarkNativeAsOptional("SourceTV_GetViewOrigin"); + MarkNativeAsOptional("SourceTV_ForceFixedCameraShot"); + MarkNativeAsOptional("SourceTV_ForceChaseCameraShot"); MarkNativeAsOptional("SourceTV_StartRecording"); MarkNativeAsOptional("SourceTV_StopRecording"); MarkNativeAsOptional("SourceTV_IsRecording");