diff --git a/M2TWEOP Code/M2TWEOP GUI/dataG.h b/M2TWEOP Code/M2TWEOP GUI/dataG.h index 26788a69..1756cd4a 100644 --- a/M2TWEOP Code/M2TWEOP GUI/dataG.h +++ b/M2TWEOP Code/M2TWEOP GUI/dataG.h @@ -35,7 +35,7 @@ class dataG // Freecam Integration string freecamExeName = "Freecam.exe"; bool freecamIntegration = false; - string freecamFolder = ""; + bool freecamStarted = false; string exeName; string gameArgs; diff --git a/M2TWEOP Code/M2TWEOP GUI/gameRunnerUI.cpp b/M2TWEOP Code/M2TWEOP GUI/gameRunnerUI.cpp index f314c420..f31624e3 100644 --- a/M2TWEOP Code/M2TWEOP GUI/gameRunnerUI.cpp +++ b/M2TWEOP Code/M2TWEOP GUI/gameRunnerUI.cpp @@ -109,6 +109,26 @@ namespace gameRunnerUI exit(0); } } + // Open Freecam if we are using the integration after waiting a bit for the game to start + std::this_thread::sleep_for(std::chrono::seconds(5)); + if (dataG::data.gameData.freecamIntegration == true && dataG::data.gameData.freecamStarted == false) + { + string currentFolder; + string freecamFolder; + string exePath; + helpers::getCurrentPath(currentFolder); + + exePath = currentFolder + ".\\eopData\\resources\\tools\\freecam\\Freecam.exe"; + freecamFolder = currentFolder + ".\\eopData\\resources\\tools\\freecam"; + + std::wstring wideFolderPath = helpers::stringToWstring(freecamFolder); + std::wstring wideExePath = helpers::stringToWstring(exePath); + + LPSTR lpstr = helpers::ConvertWideStringToLPSTR(wideExePath); + LPSTR lpstr_folder = helpers::ConvertWideStringToLPSTR(wideFolderPath); + helpers::openProcess(lpstr, lpstr_folder); + dataG::data.gameData.freecamStarted = true; + } } int drawUI(bool *isOpen) @@ -165,18 +185,6 @@ namespace gameRunnerUI runGameThread, std::ref(startProcess.isRunStarted), std::ref(startProcess.isRunEnded), std::ref(startProcess.isGetResponse), std::ref(startProcess.exePath), std::ref(startProcess.exeArgs), std::ref(startProcess.eopArgs), startProcess.isEopNeeded); thrUrl.detach(); - // Open Freecam if we are using the integration - if (dataG::data.gameData.freecamIntegration == true) - { - std::string exePath = dataG::data.gameData.freecamFolder + "\\Freecam.exe"; - - std::wstring wideFolderPath = helpers::stringToWstring(dataG::data.gameData.freecamFolder); - std::wstring wideExePath = helpers::stringToWstring(exePath); - - LPSTR lpstr = helpers::ConvertWideStringToLPSTR(wideExePath); - LPSTR lpstr_folder = helpers::ConvertWideStringToLPSTR(wideFolderPath); - helpers::openProcess(lpstr, lpstr_folder); - } // Stop the launcher background music if Rich Presence is enabled and the launcher will stay open if (dataG::data.gameData.isDiscordRichPresenceEnabled == true && dataG::data.audio.bkgMusic.isMusicNeeded == true) { diff --git a/M2TWEOP Code/M2TWEOP GUI/mainUI.cpp b/M2TWEOP Code/M2TWEOP GUI/mainUI.cpp index fc69bd05..28b674d9 100644 --- a/M2TWEOP Code/M2TWEOP GUI/mainUI.cpp +++ b/M2TWEOP Code/M2TWEOP GUI/mainUI.cpp @@ -67,7 +67,7 @@ namespace mainUI } return 0; } - else if (childs.isDiscordUIOpen == true) + else if (childs.isDiscordUIOpen == true || dataG::data.gameData.freecamIntegration == true) { gameRunnerUI::maintainGUI(); return 0; diff --git a/M2TWEOP Code/M2TWEOP GUI/managerG.cpp b/M2TWEOP Code/M2TWEOP GUI/managerG.cpp index 5e18a62f..a5e8d463 100644 --- a/M2TWEOP Code/M2TWEOP GUI/managerG.cpp +++ b/M2TWEOP Code/M2TWEOP GUI/managerG.cpp @@ -107,10 +107,6 @@ namespace managerG { getJson(dataG::data.gameData.freecamIntegration, "isFreecamIntegrationEnabled"); } - if (json.contains("freecamFolder")) - { - getJson(dataG::data.gameData.freecamFolder, "freecamFolder"); - } } catch (jsn::json::type_error& e) { @@ -238,7 +234,6 @@ namespace managerG setJson("IsOverrideBattleCamera", dataG::data.gameData.IsOverrideBattleCamera); setJson("isDiscordRichPresenceEnabled", dataG::data.gameData.isDiscordRichPresenceEnabled); setJson("isFreecamIntegrationEnabled", dataG::data.gameData.freecamIntegration); - setJson("freecamFolder", dataG::data.gameData.freecamFolder); writeJsonToFile(fPath, json); json.clear(); diff --git a/M2TWEOP Code/M2TWEOP GUI/modSettingsUI.cpp b/M2TWEOP Code/M2TWEOP GUI/modSettingsUI.cpp index 2c8441a0..fb307bf2 100644 --- a/M2TWEOP Code/M2TWEOP GUI/modSettingsUI.cpp +++ b/M2TWEOP Code/M2TWEOP GUI/modSettingsUI.cpp @@ -245,12 +245,6 @@ namespace modSettingsUI // Freecam Settings ImGui::Checkbox("Freecam Integration", &dataG::data.gameData.freecamIntegration); if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { ImGui::SetTooltip("Automatically start and close the Freecam application when the game is launched");} - - ImGui::Text("Freecam Folder"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { ImGui::SetTooltip("Path to where you have Freecam installed (e.g C:\\Users\\stead\\Documents\\Modding Tools\\Med2 Modding Tools\\Freecam)");} - ImGui::InputText("", &dataG::data.gameData.freecamFolder); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { ImGui::SetTooltip("Path to where you have Freecam installed (e.g C:\\Users\\stead\\Documents\\Modding Tools\\Med2 Modding Tools\\Freecam)");} - ImGui::NewLine(); } void drawModSettingsUI(bool* isOpen) diff --git a/M2TWEOP DataFiles/eopData/config/gameCfg.json b/M2TWEOP DataFiles/eopData/config/gameCfg.json index ea763af6..2b4e477e 100644 --- a/M2TWEOP DataFiles/eopData/config/gameCfg.json +++ b/M2TWEOP DataFiles/eopData/config/gameCfg.json @@ -2,7 +2,6 @@ "isBlockLaunchWithoutEop": false, "isDiscordRichPresenceEnabled": false, "isFreecamIntegrationEnabled": false, - "freecamFolder": "", "isDeveloperModeNeeded": true, "isTacticalMapViewerNeeded": true, "isContextMenuNeeded": true, diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/Freecam.exe b/M2TWEOP DataFiles/eopData/resources/tools/freecam/Freecam.exe new file mode 100644 index 00000000..207368a0 Binary files /dev/null and b/M2TWEOP DataFiles/eopData/resources/tools/freecam/Freecam.exe differ diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/README.txt b/M2TWEOP DataFiles/eopData/resources/tools/freecam/README.txt new file mode 100644 index 00000000..afd29d76 --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/README.txt @@ -0,0 +1,119 @@ + +FREECAM -Bugis_Duckis + This version of FREECAM most likely not perfect and weirdness may happen! I have included the source code for anyone who wants it. It's probably + not very good, but I'll add that I'm not very accustomed to C++. Use it as you wish, no need to credit! + + +What does this program actually do? + Freecam is for all intents and purposes a hack. It essentially hijacks game variables and writes its own values to them. With version 1.0 it + even edits the machine code of medieval 2 in real-time to stop jitteryness and make the program less CPU demanding. Due to the hacky nature + of the program, IT IS IMPORTANT THAT YOU ONLY RUN THIS FOR THE CORRECT VERSION OF THE GAME, or else it will crash. Have fun! + + +REQUIREMENTS: + Windows 10/11 64bit, other versions of windows have not been tested. + Medieval 2 total war (Definitive edition V1.5.2.0/1.52), as for now gold edition has not been tested. + Microsoft Visual c++ 2022. + + +How to use? + step 1: launch the game. + step 2: Make sure you use "total war" camera style ingame. + step 3: Go into conf.txt and make sure that the controls match those ingame. + step 4: tab out of the game and start Freecam.exe. + step 5: tab into the game and enjoy! + + Recommendations: + Turn off restrict camera. + Unbind alt-s in the ingame settings (It mutes sound). You can do this by binding alt-s to a useless bind such as getting rid of advisor. + + Notes: + If you want to turn off freecam, then it is very important that you do so through the exit bind (END by default) and not the X button + in the corner. Else camera will be frozen in place until you restart the game! + You do not need to place the executable in any specific place or mod folder, it can be run from anywhere! + If you momentarily want vanilla controls, freecam can be paused with the the pause key (INSERT by default) + Save often, crashing though rare may occur. + + +Questions: + Where should i place the program? Anywhere you want! It doesn't not be in any place or modfolder, just make sure that it's next to the files it comes with! + Does this work with the DLC:s? Yes! + Does this work with mods? Yes! + Does this work on multiplayer? Yes! + Does this work with any other total war games? No! + Can I use the code to develop my own projects/fix this one? Absolutely! + + +Known Issues: + Camera going below game when double clicking on units, causing the game to freeze. + There is a small chance of crashing currently (As for now it has only occurred when tabbing). + + +Troubleshooting: + General Issues. + Try starting freecam as administrator. + + +----------------------------------------------------------------------------------------------------------- +Changelog: + 1.2.2: + Additions: + Added an option for controlling the delay when the program gives or releases control of the camera. + 1.2.1: + Fixes: + Fixed an error with the config file. Where the program would mix up the binds for rotate_up and up. + + Changes: + Changed the name of the FORWARD, BACKWARD, etc... to MOVE_FORWARD, MOVE_BACKWARD, etc... + + 1.2: + Fixes: + VERTICAL_SPEED now functions properly. + Fixed a typo in the code that stopped ROTATION_CONTROLS from working. + + Additions: + Added UPS, allows you to set how many updates per second the program will be running at. for users with 60fps+ monitors. You will need to + tweak the variables in config.txt. As UPS simply changes how fast the program will run, camera will therefor appear to be twice as fast if you set it to 120 rather than the default 60 as an example. + Added an Icon for the executable. + + 1.1: + Fixes: + Fixed inconsitency in speed when pressing forward and touching the top of the screen. + Fixed EDGEPAN_SPEED so it has similar speeds as camera sensitivity. + + Changes: + Higher sensitivty values now actually makes the camera more sensitive rather than less. + + Additions: + Added OVERRIDE_MOVEMENT, which makes the movement and scrolling controls provided by freecam optional. + Added untested Gold Edition addresses. I have no way to test these as I dont own gold edition, only use + on custom battles until it has been confirmed that they infact do work. You might otherwise risk + losing progress on your save due to crashes. + Added keybinds for rotating camera. + + + 1.0: + Due to this version being completely seperate, earlier versions will not be included in this changelog as to avoid missinformation. + Due to reports of instability from many users, I have decided to rewrite the entire project. While the general idea remains the same, + some new features have been added to greatly improve stability and decrease CPU usage. The biggest of which is the fact that the + program now will delete lines of machine-code straight from the games process memory while it's running, which stops the game + from writing data to the camera position variables. In earlier versions, the program was essentially having a battle to death over + control of the variables, and for some systems the game was winning that battle. In this version the games hands got chopped off. + This only happens in RAM memory so you don't need to worry about game files, however the game might crash if you're running an + incompatible version of the game. + + Hopefully this version will run better! Sadly I cant make this work for gold edition as I dont own that version, if someone manages + to translate all of the addresses to gold edition then I'll be greatful! Otherwise I'll try to come with some solution in the future. + + Additions: + - Greatly improved stability. + - Decreased CPU usage. + - Smoother camera movements. + - More user friendly config file. + - Custom memory addresses. + - Pausing. + + Not included: + - Control of camera orientation with keyboard (Kinda defeats the point of having freecam). + - Gold Edition support, I will try do something for this in the future. + - Some configuration regarding camera speed. This can be readded if people want it. \ No newline at end of file diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/assemblyLines_Camera.txt b/M2TWEOP DataFiles/eopData/resources/tools/freecam/assemblyLines_Camera.txt new file mode 100644 index 00000000..4e4d9a21 --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/assemblyLines_Camera.txt @@ -0,0 +1,102 @@ +# Just put all the assembly memory addresses that write to camera coords in here, so that the program can remove them. + +#Definitive Edition addresses +#CAMERA_X +0x008F8E10 #Tabbing +0x008F8B50 #Cutscene +0x00E7EF6A #Cutscene2 +0x0094FCDC #Constant / restrictor +0x008FAC69 #restrictor +0x008F8C6C #Movement +0x008F9439 #Movement2 +0x0095B40E #Map click pan +0x0095B7F4 #Unit pan +0x008F8E8B #Unit zoom +0x008F6F29 #??? +0x0095B3B0 #??? +0x0094E996 #??? +0x008F9050 #??? + +#CAMERA_Y +0x008F8E1C #Tabbing +0x008F8B5C #Cutscene +0x00E7EF7F #Cutscene2 +0x0094FCE5 #Constant / restrictor +0x008FAC72 #restrictor +0x008F8C76 #Movement +0x008F9443 #Movement2 +0x0095B429 #Map click pan +0x0095B805 #Unit pan +0x008F8E97 #Unit zoom +0x008F6F39 #??? +0x0095B3BB #??? +0x0094E9DF #??? +0x008F905A #??? + +#CAMERA_Z +0x008F8E16 #Tabbing +0x008F8B56 #Cutscene +0x00E7EF74 #Cutscene2 +0x0094FCE0 #Constant / restrictor RTS +0x0094FD2D #Constant / restrictor2 RTS +0x008FAC6D #restrictor TWC +0x008F8C71 #Movement TWC +0x008F943E #Movement2 TWC +0x0095B41B #Map click pan +0x0095B499 #Map click pan2 +0x0095B7FC #Unit pan +0x008F8E91 #Unit zoom +0x008F6F2F #??? +0x0095B3B5 #??? +0x008F9011 #??? + +# Gold Edition addresses (These have not been tested, they might cause the game to crash!) +# Use a simple regex replace to quickly use these, replace "#0x" with "0x", make sure to remove or comment the previous defintive edition addresses! +#CAMERA_X +#0x00941f60 +#0x00941ca0 +#0x00ec80ba +#0x00998e2c +#0x00943db9 +#0x00941dbc +#0x00942589 +#0x009a455e +#0x009a4944 +#0x00941fdb +#0x00940079 +#0x009a4500 +#0x00997ae6 +#0x009421a0 + +#CAMERA_Y +#0x00941f6c +#0x00941cac +#0x00ec80cf +#0x00998e35 +#0x00943dc2 +#0x00941dc6 +#0x00942593 +#0x009a4579 +#0x009a4955 +#0x00941fe7 +#0x00940089 +#0x009a450b +#0x00997b2f +#0x009421aa + +#CAMERA_Z +#0x00941f66 +#0x00941ca6 +#0x00ec80c4 +#0x00998e30 +#0x00998e7d +#0x00943dbd +#0x00941dc1 +#0x0094258e +#0x009a456b +#0x009a45e9 +#0x009a494c +#0x00941fe1 +#0x0094007f +#0x009a4505 +#0x00942161 \ No newline at end of file diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/assemblyLines_Target.txt b/M2TWEOP DataFiles/eopData/resources/tools/freecam/assemblyLines_Target.txt new file mode 100644 index 00000000..62949b7a --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/assemblyLines_Target.txt @@ -0,0 +1,98 @@ +# Just put all the assembly memory addresses that write to camera coords in here, so that the program can remove them. + +#Definitive Edition addresses +#TARGET_X +0x008F8B78 +0x008F8E38 +0x008F8EB9 +0x00E7EF91 +0x008F6F5F +0x0095B5CB +0x0094FB90 +0x0095B828 +0x0095B828 +0x008F8CB6 +0x008F9480 +0x008F7056 +0x008FAC5B + +#TARGET_Y +0x008F8B84 +0x008F8E44 +0x008F8EC5 +0x00E7EFA6 +0x008F6F6B +0x0095B5D4 +0x0094FB9B +0x0095B831 +0x008F8CC0 +0x008F948A +0x008F7060 +0x008FAC63 + +#TARGET_Z +0x008F8B7E +0x008F8E3E +0x008F8EBF +0x00E7EF9B +0x008F6F65 +0x0095B5CF +0x0094FB95 +0x0094FBCE +0x0094FDCD +0x0095B82C +0x008F8CBB +0x008F9485 +0x008F705B +0x008FAC4E +0x0094E9BC +0x008F9055 + +# Gold Edition addresses (These have not been tested, they might cause the game to crash!) +# Use a simple regex replace to quickly use these, replace "#0x" with "0x" +#CAMERA_X +#0x00941cc8 +#0x00941f88 +#0x00942009 +#0x00ec80e1 +#0x009400af +#0x009a471b +#0x00998ce0 +#0x009a4978 +#0x009a4978 +#0x00941e06 +#0x009425d0 +#0x009401a6 +#0x00943dab + +#TARGET_Y +#0x00941cd4 +#0x00941f94 +#0x00942015 +#0x00ec80f6 +#0x009400bb +#0x009a4724 +#0x00998ceb +#0x009a4981 +#0x00941e10 +#0x009425da +#0x009401b0 +#0x00943db3 + +#TARGET_Z +#0x00941cce +#0x00941f8e +#0x0094200f +#0x00ec80eb +#0x009400b5 +#0x009a471f +#0x00998ce5 +#0x00998d1e +#0x00998f1d +#0x009a497c +#0x00941e0b +#0x009425d5 +#0x009401ab +#0x00943d9e +#0x00997b0c +#0x009421a5 \ No newline at end of file diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/AssemblyLine.cpp b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/AssemblyLine.cpp new file mode 100644 index 00000000..6b2ec27b --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/AssemblyLine.cpp @@ -0,0 +1,46 @@ +#include "AssemblyLine.h" + + + + +AssemblyLine::AssemblyLine(LPVOID addr) +{ + this->address = addr; + this->bytes = (int)RW::readMemoryByte(addr) == 243 ? 5 : 3; + //The 243 or F3 byte means that the operatation in total is 5 bytes long. + //Otherwise the operation is 3 bytes long. This works for this program as these are the only possibilities + this->original = new byte[this->bytes]; + + for (int i = 0; i < this->bytes; i++) + original[i] = RW::readMemoryByte((LPVOID)((int)(address)+i)); +} + +void AssemblyLine::nop() +{ + byte nop = (byte)144; + for (int i = 0; i < bytes; i++) + RW::writeMemory((LPVOID)((int)(address)+i), nop); +} + +void AssemblyLine::reset() +{ + for (int i = 0; i < bytes; i++) + RW::writeMemory((LPVOID)((int)(address)+i), original[i]); +} + +void AssemblyLine::printOriginal() +{ + std::cout << "Original = "; + for (int i = 0; i < bytes; i++) + std::cout << (int)original[i] << " "; + std::cout << "\n"; +} + +void AssemblyLine::printCurrent() +{ + std::cout << address << " is = "; + for (int i = 0; i < bytes; i++) + std::cout << (int)RW::readMemoryByte((LPVOID)((int)(address)+i)) << " "; + std::cout << "\n"; +} + diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/AssemblyLine.h b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/AssemblyLine.h new file mode 100644 index 00000000..25c5557e --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/AssemblyLine.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include "RW.h" + + +class AssemblyLine +{ +private: + LPVOID address; + int bytes; + byte *original; + +public: + AssemblyLine() {}; + AssemblyLine(LPVOID addr); + + void nop(); + + void reset(); + + void printOriginal(); + + void printCurrent(); +}; + diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/Freecam.cpp b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/Freecam.cpp new file mode 100644 index 00000000..4cda9bde --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/Freecam.cpp @@ -0,0 +1,379 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "AssemblyLine.h" +#include "codeKiller.h" +#include "Options.h" +#include "MouseDelta.h" + + +#define PI 3.14159265359 + +bool isBattle; +bool codeRemoved = false; +bool relieve_control = true; //This accounts for basically all camera pan related stuff ingame + +float camera_x = 0, camera_y = 0, camera_z = 0, camera_ground_z = 0; +float target_x = 1, target_y = 0, target_z = 0; + +float pitch = 0, yaw = 0; + +CodeKiller cameraCode; +CodeKiller targetCode; + +bool isPressed(unsigned int keycode) +{ + return GetKeyState(keycode) < 0; +} + +// These are the addresses that are only read from, this one should be run all the time +void readPassives() +{ + isBattle = RW::readMemoryInt(Options::BATTLE); +} + +// These are the addresses that are activly written too +void readActives() +{ + camera_x = RW::readMemoryFloat(Options::CAMERA_X); + camera_y = RW::readMemoryFloat(Options::CAMERA_Y); + camera_z = RW::readMemoryFloat(Options::CAMERA_Z); + + target_x = RW::readMemoryFloat(Options::TARGET_X); + target_y = RW::readMemoryFloat(Options::TARGET_Y); + target_z = RW::readMemoryFloat(Options::TARGET_Z); + + float length = sqrt(pow(target_x - camera_x, 2) + pow(target_y - camera_y, 2) + pow(target_z - camera_z, 2)); + pitch = asin((target_z - camera_z) / length); + yaw = atan2((target_y - camera_y) / length, (target_x - camera_x) / length); + if (isnan(pitch)) + pitch = 0; + if (isnan(yaw)) + yaw = 0; + +} + +//Writes to the active addresses, should never be run at the same time as readActives! +void writeActives() +{ + + //std::cout << yaw << " " << pitch << "\n"; + if (Options::OVERRIDE_MOVEMENT) + { + RW::writeMemory(Options::CAMERA_X, camera_x); + RW::writeMemory(Options::CAMERA_Y, camera_y); + RW::writeMemory(Options::CAMERA_Z, camera_z); + } + else + { + camera_x = RW::readMemoryFloat(Options::CAMERA_X); + camera_y = RW::readMemoryFloat(Options::CAMERA_Y); + camera_z = RW::readMemoryFloat(Options::CAMERA_Z); + } + + pitch = max(pitch, -(PI / 2.0f) * 0.9f); + pitch = min(pitch, (PI / 2.0f) * 0.9f); + target_x = (cos(yaw) * cos(pitch) * 1000.0f) + camera_x; + target_y = (sin(yaw) * cos(pitch) * 1000.0f) + camera_y; + target_z = (sin(pitch) * 1000.0f) + camera_z; + + RW::writeMemory(Options::TARGET_X, target_x); + RW::writeMemory(Options::TARGET_Y, target_y); + RW::writeMemory(Options::TARGET_Z, target_z); +} + +void killCode() +{ + if (!codeRemoved) + { + RW::suspend(); + Sleep(Options::RELIEVE_DELAY); + if (Options::OVERRIDE_MOVEMENT) + cameraCode.kill(); + targetCode.kill(); + Sleep(Options::RELIEVE_DELAY); + RW::resume(); + codeRemoved = true; + } +} + +void resurrectCode() +{ + if (codeRemoved) + { + RW::suspend(); + Sleep(Options::RELIEVE_DELAY); + if (Options::OVERRIDE_MOVEMENT) + cameraCode.resurrect(); + targetCode.resurrect(); + Sleep(Options::RELIEVE_DELAY); + RW::resume(); + codeRemoved = false; + } +} + +void relieve(bool set) +{ + relieve_control = set; + if (!set) + killCode(); + else + resurrectCode(); +} + +long scrollPos = 0; +void scroll_event() +{ + while (true) + scrollPos += GetScrollDelta(); +} + + +int main() +{ + std::cout << "Starting FREECAM!\n"; + std::cout << "Scroll event listener established!\n"; + thread se{ scroll_event }; + + Options::init("config.txt"); + RW::init(L"medieval 2"); + Sleep(100); + + cameraCode = CodeKiller(SS::readFile("assemblyLines_Camera.txt")); + std::cout << "successfully read assemblyLines_Camera.txt!\n"; + targetCode = CodeKiller(SS::readFile("assemblyLines_Target.txt")); + std::cout << "successfully read assemblyLines_Target.txt!\n"; + + + + readActives(); + + POINT p = { 0,0 }; + GetCursorPos(&p); + int oldX = p.x, oldY = p.y; + int oldClickX = -50000, oldClickY = -50000; + int oldScrollPos = 0; + long clickTime = 0, oldClickTime; + + bool lButtonHeld = false; + bool paused = false, pauseHeld; + bool fast_press, slow_press; + + + float x_vel = 0; + float y_vel = 0; + float z_vel = 0; + + float pitch_vel = 0; + float yaw_vel = 0; + + unsigned int sleepTime = (1000 / Options::UPS) / 2; + + HWND hwnd = FindWindow(0, L"medieval 2"); + std::cout << "Main loop started!\nFreecam 1.2.1 is now active!\n"; + + while (true) + { + RECT window; + GetWindowRect(hwnd, &window); + Sleep(sleepTime); + readPassives(); + + //Exit + if (isPressed(Options::EXIT)) + { + resurrectCode(); + RW::terminate("USER EXIT"); + } + + //PAUSE CLICKING + if (isPressed(Options::PAUSE)) + { + if (pauseHeld == false) + paused = !paused, std::cout << (paused ? "paused" : "unpaused") << "\n"; + pauseHeld = true; + } + else + pauseHeld = false; + + //Main camera code here. Only runs if you're tabbed in and when there is a battle + if (isBattle && !paused && (window.left > -32000 && window.top >= 0)) + { + GetCursorPos(&p); + + //Variables + fast_press = isPressed(Options::FAST); + slow_press = isPressed(Options::SLOW); + + float horizontal_speed = Options::HORIZONTAL_SPEED; + float vertical_speed = Options::VERTICAL_SPEED; + if (fast_press && slow_press) + horizontal_speed *= Options::BOTH_MOD, vertical_speed *= Options::BOTH_MOD; + else if (fast_press) + horizontal_speed *= Options::FAST_MOD, vertical_speed *= Options::FAST_MOD; + else if (slow_press) + horizontal_speed *= Options::SLOW_MOD, vertical_speed *= Options::SLOW_MOD; + + float x_acc = 0; + float y_acc = 0; + float z_acc = 0; + + float pitch_acc = 0; + float yaw_acc = 0; + + + //################# VANILLA FUNCTION RETENTION #################### + //Double click detection + if (isPressed(VK_LBUTTON)) + { + if (lButtonHeld == false) + { + oldClickTime = clickTime; + clickTime = chrono::duration_cast(chrono::time_point_cast(chrono::high_resolution_clock::now()).time_since_epoch()).count(); + if (clickTime - oldClickTime < GetDoubleClickTime() && abs(oldClickX - p.x) < 2 && abs(oldClickY - p.y) < 2) + relieve(true); + oldClickX = p.x, oldClickY = p.y; + } + lButtonHeld = true; + } + else + lButtonHeld = false; + + + //################# CAMERA POSITION #################### + if (isPressed(Options::FORWARD)) + y_acc += sin(0 + yaw), x_acc += cos(0 + yaw), + relieve(false); + + if (isPressed(Options::BACKWARD)) + y_acc += sin(PI + yaw), x_acc += cos(PI + yaw), + relieve(false); + + if (isPressed(Options::LEFT)) + y_acc += sin(PI / 2 + yaw), x_acc += cos(PI / 2 + yaw), + relieve(false); + + if (isPressed(Options::RIGHT)) + y_acc += sin(3 * (PI / 2) + yaw), x_acc += cos(3 * (PI / 2) + yaw), + relieve(false); + + //Up and down + float diff = (scrollPos - oldScrollPos); + diff = Options::INVERT_SCROLL ? -diff : diff; + int isNegative = (diff != 0 ? (abs(diff) / diff) : 1); + z_vel += (pow(diff, 2) * isNegative) * vertical_speed / 10.0f; //Kinda ugly + + if (isPressed(Options::UP)) + z_acc -= 1, + relieve(false); + + if (isPressed(Options::DOWN)) + z_acc -= -1, + relieve(false); + + + //################# CAMERA ORIENTATION #################### + //FREEEECAAAAAAAAM + float adjusted_sensitivty = (Options::SENSITIVITY * (1 - Options::CAMERA_SMOOTHENING)); + if (isPressed(Options::FREECAM)) + { + float invert = Options::INVERT_MOUSE ? -1.0 : 1.0; + pitch_acc -= (float)(invert * (p.y - oldY)) / (500.0f) * adjusted_sensitivty; + yaw_acc -= (float)(invert * (p.x - oldX)) / (500.0f) * adjusted_sensitivty; + relieve(false); + } + + //Edge Detection + float adjusted_panSpeed = (Options::PAN_PEED * (1 - Options::CAMERA_SMOOTHENING)); + if (Options::ENABLE_EDGEPAN) + { + if (p.x <= window.left + Options::EDGEPAN_MARGIN) + yaw_acc += (3.0f / 100.0f) * adjusted_panSpeed, + relieve(false); + + else if (p.x >= window.right - Options::EDGEPAN_MARGIN) + yaw_acc -= (3.0f / 100.0f) * adjusted_panSpeed, + relieve(false); + + if (p.y <= window.top + Options::EDGEPAN_MARGIN) + y_acc += sin(0 + yaw), x_acc += cos(0 + yaw), + relieve(false); + + else if (p.y >= window.bottom - Options::EDGEPAN_MARGIN) + y_acc += sin(PI + yaw), x_acc += cos(PI + yaw), + relieve(false); + } + + //Keycontrolled orientation + if (Options::ROTATION_CONTROLS) + { + if (isPressed(Options::ROTATE_UP)) + pitch_acc += (3.0f / 100.0f) * adjusted_panSpeed, + relieve(false); + + if (isPressed(Options::ROTATE_DOWN)) + pitch_acc -= (3.0f / 100.0f) * adjusted_panSpeed, + relieve(false); + + if (isPressed(Options::ROTATE_LEFT)) + yaw_acc += (3.0f / 100.0f) * adjusted_panSpeed, + relieve(false); + + if (isPressed(Options::ROTATE_RIGHT)) + yaw_acc -= (3.0f / 100.0f) * adjusted_panSpeed, + relieve(false); + } + + //For unit zooming + if (isPressed(Options::getVK_Key("VK_MBUTTON"))) + relieve(true); + + //################# ETC #################### + float length = sqrt(pow(x_acc, 2) + pow(y_acc, 2) + pow(z_acc, 2)); + if (length == 0) // 0/0 avoidance + length = 1; + x_vel += ((x_acc / length) * (horizontal_speed * (1 - Options::HORIZONTAL_SMOOTHENING))) / 2.0f; //Weird math is to account for smoothening + y_vel += ((y_acc / length) * (horizontal_speed * (1 - Options::HORIZONTAL_SMOOTHENING))) / 2.0f; + z_vel += ((z_acc / length) * (vertical_speed * (1 - Options::VERTICAL_SMOOTHENING))) / 2.0f; + camera_x += x_vel; + camera_y += y_vel; + camera_z += z_vel; + x_vel *= Options::HORIZONTAL_SMOOTHENING; + y_vel *= Options::HORIZONTAL_SMOOTHENING; + z_vel *= Options::VERTICAL_SMOOTHENING; + + pitch_vel += pitch_acc; + yaw_vel += yaw_acc; + pitch += pitch_vel; + yaw += yaw_vel; + pitch_vel *= Options::CAMERA_SMOOTHENING; + yaw_vel *= Options::CAMERA_SMOOTHENING; + + oldX = p.x, oldY = p.y; + + camera_x = min(max(camera_x, -900), 900); + camera_y = min(max(camera_y, -900), 900); + + //Writer / Reader + if (!relieve_control) + writeActives(); + else + readActives(); + } + else // When youre not tabbed in or in a battle + { + relieve(true); + readActives(); + } + + oldScrollPos = scrollPos; + } + std::cout << " done!"; +} diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/MouseDelta.h b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/MouseDelta.h new file mode 100644 index 00000000..94d3d1b9 --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/MouseDelta.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include +//Scroll stuff from https://www.youtube.com/watch?v=Kp0fwR1lk8g +HHOOK mh; +int delta = 0; +LRESULT CALLBACK Mouse(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (nCode < 0) + return CallNextHookEx(mh, nCode, wParam, lParam); + + MSLLHOOKSTRUCT* pMouseStruct = (MSLLHOOKSTRUCT*)lParam; + + if (pMouseStruct != NULL) + { + if (wParam == WM_MOUSEWHEEL) + if (HIWORD(pMouseStruct->mouseData) == 120) + delta = 1; + else + delta = -1; + else + delta = 0; + } + + return CallNextHookEx(mh, nCode, wParam, lParam); +} + +int GetScrollDelta() +{ + delta = 0; + if (!(mh = SetWindowsHookExA(WH_MOUSE_LL, Mouse, NULL, 0))) + delta = -404; + + MSG message; + bool peek = true; + long tm = time(0); + + while (peek) + { + PeekMessage(&message, NULL, 0, 0, PM_REMOVE); + if (!delta == 0) + peek = false; + } + + UnhookWindowsHookEx(mh); + + return delta; +} \ No newline at end of file diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/Options.h b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/Options.h new file mode 100644 index 00000000..5215daac --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/Options.h @@ -0,0 +1,281 @@ +#pragma once +#include +#include "ss.h" +#include +#include +#include + +namespace Options +{ + std::unordered_map keyFinder; + unsigned int UPS = 60; + unsigned int RELIEVE_DELAY = 2; + + bool OVERRIDE_MOVEMENT; + bool ROTATION_CONTROLS; + + unsigned int EXIT, PAUSE; + + unsigned int FORWARD, BACKWARD, LEFT, RIGHT, UP, DOWN; + unsigned int ROTATE_UP, ROTATE_DOWN, ROTATE_LEFT, ROTATE_RIGHT; + unsigned int FAST, SLOW; + unsigned int FREECAM; + + float SENSITIVITY; + float HORIZONTAL_SMOOTHENING; + float VERTICAL_SMOOTHENING; + float CAMERA_SMOOTHENING; + float HORIZONTAL_SPEED; + float VERTICAL_SPEED; + float PAN_PEED; + + float FAST_MOD, SLOW_MOD, BOTH_MOD; + + bool INVERT_MOUSE; + bool INVERT_SCROLL; + + bool ENABLE_EDGEPAN; + unsigned int EDGEPAN_MARGIN; + + LPVOID BATTLE; + LPVOID CAMERA_X, CAMERA_Y, CAMERA_Z; + LPVOID TARGET_X, TARGET_Y, TARGET_Z; + + std::string getVar(string text, string varName) + { + std::string out = std::regex_replace(text, std::regex("\n+"), " "); + out = std::regex_replace(out, std::regex(".*(\t| |^)" + varName + "="), ""); + out = std::regex_replace(out, std::regex(" .*"), ""); + //std::cout << "\t" << varName << " = " << out << "\n"; + return out; + } + + unsigned int getVK_Key(std::string key) + { + if (key.length() == 1) + return VkKeyScanExA(tolower(key[0]), GetKeyboardLayout(0)); + else if (key.length() > 1) + return keyFinder.at(key); + else + { + std::cerr << "key " << key << " does not exist!\n" ; + throw std::runtime_error("Key is empty"); + } + } + + void init(std::string file_path) + { + { + keyFinder.emplace("VK_LBUTTON", 0x01); + keyFinder.emplace("VK_RBUTTON", 0x02); + keyFinder.emplace("VK_CANCEL", 0x03); + keyFinder.emplace("VK_MBUTTON", 0x04); + keyFinder.emplace("VK_XBUTTON1", 0x05); + keyFinder.emplace("VK_XBUTTON2", 0x06); + keyFinder.emplace("VK_BACK", 0x08); + keyFinder.emplace("VK_TAB", 0x09); + keyFinder.emplace("VK_CLEAR", 0x0C); + keyFinder.emplace("VK_RETURN", 0x0D); + keyFinder.emplace("VK_SHIFT", 0x10); + keyFinder.emplace("VK_CONTROL", 0x11); + keyFinder.emplace("VK_MENU", 0x12); + keyFinder.emplace("VK_PAUSE", 0x13); + keyFinder.emplace("VK_CAPITAL", 0x14); + keyFinder.emplace("VK_KANA", 0x15); + keyFinder.emplace("VK_HANGUEL", 0x15); + keyFinder.emplace("VK_HANGUL", 0x15); + keyFinder.emplace("VK_IME_ON", 0x16); + keyFinder.emplace("VK_JUNJA", 0x17); + keyFinder.emplace("VK_FINAL", 0x18); + keyFinder.emplace("VK_HANJA", 0x19); + keyFinder.emplace("VK_KANJI", 0x19); + keyFinder.emplace("VK_IME_OFF", 0x1A); + keyFinder.emplace("VK_ESCAPE", 0x1B); + keyFinder.emplace("VK_CONVERT", 0x1C); + keyFinder.emplace("VK_NONCONVERT", 0x1D); + keyFinder.emplace("VK_ACCEPT", 0x1E); + keyFinder.emplace("VK_MODECHANGE", 0x1F); + keyFinder.emplace("VK_SPACE", 0x20); + keyFinder.emplace("VK_PRIOR", 0x21); + keyFinder.emplace("VK_NEXT", 0x22); + keyFinder.emplace("VK_END", 0x23); + keyFinder.emplace("VK_HOME", 0x24); + keyFinder.emplace("VK_LEFT", 0x25); + keyFinder.emplace("VK_UP", 0x26); + keyFinder.emplace("VK_RIGHT", 0x27); + keyFinder.emplace("VK_DOWN", 0x28); + keyFinder.emplace("VK_SELECT", 0x29); + keyFinder.emplace("VK_PRINT", 0x2A); + keyFinder.emplace("VK_EXECUTE", 0x2B); + keyFinder.emplace("VK_SNAPSHOT", 0x2C); + keyFinder.emplace("VK_INSERT", 0x2D); + keyFinder.emplace("VK_DELETE", 0x2E); + keyFinder.emplace("VK_HELP", 0x2F); + keyFinder.emplace("VK_LWIN", 0x5B); + keyFinder.emplace("VK_RWIN", 0x5C); + keyFinder.emplace("VK_APPS", 0x5D); + keyFinder.emplace("VK_SLEEP", 0x5F); + keyFinder.emplace("VK_NUMPAD0", 0x60); + keyFinder.emplace("VK_NUMPAD1", 0x61); + keyFinder.emplace("VK_NUMPAD2", 0x62); + keyFinder.emplace("VK_NUMPAD3", 0x63); + keyFinder.emplace("VK_NUMPAD4", 0x64); + keyFinder.emplace("VK_NUMPAD5", 0x65); + keyFinder.emplace("VK_NUMPAD6", 0x66); + keyFinder.emplace("VK_NUMPAD7", 0x67); + keyFinder.emplace("VK_NUMPAD8", 0x68); + keyFinder.emplace("VK_NUMPAD9", 0x69); + keyFinder.emplace("VK_MULTIPLY", 0x6A); + keyFinder.emplace("VK_ADD", 0x6B); + keyFinder.emplace("VK_SEPARATOR", 0x6C); + keyFinder.emplace("VK_SUBTRACT", 0x6D); + keyFinder.emplace("VK_DECIMAL", 0x6E); + keyFinder.emplace("VK_DIVIDE", 0x6F); + keyFinder.emplace("VK_F1", 0x70); + keyFinder.emplace("VK_F2", 0x71); + keyFinder.emplace("VK_F3", 0x72); + keyFinder.emplace("VK_F4", 0x73); + keyFinder.emplace("VK_F5", 0x74); + keyFinder.emplace("VK_F6", 0x75); + keyFinder.emplace("VK_F7", 0x76); + keyFinder.emplace("VK_F8", 0x77); + keyFinder.emplace("VK_F9", 0x78); + keyFinder.emplace("VK_F10", 0x79); + keyFinder.emplace("VK_F11", 0x7A); + keyFinder.emplace("VK_F12", 0x7B); + keyFinder.emplace("VK_F13", 0x7C); + keyFinder.emplace("VK_F14", 0x7D); + keyFinder.emplace("VK_F15", 0x7E); + keyFinder.emplace("VK_F16", 0x7F); + keyFinder.emplace("VK_F17", 0x80); + keyFinder.emplace("VK_F18", 0x81); + keyFinder.emplace("VK_F19", 0x82); + keyFinder.emplace("VK_F20", 0x83); + keyFinder.emplace("VK_F21", 0x84); + keyFinder.emplace("VK_F22", 0x85); + keyFinder.emplace("VK_F23", 0x86); + keyFinder.emplace("VK_F24", 0x87); + keyFinder.emplace("VK_NUMLOCK", 0x90); + keyFinder.emplace("VK_SCROLL", 0x91); + keyFinder.emplace("VK_LSHIFT", 0xA0); + keyFinder.emplace("VK_RSHIFT", 0xA1); + keyFinder.emplace("VK_LCONTROL", 0xA2); + keyFinder.emplace("VK_RCONTROL", 0xA3); + keyFinder.emplace("VK_LMENU", 0xA4); + keyFinder.emplace("VK_RMENU", 0xA5); + keyFinder.emplace("VK_BROWSER_BACK", 0xA6); + keyFinder.emplace("VK_BROWSER_FORWARD", 0xA7); + keyFinder.emplace("VK_BROWSER_REFRESH", 0xA8); + keyFinder.emplace("VK_BROWSER_STOP", 0xA9); + keyFinder.emplace("VK_BROWSER_SEARCH", 0xAA); + keyFinder.emplace("VK_BROWSER_FAVORITES", 0xAB); + keyFinder.emplace("VK_BROWSER_HOME", 0xAC); + keyFinder.emplace("VK_VOLUME_MUTE", 0xAD); + keyFinder.emplace("VK_VOLUME_DOWN", 0xAE); + keyFinder.emplace("VK_VOLUME_UP", 0xAF); + keyFinder.emplace("VK_MEDIA_NEXT_TRACK", 0xB0); + keyFinder.emplace("VK_MEDIA_PREV_TRACK", 0xB1); + keyFinder.emplace("VK_MEDIA_STOP", 0xB2); + keyFinder.emplace("VK_MEDIA_PLAY_PAUSE", 0xB3); + keyFinder.emplace("VK_LAUNCH_MAIL", 0xB4); + keyFinder.emplace("VK_LAUNCH_MEDIA_SELECT", 0xB5); + keyFinder.emplace("VK_LAUNCH_APP1", 0xB6); + keyFinder.emplace("VK_LAUNCH_APP2", 0xB7); + keyFinder.emplace("VK_OEM_1", 0xBA); + keyFinder.emplace("VK_OEM_PLUS", 0xBB); + keyFinder.emplace("VK_OEM_COMMA", 0xBC); + keyFinder.emplace("VK_OEM_MINUS", 0xBD); + keyFinder.emplace("VK_OEM_PERIOD", 0xBE); + keyFinder.emplace("VK_OEM_2", 0xBF); + keyFinder.emplace("VK_OEM_3", 0xC0); + keyFinder.emplace("VK_OEM_4", 0xDB); + keyFinder.emplace("VK_OEM_5", 0xDC); + keyFinder.emplace("VK_OEM_6", 0xDD); + keyFinder.emplace("VK_OEM_7", 0xDE); + keyFinder.emplace("VK_OEM_8", 0xDF); + keyFinder.emplace("VK_OEM_102", 0xE2); + keyFinder.emplace("VK_PROCESSKEY", 0xE5); + keyFinder.emplace("VK_PACKET", 0xE7); + keyFinder.emplace("VK_ATTN", 0xF6); + keyFinder.emplace("VK_CRSEL", 0xF7); + keyFinder.emplace("VK_EXSEL", 0xF8); + keyFinder.emplace("VK_EREOF", 0xF9); + keyFinder.emplace("VK_PLAY", 0xFA); + keyFinder.emplace("VK_ZOOM", 0xFB); + keyFinder.emplace("VK_PA1", 0xFD); + keyFinder.emplace("VK_OEM_CLEAR", 0xFE); + } + + try + { + std::string contents = SS::readFile(file_path); + + contents = std::regex_replace(contents, std::regex("#.*"), ""); + contents = std::regex_replace(contents, std::regex("\n+"), "\n"); + contents = std::regex_replace(contents, std::regex("^\n+"), ""); + contents = std::regex_replace(contents, std::regex("\n+$"), ""); + contents = std::regex_replace(contents, std::regex(" "), ""); + contents = std::regex_replace(contents, std::regex("\t"), ""); + + FORWARD = getVK_Key(getVar(contents, "MOVE_FORWARD")); + LEFT = getVK_Key(getVar(contents, "MOVE_LEFT")); + BACKWARD= getVK_Key(getVar(contents, "MOVE_BACKWARD")); + RIGHT = getVK_Key(getVar(contents, "MOVE_RIGHT")); + UP = getVK_Key(getVar(contents, "MOVE_UP")); + DOWN = getVK_Key(getVar(contents, "MOVE_DOWN")); + ROTATE_LEFT = getVK_Key(getVar(contents, "ROTATE_LEFT")); + ROTATE_RIGHT = getVK_Key(getVar(contents, "ROTATE_RIGHT")); + ROTATE_UP = getVK_Key(getVar(contents, "ROTATE_UP")); + ROTATE_DOWN = getVK_Key(getVar(contents, "ROTATE_DOWN")); + + EXIT = getVK_Key(getVar(contents, "EXIT")); + PAUSE = getVK_Key(getVar(contents, "PAUSE")); + FAST = getVK_Key(getVar(contents, "FAST")); + SLOW = getVK_Key(getVar(contents, "SLOW")); + FREECAM = getVK_Key(getVar(contents, "FREECAM")); + + SENSITIVITY = stof(getVar(contents, "SENSITIVITY")); + HORIZONTAL_SMOOTHENING = max(min(SS::percentageToFloat(getVar(contents, "HORIZONTAL_SMOOTHENING")),0.99999999f),0); + VERTICAL_SMOOTHENING = max(min(SS::percentageToFloat(getVar(contents, "VERTICAL_SMOOTHENING")), 0.99999999f), 0); + CAMERA_SMOOTHENING = max(min(SS::percentageToFloat(getVar(contents, "CAMERA_SMOOTHENING")), 0.99999999f), 0); + HORIZONTAL_SPEED = stof(getVar(contents, "HORIZONTAL_SPEED")); + VERTICAL_SPEED = stof(getVar(contents, "VERTICAL_SPEED")); + PAN_PEED = stof(getVar(contents, "PAN_PEED")); + + FAST_MOD = SS::percentageToFloat(getVar(contents, "FAST_MOD")); + SLOW_MOD = SS::percentageToFloat(getVar(contents, "SLOW_MOD")); + BOTH_MOD = SS::percentageToFloat(getVar(contents, "BOTH_MOD")); + + INVERT_MOUSE = getVar(contents, "INVERT_MOUSE") == "true" ? true : false; + INVERT_SCROLL = getVar(contents, "INVERT_SCROLL") == "true" ? true : false; + ENABLE_EDGEPAN = getVar(contents, "ENABLE_EDGEPAN") == "true" ? true : false; + ROTATION_CONTROLS= getVar(contents, "ROTATION_CONTROLS") == "true" ? true : false; + + EDGEPAN_MARGIN = stoi(getVar(contents, "EDGEPAN_MARGIN")); + + + OVERRIDE_MOVEMENT = getVar(contents, "OVERRIDE_MOVEMENT") == "true" ? true : false; + + //Addresses + BATTLE = (LPVOID)std::stoul(getVar(contents, "BATTLE"), nullptr, 16); + CAMERA_X = (LPVOID)std::stoul(getVar(contents, "CAMERA_X"), nullptr, 16); + CAMERA_Y = (LPVOID)std::stoul(getVar(contents, "CAMERA_Y"), nullptr, 16); + CAMERA_Z = (LPVOID)std::stoul(getVar(contents, "CAMERA_Z"), nullptr, 16); + TARGET_X = (LPVOID)std::stoul(getVar(contents, "TARGET_X"), nullptr, 16); + TARGET_Y = (LPVOID)std::stoul(getVar(contents, "TARGET_Y"), nullptr, 16); + TARGET_Z = (LPVOID)std::stoul(getVar(contents, "TARGET_Z"), nullptr, 16); + + UPS = std::stoi(getVar(contents, "UPS")); + RELIEVE_DELAY = std::stoi(getVar(contents, "RELIEVE_DELAY")); + + std::cout << "Options in " + file_path + " successfully read!\n"; + } + catch (...) + { + std::cerr << "Failed to read options " + file_path + "\n"; + Sleep(6000); + throw std::runtime_error("Failed to read options from " + file_path); + exit(0); + } + } +} \ No newline at end of file diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/RW.cpp b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/RW.cpp new file mode 100644 index 00000000..7b3659af --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/RW.cpp @@ -0,0 +1,113 @@ +#include "RW.h" + +void RW::terminate(std::string reason) +{ + CloseHandle(hProc); + for (int i = 0; i < hThread_length; i++) + CloseHandle(hThreads[i]); + std::cout << reason << "\n TERMINATING IN 6 SECONDS!"; + Sleep(6000); + exit(0); +} + +//READERS +byte RW::readMemoryByte(LPVOID address) +{ + byte var; + ReadProcessMemory(hProc, address, &var, (DWORD)sizeof(var), NULL); + return var; +} + +float RW::readMemoryFloat(LPVOID address) +{ + float var; + ReadProcessMemory(hProc, address, &var, (DWORD)sizeof(var), NULL); + return var; +} + +int RW::readMemoryInt(LPVOID address) +{ + int var; + ReadProcessMemory(hProc, address, &var, (DWORD)sizeof(var), NULL); + return var; +} + +bool RW::readMemoryBool(LPVOID address) +{ + bool var; + ReadProcessMemory(hProc, address, &var, (DWORD)sizeof(var), NULL); + return var; +} + +//WRITERS +bool RW::writeMemory(LPVOID address, byte data) +{ + return WriteProcessMemory(hProc, address, &data, (DWORD)sizeof(data), NULL); +} +bool RW::writeMemory(LPVOID address, float data) +{ + return WriteProcessMemory(hProc, address, &data, (DWORD)sizeof(data), NULL); +} +bool RW::writeMemory(LPVOID address, int data) +{ + return WriteProcessMemory(hProc, address, &data, (DWORD)sizeof(data), NULL); +} + +void RW::init(LPCWSTR proc) +{ + //hwnd = FindWindow(0, L"medieval 2"); + hwnd = FindWindow(0, proc); + if (hwnd == 0) + terminate("Cannot find window, is the game currently on?"); + else + { + std::cout << proc << " Window found!\n"; + GetWindowThreadProcessId(hwnd, &pId); + hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pId); + + if (!hProc) + terminate("Cannot open process"); + else + std::cout << "Process found!\n"; + + //Find game thread so it can be paused + HANDLE hThreadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + + THREADENTRY32 threadEntry; + threadEntry.dwSize = sizeof(THREADENTRY32); + + std::list handles{}; + Thread32First(hThreadSnapshot, &threadEntry); + do + { + if (threadEntry.th32OwnerProcessID == pId) + { + HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, + threadEntry.th32ThreadID); + handles.push_front(hThread); + hThread_length++; + } + } while (Thread32Next(hThreadSnapshot, &threadEntry)); + std::cout << "Process threads found!\n"; + + hThreads = new HANDLE[handles.size()]; + for (int i = 0; i < hThread_length; i++) + hThreads[i] = handles.front(), handles.pop_front(); + + std::cout << "RW init complete!\n"; + + CloseHandle(hThreadSnapshot); + } +} + +void RW::suspend() +{ + for (int i = 0; i < hThread_length; i++) + SuspendThread(hThreads[i]); +} + +void RW::resume() +{ + for (int i = 0; i < hThread_length; i++) + ResumeThread(hThreads[i]); +} diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/RW.h b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/RW.h new file mode 100644 index 00000000..8478e14c --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/RW.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "assemblyLine.h" + +static HWND hwnd; +static DWORD pId; +static HANDLE hProc; +static HANDLE *hThreads; +static unsigned int hThread_length; + +class RW +{ +public: + // INIT + static void init(LPCWSTR proc); + + static void terminate(std::string reason); + + //readers and writers + static byte readMemoryByte(LPVOID address); + static float readMemoryFloat(LPVOID address); + static int readMemoryInt(LPVOID address); + static bool readMemoryBool(LPVOID address); + + static bool writeMemory(LPVOID address, byte data); + static bool writeMemory(LPVOID address, float data); + static bool writeMemory(LPVOID address, int data); + + static void suspend(); + static void resume(); + + //Preferable, but does not work :( so dont use + //template + //static T readMemory(LPVOID address); + //template + //static bool writeMemory(LPVOID address, G data); + + + +}; \ No newline at end of file diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/codeKiller.h b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/codeKiller.h new file mode 100644 index 00000000..7d35ef1f --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/codeKiller.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include "ss.h" +#include "AssemblyLine.h" + + +class CodeKiller +{ + AssemblyLine* code; + int length; + +public: + + CodeKiller() {}; + CodeKiller(std::string contents) + { + try + { + std::string fileContents = contents; + fileContents = std::regex_replace(fileContents, std::regex("#.*"), ""); + fileContents = std::regex_replace(fileContents, std::regex("\n+"), "\n"); + fileContents = std::regex_replace(fileContents, std::regex("^\n+"), ""); + fileContents = std::regex_replace(fileContents, std::regex("\n+$"), ""); + fileContents = std::regex_replace(fileContents, std::regex(" +"), ""); + fileContents = std::regex_replace(fileContents, std::regex("\t+"), ""); + std::string* s = SS::split(fileContents, '\n'); + + length = SS::count(fileContents, '\n') + 1; + code = new AssemblyLine[length]; + for (int i = 0; i < length; i++) + code[i] = AssemblyLine((LPVOID)std::stoul(s[i], nullptr, 16)); + + } + catch (...) + { + std::cerr <<"Failed to generate code killer " + contents + "\n"; + throw std::runtime_error("Failed to generate code killer " + contents); + Sleep(6000); + exit(0); + } + } + + void kill() + { + for (int i = 0; i < length; i++) + code[i].nop(); + } + + void resurrect() + { + for (int i = 0; i < length; i++) + code[i].reset(); + } +}; \ No newline at end of file diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/resource.h b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/resource.h new file mode 100644 index 00000000..63c040da --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Freecam2-2.rc +// +#define IDI_ICON1 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/ss.h b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/ss.h new file mode 100644 index 00000000..32c57739 --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/code/ss.h @@ -0,0 +1,122 @@ +#pragma once +#include +#include +#include +#include +using namespace std; + +//String stuff (ss) I am aware that the name needs replacing. +namespace SS +{ + string removeCharacters(string str, char c) + { + string result; + for (size_t i = 0; i < str.size(); i++) + { + char currentChar = str[i]; + if (currentChar != c) + result += currentChar; + } + return result; + } + + string getLineWith(list text, string line) + { + for (string str : text) + if (str.find(line) != std::string::npos) + return str; + return ""; + } + + int find(string text, string marker) // Made due to some confusion :/ + { + for (int i = 0; i < text.length(); i++) + { + bool full_match = true; + if (text.at(i) == marker.at(0)) + { + for (int j = 1; j < marker.length(); j++) + if (text.at(i + j) != marker.at(j)) + { + full_match = false; + break; + } + } + else + full_match = false; + + if (full_match) + return i; + } + return -1; + } + + string getRightOfReg(string line, string seperator) + { + string out = std::regex_replace(line, std::regex(".*"+seperator), ""); + return line.substr(find(line, seperator) + seperator.length(), line.size()); + } + + string getRightOf(string line, string seperator) + { + return line.substr(find(line, seperator) + seperator.length(), line.size()); + } + + string getLeftOf(string line, string seperator) + { + return line.substr(0, find(line, seperator)); + } + + string readFile(string f) + { + ifstream file; + file.open(f); + + if (file.is_open()) + { + string line; + string text; + getline(file, text); + while (getline(file, line)) + text += "\n" + line; + + return text; + } + else + { + std::cerr << "Failed to read file " + f + "\n"; + throw std::runtime_error("Failed to open file " + f); + Sleep(6000); + exit(0); + } + } + + float percentageToFloat(string in) + { + float out = -1.0f; + if (find(in, "%") != -1) + out = stof(std::regex_replace(in, std::regex("\%"), "")) / 100.0f; + else + out = stof(in); + + return out; + } + + int count(string text, char symbol) + { + return (int)(std::count(text.begin(), text.end(), symbol)); + } + + string* split(string in, char seperator) + { + string* out = new string[count(in, seperator) + 1]; + int i = 0, s = 0; + string comp = in + ""; + while (i < count(in, seperator) + 1) + { + out[i++] = comp.substr(0, comp.find_first_of(seperator)); + comp = comp.substr(comp.find_first_of(seperator) + 1, comp.length()); + } + return out; + } +} \ No newline at end of file diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/config.txt b/M2TWEOP DataFiles/eopData/resources/tools/freecam/config.txt new file mode 100644 index 00000000..1b54841a --- /dev/null +++ b/M2TWEOP DataFiles/eopData/resources/tools/freecam/config.txt @@ -0,0 +1,87 @@ +# Welcome to the freecam configuration file. +# Advice: Do not use the middle mouse button for FREECAM, the game uses middle mouse button for zooming to units and that key is not rebindable sadly. +# VK_XBUTTON1 Also suffers from a similar issue. +# VK key codes can be found at - https://cherrytree.at/misc/vk.htm, Otherwise look up a VK_keycode map/list for your language. +# It is not neccesary but it is highly reccommended that the controls below match the controls ingame. +# Binds can either be a VK keycode or simply the character you desire. + + +UPS = 60 # Updates per second, If you play with a fps other than 60, you might want to change this to whatever fps you have. + # Camera Movements will appear to be 60fps even if game is running at 120 as an example. + # Other settings such as camera speed, smoothening and such do not scale with this value, you will need to tweak + # them if you change the UPS. + +RELIEVE_DELAY = 1 # How long the game is paused in ms when the program releases or gives back control over the camera. Higher values will cause + # a noticable stutter when double clicking, lower values might increase risk of crashing on some systems. + +#General Bindings and freecam +PAUSE = VK_INSERT +EXIT = VK_END +FAST = VK_SHIFT +SLOW = VK_MENU # VK_MENU is left-alt + +FAST_MOD = 250.0% # How FAST,SLOW or both pressed will affect the speed of the camera. +SLOW_MOD = 25.0% +BOTH_MOD = 10.0% + + +FREECAM = VK_XBUTTON2 # The key that makes mouse control control the camera orientation. + +INVERT_MOUSE = false +INVERT_SCROLL = true + +CAMERA_SMOOTHENING = 50.0% +SENSITIVITY = 1.0 + +PAN_PEED = 1.0 # Effects both edgepanning aswell as rotation controls +ENABLE_EDGEPAN = true + EDGEPAN_MARGIN = 5 # Pixel distance that triggers edgepanning + +ROTATION_CONTROLS = false + ROTATE_UP = r + ROTATE_DOWN = f + ROTATE_LEFT = q + ROTATE_RIGHT = e + + +# OVERRIDE_MOVEMENT gives control over the of the camera position to freecam. This allows for infinite zooming, more consistent camera speeds etc. +# One issue currently is that you can go below ground. +OVERRIDE_MOVEMENT = true + + HORIZONTAL_SMOOTHENING = 90.0% + VERTICAL_SMOOTHENING = 90.0% # Effects scrolling + + HORIZONTAL_SPEED = 1.0 + VERTICAL_SPEED = 1.0 + + MOVE_FORWARD = w + MOVE_LEFT = a + MOVE_BACKWARD = s + MOVE_RIGHT = d + MOVE_UP = VK_DOWN #Down arrow key + MOVE_DOWN = VK_UP #Up arrow key + + + + + + +################### ADDRESSES BELOW, DONT TOUCH UNLESS YOU KNOW WHAT YOU'RE DOING ################### + +#Definitive edition addresses +BATTLE = 0x0193D683 +TARGET_X = 0x0193D5DC +TARGET_Y = 0x0193D5E4 +TARGET_Z = 0x0193D5E0 +CAMERA_X = 0x0193D598 +CAMERA_Y = 0x0193D5A0 +CAMERA_Z = 0x0193D59C + +#Gold edition addresses (Make sure to change the addresses in assemblyLines_Camera and assemblyLines_Target) also! +#BATTLE = 0x019867D3 +#TARGET_X = 0x0198672C +#TARGET_Y = 0x01986734 +#TARGET_Z = 0x01986730 +#CAMERA_X = 0x019866E8 +#CAMERA_Y = 0x019866F0 +#CAMERA_Z = 0x019866EC \ No newline at end of file diff --git a/M2TWEOP DataFiles/eopData/resources/tools/freecam/icon/Freecam Icon.ico b/M2TWEOP DataFiles/eopData/resources/tools/freecam/icon/Freecam Icon.ico new file mode 100644 index 00000000..e904b8be Binary files /dev/null and b/M2TWEOP DataFiles/eopData/resources/tools/freecam/icon/Freecam Icon.ico differ diff --git a/documentationGenerator/releaseInfo/releaseDescription.md b/documentationGenerator/releaseInfo/releaseDescription.md index e2bb9a94..31d4d0a5 100644 --- a/documentationGenerator/releaseInfo/releaseDescription.md +++ b/documentationGenerator/releaseInfo/releaseDescription.md @@ -8,7 +8,7 @@ ### **Launcher** - Added [Freecam](https://www.moddb.com/mods/freecam-medieval-2) integration - *Medik* - - Specify the path to your Freecam installation and enable the integration to automatically launch and close Freecam whenever you play the game + - Enable the integration to automatically launch and close Freecam whenever you play the game - Updated [DXVK](https://github.com/doitsujin/dxvk/releases/tag/v2.4) to v2.4 - *Medik* - Allow users to specify a mod version which is displayed in the top left beneath the EOP version at launch - *Medik* - Launcher no longer continues to play music if Discord Rich Presence is enabled and background music is enabled - *Medik*