diff --git a/.github/workflows/msbuild.yml b/.github/workflows/msbuild.yml new file mode 100644 index 0000000..d207c84 --- /dev/null +++ b/.github/workflows/msbuild.yml @@ -0,0 +1,93 @@ +name: MSBuild + +on: + push: + branches: [ "dev" ] + +env: + SOLUTION_FILE_PATH: JG/johnnyguitar.sln + +permissions: write-all + +jobs: + build: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v2 + + - name: Build Release + working-directory: ${{env.GITHUB_WORKSPACE}} + run: msbuild /m /p:Configuration=Release ${{env.SOLUTION_FILE_PATH}} /p:PostBuildEventUseInBuild=false + + - name: Build Debug + working-directory: ${{env.GITHUB_WORKSPACE}} + run: msbuild /m /p:Configuration=Debug ${{env.SOLUTION_FILE_PATH}} /p:PostBuildEventUseInBuild=false + + - name: Generate Timestamp + run: | + timestamp=$(date +"%Y%m%d%H%M%S") + echo "artifact_timestamp=$timestamp" >> $GITHUB_ENV + shell: bash + + - name: Package Artifacts + run: | + mkdir -p artifacts + Compress-Archive -Path JG\Release\johnnyguitar.dll -DestinationPath artifacts\JohnnyGuitarNVSE-Release-${{ env.artifact_timestamp }}.zip + Compress-Archive -Path JG\Debug\johnnyguitar.dll -DestinationPath artifacts\JohnnyGuitarNVSE-Debug-${{ env.artifact_timestamp }}.zip + shell: pwsh + + - name: Publish Zipped Artifacts + uses: actions/upload-artifact@v4 + with: + name: Zipped-Artifacts-${{ env.artifact_timestamp }} + path: artifacts/ + + - name: Get Release + id: get_release + uses: cardinalby/git-get-release-action@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag: 'continuous' + doNotFailIfNotFound: true + - name: Delete old release if exists + if: steps.get_release.outputs.id != '' + uses: dev-drprasad/delete-tag-and-release@v1.0 + with: + tag_name: continuous + github_token: ${{ secrets.GITHUB_TOKEN }} + delete_release: true + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: continuous + release_name: "Continuous release" + draft: false + prerelease: true + body: | + Continuous release generated from the latest push to development branch. + - name: Upload Artifact (Release) + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: artifacts/JohnnyGuitarNVSE-Release-${{ env.artifact_timestamp }}.zip + asset_name: JohnnyGuitarNVSE-Release-${{ env.artifact_timestamp }}.zip + asset_content_type: application/zip + - name: Upload Artifact (Debug) + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: artifacts/JohnnyGuitarNVSE-Debug-${{ env.artifact_timestamp }}.zip + asset_name: JohnnyGuitarNVSE-Debug-${{ env.artifact_timestamp }}.zip + asset_content_type: application/zip diff --git a/JG/JohnnyGuitarNVSE.cpp b/JG/JohnnyGuitarNVSE.cpp index 731b363..f30f5fe 100644 --- a/JG/JohnnyGuitarNVSE.cpp +++ b/JG/JohnnyGuitarNVSE.cpp @@ -75,6 +75,8 @@ void MessageHandler(NVSEMessagingInterface::Message* msg) { hk_BarterHook::barterFilterListRight.clear(); NPCAccuracy::FlushMapRefs(); shakeRequests.clear(); + mlcOverridden = false; + mlcOverride = nullptr; break; } case NVSEMessagingInterface::kMessage_PostLoadGame: @@ -496,8 +498,11 @@ extern "C" { REG_CMD(RemoveNoteQuest); REG_CMD(SetHUDShudderPower); REG_CMD(GetHUDShudderPower); - REG_CMD(SetDialogResponseOverrideValues); // Function is subject to overrides at random and therefore not eligible for documentation. Can be removed at any time, so mod breakage due to using it will not be considered. - + REG_CMD(SetDialogResponseOverrideValues); // do not document + REG_CMD(SetMediaLocationControllerOverride); // do not document + REG_CMD(ClearMediaLocationControllerOverride); // do not document + REG_CMD(GetCasinoWinnings); + REG_CMD(SetCasinoWinnings); g_scriptInterface = (NVSEScriptInterface*)nvse->QueryInterface(kInterface_Script); g_cmdTableInterface = (NVSECommandTableInterface*)nvse->QueryInterface(kInterface_CommandTable); s_strArgBuf = (char*)malloc((sizeof(char)) * 1024); diff --git a/JG/JohnnyGuitarNVSE.h b/JG/JohnnyGuitarNVSE.h index 249eeee..352e91f 100644 --- a/JG/JohnnyGuitarNVSE.h +++ b/JG/JohnnyGuitarNVSE.h @@ -69,7 +69,8 @@ Setting** g_miscStatData = (Setting**)0x11C6D50; char g_workingDir[MAX_PATH]; std::unordered_set<DWORD> jg_gameRadioSet; static float g_viewmodel_near = 0.f; - +bool mlcOverridden = false; +MediaLocationController* mlcOverride = nullptr; extern "C" { bool __cdecl JGSetViewmodelClipDistance(float value); float __cdecl JGGetViewmodelClipDistance(); @@ -601,6 +602,7 @@ namespace hk_DialogueTopicResponseManageHook { } } PrintLog("End Dialogue Dump"); + return NULL; } @@ -1817,6 +1819,17 @@ __declspec (noinline) void HandleDLLInterop() { } } + +MediaLocationController* __fastcall MLCOverrideHook(PlayerCharacter* player) +{ + if (mlcOverridden) + { + return mlcOverride; + } + return ThisStdCall<MediaLocationController*>(0x9698A0, player); + +} + float getHUDShakePower() { if (shakeRequests.empty()) { return 0.0f; @@ -1885,6 +1898,8 @@ void HandleFunctionPatches() { WriteRelCall(0x8752F2, UInt32(SetViewmodelFrustumHook)); + WriteRelCall(0x82FC95, (UInt32)MLCOverrideHook); + } float timer22 = 30.0; void HandleGameHooks() { diff --git a/JG/functions/fn_gameplay.h b/JG/functions/fn_gameplay.h index 303c1c2..d074af7 100644 --- a/JG/functions/fn_gameplay.h +++ b/JG/functions/fn_gameplay.h @@ -1,4 +1,5 @@ #pragma once +#include "ParamInfos.h" // Functions affecting gameplay DEFINE_COMMAND_PLUGIN(ToggleLevelUpMenu, , 0, 1, kParams_OneInt); DEFINE_COMMAND_PLUGIN(TogglePipBoy, , 0, 1, kParams_OneOptionalInt); @@ -58,6 +59,11 @@ DEFINE_COMMAND_PLUGIN(ClearCustomMapMarker, , 0, 0, NULL); DEFINE_COMMAND_PLUGIN(EjectCasing, , 0, 2, kParams_EjectCasing); DEFINE_COMMAND_PLUGIN(SetHUDShudderPower, , 0, 1, kParams_OneFloat); DEFINE_COMMAND_PLUGIN(GetHUDShudderPower, , 0, 0, NULL); +DEFINE_COMMAND_ALT_PLUGIN(SetMediaLocationControllerOverride, SetMLCOverride, , 0, 1, kParams_OneForm); +DEFINE_COMMAND_ALT_PLUGIN(ClearMediaLocationControllerOverride, ClearMLCOverride, , 0, 0, NULL); +DEFINE_COMMAND_ALT_PLUGIN(GetCasinoWinnings, , , 0, 1, kParams_OneCasino); +DEFINE_COMMAND_ALT_PLUGIN(SetCasinoWinnings, , , 0, 2, kParams_OneCasinoOneInt); + void(__cdecl* HandleActorValueChange)(ActorValueOwner* avOwner, int avCode, float oldVal, float newVal, ActorValueOwner* avOwner2) = (void(__cdecl*)(ActorValueOwner*, int, float, float, ActorValueOwner*))0x66EE50; bool(*Cmd_HighLightBodyPart)(COMMAND_ARGS) = (bool (*)(COMMAND_ARGS)) 0x5BB570; @@ -67,6 +73,84 @@ void(__cdecl* HUDMainMenu_UpdateVisibilityState)(signed int) = (void(__cdecl*)(s std::unordered_map<TESForm*, std::pair<float, float>> tempEffectMap; +bool __cdecl Cmd_SetCasinoWinnings_Execute(COMMAND_ARGS) +{ + TESCasino* casino; + SInt32 earnings; + if (ExtractArgs(EXTRACT_ARGS, &casino, &earnings) && casino) + { + + auto casinoRefId = casino->refID; + auto iter = PlayerCharacter::GetSingleton()->casinoDataList->Head(); + if (iter) { + do + { + if (auto casinoData = iter->data) + { + if (casinoData->casinoRefID == casinoRefId) + { + casinoData->earnings = earnings; + return true; + } + } + } while (iter = iter->next); + } + + auto casinoStats = (CasinoStats*)GameHeapAlloc(sizeof(CasinoStats)); + casinoStats->earningStage = 0; + casinoStats->earnings = earnings; + casinoStats->casinoRefID = casinoRefId; + PlayerCharacter::GetSingleton()->casinoDataList->Insert(casinoStats); + } + + return true; +} + +bool __cdecl Cmd_GetCasinoWinnings_Execute(COMMAND_ARGS) +{ + *result = 0; + TESCasino* casino = nullptr; + if (ExtractArgs(EXTRACT_ARGS, &casino) && casino) + { + auto casinoRefId = casino->refID; + auto iter = PlayerCharacter::GetSingleton()->casinoDataList->Head(); + if (!iter) return true; + do + { + if (auto casinoData = iter->data) + { + if (casinoData->casinoRefID == casinoRefId) + { + *result = casinoData->earnings; + break; + } + } + } while (iter = iter->next); + } + + return true; +} + +bool Cmd_ClearMediaLocationControllerOverride_Execute(COMMAND_ARGS) { + *result = 0; + mlcOverridden = false; + mlcOverride = nullptr; + *result = 1; + + return true; +} + +bool Cmd_SetMediaLocationControllerOverride_Execute(COMMAND_ARGS) { + *result = 0; + MediaLocationController* ctrl = NULL; + if (ExtractArgsEx(EXTRACT_ARGS_EX, &ctrl) && IS_TYPE(ctrl, MediaLocationController)) { + mlcOverridden = true; + mlcOverride = ctrl; + *result = 1; + } + return true; +} + bool Cmd_GetHUDShudderPower_Execute(COMMAND_ARGS) { *result = 0; UInt8 modId = scriptObj->GetModIndex(); diff --git a/nvse/nvse/GameForms.h b/nvse/nvse/GameForms.h index ce9b37f..54aebbf 100644 --- a/nvse/nvse/GameForms.h +++ b/nvse/nvse/GameForms.h @@ -5990,3 +5990,11 @@ enum EWhichListForm { eWhichListForm_FormList, eWhichListForm_Max, }; + +struct CasinoStats +{ + UInt32 casinoRefID; + SInt32 earnings; + UInt16 earningStage; + UInt8 gap0A[2]; +}; \ No newline at end of file diff --git a/nvse/nvse/GameObjects.h b/nvse/nvse/GameObjects.h index 2c58724..a7074f5 100644 --- a/nvse/nvse/GameObjects.h +++ b/nvse/nvse/GameObjects.h @@ -811,7 +811,7 @@ class PlayerCharacter : public Character { TESObjectREFR* lastExteriorDoor; // 604 void* unk608; // 608 void* unk60C; // 60C - void* unk610; // 610 + tList<CasinoStats>* casinoDataList; // 610 tList<TESCaravanCard>* caravanCards1; // 614 tList<TESCaravanCard>* caravanCards2; // 618 UInt32 unk61C[7]; // 61C diff --git a/nvse/nvse/ParamInfos.h b/nvse/nvse/ParamInfos.h index a4444c8..c4406c1 100644 --- a/nvse/nvse/ParamInfos.h +++ b/nvse/nvse/ParamInfos.h @@ -831,4 +831,15 @@ static ParamInfo kParams_EjectCasing[2] = { { "Target Node", kParamType_String, 1 }, { "Custom Casing Path", kParamType_String, 1 }, +}; + +static ParamInfo kParams_OneCasino[1] = +{ + { "Casino", kParamType_Casino, 1 }, +}; + +static ParamInfo kParams_OneCasinoOneInt[2] = +{ + { "Casino", kParamType_Casino, 1 }, + { "Earnings", kParamType_Integer, 1 }, }; \ No newline at end of file