2017-01-19 16:23:59 -05:00
|
|
|
#include "STDInclude.hpp"
|
|
|
|
|
|
|
|
namespace Components
|
|
|
|
{
|
|
|
|
std::string Script::ScriptName;
|
|
|
|
std::vector<int> Script::ScriptHandles;
|
2017-05-13 06:09:58 -04:00
|
|
|
std::vector<Script::Function> Script::ScriptFunctions;
|
2017-01-19 16:23:59 -05:00
|
|
|
std::vector<std::string> Script::ScriptNameStack;
|
|
|
|
unsigned short Script::FunctionName;
|
2020-12-04 17:42:47 -05:00
|
|
|
std::unordered_map<std::string, std::string> Script::ScriptStorage;
|
2020-12-08 17:14:47 -05:00
|
|
|
std::unordered_map<int, std::string> Script::ScriptBaseProgramNum;
|
2020-12-19 17:50:51 -05:00
|
|
|
int Script::LastFrameTime = -1;
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2017-05-31 09:45:12 -04:00
|
|
|
Utils::Signal<Scheduler::Callback> Script::VMShutdownSignal;
|
2017-05-14 14:14:52 -04:00
|
|
|
|
2017-01-19 16:23:59 -05:00
|
|
|
void Script::FunctionError()
|
|
|
|
{
|
|
|
|
std::string funcName = Game::SL_ConvertToString(Script::FunctionName);
|
|
|
|
|
|
|
|
Game::Scr_ShutdownAllocNode();
|
|
|
|
|
|
|
|
Logger::Print(23, "\n");
|
|
|
|
Logger::Print(23, "******* script compile error *******\n");
|
|
|
|
Logger::Print(23, "Error: unknown function %s in %s\n", funcName.data(), Script::ScriptName.data());
|
|
|
|
Logger::Print(23, "************************************\n");
|
|
|
|
|
|
|
|
Logger::Error(5, "script compile error\nunknown function %s\n%s\n\n", funcName.data(), Script::ScriptName.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void Script::StoreFunctionNameStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
mov eax, [esp - 8h]
|
|
|
|
mov Script::FunctionName, ax
|
|
|
|
|
|
|
|
sub esp, 0Ch
|
|
|
|
push 0
|
|
|
|
push edi
|
|
|
|
|
|
|
|
mov eax, 612DB6h
|
|
|
|
jmp eax
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::StoreScriptName(const char* name)
|
|
|
|
{
|
|
|
|
Script::ScriptNameStack.push_back(Script::ScriptName);
|
|
|
|
Script::ScriptName = name;
|
|
|
|
|
|
|
|
if (!Utils::String::EndsWith(Script::ScriptName, ".gsc"))
|
|
|
|
{
|
|
|
|
Script::ScriptName.append(".gsc");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void Script::StoreScriptNameStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
2017-02-01 07:44:25 -05:00
|
|
|
pushad
|
|
|
|
|
|
|
|
lea ecx, [esp + 30h]
|
2017-01-19 16:23:59 -05:00
|
|
|
push ecx
|
|
|
|
|
|
|
|
call Script::StoreScriptName
|
|
|
|
add esp, 4h
|
|
|
|
|
2017-02-01 07:44:25 -05:00
|
|
|
popad
|
|
|
|
|
2017-01-19 16:23:59 -05:00
|
|
|
push ebp
|
|
|
|
mov ebp, ds:1CDEAA8h
|
2017-02-01 07:44:25 -05:00
|
|
|
|
|
|
|
push 427DC3h
|
|
|
|
retn
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::RestoreScriptName()
|
|
|
|
{
|
|
|
|
Script::ScriptName = Script::ScriptNameStack.back();
|
|
|
|
Script::ScriptNameStack.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void Script::RestoreScriptNameStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
2017-02-01 07:44:25 -05:00
|
|
|
pushad
|
2017-01-19 16:23:59 -05:00
|
|
|
call Script::RestoreScriptName
|
2017-02-01 07:44:25 -05:00
|
|
|
popad
|
2017-01-19 16:23:59 -05:00
|
|
|
|
|
|
|
mov ds:1CDEAA8h, ebp
|
|
|
|
|
2017-02-01 07:44:25 -05:00
|
|
|
push 427E77h
|
|
|
|
retn
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::PrintSourcePos(const char* filename, unsigned int offset)
|
|
|
|
{
|
|
|
|
FileSystem::File script(filename);
|
|
|
|
|
|
|
|
if (script.exists())
|
|
|
|
{
|
|
|
|
std::string buffer = script.getBuffer();
|
|
|
|
Utils::String::Replace(buffer, "\t", " ");
|
|
|
|
|
|
|
|
int line = 1;
|
|
|
|
int lineOffset = 0;
|
|
|
|
int inlineOffset = 0;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < buffer.size(); ++i)
|
|
|
|
{
|
|
|
|
// Terminate line
|
|
|
|
if (i == offset)
|
|
|
|
{
|
|
|
|
while (buffer[i] != '\r' && buffer[i] != '\n' && buffer[i] != '\0')
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer[i] = '\0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer[i] == '\n')
|
|
|
|
{
|
|
|
|
++line;
|
|
|
|
lineOffset = i; // Includes the line break!
|
|
|
|
inlineOffset = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++inlineOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger::Print(23, "in file %s, line %d:", filename, line);
|
|
|
|
Logger::Print(23, "%s\n", buffer.data() + lineOffset);
|
|
|
|
|
|
|
|
for (int i = 0; i < (inlineOffset - 1); ++i)
|
|
|
|
{
|
|
|
|
Logger::Print(23, " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger::Print(23, "*\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger::Print(23, "in file %s, offset %d\n", filename, offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::CompileError(unsigned int offset, const char* message, ...)
|
|
|
|
{
|
|
|
|
char msgbuf[1024] = { 0 };
|
|
|
|
va_list v;
|
|
|
|
va_start(v, message);
|
|
|
|
_vsnprintf_s(msgbuf, sizeof(msgbuf), message, v);
|
|
|
|
va_end(v);
|
|
|
|
|
|
|
|
Game::Scr_ShutdownAllocNode();
|
|
|
|
|
|
|
|
Logger::Print(23, "\n");
|
|
|
|
Logger::Print(23, "******* script compile error *******\n");
|
|
|
|
Logger::Print(23, "Error: %s ", msgbuf);
|
|
|
|
Script::PrintSourcePos(Script::ScriptName.data(), offset);
|
|
|
|
Logger::Print(23, "************************************\n\n");
|
|
|
|
|
|
|
|
Logger::Error(5, "script compile error\n%s\n%s\n(see console for actual details)\n", msgbuf, Script::ScriptName.data());
|
|
|
|
}
|
|
|
|
|
2018-12-17 08:29:18 -05:00
|
|
|
int Script::LoadScriptAndLabel(const std::string& script, const std::string& label)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
|
|
|
Logger::Print("Loading script %s.gsc...\n", script.data());
|
|
|
|
|
|
|
|
if (!Game::Scr_LoadScript(script.data()))
|
|
|
|
{
|
|
|
|
Logger::Print("Script %s encountered an error while loading. (doesn't exist?)", script.data());
|
2017-01-20 16:41:03 -05:00
|
|
|
Logger::Error(1, reinterpret_cast<char*>(0x70B810), script.data());
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger::Print("Script %s.gsc loaded successfully.\n", script.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger::Print("Finding script handle %s::%s...\n", script.data(), label.data());
|
|
|
|
int handle = Game::Scr_GetFunctionHandle(script.data(), label.data());
|
|
|
|
if (handle)
|
|
|
|
{
|
|
|
|
Logger::Print("Script handle %s::%s loaded successfully.\n", script.data(), label.data());
|
|
|
|
return handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger::Print("Script handle %s::%s couldn't be loaded. (file with no entry point?)\n", script.data(), label.data());
|
|
|
|
return handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::LoadGameType()
|
|
|
|
{
|
|
|
|
for (auto handle : Script::ScriptHandles)
|
|
|
|
{
|
|
|
|
Game::Scr_FreeThread(Game::Scr_ExecThread(handle, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
Game::Scr_LoadGameType();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::LoadGameTypeScript()
|
|
|
|
{
|
|
|
|
Script::ScriptHandles.clear();
|
|
|
|
|
|
|
|
auto list = FileSystem::GetFileList("scripts/", "gsc");
|
|
|
|
|
|
|
|
for (auto file : list)
|
|
|
|
{
|
|
|
|
file = "scripts/" + file;
|
|
|
|
|
|
|
|
if (Utils::String::EndsWith(file, ".gsc"))
|
|
|
|
{
|
|
|
|
file = file.substr(0, file.size() - 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
int handle = Script::LoadScriptAndLabel(file, "init");
|
2017-05-13 06:09:58 -04:00
|
|
|
if (handle) Script::ScriptHandles.push_back(handle);
|
|
|
|
else
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2017-05-13 06:09:58 -04:00
|
|
|
handle = Script::LoadScriptAndLabel(file, "main");
|
|
|
|
if (handle) Script::ScriptHandles.push_back(handle);
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Game::GScr_LoadGameTypeScript();
|
|
|
|
}
|
|
|
|
|
2018-12-17 08:29:18 -05:00
|
|
|
void Script::AddFunction(const std::string& name, Game::scr_function_t function, bool isDev)
|
2017-05-13 06:09:58 -04:00
|
|
|
{
|
2017-06-14 06:06:04 -04:00
|
|
|
for (auto i = Script::ScriptFunctions.begin(); i != Script::ScriptFunctions.end();)
|
2017-05-13 06:09:58 -04:00
|
|
|
{
|
2017-06-14 06:06:04 -04:00
|
|
|
if (i->getName() == name)
|
2017-05-13 06:09:58 -04:00
|
|
|
{
|
|
|
|
i = Script::ScriptFunctions.erase(i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
Script::ScriptFunctions.push_back({ name, function, isDev });
|
|
|
|
}
|
|
|
|
|
2017-05-14 14:14:52 -04:00
|
|
|
Game::scr_function_t Script::GetFunction(void* caller, const char** name, int* isDev)
|
|
|
|
{
|
2017-05-13 06:09:58 -04:00
|
|
|
for (auto& function : Script::ScriptFunctions)
|
|
|
|
{
|
2017-05-14 14:14:52 -04:00
|
|
|
if (name && *name)
|
2017-05-13 06:09:58 -04:00
|
|
|
{
|
2017-06-14 06:06:04 -04:00
|
|
|
if (Utils::String::ToLower(*name) == Utils::String::ToLower(function.getName()))
|
2017-05-13 06:09:58 -04:00
|
|
|
{
|
|
|
|
*name = function.getName();
|
|
|
|
*isDev = function.isDev();
|
|
|
|
return function.getFunction();
|
|
|
|
}
|
|
|
|
}
|
2017-06-14 06:06:04 -04:00
|
|
|
else if (caller == reinterpret_cast<void*>(0x465781))
|
2017-05-13 06:09:58 -04:00
|
|
|
{
|
|
|
|
Game::Scr_RegisterFunction(function.getFunction());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void Script::GetFunctionStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
test eax, eax
|
|
|
|
jnz returnSafe
|
|
|
|
|
2017-05-14 14:14:52 -04:00
|
|
|
sub esp, 8h
|
|
|
|
push [esp + 10h]
|
2017-05-13 06:09:58 -04:00
|
|
|
call Script::GetFunction
|
2017-05-14 14:14:52 -04:00
|
|
|
add esp, 0Ch
|
2017-05-13 06:09:58 -04:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
returnSafe:
|
2017-05-14 14:14:52 -04:00
|
|
|
pop edi
|
2020-12-08 17:18:09 -05:00
|
|
|
pop esi
|
|
|
|
retn
|
2020-12-08 17:14:47 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::StoreScriptBaseProgramNum()
|
|
|
|
{
|
|
|
|
Script::ScriptBaseProgramNum.insert_or_assign(Utils::Hook::Get<int>(0x1CFEEF8), Script::ScriptName);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::Scr_PrintPrevCodePos(int scriptPos)
|
|
|
|
{
|
|
|
|
int bestCodePos = -1;
|
|
|
|
int nextCodePos = -1;
|
|
|
|
int offset = -1;
|
|
|
|
std::string file;
|
|
|
|
|
|
|
|
for (auto kv : Script::ScriptBaseProgramNum)
|
|
|
|
{
|
|
|
|
int codePos = kv.first;
|
|
|
|
|
|
|
|
if (codePos > scriptPos)
|
|
|
|
{
|
|
|
|
if (nextCodePos == -1 || codePos < nextCodePos)
|
|
|
|
nextCodePos = codePos;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (codePos < bestCodePos)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bestCodePos = codePos;
|
|
|
|
|
|
|
|
file = kv.second;
|
|
|
|
offset = scriptPos - bestCodePos;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bestCodePos == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
float onehundred = 100.0;
|
|
|
|
|
|
|
|
Logger::Print(23, "\n@ %d (%d - %d)\n", scriptPos, bestCodePos, nextCodePos);
|
|
|
|
Logger::Print(23, "in %s (%.1f%% through the source)\n\n", file.c_str(), ((offset * onehundred) / (nextCodePos - bestCodePos)));
|
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void Script::Scr_PrintPrevCodePosStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
push esi
|
|
|
|
call Script::Scr_PrintPrevCodePos
|
|
|
|
add esp, 4h
|
|
|
|
|
2017-05-14 14:14:52 -04:00
|
|
|
pop esi
|
2017-05-13 06:09:58 -04:00
|
|
|
retn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-08 17:14:47 -05:00
|
|
|
__declspec(naked) void Script::StoreScriptBaseProgramNumStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
// execute our hook
|
|
|
|
pushad
|
|
|
|
|
|
|
|
call Script::StoreScriptBaseProgramNum
|
|
|
|
|
|
|
|
popad
|
|
|
|
|
|
|
|
// execute overwritten code caused by the jump hook
|
|
|
|
sub eax, ds:201A460h // gScrVarPub_programBuffer
|
|
|
|
add esp, 0Ch
|
|
|
|
mov ds : 1CFEEF8h, eax // gScrCompilePub_programLen
|
|
|
|
|
|
|
|
// jump back to the original code
|
|
|
|
push 426C3Bh
|
|
|
|
retn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-22 23:36:35 -04:00
|
|
|
void Script::OnVMShutdown(Utils::Slot<Scheduler::Callback> callback)
|
|
|
|
{
|
2020-12-08 17:14:47 -05:00
|
|
|
Script::ScriptBaseProgramNum.clear();
|
2020-07-22 23:36:35 -04:00
|
|
|
Script::VMShutdownSignal.connect(callback);
|
|
|
|
}
|
|
|
|
|
2017-05-14 14:14:52 -04:00
|
|
|
void Script::ScrShutdownSystemStub(int num)
|
|
|
|
{
|
|
|
|
Script::VMShutdownSignal();
|
|
|
|
|
|
|
|
// Scr_ShutdownSystem
|
|
|
|
Utils::Hook::Call<void(int)>(0x421EE0)(num);
|
|
|
|
}
|
|
|
|
|
2017-04-29 17:08:41 -04:00
|
|
|
int Script::SetExpFogStub()
|
|
|
|
{
|
2017-05-14 14:14:52 -04:00
|
|
|
if (Game::Scr_GetNumParam() == 6)
|
2017-04-29 17:08:41 -04:00
|
|
|
{
|
2017-05-14 14:14:52 -04:00
|
|
|
std::memmove(&Game::scriptContainer->stack[-4], &Game::scriptContainer->stack[-5], sizeof(Game::VariableValue) * 6);
|
|
|
|
Game::scriptContainer->stack += 1;
|
|
|
|
Game::scriptContainer->stack[-6].type = Game::VAR_FLOAT;
|
|
|
|
Game::scriptContainer->stack[-6].u.floatValue = 0;
|
2017-05-07 13:04:57 -04:00
|
|
|
|
2017-05-14 14:14:52 -04:00
|
|
|
++Game::scriptContainer->numParam;
|
2017-04-29 17:08:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return Game::Scr_GetNumParam();
|
|
|
|
}
|
|
|
|
|
2020-07-22 23:36:35 -04:00
|
|
|
Game::gentity_t* Script::getEntFromEntRef(Game::scr_entref_t entref)
|
|
|
|
{
|
|
|
|
Game::gentity_t* gentity = &Game::g_entities[entref];
|
|
|
|
return gentity;
|
|
|
|
}
|
|
|
|
|
|
|
|
Game::client_t* Script::getClientFromEnt(Game::gentity_t* gentity)
|
|
|
|
{
|
|
|
|
if (!gentity->client)
|
|
|
|
{
|
|
|
|
Logger::Error(5, "Entity: %i is not a client", gentity);
|
|
|
|
}
|
2021-04-03 18:56:15 -04:00
|
|
|
return &Game::svs_clients[gentity->s.number];
|
2020-07-22 23:36:35 -04:00
|
|
|
}
|
|
|
|
|
2020-12-04 17:42:47 -05:00
|
|
|
void Script::AddFunctions()
|
|
|
|
{
|
|
|
|
// System time
|
|
|
|
Script::AddFunction("GetSystemTime", [](Game::scr_entref_t) // gsc: GetSystemTime()
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
SYSTEMTIME time;
|
|
|
|
GetSystemTime(&time);
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
Game::Scr_AddInt(time.wSecond);
|
|
|
|
});
|
2020-12-04 17:42:47 -05:00
|
|
|
|
|
|
|
Script::AddFunction("GetSystemMilliseconds", [](Game::scr_entref_t) // gsc: GetSystemMilliseconds()
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
SYSTEMTIME time;
|
|
|
|
GetSystemTime(&time);
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
Game::Scr_AddInt(time.wMilliseconds);
|
|
|
|
});
|
2020-12-04 17:42:47 -05:00
|
|
|
|
|
|
|
// Print to console, even without being in 'developer 1'.
|
|
|
|
Script::AddFunction("PrintConsole", [](Game::scr_entref_t) // gsc: PrintConsole(<string>)
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
2020-12-04 17:42:47 -05:00
|
|
|
{
|
2020-12-08 17:18:09 -05:00
|
|
|
Game::Scr_Error("^1PrintConsole: Needs one string parameter!\n");
|
|
|
|
return;
|
|
|
|
}
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
auto str = Game::Scr_GetString(0);
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
Game::Com_Printf(0, str);
|
|
|
|
});
|
2020-12-04 17:42:47 -05:00
|
|
|
|
|
|
|
// Executes command to the console
|
|
|
|
Script::AddFunction("Exec", [](Game::scr_entref_t) // gsc: Exec(<string>)
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
2020-12-04 17:42:47 -05:00
|
|
|
{
|
2020-12-08 17:18:09 -05:00
|
|
|
Game::Scr_Error("^1Exec: Needs one string parameter!\n");
|
|
|
|
return;
|
|
|
|
}
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
auto str = Game::Scr_GetString(0);
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
Command::Execute(str, false);
|
|
|
|
});
|
2020-12-04 17:42:47 -05:00
|
|
|
|
|
|
|
|
|
|
|
// Script Storage Funcs
|
|
|
|
Script::AddFunction("StorageSet", [](Game::scr_entref_t) // gsc: StorageSet(<str key>, <str data>);
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
if (Game::Scr_GetNumParam() != 2 || Game::Scr_GetType(0) != Game::VAR_STRING || Game::Scr_GetType(1) != Game::VAR_STRING)
|
2020-12-04 17:42:47 -05:00
|
|
|
{
|
2020-12-08 17:18:09 -05:00
|
|
|
Game::Scr_Error("^1StorageSet: Needs two string parameters!\n");
|
|
|
|
return;
|
|
|
|
}
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
std::string key = Game::Scr_GetString(0);
|
|
|
|
std::string data = Game::Scr_GetString(1);
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
Script::ScriptStorage.insert_or_assign(key, data);
|
|
|
|
});
|
2020-12-04 17:42:47 -05:00
|
|
|
|
|
|
|
Script::AddFunction("StorageRemove", [](Game::scr_entref_t) // gsc: StorageRemove(<str key>);
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
2020-12-04 17:42:47 -05:00
|
|
|
{
|
2020-12-08 17:18:09 -05:00
|
|
|
Game::Scr_Error("^1StorageRemove: Needs one string parameter!\n");
|
|
|
|
return;
|
|
|
|
}
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
std::string key = Game::Scr_GetString(0);
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-09 12:06:26 -05:00
|
|
|
if (!Script::ScriptStorage.count(key))
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key.c_str()));
|
|
|
|
return;
|
|
|
|
}
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
Script::ScriptStorage.erase(key);
|
|
|
|
});
|
2020-12-04 17:42:47 -05:00
|
|
|
|
|
|
|
Script::AddFunction("StorageGet", [](Game::scr_entref_t) // gsc: StorageGet(<str key>);
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
2020-12-04 17:42:47 -05:00
|
|
|
{
|
2020-12-08 17:18:09 -05:00
|
|
|
Game::Scr_Error("^1StorageGet: Needs one string parameter!\n");
|
|
|
|
return;
|
|
|
|
}
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
std::string key = Game::Scr_GetString(0);
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-09 12:06:26 -05:00
|
|
|
if (!Script::ScriptStorage.count(key))
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key.c_str()));
|
|
|
|
return;
|
|
|
|
}
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
auto data = Script::ScriptStorage.at(key);
|
|
|
|
Game::Scr_AddString(data.c_str());
|
|
|
|
});
|
2020-12-04 17:42:47 -05:00
|
|
|
|
|
|
|
Script::AddFunction("StorageHas", [](Game::scr_entref_t) // gsc: StorageHas(<str key>);
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
2020-12-04 17:42:47 -05:00
|
|
|
{
|
2020-12-08 17:18:09 -05:00
|
|
|
Game::Scr_Error("^1StorageHas: Needs one string parameter!\n");
|
|
|
|
return;
|
|
|
|
}
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-08 17:18:09 -05:00
|
|
|
std::string key = Game::Scr_GetString(0);
|
2020-12-04 17:42:47 -05:00
|
|
|
|
2020-12-09 12:06:26 -05:00
|
|
|
Game::Scr_AddInt(Script::ScriptStorage.count(key));
|
2020-12-08 17:18:09 -05:00
|
|
|
});
|
2020-12-04 17:42:47 -05:00
|
|
|
|
|
|
|
Script::AddFunction("StorageClear", [](Game::scr_entref_t) // gsc: StorageClear();
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
Script::ScriptStorage.clear();
|
|
|
|
});
|
2021-10-16 08:36:29 -04:00
|
|
|
|
|
|
|
Script::AddFunction("NoClip", [](Game::scr_entref_t entref)
|
|
|
|
{
|
|
|
|
if (entref >= Game::MAX_GENTITIES || Game::g_entities[entref].client == nullptr)
|
|
|
|
{
|
|
|
|
Game::Scr_Error(Utils::String::VA("^1NoClip: entity %u is not a client\n", entref));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Game::g_entities[entref].client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
|
|
|
});
|
|
|
|
|
|
|
|
Script::AddFunction("Ufo", [](Game::scr_entref_t entref)
|
|
|
|
{
|
|
|
|
if (entref >= Game::MAX_GENTITIES || Game::g_entities[entref].client == nullptr)
|
|
|
|
{
|
|
|
|
Game::Scr_Error(Utils::String::VA("^1Ufo: entity %u is not a client\n", entref));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Game::g_entities[entref].client->flags ^= Game::PLAYER_FLAG_UFO;
|
|
|
|
});
|
2020-12-04 17:42:47 -05:00
|
|
|
}
|
|
|
|
|
2017-01-19 16:23:59 -05:00
|
|
|
Script::Script()
|
|
|
|
{
|
|
|
|
Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick();
|
|
|
|
Utils::Hook(0x427E71, Script::RestoreScriptNameStub, HOOK_JUMP).install()->quick();
|
|
|
|
Utils::Hook(0x427DBC, Script::StoreScriptNameStub, HOOK_JUMP).install()->quick();
|
2020-12-08 17:14:47 -05:00
|
|
|
Utils::Hook(0x426C2D, Script::StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick();
|
|
|
|
Utils::Hook(0x42281B, Script::Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick();
|
|
|
|
|
|
|
|
// enable scr_error printing if in developer
|
|
|
|
Dvar::OnInit([]()
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
int developer = Dvar::Var("developer").get<int>();
|
2020-12-08 17:14:47 -05:00
|
|
|
|
2021-05-08 19:24:37 -04:00
|
|
|
if (developer > 0 && Dedicated::IsEnabled())
|
2020-12-08 17:18:09 -05:00
|
|
|
Utils::Hook::Set<BYTE>(0x48D8C7, 0x75);
|
|
|
|
});
|
2017-01-19 16:23:59 -05:00
|
|
|
|
|
|
|
Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick();
|
|
|
|
Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick();
|
|
|
|
Utils::Hook(0x434260, Script::CompileError, HOOK_JUMP).install()->quick();
|
|
|
|
|
|
|
|
Utils::Hook(0x48EFFE, Script::LoadGameType, HOOK_CALL).install()->quick();
|
|
|
|
Utils::Hook(0x45D44A, Script::LoadGameTypeScript, HOOK_CALL).install()->quick();
|
2017-04-29 17:08:41 -04:00
|
|
|
|
2017-05-14 14:14:52 -04:00
|
|
|
Utils::Hook(0x44E736, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetFunction
|
2020-07-22 23:36:35 -04:00
|
|
|
Utils::Hook(0x4EC8E5, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetMethod
|
2017-05-13 06:09:58 -04:00
|
|
|
|
2017-04-29 17:08:41 -04:00
|
|
|
Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick();
|
2017-05-14 14:14:52 -04:00
|
|
|
|
|
|
|
Utils::Hook(0x47548B, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick();
|
|
|
|
Utils::Hook(0x4D06BA, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick();
|
|
|
|
|
2020-12-19 17:50:51 -05:00
|
|
|
Scheduler::OnFrame([]()
|
|
|
|
{
|
|
|
|
if (!Game::SV_Loaded())
|
|
|
|
return;
|
|
|
|
|
|
|
|
int nowMs = Game::Sys_Milliseconds();
|
|
|
|
|
|
|
|
if (Script::LastFrameTime != -1)
|
|
|
|
{
|
|
|
|
int timeTaken = static_cast<int>((nowMs - Script::LastFrameTime) * Dvar::Var("timescale").get<float>());
|
|
|
|
|
|
|
|
if (timeTaken >= 500)
|
|
|
|
Logger::Print(23, "Hitch warning: %i msec frame time\n", timeTaken);
|
|
|
|
}
|
|
|
|
|
|
|
|
Script::LastFrameTime = nowMs;
|
|
|
|
});
|
|
|
|
|
2017-05-14 14:14:52 -04:00
|
|
|
Script::AddFunction("debugBox", [](Game::scr_entref_t)
|
2020-12-08 17:18:09 -05:00
|
|
|
{
|
|
|
|
MessageBoxA(nullptr, Game::Scr_GetString(0), "DEBUG", 0);
|
|
|
|
}, true);
|
2018-07-17 08:30:29 -04:00
|
|
|
|
2020-12-04 17:42:47 -05:00
|
|
|
Script::AddFunctions();
|
|
|
|
|
2018-07-17 08:30:29 -04:00
|
|
|
// Script::AddFunction("playviewmodelfx", [](Game::scr_entref_t /*index*/)
|
|
|
|
// {
|
|
|
|
// /*auto Scr_Error = Utils::Hook::Call<void(const char*)>(0x42EF40);
|
|
|
|
// if (index >> 16)
|
|
|
|
// {
|
|
|
|
// Scr_Error("not an entity");
|
|
|
|
// return;
|
|
|
|
// }*/
|
|
|
|
|
|
|
|
// // obtain FX name
|
|
|
|
// auto fxName = Game::Scr_GetString(0);
|
|
|
|
// auto fx = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FX, fxName).fx;
|
|
|
|
|
|
|
|
// auto tagName = Game::Scr_GetString(1);
|
|
|
|
// auto tagIndex = Game::SL_GetString(tagName, 0);
|
|
|
|
|
|
|
|
// /*char boneIndex = -2;
|
|
|
|
// if (!Game::CG_GetBoneIndex(2048, tagIndex, &boneIndex))
|
|
|
|
// {
|
|
|
|
// Scr_Error(Utils::String::VA("Unknown bone %s.\n", tagName));
|
|
|
|
// return;
|
|
|
|
// }*/
|
|
|
|
|
|
|
|
// Game::CG_PlayBoltedEffect(0, fx, 2048, tagIndex);
|
|
|
|
// });
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Script::~Script()
|
|
|
|
{
|
|
|
|
Script::ScriptName.clear();
|
|
|
|
Script::ScriptHandles.clear();
|
|
|
|
Script::ScriptNameStack.clear();
|
2017-05-13 06:09:58 -04:00
|
|
|
Script::ScriptFunctions.clear();
|
2017-05-14 14:14:52 -04:00
|
|
|
Script::VMShutdownSignal.clear();
|
2020-12-04 17:42:47 -05:00
|
|
|
|
|
|
|
Script::ScriptStorage.clear();
|
2020-12-08 17:14:47 -05:00
|
|
|
Script::ScriptBaseProgramNum.clear();
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
|
|
|
}
|