diff --git a/.github/workflows/msbuild.yml b/.github/workflows/msbuild.yml index eae2b4b4..72e899e4 100644 --- a/.github/workflows/msbuild.yml +++ b/.github/workflows/msbuild.yml @@ -1,46 +1,25 @@ name: MSBuild on: - push: - branches: [ "master" ] pull_request: - branches: [ "master" ] + branches: + - "**" + types: + - opened + - synchronize + - reopened + paths-ignore: + - 'image/**' + - 'docs/**' + - '**/*.md' + workflow_dispatch: permissions: contents: read jobs: - build: - runs-on: windows-2022 - - strategy: - matrix: - configuration: [ Release ] - - env: - SOLUTION_FILE_PATH: . - BUILD_CONFIGURATION: ${{ matrix.configuration }} - - steps: - - name: Checkout repository (with submodules) - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v1.0.2 - - - name: Restore NuGet packages - run: nuget restore ${{ env.SOLUTION_FILE_PATH }} - - - name: Build ${{ matrix.configuration }} - run: msbuild /m /p:Configuration=${{ env.BUILD_CONFIGURATION }} ${{ env.SOLUTION_FILE_PATH }} - - - name: Upload ${{ matrix.configuration }} Artifacts - if: ${{ success() }} - uses: actions/upload-artifact@v4 - with: - name: FA2sp_${{ matrix.configuration }}_${{ github.sha }} - path: | - ${{ env.SOLUTION_FILE_PATH }}/${{ env.BUILD_CONFIGURATION }}/FA2sp.dll - ${{ env.SOLUTION_FILE_PATH }}/${{ env.BUILD_CONFIGURATION }}/FA2sp.pdb \ No newline at end of file + verify: + uses: ./.github/workflows/reusable-build-and-test.yml + with: + configuration: Release + run_tests: true \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..c9d02348 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,138 @@ +name: Release + +permissions: + contents: write + +on: + pull_request: + branches: + - "master" + types: + - closed + paths-ignore: + - 'image/**' + - 'docs/**' + - '**/*.md' + workflow_dispatch: + inputs: + tag_name: + description: 'Custom Release Tag (leave empty to use auto-version)' + required: false + default: '' + type: string + prerelease: + description: 'Is Pre-release' + required: false + default: false + type: boolean + +env: + VERSION_MAJOR: 1 + VERSION_MINOR: 6 + VERSION_PREFIX: "v" + +jobs: + build-and-test: + if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true) + uses: ./.github/workflows/reusable-build-and-test.yml + with: + configuration: Release + run_tests: true + + prepare-version: + needs: build-and-test + if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true) + runs-on: windows-2022 + outputs: + release_tag: ${{ steps.determine_version.outputs.release_tag }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Generate Build Number + if: ${{ !inputs.tag_name }} + id: buildnumber + uses: onyxmueller/build-tag-number@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + prefix: auto-version-${{ env.VERSION_MAJOR }}.${{ env.VERSION_MINOR }} + + - name: Determine Release Tag + id: determine_version + shell: pwsh + run: | + $customTag = "${{ inputs.tag_name }}" + if (-not [string]::IsNullOrWhiteSpace($customTag)) { + $tag = $customTag + } else { + $buildNum = [int]"${{ steps.buildnumber.outputs.build_number }}" + $revision = $buildNum - 1 + $tag = "${{ env.VERSION_PREFIX }}${{ env.VERSION_MAJOR }}.${{ env.VERSION_MINOR }}.$revision" + } + Add-Content -Path $env:GITHUB_OUTPUT -Value "release_tag=$tag" -Encoding utf8 + Write-Host "Determined Release Tag: $tag" + + package: + needs: [build-and-test, prepare-version] + if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true) + runs-on: windows-2022 + strategy: + matrix: + target: [YR, MO, RN] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + name: FA2sp_Release_${{ github.sha }} + path: bin_output + + - name: Package Target (${{ matrix.target }}) + shell: pwsh + run: | + ./Scripts/PackageRelease.ps1 -Target ${{ matrix.target }} -Version ${{ needs.prepare-version.outputs.release_tag }} -BinDir bin_output -OutputDir . + Add-Content -Path $env:GITHUB_ENV -Value "PACKAGE_NAME=FA2sp-${{ matrix.target }}-${{ needs.prepare-version.outputs.release_tag }}.zip" -Encoding utf8 + + - name: Upload Target Artifact + uses: actions/upload-artifact@v4 + with: + name: release-pkg-${{ matrix.target }} + path: ${{ env.PACKAGE_NAME }} + retention-days: 1 + + publish-release: + needs: [prepare-version, package] + if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true) + runs-on: windows-2022 + steps: + - name: Download all release target packages + uses: actions/download-artifact@v4 + with: + pattern: release-pkg-* + merge-multiple: true + path: release_assets + + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + name: FA2sp_Release_${{ github.sha }} + path: bin_output + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ needs.prepare-version.outputs.release_tag }} + name: FA2sp ${{ needs.prepare-version.outputs.release_tag }} + files: | + release_assets/*.zip + bin_output/FA2sp.dll + bin_output/FA2sp.pdb + target_commitish: ${{ github.sha }} + prerelease: ${{ inputs.prerelease || false }} + generate_release_notes: true diff --git a/.github/workflows/reusable-build-and-test.yml b/.github/workflows/reusable-build-and-test.yml new file mode 100644 index 00000000..d9b7a6c9 --- /dev/null +++ b/.github/workflows/reusable-build-and-test.yml @@ -0,0 +1,82 @@ +name: Reusable Build and Test + +on: + workflow_call: + inputs: + configuration: + description: 'Build Configuration (e.g. Release)' + required: false + default: 'Release' + type: string + run_tests: + description: 'Whether to run Unit Tests' + required: false + default: true + type: boolean + outputs: + artifact_name: + description: 'Name of the uploaded binary artifact' + value: ${{ jobs.build-and-test.outputs.artifact_name }} + +jobs: + build-and-test: + runs-on: windows-2022 + env: + SOLUTION_FILE_PATH: FA2sp.sln + CONFIGURATION: ${{ inputs.configuration }} + outputs: + artifact_name: FA2sp_${{ inputs.configuration }}_${{ github.sha }} + + steps: + - name: Checkout repository (with submodules) + uses: actions/checkout@v4 + with: + fetch-depth: 1 + submodules: recursive + lfs: true + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v1.0.2 + + - name: Restore NuGet packages + run: nuget restore ${{ env.SOLUTION_FILE_PATH }} + + - name: Build Program and UT (MSVC) + run: msbuild /m /p:Configuration="${{ env.CONFIGURATION }}" /p:Platform=x86 ${{ env.SOLUTION_FILE_PATH }} + + - name: Copy binaries to Supplementary + shell: pwsh + run: | + if (Test-Path "${{ env.CONFIGURATION }}/FA2sp.dll") { + Copy-Item -Force "${{ env.CONFIGURATION }}/FA2sp.dll" "Supplementary/FA2sp.dll" + } + if (Test-Path "${{ env.CONFIGURATION }}/FA2sp.UnitTest.dll") { + Copy-Item -Force "${{ env.CONFIGURATION }}/FA2sp.UnitTest.dll" "Supplementary/FA2sp.UnitTest.dll" + } + + - name: do UT + if: inputs.run_tests + shell: cmd + run: | + call Scripts\RunUnitTest.bat + + - name: Upload UT Results + if: always() && inputs.run_tests + uses: actions/upload-artifact@v4 + with: + name: FA2sp-UT-Results-${{ github.run_number }} + path: | + Supplementary/UnitTest.log + Supplementary/UnitTest.xml + Supplementary/syringe.log + retention-days: 7 + + - name: Upload Build Artifacts + if: success() + uses: actions/upload-artifact@v4 + with: + name: FA2sp_${{ env.CONFIGURATION }}_${{ github.sha }} + path: | + ${{ env.CONFIGURATION }}/FA2sp.dll + ${{ env.CONFIGURATION }}/FA2sp.pdb + retention-days: 7 diff --git a/.gitignore b/.gitignore index 85658862..783a8204 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,15 @@ MFC42 !Debug/Scintilla.lib /ida_mapping /migration + +/FA2sp.UnitTest/Debug/* +/FA2sp.UnitTest/Release/* +/FA2sp.UnitTest/FA2sp.UnitTest/* + +Supplementary/FA2sp.UnitTest.dll +Supplementary/FA2sp.dll +Supplementary/*.log +Supplementary/*.txt +Supplementary/UnitTest.xml +dist/ +bin_output/ diff --git a/.gitmodules b/.gitmodules index d7cbc11c..2375e84c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,4 +9,7 @@ url = https://github.com/ScintillaOrg/lexilla [submodule "scintilla"] path = scintilla - url = https://github.com/mirror/scintilla \ No newline at end of file + url = https://github.com/mirror/scintilla +[submodule "googletest"] + path = googletest + url = https://github.com/google/googletest.git diff --git a/Claude.md b/Claude.md new file mode 100644 index 00000000..4fe0819c --- /dev/null +++ b/Claude.md @@ -0,0 +1,5 @@ +# Instructions for Claude + +Must read [agents.md](agents.md). + +Always adhere strictly to the project rules, test verification mandates, and git constraints specified in [agents.md](agents.md). diff --git a/Codex.md b/Codex.md new file mode 100644 index 00000000..ff7ca203 --- /dev/null +++ b/Codex.md @@ -0,0 +1,5 @@ +# Instructions for Codex + +Must read [agents.md](agents.md). + +Always adhere strictly to the project rules, test verification mandates, and git constraints specified in [agents.md](agents.md). diff --git a/FA2sp.UnitTest/CINIOrderTrackerTest.cpp b/FA2sp.UnitTest/CINIOrderTrackerTest.cpp new file mode 100644 index 00000000..1dcb560d --- /dev/null +++ b/FA2sp.UnitTest/CINIOrderTrackerTest.cpp @@ -0,0 +1,888 @@ +#include "TestCommon.h" +#include +#include +#include +#include +#include + +static void LoadRawINI(CINIExt& ini, const std::string_view raw) +{ + ini.LoadINIExt(reinterpret_cast(const_cast(raw.data())), raw.size(), nullptr, true, true, false); +} + +TEST(CINIOrderTrackerTest, SectionOrderTrackingAndQueueSemantics) +{ + // Dummy pointer for tracking + CINI* pDummy = reinterpret_cast(0x12340000); + CINIOrderTracker::Clear(pDummy); + + CINIOrderTracker::RecordSection(pDummy, "Header"); + CINIOrderTracker::RecordSection(pDummy, "Map"); + CINIOrderTracker::RecordSection(pDummy, "Basic"); + + const auto* order = CINIOrderTracker::GetSectionOrder(pDummy); + ASSERT_NE(order, nullptr); + EXPECT_EQ(order->size(), 3u); + + // Remove Map, then re-record Map -> must be at the end! + CINIOrderTracker::RemoveSection(pDummy, "Map"); + EXPECT_EQ(order->size(), 2u); + + CINIOrderTracker::RecordSection(pDummy, "Map"); + EXPECT_EQ(order->size(), 3u); + + std::vector expected = { "Header", "Basic", "Map" }; + std::vector actual; + for (const auto& sec : *order) + { + actual.push_back(sec.GetString()); + } + EXPECT_EQ(actual, expected); + + CINIOrderTracker::Clear(pDummy); + EXPECT_EQ(CINIOrderTracker::GetSectionOrder(pDummy), nullptr); +} + +TEST(CINIOrderTrackerTest, KeyOrderTrackingAndQueueSemantics) +{ + CINI* pDummy = reinterpret_cast(0x12340000); + CINIOrderTracker::Clear(pDummy); + + CINIOrderTracker::RecordKey(pDummy, "Basic", "Name"); + CINIOrderTracker::RecordKey(pDummy, "Basic", "Player"); + CINIOrderTracker::RecordKey(pDummy, "Basic", "Theme"); + + const auto* kOrder = CINIOrderTracker::GetKeyOrder(pDummy, "Basic"); + ASSERT_NE(kOrder, nullptr); + EXPECT_EQ(kOrder->size(), 3u); + + // Remove Player, then re-record Player -> must be moved to the end! + CINIOrderTracker::RemoveKey(pDummy, "Basic", "Player"); + EXPECT_EQ(kOrder->size(), 2u); + + CINIOrderTracker::RecordKey(pDummy, "Basic", "Player"); + EXPECT_EQ(kOrder->size(), 3u); + + std::vector expected = { "Name", "Theme", "Player" }; + std::vector actual; + for (const auto& k : *kOrder) + { + actual.push_back(k.GetString()); + } + EXPECT_EQ(actual, expected); + + CINIOrderTracker::Clear(pDummy); +} + +static std::vector ToStringVector(const std::vector& vec) +{ + std::vector res; + res.reserve(vec.size()); + for (const auto& s : vec) + { + res.push_back(s.GetString()); + } + return res; +} + +TEST(CINIOrderTrackerTest, RealCINICrudKeyOperations) +{ + CINIExt ini; + const char* raw = R"( +[Basic] +Name=Island Wars +Player=Soviet +Theme=NoTheme +)"; + LoadRawINI(ini, raw); + + // Query initial state loaded through LoadINIExt + EXPECT_TRUE(ini.KeyExists("Basic", "Name")); + EXPECT_TRUE(ini.KeyExists("Basic", "Player")); + EXPECT_TRUE(ini.KeyExists("Basic", "Theme")); + EXPECT_STREQ(ini.GetString("Basic", "Name"), "Island Wars"); + EXPECT_STREQ(ini.GetString("Basic", "Player"), "Soviet"); + EXPECT_STREQ(ini.GetString("Basic", "Theme"), "NoTheme"); + + std::vector expectedKeys = { "Name", "Player", "Theme" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Basic")), expectedKeys); + + // 2. Modify / Update existing key in-place + // Updating existing key value must NOT alter its sequence order in SequencedKeyList + ini.WriteString("Basic", "Name", "Island Wars Remastered"); + + EXPECT_STREQ(ini.GetString("Basic", "Name"), "Island Wars Remastered"); + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Basic")), expectedKeys); + + // 3. Add new key + // Newly added key AltPalette must be appended to the tail + ini.WriteString("Basic", "AltPalette", "1"); + + EXPECT_STREQ(ini.GetString("Basic", "AltPalette"), "1"); + expectedKeys = { "Name", "Player", "Theme", "AltPalette" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Basic")), expectedKeys); + + // 4. Delete key + // Remove middle key 'Player' + bool deleted = ini.DeleteKey("Basic", "Player"); + EXPECT_TRUE(deleted); + + EXPECT_FALSE(ini.KeyExists("Basic", "Player")); + expectedKeys = { "Name", "Theme", "AltPalette" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Basic")), expectedKeys); + + // 5. Re-add deleted key -> must be pushed to tail of queue (FIFO queue semantics) + // Re-write Player="Allies" -> must now be placed at the end + ini.WriteString("Basic", "Player", "Allies"); + + EXPECT_STREQ(ini.GetString("Basic", "Player"), "Allies"); + expectedKeys = { "Name", "Theme", "AltPalette", "Player" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Basic")), expectedKeys); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, RealCINICrudSectionOperations) +{ + CINIExt ini; + const char* raw = R"( +[Header] +Version=4 + +[Map] +Width=50 +Height=50 + +[Lighting] +Ambient=1.0 + +[Units] +0=E1,Soviet,256,10,10 +)"; + LoadRawINI(ini, raw); + + // Query initial sections loaded through LoadINIExt + std::vector expectedSections = { "Header", "Map", "Lighting", "Units" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expectedSections); + + // 2. Modify within existing section (Section order must not be affected) + ini.WriteString("Map", "Width", "100"); + EXPECT_STREQ(ini.GetString("Map", "Width"), "100"); + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expectedSections); + + // 3. Add new section -> must be appended to tail + ini.WriteString("Triggers", "0", "TrigAlpha"); + + expectedSections = { "Header", "Map", "Lighting", "Units", "Triggers" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expectedSections); + + // 4. Delete section + bool deleted = ini.DeleteSection("Lighting"); + EXPECT_TRUE(deleted); + + EXPECT_FALSE(ini.SectionExists("Lighting")); + expectedSections = { "Header", "Map", "Units", "Triggers" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expectedSections); + + // 5. Re-add deleted section -> must be pushed to tail of queue! + ini.WriteString("Lighting", "Ambient", "1.5"); + + EXPECT_TRUE(ini.SectionExists("Lighting")); + EXPECT_STREQ(ini.GetString("Lighting", "Ambient"), "1.5"); + expectedSections = { "Header", "Map", "Units", "Triggers", "Lighting" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expectedSections); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, RealCINIAutoTrackingUntrackedItems) +{ + CINI ini; + CINIOrderTracker::Clear(&ini); + + // 1. Record initial section and keys + ini.WriteString("Alpha", "k1", "v1"); + ini.WriteString("Alpha", "k2", "v2"); + CINIOrderTracker::RecordSection(&ini, "Alpha"); + CINIOrderTracker::RecordKey(&ini, "Alpha", "k1"); + CINIOrderTracker::RecordKey(&ini, "Alpha", "k2"); + + ini.WriteString("Beta", "b1", "vb1"); + CINIOrderTracker::RecordSection(&ini, "Beta"); + CINIOrderTracker::RecordKey(&ini, "Beta", "b1"); + + // 2. Add untracked key to 'Alpha' without calling RecordKey + ini.WriteString("Alpha", "untracked_k3", "v3"); + + // 3. Add untracked section 'Gamma' without calling RecordSection + ini.WriteString("Gamma", "g1", "vg1"); + + // Pass 2 of GetEntries should discover untracked_k3, append it and register it + std::vector expectedKeys = { "k1", "k2", "untracked_k3" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Alpha", nullptr, true)), expectedKeys); + + // Verify it is now registered in the tracker's internal order + const auto* kOrder = CINIOrderTracker::GetKeyOrder(&ini, "Alpha"); + ASSERT_NE(kOrder, nullptr); + EXPECT_EQ(kOrder->size(), 3u); + + // Pass 2 of GetSections should discover Gamma, append it and register it + std::vector expectedSections = { "Alpha", "Beta", "Gamma" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini, true)), expectedSections); + + const auto* sOrder = CINIOrderTracker::GetSectionOrder(&ini); + ASSERT_NE(sOrder, nullptr); + EXPECT_EQ(sOrder->size(), 3u); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, RealCINIDeletedItemsOmittedDuringIterationEvenWithoutUntrack) +{ + CINIExt ini; + const char* raw = R"( +[Basic] +Name=MapName +GhostKey=WillBeDeleted +Theme=ThemeA + +[GhostSection] +dummy=1 +)"; + LoadRawINI(ini, raw); + + // Delete key in CINI, but deliberately do NOT call CINIOrderTracker::RemoveKey + ini.DeleteKey("Basic", "GhostKey"); + + // Delete section in CINI, but deliberately do NOT call CINIOrderTracker::RemoveSection + ini.DeleteSection("GhostSection"); + + // GetEntries should skip GhostKey because it no longer exists in CINI + std::vector expectedKeys = { "Name", "Theme" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Basic", nullptr, true)), expectedKeys); + + // GetSections should skip GhostSection because it no longer exists in CINI + std::vector expectedSections = { "Basic" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini, true)), expectedSections); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, RealCINIPreserveOrderTrueVsNaturalMapOrder) +{ + CINIExt ini; + // Input keys where insertion sequence deliberately differs from INISectionEntriesComparator + // INISectionEntriesComparator sorts: length ascending, then strcmp. + // Insertion order: "Zebra" (len 5), "Beta" (len 4), "Alpha" (len 5), "A" (len 1) + // Natural comparator order would be: "A" (len 1), "Beta" (len 4), "Alpha" (len 5), "Zebra" (len 5) + const char* raw = R"( +[TestSec] +Zebra=1 +Beta=2 +Alpha=3 +A=4 +)"; + LoadRawINI(ini, raw); + + // preserveOrder = true: preserves exact insertion order + std::vector preservedOrder = { "Zebra", "Beta", "Alpha", "A" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "TestSec", nullptr, true)), preservedOrder); + + // preserveOrder = false: returns natural map dictionary order + std::vector naturalOrder = { "A", "Beta", "Alpha", "Zebra" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "TestSec", nullptr, false)), naturalOrder); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, RealCINIComplexMapLifecycleSimulation) +{ + CINIExt ini; + // Step 1: Initial map loading simulation + const char* raw = R"( +[Basic] +Name=Battleground +Author=Tester +Player=Soviet +Theme=NoTheme + +[Map] +Theater=TEMPERATE +Size=0,0,50,50 +LocalSize=2,2,46,46 + +[Waypoints] +0=100 +1=200 +2=300 +3=400 + +[Units] +0=E1,Soviet,256,10,10 +1=HTNK,Soviet,256,12,12 +)"; + LoadRawINI(ini, raw); + + // Step 2 (Modify): Rename map and change theater + ini.WriteString("Basic", "Name", "Battleground 2.0"); + ini.WriteString("Map", "Theater", "SNOW"); + + // Step 3 (Delete): Delete waypoint 1 + ini.DeleteKey("Waypoints", "1"); + + // Step 4 (Add): Add waypoint 4 + ini.WriteString("Waypoints", "4", "500"); + + // Step 5 (Delete + Re-add): Move waypoint 0 to tail by deleting and re-adding + ini.DeleteKey("Waypoints", "0"); + ini.WriteString("Waypoints", "0", "150"); + + // Step 6 (Add Section): Add new [Triggers] section + ini.WriteString("Triggers", "0", "TriggerA"); + + // Step 7 (Delete Section): Delete [Units] section + ini.DeleteSection("Units"); + + // Step 8 (Verify complete final state): + // Sections order: Basic, Map, Waypoints, Triggers + std::vector expectedSections = { "Basic", "Map", "Waypoints", "Triggers" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini, true)), expectedSections); + + // Basic keys: Name, Author, Player, Theme (values updated, order intact) + std::vector expectedBasicKeys = { "Name", "Author", "Player", "Theme" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Basic", nullptr, true)), expectedBasicKeys); + EXPECT_STREQ(ini.GetString("Basic", "Name"), "Battleground 2.0"); + EXPECT_STREQ(ini.GetString("Basic", "Author"), "Tester"); + EXPECT_STREQ(ini.GetString("Basic", "Player"), "Soviet"); + EXPECT_STREQ(ini.GetString("Basic", "Theme"), "NoTheme"); + + // Map keys: Theater, Size, LocalSize + std::vector expectedMapKeys = { "Theater", "Size", "LocalSize" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Map", nullptr, true)), expectedMapKeys); + EXPECT_STREQ(ini.GetString("Map", "Theater"), "SNOW"); + + // Waypoints keys with adaptive sorting: 0, 2, 3, 4 (0 is adaptively sorted to the front instead of trailing at tail) + std::vector expectedWaypointKeys = { "0", "2", "3", "4" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Waypoints", nullptr, true)), expectedWaypointKeys); + EXPECT_STREQ(ini.GetString("Waypoints", "0"), "150"); + EXPECT_STREQ(ini.GetString("Waypoints", "2"), "300"); + EXPECT_STREQ(ini.GetString("Waypoints", "3"), "400"); + EXPECT_STREQ(ini.GetString("Waypoints", "4"), "500"); + + // Verify GetEntries returns exact key-value pairs in adaptive order + auto waypointEntries = CINIOrderTracker::GetEntries(&ini, "Waypoints", nullptr, true); + ASSERT_EQ(waypointEntries.size(), 4u); + EXPECT_STREQ(waypointEntries[0].Key, "0"); + EXPECT_STREQ(waypointEntries[0].Value, "150"); + EXPECT_STREQ(waypointEntries[1].Key, "2"); + EXPECT_STREQ(waypointEntries[1].Value, "300"); + EXPECT_STREQ(waypointEntries[2].Key, "3"); + EXPECT_STREQ(waypointEntries[2].Value, "400"); + EXPECT_STREQ(waypointEntries[3].Key, "4"); + EXPECT_STREQ(waypointEntries[3].Value, "500"); + + // Triggers keys: 0 + std::vector expectedTriggerKeys = { "0" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Triggers", nullptr, true)), expectedTriggerKeys); + EXPECT_STREQ(ini.GetString("Triggers", "0"), "TriggerA"); + + // Units section is gone + EXPECT_FALSE(ini.SectionExists("Units")); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, RealCINISameKeyDeletedAndReaddedHeadMiddleTailCombinations) +{ + CINIExt ini; + const char* sec = "KeyPermutations"; + const char* raw = R"( +[KeyPermutations] +K_Head=1 +K_Mid1=2 +K_Mid2=3 +K_Mid3=4 +K_Tail=5 +)"; + LoadRawINI(ini, raw); + + std::vector expected = { "K_Head", "K_Mid1", "K_Mid2", "K_Mid3", "K_Tail" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, sec)), expected); + + // --- Scenario A: Delete HEAD key (K_Head), verify, then re-add --- + EXPECT_TRUE(ini.DeleteKey(sec, "K_Head")); + EXPECT_FALSE(ini.KeyExists(sec, "K_Head")); + EXPECT_STREQ(ini.GetString(sec, "K_Head"), ""); + expected = { "K_Mid1", "K_Mid2", "K_Mid3", "K_Tail" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, sec)), expected); + + // Re-add K_Head -> must be placed at tail + ini.WriteString(sec, "K_Head", "1_new"); + EXPECT_TRUE(ini.KeyExists(sec, "K_Head")); + EXPECT_STREQ(ini.GetString(sec, "K_Head"), "1_new"); + expected = { "K_Mid1", "K_Mid2", "K_Mid3", "K_Tail", "K_Head" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, sec)), expected); + + // --- Scenario B: Delete MIDDLE key (K_Mid2), verify, then re-add --- + EXPECT_TRUE(ini.DeleteKey(sec, "K_Mid2")); + EXPECT_FALSE(ini.KeyExists(sec, "K_Mid2")); + expected = { "K_Mid1", "K_Mid3", "K_Tail", "K_Head" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, sec)), expected); + + // Re-add K_Mid2 -> must be placed at tail + ini.WriteString(sec, "K_Mid2", "3_new"); + EXPECT_STREQ(ini.GetString(sec, "K_Mid2"), "3_new"); + expected = { "K_Mid1", "K_Mid3", "K_Tail", "K_Head", "K_Mid2" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, sec)), expected); + + // --- Scenario C: Delete TAIL key (currently K_Mid2), verify, then re-add --- + EXPECT_TRUE(ini.DeleteKey(sec, "K_Mid2")); + EXPECT_FALSE(ini.KeyExists(sec, "K_Mid2")); + expected = { "K_Mid1", "K_Mid3", "K_Tail", "K_Head" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, sec)), expected); + + // Re-add K_Mid2 again (multi-cycle on the same key!) + ini.WriteString(sec, "K_Mid2", "3_v3"); + EXPECT_STREQ(ini.GetString(sec, "K_Mid2"), "3_v3"); + expected = { "K_Mid1", "K_Mid3", "K_Tail", "K_Head", "K_Mid2" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, sec)), expected); + + // --- Scenario D: Permutation delete Head, Mid, Tail, then re-add in reverse order --- + // Current order: ["K_Mid1" (Head), "K_Mid3" (Mid), "K_Tail" (Mid), "K_Head" (Mid), "K_Mid2" (Tail)] + // Delete Head ("K_Mid1") + ini.DeleteKey(sec, "K_Mid1"); + // Delete Mid ("K_Tail") + ini.DeleteKey(sec, "K_Tail"); + // Delete Tail ("K_Mid2") + ini.DeleteKey(sec, "K_Mid2"); + + // Remaining should be: ["K_Mid3", "K_Head"] + expected = { "K_Mid3", "K_Head" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, sec)), expected); + + // Re-add in reverse order: K_Mid2 (former tail), K_Tail (former mid), K_Mid1 (former head) + ini.WriteString(sec, "K_Mid2", "200"); + ini.WriteString(sec, "K_Tail", "300"); + ini.WriteString(sec, "K_Mid1", "100"); + + expected = { "K_Mid3", "K_Head", "K_Mid2", "K_Tail", "K_Mid1" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, sec)), expected); + + // Verify all values are retrievable correctly + EXPECT_STREQ(ini.GetString(sec, "K_Mid3"), "4"); + EXPECT_STREQ(ini.GetString(sec, "K_Head"), "1_new"); + EXPECT_STREQ(ini.GetString(sec, "K_Mid2"), "200"); + EXPECT_STREQ(ini.GetString(sec, "K_Tail"), "300"); + EXPECT_STREQ(ini.GetString(sec, "K_Mid1"), "100"); + + // --- Scenario E: Delete all remaining keys to empty, then re-populate --- + for (const auto& k : expected) + { + EXPECT_TRUE(ini.DeleteKey(sec, k.c_str())); + } + EXPECT_EQ(ini.GetKeyCount(sec), 0); + EXPECT_TRUE(CINIOrderTracker::GetKeyNames(&ini, sec).empty()); + + // Re-add in a new order + ini.WriteString(sec, "Reborn_A", "A"); + ini.WriteString(sec, "Reborn_B", "B"); + expected = { "Reborn_A", "Reborn_B" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, sec)), expected); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, RealCINISameSectionDeletedAndReaddedHeadMiddleTailCombinations) +{ + CINIExt ini; + const char* raw = R"( +[Sec_Head] +val=0 + +[Sec_Mid1] +val=1 + +[Sec_Mid2] +val=2 + +[Sec_Mid3] +val=3 + +[Sec_Tail] +val=4 +)"; + LoadRawINI(ini, raw); + const std::vector initSections = { "Sec_Head", "Sec_Mid1", "Sec_Mid2", "Sec_Mid3", "Sec_Tail" }; + + std::vector expected = initSections; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expected); + + // --- Scenario A: Delete HEAD section ("Sec_Head"), verify, then re-add --- + EXPECT_TRUE(ini.DeleteSection("Sec_Head")); + EXPECT_FALSE(ini.SectionExists("Sec_Head")); + expected = { "Sec_Mid1", "Sec_Mid2", "Sec_Mid3", "Sec_Tail" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expected); + + // Re-add "Sec_Head" -> must move to tail + ini.WriteString("Sec_Head", "new_val", "100"); + EXPECT_TRUE(ini.SectionExists("Sec_Head")); + EXPECT_STREQ(ini.GetString("Sec_Head", "new_val"), "100"); + expected = { "Sec_Mid1", "Sec_Mid2", "Sec_Mid3", "Sec_Tail", "Sec_Head" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expected); + + // --- Scenario B: Delete MIDDLE section ("Sec_Mid2"), verify, then re-add --- + EXPECT_TRUE(ini.DeleteSection("Sec_Mid2")); + EXPECT_FALSE(ini.SectionExists("Sec_Mid2")); + expected = { "Sec_Mid1", "Sec_Mid3", "Sec_Tail", "Sec_Head" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expected); + + // Re-add "Sec_Mid2" -> must move to tail + ini.WriteString("Sec_Mid2", "mid_val", "200"); + expected = { "Sec_Mid1", "Sec_Mid3", "Sec_Tail", "Sec_Head", "Sec_Mid2" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expected); + + // --- Scenario C: Delete TAIL section (currently "Sec_Mid2"), verify, then re-add --- + EXPECT_TRUE(ini.DeleteSection("Sec_Mid2")); + EXPECT_FALSE(ini.SectionExists("Sec_Mid2")); + expected = { "Sec_Mid1", "Sec_Mid3", "Sec_Tail", "Sec_Head" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expected); + + // Re-add "Sec_Mid2" again (multi-cycle on the same section!) + ini.WriteString("Sec_Mid2", "mid_val_v3", "300"); + expected = { "Sec_Mid1", "Sec_Mid3", "Sec_Tail", "Sec_Head", "Sec_Mid2" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expected); + + // --- Scenario D: Permutation delete Head, Mid, Tail, then re-add in reverse order --- + // Current sections: ["Sec_Mid1" (Head), "Sec_Mid3" (Mid), "Sec_Tail" (Mid), "Sec_Head" (Mid), "Sec_Mid2" (Tail)] + // Delete Head ("Sec_Mid1") + ini.DeleteSection("Sec_Mid1"); + // Delete Mid ("Sec_Tail") + ini.DeleteSection("Sec_Tail"); + // Delete Tail ("Sec_Mid2") + ini.DeleteSection("Sec_Mid2"); + + // Remaining should be: ["Sec_Mid3", "Sec_Head"] + expected = { "Sec_Mid3", "Sec_Head" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expected); + + // Re-add in reverse order: Sec_Mid2 (former tail), Sec_Tail (former mid), Sec_Mid1 (former head) + ini.WriteString("Sec_Mid2", "v", "1"); + ini.WriteString("Sec_Tail", "v", "2"); + ini.WriteString("Sec_Mid1", "v", "3"); + + expected = { "Sec_Mid3", "Sec_Head", "Sec_Mid2", "Sec_Tail", "Sec_Mid1" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expected); + + // --- Scenario E: Delete all sections to empty, then re-populate in reverse --- + for (const auto& secName : expected) + { + EXPECT_TRUE(ini.DeleteSection(secName.c_str())); + } + EXPECT_TRUE(CINIOrderTracker::GetSectionNames(&ini).empty()); + + // Re-populate in reversed order + ini.WriteString("Z_Sec", "k", "1"); + ini.WriteString("A_Sec", "k", "2"); + + expected = { "Z_Sec", "A_Sec" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expected); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, RealCINIInterleavedKeyAndSectionDeleteReaddStressTest) +{ + CINIExt ini; + // Create 3 sections, each with 3 keys: Head, Mid, Tail + const char* raw = R"( +[Section_1] +Key_H=val_H +Key_M=val_M +Key_T=val_T + +[Section_2] +Key_H=val_H +Key_M=val_M +Key_T=val_T + +[Section_3] +Key_H=val_H +Key_M=val_M +Key_T=val_T +)"; + LoadRawINI(ini, raw); + + // Step 1: In Section_1 (Head section), delete Head key Key_H and re-add it + ini.DeleteKey("Section_1", "Key_H"); + ini.WriteString("Section_1", "Key_H", "val_H_reborn"); + + std::vector expectedKeysSec1 = { "Key_M", "Key_T", "Key_H" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Section_1")), expectedKeysSec1); + EXPECT_STREQ(ini.GetString("Section_1", "Key_H"), "val_H_reborn"); + + // Step 2: Delete Section_1 entirely (Head section deleted) + ini.DeleteSection("Section_1"); + + std::vector expectedSections = { "Section_2", "Section_3" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expectedSections); + + // Step 3: In Section_2 (now Head section), delete Tail key Key_T, then delete Mid key Key_M + ini.DeleteKey("Section_2", "Key_T"); + ini.DeleteKey("Section_2", "Key_M"); + + std::vector expectedKeysSec2 = { "Key_H" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Section_2")), expectedKeysSec2); + + // Re-add Key_M then Key_T in Section_2 + ini.WriteString("Section_2", "Key_M", "val_M_new"); + ini.WriteString("Section_2", "Key_T", "val_T_new"); + + expectedKeysSec2 = { "Key_H", "Key_M", "Key_T" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Section_2")), expectedKeysSec2); + + // Step 4: Re-add Section_1 (must now become the TAIL section) + ini.WriteString("Section_1", "Alpha", "1"); + ini.WriteString("Section_1", "Beta", "2"); + + expectedSections = { "Section_2", "Section_3", "Section_1" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expectedSections); + + expectedKeysSec1 = { "Alpha", "Beta" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Section_1")), expectedKeysSec1); + + // Step 5: Delete Section_3 (Middle section deleted) + ini.DeleteSection("Section_3"); + + expectedSections = { "Section_2", "Section_1" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expectedSections); + + // Step 6: Re-add Section_3 (becomes TAIL section) + ini.WriteString("Section_3", "Gamma", "3"); + + expectedSections = { "Section_2", "Section_1", "Section_3" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetSectionNames(&ini)), expectedSections); + + // Step 7: Verify all entries across all sections + auto sec2Entries = CINIOrderTracker::GetEntries(&ini, "Section_2"); + ASSERT_EQ(sec2Entries.size(), 3u); + EXPECT_STREQ(sec2Entries[0].Key, "Key_H"); + EXPECT_STREQ(sec2Entries[0].Value, "val_H"); + EXPECT_STREQ(sec2Entries[1].Key, "Key_M"); + EXPECT_STREQ(sec2Entries[1].Value, "val_M_new"); + EXPECT_STREQ(sec2Entries[2].Key, "Key_T"); + EXPECT_STREQ(sec2Entries[2].Value, "val_T_new"); + + auto sec1Entries = CINIOrderTracker::GetEntries(&ini, "Section_1"); + ASSERT_EQ(sec1Entries.size(), 2u); + EXPECT_STREQ(sec1Entries[0].Key, "Alpha"); + EXPECT_STREQ(sec1Entries[0].Value, "1"); + EXPECT_STREQ(sec1Entries[1].Key, "Beta"); + EXPECT_STREQ(sec1Entries[1].Value, "2"); + + auto sec3Entries = CINIOrderTracker::GetEntries(&ini, "Section_3"); + ASSERT_EQ(sec3Entries.size(), 1u); + EXPECT_STREQ(sec3Entries[0].Key, "Gamma"); + EXPECT_STREQ(sec3Entries[0].Value, "3"); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, AdaptiveSortingNumericKeysPreserveNaturalOrderOnDeleteAndReadd) +{ + CINIExt ini; + const char* raw = R"( +[Units] +0=Unit_Zero +1=Unit_One +2=Unit_Two +3=Unit_Three +)"; + LoadRawINI(ini, raw); + + std::vector expectedInitial = { "0", "1", "2", "3" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Units", nullptr, true)), expectedInitial); + + // Case 1: Head deletion and re-add of "0" + // Without adaptive sorting, "0" would end up at the tail: {"1", "2", "3", "0"} + // With adaptive sorting, "0" must return to the front: {"0", "1", "2", "3"} + ini.DeleteKey("Units", "0"); + ini.WriteString("Units", "0", "Unit_Zero_Readded"); + + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Units", nullptr, true)), expectedInitial); + auto entries0 = CINIOrderTracker::GetEntries(&ini, "Units", nullptr, true); + ASSERT_EQ(entries0.size(), 4u); + EXPECT_STREQ(entries0[0].Key, "0"); + EXPECT_STREQ(entries0[0].Value, "Unit_Zero_Readded"); + + // Case 2: Middle deletion and re-add of "2" + ini.DeleteKey("Units", "2"); + ini.WriteString("Units", "2", "Unit_Two_Readded"); + + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Units", nullptr, true)), expectedInitial); + auto entries2 = CINIOrderTracker::GetEntries(&ini, "Units", nullptr, true); + EXPECT_STREQ(entries2[2].Key, "2"); + EXPECT_STREQ(entries2[2].Value, "Unit_Two_Readded"); + + // Case 3: Tail deletion and re-add of "3" + ini.DeleteKey("Units", "3"); + ini.WriteString("Units", "3", "Unit_Three_Readded"); + + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Units", nullptr, true)), expectedInitial); + + // Case 4: Delete multiple keys ("1" and "0"), re-add in reverse order ("1" then "0") + ini.DeleteKey("Units", "1"); + ini.DeleteKey("Units", "0"); + + ini.WriteString("Units", "1", "Unit_One_V2"); + ini.WriteString("Units", "0", "Unit_Zero_V2"); + + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Units", nullptr, true)), expectedInitial); + auto entriesMulti = CINIOrderTracker::GetEntries(&ini, "Units", nullptr, true); + EXPECT_STREQ(entriesMulti[0].Key, "0"); + EXPECT_STREQ(entriesMulti[0].Value, "Unit_Zero_V2"); + EXPECT_STREQ(entriesMulti[1].Key, "1"); + EXPECT_STREQ(entriesMulti[1].Value, "Unit_One_V2"); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, AdaptiveSortingWestwoodNumericComparatorMultiDigit) +{ + CINIExt ini; + // Multi-digit numbers written in raw INI in intentionally scrambled order + // "100", "10", "0", "9", "1", "11", "8", "99" + // In ASCII alphabetical sort: "0", "1", "10", "100", "11", "8", "9", "99" (WRONG for game engine) + // In Westwood numeric sort: "0", "1", "8", "9", "10", "11", "99", "100" (CORRECT) + const char* raw = R"( +[Waypoints] +100=pos_100 +10=pos_10 +0=pos_0 +9=pos_9 +1=pos_1 +11=pos_11 +8=pos_8 +99=pos_99 +)"; + LoadRawINI(ini, raw); + + std::vector expectedWestwoodOrder = { "0", "1", "8", "9", "10", "11", "99", "100" }; + auto actualKeys = ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Waypoints", nullptr, true)); + EXPECT_EQ(actualKeys, expectedWestwoodOrder); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, AdaptiveSortingMixedSectionPreservesTextHeaderAndOrdersNumericArray) +{ + CINIExt ini; + // Simulate Script / TaskForce section like [01000001] + // Header properties: Name, Group + // Numeric action/member entries: 0, 1, 2 + const char* raw = R"( +[01000001] +Name=Assault Squad +Group=-1 +0=1,E1 +1=2,HTK +2=1,APOC +)"; + LoadRawINI(ini, raw); + + const char* sec = "01000001"; + // Delete action "0", then re-add "0" -> should stay right after headers and before "1" + ini.DeleteKey(sec, "0"); + ini.WriteString(sec, "0", "1,E1_New"); + + std::vector expectedOrder = { "Name", "Group", "0", "1", "2" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, sec, nullptr, true)), expectedOrder); + + auto entries = CINIOrderTracker::GetEntries(&ini, sec, nullptr, true); + ASSERT_EQ(entries.size(), 5u); + EXPECT_STREQ(entries[0].Key, "Name"); + EXPECT_STREQ(entries[0].Value, "Assault Squad"); + EXPECT_STREQ(entries[1].Key, "Group"); + EXPECT_STREQ(entries[1].Value, "-1"); + EXPECT_STREQ(entries[2].Key, "0"); + EXPECT_STREQ(entries[2].Value, "1,E1_New"); + EXPECT_STREQ(entries[3].Key, "1"); + EXPECT_STREQ(entries[3].Value, "2,HTK"); + EXPECT_STREQ(entries[4].Key, "2"); + EXPECT_STREQ(entries[4].Value, "1,APOC"); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, AdaptiveSortingUntrackedKeyWithoutManualRecordKeyAutoSorted) +{ + CINIExt ini; + const char* raw = R"( +[Units] +1=Unit_One +2=Unit_Two +3=Unit_Three +)"; + LoadRawINI(ini, raw); + + // Call WriteString alone without ANY manual RecordKey call + ini.WriteString("Units", "0", "Unit_Zero_Auto"); + + // Pass 2 should auto-discover "0", Pass 3 should adaptively sort it to index 0 + std::vector expectedOrder = { "0", "1", "2", "3" }; + EXPECT_EQ(ToStringVector(CINIOrderTracker::GetKeyNames(&ini, "Units", nullptr, true)), expectedOrder); + + auto entries = CINIOrderTracker::GetEntries(&ini, "Units", nullptr, true); + ASSERT_EQ(entries.size(), 4u); + EXPECT_STREQ(entries[0].Key, "0"); + EXPECT_STREQ(entries[0].Value, "Unit_Zero_Auto"); + EXPECT_STREQ(entries[1].Key, "1"); + EXPECT_STREQ(entries[1].Value, "Unit_One"); + + CINIOrderTracker::Clear(&ini); +} + +TEST(CINIOrderTrackerTest, DynamicFunctionPointerSwitchingAdaptiveSortingToggle) +{ + CINIExt ini; + CINIOrderTracker::Clear(&ini); + + // 1. Disable AdaptiveSorting -> function pointer switched to NoOp + CINIExt::SetAdaptiveSorting(false); + EXPECT_FALSE(CINIExt::GetAdaptiveSorting()); + + ini.WriteString("TestSec", "Key_NoOp", "Value1"); + // Since tracker was bypassed via NoOp function pointer, GetKeyOrder should be empty / null + const auto* kOrderDisabled = CINIOrderTracker::GetKeyOrder(&ini, "TestSec"); + EXPECT_TRUE(kOrderDisabled == nullptr || kOrderDisabled->empty()); + + // 2. Enable AdaptiveSorting -> function pointer switched to real tracker + CINIExt::SetAdaptiveSorting(true); + EXPECT_TRUE(CINIExt::GetAdaptiveSorting()); + + ini.WriteString("TestSec", "Key_Tracked", "Value2"); + const auto* kOrderEnabled = CINIOrderTracker::GetKeyOrder(&ini, "TestSec"); + ASSERT_NE(kOrderEnabled, nullptr); + EXPECT_EQ(kOrderEnabled->size(), 1u); + EXPECT_STREQ(kOrderEnabled->GetOrder().front().GetString(), "Key_Tracked"); + + // 3. Test DeleteKey with NoOp vs Real + CINIExt::SetAdaptiveSorting(false); + ini.DeleteKey("TestSec", "Key_Tracked"); + // With NoOp, RemoveKey was bypassed, so tracker still retains the key + EXPECT_EQ(kOrderEnabled->size(), 1u); + + // Switch back to enabled + CINIExt::SetAdaptiveSorting(true); + // Add again and delete with real tracker active + ini.WriteString("TestSec", "Key_Tracked", "Value2_New"); + EXPECT_EQ(kOrderEnabled->size(), 1u); + ini.DeleteKey("TestSec", "Key_Tracked"); + // With real tracker, RemoveKey removed it! + EXPECT_TRUE(kOrderEnabled->empty()); + + CINIOrderTracker::Clear(&ini); + // Restore default state + CINIExt::SetAdaptiveSorting(true); + CINIExt::SetKeepSectionSorting(true); +} + diff --git a/FA2sp.UnitTest/FA2sp.UnitTest.vcxproj b/FA2sp.UnitTest/FA2sp.UnitTest.vcxproj new file mode 100644 index 00000000..679aab23 --- /dev/null +++ b/FA2sp.UnitTest/FA2sp.UnitTest.vcxproj @@ -0,0 +1,122 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + 17.0 + Win32Proj + {C9F18204-74EE-47D5-816C-5198901E278F} + FA2spUnitTest + 10.0 + + + + DynamicLibrary + true + v143 + MultiByte + + + DynamicLibrary + false + v143 + false + MultiByte + + + + + + + + + + + + + + + FA2sp.UnitTest + false + $(ProjectDir)..\Debug\ + + + FA2sp.UnitTest + false + $(ProjectDir)..\Release\ + + + + Level3 + true + WIN32;_CRT_SECURE_NO_WARNINGS;_X86_;_DEBUG;NOMINMAX;SYR_VER=2;_WINDLL;%(PreprocessorDefinitions) + true + $(ProjectDir)..\googletest\googletest\include;$(ProjectDir)..\googletest\googletest;$(ProjectDir)..\FA2pp;$(ProjectDir)..\FA2sp;$(ProjectDir)..\MFC42\include;%(AdditionalIncludeDirectories) + stdcpp20 + MultiThreadedDebug + Sync + true + 4065;4530;4731;4244;4114;4172;4018;4390;4091;6269;28159;26812;28251;26495; + /source-charset:gbk %(AdditionalOptions) + + + Console + true + uxtheme.lib;windowscodecs.lib;legacy_stdio_definitions.lib;%(AdditionalDependencies) + /SAFESEH:NO %(AdditionalOptions) + + + copy /Y "$(TargetPath)" "$(ProjectDir)..\Supplementary\$(TargetFileName)" + + + + + Level3 + true + false + true + WIN32;_CRT_SECURE_NO_WARNINGS;_X86_;NDEBUG;NOMINMAX;SYR_VER=2;_WINDLL;%(PreprocessorDefinitions) + true + $(ProjectDir)..\googletest\googletest\include;$(ProjectDir)..\googletest\googletest;$(ProjectDir)..\FA2pp;$(ProjectDir)..\FA2sp;$(ProjectDir)..\MFC42\include;%(AdditionalIncludeDirectories) + stdcpp20 + MultiThreaded + Sync + MaxSpeed + true + 4065;4530;4731;4244;4114;4172;4018;4390;4091;6269;28159;26812;28251;26495; + /source-charset:gbk %(AdditionalOptions) + + + Console + true + true + true + uxtheme.lib;windowscodecs.lib;legacy_stdio_definitions.lib;%(AdditionalDependencies) + /SAFESEH:NO %(AdditionalOptions) + + + copy /Y "$(TargetPath)" "$(ProjectDir)..\Supplementary\$(TargetFileName)" + + + + + + + + + + + + + + + + diff --git a/FA2sp.UnitTest/SequencedKeyListTest.cpp b/FA2sp.UnitTest/SequencedKeyListTest.cpp new file mode 100644 index 00000000..d1e725e7 --- /dev/null +++ b/FA2sp.UnitTest/SequencedKeyListTest.cpp @@ -0,0 +1,113 @@ +#include "TestCommon.h" +#include +#include +#include + +TEST(SequencedKeyListTest, PreservesInsertionOrder) +{ + SequencedKeyList list; + EXPECT_TRUE(list.Empty()); + EXPECT_EQ(list.size(), 0u); + + list.Add("Alpha"); + list.Add("Beta"); + list.Add("Gamma"); + + EXPECT_FALSE(list.Empty()); + EXPECT_EQ(list.size(), 3u); + + std::vector expected = { "Alpha", "Beta", "Gamma" }; + std::vector actual; + for (const auto& key : list) + { + actual.push_back(key.GetString()); + } + + EXPECT_EQ(actual, expected); +} + +TEST(SequencedKeyListTest, IgnoresDuplicateAdditions) +{ + SequencedKeyList list; + list.Add("One"); + list.Add("Two"); + list.Add("One"); // duplicate + + EXPECT_EQ(list.size(), 2u); + + std::vector expected = { "One", "Two" }; + std::vector actual; + for (const auto& key : list) + { + actual.push_back(key.GetString()); + } + + EXPECT_EQ(actual, expected); +} + +TEST(SequencedKeyListTest, ReAddAfterRemovePushedToTailQueueSemantics) +{ + // Verification of FIFO queue semantics: + // If an item is deleted and added back later, it must be queued at the tail! + SequencedKeyList list; + list.Add("Item1"); + list.Add("Item2"); + list.Add("Item3"); + + // Remove middle item + list.Remove("Item2"); + EXPECT_EQ(list.size(), 2u); + EXPECT_FALSE(list.Contains("Item2")); + + // Re-add Item2 + list.Add("Item2"); + EXPECT_EQ(list.size(), 3u); + EXPECT_TRUE(list.Contains("Item2")); + + // Sequence MUST now be: Item1, Item3, Item2 + std::vector expected = { "Item1", "Item3", "Item2" }; + std::vector actual; + for (const auto& key : list) + { + actual.push_back(key.GetString()); + } + + EXPECT_EQ(actual, expected); +} + +TEST(SequencedKeyListTest, RemoveHeadAndTail) +{ + SequencedKeyList list; + list.Add("Head"); + list.Add("Mid"); + list.Add("Tail"); + + // Remove Head + list.Remove("Head"); + EXPECT_FALSE(list.Contains("Head")); + EXPECT_EQ(list.size(), 2u); + + // Remove Tail + list.Remove("Tail"); + EXPECT_FALSE(list.Contains("Tail")); + EXPECT_EQ(list.size(), 1u); + + EXPECT_STREQ((*list.begin()).GetString(), "Mid"); +} + +TEST(SequencedKeyListTest, ClearResetsState) +{ + SequencedKeyList list; + for (int i = 0; i < 50; ++i) + { + char buf[32]; + sprintf_s(buf, "Key_%d", i); + list.Add(buf); + } + EXPECT_EQ(list.size(), 50u); + + list.Clear(); + EXPECT_TRUE(list.Empty()); + EXPECT_EQ(list.size(), 0u); + EXPECT_FALSE(list.Contains("Key_0")); +} diff --git a/FA2sp.UnitTest/TestCommon.h b/FA2sp.UnitTest/TestCommon.h new file mode 100644 index 00000000..7975c2e5 --- /dev/null +++ b/FA2sp.UnitTest/TestCommon.h @@ -0,0 +1,6 @@ +#pragma once + +// Must include MFC headers before any Windows.h or GoogleTest headers +#include +#include +#include diff --git a/FA2sp.UnitTest/TestStubs.cpp b/FA2sp.UnitTest/TestStubs.cpp new file mode 100644 index 00000000..8398405b --- /dev/null +++ b/FA2sp.UnitTest/TestStubs.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// Logger stubs +FILE* Logger::pFile = nullptr; +bool Logger::bInitialized = false; + +// ExtConfigs stubs +bool ExtConfigs::AllowIncludes = false; +bool ExtConfigs::AllowInherits = false; +bool ExtConfigs::AllowPlusEqual = false; +bool ExtConfigs::SaveMap_KeepComments = false; +bool ExtConfigs::IncludeType = false; +bool ExtConfigs::InheritType = false; +bool ExtConfigs::SaveMap_PreserveINISorting = true; +bool ExtConfigs::SaveMap_AdaptiveSorting = true; +bool ExtConfigs::UTF8Support_InferEncoding = true; + +// CMapDataExt stubs +bool CMapDataExt::IsLoadingMapFile = false; +bool CMapDataExt::IsUTF8File = false; +std::unordered_map CMapDataExt::TileSetOriginSetNames[6]; +std::vector CMapDataExt::MapIniSectionSorting; +FMap> CMapDataExt::MapInlineComments; +FMap> CMapDataExt::MapFrontlineComments; +FMap CMapDataExt::MapInsectionComments; +FMap CMapDataExt::MapFrontsectionComments; + +ppmfc::CString CMapDataExt::GetAvailableIndex(EIndexType type) +{ + return ""; +} + +// CLoadingExt stubs +void* CLoadingExt::ReadWholeFile(char const* filename, unsigned long* pDwSize, bool fa2path, bool useCache) +{ + return nullptr; +} + +// STDHelpers stubs needed by Hooks.INI.cpp +FileEncoding STDHelpers::GetFileEncoding(const uint8_t* data, size_t size) +{ + if (!data || size == 0) + return FileEncoding::ANSI; + if (size >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF) + return FileEncoding::UTF8_BOM; + return FileEncoding::ANSI; +} + +std::vector STDHelpers::SplitString(const ppmfc::CString& pSource, const char* pSplit) +{ + std::vector ret; + if (pSplit == nullptr || pSource.GetLength() == 0) + return ret; + int nIdx = 0; + while (true) + { + int nPos = pSource.Find(pSplit, nIdx); + if (nPos == -1) + break; + if (nPos >= nIdx) + ret.push_back(pSource.Mid(nIdx, nPos - nIdx)); + nIdx = nPos + strlen(pSplit); + } + ret.push_back(pSource.Mid(nIdx)); + return ret; +} + +std::vector STDHelpers::SplitString(const ppmfc::CString& pSource, size_t nth, const char* pSplit) +{ + std::vector ret = SplitString(pSource, pSplit); + while (ret.size() <= nth) + { + ret.push_back(""); + } + return ret; +} + +bool STDHelpers::IsNoneOrEmpty(const char* pSource) +{ + return !pSource || *pSource == '\0' || _stricmp(pSource, "") == 0; +} + diff --git a/FA2sp.UnitTest/main.cpp b/FA2sp.UnitTest/main.cpp new file mode 100644 index 00000000..8301090c --- /dev/null +++ b/FA2sp.UnitTest/main.cpp @@ -0,0 +1,98 @@ +#include "TestCommon.h" +#include +#include +#include +#include +#include + +void SetupLogAndConsole() +{ + // Try to attach to parent console or allocate a new one + if (!AttachConsole(ATTACH_PARENT_PROCESS)) + { + AllocConsole(); + } + + // Redirect stdout and stderr to UnitTest.log so test reports can be printed and inspected + FILE* fpLog = nullptr; + freopen_s(&fpLog, "UnitTest.log", "w", stdout); + freopen_s(&fpLog, "UnitTest.log", "a", stderr); +} + +// Hook 0x41FAD0 (CFinalSunApp::InitInstance in FA2) via Syringe DEFINE_HOOK +// At this point, CRT and MFC are fully initialized and safe to use. +DEFINE_HOOK(41FAD0, UnitTest_InitInstance, 8) +{ + SetupLogAndConsole(); + + printf("=======================================================\n"); + printf(" FA2sp GoogleTest In-Process Test Runner Initiated \n"); + printf("=======================================================\n\n"); + + int rawArgc = 0; + LPWSTR* szArglist = CommandLineToArgvW(GetCommandLineW(), &rawArgc); + std::vector argStorage; + bool hasXmlOutput = false; + + if (szArglist && rawArgc > 0) + { + argStorage.reserve(rawArgc + 2); + for (int i = 0; i < rawArgc; ++i) + { + int sizeNeeded = WideCharToMultiByte(CP_ACP, 0, szArglist[i], -1, NULL, 0, NULL, NULL); + std::string argStr(sizeNeeded, 0); + WideCharToMultiByte(CP_ACP, 0, szArglist[i], -1, &argStr[0], sizeNeeded, NULL, NULL); + if (!argStr.empty() && argStr.back() == '\0') + { + argStr.pop_back(); + } + if (argStr.find("--gtest_output=") != std::string::npos) + { + hasXmlOutput = true; + } + argStorage.push_back(std::move(argStr)); + } + LocalFree(szArglist); + } + else + { + argStorage.push_back("FinalAlert2YR.dat"); + } + + if (!hasXmlOutput) + { + argStorage.push_back("--gtest_output=xml:UnitTest.xml"); + } + + std::vector argvPointers; + argvPointers.reserve(argStorage.size() + 1); + for (auto& s : argStorage) + { + argvPointers.push_back(s.data()); + } + argvPointers.push_back(nullptr); + + int argc = static_cast(argStorage.size()); + char** argv = argvPointers.data(); + + testing::InitGoogleTest(&argc, argv); + + int res = RUN_ALL_TESTS(); + + printf("\n=======================================================\n"); + printf(" Tests completed with exit code: %d\n", res); + printf("=======================================================\n\n"); + fflush(stdout); + fflush(stderr); + fclose(stdout); + fclose(stderr); + + // Exit immediately so host FA2 does not initialize GUI windows + ExitProcess(static_cast(res)); + return 0; +} + +BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) +{ + return TRUE; +} diff --git a/FA2sp.sln b/FA2sp.sln index 857e5d09..a831b875 100644 --- a/FA2sp.sln +++ b/FA2sp.sln @@ -1,10 +1,11 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31423.177 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FA2sp", "FA2sp.vcxproj", "{3BBEA2D2-97DA-4B96-9516-36A6C8D3B4A3}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FA2sp.UnitTest", "FA2sp.UnitTest\FA2sp.UnitTest.vcxproj", "{C9F18204-74EE-47D5-816C-5198901E278F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 @@ -15,6 +16,10 @@ Global {3BBEA2D2-97DA-4B96-9516-36A6C8D3B4A3}.Debug|x86.Build.0 = Debug|Win32 {3BBEA2D2-97DA-4B96-9516-36A6C8D3B4A3}.Release|x86.ActiveCfg = Release|Win32 {3BBEA2D2-97DA-4B96-9516-36A6C8D3B4A3}.Release|x86.Build.0 = Release|Win32 + {C9F18204-74EE-47D5-816C-5198901E278F}.Debug|x86.ActiveCfg = Debug|Win32 + {C9F18204-74EE-47D5-816C-5198901E278F}.Debug|x86.Build.0 = Debug|Win32 + {C9F18204-74EE-47D5-816C-5198901E278F}.Release|x86.ActiveCfg = Release|Win32 + {C9F18204-74EE-47D5-816C-5198901E278F}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FA2sp.vcxproj b/FA2sp.vcxproj index ca09e023..f952bcbe 100644 --- a/FA2sp.vcxproj +++ b/FA2sp.vcxproj @@ -342,6 +342,7 @@ + @@ -477,6 +478,8 @@ + + diff --git a/FA2sp.vcxproj.filters b/FA2sp.vcxproj.filters index 10d4e271..bc6590d8 100644 --- a/FA2sp.vcxproj.filters +++ b/FA2sp.vcxproj.filters @@ -45,6 +45,12 @@ 头文件 + + 头文件 + + + 头文件 + 头文件 @@ -623,6 +629,9 @@ 源文件 + + 源文件 + 源文件 diff --git a/FA2sp/Ext/CCreateMap3B/Hooks.cpp b/FA2sp/Ext/CCreateMap3B/Hooks.cpp index a43d0252..3f4420b7 100644 --- a/FA2sp/Ext/CCreateMap3B/Hooks.cpp +++ b/FA2sp/Ext/CCreateMap3B/Hooks.cpp @@ -1,4 +1,5 @@ #include +#include "../../Miscs/Hooks.INI.h" #include #include @@ -24,10 +25,10 @@ DEFINE_HOOK(42CCC8, CCreateMap3B_NoImportTrees, 5) DEFINE_HOOK(42CCE2, CCreateMap3B_NoImportUnits, 5) { - CINI::CurrentDocument->DeleteSection("Structures"); - CINI::CurrentDocument->DeleteSection("Units"); - CINI::CurrentDocument->DeleteSection("Aircraft"); - CINI::CurrentDocument->DeleteSection("Infantry"); + CINIExt::CurrentDocument->DeleteSection("Structures"); + CINIExt::CurrentDocument->DeleteSection("Units"); + CINIExt::CurrentDocument->DeleteSection("Aircraft"); + CINIExt::CurrentDocument->DeleteSection("Infantry"); CMapDataExt::UpdateFieldStructureData_RedrawMinimap(); CMapDataExt::UpdateFieldUnitData_RedrawMinimap(); CMapDataExt::UpdateFieldAircraftData_RedrawMinimap(); diff --git a/FA2sp/Ext/CFinalSunDlg/Body.ObjectBrowserControl.cpp b/FA2sp/Ext/CFinalSunDlg/Body.ObjectBrowserControl.cpp index 1b1ec701..ecec476b 100644 --- a/FA2sp/Ext/CFinalSunDlg/Body.ObjectBrowserControl.cpp +++ b/FA2sp/Ext/CFinalSunDlg/Body.ObjectBrowserControl.cpp @@ -197,7 +197,7 @@ HTREEITEM CViewObjectsExt::InsertString(const char* pString, DWORD dwItemData, auto& subTile = tile.TileBlockDatas[0]; FString fileName; - fileName.Format("%s-tile-%d-%d.bmp", CINI::CurrentDocument->GetString("Map", "Theater"), + fileName.Format("%s-tile-%d-%d.bmp", CINIExt::CurrentDocument->GetString("Map", "Theater"), InsertingTileIndex, ExtConfigs::TreeViewCameo_Size); std::string path = CFinalSunAppExt::ExePathExt(); @@ -246,7 +246,7 @@ HTREEITEM CViewObjectsExt::InsertString(const char* pString, DWORD dwItemData, case CLoadingExt::GameObjectType::Terrain: case CLoadingExt::GameObjectType::Smudge: imageName = CLoadingExt::GetImageName(InsertingObjectID, 0); - fileID = FString(CINI::CurrentDocument->GetString("Map", "Theater")) + "-" + + fileID = FString(CINIExt::CurrentDocument->GetString("Map", "Theater")) + "-" + CLoadingExt::GetExtension()->GetTerrainOrSmudgeFileID(InsertingObjectID); break; case CLoadingExt::GameObjectType::Vehicle: @@ -293,7 +293,7 @@ HTREEITEM CViewObjectsExt::InsertString(const char* pString, DWORD dwItemData, fileName.Format("%s-%d.bmp", fileID, ExtConfigs::TreeViewCameo_Size); if (InsertingOverlay > -1) { - fileName.Format("%s-overlay-%d-%d-%d.bmp", CINI::CurrentDocument->GetString("Map", "Theater"), + fileName.Format("%s-overlay-%d-%d-%d.bmp", CINIExt::CurrentDocument->GetString("Map", "Theater"), InsertingOverlay, InsertingOverlayData, ExtConfigs::TreeViewCameo_Size); } path += "\\"; @@ -922,7 +922,7 @@ void CViewObjectsExt::Redraw_Initialize() Owners.clear(); auto& fadata = CINI::FAData(); - auto& doc = CINI::CurrentDocument(); + auto& doc = CINIExt::CurrentDocument(); if (ExtConfigs::ObjectBrowser_GuessMode == 1) { auto loadOwner = []() @@ -1125,7 +1125,7 @@ void CViewObjectsExt::Redraw_Ground() if (hGround == NULL) return; InsertedTileIndices.clear(); - auto& doc = CINI::CurrentDocument(); + auto& doc = CINIExt::CurrentDocument(); auto theater = doc.GetString("Map", "Theater"); if (theater == "NEWURBAN") theater = "UBN"; @@ -1240,7 +1240,7 @@ void CViewObjectsExt::Redraw_Ground() auto allowed = STDHelpers::SplitString(CINI::FAData().GetString(pKey.second, "AllowedTheater", "")); if (allowed.size() > 0) for (auto& a : allowed) - if (a == CINI::CurrentDocument().GetString("Map", "Theater")) + if (a == CINIExt::CurrentDocument().GetString("Map", "Theater")) add = true; if (add) { @@ -1363,7 +1363,7 @@ void CViewObjectsExt::Redraw_Owner() } else { - if (auto pSection = CINI::CurrentDocument->GetSection("Houses")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Houses")) { auto& section = pSection->GetEntities(); size_t i = 0; @@ -1480,7 +1480,7 @@ void CViewObjectsExt::Redraw_Owner() } else { - if (auto pSection = CINI::CurrentDocument->GetSection("Houses")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Houses")) { auto& section = pSection->GetEntities(); size_t i = 0; @@ -1782,7 +1782,7 @@ void CViewObjectsExt::Redraw_Infantry() auto banned = STDHelpers::SplitString(CINI::FAData().GetString(pKey.second, "BannedTheater", "")); if (banned.size() > 0) for (auto& ban : banned) - if (ban == CINI::CurrentDocument().GetString("Map", "Theater")) + if (ban == CINIExt::CurrentDocument().GetString("Map", "Theater")) add = false; if (add) { @@ -1971,7 +1971,7 @@ void CViewObjectsExt::Redraw_Vehicle() auto banned = STDHelpers::SplitString(CINI::FAData().GetString(pKey.second, "BannedTheater", "")); if (banned.size() > 0) for (auto& ban : banned) - if (ban == CINI::CurrentDocument().GetString("Map", "Theater")) + if (ban == CINIExt::CurrentDocument().GetString("Map", "Theater")) add = false; if (add) { @@ -2159,7 +2159,7 @@ void CViewObjectsExt::Redraw_Aircraft() auto banned = STDHelpers::SplitString(CINI::FAData().GetString(pKey.second, "BannedTheater", "")); if (banned.size() > 0) for (auto& ban : banned) - if (ban == CINI::CurrentDocument().GetString("Map", "Theater")) + if (ban == CINIExt::CurrentDocument().GetString("Map", "Theater")) add = false; if (add) { @@ -2217,7 +2217,7 @@ void CViewObjectsExt::Redraw_Building() auto& fadata = CINI::FAData(); auto& art = CINI::Art(); - auto& doc = CINI::CurrentDocument(); + auto& doc = CINIExt::CurrentDocument(); int i = 0; if (auto sides = fadata.GetSection(ExtraWindow::GetTranslatedSectionName("Sides"))) @@ -2385,7 +2385,7 @@ void CViewObjectsExt::Redraw_Building() auto banned = STDHelpers::SplitString(CINI::FAData().GetString(pKey.second, "BannedTheater", "")); if (banned.size() > 0) for (auto& ban : banned) - if (ban == CINI::CurrentDocument().GetString("Map", "Theater")) + if (ban == CINIExt::CurrentDocument().GetString("Map", "Theater")) add = false; if (add) { @@ -2524,7 +2524,7 @@ void CViewObjectsExt::Redraw_Terrain() auto banned = STDHelpers::SplitString(CINI::FAData().GetString(pKey.second, "BannedTheater", "")); if (banned.size() > 0) for (auto& ban : banned) - if (ban == CINI::CurrentDocument().GetString("Map", "Theater")) + if (ban == CINIExt::CurrentDocument().GetString("Map", "Theater")) add = false; if (add) { @@ -2608,7 +2608,7 @@ void CViewObjectsExt::Redraw_Smudge() auto banned = STDHelpers::SplitString(CINI::FAData().GetString(pKey.second, "BannedTheater", "")); if (banned.size() > 0) for (auto& ban : banned) - if (ban == CINI::CurrentDocument().GetString("Map", "Theater")) + if (ban == CINIExt::CurrentDocument().GetString("Map", "Theater")) add = false; if (add) { @@ -2894,7 +2894,7 @@ void CViewObjectsExt::Redraw_Overlay() auto banned = STDHelpers::SplitString(CINI::FAData().GetString(pKey.second, "BannedTheater", "")); if (banned.size() > 0) for (auto& ban : banned) - if (ban == CINI::CurrentDocument().GetString("Map", "Theater")) + if (ban == CINIExt::CurrentDocument().GetString("Map", "Theater")) add = false; if (add) { @@ -3352,9 +3352,9 @@ void CViewObjectsExt::AddAnnotation(int X, int Y) CAnnotationDlg dlg; bool folded = false; - if (CINI::CurrentDocument->KeyExists("Annotations", key)) + if (CINIExt::CurrentDocument->KeyExists("Annotations", key)) { - auto atoms = STDHelpers::SplitString(CINI::CurrentDocument->GetString("Annotations", key), 6); + auto atoms = STDHelpers::SplitString(CINIExt::CurrentDocument->GetString("Annotations", key), 6); dlg.m_Text = atoms[5]; for (int i = 6; i < atoms.size() - 1; i++) { @@ -3388,7 +3388,7 @@ void CViewObjectsExt::AddAnnotation(int X, int Y) STDHelpers::ColorRefRGBToHexString(textColor), STDHelpers::ColorRefRGBToHexString(bgColor), dlg.m_Text); - CINI::CurrentDocument->WriteString("Annotations", key, value); + CINIExt::CurrentDocument->WriteString("Annotations", key, value); auto& cellExt = CMapDataExt::CellDataExts[CMapData::Instance->GetCoordIndex(X, Y)]; cellExt.HasAnnotation = true; @@ -3404,7 +3404,7 @@ void CViewObjectsExt::RemoveAnnotation(int X, int Y) auto& cellExt = CMapDataExt::CellDataExts[CMapData::Instance->GetCoordIndex(X, Y)]; cellExt.HasAnnotation = false; - if (CINI::CurrentDocument->KeyExists("Annotations", key)) + if (CINIExt::CurrentDocument->KeyExists("Annotations", key)) { deleteAnnotation = true; } @@ -3445,11 +3445,11 @@ void CViewObjectsExt::RemoveAnnotation(int X, int Y) if (deleteAnnotation) { - CINI::CurrentDocument->DeleteKey("Annotations", key); + CINIExt::CurrentDocument->DeleteKey("Annotations", key); } if (deleteGeometricAnnotation) { - if (auto pSection = CINI::CurrentDocument->GetSection("GeometricAnnotations")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("GeometricAnnotations")) { std::vector keysToDelete; for (auto& [key, value] : pSection->GetEntities()) @@ -3471,7 +3471,7 @@ void CViewObjectsExt::RemoveAnnotation(int X, int Y) for (const auto& key : keysToDelete) { - CINI::CurrentDocument->DeleteKey("GeometricAnnotations", key); + CINIExt::CurrentDocument->DeleteKey("GeometricAnnotations", key); } } } @@ -3511,16 +3511,16 @@ void CViewObjectsExt::SetWpTagColor(int X, int Y, bool isWp) { if (cell->Waypoint > -1) { - auto id = CINI::CurrentDocument->GetKeyAt("Waypoints", cell->Waypoint); + auto id = CINIExt::CurrentDocument->GetKeyAt("Waypoints", cell->Waypoint); if (!id.IsEmpty()) { CMapDataExt::CustomWaypointColors[id] = color; ppmfc::CString key = "Wp"; key += id; if (color != ExtConfigs::DisplayColor_Waypoint) - CINI::CurrentDocument->WriteString("FA2spColors", key, value); + CINIExt::CurrentDocument->WriteString("FA2spColors", key, value); else - CINI::CurrentDocument->DeleteKey("FA2spColors", key); + CINIExt::CurrentDocument->DeleteKey("FA2spColors", key); ::RedrawWindow(CFinalSunDlg::Instance->MyViewFrame.pIsoView->m_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); } } @@ -3529,16 +3529,16 @@ void CViewObjectsExt::SetWpTagColor(int X, int Y, bool isWp) { if (cell->CellTag > -1) { - auto id = CINI::CurrentDocument->GetStringAt("CellTags", cell->CellTag); + auto id = CINIExt::CurrentDocument->GetStringAt("CellTags", cell->CellTag); if (!id.IsEmpty()) { CMapDataExt::CustomCelltagColors[id] = color; ppmfc::CString key = "Tag"; key += id; if (color != ExtConfigs::DisplayColor_Celltag) - CINI::CurrentDocument->WriteString("FA2spColors", key, value); + CINIExt::CurrentDocument->WriteString("FA2spColors", key, value); else - CINI::CurrentDocument->DeleteKey("FA2spColors", key); + CINIExt::CurrentDocument->DeleteKey("FA2spColors", key); ::RedrawWindow(CFinalSunDlg::Instance->MyViewFrame.pIsoView->m_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); } } @@ -3553,11 +3553,11 @@ void CViewObjectsExt::RemoveWpTagColor(int X, int Y, bool isWp) { if (cell->Waypoint > -1) { - auto id = CINI::CurrentDocument->GetKeyAt("Waypoints", cell->Waypoint); + auto id = CINIExt::CurrentDocument->GetKeyAt("Waypoints", cell->Waypoint); CMapDataExt::CustomWaypointColors.erase(id); ppmfc::CString key = "Wp"; key += id; - CINI::CurrentDocument->DeleteKey("FA2spColors", key); + CINIExt::CurrentDocument->DeleteKey("FA2spColors", key); ::RedrawWindow(CFinalSunDlg::Instance->MyViewFrame.pIsoView->m_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); } } @@ -3565,11 +3565,11 @@ void CViewObjectsExt::RemoveWpTagColor(int X, int Y, bool isWp) { if (cell->CellTag > -1) { - auto id = CINI::CurrentDocument->GetStringAt("CellTags", cell->CellTag); + auto id = CINIExt::CurrentDocument->GetStringAt("CellTags", cell->CellTag); CMapDataExt::CustomCelltagColors.erase(id); ppmfc::CString key = "Tag"; key += id; - CINI::CurrentDocument->DeleteKey("FA2spColors", key); + CINIExt::CurrentDocument->DeleteKey("FA2spColors", key); ::RedrawWindow(CFinalSunDlg::Instance->MyViewFrame.pIsoView->m_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); } } @@ -3590,7 +3590,7 @@ void CViewObjectsExt::DeleteTube(int X, int Y) CMapDataExt::MakeObjectRecord(ObjectRecord::RecordType::Tunnel); once = true; } - CINI::CurrentDocument->DeleteKey("Tubes", tube.key); + CINIExt::CurrentDocument->DeleteKey("Tubes", tube.key); break; } } @@ -3602,7 +3602,7 @@ void CViewObjectsExt::DeleteTube(int X, int Y) void CViewObjectsExt::DeleteCelltag(int X, int Y) { auto pIsoView = CIsoViewExt::GetExtension(); - auto pSection = CINI::CurrentDocument->GetSection("CellTags"); + auto pSection = CINIExt::CurrentDocument->GetSection("CellTags"); if (!pSection) return; @@ -3631,7 +3631,7 @@ void CViewObjectsExt::DeleteCelltag(int X, int Y) { for (auto& key : keys) { - CINI::CurrentDocument->DeleteKey(pSection, key); + CINIExt::CurrentDocument->DeleteKey(pSection, key); } CMapData::Instance->UpdateFieldCelltagData(FALSE); ::RedrawWindow(CFinalSunDlg::Instance->MyViewFrame.pIsoView->m_hWnd, 0, 0, RDW_UPDATENOW | RDW_INVALIDATE); @@ -3650,7 +3650,7 @@ void CViewObjectsExt::MoveBaseNodeOrder(int X, int Y) char key[10]; sprintf(key, "%03d", cell->BaseNode.BasenodeID); - auto&& atoms = STDHelpers::SplitString(CINI::CurrentDocument->GetString(cell->BaseNode.House, key), 2); + auto&& atoms = STDHelpers::SplitString(CINIExt::CurrentDocument->GetString(cell->BaseNode.House, key), 2); const auto& ID = atoms[0]; targetIndex = cell->BaseNode.BasenodeID; @@ -3702,7 +3702,7 @@ void CViewObjectsExt::MoveBaseNode(int X, int Y) char key[10]; sprintf(key, "%03d", cell->BaseNode.BasenodeID); - auto&& atoms = STDHelpers::SplitString(CINI::CurrentDocument->GetString(cell->BaseNode.House, key), 2); + auto&& atoms = STDHelpers::SplitString(CINIExt::CurrentDocument->GetString(cell->BaseNode.House, key), 2); const auto& ID = atoms[0]; int bnX = atoi(atoms[2]); int bnY = atoi(atoms[1]); diff --git a/FA2sp/Ext/CFinalSunDlg/Body.PlaceConnectedTiles.cpp b/FA2sp/Ext/CFinalSunDlg/Body.PlaceConnectedTiles.cpp index 0e3f9e20..58e8326f 100644 --- a/FA2sp/Ext/CFinalSunDlg/Body.PlaceConnectedTiles.cpp +++ b/FA2sp/Ext/CFinalSunDlg/Body.PlaceConnectedTiles.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include "../../Helpers/Translations.h" #include "../../Helpers/STDHelpers.h" #include "../../Miscs/TheaterInfo.h" @@ -332,7 +333,7 @@ void CViewObjectsExt::ConnectedTile_Initialize() TreeView_ConnectedTileMap.clear(); CurrentConnectedTileType = -1; ConnectedTileSets.clear(); - auto thisTheater = CINI::CurrentDocument().GetString("Map", "Theater"); + auto thisTheater = CINIExt::CurrentDocument().GetString("Map", "Theater"); if (CMapDataExt::TileData) { std::string path = CFinalSunApp::Instance->ExePath(); @@ -545,7 +546,7 @@ void CViewObjectsExt::Redraw_ConnectedTile(CViewObjectsExt* pThis) subNodes.push_back(hCliff); subNodes.push_back(hRailroad); - auto thisTheater = CINI::CurrentDocument().GetString("Map", "Theater"); + auto thisTheater = CINIExt::CurrentDocument().GetString("Map", "Theater"); int i = -1; for (auto& ct : ConnectedTileSets) diff --git a/FA2sp/Ext/CFinalSunDlg/Body.cpp b/FA2sp/Ext/CFinalSunDlg/Body.cpp index 50842998..da584a5c 100644 --- a/FA2sp/Ext/CFinalSunDlg/Body.cpp +++ b/FA2sp/Ext/CFinalSunDlg/Body.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include "../../FA2sp.h" #include "../CIsoView/Body.h" @@ -798,7 +799,7 @@ BOOL CFinalSunDlgExt::OnCommandExt(WPARAM wParam, LPARAM lParam) { for (const auto& type : pSection->GetEntities()) { - auto thisTheater = CINI::CurrentDocument().GetString("Map", "Theater"); + auto thisTheater = CINIExt::CurrentDocument().GetString("Map", "Theater"); if (STDHelpers::SplitString(type.second)[0] == thisTheater) { found = true; @@ -949,7 +950,7 @@ BOOL CFinalSunDlgExt::OnCommandExt(WPARAM wParam, LPARAM lParam) break; } - auto thisTheater = CINI::CurrentDocument().GetString("Map", "Theater"); + auto thisTheater = CINIExt::CurrentDocument().GetString("Map", "Theater"); thisTheater.MakeUpper(); if (thisTheater == "NEWURBAN") thisTheater = "UBN"; diff --git a/FA2sp/Ext/CFinalSunDlg/Hooks.ObjectBrowserControl.cpp b/FA2sp/Ext/CFinalSunDlg/Hooks.ObjectBrowserControl.cpp index 1880dc13..c3bf481e 100644 --- a/FA2sp/Ext/CFinalSunDlg/Hooks.ObjectBrowserControl.cpp +++ b/FA2sp/Ext/CFinalSunDlg/Hooks.ObjectBrowserControl.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include #include @@ -1204,7 +1205,7 @@ DEFINE_HOOK(461766, CIsoView_OnLButtonDown_PropertyBrush, 5) continue; int intKey = gx * 1000 + gy; - CINI::CurrentDocument->WriteString("CellTags", std::to_string(intKey).c_str(), CIsoView::CurrentCommand->ObjectID); + CINIExt::CurrentDocument->WriteString("CellTags", std::to_string(intKey).c_str(), CIsoView::CurrentCommand->ObjectID); } } } diff --git a/FA2sp/Ext/CIsoView/Body.DrawObjects.cpp b/FA2sp/Ext/CIsoView/Body.DrawObjects.cpp index 76d979e9..880acd42 100644 --- a/FA2sp/Ext/CIsoView/Body.DrawObjects.cpp +++ b/FA2sp/Ext/CIsoView/Body.DrawObjects.cpp @@ -1583,14 +1583,14 @@ static void DrawMap() pasteBuildings.clear(); pasteBuildingFSs.clear(); if (CIsoViewExt::DrawCelltags) - if (auto pSection = CINI::CurrentDocument->GetSection("CellTags")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("CellTags")) { Celltags.reserve(pSection->GetEntities().size()); for (auto &[key, value] : pSection->GetEntities()) Celltags.push_back(&value); } if (CIsoViewExt::DrawWaypoints) - if (auto pSection = CINI::CurrentDocument->GetSection("Waypoints")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints")) { Waypoints.reserve(pSection->GetEntities().size()); for (auto &[key, value] : pSection->GetEntities()) @@ -1630,7 +1630,7 @@ static void DrawMap() if (INIIncludes::MapINIWarn) { - if (!CINI::CurrentDocument->GetBool("FA2spVersionControl", "MapIncludeWarned")) + if (!CINIExt::CurrentDocument->GetBool("FA2spVersionControl", "MapIncludeWarned")) { int result = MessageBox(CIsoView::GetInstance()->GetSafeHwnd(), Translations::TranslateOrDefault("MapIncludeWarningMessage", @@ -1640,7 +1640,7 @@ static void DrawMap() MB_OKCANCEL | MB_DEFBUTTON2 | MB_ICONEXCLAMATION); if (result == IDOK) - CINI::CurrentDocument->WriteBool("FA2spVersionControl", "MapIncludeWarned", true); + CINIExt::CurrentDocument->WriteBool("FA2spVersionControl", "MapIncludeWarned", true); } INIIncludes::MapINIWarn = false; } @@ -3549,7 +3549,7 @@ static void DrawMap() } if (CIsoViewExt::RockCells) { - auto thisTheater = CINI::CurrentDocument().GetString("Map", "Theater"); + auto thisTheater = CINIExt::CurrentDocument().GetString("Map", "Theater"); auto Map = &CMapData::Instance(); for (int i = 0; i < Map->CellDataCount; i++) { @@ -4045,7 +4045,7 @@ static void DrawMap() if (CIsoViewExt::DrawAnnotations) { - if (auto pSection = CINI::CurrentDocument->GetSection("Annotations")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Annotations")) { for (const auto &[key, value] : pSection->GetEntities()) { diff --git a/FA2sp/Ext/CIsoView/Body.OnMouseMove.cpp b/FA2sp/Ext/CIsoView/Body.OnMouseMove.cpp index 6006df77..6f03e574 100644 --- a/FA2sp/Ext/CIsoView/Body.OnMouseMove.cpp +++ b/FA2sp/Ext/CIsoView/Body.OnMouseMove.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include "../../FA2sp.h" #include @@ -567,10 +568,10 @@ void CIsoViewExt::DrawMouseMove(HDC hDC, const RECT &rect) int i = 1; int tab = 10; auto Map = &CMapData::Instance(); - auto &mapIni = CINI::CurrentDocument(); + auto &mapIni = CINIExt::CurrentDocument(); MultimapHelper mmh; mmh.AddINI(&CINI::Rules()); - mmh.AddINI(&CINI::CurrentDocument()); + mmh.AddINI(&CINIExt::CurrentDocument()); if (!CMapDataExt::IsCoordInFullMap(point.X, point.Y)) { @@ -2334,7 +2335,7 @@ void CIsoViewExt::DrawMouseMove(HDC hDC, const RECT &rect) if (cell->Waypoint != -1) { - auto pSection = CINI::CurrentDocument->GetSection("Waypoints"); + auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints"); auto pWP = pSection->GetKeyAt(cell->Waypoint); auto pVal = pSection->GetValueAt(cell->Waypoint); if (pWP && pVal) @@ -2478,7 +2479,7 @@ void CIsoViewExt::DrawMouseMove(HDC hDC, const RECT &rect) } } - if (auto pSection = CINI::CurrentDocument->GetSection("ScriptTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("ScriptTypes")) { for (auto &pair : pSection->GetEntities()) { @@ -2488,7 +2489,7 @@ void CIsoViewExt::DrawMouseMove(HDC hDC, const RECT &rect) { char id[10]; _itoa(i, id, 10); - auto line = CINI::CurrentDocument->GetString(pair.second, id); + auto line = CINIExt::CurrentDocument->GetString(pair.second, id); if (line == "") continue; @@ -2529,7 +2530,7 @@ void CIsoViewExt::DrawMouseMove(HDC hDC, const RECT &rect) { pSrc.Format(Translations::TranslateOrDefault("ObjectInfo.Waypoint.Script", "Script: %s (%s)"), - CINI::CurrentDocument->GetString(pair.second, "Name"), pair.second); + CINIExt::CurrentDocument->GetString(pair.second, "Name"), pair.second); if (ExtConfigs::DirectXRendering) { TextOutDirectX(drawX, drawY + lineHeight * i, pSrc, fontSize, true, 2); @@ -2544,7 +2545,7 @@ void CIsoViewExt::DrawMouseMove(HDC hDC, const RECT &rect) } } } - if (auto pSection = CINI::CurrentDocument->GetSection("TeamTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("TeamTypes")) { auto process = [](const char *s) { @@ -2563,15 +2564,15 @@ void CIsoViewExt::DrawMouseMove(HDC hDC, const RECT &rect) }; for (auto &pair : pSection->GetEntities()) { - auto wp = CINI::CurrentDocument->GetString(pair.second, "Waypoint"); - auto wp2 = CINI::CurrentDocument->GetString(pair.second, "TransportWaypoint"); + auto wp = CINIExt::CurrentDocument->GetString(pair.second, "Waypoint"); + auto wp2 = CINIExt::CurrentDocument->GetString(pair.second, "TransportWaypoint"); if (process(wp) == atoi(*pWP) - || (process(wp2) == atoi(*pWP) && CINI::CurrentDocument->GetBool(pair.second, "UseTransportOrigin"))) + || (process(wp2) == atoi(*pWP) && CINIExt::CurrentDocument->GetBool(pair.second, "UseTransportOrigin"))) { pSrc.Format(Translations::TranslateOrDefault("ObjectInfo.Waypoint.Team", "Team: %s (%s)"), - CINI::CurrentDocument->GetString(pair.second, "Name"), pair.second); + CINIExt::CurrentDocument->GetString(pair.second, "Name"), pair.second); if (ExtConfigs::DirectXRendering) { TextOutDirectX(drawX, drawY + lineHeight * i, pSrc, fontSize, true, 2); @@ -2650,13 +2651,13 @@ void CIsoViewExt::DrawMouseMove(HDC hDC, const RECT &rect) } else { - auto tag = FString::SplitString(CINI::CurrentDocument->GetString("Tags", currentTag)); + auto tag = FString::SplitString(CINIExt::CurrentDocument->GetString("Tags", currentTag)); if (tag.size() > 1) currentTag.Format("%s (%s)", currentTag, tag[1]); else currentTag.Format("%s", currentTag); } - auto tag = FString::SplitString(CINI::CurrentDocument->GetString("Tags", newTag)); + auto tag = FString::SplitString(CINIExt::CurrentDocument->GetString("Tags", newTag)); if (tag.size() > 1) newTag.Format("%s (%s)", newTag, tag[1]); else diff --git a/FA2sp/Ext/CIsoView/Body.cpp b/FA2sp/Ext/CIsoView/Body.cpp index 2a40ab1d..c9adab50 100644 --- a/FA2sp/Ext/CIsoView/Body.cpp +++ b/FA2sp/Ext/CIsoView/Body.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include "../../FA2sp.h" #include @@ -270,7 +271,7 @@ void CIsoViewExt::ConfirmTube(bool addReverse) value += direc; } value += ",-1"; - CINI::CurrentDocument->WriteString("Tubes", key, value); + CINIExt::CurrentDocument->WriteString("Tubes", key, value); if (addReverse) { @@ -283,7 +284,7 @@ void CIsoViewExt::ConfirmTube(bool addReverse) value += direc; } value += ",-1"; - CINI::CurrentDocument->WriteString("Tubes", key, value); + CINIExt::CurrentDocument->WriteString("Tubes", key, value); } CMapData::Instance->UpdateFieldTubeData(false); diff --git a/FA2sp/Ext/CIsoView/Hooks.cpp b/FA2sp/Ext/CIsoView/Hooks.cpp index f415ebd8..ed74cff6 100644 --- a/FA2sp/Ext/CIsoView/Hooks.cpp +++ b/FA2sp/Ext/CIsoView/Hooks.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include "../../FA2sp.h" #include @@ -404,17 +405,17 @@ DEFINE_HOOK(466DDE, CIsoView_OnLButtonUp_DragOthers, 7) int oldY = CMapData::Instance->GetYFromCoordIndex(m_id); ppmfc::CString key; key.Format("%d", oldX * 1000 + oldY); - if (CINI::CurrentDocument->KeyExists("Annotations", key)) + if (CINIExt::CurrentDocument->KeyExists("Annotations", key)) { - auto value = CINI::CurrentDocument->GetString("Annotations", key); + auto value = CINIExt::CurrentDocument->GetString("Annotations", key); if (nLButtonUpFlags != MK_SHIFT) { auto &cellExt = CMapDataExt::CellDataExts[CMapData::Instance->GetCoordIndex(oldX, oldY)]; cellExt.HasAnnotation = false; - CINI::CurrentDocument->DeleteKey("Annotations", key); + CINIExt::CurrentDocument->DeleteKey("Annotations", key); } key.Format("%d", X * 1000 + Y); - CINI::CurrentDocument->WriteString("Annotations", key, value); + CINIExt::CurrentDocument->WriteString("Annotations", key, value); auto &cellExt = CMapDataExt::CellDataExts[CMapData::Instance->GetCoordIndex(X, Y)]; cellExt.HasAnnotation = true; } @@ -427,30 +428,30 @@ DEFINE_HOOK(466DDE, CIsoView_OnLButtonUp_DragOthers, 7) CMapDataExt::MakeObjectRecord(ObjectRecord::RecordType::Basenode); char key[10]; sprintf(key, "%03d", m_id); - if (CINI::CurrentDocument->KeyExists(CIsoViewExt::CurrentCellObjectHouse, key)) + if (CINIExt::CurrentDocument->KeyExists(CIsoViewExt::CurrentCellObjectHouse, key)) { - auto atoms = STDHelpers::SplitString(CINI::CurrentDocument->GetString(CIsoViewExt::CurrentCellObjectHouse, key), 2); + auto atoms = STDHelpers::SplitString(CINIExt::CurrentDocument->GetString(CIsoViewExt::CurrentCellObjectHouse, key), 2); ppmfc::CString value; value.Format("%s,%d,%d", atoms[0], Y, X); if (nLButtonUpFlags == MK_SHIFT) { - int nodeCount = CINI::CurrentDocument->GetInteger(CIsoViewExt::CurrentCellObjectHouse, "NodeCount"); + int nodeCount = CINIExt::CurrentDocument->GetInteger(CIsoViewExt::CurrentCellObjectHouse, "NodeCount"); if (nodeCount < 1000) { for (int i = 0; i < 1000; ++i) { sprintf(key, "%03d", i); - if (!CINI::CurrentDocument->KeyExists(CIsoViewExt::CurrentCellObjectHouse, key)) + if (!CINIExt::CurrentDocument->KeyExists(CIsoViewExt::CurrentCellObjectHouse, key)) { break; } } char count[10]; sprintf(count, "%d", nodeCount + 1); - CINI::CurrentDocument->WriteString(CIsoViewExt::CurrentCellObjectHouse, "NodeCount", count); + CINIExt::CurrentDocument->WriteString(CIsoViewExt::CurrentCellObjectHouse, "NodeCount", count); } } - CINI::CurrentDocument->WriteString(CIsoViewExt::CurrentCellObjectHouse, key, value); + CINIExt::CurrentDocument->WriteString(CIsoViewExt::CurrentCellObjectHouse, key, value); CMapData::Instance->UpdateFieldBasenodeData(false); } m_id = -1; @@ -509,7 +510,7 @@ DEFINE_HOOK(466DDE, CIsoView_OnLButtonUp_DragOthers, 7) { FString size; size.Format("%d,%d,%d,%d", mpL, mpT, mpW, mpH); - CINI::CurrentDocument->WriteString("Map", "LocalSize", size); + CINIExt::CurrentDocument->WriteString("Map", "LocalSize", size); if (IsWindowVisible(CFinalSunDlg::Instance->MapD)) { auto dlg = GetDlgItem(CFinalSunDlg::Instance->MapD, 1045); @@ -870,11 +871,11 @@ DEFINE_HOOK(45EC1A, CIsoView_OnCommand_HandleProperty, A) { ppmfc::CString key; key.Format("%d", coord.X * 1000 + coord.Y); - if (CINI::CurrentDocument->KeyExists("Annotations", key)) + if (CINIExt::CurrentDocument->KeyExists("Annotations", key)) { if ((GetKeyState(VK_CONTROL) & 0x8000)) { - auto atoms = STDHelpers::SplitString(CINI::CurrentDocument->GetString("Annotations", key), 6); + auto atoms = STDHelpers::SplitString(CINIExt::CurrentDocument->GetString("Annotations", key), 6); ppmfc::CString value; bool folded = STDHelpers::IsTrue(atoms[2]); for (int i = 0; i < atoms.size(); ++i) @@ -891,7 +892,7 @@ DEFINE_HOOK(45EC1A, CIsoView_OnCommand_HandleProperty, A) value += "yes"; } } - CINI::CurrentDocument->WriteString("Annotations", key, value); + CINIExt::CurrentDocument->WriteString("Annotations", key, value); ::RedrawWindow(CFinalSunDlg::Instance->MyViewFrame.pIsoView->m_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); } else @@ -1793,7 +1794,7 @@ DEFINE_HOOK(45BFE1, CIsoView_OnMouseMove_Waypoint_Add, 8) { int i = 8; ppmfc::CString key = "8"; - while (CINI::CurrentDocument->KeyExists("Waypoints", key)) + while (CINIExt::CurrentDocument->KeyExists("Waypoints", key)) { i++; key.Format("%d", i); @@ -1821,14 +1822,14 @@ DEFINE_HOOK(45C0CF, CIsoView_OnMouseMove_Waypoint_AddPlayerLocation, 6) CMapDataExt::MakeObjectRecord(ObjectRecord::RecordType::Waypoint, true); auto deleteWaypoint = [](ppmfc::CString key) { - if (auto pSection = CINI::CurrentDocument->GetSection("Waypoints")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints")) { - if (CINI::CurrentDocument->KeyExists("Waypoints", key)) + if (CINIExt::CurrentDocument->KeyExists("Waypoints", key)) { auto &&value = pSection->GetString(key); int x = atoi(value) / 1000; int y = atoi(value) % 1000; - CINI::CurrentDocument->DeleteKey(pSection, key); + CINIExt::CurrentDocument->DeleteKey(pSection, key); CMapData::Instance->UpdateFieldWaypointData(false); if (CMapData::Instance->IsMultiOnly()) @@ -2184,16 +2185,16 @@ DEFINE_HOOK(469E70, CIsoView_UpdateStatusBar, 7) if (cell->CellTag > -1) { FString id = ""; - if (CINI::CurrentDocument->SectionExists("CellTags")) + if (CINIExt::CurrentDocument->SectionExists("CellTags")) { char tmp[10]; _itoa((X * 1000 + Y), tmp, 10); - id = CINI::CurrentDocument->GetString("CellTags", tmp); + id = CINIExt::CurrentDocument->GetString("CellTags", tmp); } if (id != "") { FString name = "MISSING"; - auto tag = CINI::CurrentDocument->GetString("Tags", id); + auto tag = CINIExt::CurrentDocument->GetString("Tags", id); auto atoms = FString::SplitString(tag); if (atoms.size() > 1) { diff --git a/FA2sp/Ext/CLighting/Body.cpp b/FA2sp/Ext/CLighting/Body.cpp index 0a3f97d5..40fe1281 100644 --- a/FA2sp/Ext/CLighting/Body.cpp +++ b/FA2sp/Ext/CLighting/Body.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include "../../Helpers/Translations.h" @@ -77,9 +78,9 @@ BOOL CLightingExt::PreTranslateMessageExt(MSG* pMsg) buffer = "0.000000"; pWnd->SetWindowText(buffer); } - if (CINI::CurrentDocument->GetString("Lighting", pKey) != buffer) + if (CINIExt::CurrentDocument->GetString("Lighting", pKey) != buffer) { - CINI::CurrentDocument->WriteString("Lighting", pKey, buffer); + CINIExt::CurrentDocument->WriteString("Lighting", pKey, buffer); if (edit) edited = true; } diff --git a/FA2sp/Ext/CLighting/Hooks.cpp b/FA2sp/Ext/CLighting/Hooks.cpp index caba9a1b..0f8e0381 100644 --- a/FA2sp/Ext/CLighting/Hooks.cpp +++ b/FA2sp/Ext/CLighting/Hooks.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include #include @@ -30,18 +31,18 @@ DEFINE_HOOK(478407, CLighting_UpdateDialog, 7) { GET(CLighting*, pThis, ESI); - pThis->SetDlgItemText(1068, CINI::CurrentDocument->GetString("Lighting", "DominatorAmbient", "0.850000")); - pThis->SetDlgItemText(1069, CINI::CurrentDocument->GetString("Lighting", "DominatorGreen", "0.300000")); - pThis->SetDlgItemText(1070, CINI::CurrentDocument->GetString("Lighting", "DominatorRed", "0.200000")); - pThis->SetDlgItemText(1071, CINI::CurrentDocument->GetString("Lighting", "DominatorBlue", "0.000000")); - pThis->SetDlgItemText(1072, CINI::CurrentDocument->GetString("Lighting", "DominatorLevel", "1.000000")); + pThis->SetDlgItemText(1068, CINIExt::CurrentDocument->GetString("Lighting", "DominatorAmbient", "0.850000")); + pThis->SetDlgItemText(1069, CINIExt::CurrentDocument->GetString("Lighting", "DominatorGreen", "0.300000")); + pThis->SetDlgItemText(1070, CINIExt::CurrentDocument->GetString("Lighting", "DominatorRed", "0.200000")); + pThis->SetDlgItemText(1071, CINIExt::CurrentDocument->GetString("Lighting", "DominatorBlue", "0.000000")); + pThis->SetDlgItemText(1072, CINIExt::CurrentDocument->GetString("Lighting", "DominatorLevel", "1.000000")); - pThis->SetDlgItemText(1073, CINI::CurrentDocument->GetString("Lighting", "Ground", "0.080000")); - pThis->SetDlgItemText(1074, CINI::CurrentDocument->GetString("Lighting", "IonGround", "0.000000")); - pThis->SetDlgItemText(1075, CINI::CurrentDocument->GetString("Lighting", "DominatorGround", "0.000000")); + pThis->SetDlgItemText(1073, CINIExt::CurrentDocument->GetString("Lighting", "Ground", "0.080000")); + pThis->SetDlgItemText(1074, CINIExt::CurrentDocument->GetString("Lighting", "IonGround", "0.000000")); + pThis->SetDlgItemText(1075, CINIExt::CurrentDocument->GetString("Lighting", "DominatorGround", "0.000000")); - pThis->SetDlgItemText(1076, CINI::CurrentDocument->GetString("Lighting", "NukeAmbientChangeRate", "1.500000")); - pThis->SetDlgItemText(1077, CINI::CurrentDocument->GetString("Lighting", "DominatorAmbientChangeRate", "0.000000")); + pThis->SetDlgItemText(1076, CINIExt::CurrentDocument->GetString("Lighting", "NukeAmbientChangeRate", "1.500000")); + pThis->SetDlgItemText(1077, CINIExt::CurrentDocument->GetString("Lighting", "DominatorAmbientChangeRate", "0.000000")); IsUpdatingDialog = false; diff --git a/FA2sp/Ext/CLoading/Hooks.LoadObjects.cpp b/FA2sp/Ext/CLoading/Hooks.LoadObjects.cpp index bf032d7d..763829a3 100644 --- a/FA2sp/Ext/CLoading/Hooks.LoadObjects.cpp +++ b/FA2sp/Ext/CLoading/Hooks.LoadObjects.cpp @@ -20,6 +20,7 @@ #include "../CMapData/Body.h" #include "../../Miscs/Hooks.INI.h" #include "../../Helpers/Helper.h" +#include "../../Helpers/CINIOrderTracker.h" DEFINE_HOOK(4808A0, CLoading_LoadObjects, 5) { @@ -149,7 +150,7 @@ DEFINE_HOOK(49D63A, CLoading_LoadMap_ReloadGame, 5) CINI::Desert().Release(); //CINI::FAData().Release(); //CINI::FALanguage().Release(); - CINI::CurrentDocument().Release(); + CINIExt::CurrentDocument().Release(); CLoading::Instance()->Release(); strcpy_s(CFinalSunApp::FilePath, 260, folder); @@ -250,6 +251,7 @@ DEFINE_HOOK(49D63A, CLoading_LoadMap_ReloadGame, 5) INIIncludes::SkipBracketFix = true; CMapDataExt::IsLoadingMapFile = true; + CINIOrderTracker::Clear(&CINIExt::CurrentDocument); CMapDataExt::MapIniSectionSorting.clear(); CMapDataExt::MapInlineComments.clear(); CMapDataExt::MapFrontlineComments.clear(); diff --git a/FA2sp/Ext/CMapData/Body.ResizeMap.cpp b/FA2sp/Ext/CMapData/Body.ResizeMap.cpp index 8f31a4c8..32359787 100644 --- a/FA2sp/Ext/CMapData/Body.ResizeMap.cpp +++ b/FA2sp/Ext/CMapData/Body.ResizeMap.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include #include "../../Helpers/Translations.h" #include "../../ExtraWindow/CMeasurementToolbox/CMeasurementToolbox.h" @@ -297,7 +298,7 @@ bool CMapDataExt::ResizeMapExt(MapRect* const pRect) } UpdateFieldBasenodeData(false); - if (auto pSection = CINI::CurrentDocument->GetSection("Tubes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Tubes")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -322,12 +323,12 @@ bool CMapDataExt::ResizeMapExt(MapRect* const pRect) val += ","; } val.Delete(val.GetLength() - 1, 1); - CINI::CurrentDocument->WriteString(pSection, key, val); + CINIExt::CurrentDocument->WriteString(pSection, key, val); } } UpdateFieldTubeData(false); - if (auto pSection = CINI::CurrentDocument->GetSection("Annotations")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Annotations")) { std::vector> annotations; for (const auto& [key, value] : pSection->GetEntities()) @@ -340,16 +341,16 @@ bool CMapDataExt::ResizeMapExt(MapRect* const pRect) buffer.Format("%d", y + x * 1000); annotations.push_back(std::make_pair(FString(buffer), FString(value))); } - CINI::CurrentDocument->DeleteSection("Annotations"); - pSection = CINI::CurrentDocument->AddSection("Annotations"); + CINIExt::CurrentDocument->DeleteSection("Annotations"); + pSection = CINIExt::CurrentDocument->AddSection("Annotations"); for (const auto& [key, value] : annotations) { - CINI::CurrentDocument->WriteString(pSection, key, value); + CINIExt::CurrentDocument->WriteString(pSection, key, value); } } CMapDataExt::UpdateAnnotation(); - if (auto pSection = CINI::CurrentDocument->GetSection("GeometricAnnotations")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("GeometricAnnotations")) { std::vector keysToDelete; for (auto& [key, value] : pSection->GetEntities()) @@ -372,7 +373,7 @@ bool CMapDataExt::ResizeMapExt(MapRect* const pRect) } for (auto& key : keysToDelete) { - CINI::CurrentDocument->DeleteKey(pSection, key); + CINIExt::CurrentDocument->DeleteKey(pSection, key); } } CMapDataExt::UpdateGeometricAnnotation(); diff --git a/FA2sp/Ext/CMapData/Body.cpp b/FA2sp/Ext/CMapData/Body.cpp index c0c64113..b99a7e77 100644 --- a/FA2sp/Ext/CMapData/Body.cpp +++ b/FA2sp/Ext/CMapData/Body.cpp @@ -1003,7 +1003,7 @@ BuildingPowers CMapDataExt::GetStructurePower(ppmfc::CString value) void CMapDataExt::GetBuildingDataByIniID(int bldID, CBuildingData& data) { - auto atoms = STDHelpers::SplitString(CINI::CurrentDocument->GetValueAt("Structures", bldID), 16); + auto atoms = STDHelpers::SplitString(CINIExt::CurrentDocument->GetValueAt("Structures", bldID), 16); data.House = atoms[0]; data.TypeID = atoms[1]; data.Health = atoms[2]; @@ -1053,7 +1053,7 @@ void CMapDataExt::AddCellTagExt(const char* lpTag, int dwPos) continue; int intKey = gx * 1000 + gy; - CINI::CurrentDocument->WriteString("CellTags", std::to_string(intKey).c_str(), lpTag); + CINIExt::CurrentDocument->WriteString("CellTags", std::to_string(intKey).c_str(), lpTag); } } @@ -1064,7 +1064,7 @@ void CMapDataExt::UpdateTriggers() { CMapDataExt::Triggers.clear(); FMap TagMap; - if (auto pSection = CINI::CurrentDocument().GetSection("Tags")) + if (auto pSection = CINIExt::CurrentDocument().GetSection("Tags")) { for (auto& kvp : pSection->GetEntities()) { @@ -1074,7 +1074,7 @@ void CMapDataExt::UpdateTriggers() TagMap[tagAtoms[2]] = kvp.first; } } - if (auto pSection = CINI::CurrentDocument->GetSection("Triggers")) { + if (auto pSection = CINIExt::CurrentDocument->GetSection("Triggers")) { for (const auto& pair : pSection->GetEntities()) { std::shared_ptr trigger(Trigger::create(pair.first, &TagMap)); if (!trigger) { @@ -1139,7 +1139,7 @@ void CMapDataExt::ReloadTrigger(const FString& id) { auto it = Triggers.find(id); if (it != Triggers.end()) { - auto atoms = FString::SplitString(CINI::CurrentDocument->GetString("Triggers", id)); + auto atoms = FString::SplitString(CINIExt::CurrentDocument->GetString("Triggers", id)); it->second->LoadFromMap(id, atoms); } } @@ -2022,7 +2022,7 @@ int CMapDataExt::GetRampIndex(int tileIndex, int rampType) void CMapDataExt::UpdateFieldStructureData_Index(int iniIndex, ppmfc::CString value, bool refreshCenter) { if (value == "") - value = CINI::CurrentDocument->GetValueAt("Structures", iniIndex); + value = CINIExt::CurrentDocument->GetValueAt("Structures", iniIndex); int cellIndex = StructureIndexMap.size(); if (cellIndex > SHRT_MAX && !CIsoView::IsMouseMoving) @@ -2143,7 +2143,7 @@ void CMapDataExt::UpdateFieldStructureData_Optimized() StructureIndexMap.clear(); BuildingRenderDatasFix.clear(); BuildingDatasExt.clear(); - if (auto sec = CINI::CurrentDocument->GetSection("Structures")) + if (auto sec = CINIExt::CurrentDocument->GetSection("Structures")) { i = 0; BuildingRenderDatasFix.reserve(sec->GetEntities().size()); @@ -2169,7 +2169,7 @@ std::vector CMapDataExt::GetStructureSize(ppmfc::CString structure) std::vector result; MultimapHelper mmh; mmh.AddINI(&CINI::Rules()); - mmh.AddINI(&CINI::CurrentDocument()); + mmh.AddINI(&CINIExt::CurrentDocument()); auto art = &CINI::Art(); auto image = mmh.GetString(structure, "Image", structure); std::string foundation = std::string(art->GetString(image, "Foundation", "1X1")); @@ -2278,7 +2278,7 @@ ppmfc::CString CMapDataExt::GetAvailableIndex(EIndexType type) type = EIndexType::Generic; auto v = VEHGuard(false); - auto& ini = CINI::CurrentDocument; + auto& ini = CINIExt::CurrentDocument; const int initNumber = 1000000; const char* suffix = ""; @@ -2372,19 +2372,19 @@ ppmfc::CString CMapDataExt::GetAvailableIndex(EIndexType type) void CMapDataExt::UpdateMapSectionIndicies(const ppmfc::CString& lpSection) { - if (auto pSection = CINI::CurrentDocument->GetSection(lpSection)) + if (auto pSection = CINIExt::CurrentDocument->GetSection(lpSection)) { std::vector> buffer; for (auto& [key, value] : pSection->GetEntities()) { buffer.push_back(std::make_pair(key, value)); } - CINI::CurrentDocument->DeleteSection(lpSection); - pSection = CINI::CurrentDocument->AddSection(lpSection); + CINIExt::CurrentDocument->DeleteSection(lpSection); + pSection = CINIExt::CurrentDocument->AddSection(lpSection); int index = 0; for (auto& [key, value] : buffer) { - CINI::CurrentDocument->WriteString(pSection, key, value); + CINIExt::CurrentDocument->WriteString(pSection, key, value); std::pair ins = std::make_pair((ppmfc::CString)key, index++); std::pair ret; @@ -2400,7 +2400,7 @@ void CMapDataExt::UpdateAnnotation() { cellExt.HasAnnotation = false; } - if (auto pSection = CINI::CurrentDocument->GetSection("Annotations")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Annotations")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -2420,7 +2420,7 @@ void CMapDataExt::UpdateGeometricAnnotation() CIsoViewExt::Circles_Annotation.clear(); CIsoViewExt::TempCircle_Annotation[0] = { 0, 0 }; CIsoViewExt::TempCircle_Annotation[1] = { 0, 0 }; - if (auto pSection = CINI::CurrentDocument->GetSection("GeometricAnnotations")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("GeometricAnnotations")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -2583,9 +2583,9 @@ int CMapDataExt::GetPlayerLocationCountAtCell(int x, int y) { FString key; key.Format("%d", i); - if (CINI::CurrentDocument->KeyExists("Waypoints", key)) + if (CINIExt::CurrentDocument->KeyExists("Waypoints", key)) { - auto value = CINI::CurrentDocument->GetInteger("Waypoints", key); + auto value = CINIExt::CurrentDocument->GetInteger("Waypoints", key); if (value >= 0) { int wx = value / 1000; @@ -3332,7 +3332,7 @@ void ObjectRecord::recover() { std::map oriWps; if (map->IsMultiOnly()) - if (auto pSection = CINI::CurrentDocument->GetSection("Waypoints")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints")) for (const auto& data : pSection->GetEntities()) oriWps[atoi(data.first)] = data.second; recoverIniMap("Waypoints", WaypointList); @@ -3379,7 +3379,7 @@ void ObjectRecord::recover() FString size; size.Format("%d,%d,%d,%d", bound.left, bound.top, bound.width, bound.height); - CINI::CurrentDocument->WriteString("Map", "LocalSize", size); + CINIExt::CurrentDocument->WriteString("Map", "LocalSize", size); if (IsWindowVisible(CFinalSunDlg::Instance->MapD)) { auto dlg = GetDlgItem(CFinalSunDlg::Instance->MapD, 1045); @@ -3630,7 +3630,7 @@ void CMapDataExt::UpdateFieldAircraftData_RedrawMinimap() void CMapDataExt::InitializeTileDataInfo() { auto pLoading = CLoadingExt::GetExtension(); - FString thisTheater = CINI::CurrentDocument().GetString("Map", "Theater"); + FString thisTheater = CINIExt::CurrentDocument().GetString("Map", "Theater"); thisTheater.MakeUpper(); if (thisTheater == "TEMPERATE") @@ -3792,7 +3792,7 @@ void CMapDataExt::InitializeTileDataInfo() if (auto pSection = CINI::FAData->GetSection("AutoShoreTypes")) { - auto thisTheater = CINI::CurrentDocument().GetString("Map", "Theater"); + auto thisTheater = CINIExt::CurrentDocument().GetString("Map", "Theater"); for (const auto& type : pSection->GetEntities()) { auto atoms = STDHelpers::SplitString(type.second, 3); @@ -3882,7 +3882,7 @@ void CMapDataExt::InitializeTileData() CMapDataExt::TileData = nullptr; CMapDataExt::TileBlockDataExt.clear(); - auto thisTheater = CINI::CurrentDocument().GetString("Map", "Theater"); + auto thisTheater = CINIExt::CurrentDocument().GetString("Map", "Theater"); thisTheater.MakeUpper(); if (thisTheater == "TEMPERATE" && CTileTypeInfo::Temperate().Datas) { @@ -5755,7 +5755,7 @@ void CMapDataExt::InitializeAllHdmEdition(bool updateMinimap, bool reloadCellDat if (atoms.size() >= 3) { std::vector group; - if (CINI::CurrentDocument().GetString("Map", "Theater") != atoms[0]) + if (CINIExt::CurrentDocument().GetString("Map", "Theater") != atoms[0]) continue; group.push_back(atoms[1]); group.push_back(atoms[2]); @@ -6155,30 +6155,30 @@ void CMapDataExt::InitializeAllHdmEdition(bool updateMinimap, bool reloadCellDat CustomWaypointColors.clear(); CustomCelltagColors.clear(); - if (auto pColors = CINI::CurrentDocument->GetSection("FA2spColors")) + if (auto pColors = CINIExt::CurrentDocument->GetSection("FA2spColors")) { - if (auto pSection = CINI::CurrentDocument->GetSection("Waypoints")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints")) { for (const auto& [key, value] : pSection->GetEntities()) { ppmfc::CString colorkey = "Wp"; colorkey += key; - if (CINI::CurrentDocument->KeyExists("FA2spColors", colorkey)) + if (CINIExt::CurrentDocument->KeyExists("FA2spColors", colorkey)) { - auto color = CINI::CurrentDocument->GetColor(pColors, colorkey, ExtConfigs::DisplayColor_Waypoint); + auto color = CINIExt::CurrentDocument->GetColor(pColors, colorkey, ExtConfigs::DisplayColor_Waypoint); CMapDataExt::CustomWaypointColors[key] = color; } } } - if (auto pSection = CINI::CurrentDocument->GetSection("CellTags")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("CellTags")) { for (const auto& [key, value] : pSection->GetEntities()) { ppmfc::CString colorkey = "Tag"; colorkey += value; - if (CINI::CurrentDocument->KeyExists("FA2spColors", colorkey)) + if (CINIExt::CurrentDocument->KeyExists("FA2spColors", colorkey)) { - auto color = CINI::CurrentDocument->GetColor(pColors, colorkey, ExtConfigs::DisplayColor_Celltag); + auto color = CINIExt::CurrentDocument->GetColor(pColors, colorkey, ExtConfigs::DisplayColor_Celltag); CMapDataExt::CustomCelltagColors[value] = color; } } @@ -6258,7 +6258,7 @@ void CMapDataExt::InitializeAllHdmEdition(bool updateMinimap, bool reloadCellDat }; for (int i = 0; i < 3; ++i) { - if (auto pSection = CINI::CurrentDocument->GetSection(WAESections[i])) + if (auto pSection = CINIExt::CurrentDocument->GetSection(WAESections[i])) { for (auto& [key, value] : pSection->GetEntities()) { diff --git a/FA2sp/Ext/CMapData/Hooks.cpp b/FA2sp/Ext/CMapData/Hooks.cpp index adfef92d..95ffc6cf 100644 --- a/FA2sp/Ext/CMapData/Hooks.cpp +++ b/FA2sp/Ext/CMapData/Hooks.cpp @@ -1,6 +1,9 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include +#include +#include "../../Helpers/CINIOrderTracker.h" #include "../../FA2sp.h" #include "../../Helpers/TheaterHelpers.h" @@ -432,7 +435,7 @@ DEFINE_HOOK(4AC210, CMapData_AddInfantry, 7) std::vector availableAndEmptySubcells; auto& name = CMapData::Instance->TerrainDatas[cell->Terrain].TypeID; ppmfc::CString key = "TemperateOccupationBits"; - auto theaterIg = CINI::CurrentDocument->GetString("Map", "Theater"); + auto theaterIg = CINIExt::CurrentDocument->GetString("Map", "Theater"); if (theaterIg == "SNOW") key = "SnowOccupationBits"; @@ -922,7 +925,7 @@ DEFINE_HOOK(4A8FB0, CMapData_DeleteStructure, 7) GET_STACK(unsigned int, cellIndex, 0x4); auto& mapdata = CMapData::Instance(); - auto& ini = CINI::CurrentDocument; + auto& ini = CINIExt::CurrentDocument; if (!ini->SectionExists("Structures")) return 0x4A98AC; @@ -1096,6 +1099,7 @@ DEFINE_HOOK(4B9E38, CMapData_CreateMap_InitializeMapDataExt, 5) CMapDataExt::IsUTF8File = false; Logger::Debug("CMapData::CreateMap(): About to call InitializeAllHdmEdition()\n"); CMapDataExt::InitializeAllHdmEdition(); + CINIOrderTracker::Clear(&CINIExt::CurrentDocument); CMapDataExt::MapIniSectionSorting.clear(); CMapDataExt::MapInlineComments.clear(); CMapDataExt::MapFrontlineComments.clear(); @@ -1154,7 +1158,7 @@ DEFINE_HOOK(4A6040, CMapData_UpdateUnits, 6) CMapDataExt::UnitDatasExt.clear(); - if (auto pSection = CINI::CurrentDocument->GetSection("Units")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Units")) { CMapDataExt::UnitDatasExt.reserve(pSection->GetEntities().size()); int i = 0; @@ -1181,7 +1185,7 @@ DEFINE_HOOK(4A87A0, CMapData_DeleteUnit, 7) { GET(CMapData*, pThis, ECX); GET_STACK(int, index, 0x4); - if (auto pSection = CINI::CurrentDocument->GetSection("Units")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Units")) { if (index < pSection->GetEntities().size()) { @@ -1189,7 +1193,7 @@ DEFINE_HOOK(4A87A0, CMapData_DeleteUnit, 7) auto atoms = STDHelpers::SplitString(value, 4); int x = atoi(atoms[4]); int y = atoi(atoms[3]); - CINI::CurrentDocument->DeleteKey(pSection, *pSection->GetKeyAt(index)); + CINIExt::CurrentDocument->DeleteKey(pSection, *pSection->GetKeyAt(index)); pThis->UpdateFieldUnitData(false); if (!CMapDataExt::SkipUpdateMinimap) pThis->UpdateMapPreviewAt(x, y); @@ -1211,7 +1215,7 @@ DEFINE_HOOK(4A4270, CMapData_UpdateAircraft, 6) } CMapDataExt::AircraftDatasExt.clear(); - if (auto pSection = CINI::CurrentDocument->GetSection("Aircraft")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Aircraft")) { int i = 0; CMapDataExt::AircraftDatasExt.reserve(pSection->GetEntities().size()); @@ -1238,7 +1242,7 @@ DEFINE_HOOK(4A98B0, CMapData_DeleteAircraft, 7) { GET(CMapData*, pThis, ECX); GET_STACK(int, index, 0x4); - if (auto pSection = CINI::CurrentDocument->GetSection("Aircraft")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Aircraft")) { if (index < pSection->GetEntities().size()) { @@ -1246,7 +1250,7 @@ DEFINE_HOOK(4A98B0, CMapData_DeleteAircraft, 7) auto atoms = STDHelpers::SplitString(value, 4); int x = atoi(atoms[4]); int y = atoi(atoms[3]); - CINI::CurrentDocument->DeleteKey(pSection, *pSection->GetKeyAt(index)); + CINIExt::CurrentDocument->DeleteKey(pSection, *pSection->GetKeyAt(index)); pThis->UpdateFieldAircraftData(false); if (!CMapDataExt::SkipUpdateMinimap) pThis->UpdateMapPreviewAt(x, y); @@ -1267,7 +1271,7 @@ DEFINE_HOOK(4A67D0, CMapData_UpdateWaypoints, 6) pThis->CellDatas[i].Waypoint = -1; } - if (auto pSection = CINI::CurrentDocument->GetSection("Waypoints")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints")) { int i = 0; for (const auto& data : pSection->GetEntities()) @@ -1296,14 +1300,14 @@ DEFINE_HOOK(4A7CB0, CMapData_DeleteWaypoints, 7) { GET(CMapData*, pThis, ECX); GET_STACK(int, index, 0x4); - if (auto pSection = CINI::CurrentDocument->GetSection("Waypoints")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints")) { if (index < pSection->GetEntities().size()) { auto& value = *pSection->GetValueAt(index); int x = atoi(value) / 1000; int y = atoi(value) % 1000; - CINI::CurrentDocument->DeleteKey(pSection, *pSection->GetKeyAt(index)); + CINIExt::CurrentDocument->DeleteKey(pSection, *pSection->GetKeyAt(index)); pThis->UpdateFieldWaypointData(false); if (pThis->IsMultiOnly()) @@ -1341,11 +1345,11 @@ DEFINE_HOOK(4A6FB0, CMapData_UpdateFieldBasenodeData, 6) mapData->CellDatas[i].BaseNode.House = ""; } - if (auto pSection = CINI::CurrentDocument->GetSection("Houses")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Houses")) { for (const auto& [key, house] : pSection->GetEntities()) { - if (auto pHouse = CINI::CurrentDocument->GetSection(house)) + if (auto pHouse = CINIExt::CurrentDocument->GetSection(house)) { for (int j = 0; j < pHouse->GetInteger("NodeCount"); ++j) { @@ -1646,7 +1650,7 @@ DEFINE_HOOK(4AA111, CMapData_DeleteTerrain, 6) DEFINE_HOOK(4BA5F0, CMapData_UpdateFieldTubeData, 6) { CMapDataExt::Tubes.clear(); - if (auto pSection = CINI::CurrentDocument->GetSection("Tubes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Tubes")) { for (const auto& [key, value] : pSection->GetEntities()) { diff --git a/FA2sp/Ext/CMapValidator/Body.cpp b/FA2sp/Ext/CMapValidator/Body.cpp index 9497bc23..67a7c12e 100644 --- a/FA2sp/Ext/CMapValidator/Body.cpp +++ b/FA2sp/Ext/CMapValidator/Body.cpp @@ -23,7 +23,7 @@ void CMapValidatorExt::ProgramStartupInit() void CMapValidatorExt::ValidateOverlayLimit(BOOL& result) { - bool needNewIniFormat = CINI::CurrentDocument->GetInteger("Basic", "NewINIFormat", 4) > 4; + bool needNewIniFormat = CINIExt::CurrentDocument->GetInteger("Basic", "NewINIFormat", 4) > 4; if (!needNewIniFormat) { for (const auto& cellExt : CMapDataExt::CellDataExts) @@ -441,7 +441,7 @@ void CMapValidatorExt::ValidateEmptyTeamTrigger(BOOL& result) return param == "15" || param == "TeamTypes"; }; auto teamExists = [](ppmfc::CString team) { - if (auto pSection = CINI::CurrentDocument->GetSection("TeamTypes")) { + if (auto pSection = CINIExt::CurrentDocument->GetSection("TeamTypes")) { for (auto& pair : pSection->GetEntities()) { if (pair.second == team) return true; @@ -530,7 +530,7 @@ void CMapValidatorExt::ValidateEmptyTeamTrigger(BOOL& result) void CMapValidatorExt::ValidateTubes(BOOL& result) { CMapDataExt::Tubes.clear(); - if (auto pSection = CINI::CurrentDocument->GetSection("Tubes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Tubes")) { for (const auto& [key, value] : pSection->GetEntities()) { diff --git a/FA2sp/Ext/CPropertyAircraft/Hooks.cpp b/FA2sp/Ext/CPropertyAircraft/Hooks.cpp index 7d73d01e..7562e654 100644 --- a/FA2sp/Ext/CPropertyAircraft/Hooks.cpp +++ b/FA2sp/Ext/CPropertyAircraft/Hooks.cpp @@ -1,4 +1,5 @@ #include +#include "../../Miscs/Hooks.INI.h" #include "../../FA2sp/Helpers/Translations.h" #include "../../FA2sp/Helpers/Helper.h" #include "../../FA2sp/Ext/CMapData/Body.h" @@ -67,7 +68,7 @@ DEFINE_HOOK(4448F0, Miscs_LoadParams_Houses, 7) while(cb->DeleteString(0) != CB_ERR); - auto pHouse = CINI::CurrentDocument->GetSection("Houses"); + auto pHouse = CINIExt::CurrentDocument->GetSection("Houses"); INISection* pSecton = nullptr; if (CMapDataExt::IsInitingPropertyDialog) diff --git a/FA2sp/Ext/CRandomTree/Hooks.cpp b/FA2sp/Ext/CRandomTree/Hooks.cpp index 2d341767..2eb875a9 100644 --- a/FA2sp/Ext/CRandomTree/Hooks.cpp +++ b/FA2sp/Ext/CRandomTree/Hooks.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include #include @@ -43,7 +44,7 @@ DEFINE_HOOK(4D4150, CRandomTree_OnInitDialog, 7) } } - auto thisTheater = CINI::CurrentDocument().GetString("Map", "Theater"); + auto thisTheater = CINIExt::CurrentDocument().GetString("Map", "Theater"); thisTheater.MakeUpper(); thisTheater += "Limits"; int TreeMin = CINI::FAData->GetInteger(thisTheater, "TreeMin"); diff --git a/FA2sp/Ext/CSingleplayerSettings/Body.cpp b/FA2sp/Ext/CSingleplayerSettings/Body.cpp index bfbc1f0b..963c4e70 100644 --- a/FA2sp/Ext/CSingleplayerSettings/Body.cpp +++ b/FA2sp/Ext/CSingleplayerSettings/Body.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include #include @@ -27,7 +28,7 @@ BOOL CSingleplayerSettingsExt::PreTranslateMessageExt(MSG* pMsg) auto pWnd = (ppmfc::CWnd*)this->GetDlgItem(nID); ppmfc::CString buffer; pWnd->GetWindowText(buffer); - CINI::CurrentDocument->WriteString("Ranking", pKey, buffer); + CINIExt::CurrentDocument->WriteString("Ranking", pKey, buffer); }; auto processGeneral = [&pMsg, this](int nID, const char* pKey)// -> bool { @@ -35,9 +36,9 @@ BOOL CSingleplayerSettingsExt::PreTranslateMessageExt(MSG* pMsg) ppmfc::CString buffer; pWnd->GetWindowText(buffer); if (buffer == "") - CINI::CurrentDocument->DeleteKey("General", pKey); + CINIExt::CurrentDocument->DeleteKey("General", pKey); else - CINI::CurrentDocument->WriteString("General", pKey, buffer); + CINIExt::CurrentDocument->WriteString("General", pKey, buffer); }; if (pMsg->hwnd == pWnd->m_hWnd) diff --git a/FA2sp/Ext/CSingleplayerSettings/Hooks.cpp b/FA2sp/Ext/CSingleplayerSettings/Hooks.cpp index 1d105599..042615e3 100644 --- a/FA2sp/Ext/CSingleplayerSettings/Hooks.cpp +++ b/FA2sp/Ext/CSingleplayerSettings/Hooks.cpp @@ -1,4 +1,5 @@ #include "Body.h" +#include "../../Miscs/Hooks.INI.h" #include "../../Helpers/Translations.h" @@ -33,19 +34,19 @@ DEFINE_HOOK(4DA1A6, CSingleplayerSettings_UpdateDialog, 5) { GET(CSingleplayerSettings*, pThis, EDI); - pThis->SetDlgItemText(1356, CINI::CurrentDocument->GetString("Ranking", "ParTimeEasy")); - pThis->SetDlgItemText(1357, CINI::CurrentDocument->GetString("Ranking", "ParTimeMedium")); - pThis->SetDlgItemText(1358, CINI::CurrentDocument->GetString("Ranking", "ParTimeHard")); - pThis->SetDlgItemText(1359, CINI::CurrentDocument->GetString("Ranking", "OverParTitle")); - pThis->SetDlgItemText(1360, CINI::CurrentDocument->GetString("Ranking", "OverParMessage")); - pThis->SetDlgItemText(1361, CINI::CurrentDocument->GetString("Ranking", "UnderParTitle")); - pThis->SetDlgItemText(1362, CINI::CurrentDocument->GetString("Ranking", "UnderParMessage")); - pThis->SetDlgItemText(1366, CINI::CurrentDocument->GetString("General", "CampaignMoneyDeltaEasy")); - pThis->SetDlgItemText(1368, CINI::CurrentDocument->GetString("General", "CampaignMoneyDeltaHard")); - pThis->SetDlgItemText(1370, CINI::CurrentDocument->GetString("General", "SpyMoneyStealPercent")); - pThis->SetDlgItemText(1372, CINI::CurrentDocument->GetString("General", "TeamDelays")); - pThis->SetDlgItemText(1374, CINI::CurrentDocument->GetString("General", "PrismSupportModifier")); - pThis->SetDlgItemText(1376, CINI::CurrentDocument->GetString("General", "DefaultMirageDisguises")); + pThis->SetDlgItemText(1356, CINIExt::CurrentDocument->GetString("Ranking", "ParTimeEasy")); + pThis->SetDlgItemText(1357, CINIExt::CurrentDocument->GetString("Ranking", "ParTimeMedium")); + pThis->SetDlgItemText(1358, CINIExt::CurrentDocument->GetString("Ranking", "ParTimeHard")); + pThis->SetDlgItemText(1359, CINIExt::CurrentDocument->GetString("Ranking", "OverParTitle")); + pThis->SetDlgItemText(1360, CINIExt::CurrentDocument->GetString("Ranking", "OverParMessage")); + pThis->SetDlgItemText(1361, CINIExt::CurrentDocument->GetString("Ranking", "UnderParTitle")); + pThis->SetDlgItemText(1362, CINIExt::CurrentDocument->GetString("Ranking", "UnderParMessage")); + pThis->SetDlgItemText(1366, CINIExt::CurrentDocument->GetString("General", "CampaignMoneyDeltaEasy")); + pThis->SetDlgItemText(1368, CINIExt::CurrentDocument->GetString("General", "CampaignMoneyDeltaHard")); + pThis->SetDlgItemText(1370, CINIExt::CurrentDocument->GetString("General", "SpyMoneyStealPercent")); + pThis->SetDlgItemText(1372, CINIExt::CurrentDocument->GetString("General", "TeamDelays")); + pThis->SetDlgItemText(1374, CINIExt::CurrentDocument->GetString("General", "PrismSupportModifier")); + pThis->SetDlgItemText(1376, CINIExt::CurrentDocument->GetString("General", "DefaultMirageDisguises")); return 0; } \ No newline at end of file diff --git a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/ScriptSort.cpp b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/ScriptSort.cpp index cf49cb56..8eb9c599 100644 --- a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/ScriptSort.cpp +++ b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/ScriptSort.cpp @@ -1,4 +1,5 @@ #include "ScriptSort.h" +#include "../../../Miscs/Hooks.INI.h" #include "../../../FA2sp.h" #include "../../../Helpers/STDHelpers.h" @@ -18,7 +19,7 @@ void ScriptSort::LoadAllTriggers() // TODO : // Optimisze the efficiency SendMessage(this->GetHwnd(), WM_SETREDRAW, FALSE, 0); - if (auto pSection = CINI::CurrentDocument->GetSection("ScriptTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("ScriptTypes")) { for (auto& pair : pSection->GetEntities()) { @@ -51,7 +52,7 @@ BOOL ScriptSort::OnNotify(LPNMTREEVIEW lpNmTreeView) bool Success = false; if (IsWindowVisible(CNewScript::GetHandle())) { - auto pStr = CINI::CurrentDocument->GetString(pID, "Name"); + auto pStr = CINIExt::CurrentDocument->GetString(pID, "Name"); FString space1 = " ("; FString space2 = ")"; @@ -65,7 +66,7 @@ BOOL ScriptSort::OnNotify(LPNMTREEVIEW lpNmTreeView) } //else if (IsWindowVisible(CNewTeamTypes::GetHandle())) //{ - // auto pStr = CINI::CurrentDocument->GetString(pID, "Name"); + // auto pStr = CINIExt::CurrentDocument->GetString(pID, "Name"); // FString space1 = " ("; // FString space2 = ")"; // @@ -270,7 +271,7 @@ HTREEITEM ScriptSort::FindLabel(HTREEITEM hItemParent, LPCSTR pszLabel) const std::vector ScriptSort::GetGroup(FString triggerId, FString& name) const { - FString pSrc = CINI::CurrentDocument->GetString(triggerId, "Name", ""); + FString pSrc = CINIExt::CurrentDocument->GetString(triggerId, "Name", ""); auto ret = std::vector{}; int nStart = pSrc.Find('['); diff --git a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TagSort.cpp b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TagSort.cpp index 0a93f1ee..e69e316f 100644 --- a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TagSort.cpp +++ b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TagSort.cpp @@ -1,4 +1,5 @@ #include "TagSort.h" +#include "../../../Miscs/Hooks.INI.h" #include "../../../FA2sp.h" #include "../../../Helpers/STDHelpers.h" @@ -32,7 +33,7 @@ void TagSort::LoadAllTriggers() ExtConfigs::InitializeMap = false; this->Clear(); BuildingTags.clear(); - if (auto pObjSection = CINI::CurrentDocument->GetSection("Structures")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Structures")) { for (auto& pair : pObjSection->GetEntities()) { @@ -42,7 +43,7 @@ void TagSort::LoadAllTriggers() } } AircraftTags.clear(); - if (auto pObjSection = CINI::CurrentDocument->GetSection("Aircraft")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Aircraft")) { for (auto& pair : pObjSection->GetEntities()) { @@ -52,7 +53,7 @@ void TagSort::LoadAllTriggers() } } UnitTags.clear(); - if (auto pObjSection = CINI::CurrentDocument->GetSection("Units")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Units")) { for (auto& pair : pObjSection->GetEntities()) { @@ -62,7 +63,7 @@ void TagSort::LoadAllTriggers() } } InfantryTags.clear(); - if (auto pObjSection = CINI::CurrentDocument->GetSection("Infantry")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Infantry")) { for (auto& pair : pObjSection->GetEntities()) { @@ -72,19 +73,19 @@ void TagSort::LoadAllTriggers() } } TeamTags.clear(); - if (auto pObjSection = CINI::CurrentDocument->GetSection("TeamTypes")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("TeamTypes")) { for (auto& pair : pObjSection->GetEntities()) { - if (CINI::CurrentDocument->SectionExists(pair.second)) + if (CINIExt::CurrentDocument->SectionExists(pair.second)) { - auto tag = CINI::CurrentDocument->GetString(pair.second, "Tag"); + auto tag = CINIExt::CurrentDocument->GetString(pair.second, "Tag"); TeamTags[tag].push_back(pair.second); } } } CellTagTags.clear(); - if (auto pObjSection = CINI::CurrentDocument->GetSection("CellTags")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("CellTags")) { for (auto& pair : pObjSection->GetEntities()) { @@ -92,7 +93,7 @@ void TagSort::LoadAllTriggers() } } TriggerTags.clear(); - if (auto pObjSection = CINI::CurrentDocument->GetSection("Triggers")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Triggers")) { for (auto& pair : pObjSection->GetEntities()) { @@ -114,7 +115,7 @@ void TagSort::LoadAllTriggers() } } TriggerTagsParent.clear(); - if (auto pObjSection = CINI::CurrentDocument->GetSection("Triggers")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Triggers")) { for (auto& pair : pObjSection->GetEntities()) { @@ -127,14 +128,14 @@ void TagSort::LoadAllTriggers() } } TagTriggers.clear(); - if (auto pObjSection = CINI::CurrentDocument->GetSection("Tags")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Tags")) { for (auto& pair : pObjSection->GetEntities()) { auto atoms = FString::SplitString(pair.second); if (atoms.size() > 2) { - if (auto pObjSection = CINI::CurrentDocument->GetSection("Triggers")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Triggers")) { for (auto& pair2 : pObjSection->GetEntities()) { @@ -150,7 +151,7 @@ void TagSort::LoadAllTriggers() } SendMessage(this->GetHwnd(), WM_SETREDRAW, FALSE, 0); - if (auto pSection = CINI::CurrentDocument->GetSection("Tags")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Tags")) { for (auto& pair : pSection->GetEntities()) { @@ -193,7 +194,7 @@ BOOL TagSort::OnNotify(LPNMTREEVIEW lpNmTreeView) } if (IsWindowVisible(CNewTrigger::GetFirstValidInstance().GetHandle())) { - FString pStr = CINI::CurrentDocument->GetString("Triggers", pID); + FString pStr = CINIExt::CurrentDocument->GetString("Triggers", pID); auto results = FString::SplitString(pStr); if (results.size() > 3) { @@ -211,7 +212,7 @@ BOOL TagSort::OnNotify(LPNMTREEVIEW lpNmTreeView) } if (IsWindowVisible(CNewTeamTypes::GetHandle())) { - FString pStr = CINI::CurrentDocument->GetString(pID, "Name"); + FString pStr = CINIExt::CurrentDocument->GetString(pID, "Name"); FString space1 = " ("; FString space2 = ")"; @@ -249,7 +250,7 @@ BOOL TagSort::OnNotify(LPNMTREEVIEW lpNmTreeView) if (!finished) { - if (auto pSection = CINI::CurrentDocument->GetSection("CellTags")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("CellTags")) { for (auto& pairObj : pSection->GetEntities()) { @@ -472,7 +473,7 @@ HTREEITEM TagSort::FindLabel(HTREEITEM hItemParent, LPCSTR pszLabel) const std::vector TagSort::GetGroup(FString triggerId, FString& name) const { - FString pSrc = CINI::CurrentDocument->GetString("Tags", triggerId, ""); + FString pSrc = CINIExt::CurrentDocument->GetString("Tags", triggerId, ""); auto ret = FString::SplitString(pSrc, 1); pSrc = ret[1]; @@ -510,7 +511,7 @@ void TagSort::AddAttachedTrigger(HTREEITEM hParent, FString triggerID, FString p if (TriggerTags[triggerID] != "") if (HTREEITEM hNode = this->FindLabel(hParent, parentName)) { - FString pTrigger2 = CINI::CurrentDocument->GetString("Triggers", TriggerTags[triggerID], ""); + FString pTrigger2 = CINIExt::CurrentDocument->GetString("Triggers", TriggerTags[triggerID], ""); auto RET2 = FString::SplitString(pTrigger2, 2); pTrigger2 = RET2[2]; pTrigger2 = FString(Translations::TranslateOrDefault("Sort.AttachedTrigger", "Attached Trigger:")) + " " + pTrigger2; @@ -545,7 +546,7 @@ void TagSort::AddAttachedTriggerReverse(HTREEITEM hParent, FString triggerID, FS } } - FString pTrigger2 = CINI::CurrentDocument->GetString("Triggers", parentTrigger, ""); + FString pTrigger2 = CINIExt::CurrentDocument->GetString("Triggers", parentTrigger, ""); auto RET2 = FString::SplitString(pTrigger2, 2); pTrigger2 = RET2[2]; @@ -622,15 +623,15 @@ void TagSort::AddTrigger(std::vector group, FString name, FString id) c auto hTagItem = TreeViewHelper::InsertTreeItem(this->GetHwnd(), text, id, hParent); this->IndexAdd(hParent, text, hTagItem); - auto tag = CINI::CurrentDocument->GetString("Tags", id); + auto tag = CINIExt::CurrentDocument->GetString("Tags", id); auto atoms = FString::SplitString(tag); if (atoms.size() >= 3) { auto& triggerID = atoms[2]; - if (!CINI::CurrentDocument->SectionExists("Triggers")) + if (!CINIExt::CurrentDocument->SectionExists("Triggers")) return; - FString pSrc = CINI::CurrentDocument->GetString("Triggers", triggerID, ""); + FString pSrc = CINIExt::CurrentDocument->GetString("Triggers", triggerID, ""); if (pSrc == "") return; @@ -793,7 +794,7 @@ void TagSort::AddTrigger(std::vector group, FString name, FString id) c if (HTREEITEM hNode = this->FindLabel(hParentTeam, objList)) { - FString uiname = FString(CINI::CurrentDocument->GetString(teamID, "Name")) + " (" + teamID + ")"; + FString uiname = FString(CINIExt::CurrentDocument->GetString(teamID, "Name")) + " (" + teamID + ")"; hParent = hNode; auto hObjItem = TreeViewHelper::InsertTreeItem(this->GetHwnd(), uiname, teamID, hParent); diff --git a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TaskForceSort.cpp b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TaskForceSort.cpp index 6087517d..fc8af0cb 100644 --- a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TaskForceSort.cpp +++ b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TaskForceSort.cpp @@ -1,4 +1,5 @@ #include "TaskforceSort.h" +#include "../../../Miscs/Hooks.INI.h" #include "../../../FA2sp.h" #include "../../../Helpers/STDHelpers.h" @@ -17,7 +18,7 @@ void TaskforceSort::LoadAllTriggers() // TODO : // Optimisze the efficiency SendMessage(this->GetHwnd(), WM_SETREDRAW, FALSE, 0); - if (auto pSection = CINI::CurrentDocument->GetSection("TaskForces")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("TaskForces")) { for (auto& pair : pSection->GetEntities()) { @@ -49,7 +50,7 @@ BOOL TaskforceSort::OnNotify(LPNMTREEVIEW lpNmTreeView) bool Success = false; if (IsWindowVisible(CNewTaskforce::GetHandle())) { - auto pStr = CINI::CurrentDocument->GetString(pID, "Name"); + auto pStr = CINIExt::CurrentDocument->GetString(pID, "Name"); FString space1 = " ("; FString space2 = ")"; @@ -63,7 +64,7 @@ BOOL TaskforceSort::OnNotify(LPNMTREEVIEW lpNmTreeView) } //else if (IsWindowVisible(CNewTeamTypes::GetHandle())) //{ - // auto pStr = CINI::CurrentDocument->GetString(pID, "Name"); + // auto pStr = CINIExt::CurrentDocument->GetString(pID, "Name"); // FString space1 = " ("; // FString space2 = ")"; // @@ -267,7 +268,7 @@ HTREEITEM TaskforceSort::FindLabel(HTREEITEM hItemParent, LPCSTR pszLabel) const std::vector TaskforceSort::GetGroup(FString triggerId, FString& name) const { - FString pSrc = CINI::CurrentDocument->GetString(triggerId, "Name", ""); + FString pSrc = CINIExt::CurrentDocument->GetString(triggerId, "Name", ""); auto ret = std::vector{}; int nStart = pSrc.Find('['); diff --git a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TeamSort.cpp b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TeamSort.cpp index 9860a576..71a1e58a 100644 --- a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TeamSort.cpp +++ b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TeamSort.cpp @@ -1,4 +1,5 @@ #include "TeamSort.h" +#include "../../../Miscs/Hooks.INI.h" #include "../../../FA2sp.h" #include "../../../Helpers/STDHelpers.h" @@ -21,7 +22,7 @@ void TeamSort::LoadAllTriggers() // TODO : // Optimisze the efficiency SendMessage(this->GetHwnd(), WM_SETREDRAW, FALSE, 0); - if (auto pSection = CINI::CurrentDocument->GetSection("TeamTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("TeamTypes")) { for (auto& pair : pSection->GetEntities()) { @@ -53,7 +54,7 @@ BOOL TeamSort::OnNotify(LPNMTREEVIEW lpNmTreeView) bool Success = false; if (IsWindowVisible(CNewTeamTypes::GetHandle())) { - auto pStr = CINI::CurrentDocument->GetString(pID, "Name"); + auto pStr = CINIExt::CurrentDocument->GetString(pID, "Name"); FString space1 = " ("; FString space2 = ")"; @@ -68,7 +69,7 @@ BOOL TeamSort::OnNotify(LPNMTREEVIEW lpNmTreeView) } //else if (IsWindowVisible(CNewTrigger::GetHandle())) //{ - // auto pStr = CINI::CurrentDocument->GetString(pID, "Name"); + // auto pStr = CINIExt::CurrentDocument->GetString(pID, "Name"); // FString space = " - "; // for (int i = 0; i < EVENT_PARAM_COUNT; i++) // { @@ -283,7 +284,7 @@ HTREEITEM TeamSort::FindLabel(HTREEITEM hItemParent, LPCSTR pszLabel) const std::vector TeamSort::GetGroup(FString triggerId, FString& name) const { - FString pSrc = CINI::CurrentDocument->GetString(triggerId, "Name", ""); + FString pSrc = CINIExt::CurrentDocument->GetString(triggerId, "Name", ""); auto ret = std::vector{}; int nStart = pSrc.Find('['); diff --git a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TriggerSort.cpp b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TriggerSort.cpp index aea5f2ea..94b7f635 100644 --- a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TriggerSort.cpp +++ b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/TriggerSort.cpp @@ -1,4 +1,5 @@ #include "TriggerSort.h" +#include "../../../Miscs/Hooks.INI.h" #include "../../../FA2sp.h" #include "../../../Helpers/STDHelpers.h" @@ -29,7 +30,7 @@ void TriggerSort::LoadAllTriggers() { CMapDataExt::Triggers.clear(); FMap TagMap; - if (auto pSection = CINI::CurrentDocument().GetSection("Tags")) + if (auto pSection = CINIExt::CurrentDocument().GetSection("Tags")) { for (auto& kvp : pSection->GetEntities()) { @@ -40,7 +41,7 @@ void TriggerSort::LoadAllTriggers() TagMap[tagAtoms[2]] = kvp.first; } } - if (auto pSection = CINI::CurrentDocument->GetSection("Triggers")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Triggers")) { for (const auto& pair : pSection->GetEntities()) { @@ -314,7 +315,7 @@ void TriggerSort::AddAttachedTrigger(HTREEITEM hParent, FString triggerID, FStri if (TriggerTags[triggerID] != "") if (HTREEITEM hNode = this->FindLabel(hParent, parentName)) { - FString pTrigger2 = CINI::CurrentDocument->GetString("Triggers", TriggerTags[triggerID], ""); + FString pTrigger2 = CINIExt::CurrentDocument->GetString("Triggers", TriggerTags[triggerID], ""); auto RET2 = FString::SplitString(pTrigger2); if (RET2.size() > 2) { @@ -349,7 +350,7 @@ void TriggerSort::AddAttachedTriggerReverse(HTREEITEM hParent, FString triggerID } } - FString pTrigger2 = CINI::CurrentDocument->GetString("Triggers", parentTrigger, ""); + FString pTrigger2 = CINIExt::CurrentDocument->GetString("Triggers", parentTrigger, ""); auto RET2 = FString::SplitString(pTrigger2); if (RET2.size() > 2) { @@ -368,7 +369,7 @@ void TriggerSort::AddAttachedTriggerReverse(HTREEITEM hParent, FString triggerID std::vector TriggerSort::GetGroup(FString triggerId, FString& name) const { - FString pSrc = CINI::CurrentDocument->GetString("Triggers", triggerId, ""); + FString pSrc = CINIExt::CurrentDocument->GetString("Triggers", triggerId, ""); auto ret = FString::SplitString(pSrc, 2); pSrc = ret[2]; diff --git a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/WaypointSort.cpp b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/WaypointSort.cpp index 6ae798a8..e649ce76 100644 --- a/FA2sp/Ext/CTileSetBrowserFrame/TabPages/WaypointSort.cpp +++ b/FA2sp/Ext/CTileSetBrowserFrame/TabPages/WaypointSort.cpp @@ -1,4 +1,5 @@ #include "WaypointSort.h" +#include "../../../Miscs/Hooks.INI.h" #include "../../../FA2sp.h" #include "../../../Helpers/STDHelpers.h" @@ -150,7 +151,7 @@ void WaypointSort::LoadAllTriggers() InitCache(); - auto pSection = CINI::CurrentDocument->GetSection("Waypoints"); + auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints"); if (!pSection) { ExtConfigs::InitializeMap = true; @@ -235,7 +236,7 @@ void WaypointSort::LoadAllTriggers() } } - if (auto pScriptSection = CINI::CurrentDocument->GetSection("ScriptTypes")) + if (auto pScriptSection = CINIExt::CurrentDocument->GetSection("ScriptTypes")) { for (auto& pair : pScriptSection->GetEntities()) { @@ -245,7 +246,7 @@ void WaypointSort::LoadAllTriggers() { char id[10]; _itoa(i, id, 10); - auto line = CINI::CurrentDocument->GetString(pair.second, id); + auto line = CINIExt::CurrentDocument->GetString(pair.second, id); if (line == "") continue; @@ -261,7 +262,7 @@ void WaypointSort::LoadAllTriggers() { FString text; text.Format(Translations::TranslateOrDefault("ObjectInfo.Waypoint.Script", - "Script: %s (%s)"), CINI::CurrentDocument->GetString(pair.second, "Name"), pair.second); + "Script: %s (%s)"), CINIExt::CurrentDocument->GetString(pair.second, "Name"), pair.second); std::string textStr = text; std::string idStr = FString(pair.second); for (auto& wp : scriptWPs) @@ -270,19 +271,19 @@ void WaypointSort::LoadAllTriggers() } } - if (auto pTeamSection = CINI::CurrentDocument->GetSection("TeamTypes")) + if (auto pTeamSection = CINIExt::CurrentDocument->GetSection("TeamTypes")) { for (auto& pair : pTeamSection->GetEntities()) { - auto wp = CINI::CurrentDocument->GetString(pair.second, "Waypoint"); - auto wp2 = CINI::CurrentDocument->GetString(pair.second, "TransportWaypoint"); + auto wp = CINIExt::CurrentDocument->GetString(pair.second, "Waypoint"); + auto wp2 = CINIExt::CurrentDocument->GetString(pair.second, "TransportWaypoint"); int wpNum = ProcessWaypointLetter(wp); int wpNum2 = ProcessWaypointLetter(wp2); - if (wpNum >= 0 || wpNum2 >= 0 && CINI::CurrentDocument->GetBool(pair.second, "UseTransportOrigin")) + if (wpNum >= 0 || wpNum2 >= 0 && CINIExt::CurrentDocument->GetBool(pair.second, "UseTransportOrigin")) { FString text; text.Format(Translations::TranslateOrDefault("ObjectInfo.Waypoint.Team", - "Team: %s (%s)"), CINI::CurrentDocument->GetString(pair.second, "Name"), pair.second); + "Team: %s (%s)"), CINIExt::CurrentDocument->GetString(pair.second, "Name"), pair.second); std::string textStr = text; std::string idStr = FString(pair.second); @@ -361,7 +362,7 @@ BOOL WaypointSort::OnNotify(LPNMTREEVIEW lpNmTreeView) bool Success = false; if (strlen(pID) && ExtConfigs::InitializeMap) { - if (auto pCord = CINI::CurrentDocument->TryGetString("Waypoints", pID)) + if (auto pCord = CINIExt::CurrentDocument->TryGetString("Waypoints", pID)) { auto second = atoi(*pCord); if (second > 0) @@ -371,7 +372,7 @@ BOOL WaypointSort::OnNotify(LPNMTREEVIEW lpNmTreeView) } if (IsWindowVisible(CNewTrigger::GetFirstValidInstance().GetHandle())) { - FString pStr = CINI::CurrentDocument->GetString("Triggers", pID); + FString pStr = CINIExt::CurrentDocument->GetString("Triggers", pID); auto results = FString::SplitString(pStr); if (results.size() > 3) { @@ -389,7 +390,7 @@ BOOL WaypointSort::OnNotify(LPNMTREEVIEW lpNmTreeView) } if (IsWindowVisible(CNewScript::GetHandle())) { - FString pStr = CINI::CurrentDocument->GetString(pID, "Name"); + FString pStr = CINIExt::CurrentDocument->GetString(pID, "Name"); FString space1 = " ("; FString space2 = ")"; @@ -403,7 +404,7 @@ BOOL WaypointSort::OnNotify(LPNMTREEVIEW lpNmTreeView) } if (IsWindowVisible(CNewTeamTypes::GetHandle())) { - FString pStr = CINI::CurrentDocument->GetString(pID, "Name"); + FString pStr = CINIExt::CurrentDocument->GetString(pID, "Name"); FString space1 = " ("; FString space2 = ")"; @@ -549,7 +550,7 @@ HTREEITEM WaypointSort::FindLabel(HTREEITEM hItemParent, LPCSTR pszLabel) const std::vector WaypointSort::GetGroup(FString triggerId, FString& name) const { - FString pSrc = CINI::CurrentDocument->GetString(triggerId, "Name", ""); + FString pSrc = CINIExt::CurrentDocument->GetString(triggerId, "Name", ""); auto ret = std::vector{}; int nStart = pSrc.Find('['); @@ -691,7 +692,7 @@ void WaypointSort::AddTrigger(FString triggerId, int x, int y) const } } - if (auto pSection = CINI::CurrentDocument->GetSection("ScriptTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("ScriptTypes")) { for (auto& pair : pSection->GetEntities()) { @@ -701,7 +702,7 @@ void WaypointSort::AddTrigger(FString triggerId, int x, int y) const { char id[10]; _itoa(i, id, 10); - auto line = CINI::CurrentDocument->GetString(pair.second, id); + auto line = CINIExt::CurrentDocument->GetString(pair.second, id); if (line == "") continue; @@ -717,12 +718,12 @@ void WaypointSort::AddTrigger(FString triggerId, int x, int y) const { FString text; text.Format(Translations::TranslateOrDefault("ObjectInfo.Waypoint.Script", - "Script: %s (%s)"), CINI::CurrentDocument->GetString(pair.second, "Name"), pair.second); + "Script: %s (%s)"), CINIExt::CurrentDocument->GetString(pair.second, "Name"), pair.second); TreeViewHelper::InsertTreeItem(this->GetHwnd(), text, pair.second, hParent); } } } - if (auto pSection = CINI::CurrentDocument->GetSection("TeamTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("TeamTypes")) { auto process = [](const char* s) { @@ -740,15 +741,15 @@ void WaypointSort::AddTrigger(FString triggerId, int x, int y) const }; for (auto& pair : pSection->GetEntities()) { - auto wp = CINI::CurrentDocument->GetString(pair.second, "Waypoint"); - auto wp2 = CINI::CurrentDocument->GetString(pair.second, "TransportWaypoint"); + auto wp = CINIExt::CurrentDocument->GetString(pair.second, "Waypoint"); + auto wp2 = CINIExt::CurrentDocument->GetString(pair.second, "TransportWaypoint"); if (process(wp) == atoi(triggerId) - || (process(wp2) == atoi(triggerId) && CINI::CurrentDocument->GetBool(pair.second, "UseTransportOrigin"))) + || (process(wp2) == atoi(triggerId) && CINIExt::CurrentDocument->GetBool(pair.second, "UseTransportOrigin"))) { FString text; text.Format(Translations::TranslateOrDefault("ObjectInfo.Waypoint.Team", - "Team: %s (%s)"), CINI::CurrentDocument->GetString(pair.second, "Name"), pair.second); + "Team: %s (%s)"), CINIExt::CurrentDocument->GetString(pair.second, "Name"), pair.second); TreeViewHelper::InsertTreeItem(this->GetHwnd(), text, pair.second, hParent); } } diff --git a/FA2sp/ExtraWindow/CBatchTrigger/CBatchTrigger.cpp b/FA2sp/ExtraWindow/CBatchTrigger/CBatchTrigger.cpp index 381b84ff..ceaf7ae9 100644 --- a/FA2sp/ExtraWindow/CBatchTrigger/CBatchTrigger.cpp +++ b/FA2sp/ExtraWindow/CBatchTrigger/CBatchTrigger.cpp @@ -21,7 +21,7 @@ HWND CBatchTrigger::m_hwnd; CFinalSunDlg* CBatchTrigger::m_parent; -CINI& CBatchTrigger::map = CINI::CurrentDocument; +CINIExt& CBatchTrigger::map = CINIExt::CurrentDocument; MultimapHelper& CBatchTrigger::rules = Variables::RulesMap; HWND CBatchTrigger::hListbox; @@ -720,9 +720,9 @@ void CBatchTrigger::Update(bool afterInit, bool updateTrigger) tagNameIndex.clear(); teamNameIndex.clear(); - auto pTag = CINI::CurrentDocument().GetSection("Tags"); - auto pTrigger = CINI::CurrentDocument().GetSection("Triggers"); - auto pTeam = CINI::CurrentDocument().GetSection("TeamTypes"); + auto pTag = CINIExt::CurrentDocument().GetSection("Tags"); + auto pTrigger = CINIExt::CurrentDocument().GetSection("Triggers"); + auto pTeam = CINIExt::CurrentDocument().GetSection("TeamTypes"); objects.reserve( (pTag ? pTag->GetEntities().size() : 0) + diff --git a/FA2sp/ExtraWindow/CBatchTrigger/CBatchTrigger.h b/FA2sp/ExtraWindow/CBatchTrigger/CBatchTrigger.h index f1000c4d..8063b8c2 100644 --- a/FA2sp/ExtraWindow/CBatchTrigger/CBatchTrigger.h +++ b/FA2sp/ExtraWindow/CBatchTrigger/CBatchTrigger.h @@ -11,6 +11,7 @@ class Trigger; class HelpDlg; +class CINIExt; struct CellColor { int row; @@ -120,7 +121,7 @@ class CBatchTrigger private: static HWND m_hwnd; static CFinalSunDlg* m_parent; - static CINI& map; + static CINIExt& map; static MultimapHelper& rules; static HWND hListbox; diff --git a/FA2sp/ExtraWindow/CLuaConsole/CLuaConsole.cpp b/FA2sp/ExtraWindow/CLuaConsole/CLuaConsole.cpp index 52d82005..59bb6b3a 100644 --- a/FA2sp/ExtraWindow/CLuaConsole/CLuaConsole.cpp +++ b/FA2sp/ExtraWindow/CLuaConsole/CLuaConsole.cpp @@ -229,13 +229,13 @@ void CLuaConsole::InitializeLuaState() Lua.set_function("local_height", []() {return CMapData::Instance->LocalSize.Height; }); Lua.set_function("local_top", []() {return CMapData::Instance->LocalSize.Top; }); Lua.set_function("local_left", []() {return CMapData::Instance->LocalSize.Left; }); - Lua.set_function("waypoint_count", []() {return CINI::CurrentDocument->GetKeyCount("Waypoints"); }); - Lua.set_function("unit_count", []() {return CINI::CurrentDocument->GetKeyCount("Units"); }); - Lua.set_function("infantry_count", []() {return CINI::CurrentDocument->GetKeyCount("Infantry"); }); - Lua.set_function("building_count", []() {return CINI::CurrentDocument->GetKeyCount("Structures"); }); - Lua.set_function("aircraft_count", []() {return CINI::CurrentDocument->GetKeyCount("Aircraft"); }); - Lua.set_function("terrain_count", []() {return CINI::CurrentDocument->GetKeyCount("Terrain"); }); - Lua.set_function("smudge_count", []() {return CINI::CurrentDocument->GetKeyCount("Smudge"); }); + Lua.set_function("waypoint_count", []() {return CINIExt::CurrentDocument->GetKeyCount("Waypoints"); }); + Lua.set_function("unit_count", []() {return CINIExt::CurrentDocument->GetKeyCount("Units"); }); + Lua.set_function("infantry_count", []() {return CINIExt::CurrentDocument->GetKeyCount("Infantry"); }); + Lua.set_function("building_count", []() {return CINIExt::CurrentDocument->GetKeyCount("Structures"); }); + Lua.set_function("aircraft_count", []() {return CINIExt::CurrentDocument->GetKeyCount("Aircraft"); }); + Lua.set_function("terrain_count", []() {return CINIExt::CurrentDocument->GetKeyCount("Terrain"); }); + Lua.set_function("smudge_count", []() {return CINIExt::CurrentDocument->GetKeyCount("Smudge"); }); Lua.set_function("player_count", []() { if (CMapData::Instance->IsMultiOnly()) { @@ -244,7 +244,7 @@ void CLuaConsole::InitializeLuaState() { FString key; key.Format("%d", i); - if (CINI::CurrentDocument->TryGetString("Waypoints", key) != nullptr) + if (CINIExt::CurrentDocument->TryGetString("Waypoints", key) != nullptr) wp_count++; } return wp_count; @@ -254,35 +254,35 @@ void CLuaConsole::InitializeLuaState() return 1; } }); - Lua.set_function("house_count", []() { return CINI::CurrentDocument->GetKeyCount("Houses"); }); - Lua.set_function("country_count", []() { return CINI::CurrentDocument->GetKeyCount("Countries"); }); + Lua.set_function("house_count", []() { return CINIExt::CurrentDocument->GetKeyCount("Houses"); }); + Lua.set_function("country_count", []() { return CINIExt::CurrentDocument->GetKeyCount("Countries"); }); Lua.set_function("node_count", [](std::string house) { if (house == "") { - if (auto pSection = CINI::CurrentDocument->GetSection("Houses")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Houses")) { int count = 0; for (auto& [_, house] : pSection->GetEntities()) { - count += CINI::CurrentDocument->GetInteger(house, "NodeCount"); + count += CINIExt::CurrentDocument->GetInteger(house, "NodeCount"); } return count; } } - if (!CINI::CurrentDocument->TryGetString("Houses", house.c_str())) + if (!CINIExt::CurrentDocument->TryGetString("Houses", house.c_str())) { house += " House"; } - if (auto pSection = CINI::CurrentDocument->GetSection(house.c_str())) + if (auto pSection = CINIExt::CurrentDocument->GetSection(house.c_str())) { return pSection->GetInteger("NodeCount"); } return 0; }); - Lua.set_function("trigger_count", []() { return CINI::CurrentDocument->GetKeyCount("Triggers"); }); + Lua.set_function("trigger_count", []() { return CINIExt::CurrentDocument->GetKeyCount("Triggers"); }); Lua.set_function("tile_count", []() { return CMapDataExt::TileDataCount; }); Lua.set_function("tile_set_count", []() { return CMapDataExt::TileSet_starts.size() - 1; }); - Lua.set_function("tag_count", []() { return CINI::CurrentDocument->GetKeyCount("Tags"); }); - Lua.set_function("theater", []() {return CINI::CurrentDocument->GetString("Map", "Theater").GetString(); }); + Lua.set_function("tag_count", []() { return CINIExt::CurrentDocument->GetKeyCount("Tags"); }); + Lua.set_function("theater", []() {return CINIExt::CurrentDocument->GetString("Map", "Theater").GetString(); }); Lua.set_function("is_multiplay", []() {return CMapData::Instance->IsMultiOnly(); }); Lua.set_function("language", []() {return (std::string)FinalAlertConfig::Language; }); Lua.set_function("exe_path", []() {return (std::string)CFinalSunAppExt::ExePathExt; }); diff --git a/FA2sp/ExtraWindow/CLuaConsole/CMcpServer.cpp b/FA2sp/ExtraWindow/CLuaConsole/CMcpServer.cpp index dcbc70b0..52e58eab 100644 --- a/FA2sp/ExtraWindow/CLuaConsole/CMcpServer.cpp +++ b/FA2sp/ExtraWindow/CLuaConsole/CMcpServer.cpp @@ -112,7 +112,7 @@ static std::string GetRelativePath(const std::filesystem::path& full, const std: // --------------------------------------------------------------------------- static std::string GetIniSection(const std::string& section) { - auto& pMap = CINI::CurrentDocument; + auto& pMap = CINIExt::CurrentDocument; auto pSection = pMap->GetSection(section.c_str()); if (!pSection) return "Section \"" + section + "\" not found or empty."; @@ -965,7 +965,7 @@ bool CMcpServer::IsRunning() static std::string CaptureMapIniState() { std::ostringstream oss; - for (auto& section : CINI::CurrentDocument->Dict) + for (auto& section : CINIExt::CurrentDocument->Dict) { if (!strcmp(section.first, "Preview") || !strcmp(section.first, "PreviewPack") diff --git a/FA2sp/ExtraWindow/CLuaConsole/LuaFunctions.cpp b/FA2sp/ExtraWindow/CLuaConsole/LuaFunctions.cpp index 144c4004..37fc4a88 100644 --- a/FA2sp/ExtraWindow/CLuaConsole/LuaFunctions.cpp +++ b/FA2sp/ExtraWindow/CLuaConsole/LuaFunctions.cpp @@ -603,7 +603,7 @@ namespace LuaFunctions } ini->ClearAndLoad(GetINIPath()); presets.clear(); - auto currentTheater = CINI::CurrentDocument->GetString("Map", "Theater"); + auto currentTheater = CINIExt::CurrentDocument->GetString("Map", "Theater"); auto itr = ini->Dict.begin(); for (size_t i = 0, sz = ini->Dict.size(); i < sz; ++i, ++itr) { @@ -693,7 +693,7 @@ namespace LuaFunctions if (!ini->SectionExists(ID)) { ini->WriteString(ID, "Name", name.c_str()); ini->WriteString(ID, "Scale", "25"); - ini->WriteString(ID, "Theaters", CINI::CurrentDocument->GetString("Map", "Theater")); + ini->WriteString(ID, "Theaters", CINIExt::CurrentDocument->GetString("Map", "Theater")); auto preset = std::shared_ptr(TerrainGeneratorPreset::create(ID, ini->GetSection(ID))); presets[ID] = preset; current = preset; @@ -1277,13 +1277,13 @@ namespace LuaFunctions } void remove() const { - for (int i = 0; i < CINI::CurrentDocument->GetKeyCount("Infantry"); ++i) + for (int i = 0; i < CINIExt::CurrentDocument->GetKeyCount("Infantry"); ++i) { CInfantryData obj; CMapData::Instance->GetInfantryData(i, obj); if (*this == convert(obj)) { - //CINI::CurrentDocument->DeleteKey("Infantry", CINI::CurrentDocument->GetKeyAt("Infantry", i)); + //CINIExt::CurrentDocument->DeleteKey("Infantry", CINIExt::CurrentDocument->GetKeyAt("Infantry", i)); CMapData::Instance->DeleteInfantryData(i); CLuaConsole::needRedraw = true; CLuaConsole::updateInfantry = true; @@ -1386,13 +1386,13 @@ namespace LuaFunctions } void remove() const { - for (int i = 0; i < CINI::CurrentDocument->GetKeyCount("Units"); ++i) + for (int i = 0; i < CINIExt::CurrentDocument->GetKeyCount("Units"); ++i) { CUnitData obj; CMapData::Instance->GetUnitData(i, obj); if (*this == convert(obj)) { - CINI::CurrentDocument->DeleteKey("Units", CINI::CurrentDocument->GetKeyAt("Units", i)); + CINIExt::CurrentDocument->DeleteKey("Units", CINIExt::CurrentDocument->GetKeyAt("Units", i)); CLuaConsole::needRedraw = true; CLuaConsole::updateUnit = true; CLuaConsole::updateMinimap = true; @@ -1484,13 +1484,13 @@ namespace LuaFunctions } void remove() const { - for (int i = 0; i < CINI::CurrentDocument->GetKeyCount("Aircraft"); ++i) + for (int i = 0; i < CINIExt::CurrentDocument->GetKeyCount("Aircraft"); ++i) { CAircraftData obj; CMapData::Instance->GetAircraftData(i, obj); if (*this == convert(obj)) { - CINI::CurrentDocument->DeleteKey("Aircraft", CINI::CurrentDocument->GetKeyAt("Aircraft", i)); + CINIExt::CurrentDocument->DeleteKey("Aircraft", CINIExt::CurrentDocument->GetKeyAt("Aircraft", i)); CLuaConsole::needRedraw = true; CLuaConsole::updateAircraft = true; CLuaConsole::updateMinimap = true; @@ -1572,7 +1572,7 @@ namespace LuaFunctions this->PoweredOn, this->Upgrades, this->SpotLight, this->Upgrade1, this->Upgrade2, this->Upgrade3, this->AIRepairable, this->Nominal); - CINI::CurrentDocument->WriteString("Structures", id, value); + CINIExt::CurrentDocument->WriteString("Structures", id, value); CLuaConsole::needRedraw = true; CLuaConsole::updateBuilding = true; } @@ -1583,20 +1583,20 @@ namespace LuaFunctions if (this->X == -1 || this->Y == -1) return; auto cell = CMapData::Instance->TryGetCellAt(this->X, this->Y); - if (auto pHouse = CINI::CurrentDocument->GetSection(this->House.c_str())) + if (auto pHouse = CINIExt::CurrentDocument->GetSection(this->House.c_str())) { for (int i = 0; i < 1000; ++i) { FString key; key.Format("%03d", i); - if (!CINI::CurrentDocument->KeyExists(this->House.c_str(), key)) + if (!CINIExt::CurrentDocument->KeyExists(this->House.c_str(), key)) { FString count; FString value; count.Format("%d", i + 1); value.Format("%s,%d,%d", this->TypeID, this->Y, this->X); - CINI::CurrentDocument->WriteString(this->House.c_str(), "NodeCount", count); - CINI::CurrentDocument->WriteString(this->House.c_str(), key, value); + CINIExt::CurrentDocument->WriteString(this->House.c_str(), "NodeCount", count); + CINIExt::CurrentDocument->WriteString(this->House.c_str(), key, value); CLuaConsole::updateNode = true; CLuaConsole::needRedraw = true; if (deleteBuilding && cell->Structure > -1) @@ -1611,13 +1611,13 @@ namespace LuaFunctions } void remove() const { - for (int i = 0; i < CINI::CurrentDocument->GetKeyCount("Structures"); ++i) + for (int i = 0; i < CINIExt::CurrentDocument->GetKeyCount("Structures"); ++i) { CBuildingData obj; CMapDataExt::GetBuildingDataByIniID(i, obj); if (*this == convert(obj)) { - CINI::CurrentDocument->DeleteKey("Structures", CINI::CurrentDocument->GetKeyAt("Structures", i)); + CINIExt::CurrentDocument->DeleteKey("Structures", CINIExt::CurrentDocument->GetKeyAt("Structures", i)); CLuaConsole::needRedraw = true; CLuaConsole::updateBuilding = true; CLuaConsole::updateMinimap = true; @@ -1879,7 +1879,7 @@ namespace LuaFunctions type = EIndexType::Generic; auto v = VEHGuard(false); - auto& ini = CINI::CurrentDocument; + auto& ini = CINIExt::CurrentDocument; const int initNumber = 1000000; const char* suffix = ""; @@ -2152,7 +2152,7 @@ namespace LuaFunctions if (0 <= index && index < Tags.size()) { if (removeIni) - CINI::CurrentDocument->DeleteKey("Tags", Tags[index].ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("Tags", Tags[index].ID.c_str()); UsedINIIndices.erase(Tags[index].ID); Tags.erase(Tags.begin() + index); } @@ -2162,7 +2162,7 @@ namespace LuaFunctions for (int i = 0; i < Tags.size(); ++i) { if (removeIni) - CINI::CurrentDocument->DeleteKey("Tags", Tags[i].ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("Tags", Tags[i].ID.c_str()); UsedINIIndices.erase(Tags[i].ID); } Tags.clear(); @@ -2483,13 +2483,13 @@ namespace LuaFunctions FString trigger; trigger.Format("%s,%s,%s,%s,%s,%s,%s,%s", House.c_str(), AttachedTrigger.c_str(), Name.c_str(), Disabled ? "1" : "0", EasyEnabled ? "1" : "0", MediumEnabled ? "1" : "0", HardEnabled ? "1" : "0", Obsolete.c_str()); - CINI::CurrentDocument->WriteString("Triggers", ID.c_str(), trigger); + CINIExt::CurrentDocument->WriteString("Triggers", ID.c_str(), trigger); for (const auto& tag : Tags) { FString tagStr; tagStr.Format("%s,%s,%s", tag.RepeatType.c_str(), tag.Name.c_str(), ID.c_str()); - CINI::CurrentDocument->WriteString("Tags", tag.ID.c_str(), tagStr); + CINIExt::CurrentDocument->WriteString("Tags", tag.ID.c_str(), tagStr); } FString events; @@ -2499,7 +2499,7 @@ namespace LuaFunctions events += ","; events += e.c_str(); } - CINI::CurrentDocument->WriteString("Events", ID.c_str(), events); + CINIExt::CurrentDocument->WriteString("Events", ID.c_str(), events); FString actions; actions.Format("%d", Actions.size()); @@ -2513,21 +2513,21 @@ namespace LuaFunctions if (validate.GetLength() >= 512) write_lua_console(std::format("Warn: length of action {} exceeds 512.", ID)); - CINI::CurrentDocument->WriteString("Actions", ID.c_str(), actions); + CINIExt::CurrentDocument->WriteString("Actions", ID.c_str(), actions); CMapDataExt::AddTrigger(ID.c_str()); UsedINIIndices.insert(ID); CLuaConsole::updateTrigger = true; } void delete_trigger_self(bool keepTag) { - CINI::CurrentDocument->DeleteKey("Triggers", ID.c_str()); - CINI::CurrentDocument->DeleteKey("Events", ID.c_str()); - CINI::CurrentDocument->DeleteKey("Actions", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("Triggers", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("Events", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("Actions", ID.c_str()); if (!keepTag) { for (const auto& tag : Tags) { - CINI::CurrentDocument->DeleteKey("Tags", tag.ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("Tags", tag.ID.c_str()); } } @@ -2537,13 +2537,13 @@ namespace LuaFunctions } static void delete_trigger(std::string ID, bool keepTag) { - CINI::CurrentDocument->DeleteKey("Triggers", ID.c_str()); - CINI::CurrentDocument->DeleteKey("Events", ID.c_str()); - CINI::CurrentDocument->DeleteKey("Actions", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("Triggers", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("Events", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("Actions", ID.c_str()); if (!keepTag) { std::vector keys; - if (auto pSection = CINI::CurrentDocument->GetSection("Tags")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Tags")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -2553,7 +2553,7 @@ namespace LuaFunctions } } for (auto& key : keys) - CINI::CurrentDocument->DeleteKey("Tags", key); + CINIExt::CurrentDocument->DeleteKey("Tags", key); } CMapDataExt::DeleteTrigger(ID.c_str()); UsedINIIndices.erase(ID); @@ -2563,13 +2563,13 @@ namespace LuaFunctions { if (!keepTrigger) { - auto&& atoms = STDHelpers::SplitString(CINI::CurrentDocument->GetString("Tags", ID.c_str()), 2); - CINI::CurrentDocument->DeleteKey("Triggers", atoms[2]); - CINI::CurrentDocument->DeleteKey("Events", atoms[2]); - CINI::CurrentDocument->DeleteKey("Actions", atoms[2]); + auto&& atoms = STDHelpers::SplitString(CINIExt::CurrentDocument->GetString("Tags", ID.c_str()), 2); + CINIExt::CurrentDocument->DeleteKey("Triggers", atoms[2]); + CINIExt::CurrentDocument->DeleteKey("Events", atoms[2]); + CINIExt::CurrentDocument->DeleteKey("Actions", atoms[2]); CMapDataExt::DeleteTrigger(atoms[2]); } - CINI::CurrentDocument->DeleteKey("Tags", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("Tags", ID.c_str()); CLuaConsole::updateTrigger = true; } static sol::object get_trigger(std::string ID) @@ -2585,7 +2585,7 @@ namespace LuaFunctions ret.EasyEnabled = t->EasyEnabled; ret.MediumEnabled = t->MediumEnabled; ret.HardEnabled = t->HardEnabled; - if (auto pSection = CINI::CurrentDocument->GetSection("Tags")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Tags")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -2632,7 +2632,7 @@ namespace LuaFunctions static std::vector get_triggers() { std::vector ret; - if (auto pSection = CINI::CurrentDocument->GetSection("Triggers")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Triggers")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -2726,11 +2726,11 @@ namespace LuaFunctions static sol::object get_team(std::string id) { bool found = false; - if (auto pSection = CINI::CurrentDocument->GetSection("TeamTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("TeamTypes")) { for (const auto& [key, value] : pSection->GetEntities()) { - if (value == id.c_str() && CINI::CurrentDocument->SectionExists(value)) + if (value == id.c_str() && CINIExt::CurrentDocument->SectionExists(value)) { found = true; break; @@ -2741,41 +2741,41 @@ namespace LuaFunctions { team ret; ret.ID = id; - ret.Name = CINI::CurrentDocument->GetString(id.c_str(), "Name", "New Teamtype").GetString(); - ret.House = CINI::CurrentDocument->GetString(id.c_str(), "House").GetString(); - ret.Taskforce = CINI::CurrentDocument->GetString(id.c_str(), "TaskForce").GetString(); - ret.Script = CINI::CurrentDocument->GetString(id.c_str(), "Script").GetString(); - ret.Tag = CINI::CurrentDocument->GetString(id.c_str(), "Tag").GetString(); - ret.VeteranLevel = CINI::CurrentDocument->GetString(id.c_str(), "VeteranLevel").GetString(); - ret.Priority = CINI::CurrentDocument->GetString(id.c_str(), "Priority").GetString(); - ret.Max = CINI::CurrentDocument->GetString(id.c_str(), "Max").GetString(); - ret.Techlevel = CINI::CurrentDocument->GetString(id.c_str(), "TechLevel").GetString(); - if (CINI::CurrentDocument->KeyExists(id.c_str(), "TransportWaypoint")) - ret.TransportWaypoint = STDHelpers::StringToWaypointStr(CINI::CurrentDocument->GetString(id.c_str(), "TransportWaypoint")).GetString(); - ret.Group = CINI::CurrentDocument->GetString(id.c_str(), "Group").GetString(); - ret.Waypoint = STDHelpers::StringToWaypointStr(CINI::CurrentDocument->GetString(id.c_str(), "Waypoint")).GetString(); - ret.MindControlDecision = CINI::CurrentDocument->GetString(id.c_str(), "MindControlDecision").GetString(); - ret.Full = CINI::CurrentDocument->GetBool(id.c_str(), "Full"); - ret.Whiner = CINI::CurrentDocument->GetBool(id.c_str(), "Whiner"); - ret.Droppod = CINI::CurrentDocument->GetBool(id.c_str(), "Droppod"); - ret.Suicide = CINI::CurrentDocument->GetBool(id.c_str(), "Suicide"); - ret.Loadable = CINI::CurrentDocument->GetBool(id.c_str(), "Loadable"); - ret.Prebuild = CINI::CurrentDocument->GetBool(id.c_str(), "Prebuild"); - ret.Annoyance = CINI::CurrentDocument->GetBool(id.c_str(), "Annoyance"); - ret.IonImmune = CINI::CurrentDocument->GetBool(id.c_str(), "IonImmune"); - ret.Recruiter = CINI::CurrentDocument->GetBool(id.c_str(), "Recruiter"); - ret.Reinforce = CINI::CurrentDocument->GetBool(id.c_str(), "Reinforce"); - ret.Aggressive = CINI::CurrentDocument->GetBool(id.c_str(), "Aggressive"); - ret.Autocreate = CINI::CurrentDocument->GetBool(id.c_str(), "Autocreate"); - ret.GuardSlower = CINI::CurrentDocument->GetBool(id.c_str(), "GuardSlower"); - ret.OnTransOnly = CINI::CurrentDocument->GetBool(id.c_str(), "OnTransOnly"); - ret.AvoidThreats = CINI::CurrentDocument->GetBool(id.c_str(), "AvoidThreats"); - ret.LooseRecruit = CINI::CurrentDocument->GetBool(id.c_str(), "LooseRecruit"); - ret.IsBaseDefense = CINI::CurrentDocument->GetBool(id.c_str(), "IsBaseDefense"); - ret.UseTransportOrigin = CINI::CurrentDocument->GetBool(id.c_str(), "UseTransportOrigin"); - ret.OnlyTargetHouseEnemy = CINI::CurrentDocument->GetBool(id.c_str(), "OnlyTargetHouseEnemy"); - ret.TransportsReturnOnUnload = CINI::CurrentDocument->GetBool(id.c_str(), "TransportsReturnOnUnload"); - ret.AreTeamMembersRecruitable = CINI::CurrentDocument->GetBool(id.c_str(), "AreTeamMembersRecruitable"); + ret.Name = CINIExt::CurrentDocument->GetString(id.c_str(), "Name", "New Teamtype").GetString(); + ret.House = CINIExt::CurrentDocument->GetString(id.c_str(), "House").GetString(); + ret.Taskforce = CINIExt::CurrentDocument->GetString(id.c_str(), "TaskForce").GetString(); + ret.Script = CINIExt::CurrentDocument->GetString(id.c_str(), "Script").GetString(); + ret.Tag = CINIExt::CurrentDocument->GetString(id.c_str(), "Tag").GetString(); + ret.VeteranLevel = CINIExt::CurrentDocument->GetString(id.c_str(), "VeteranLevel").GetString(); + ret.Priority = CINIExt::CurrentDocument->GetString(id.c_str(), "Priority").GetString(); + ret.Max = CINIExt::CurrentDocument->GetString(id.c_str(), "Max").GetString(); + ret.Techlevel = CINIExt::CurrentDocument->GetString(id.c_str(), "TechLevel").GetString(); + if (CINIExt::CurrentDocument->KeyExists(id.c_str(), "TransportWaypoint")) + ret.TransportWaypoint = STDHelpers::StringToWaypointStr(CINIExt::CurrentDocument->GetString(id.c_str(), "TransportWaypoint")).GetString(); + ret.Group = CINIExt::CurrentDocument->GetString(id.c_str(), "Group").GetString(); + ret.Waypoint = STDHelpers::StringToWaypointStr(CINIExt::CurrentDocument->GetString(id.c_str(), "Waypoint")).GetString(); + ret.MindControlDecision = CINIExt::CurrentDocument->GetString(id.c_str(), "MindControlDecision").GetString(); + ret.Full = CINIExt::CurrentDocument->GetBool(id.c_str(), "Full"); + ret.Whiner = CINIExt::CurrentDocument->GetBool(id.c_str(), "Whiner"); + ret.Droppod = CINIExt::CurrentDocument->GetBool(id.c_str(), "Droppod"); + ret.Suicide = CINIExt::CurrentDocument->GetBool(id.c_str(), "Suicide"); + ret.Loadable = CINIExt::CurrentDocument->GetBool(id.c_str(), "Loadable"); + ret.Prebuild = CINIExt::CurrentDocument->GetBool(id.c_str(), "Prebuild"); + ret.Annoyance = CINIExt::CurrentDocument->GetBool(id.c_str(), "Annoyance"); + ret.IonImmune = CINIExt::CurrentDocument->GetBool(id.c_str(), "IonImmune"); + ret.Recruiter = CINIExt::CurrentDocument->GetBool(id.c_str(), "Recruiter"); + ret.Reinforce = CINIExt::CurrentDocument->GetBool(id.c_str(), "Reinforce"); + ret.Aggressive = CINIExt::CurrentDocument->GetBool(id.c_str(), "Aggressive"); + ret.Autocreate = CINIExt::CurrentDocument->GetBool(id.c_str(), "Autocreate"); + ret.GuardSlower = CINIExt::CurrentDocument->GetBool(id.c_str(), "GuardSlower"); + ret.OnTransOnly = CINIExt::CurrentDocument->GetBool(id.c_str(), "OnTransOnly"); + ret.AvoidThreats = CINIExt::CurrentDocument->GetBool(id.c_str(), "AvoidThreats"); + ret.LooseRecruit = CINIExt::CurrentDocument->GetBool(id.c_str(), "LooseRecruit"); + ret.IsBaseDefense = CINIExt::CurrentDocument->GetBool(id.c_str(), "IsBaseDefense"); + ret.UseTransportOrigin = CINIExt::CurrentDocument->GetBool(id.c_str(), "UseTransportOrigin"); + ret.OnlyTargetHouseEnemy = CINIExt::CurrentDocument->GetBool(id.c_str(), "OnlyTargetHouseEnemy"); + ret.TransportsReturnOnUnload = CINIExt::CurrentDocument->GetBool(id.c_str(), "TransportsReturnOnUnload"); + ret.AreTeamMembersRecruitable = CINIExt::CurrentDocument->GetBool(id.c_str(), "AreTeamMembersRecruitable"); return sol::make_object(CLuaConsole::Lua, ret); } @@ -2787,92 +2787,92 @@ namespace LuaFunctions } void apply() { - if (auto pSection = CINI::CurrentDocument->GetSection("TeamTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("TeamTypes")) { for (const auto& [key, value] : pSection->GetEntities()) { if (value == ID.c_str()) { - CINI::CurrentDocument->DeleteKey("TeamTypes", key); + CINIExt::CurrentDocument->DeleteKey("TeamTypes", key); break; } } } - CINI::CurrentDocument->WriteString("TeamTypes", CINI::GetAvailableKey("TeamTypes"), ID.c_str()); + CINIExt::CurrentDocument->WriteString("TeamTypes", CINI::GetAvailableKey("TeamTypes"), ID.c_str()); - CINI::CurrentDocument->WriteString(ID.c_str(), "Name", Name.c_str()); - CINI::CurrentDocument->WriteString(ID.c_str(), "House", House.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "Name", Name.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "House", House.c_str()); if (Taskforce == "") - CINI::CurrentDocument->DeleteKey(ID.c_str(), "TaskForce"); + CINIExt::CurrentDocument->DeleteKey(ID.c_str(), "TaskForce"); else - CINI::CurrentDocument->WriteString(ID.c_str(), "TaskForce", Taskforce.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "TaskForce", Taskforce.c_str()); if (Script == "") - CINI::CurrentDocument->DeleteKey(ID.c_str(), "Script"); + CINIExt::CurrentDocument->DeleteKey(ID.c_str(), "Script"); else - CINI::CurrentDocument->WriteString(ID.c_str(), "Script", Script.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "Script", Script.c_str()); if (Tag == "") - CINI::CurrentDocument->DeleteKey(ID.c_str(), "Tag"); + CINIExt::CurrentDocument->DeleteKey(ID.c_str(), "Tag"); else - CINI::CurrentDocument->WriteString(ID.c_str(), "Tag", Tag.c_str()); - CINI::CurrentDocument->WriteString(ID.c_str(), "VeteranLevel", VeteranLevel.c_str()); - CINI::CurrentDocument->WriteString(ID.c_str(), "Priority", Priority.c_str()); - CINI::CurrentDocument->WriteString(ID.c_str(), "Max", Max.c_str()); - CINI::CurrentDocument->WriteString(ID.c_str(), "TechLevel", Techlevel.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "Tag", Tag.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "VeteranLevel", VeteranLevel.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "Priority", Priority.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "Max", Max.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "TechLevel", Techlevel.c_str()); if (TransportWaypoint == "") { UseTransportOrigin = false; - CINI::CurrentDocument->DeleteKey(ID.c_str(), "TransportWaypoint"); + CINIExt::CurrentDocument->DeleteKey(ID.c_str(), "TransportWaypoint"); } else { UseTransportOrigin = true; if (STDHelpers::IsNumber(TransportWaypoint.c_str())) - CINI::CurrentDocument->WriteString(ID.c_str(), "TransportWaypoint", STDHelpers::WaypointToString(atoi(TransportWaypoint.c_str()))); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "TransportWaypoint", STDHelpers::WaypointToString(atoi(TransportWaypoint.c_str()))); else - CINI::CurrentDocument->WriteString(ID.c_str(), "TransportWaypoint", TransportWaypoint.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "TransportWaypoint", TransportWaypoint.c_str()); } - CINI::CurrentDocument->WriteString(ID.c_str(), "Group", Group.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "Group", Group.c_str()); if (STDHelpers::IsNumber(Waypoint.c_str())) - CINI::CurrentDocument->WriteString(ID.c_str(), "Waypoint", STDHelpers::WaypointToString(atoi(Waypoint.c_str()))); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "Waypoint", STDHelpers::WaypointToString(atoi(Waypoint.c_str()))); else - CINI::CurrentDocument->WriteString(ID.c_str(), "Waypoint", Waypoint.c_str()); - CINI::CurrentDocument->WriteString(ID.c_str(), "MindControlDecision", MindControlDecision.c_str()); - - CINI::CurrentDocument->WriteBool(ID.c_str(), "Full", Full); - CINI::CurrentDocument->WriteBool(ID.c_str(), "Whiner", Whiner); - CINI::CurrentDocument->WriteBool(ID.c_str(), "Droppod", Droppod); - CINI::CurrentDocument->WriteBool(ID.c_str(), "Suicide", Suicide); - CINI::CurrentDocument->WriteBool(ID.c_str(), "Loadable", Loadable); - CINI::CurrentDocument->WriteBool(ID.c_str(), "Prebuild", Prebuild); - CINI::CurrentDocument->WriteBool(ID.c_str(), "Annoyance", Annoyance); - CINI::CurrentDocument->WriteBool(ID.c_str(), "IonImmune", IonImmune); - CINI::CurrentDocument->WriteBool(ID.c_str(), "Recruiter", Recruiter); - CINI::CurrentDocument->WriteBool(ID.c_str(), "Reinforce", Reinforce); - CINI::CurrentDocument->WriteBool(ID.c_str(), "Aggressive", Aggressive); - CINI::CurrentDocument->WriteBool(ID.c_str(), "Autocreate", Autocreate); - CINI::CurrentDocument->WriteBool(ID.c_str(), "GuardSlower", GuardSlower); - CINI::CurrentDocument->WriteBool(ID.c_str(), "OnTransOnly", OnTransOnly); - CINI::CurrentDocument->WriteBool(ID.c_str(), "AvoidThreats", AvoidThreats); - CINI::CurrentDocument->WriteBool(ID.c_str(), "LooseRecruit", LooseRecruit); - CINI::CurrentDocument->WriteBool(ID.c_str(), "IsBaseDefense", IsBaseDefense); - CINI::CurrentDocument->WriteBool(ID.c_str(), "UseTransportOrigin", UseTransportOrigin); - CINI::CurrentDocument->WriteBool(ID.c_str(), "OnlyTargetHouseEnemy", OnlyTargetHouseEnemy); - CINI::CurrentDocument->WriteBool(ID.c_str(), "TransportsReturnOnUnload", TransportsReturnOnUnload); - CINI::CurrentDocument->WriteBool(ID.c_str(), "AreTeamMembersRecruitable", AreTeamMembersRecruitable); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "Waypoint", Waypoint.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "MindControlDecision", MindControlDecision.c_str()); + + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "Full", Full); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "Whiner", Whiner); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "Droppod", Droppod); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "Suicide", Suicide); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "Loadable", Loadable); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "Prebuild", Prebuild); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "Annoyance", Annoyance); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "IonImmune", IonImmune); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "Recruiter", Recruiter); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "Reinforce", Reinforce); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "Aggressive", Aggressive); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "Autocreate", Autocreate); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "GuardSlower", GuardSlower); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "OnTransOnly", OnTransOnly); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "AvoidThreats", AvoidThreats); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "LooseRecruit", LooseRecruit); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "IsBaseDefense", IsBaseDefense); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "UseTransportOrigin", UseTransportOrigin); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "OnlyTargetHouseEnemy", OnlyTargetHouseEnemy); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "TransportsReturnOnUnload", TransportsReturnOnUnload); + CINIExt::CurrentDocument->WriteBool(ID.c_str(), "AreTeamMembersRecruitable", AreTeamMembersRecruitable); UsedINIIndices.insert(ID); CLuaConsole::updateTeam = true; } void delete_team_self() const { - CINI::CurrentDocument->DeleteSection(ID.c_str()); - if (auto pSection = CINI::CurrentDocument->GetSection("TeamTypes")) + CINIExt::CurrentDocument->DeleteSection(ID.c_str()); + if (auto pSection = CINIExt::CurrentDocument->GetSection("TeamTypes")) { for (const auto& [key, value] : pSection->GetEntities()) { if (value == ID.c_str()) { - CINI::CurrentDocument->DeleteKey("TeamTypes", key); + CINIExt::CurrentDocument->DeleteKey("TeamTypes", key); break; } } @@ -2882,14 +2882,14 @@ namespace LuaFunctions } static void delete_team(std::string id) { - CINI::CurrentDocument->DeleteSection(id.c_str()); - if (auto pSection = CINI::CurrentDocument->GetSection("TeamTypes")) + CINIExt::CurrentDocument->DeleteSection(id.c_str()); + if (auto pSection = CINIExt::CurrentDocument->GetSection("TeamTypes")) { for (const auto& [key, value] : pSection->GetEntities()) { if (value == id.c_str()) { - CINI::CurrentDocument->DeleteKey("TeamTypes", key); + CINIExt::CurrentDocument->DeleteKey("TeamTypes", key); break; } } @@ -2900,7 +2900,7 @@ namespace LuaFunctions static std::vector get_teams() { std::vector ret; - if (auto pSection = CINI::CurrentDocument->GetSection("TeamTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("TeamTypes")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -2992,11 +2992,11 @@ namespace LuaFunctions static sol::object get_task_force(std::string id) { bool found = false; - if (auto pSection = CINI::CurrentDocument->GetSection("TaskForces")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("TaskForces")) { for (const auto& [key, value] : pSection->GetEntities()) { - if (value == id.c_str() && CINI::CurrentDocument->SectionExists(value)) + if (value == id.c_str() && CINIExt::CurrentDocument->SectionExists(value)) { found = true; break; @@ -3007,15 +3007,15 @@ namespace LuaFunctions { task_force ret; ret.ID = id; - ret.Name = CINI::CurrentDocument->GetString(id.c_str(), "Name", "New task force").GetString(); - ret.Group = CINI::CurrentDocument->GetString(id.c_str(), "Group", "-1").GetString(); + ret.Name = CINIExt::CurrentDocument->GetString(id.c_str(), "Name", "New task force").GetString(); + ret.Group = CINIExt::CurrentDocument->GetString(id.c_str(), "Group", "-1").GetString(); FString key; for (int i = 0; i < 6; ++i) { key.Format("%d", i); - if (CINI::CurrentDocument->KeyExists(id.c_str(), key)) + if (CINIExt::CurrentDocument->KeyExists(id.c_str(), key)) { - auto atoms = STDHelpers::SplitString(CINI::CurrentDocument->GetString(id.c_str(), key, "1,E1"), 1); + auto atoms = STDHelpers::SplitString(CINIExt::CurrentDocument->GetString(id.c_str(), key, "1,E1"), 1); ret.Numbers.push_back(atoi(atoms[0])); ret.Units.push_back(atoms[1].GetString()); } @@ -3030,46 +3030,46 @@ namespace LuaFunctions } void apply() const { - if (auto pSection = CINI::CurrentDocument->GetSection("TaskForces")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("TaskForces")) { for (const auto& [key, value] : pSection->GetEntities()) { if (value == ID.c_str()) { - CINI::CurrentDocument->DeleteKey("TaskForces", key); + CINIExt::CurrentDocument->DeleteKey("TaskForces", key); break; } } } - CINI::CurrentDocument->WriteString("TaskForces", CINI::GetAvailableKey("TaskForces"), ID.c_str()); - CINI::CurrentDocument->WriteString(ID.c_str(), "Name", Name.c_str()); - CINI::CurrentDocument->WriteString(ID.c_str(), "Group", Group.c_str()); + CINIExt::CurrentDocument->WriteString("TaskForces", CINI::GetAvailableKey("TaskForces"), ID.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "Name", Name.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "Group", Group.c_str()); FString key; FString value; for (int i = 0; i < 6; ++i) { key.Format("%d", i); - CINI::CurrentDocument->DeleteKey(ID.c_str(), key); + CINIExt::CurrentDocument->DeleteKey(ID.c_str(), key); } for (int i = 0; i < Numbers.size(); ++i) { key.Format("%d", i); value.Format("%d,%s", Numbers[i], Units[i].c_str()); - CINI::CurrentDocument->WriteString(ID.c_str(), key, value); + CINIExt::CurrentDocument->WriteString(ID.c_str(), key, value); } UsedINIIndices.insert(ID); CLuaConsole::updateTaskforce = true; } void delete_task_force_self() const { - CINI::CurrentDocument->DeleteSection(ID.c_str()); - if (auto pSection = CINI::CurrentDocument->GetSection("TaskForces")) + CINIExt::CurrentDocument->DeleteSection(ID.c_str()); + if (auto pSection = CINIExt::CurrentDocument->GetSection("TaskForces")) { for (const auto& [key, value] : pSection->GetEntities()) { if (value == ID.c_str()) { - CINI::CurrentDocument->DeleteKey("TaskForces", key); + CINIExt::CurrentDocument->DeleteKey("TaskForces", key); break; } } @@ -3079,14 +3079,14 @@ namespace LuaFunctions } static void delete_task_force(std::string id) { - CINI::CurrentDocument->DeleteSection(id.c_str()); - if (auto pSection = CINI::CurrentDocument->GetSection("TaskForces")) + CINIExt::CurrentDocument->DeleteSection(id.c_str()); + if (auto pSection = CINIExt::CurrentDocument->GetSection("TaskForces")) { for (const auto& [key, value] : pSection->GetEntities()) { if (value == id.c_str()) { - CINI::CurrentDocument->DeleteKey("TaskForces", key); + CINIExt::CurrentDocument->DeleteKey("TaskForces", key); break; } } @@ -3097,7 +3097,7 @@ namespace LuaFunctions static std::vector get_task_forces() { std::vector ret; - if (auto pSection = CINI::CurrentDocument->GetSection("TaskForces")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("TaskForces")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -3170,11 +3170,11 @@ namespace LuaFunctions static sol::object get_script(std::string id) { bool found = false; - if (auto pSection = CINI::CurrentDocument->GetSection("ScriptTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("ScriptTypes")) { for (const auto& [key, value] : pSection->GetEntities()) { - if (value == id.c_str() && CINI::CurrentDocument->SectionExists(value)) + if (value == id.c_str() && CINIExt::CurrentDocument->SectionExists(value)) { found = true; break; @@ -3185,14 +3185,14 @@ namespace LuaFunctions { script ret; ret.ID = id; - ret.Name = CINI::CurrentDocument->GetString(id.c_str(), "Name", "New script").GetString(); + ret.Name = CINIExt::CurrentDocument->GetString(id.c_str(), "Name", "New script").GetString(); FString key; for (int i = 0; i < 50; ++i) { key.Format("%d", i); - if (CINI::CurrentDocument->KeyExists(id.c_str(), key)) + if (CINIExt::CurrentDocument->KeyExists(id.c_str(), key)) { - auto atoms = STDHelpers::SplitString(CINI::CurrentDocument->GetString(id.c_str(), key, "0,0"), 1); + auto atoms = STDHelpers::SplitString(CINIExt::CurrentDocument->GetString(id.c_str(), key, "0,0"), 1); ret.Actions.push_back(atoi(atoms[0])); ret.Params.push_back(atoi(atoms[1])); } @@ -3304,45 +3304,45 @@ namespace LuaFunctions void apply() const { - if (auto pSection = CINI::CurrentDocument->GetSection("ScriptTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("ScriptTypes")) { for (const auto& [key, value] : pSection->GetEntities()) { if (value == ID.c_str()) { - CINI::CurrentDocument->DeleteKey("ScriptTypes", key); + CINIExt::CurrentDocument->DeleteKey("ScriptTypes", key); break; } } } - CINI::CurrentDocument->WriteString("ScriptTypes", CINI::GetAvailableKey("ScriptTypes"), ID.c_str()); - CINI::CurrentDocument->WriteString(ID.c_str(), "Name", Name.c_str()); + CINIExt::CurrentDocument->WriteString("ScriptTypes", CINI::GetAvailableKey("ScriptTypes"), ID.c_str()); + CINIExt::CurrentDocument->WriteString(ID.c_str(), "Name", Name.c_str()); FString key; FString value; for (int i = 0; i < 50; ++i) { key.Format("%d", i); - CINI::CurrentDocument->DeleteKey(ID.c_str(), key); + CINIExt::CurrentDocument->DeleteKey(ID.c_str(), key); } for (int i = 0; i < Actions.size(); ++i) { key.Format("%d", i); value.Format("%d,%d", Actions[i], Params[i]); - CINI::CurrentDocument->WriteString(ID.c_str(), key, value); + CINIExt::CurrentDocument->WriteString(ID.c_str(), key, value); } UsedINIIndices.insert(ID); CLuaConsole::updateScript = true; } void delete_script_self() const { - CINI::CurrentDocument->DeleteSection(ID.c_str()); - if (auto pSection = CINI::CurrentDocument->GetSection("ScriptTypes")) + CINIExt::CurrentDocument->DeleteSection(ID.c_str()); + if (auto pSection = CINIExt::CurrentDocument->GetSection("ScriptTypes")) { for (const auto& [key, value] : pSection->GetEntities()) { if (value == ID.c_str()) { - CINI::CurrentDocument->DeleteKey("ScriptTypes", key); + CINIExt::CurrentDocument->DeleteKey("ScriptTypes", key); break; } } @@ -3352,14 +3352,14 @@ namespace LuaFunctions } static void delete_script(std::string id) { - CINI::CurrentDocument->DeleteSection(id.c_str()); - if (auto pSection = CINI::CurrentDocument->GetSection("ScriptTypes")) + CINIExt::CurrentDocument->DeleteSection(id.c_str()); + if (auto pSection = CINIExt::CurrentDocument->GetSection("ScriptTypes")) { for (const auto& [key, value] : pSection->GetEntities()) { if (value == id.c_str()) { - CINI::CurrentDocument->DeleteKey("ScriptTypes", key); + CINIExt::CurrentDocument->DeleteKey("ScriptTypes", key); break; } } @@ -3370,7 +3370,7 @@ namespace LuaFunctions static std::vector get_scripts() { std::vector ret; - if (auto pSection = CINI::CurrentDocument->GetSection("ScriptTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("ScriptTypes")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -3424,7 +3424,7 @@ namespace LuaFunctions } static sol::object get_ai_trigger(std::string id) { - auto atoms = STDHelpers::SplitString(CINI::CurrentDocument().GetString("AITriggerTypes", id.c_str())); + auto atoms = STDHelpers::SplitString(CINIExt::CurrentDocument().GetString("AITriggerTypes", id.c_str())); if (atoms.size() >= 18) { ai_trigger ret(id); @@ -3452,7 +3452,7 @@ namespace LuaFunctions ret.Amount = ret.Comparators[0]; ret.Comparator = std::to_string(ret.Comparators[1]); - ret.Enabled = CINI::CurrentDocument().GetBool("AITriggerTypesEnable", id.c_str()); + ret.Enabled = CINIExt::CurrentDocument().GetBool("AITriggerTypesEnable", id.c_str()); return sol::make_object(CLuaConsole::Lua, ret); } else @@ -3518,12 +3518,12 @@ namespace LuaFunctions EnabledInH ? "1" : "0" ); if (Enabled) { - CINI::CurrentDocument->WriteBool("AITriggerTypesEnable", ID.c_str(), Enabled); + CINIExt::CurrentDocument->WriteBool("AITriggerTypesEnable", ID.c_str(), Enabled); } else { - CINI::CurrentDocument->DeleteKey("AITriggerTypesEnable", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("AITriggerTypesEnable", ID.c_str()); } - CINI::CurrentDocument->WriteString("AITriggerTypes", ID.c_str(), value); + CINIExt::CurrentDocument->WriteString("AITriggerTypes", ID.c_str(), value); UsedINIIndices.insert(ID); CLuaConsole::updateAITrigger = true; } @@ -3538,22 +3538,22 @@ namespace LuaFunctions } void delete_ai_trigger_self() const { - CINI::CurrentDocument->DeleteKey("AITriggerTypes", ID.c_str()); - CINI::CurrentDocument->DeleteKey("AITriggerTypesEnable", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("AITriggerTypes", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("AITriggerTypesEnable", ID.c_str()); UsedINIIndices.erase(ID); CLuaConsole::updateAITrigger = true; } static void delete_ai_trigger(std::string ID) { - CINI::CurrentDocument->DeleteKey("AITriggerTypes", ID.c_str()); - CINI::CurrentDocument->DeleteKey("AITriggerTypesEnable", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("AITriggerTypes", ID.c_str()); + CINIExt::CurrentDocument->DeleteKey("AITriggerTypesEnable", ID.c_str()); UsedINIIndices.erase(ID); CLuaConsole::updateAITrigger = true; } static std::vector get_ai_triggers() { std::vector ret; - if (auto pSection = CINI::CurrentDocument->GetSection("AITriggerTypes")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("AITriggerTypes")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -3606,7 +3606,7 @@ namespace LuaFunctions { auto str = std::to_string(index); if (auto pValue = (is_global ? CINI::Rules->TryGetString("VariableNames", str.c_str()) - : CINI::CurrentDocument->TryGetString("VariableNames", str.c_str()))) + : CINIExt::CurrentDocument->TryGetString("VariableNames", str.c_str()))) { auto value = FString::GetParam(*pValue, 1); return atoi(value); @@ -3618,7 +3618,7 @@ namespace LuaFunctions { auto str = std::to_string(index); if (auto pValue = (is_global ? CINI::Rules->TryGetString("VariableNames", str.c_str()) - : CINI::CurrentDocument->TryGetString("VariableNames", str.c_str()))) + : CINIExt::CurrentDocument->TryGetString("VariableNames", str.c_str()))) { auto value = FString::GetParam(*pValue, 0); return value; @@ -3629,11 +3629,11 @@ namespace LuaFunctions static void set_variable_value(int index, int value) { auto str = std::to_string(index); - if (auto pValue = CINI::CurrentDocument->TryGetString("VariableNames", str.c_str())) + if (auto pValue = CINIExt::CurrentDocument->TryGetString("VariableNames", str.c_str())) { FString newValue = *pValue; newValue.SetParam(1, std::to_string(value)); - CINI::CurrentDocument->WriteString("VariableNames", str.c_str(), newValue); + CINIExt::CurrentDocument->WriteString("VariableNames", str.c_str(), newValue); CLuaConsole::updateVariable = true; } else @@ -3647,11 +3647,11 @@ namespace LuaFunctions static void set_variable_name(int index, std::string name) { auto str = std::to_string(index); - if (auto pValue = CINI::CurrentDocument->TryGetString("VariableNames", str.c_str())) + if (auto pValue = CINIExt::CurrentDocument->TryGetString("VariableNames", str.c_str())) { FString newValue = *pValue; newValue.SetParam(0, name); - CINI::CurrentDocument->WriteString("VariableNames", str.c_str(), newValue); + CINIExt::CurrentDocument->WriteString("VariableNames", str.c_str(), newValue); CLuaConsole::updateVariable = true; } else @@ -3667,7 +3667,7 @@ namespace LuaFunctions auto key = CINI::GetAvailableKey("VariableNames"); FString val; val.Format("%s,%d", name, value); - CINI::CurrentDocument->WriteString("VariableNames", key, val); + CINIExt::CurrentDocument->WriteString("VariableNames", key, val); CLuaConsole::updateVariable = true; int index = atoi(key); @@ -3919,20 +3919,20 @@ namespace LuaFunctions if (oldWp > -1) { write_lua_console(std::format("Waypoint {} already exists at ({},{}), abort.", - CINI::CurrentDocument->GetKeyAt("Waypoints", oldWp).GetString(), x, y)); - return atoi(CINI::CurrentDocument->GetKeyAt("Waypoints", oldWp)); + CINIExt::CurrentDocument->GetKeyAt("Waypoints", oldWp).GetString(), x, y)); + return atoi(CINIExt::CurrentDocument->GetKeyAt("Waypoints", oldWp)); } CLuaConsole::needRedraw = true; if (index < 0) { - index = atoi(CINI::CurrentDocument->GetAvailableKey("Waypoints")); + index = atoi(CINIExt::CurrentDocument->GetAvailableKey("Waypoints")); } FString key; FString value; key.Format("%d", index); value.Format("%d", x * 1000 + y); - CINI::CurrentDocument->WriteString("Waypoints", key, value); + CINIExt::CurrentDocument->WriteString("Waypoints", key, value); CMapData::Instance->UpdateFieldWaypointData(false); return index; } @@ -3940,16 +3940,16 @@ namespace LuaFunctions static void remove_waypoint(int index) { CLuaConsole::needRedraw = true; - if (auto pSection = CINI::CurrentDocument->GetSection("Waypoints")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints")) { FString key; key.Format("%d", index); - if (CINI::CurrentDocument->KeyExists("Waypoints", key)) + if (CINIExt::CurrentDocument->KeyExists("Waypoints", key)) { auto&& value = pSection->GetString(key); int x = atoi(value) / 1000; int y = atoi(value) % 1000; - CINI::CurrentDocument->DeleteKey(pSection, key); + CINIExt::CurrentDocument->DeleteKey(pSection, key); CMapData::Instance->UpdateFieldWaypointData(false); if (CMapData::Instance->IsMultiOnly()) @@ -3969,12 +3969,12 @@ namespace LuaFunctions return; int index = CMapData::Instance->GetCellAt(x, y)->Waypoint; if (index > -1) - remove_waypoint(atoi(CINI::CurrentDocument->GetKeyAt("Waypoints", index))); + remove_waypoint(atoi(CINIExt::CurrentDocument->GetKeyAt("Waypoints", index))); } static int get_waypoint(int index) { - if (auto pSection = CINI::CurrentDocument->GetSection("Waypoints")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints")) { if (auto pKey = pSection->GetKeyAt(index)) return atoi(pKey->GetString()); @@ -4317,27 +4317,27 @@ namespace LuaFunctions static void write_string(std::string section, std::string key, std::string value) { - CINI::CurrentDocument->WriteString(section.c_str(), key.c_str(), value.c_str()); + CINIExt::CurrentDocument->WriteString(section.c_str(), key.c_str(), value.c_str()); } static void delete_key(std::string section, std::string key) { - CINI::CurrentDocument->DeleteKey(section.c_str(), key.c_str()); + CINIExt::CurrentDocument->DeleteKey(section.c_str(), key.c_str()); } static void delete_section(std::string section) { - CINI::CurrentDocument->DeleteSection(section.c_str()); + CINIExt::CurrentDocument->DeleteSection(section.c_str()); } static std::string get_free_waypoint() { - return CINI::CurrentDocument->GetAvailableKey("Waypoints").GetString(); + return CINIExt::CurrentDocument->GetAvailableKey("Waypoints").GetString(); } static std::string get_free_key(std::string section) { - return CINI::CurrentDocument->GetAvailableKey(section.c_str()).GetString(); + return CINIExt::CurrentDocument->GetAvailableKey(section.c_str()).GetString(); } static std::string get_param(std::string section, std::string key, int index, std::string delimiter = ",", std::string loadFrom = "map") @@ -4661,7 +4661,7 @@ namespace LuaFunctions static std::vector get_buildings() { std::vector ret; - for (int i = 0; i < CINI::CurrentDocument->GetKeyCount("Structures"); ++i) + for (int i = 0; i < CINIExt::CurrentDocument->GetKeyCount("Structures"); ++i) { auto obj = get_building(i); if (!obj.TypeID.empty()) @@ -4747,7 +4747,7 @@ namespace LuaFunctions static std::vector get_aircrafts() { std::vector ret; - for (int i = 0; i < CINI::CurrentDocument->GetKeyCount("Aircraft"); ++i) + for (int i = 0; i < CINIExt::CurrentDocument->GetKeyCount("Aircraft"); ++i) { auto obj = get_aircraft(i); if (!obj.TypeID.empty()) @@ -4759,7 +4759,7 @@ namespace LuaFunctions static std::vector get_infantries() { std::vector ret; - for (int i = 0; i < CINI::CurrentDocument->GetKeyCount("Infantry"); ++i) + for (int i = 0; i < CINIExt::CurrentDocument->GetKeyCount("Infantry"); ++i) { auto obj = get_infantry(i); if (!obj.TypeID.empty()) @@ -4771,7 +4771,7 @@ namespace LuaFunctions static std::vector get_units() { std::vector ret; - for (int i = 0; i < CINI::CurrentDocument->GetKeyCount("Units"); ++i) + for (int i = 0; i < CINIExt::CurrentDocument->GetKeyCount("Units"); ++i) { auto obj = get_unit(i); if (!obj.TypeID.empty()) @@ -4801,7 +4801,7 @@ namespace LuaFunctions } else { - if (auto pSection = CINI::CurrentDocument->GetSection("Houses")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Houses")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -4816,13 +4816,13 @@ namespace LuaFunctions { FString key; key.Format("%d", x * 1000 + y); - if (CINI::CurrentDocument->KeyExists("CellTags", key)) + if (CINIExt::CurrentDocument->KeyExists("CellTags", key)) { write_lua_console(std::format("CellTag {} already exists at ({},{}), abort.", - CINI::CurrentDocument->GetString("CellTags", key).GetString(), x, y)); + CINIExt::CurrentDocument->GetString("CellTags", key).GetString(), x, y)); return; } - CINI::CurrentDocument->WriteString("CellTags", key, id.c_str()); + CINIExt::CurrentDocument->WriteString("CellTags", key, id.c_str()); CLuaConsole::needRedraw = true; CLuaConsole::updateCellTag = true; } @@ -4830,7 +4830,7 @@ namespace LuaFunctions static void remove_celltags(std::string id) { std::vector keys; - if (auto pSection = CINI::CurrentDocument->GetSection("CellTags")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("CellTags")) { for (const auto& [key, value] : pSection->GetEntities()) { @@ -4840,7 +4840,7 @@ namespace LuaFunctions } for (const auto& key : keys) { - CINI::CurrentDocument->DeleteKey("CellTags", key); + CINIExt::CurrentDocument->DeleteKey("CellTags", key); } CLuaConsole::needRedraw = true; CLuaConsole::updateCellTag = true; @@ -4850,7 +4850,7 @@ namespace LuaFunctions { FString key; key.Format("%d", x * 1000 + y); - CINI::CurrentDocument->DeleteKey("CellTags", key); + CINIExt::CurrentDocument->DeleteKey("CellTags", key); CLuaConsole::needRedraw = true; CLuaConsole::updateCellTag = true; } @@ -4862,7 +4862,7 @@ namespace LuaFunctions if (!CMapData::Instance->IsCoordInMap(x, y)) return; - if (auto pHouse = CINI::CurrentDocument->GetSection(house.c_str())) + if (auto pHouse = CINIExt::CurrentDocument->GetSection(house.c_str())) { FString value; value.Format("%s,%d,%d", type.c_str(), y, x); @@ -4872,12 +4872,12 @@ namespace LuaFunctions { FString key; key.Format("%03d", i); - if (!CINI::CurrentDocument->KeyExists(house.c_str(), key)) + if (!CINIExt::CurrentDocument->KeyExists(house.c_str(), key)) { FString count; count.Format("%d", i + 1); - CINI::CurrentDocument->WriteString(house.c_str(), "NodeCount", count); - CINI::CurrentDocument->WriteString(house.c_str(), key, value); + CINIExt::CurrentDocument->WriteString(house.c_str(), "NodeCount", count); + CINIExt::CurrentDocument->WriteString(house.c_str(), key, value); CLuaConsole::updateNode = true; CLuaConsole::needRedraw = true; break; @@ -4887,14 +4887,14 @@ namespace LuaFunctions else { std::vector nodes; - int nodeCount = CINI::CurrentDocument->GetInteger(house.c_str(), "NodeCount", 0); + int nodeCount = CINIExt::CurrentDocument->GetInteger(house.c_str(), "NodeCount", 0); for (int i = 0; i < nodeCount; ++i) { FString key; key.Format("%03d", i); - if (CINI::CurrentDocument->KeyExists(house.c_str(), key)) + if (CINIExt::CurrentDocument->KeyExists(house.c_str(), key)) { - nodes.push_back(CINI::CurrentDocument->GetString(house.c_str(), key)); + nodes.push_back(CINIExt::CurrentDocument->GetString(house.c_str(), key)); } } if (index > nodes.size()) index = nodes.size(); @@ -4904,12 +4904,12 @@ namespace LuaFunctions { FString key; key.Format("%03d", i); - CINI::CurrentDocument->WriteString(house.c_str(), key, node); + CINIExt::CurrentDocument->WriteString(house.c_str(), key, node); i++; } FString count; count.Format("%d", nodes.size()); - CINI::CurrentDocument->WriteString(house.c_str(), "NodeCount", count); + CINIExt::CurrentDocument->WriteString(house.c_str(), "NodeCount", count); CLuaConsole::updateNode = true; CLuaConsole::needRedraw = true; } @@ -5289,7 +5289,7 @@ namespace LuaFunctions CMapData::Instance->UpdateINIFile(SaveMapFlag::UpdateMapFieldData); - if (SaveMapExt::SaveMap(&CINI::CurrentDocument, CFinalSunDlg::Instance(), target, previewOption, false, false)) + if (SaveMapExt::SaveMap(&CINIExt::CurrentDocument, CFinalSunDlg::Instance(), target, previewOption, false, false)) { FString buffer = "Map saved as \"%1\""; Translations::GetTranslationItem("FileSaved", buffer); @@ -5365,7 +5365,7 @@ namespace LuaFunctions } auto& snapshot = snapshots[version]; Logger::Debug("Restoring map snapshot #%d, time point: %s\n", version, formatTime(snapshot.savedTime).c_str()); - if (CINI::CurrentDocument->GetString("Map", "Theater") != snapshot.INI["Map"]["Theater"]) + if (CINIExt::CurrentDocument->GetString("Map", "Theater") != snapshot.INI["Map"]["Theater"]) { write_lua_console(std::format("Cannot restore snapshot #{}, theater dismatch.", version)); return; @@ -5478,7 +5478,7 @@ namespace LuaFunctions { FString wp; wp.Format("%d", y); - auto pWP = CINI::CurrentDocument->GetString("Waypoints", wp, "-1"); + auto pWP = CINIExt::CurrentDocument->GetString("Waypoints", wp, "-1"); auto second = atoi(pWP); if (second >= 0) { diff --git a/FA2sp/ExtraWindow/CMeasurementToolbox/CMeasurementToolbox.cpp b/FA2sp/ExtraWindow/CMeasurementToolbox/CMeasurementToolbox.cpp index 52515678..639ec7a9 100644 --- a/FA2sp/ExtraWindow/CMeasurementToolbox/CMeasurementToolbox.cpp +++ b/FA2sp/ExtraWindow/CMeasurementToolbox/CMeasurementToolbox.cpp @@ -445,7 +445,7 @@ void CMeasurementToolbox::SetMeasurementToolbox(int X, int Y) CIsoView::CurrentCommand->Type == MeasurementTypes::LineSegment_Annotation ? "LineSegment" : "ArrowSegment", CIsoViewExt::TwoPointDistance_Annotation.back().Point1.X, CIsoViewExt::TwoPointDistance_Annotation.back().Point1.Y, CIsoViewExt::TwoPointDistance_Annotation.back().Point2.X, CIsoViewExt::TwoPointDistance_Annotation.back().Point2.Y); - CINI::CurrentDocument->WriteString("GeometricAnnotations", + CINIExt::CurrentDocument->WriteString("GeometricAnnotations", CINI::GetAvailableKey("GeometricAnnotations"), value ); @@ -586,7 +586,7 @@ void CMeasurementToolbox::SetMeasurementToolbox(int X, int Y) "Circle", CIsoViewExt::TempCircle_Annotation[0].X, CIsoViewExt::TempCircle_Annotation[0].Y, CIsoViewExt::TempCircle_Annotation[1].X, CIsoViewExt::TempCircle_Annotation[1].Y); - CINI::CurrentDocument->WriteString("GeometricAnnotations", + CINIExt::CurrentDocument->WriteString("GeometricAnnotations", CINI::GetAvailableKey("GeometricAnnotations"), value ); diff --git a/FA2sp/ExtraWindow/CMiscSettings/CMiscSettings.cpp b/FA2sp/ExtraWindow/CMiscSettings/CMiscSettings.cpp index e9cccc02..61f87189 100644 --- a/FA2sp/ExtraWindow/CMiscSettings/CMiscSettings.cpp +++ b/FA2sp/ExtraWindow/CMiscSettings/CMiscSettings.cpp @@ -15,7 +15,7 @@ namespace void LaunchRandomTree() { CRandomTreeExt randomTree; - auto mirageIni = CINI::CurrentDocument->GetString("General", "DefaultMirageDisguises"); + auto mirageIni = CINIExt::CurrentDocument->GetString("General", "DefaultMirageDisguises"); mirageIni.Trim(); randomTree.MirageDisguiseTrees = mirageIni; randomTree.LaunchingFromSingleplayerSettings = true; diff --git a/FA2sp/ExtraWindow/CNewAITrigger/CNewAITrigger.cpp b/FA2sp/ExtraWindow/CNewAITrigger/CNewAITrigger.cpp index 363524fe..6a27ec37 100644 --- a/FA2sp/ExtraWindow/CNewAITrigger/CNewAITrigger.cpp +++ b/FA2sp/ExtraWindow/CNewAITrigger/CNewAITrigger.cpp @@ -20,7 +20,7 @@ HWND CNewAITrigger::m_hwnd; CFinalSunDlg* CNewAITrigger::m_parent; -CINI& CNewAITrigger::map = CINI::CurrentDocument; +CINIExt& CNewAITrigger::map = CINIExt::CurrentDocument; MultimapHelper& CNewAITrigger::rules = Variables::RulesMap; CINI& CNewAITrigger::fadata = CINI::FAData; bool CNewAITrigger::AutoChangeName = false; diff --git a/FA2sp/ExtraWindow/CNewAITrigger/CNewAITrigger.h b/FA2sp/ExtraWindow/CNewAITrigger/CNewAITrigger.h index 72da31bb..1aaca8e6 100644 --- a/FA2sp/ExtraWindow/CNewAITrigger/CNewAITrigger.h +++ b/FA2sp/ExtraWindow/CNewAITrigger/CNewAITrigger.h @@ -1,6 +1,7 @@ #pragma once #include +#include "../../Miscs/Hooks.INI.h" #include #include #include @@ -11,6 +12,7 @@ #include "../../Helpers/FString.h" class VirtualComboBoxEx; +class CINIExt; class AITrigger { @@ -39,7 +41,7 @@ class AITrigger static std::unique_ptr create(const char* id) { - auto atoms = STDHelpers::SplitString(CINI::CurrentDocument().GetString("AITriggerTypes", id)); + auto atoms = STDHelpers::SplitString(CINIExt::CurrentDocument().GetString("AITriggerTypes", id)); if (atoms.size() < 18) return nullptr; return std::make_unique(id); @@ -55,7 +57,7 @@ class AITrigger } AITrigger(const char* id) { - auto atoms = FString::SplitString(CINI::CurrentDocument().GetString("AITriggerTypes", id), 17); + auto atoms = FString::SplitString(CINIExt::CurrentDocument().GetString("AITriggerTypes", id), 17); ID = id; Name = atoms[0]; Team1 = atoms[1]; @@ -78,7 +80,7 @@ class AITrigger EnabledInM = atoms[16] == "0" ? false : true; EnabledInH = atoms[17] == "0" ? false : true; - Enabled = CINI::CurrentDocument().GetBool("AITriggerTypesEnable", id); + Enabled = CINIExt::CurrentDocument().GetBool("AITriggerTypesEnable", id); } void Save() const @@ -123,12 +125,12 @@ class AITrigger EnabledInH ? "1" : "0" ); if (Enabled) { - CINI::CurrentDocument->WriteBool("AITriggerTypesEnable", ID, Enabled); + CINIExt::CurrentDocument->WriteBool("AITriggerTypesEnable", ID, Enabled); } else { - CINI::CurrentDocument->DeleteKey("AITriggerTypesEnable", ID); + CINIExt::CurrentDocument->DeleteKey("AITriggerTypesEnable", ID); } - CINI::CurrentDocument->WriteString("AITriggerTypes", ID, value); + CINIExt::CurrentDocument->WriteString("AITriggerTypes", ID, value); } private: @@ -240,7 +242,7 @@ class CNewAITrigger private: static HWND m_hwnd; static CFinalSunDlg* m_parent; - static CINI& map; + static CINIExt& map; static CINI& fadata; static MultimapHelper& rules; static bool AutoChangeName; diff --git a/FA2sp/ExtraWindow/CNewHouse/CNewHouse.cpp b/FA2sp/ExtraWindow/CNewHouse/CNewHouse.cpp index 540d41b1..159539b3 100644 --- a/FA2sp/ExtraWindow/CNewHouse/CNewHouse.cpp +++ b/FA2sp/ExtraWindow/CNewHouse/CNewHouse.cpp @@ -17,7 +17,7 @@ HWND CNewHouse::m_hwnd; CFinalSunDlg* CNewHouse::m_parent; -CINI& CNewHouse::map = CINI::CurrentDocument; +CINIExt& CNewHouse::map = CINIExt::CurrentDocument; MultimapHelper& CNewHouse::rules = Variables::RulesMap; HWND CNewHouse::hSelectedHouse; diff --git a/FA2sp/ExtraWindow/CNewHouse/CNewHouse.h b/FA2sp/ExtraWindow/CNewHouse/CNewHouse.h index a7f0c978..efdc70b0 100644 --- a/FA2sp/ExtraWindow/CNewHouse/CNewHouse.h +++ b/FA2sp/ExtraWindow/CNewHouse/CNewHouse.h @@ -78,7 +78,7 @@ class CNewHouse private: static HWND m_hwnd; static CFinalSunDlg* m_parent; - static CINI& map; + static CINIExt& map; static MultimapHelper& rules; public: static HWND hSelectedHouse; diff --git a/FA2sp/ExtraWindow/CNewINIEditor/CNewINIEditor.cpp b/FA2sp/ExtraWindow/CNewINIEditor/CNewINIEditor.cpp index a77f6f33..04a3cba6 100644 --- a/FA2sp/ExtraWindow/CNewINIEditor/CNewINIEditor.cpp +++ b/FA2sp/ExtraWindow/CNewINIEditor/CNewINIEditor.cpp @@ -23,7 +23,7 @@ HWND CNewINIEditor::m_hwnd; CFinalSunDlg* CNewINIEditor::m_parent; HWND CNewINIEditor::m_hwndImporter; -CINI& CNewINIEditor::map = CINI::CurrentDocument; +CINIExt& CNewINIEditor::map = CINIExt::CurrentDocument; CINI& CNewINIEditor::fadata = CINI::FAData; MultimapHelper& CNewINIEditor::rules = Variables::RulesMap; diff --git a/FA2sp/ExtraWindow/CNewINIEditor/CNewINIEditor.h b/FA2sp/ExtraWindow/CNewINIEditor/CNewINIEditor.h index 50d4ce37..5f4800aa 100644 --- a/FA2sp/ExtraWindow/CNewINIEditor/CNewINIEditor.h +++ b/FA2sp/ExtraWindow/CNewINIEditor/CNewINIEditor.h @@ -14,6 +14,8 @@ #include "SciLexer.h" #include "Lexilla.h" +class CINIExt; + // A static window class class CNewINIEditor { @@ -81,7 +83,7 @@ class CNewINIEditor static HWND m_hwnd; static CFinalSunDlg* m_parent; static HWND m_hwndImporter; - static CINI& map; + static CINIExt& map; static CINI& fadata; static MultimapHelper& rules; public: diff --git a/FA2sp/ExtraWindow/CNewLocalVariables/CNewLocalVariables.cpp b/FA2sp/ExtraWindow/CNewLocalVariables/CNewLocalVariables.cpp index a63d66d8..122b233b 100644 --- a/FA2sp/ExtraWindow/CNewLocalVariables/CNewLocalVariables.cpp +++ b/FA2sp/ExtraWindow/CNewLocalVariables/CNewLocalVariables.cpp @@ -11,7 +11,7 @@ HWND CNewLocalVariables::m_hwnd; CFinalSunDlg* CNewLocalVariables::m_parent; -CINI& CNewLocalVariables::map = CINI::CurrentDocument; +CINIExt& CNewLocalVariables::map = CINIExt::CurrentDocument; VirtualComboBoxEx CNewLocalVariables::vcbVariables; HWND CNewLocalVariables::hVariables; diff --git a/FA2sp/ExtraWindow/CNewLocalVariables/CNewLocalVariables.h b/FA2sp/ExtraWindow/CNewLocalVariables/CNewLocalVariables.h index 4cb081ec..0f7f240b 100644 --- a/FA2sp/ExtraWindow/CNewLocalVariables/CNewLocalVariables.h +++ b/FA2sp/ExtraWindow/CNewLocalVariables/CNewLocalVariables.h @@ -44,7 +44,7 @@ class CNewLocalVariables private: static HWND m_hwnd; static CFinalSunDlg* m_parent; - static CINI& map; + static CINIExt& map; static HWND hVariables; static HWND hName; diff --git a/FA2sp/ExtraWindow/CNewPropertyAircraft/CNewPropertyAircraft.cpp b/FA2sp/ExtraWindow/CNewPropertyAircraft/CNewPropertyAircraft.cpp index bc5fe199..daca0168 100644 --- a/FA2sp/ExtraWindow/CNewPropertyAircraft/CNewPropertyAircraft.cpp +++ b/FA2sp/ExtraWindow/CNewPropertyAircraft/CNewPropertyAircraft.cpp @@ -1,4 +1,5 @@ #include "CNewPropertyAircraft.h" +#include "../../Miscs/Hooks.INI.h" #include #include "../../FA2sp.h" #include "../../Ext/CFinalSunApp/Body.h" @@ -263,7 +264,7 @@ BOOL CNewPropertyAircraft::OnInitDialog(HWND hDlg) { FString name; name.Format("%s - %s", CString_Tag, - FString::GetParam(CINI::CurrentDocument->GetString("Tags", CString_Tag, "0,MISSING,01000000"), 1)); + FString::GetParam(CINIExt::CurrentDocument->GetString("Tags", CString_Tag, "0,MISSING,01000000"), 1)); index = m_comboBoxes[hTag]->FindStringExact(name); } else diff --git a/FA2sp/ExtraWindow/CNewPropertyBuilding/CNewPropertyBuilding.cpp b/FA2sp/ExtraWindow/CNewPropertyBuilding/CNewPropertyBuilding.cpp index 0c66de3c..bfd2c13c 100644 --- a/FA2sp/ExtraWindow/CNewPropertyBuilding/CNewPropertyBuilding.cpp +++ b/FA2sp/ExtraWindow/CNewPropertyBuilding/CNewPropertyBuilding.cpp @@ -1,4 +1,5 @@ #include "CNewPropertyBuilding.h" +#include "../../Miscs/Hooks.INI.h" #include #include "../../FA2sp.h" #include "../../Ext/CFinalSunApp/Body.h" @@ -363,7 +364,7 @@ BOOL CNewPropertyBuilding::OnInitDialog(HWND hDlg) { FString name; name.Format("%s - %s", CString_Tag, - FString::GetParam(CINI::CurrentDocument->GetString("Tags", CString_Tag, "0,MISSING,01000000"), 1)); + FString::GetParam(CINIExt::CurrentDocument->GetString("Tags", CString_Tag, "0,MISSING,01000000"), 1)); index = m_comboBoxes[hTag]->FindStringExact(name); } else diff --git a/FA2sp/ExtraWindow/CNewPropertyInfantry/CNewPropertyInfantry.cpp b/FA2sp/ExtraWindow/CNewPropertyInfantry/CNewPropertyInfantry.cpp index c9698b30..d9146691 100644 --- a/FA2sp/ExtraWindow/CNewPropertyInfantry/CNewPropertyInfantry.cpp +++ b/FA2sp/ExtraWindow/CNewPropertyInfantry/CNewPropertyInfantry.cpp @@ -1,4 +1,5 @@ #include "CNewPropertyInfantry.h" +#include "../../Miscs/Hooks.INI.h" #include #include "../../FA2sp.h" #include "../../Ext/CFinalSunApp/Body.h" @@ -267,7 +268,7 @@ BOOL CNewPropertyInfantry::OnInitDialog(HWND hDlg) { FString name; name.Format("%s - %s", CString_Tag, - FString::GetParam(CINI::CurrentDocument->GetString("Tags", CString_Tag, "0,MISSING,01000000"), 1)); + FString::GetParam(CINIExt::CurrentDocument->GetString("Tags", CString_Tag, "0,MISSING,01000000"), 1)); index = m_comboBoxes[hTag]->FindStringExact(name); } else diff --git a/FA2sp/ExtraWindow/CNewPropertyUnit/CNewPropertyUnit.cpp b/FA2sp/ExtraWindow/CNewPropertyUnit/CNewPropertyUnit.cpp index c6c7e168..6c556793 100644 --- a/FA2sp/ExtraWindow/CNewPropertyUnit/CNewPropertyUnit.cpp +++ b/FA2sp/ExtraWindow/CNewPropertyUnit/CNewPropertyUnit.cpp @@ -1,4 +1,5 @@ #include "CNewPropertyUnit.h" +#include "../../Miscs/Hooks.INI.h" #include #include "../../FA2sp.h" #include "../../Ext/CFinalSunApp/Body.h" @@ -266,7 +267,7 @@ BOOL CNewPropertyUnit::OnInitDialog(HWND hDlg) { FString name; name.Format("%s - %s", CString_Tag, - FString::GetParam(CINI::CurrentDocument->GetString("Tags", CString_Tag, "0,MISSING,01000000"), 1)); + FString::GetParam(CINIExt::CurrentDocument->GetString("Tags", CString_Tag, "0,MISSING,01000000"), 1)); index = m_comboBoxes[hTag]->FindStringExact(name); } else @@ -316,7 +317,7 @@ BOOL CNewPropertyUnit::OnInitDialog(HWND hDlg) auto vcb = std::make_unique(); vcb->Attach(hFollow, nullptr, true); vcb->AddString("-1 - None"); - if (auto pSection = CINI::CurrentDocument->GetSection("Units")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Units")) { int index = 0; FString text; diff --git a/FA2sp/ExtraWindow/CNewScript/CNewScript.cpp b/FA2sp/ExtraWindow/CNewScript/CNewScript.cpp index 880947a7..ee9ed5f5 100644 --- a/FA2sp/ExtraWindow/CNewScript/CNewScript.cpp +++ b/FA2sp/ExtraWindow/CNewScript/CNewScript.cpp @@ -1,4 +1,5 @@ #include "CNewScript.h" +#include "../../Miscs/Hooks.INI.h" #include "../../FA2sp.h" #include "../../Helpers/Translations.h" #include "../../Helpers/STDHelpers.h" @@ -24,7 +25,7 @@ HWND CNewScript::m_hwnd; CFinalSunDlg* CNewScript::m_parent; -CINI& CNewScript::map = CINI::CurrentDocument; +CINIExt& CNewScript::map = CINIExt::CurrentDocument; CINI& CNewScript::fadata = CINI::FAData; MultimapHelper& CNewScript::rules = Variables::RulesMap; @@ -1224,12 +1225,12 @@ void CNewScript::OnClickCloScript(HWND& hWnd) auto key = CINI::GetAvailableKey("ScriptTypes"); auto value = CMapDataExt::GetAvailableIndex(EIndexType::Script); - CINI::CurrentDocument->WriteString("ScriptTypes", key, value); + CINIExt::CurrentDocument->WriteString("ScriptTypes", key, value); - auto oldname = CINI::CurrentDocument->GetString(CurrentScriptID, "Name", "New script"); + auto oldname = CINIExt::CurrentDocument->GetString(CurrentScriptID, "Name", "New script"); FString newName = ExtraWindow::GetCloneName(oldname); - CINI::CurrentDocument->WriteString(value, "Name", newName); + CINIExt::CurrentDocument->WriteString(value, "Name", newName); CNewTeamTypes::ScriptListChanged = true; auto copyitem = [&value](FString key) @@ -1784,7 +1785,7 @@ void CNewScript::UpdateScriptPath() { if (!CIsoViewExt::DrawScriptPath ||CurrentScriptID.IsEmpty() - || !CINI::CurrentDocument->SectionExists(CurrentScriptID)) + || !CINIExt::CurrentDocument->SectionExists(CurrentScriptID)) return; CIsoViewExt::ScriptPath.clear(); @@ -1793,7 +1794,7 @@ void CNewScript::UpdateScriptPath() { FString key; key.Format("%d", i); - auto value = CINI::CurrentDocument->GetString(CurrentScriptID, key); + auto value = CINIExt::CurrentDocument->GetString(CurrentScriptID, key); auto atoms = FString::SplitString(value); if (atoms.size() < 2) break; auto& action = atoms[0]; @@ -1832,7 +1833,7 @@ void CNewScript::UpdateScriptPath() { if (param[3] == "1") // waypoints { - auto pos = CINI::CurrentDocument->GetInteger("Waypoints", actionParam); + auto pos = CINIExt::CurrentDocument->GetInteger("Waypoints", actionParam); int x = pos / 1000; int y = pos % 1000; CIsoViewExt::ScriptPath.push_back({ x,y }); @@ -1842,7 +1843,7 @@ void CNewScript::UpdateScriptPath() { if (param[1] == "1") // waypoints { - auto pos = CINI::CurrentDocument->GetInteger("Waypoints", actionParam); + auto pos = CINIExt::CurrentDocument->GetInteger("Waypoints", actionParam); int x = pos / 1000; int y = pos % 1000; CIsoViewExt::ScriptPath.push_back({ x,y }); @@ -1921,7 +1922,7 @@ void CNewScript::OnClickJumpButton(bool extra) if (type == ParamType::Waypoint) { FString::TrimIndex(value); - if (auto pCord = CINI::CurrentDocument->TryGetString("Waypoints", value)) + if (auto pCord = CINIExt::CurrentDocument->TryGetString("Waypoints", value)) { auto second = atoi(*pCord); if (second > 0) diff --git a/FA2sp/ExtraWindow/CNewScript/CNewScript.h b/FA2sp/ExtraWindow/CNewScript/CNewScript.h index 62648485..e60ddb6b 100644 --- a/FA2sp/ExtraWindow/CNewScript/CNewScript.h +++ b/FA2sp/ExtraWindow/CNewScript/CNewScript.h @@ -87,7 +87,7 @@ class CNewScript private: static HWND m_hwnd; static CFinalSunDlg* m_parent; - static CINI& map; + static CINIExt& map; static CINI& fadata; static MultimapHelper& rules; public: diff --git a/FA2sp/ExtraWindow/CNewTag/CNewTag.cpp b/FA2sp/ExtraWindow/CNewTag/CNewTag.cpp index 6106bece..e52d3f94 100644 --- a/FA2sp/ExtraWindow/CNewTag/CNewTag.cpp +++ b/FA2sp/ExtraWindow/CNewTag/CNewTag.cpp @@ -1,4 +1,5 @@ #include "CNewTag.h" +#include "../../Miscs/Hooks.INI.h" #include "../../FA2sp.h" #include "../../Helpers/Translations.h" #include "../../Helpers/STDHelpers.h" @@ -25,7 +26,7 @@ HWND CNewTag::m_hwnd; CFinalSunDlg* CNewTag::m_parent; -CINI& CNewTag::map = CINI::CurrentDocument; +CINIExt& CNewTag::map = CINIExt::CurrentDocument; MultimapHelper& CNewTag::rules = Variables::RulesMap; HWND CNewTag::hSelectedTag; @@ -1026,7 +1027,7 @@ void CNewTag::OnClickCloTag(HWND& hWnd) FString newName = ExtraWindow::GetCloneName(oldName); value.SetParam(1, newName); - CINI::CurrentDocument->WriteString("Tags", key, value); + CINIExt::CurrentDocument->WriteString("Tags", key, value); ExtraWindow::SetTriggerColor(key, ExtraWindow::GetTriggerColor(CurrentTagID)); SortTags(vcbSelectedTag, SelectedTagIndex, key); diff --git a/FA2sp/ExtraWindow/CNewTag/CNewTag.h b/FA2sp/ExtraWindow/CNewTag/CNewTag.h index 5b6d652b..e1e5382d 100644 --- a/FA2sp/ExtraWindow/CNewTag/CNewTag.h +++ b/FA2sp/ExtraWindow/CNewTag/CNewTag.h @@ -63,7 +63,7 @@ class CNewTag private: static HWND m_hwnd; static CFinalSunDlg* m_parent; - static CINI& map; + static CINIExt& map; static MultimapHelper& rules; public: static HWND hSelectedTag; diff --git a/FA2sp/ExtraWindow/CNewTaskforce/CNewTaskforce.cpp b/FA2sp/ExtraWindow/CNewTaskforce/CNewTaskforce.cpp index dd219569..a6096bc1 100644 --- a/FA2sp/ExtraWindow/CNewTaskforce/CNewTaskforce.cpp +++ b/FA2sp/ExtraWindow/CNewTaskforce/CNewTaskforce.cpp @@ -1,4 +1,5 @@ #include "CNewTaskforce.h" +#include "../../Miscs/Hooks.INI.h" #include "../../FA2sp.h" #include "../../Helpers/Translations.h" #include "../../Helpers/STDHelpers.h" @@ -24,7 +25,7 @@ HWND CNewTaskforce::m_hwnd; CFinalSunDlg* CNewTaskforce::m_parent; -CINI& CNewTaskforce::map = CINI::CurrentDocument; +CINIExt& CNewTaskforce::map = CINIExt::CurrentDocument; MultimapHelper& CNewTaskforce::rules = Variables::RulesMap; HWND CNewTaskforce::hSelectedTaskforce; @@ -876,12 +877,12 @@ void CNewTaskforce::OnClickCloTaskforce(HWND& hWnd) auto key = CINI::GetAvailableKey("TaskForces"); auto value = CMapDataExt::GetAvailableIndex(EIndexType::TaskForce); - CINI::CurrentDocument->WriteString("TaskForces", key, value); + CINIExt::CurrentDocument->WriteString("TaskForces", key, value); - auto oldname = CINI::CurrentDocument->GetString(CurrentTaskForceID, "Name", "New task force"); + auto oldname = CINIExt::CurrentDocument->GetString(CurrentTaskForceID, "Name", "New task force"); FString newName = ExtraWindow::GetCloneName(oldname); - CINI::CurrentDocument->WriteString(value, "Name", newName); + CINIExt::CurrentDocument->WriteString(value, "Name", newName); CNewTeamTypes::TaskforceListChanged = true; auto copyitem = [&value](FString key) diff --git a/FA2sp/ExtraWindow/CNewTaskforce/CNewTaskforce.h b/FA2sp/ExtraWindow/CNewTaskforce/CNewTaskforce.h index 3d9b3051..9b65a095 100644 --- a/FA2sp/ExtraWindow/CNewTaskforce/CNewTaskforce.h +++ b/FA2sp/ExtraWindow/CNewTaskforce/CNewTaskforce.h @@ -67,7 +67,7 @@ class CNewTaskforce private: static HWND m_hwnd; static CFinalSunDlg* m_parent; - static CINI& map; + static CINIExt& map; static MultimapHelper& rules; public: static HWND hSelectedTaskforce; diff --git a/FA2sp/ExtraWindow/CNewTeamTypes/CNewTeamTypes.cpp b/FA2sp/ExtraWindow/CNewTeamTypes/CNewTeamTypes.cpp index f24ca62f..43571ecd 100644 --- a/FA2sp/ExtraWindow/CNewTeamTypes/CNewTeamTypes.cpp +++ b/FA2sp/ExtraWindow/CNewTeamTypes/CNewTeamTypes.cpp @@ -1,4 +1,5 @@ #include "CNewTeamTypes.h" +#include "../../Miscs/Hooks.INI.h" #include "../CNewTaskforce/CNewTaskforce.h" #include "../CNewScript/CNewScript.h" #include "../CNewTrigger/CNewTrigger.h" @@ -27,7 +28,7 @@ HWND CNewTeamTypes::m_hwnd; CFinalSunDlg* CNewTeamTypes::m_parent; -CINI& CNewTeamTypes::map = CINI::CurrentDocument; +CINIExt& CNewTeamTypes::map = CINIExt::CurrentDocument; MultimapHelper& CNewTeamTypes::rules = Variables::RulesMap; HWND CNewTeamTypes::hSelectedTeam; HWND CNewTeamTypes::hNewTeam; @@ -369,7 +370,7 @@ void CNewTeamTypes::Update(HWND& hWnd) ExtraWindow::ClearComboKeepText(hWaypoint); ExtraWindow::ClearComboKeepText(hTransportWaypoint); vcbTransportWaypoint.AddString("None"); - if (auto pSection = CINI::CurrentDocument->GetSection("Waypoints")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints")) { FString output; for (auto& pair : pSection->GetEntities()) @@ -1863,7 +1864,7 @@ void CNewTeamTypes::OnClickTurnToTransportWaypoint() FString value = vcbTransportWaypoint.GetSelectedText(true); FString::TrimIndex(value); - if (auto pCord = CINI::CurrentDocument->TryGetString("Waypoints", value)) + if (auto pCord = CINIExt::CurrentDocument->TryGetString("Waypoints", value)) { auto second = atoi(*pCord); if (second > 0) @@ -1880,7 +1881,7 @@ void CNewTeamTypes::OnClickTurnToWaypoint() FString value = vcbWaypoint.GetSelectedText(true); FString::TrimIndex(value); - if (auto pCord = CINI::CurrentDocument->TryGetString("Waypoints", value)) + if (auto pCord = CINIExt::CurrentDocument->TryGetString("Waypoints", value)) { auto second = atoi(*pCord); if (second > 0) @@ -2026,12 +2027,12 @@ void CNewTeamTypes::OnClickCloTeam(HWND& hWnd) auto key = CINI::GetAvailableKey("TeamTypes"); auto value = CMapDataExt::GetAvailableIndex(EIndexType::Team); - CINI::CurrentDocument->WriteString("TeamTypes", key, value); + CINIExt::CurrentDocument->WriteString("TeamTypes", key, value); - auto oldname = CINI::CurrentDocument->GetString(CurrentTeamID, "Name", "New Teamtype"); + auto oldname = CINIExt::CurrentDocument->GetString(CurrentTeamID, "Name", "New Teamtype"); FString newName = ExtraWindow::GetCloneName(oldname); - CINI::CurrentDocument->WriteString(value, "Name", newName); + CINIExt::CurrentDocument->WriteString(value, "Name", newName); auto copyitem = [&value](FString key) { diff --git a/FA2sp/ExtraWindow/CNewTeamTypes/CNewTeamTypes.h b/FA2sp/ExtraWindow/CNewTeamTypes/CNewTeamTypes.h index 9e19b22e..a6d05deb 100644 --- a/FA2sp/ExtraWindow/CNewTeamTypes/CNewTeamTypes.h +++ b/FA2sp/ExtraWindow/CNewTeamTypes/CNewTeamTypes.h @@ -118,7 +118,7 @@ class CNewTeamTypes private: static HWND m_hwnd; static CFinalSunDlg* m_parent; - static CINI& map; + static CINIExt& map; static MultimapHelper& rules; public: static HWND hSelectedTeam; diff --git a/FA2sp/ExtraWindow/CNewTrigger/CNewTrigger.cpp b/FA2sp/ExtraWindow/CNewTrigger/CNewTrigger.cpp index be3620dd..3308cf34 100644 --- a/FA2sp/ExtraWindow/CNewTrigger/CNewTrigger.cpp +++ b/FA2sp/ExtraWindow/CNewTrigger/CNewTrigger.cpp @@ -1,4 +1,5 @@ #include "CNewTrigger.h" +#include "../../Miscs/Hooks.INI.h" #include "../../FA2sp.h" #include "../../Helpers/Translations.h" #include "../../Helpers/MultimapHelper.h" @@ -29,7 +30,7 @@ #include "../../Miscs/StringtableLoader.h" #include "../CNewTag/CNewTag.h" -CINI& CNewTrigger::map = CINI::CurrentDocument; +CINIExt& CNewTrigger::map = CINIExt::CurrentDocument; CINI& CNewTrigger::fadata = CINI::FAData; MultimapHelper& CNewTrigger::rules = Variables::RulesMap; CNewTrigger CNewTrigger::Instance[TRIGGER_EDITOR_MAX_COUNT]; @@ -3943,7 +3944,7 @@ void CNewTrigger::OnClickParamJump(bool isEvent, int index) if (type == ParamType::Waypoint) { FString::TrimIndex(value); - if (auto pCord = CINI::CurrentDocument->TryGetString("Waypoints", value)) + if (auto pCord = CINIExt::CurrentDocument->TryGetString("Waypoints", value)) { auto second = atoi(*pCord); if (second > 0) @@ -4150,7 +4151,7 @@ void CNewTrigger::OnClickParamJump(bool isEvent, int index) { FString wp = vcb->GetSelectedText(true); FString::TrimIndex(wp); - if (auto pCord = CINI::CurrentDocument->TryGetString("Waypoints", wp)) + if (auto pCord = CINIExt::CurrentDocument->TryGetString("Waypoints", wp)) { auto second = atoi(*pCord); if (second > 0) diff --git a/FA2sp/ExtraWindow/CNewTrigger/CNewTrigger.h b/FA2sp/ExtraWindow/CNewTrigger/CNewTrigger.h index 0559006a..70e7906f 100644 --- a/FA2sp/ExtraWindow/CNewTrigger/CNewTrigger.h +++ b/FA2sp/ExtraWindow/CNewTrigger/CNewTrigger.h @@ -61,7 +61,7 @@ class Trigger static Trigger* create(const char* id, FMap* pTagMap = nullptr) { - auto atoms = FString::SplitString(CINI::CurrentDocument().GetString("Triggers", id)); + auto atoms = FString::SplitString(CINIExt::CurrentDocument().GetString("Triggers", id)); if (atoms.size() < 8) return nullptr; return new Trigger(id, atoms, pTagMap); @@ -71,15 +71,15 @@ class Trigger FString trigger; trigger.Format("%s,%s,%s,%s,%s,%s,%s,%s", House, AttachedTrigger, Name, Disabled ? "1" : "0", EasyEnabled ? "1" : "0", MediumEnabled ? "1" : "0", HardEnabled ? "1" : "0", Obsolete); - CINI::CurrentDocument().WriteString("Triggers", ID, trigger); - auto tag = CINI::CurrentDocument().GetString("Tags", Tag, ""); + CINIExt::CurrentDocument().WriteString("Triggers", ID, trigger); + auto tag = CINIExt::CurrentDocument().GetString("Tags", Tag, ""); if (tag != "") { auto triggerId = FString::GetParam(tag, 2); if (triggerId == ID) { tag.Format("%s,%s,%s", RepeatType, TagName, triggerId); - CINI::CurrentDocument().WriteString("Tags", Tag, tag); + CINIExt::CurrentDocument().WriteString("Tags", Tag, tag); } } FString cEvent; @@ -93,7 +93,7 @@ class Trigger tmp.Format(",%s,%s,%s", thisEvent.EventNum, thisEvent.Params[0], thisEvent.Params[1]); cEvent += tmp; } - CINI::CurrentDocument().WriteString("Events", ID, cEvent); + CINIExt::CurrentDocument().WriteString("Events", ID, cEvent); FString cAction; cAction.Format("%d", ActionCount); @@ -105,7 +105,7 @@ class Trigger , thisAction.Params[4], thisAction.Params[5], thisAction.Params[6]); cAction += tmp; } - CINI::CurrentDocument().WriteString("Actions", ID, cAction); + CINIExt::CurrentDocument().WriteString("Actions", ID, cAction); } void LoadFromMap(const char* id, std::vector& atoms, FMap* pTagMap = nullptr) @@ -113,7 +113,7 @@ class Trigger if (atoms.size() < 8) return; - auto& doc = CINI::CurrentDocument(); + auto& doc = CINIExt::CurrentDocument(); ID = id; House = atoms[0]; @@ -594,7 +594,7 @@ class CNewTrigger HWND m_hwnd; CFinalSunDlg* m_parent; - static CINI& map; + static CINIExt& map; static CINI& fadata; static MultimapHelper& rules; public: diff --git a/FA2sp/ExtraWindow/CObjectSearch/CObjectSearch.cpp b/FA2sp/ExtraWindow/CObjectSearch/CObjectSearch.cpp index 2b26926e..0129fb49 100644 --- a/FA2sp/ExtraWindow/CObjectSearch/CObjectSearch.cpp +++ b/FA2sp/ExtraWindow/CObjectSearch/CObjectSearch.cpp @@ -1080,7 +1080,7 @@ void CObjectSearch::OnSearchButtonUp(HWND hWnd) wp = "0"; } wp = wp.substr(start, end - start); - auto pWP = CINI::CurrentDocument->GetString("Waypoints", wp.c_str(), "-1"); + auto pWP = CINIExt::CurrentDocument->GetString("Waypoints", wp.c_str(), "-1"); auto second = atoi(pWP); if (second >= 0) { @@ -1801,7 +1801,7 @@ void CObjectSearch::UpdateDetailsWaypoint(HWND hWnd) while (SendMessage(hListBox, LB_DELETESTRING, 0, NULL) != LB_ERR); CObjectSearch::ListBoxIndex = 0; CObjectSearch::ListBox_MapCoord.clear(); - if (auto pSection = CINI::CurrentDocument->GetSection("Waypoints")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints")) { for (auto& pair : pSection->GetEntities()) { diff --git a/FA2sp/ExtraWindow/CSearhReference/CSearhReference.cpp b/FA2sp/ExtraWindow/CSearhReference/CSearhReference.cpp index 15b50992..2bfa1bf3 100644 --- a/FA2sp/ExtraWindow/CSearhReference/CSearhReference.cpp +++ b/FA2sp/ExtraWindow/CSearhReference/CSearhReference.cpp @@ -19,7 +19,7 @@ HWND CSearhReference::m_hwnd; CFinalSunDlg* CSearhReference::m_parent; -CINI& CSearhReference::map = CINI::CurrentDocument; +CINIExt& CSearhReference::map = CINIExt::CurrentDocument; MultimapHelper& CSearhReference::rules = Variables::RulesMap; HWND CSearhReference::hListbox; @@ -479,7 +479,7 @@ void CSearhReference::Update(bool top) } } } - if (auto pObjSection = CINI::CurrentDocument->GetSection("Structures")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Structures")) { for (auto& pair : pObjSection->GetEntities()) { @@ -506,7 +506,7 @@ void CSearhReference::Update(bool top) } } } - if (auto pObjSection = CINI::CurrentDocument->GetSection("Aircraft")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Aircraft")) { for (auto& pair : pObjSection->GetEntities()) { @@ -533,7 +533,7 @@ void CSearhReference::Update(bool top) } } } - if (auto pObjSection = CINI::CurrentDocument->GetSection("Units")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Units")) { for (auto& pair : pObjSection->GetEntities()) { @@ -560,7 +560,7 @@ void CSearhReference::Update(bool top) } } } - if (auto pObjSection = CINI::CurrentDocument->GetSection("Infantry")) + if (auto pObjSection = CINIExt::CurrentDocument->GetSection("Infantry")) { for (auto& pair : pObjSection->GetEntities()) { @@ -587,7 +587,7 @@ void CSearhReference::Update(bool top) } } } - if (auto pSection = CINI::CurrentDocument->GetSection("CellTags")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("CellTags")) { for (auto& pairObj : pSection->GetEntities()) { diff --git a/FA2sp/ExtraWindow/CSearhReference/CSearhReference.h b/FA2sp/ExtraWindow/CSearhReference/CSearhReference.h index 87311124..14830ea7 100644 --- a/FA2sp/ExtraWindow/CSearhReference/CSearhReference.h +++ b/FA2sp/ExtraWindow/CSearhReference/CSearhReference.h @@ -74,7 +74,7 @@ class CSearhReference private: static HWND m_hwnd; static CFinalSunDlg* m_parent; - static CINI& map; + static CINIExt& map; static MultimapHelper& rules; static HWND hListbox; diff --git a/FA2sp/ExtraWindow/CSelectAutoShore/CSelectAutoShore.cpp b/FA2sp/ExtraWindow/CSelectAutoShore/CSelectAutoShore.cpp index 6c595359..1b61bec2 100644 --- a/FA2sp/ExtraWindow/CSelectAutoShore/CSelectAutoShore.cpp +++ b/FA2sp/ExtraWindow/CSelectAutoShore/CSelectAutoShore.cpp @@ -38,7 +38,7 @@ BOOL CSelectAutoShore::OnInitDialog() if (auto pSection = CINI::FAData->GetSection("AutoShoreTypes")) { int index = 0; - auto thisTheater = CINI::CurrentDocument().GetString("Map", "Theater"); + auto thisTheater = CINIExt::CurrentDocument().GetString("Map", "Theater"); for (const auto& type : pSection->GetEntities()) { auto atoms = STDHelpers::SplitString(type.second, 3); diff --git a/FA2sp/ExtraWindow/CTechnoDialog/CTechnoDialog.cpp b/FA2sp/ExtraWindow/CTechnoDialog/CTechnoDialog.cpp index 0c63773c..cd6ce7fe 100644 --- a/FA2sp/ExtraWindow/CTechnoDialog/CTechnoDialog.cpp +++ b/FA2sp/ExtraWindow/CTechnoDialog/CTechnoDialog.cpp @@ -284,7 +284,7 @@ BOOL CTechnoDialog::OnInitDialog(HWND hDlg) { FString name; name.Format("%s - %s", m_strTag, - FString::GetParam(CINI::CurrentDocument->GetString("Tags", m_strTag, "0,MISSING,01000000"), 1)); + FString::GetParam(CINIExt::CurrentDocument->GetString("Tags", m_strTag, "0,MISSING,01000000"), 1)); index = m_comboBoxes[hTag]->FindStringExact(name); } else @@ -484,7 +484,7 @@ BOOL CTechnoDialog::OnInitDialog(HWND hDlg) auto vcb = std::make_unique(); vcb->Attach(hFollow, nullptr, true); vcb->AddString("-1 - None"); - if (auto pSection = CINI::CurrentDocument->GetSection("Units")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Units")) { int index = 0; FString text; diff --git a/FA2sp/ExtraWindow/CTerrainGenerator/CTerrainGenerator.cpp b/FA2sp/ExtraWindow/CTerrainGenerator/CTerrainGenerator.cpp index 4b27b70b..abfbc249 100644 --- a/FA2sp/ExtraWindow/CTerrainGenerator/CTerrainGenerator.cpp +++ b/FA2sp/ExtraWindow/CTerrainGenerator/CTerrainGenerator.cpp @@ -15,7 +15,7 @@ HWND CTerrainGenerator::m_hwnd; CFinalSunDlg* CTerrainGenerator::m_parent; -CINI& CTerrainGenerator::map = CINI::CurrentDocument; +CINIExt& CTerrainGenerator::map = CINIExt::CurrentDocument; std::unique_ptr> CTerrainGenerator::ini = nullptr; MultimapHelper& CTerrainGenerator::rules = Variables::RulesMap; HWND CTerrainGenerator::hTab; diff --git a/FA2sp/ExtraWindow/CTerrainGenerator/CTerrainGenerator.h b/FA2sp/ExtraWindow/CTerrainGenerator/CTerrainGenerator.h index 5ea6d022..a56af095 100644 --- a/FA2sp/ExtraWindow/CTerrainGenerator/CTerrainGenerator.h +++ b/FA2sp/ExtraWindow/CTerrainGenerator/CTerrainGenerator.h @@ -423,7 +423,7 @@ class CTerrainGenerator static bool bIgnoreLandtypes; static bool ProgrammaticallySettingText; - static CINI& map; + static CINIExt& map; static MultimapHelper& rules; static FMap> TerrainGeneratorPresets; static WNDPROC g_pOriginalTabPageProc; diff --git a/FA2sp/ExtraWindow/CTriggerAnnotation/CTriggerAnnotation.cpp b/FA2sp/ExtraWindow/CTriggerAnnotation/CTriggerAnnotation.cpp index 50507c55..5db1e29e 100644 --- a/FA2sp/ExtraWindow/CTriggerAnnotation/CTriggerAnnotation.cpp +++ b/FA2sp/ExtraWindow/CTriggerAnnotation/CTriggerAnnotation.cpp @@ -104,7 +104,7 @@ void CTriggerAnnotation::Update(const char* filter) text.Format(Translations::TranslateOrDefault("TriggerAnnotation.Text", "Current Object: %s"), name); SendMessage(hText, WM_SETTEXT, 0, text); - FString annotation = CINI::CurrentDocument->GetString("TriggerAnnotations", ID); + FString annotation = CINIExt::CurrentDocument->GetString("TriggerAnnotations", ID); if (!annotation.IsEmpty()) { size_t start = annotation.Find("START,"); @@ -236,7 +236,7 @@ void CTriggerAnnotation::OnEditchangeEdit() FString text(Buffer); if (text.IsEmpty()) { - CINI::CurrentDocument->DeleteKey("TriggerAnnotations", ID); + CINIExt::CurrentDocument->DeleteKey("TriggerAnnotations", ID); return; } text.Replace("\n\r", "\\n"); @@ -245,5 +245,5 @@ void CTriggerAnnotation::OnEditchangeEdit() text.Replace("\r", "\\n"); text = "START," + text; text = text + ",END"; - CINI::CurrentDocument->WriteString("TriggerAnnotations", ID, text); + CINIExt::CurrentDocument->WriteString("TriggerAnnotations", ID, text); } diff --git a/FA2sp/ExtraWindow/Common.cpp b/FA2sp/ExtraWindow/Common.cpp index 0787133d..2cb04e7e 100644 --- a/FA2sp/ExtraWindow/Common.cpp +++ b/FA2sp/ExtraWindow/Common.cpp @@ -28,7 +28,7 @@ #include #pragma comment(lib, "winmm.lib") -CINI& ExtraWindow::map = CINI::CurrentDocument; +CINIExt& ExtraWindow::map = CINIExt::CurrentDocument; CINI& ExtraWindow::fadata = CINI::FAData; MultimapHelper& ExtraWindow::rules = Variables::RulesMap; std::vector ExtraWindow::g_DropTargets; @@ -414,7 +414,7 @@ void ExtraWindow::LoadParams(VirtualComboBoxEx& vcb, FString idx, CNewTrigger* i { for (auto pINI : mmh.GetINIData()) { - if (pINI == &CINI::CurrentDocument) + if (pINI == &CINIExt::CurrentDocument) { // refresh indicies CMapDataExt::UpdateMapSectionIndicies(sectionName); @@ -525,7 +525,7 @@ void ExtraWindow::LoadParam_CountryList(VirtualComboBoxEx& vcb) { MultimapHelper mmh; mmh.AddINI(&CINI::Rules); - mmh.AddINI(&CINI::CurrentDocument); + mmh.AddINI(&CINIExt::CurrentDocument); int rIdx = 0; const auto& indicies = Variables::RulesMap.ParseIndicies("Countries", true); @@ -823,7 +823,7 @@ void ExtraWindow::LoadParams_Houses(VirtualComboBoxEx& vcb, bool multiOnly, bool players = false; auto pCountry = CINI::Rules->GetSection("Countries"); - auto pHouse = CINI::CurrentDocument->GetSection("Houses"); + auto pHouse = CINIExt::CurrentDocument->GetSection("Houses"); INISection* pSection = nullptr; if (CMapDataExt::IsInitingPropertyDialog) @@ -1404,7 +1404,7 @@ void ExtraWindow::LoadFrom(MultimapHelper& mmh, FString loadfrom) mmh = Variables::RulesMap; } else if (loadfrom == "3" || loadfrom == "map") - mmh.AddINI(&CINI::CurrentDocument); + mmh.AddINI(&CINIExt::CurrentDocument); else if (loadfrom == "4" || loadfrom == "art") mmh.AddINI(&CINI::Art); else if (loadfrom == "5" || loadfrom == "sound") @@ -1414,7 +1414,7 @@ void ExtraWindow::LoadFrom(MultimapHelper& mmh, FString loadfrom) else if (loadfrom == "7" || loadfrom == "ai+map") { mmh.AddINI(&CINI::Ai); - mmh.AddINI(&CINI::CurrentDocument); + mmh.AddINI(&CINIExt::CurrentDocument); } else if (loadfrom == "8" || loadfrom == "eva") mmh.AddINI(&CINI::Eva); @@ -4249,11 +4249,11 @@ BOOL CINIDialog::OnInitDialog() { if (info.Type == ControlType::CheckBox) { - CheckDlgButton(GetSafeHwnd(), id, CINI::CurrentDocument->GetBool(info.IniSection, info.IniKey) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(GetSafeHwnd(), id, CINIExt::CurrentDocument->GetBool(info.IniSection, info.IniKey) ? BST_CHECKED : BST_UNCHECKED); } else if (info.Type == ControlType::Edit) { - GetDlgItem(id)->SetWindowText(CINI::CurrentDocument->GetString(info.IniSection, info.IniKey)); + GetDlgItem(id)->SetWindowText(CINIExt::CurrentDocument->GetString(info.IniSection, info.IniKey)); } else if (info.Type == ControlType::Combobox) { @@ -4263,7 +4263,7 @@ BOOL CINIDialog::OnInitDialog() box->AddString(label); } ExtraWindow::AdjustDropdownWidth(box->GetSafeHwnd()); - auto text = CINI::CurrentDocument->GetString(info.IniSection, info.IniKey); + auto text = CINIExt::CurrentDocument->GetString(info.IniSection, info.IniKey); int idx = box->FindStringExact(0, text); if (idx != CB_ERR) { @@ -4318,7 +4318,7 @@ BOOL CINIDialog::OnCommand(WPARAM wParam, LPARAM lParam) if (info.Type == ControlType::CheckBox && nNotify == BN_CLICKED) { bool checked = (IsDlgButtonChecked(GetSafeHwnd(), nID) == BST_CHECKED); - CINI::CurrentDocument->WriteString(info.IniSection, info.IniKey, checked ? "yes" : "no"); + CINIExt::CurrentDocument->WriteString(info.IniSection, info.IniKey, checked ? "yes" : "no"); info.CallBack(); } else if (info.Type == ControlType::Edit && nNotify == EN_CHANGE) @@ -4327,9 +4327,9 @@ BOOL CINIDialog::OnCommand(WPARAM wParam, LPARAM lParam) GetDlgItem(nID)->GetWindowText(buffer); buffer.Trim(); if (!buffer.IsEmpty()) - CINI::CurrentDocument->WriteString(info.IniSection, info.IniKey, buffer); + CINIExt::CurrentDocument->WriteString(info.IniSection, info.IniKey, buffer); else - CINI::CurrentDocument->DeleteKey(info.IniSection, info.IniKey); + CINIExt::CurrentDocument->DeleteKey(info.IniSection, info.IniKey); info.CallBack(); } else if (info.Type == ControlType::Combobox && nNotify == CBN_SELCHANGE) @@ -4343,9 +4343,9 @@ BOOL CINIDialog::OnCommand(WPARAM wParam, LPARAM lParam) box->GetWindowText(buffer); buffer.Trim(); if (!buffer.IsEmpty()) - CINI::CurrentDocument->WriteString(info.IniSection, info.IniKey, buffer); + CINIExt::CurrentDocument->WriteString(info.IniSection, info.IniKey, buffer); else - CINI::CurrentDocument->DeleteKey(info.IniSection, info.IniKey); + CINIExt::CurrentDocument->DeleteKey(info.IniSection, info.IniKey); info.CallBack(); } else if (info.Type == ControlType::Combobox && nNotify == CBN_EDITCHANGE) @@ -4354,9 +4354,9 @@ BOOL CINIDialog::OnCommand(WPARAM wParam, LPARAM lParam) GetDlgItem(nID)->GetWindowText(buffer); buffer.Trim(); if (!buffer.IsEmpty()) - CINI::CurrentDocument->WriteString(info.IniSection, info.IniKey, buffer); + CINIExt::CurrentDocument->WriteString(info.IniSection, info.IniKey, buffer); else - CINI::CurrentDocument->DeleteKey(info.IniSection, info.IniKey); + CINIExt::CurrentDocument->DeleteKey(info.IniSection, info.IniKey); info.CallBack(); } else if (info.Type == ControlType::Button && nNotify == BN_CLICKED) diff --git a/FA2sp/ExtraWindow/Common.h b/FA2sp/ExtraWindow/Common.h index 1d4d21fe..a4897ef0 100644 --- a/FA2sp/ExtraWindow/Common.h +++ b/FA2sp/ExtraWindow/Common.h @@ -1,6 +1,7 @@ #pragma once #include "FA2PP.h" #include "../Helpers/MultimapHelper.h" +#include "../Miscs/Hooks.INI.h" #include class FString; @@ -202,7 +203,7 @@ class ExtraWindow }; static BOOL CALLBACK DisableOtherWindowsProc(HWND hEnum, LPARAM lParam); static std::vector s_disabledWindows; - static CINI& map; + static CINIExt& map; static CINI& fadata; static MultimapHelper& rules; }; diff --git a/FA2sp/FA2sp.cpp b/FA2sp/FA2sp.cpp index e4388ac3..87109969 100644 --- a/FA2sp/FA2sp.cpp +++ b/FA2sp/FA2sp.cpp @@ -21,6 +21,7 @@ #include #include "Helpers/Translations.h" #include "Miscs/DialogStyle.h" +#include "Miscs/Hooks.INI.h" #include "Helpers/TheaterHelpers.h" #define ENABLE_VISUAL_STYLE @@ -226,6 +227,7 @@ bool ExtConfigs::PreciseDepthCalculation; bool ExtConfigs::DisableDirectoryCheck; bool ExtConfigs::ExtOverlays; bool ExtConfigs::SaveMap_PreserveINISorting; +bool ExtConfigs::SaveMap_AdaptiveSorting; bool ExtConfigs::ExtMixLoader; int ExtConfigs::DisplayTextSize; int ExtConfigs::DistanceRuler_Records; @@ -261,10 +263,10 @@ float ExtConfigs::IsoViewHeightPercentage = 0.5f; std::vector ExtConfigs::Options; -MultimapHelper Variables::RulesMap = {&CINI::Rules(), &CINI::CurrentDocument()}; +MultimapHelper Variables::RulesMap = {&CINI::Rules(), &CINIExt::CurrentDocument()}; MultimapHelper Variables::Rules = {&CINI::Rules()}; MultimapHelper Variables::FAData = {&CINI::FAData()}; -MultimapHelper Variables::Rules_FAData = {&CINI::Rules(), &CINI::CurrentDocument(), &CINI::FAData()}; +MultimapHelper Variables::Rules_FAData = {&CINI::Rules(), &CINIExt::CurrentDocument(), &CINI::FAData()}; void FA2sp::ExtConfigsInitialize() { @@ -460,6 +462,9 @@ void FA2sp::ExtConfigsInitialize() ExtConfigs::SaveMap_OnlySaveMAP = CINI::FAData->GetBool("ExtConfigs", "SaveMap.OnlySaveMAP"); ExtConfigs::SaveMap_KeepComments = CINI::FAData->GetBool("ExtConfigs", "SaveMap.KeepComments"); ExtConfigs::SaveMap_PreserveINISorting = CINI::FAData->GetBool("ExtConfigs", "SaveMap.PreserveINISorting"); + ExtConfigs::SaveMap_AdaptiveSorting = CINI::FAData->GetBool("ExtConfigs", "SaveMap.AdaptiveSorting"); + CINIExt::SetKeepSectionSorting(ExtConfigs::SaveMap_PreserveINISorting); + CINIExt::SetAdaptiveSorting(ExtConfigs::SaveMap_AdaptiveSorting); // ExtConfigs::SaveMap_MultiPlayOnlySaveYRM = CINI::FAData->GetBool("ExtConfigs", "SaveMap.OnlySaveYRM.MultiPlay"); // ExtConfigs::SaveMap_SinglePlayOnlySaveMAP = CINI::FAData->GetBool("ExtConfigs", "SaveMap.OnlySaveMAP.SinglePlay"); ExtConfigs::SaveMap_DefaultPreviewOptionMP = CINI::FAData->GetInteger("ExtConfigs", "SaveMap.DefaultPreviewOptionMP", 0); @@ -1111,6 +1116,12 @@ void ExtConfigs::UpdateOptionTranslations() .Value = &ExtConfigs::SaveMap_PreserveINISorting, .Type = ExtConfigs::SpecialOptionType::ReloadMap}); + ExtConfigs::Options.push_back(ExtConfigs::DynamicOptions{ + .DisplayName = Translations::TranslateOrDefault("Options.SaveMap.AdaptiveSorting", "Adaptive INI key sorting when saving"), + .IniKey = "SaveMap.AdaptiveSorting", + .Value = &ExtConfigs::SaveMap_AdaptiveSorting, + .Type = ExtConfigs::SpecialOptionType::ReloadMap}); + ExtConfigs::Options.push_back(ExtConfigs::DynamicOptions{ .DisplayName = Translations::TranslateOrDefault("Options.FileWatcher", "Enable file watcher"), .IniKey = "FileWatcher", diff --git a/FA2sp/FA2sp.h b/FA2sp/FA2sp.h index e3c2ca83..69672f17 100644 --- a/FA2sp/FA2sp.h +++ b/FA2sp/FA2sp.h @@ -2,6 +2,7 @@ #include "Ext/FA2Expand.h" #include "Helpers/MultimapHelper.h" +#include "Helpers/Helper.h" #include #include @@ -239,6 +240,7 @@ class ExtConfigs static bool DisableDirectoryCheck; static bool ExtOverlays; static bool SaveMap_PreserveINISorting; + static bool SaveMap_AdaptiveSorting; static bool ExtMixLoader; static bool AVX2_Support; static bool AutoDarkMode; @@ -289,16 +291,9 @@ class ExtConfigs static void UpdateOptionTranslations(); }; + namespace std { - template <> - struct hash - { - size_t operator()(const ppmfc::CString &str) const - { - return hash()(string_view(str, str.GetLength())); - } - }; template <> struct hash { diff --git a/FA2sp/Helpers/CINIOrderTracker.cpp b/FA2sp/Helpers/CINIOrderTracker.cpp new file mode 100644 index 00000000..5fe903de --- /dev/null +++ b/FA2sp/Helpers/CINIOrderTracker.cpp @@ -0,0 +1,321 @@ +#include +#include "CINIOrderTracker.h" +#include +#include +#include +#include + +static inline bool IsNumericKey(const ppmfc::CString& key) +{ + if (key.IsEmpty()) + return false; + for (int i = 0; i < key.GetLength(); ++i) + { + if (!isdigit(static_cast(key[i]))) + return false; + } + return true; +} + +static inline bool NumericKeyLess(const ppmfc::CString& a, const ppmfc::CString& b) +{ + int lenA = a.GetLength(); + int lenB = b.GetLength(); + if (lenA != lenB) + return lenA < lenB; + return strcmp(a.GetString(), b.GetString()) < 0; +} + +struct INIOrderData +{ + SequencedKeyList SectionOrder; + std::unordered_map KeyOrder; +}; + +static std::unordered_map g_iniOrderMap; + +void CINIOrderTracker::RecordSection(CINI* ini, const ppmfc::CString& section) +{ + if (ini && !section.IsEmpty()) + { + g_iniOrderMap[ini].SectionOrder.Add(section); + } +} + +void CINIOrderTracker::RecordKey(CINI* ini, const ppmfc::CString& section, const ppmfc::CString& key) +{ + if (ini && !section.IsEmpty() && !key.IsEmpty()) + { + auto& data = g_iniOrderMap[ini]; + data.SectionOrder.Add(section); + data.KeyOrder[section].Add(key); + } +} + +void CINIOrderTracker::RemoveKey(CINI* ini, const ppmfc::CString& section, const ppmfc::CString& key) +{ + if (ini) + { + auto it = g_iniOrderMap.find(ini); + if (it != g_iniOrderMap.end()) + { + auto kIt = it->second.KeyOrder.find(section); + if (kIt != it->second.KeyOrder.end()) + { + kIt->second.Remove(key); + } + } + } +} + +void CINIOrderTracker::RemoveSection(CINI* ini, const ppmfc::CString& section) +{ + if (ini) + { + auto it = g_iniOrderMap.find(ini); + if (it != g_iniOrderMap.end()) + { + it->second.SectionOrder.Remove(section); + it->second.KeyOrder.erase(section); + } + } +} + +void CINIOrderTracker::Clear(CINI* ini) +{ + if (ini) + { + auto it = g_iniOrderMap.find(ini); + if (it != g_iniOrderMap.end()) + { + it->second.SectionOrder.Clear(); + it->second.KeyOrder.clear(); + } + } +} + +const SequencedKeyList* CINIOrderTracker::GetSectionOrder(CINI* ini) +{ + if (ini) + { + auto it = g_iniOrderMap.find(ini); + if (it != g_iniOrderMap.end() && !it->second.SectionOrder.Empty()) + { + return &it->second.SectionOrder; + } + } + return nullptr; +} + +const SequencedKeyList* CINIOrderTracker::GetKeyOrder(CINI* ini, const ppmfc::CString& section) +{ + if (ini) + { + auto it = g_iniOrderMap.find(ini); + if (it != g_iniOrderMap.end()) + { + auto kIt = it->second.KeyOrder.find(section); + if (kIt != it->second.KeyOrder.end() && !kIt->second.Empty()) + { + return &kIt->second; + } + } + } + return nullptr; +} + +SequencedKeyList* CINIOrderTracker::GetOrCreateKeyOrder(CINI* ini, const ppmfc::CString& section) +{ + if (ini && !section.IsEmpty()) + { + auto& data = g_iniOrderMap[ini]; + data.SectionOrder.Add(section); + return &data.KeyOrder[section]; + } + return nullptr; +} + +std::vector CINIOrderTracker::GetSections(CINI* ini, bool preserveOrder) +{ + std::vector result; + if (!ini) + return result; + + result.reserve(ini->Dict.size()); + + if (!preserveOrder) + { + for (auto& pair : ini->Dict) + { + result.push_back({ pair.first, &pair.second }); + } + return result; + } + + std::unordered_set emitted; + emitted.reserve(ini->Dict.size()); + + // Pass 1: emit tracked sections in queued order that still exist in Dict + const auto* pSectionOrder = GetSectionOrder(ini); + if (pSectionOrder) + { + for (const auto& secName : *pSectionOrder) + { + auto it = ini->Dict.find(secName); + if (it != ini->Dict.end()) + { + result.push_back({ it->first, &it->second }); + emitted.insert(it->first); + } + } + } + + // Pass 2: emit untracked sections in Dict order and record them into tracker + for (auto& pair : ini->Dict) + { + if (emitted.find(pair.first) == emitted.end()) + { + RecordSection(ini, pair.first); + result.push_back({ pair.first, &pair.second }); + emitted.insert(pair.first); + } + } + + return result; +} + +std::vector CINIOrderTracker::GetSectionNames(CINI* ini, bool preserveOrder) +{ + auto sections = GetSections(ini, preserveOrder); + std::vector names; + names.reserve(sections.size()); + for (auto& s : sections) + { + names.push_back(std::move(s.Name)); + } + return names; +} + +std::vector CINIOrderTracker::GetEntries(CINI* ini, const ppmfc::CString& sectionName, INISection* section, bool adaptiveSorting) +{ + std::vector result; + if (!section) + { + if (!ini) + return result; + section = ini->GetSection(sectionName); + if (!section) + return result; + } + + auto& entities = section->GetEntities(); + result.reserve(entities.size()); + + if (!adaptiveSorting || !ini) + { + for (const auto& pair : entities) + { + result.push_back({ pair.first, pair.second }); + } + return result; + } + + std::unordered_set emitted; + emitted.reserve(entities.size()); + + // Pass 1: emit tracked keys in queued order that still exist in entities + const auto* pKeyOrder = GetKeyOrder(ini, sectionName); + if (pKeyOrder) + { + for (const auto& key : *pKeyOrder) + { + auto it = entities.find(key); + if (it != entities.end()) + { + result.push_back({ it->first, it->second }); + emitted.insert(it->first); + } + } + } + + // Pass 2: emit untracked keys in entities order and record them into tracker + for (const auto& pair : entities) + { + if (emitted.find(pair.first) == emitted.end()) + { + RecordKey(ini, sectionName, pair.first); + result.push_back({ pair.first, pair.second }); + emitted.insert(pair.first); + } + } + + // Pass 3: Adaptive sorting for numeric keys + // If the section contains numeric keys, sort them naturally among themselves + // while keeping non-numeric (text) keys in their original relative positions. + std::vector numericIndices; + numericIndices.reserve(result.size()); + for (size_t i = 0; i < result.size(); ++i) + { + if (IsNumericKey(result[i].Key)) + { + numericIndices.push_back(i); + } + } + + if (numericIndices.size() > 1) + { + bool isSorted = true; + for (size_t i = 1; i < numericIndices.size(); ++i) + { + if (!NumericKeyLess(result[numericIndices[i - 1]].Key, result[numericIndices[i]].Key)) + { + isSorted = false; + break; + } + } + + if (!isSorted) + { + std::vector numericItems; + numericItems.reserve(numericIndices.size()); + for (size_t idx : numericIndices) + { + numericItems.push_back(std::move(result[idx])); + } + + std::stable_sort(numericItems.begin(), numericItems.end(), [](const KeyValueItem& a, const KeyValueItem& b) { + return NumericKeyLess(a.Key, b.Key); + }); + + for (size_t i = 0; i < numericIndices.size(); ++i) + { + result[numericIndices[i]] = std::move(numericItems[i]); + } + + // Sync the adaptively sorted order back into tracker + auto* pOrder = GetOrCreateKeyOrder(ini, sectionName); + if (pOrder) + { + pOrder->Clear(); + for (const auto& item : result) + { + pOrder->Add(item.Key); + } + } + } + } + + return result; +} + +std::vector CINIOrderTracker::GetKeyNames(CINI* ini, const ppmfc::CString& sectionName, INISection* section, bool adaptiveSorting) +{ + auto entries = GetEntries(ini, sectionName, section, adaptiveSorting); + std::vector keys; + keys.reserve(entries.size()); + for (auto& e : entries) + { + keys.push_back(std::move(e.Key)); + } + return keys; +} diff --git a/FA2sp/Helpers/CINIOrderTracker.h b/FA2sp/Helpers/CINIOrderTracker.h new file mode 100644 index 00000000..6f6daa7f --- /dev/null +++ b/FA2sp/Helpers/CINIOrderTracker.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include "SequencedKeyList.h" + +class CINI; +class INISection; + +struct SectionItem +{ + ppmfc::CString Name; + INISection* Section{ nullptr }; + + operator const ppmfc::CString&() const { return Name; } + operator const char*() const { return Name.GetString(); } +}; + +struct KeyValueItem +{ + ppmfc::CString Key; + ppmfc::CString Value; +}; + +class CINIOrderTracker +{ +public: + static void RecordSection(CINI* ini, const ppmfc::CString& section); + static void RecordKey(CINI* ini, const ppmfc::CString& section, const ppmfc::CString& key); + static void RemoveKey(CINI* ini, const ppmfc::CString& section, const ppmfc::CString& key); + static void RemoveSection(CINI* ini, const ppmfc::CString& section); + static void Clear(CINI* ini); + static const SequencedKeyList* GetSectionOrder(CINI* ini); + static const SequencedKeyList* GetKeyOrder(CINI* ini, const ppmfc::CString& section); + static SequencedKeyList* GetOrCreateKeyOrder(CINI* ini, const ppmfc::CString& section); + + // Range / iteration helpers: returns items in preserved queue order (or map dictionary order if preserveOrder is false) + static std::vector GetSections(CINI* ini, bool preserveOrder = true); + static std::vector GetSectionNames(CINI* ini, bool preserveOrder = true); + static std::vector GetEntries(CINI* ini, const ppmfc::CString& sectionName, INISection* section = nullptr, bool adaptiveSorting = true); + static std::vector GetKeyNames(CINI* ini, const ppmfc::CString& sectionName, INISection* section = nullptr, bool adaptiveSorting = true); +}; diff --git a/FA2sp/Helpers/ControlHelpers.cpp b/FA2sp/Helpers/ControlHelpers.cpp index 6099abbb..87a25c00 100644 --- a/FA2sp/Helpers/ControlHelpers.cpp +++ b/FA2sp/Helpers/ControlHelpers.cpp @@ -4,6 +4,7 @@ #include "MultimapHelper.h" #include "../FA2sp.h" +#include "../Miscs/Hooks.INI.h" #include @@ -29,12 +30,12 @@ namespace ControlHelpers void ComboBox::LoadCountries(ppmfc::CComboBox& combobox, bool bShowIndex) { combobox.DeleteAllStrings(); - auto& doc = CINI::CurrentDocument(); + auto& doc = CINIExt::CurrentDocument(); MultimapHelper mmh; mmh.AddINI(&CINI::Rules()); - mmh.AddINI(&CINI::CurrentDocument()); + mmh.AddINI(&CINIExt::CurrentDocument()); auto&& entries = mmh.ParseIndicies("Countries", true); @@ -108,10 +109,10 @@ namespace ControlHelpers break; case 2: mmh.AddINI(&CINI::Rules()); - mmh.AddINI(&CINI::CurrentDocument()); + mmh.AddINI(&CINIExt::CurrentDocument()); break; case 3: - mmh.AddINI(&CINI::CurrentDocument()); + mmh.AddINI(&CINIExt::CurrentDocument()); break; case 4: mmh.AddINI(&CINI::Art()); @@ -119,7 +120,7 @@ namespace ControlHelpers case 5: mmh.AddINI(&CINI::FAData()); mmh.AddINI(&CINI::Rules()); - mmh.AddINI(&CINI::CurrentDocument()); + mmh.AddINI(&CINIExt::CurrentDocument()); break; } if (bUseStrictOrder) @@ -242,39 +243,39 @@ namespace ControlHelpers { if (bShowIndex) { - combobox.SetItemData(combobox.AddString("-1 - Ëæ»ú·ÇÖÐÁ¢ËùÊô·½"), -1); - combobox.SetItemData(combobox.AddString("-2 - µÚһ¸öÖÐÁ¢ËùÊô·½"), -2); - combobox.SetItemData(combobox.AddString("-3 - Ëæ»úÈËÀàÍæ¼Ò"), -3); + combobox.SetItemData(combobox.AddString("-1 - 随机非中立所属方"), -1); + combobox.SetItemData(combobox.AddString("-2 - 第一个中立所属方"), -2); + combobox.SetItemData(combobox.AddString("-3 - 随机人类玩家"), -3); } else { - combobox.SetItemData(combobox.AddString("Ëæ»ú·ÇÖÐÁ¢ËùÊô·½"), -1); - combobox.SetItemData(combobox.AddString("µÚһ¸öÖÐÁ¢ËùÊô·½"), -2); - combobox.SetItemData(combobox.AddString("Ëæ»úÈËÀàÍæ¼Ò"), -3); + combobox.SetItemData(combobox.AddString("随机非中立所属方"), -1); + combobox.SetItemData(combobox.AddString("第一个中立所属方"), -2); + combobox.SetItemData(combobox.AddString("随机人类玩家"), -3); } } void ComboBox::LoadHousesAddon2(ppmfc::CComboBox& combobox, bool bShowIndex) { if (bShowIndex) { - combobox.SetItemData(combobox.AddString("-1 - ÈÎÒâËùÊô·½"), -1); - combobox.SetItemData(combobox.AddString("-2 - ´¥·¢ËùÊô·½"), -2); + combobox.SetItemData(combobox.AddString("-1 - 任意所属方"), -1); + combobox.SetItemData(combobox.AddString("-2 - 触发所属方"), -2); } else { - combobox.SetItemData(combobox.AddString("ÈÎÒâËùÊô·½"), -1); - combobox.SetItemData(combobox.AddString("´¥·¢ËùÊô·½"), -2); + combobox.SetItemData(combobox.AddString("任意所属方"), -1); + combobox.SetItemData(combobox.AddString("触发所属方"), -2); } } void ComboBox::LoadHousesAddon3(ppmfc::CComboBox& combobox, bool bShowIndex) { if (bShowIndex) { - combobox.SetItemData(combobox.AddString("-1 - ÈÎÒâËùÊô·½"), -1); + combobox.SetItemData(combobox.AddString("-1 - 任意所属方"), -1); } else { - combobox.SetItemData(combobox.AddString("ÈÎÒâËùÊô·½"), -1); + combobox.SetItemData(combobox.AddString("任意所属方"), -1); } } void ComboBox::LoadHousesAddonMulti(ppmfc::CComboBox& combobox, bool bShowIndex) diff --git a/FA2sp/Helpers/Helper.h b/FA2sp/Helpers/Helper.h index 2c0ae4da..3f2e64b3 100644 --- a/FA2sp/Helpers/Helper.h +++ b/FA2sp/Helpers/Helper.h @@ -1,6 +1,23 @@ #pragma once #include +#include +#include + +#ifndef PPMFC_CSTRING_HASH_DEFINED +#define PPMFC_CSTRING_HASH_DEFINED +namespace std +{ + template <> + struct hash + { + size_t operator()(const ppmfc::CString &str) const noexcept + { + return hash()(string_view(str, str.GetLength())); + } + }; +} +#endif static size_t ParseList(const char* pValue, const char** outValue, size_t Count = 1) { char buffer[0x400] = { 0 }; diff --git a/FA2sp/Helpers/SequencedKeyList.h b/FA2sp/Helpers/SequencedKeyList.h new file mode 100644 index 00000000..fe735aa3 --- /dev/null +++ b/FA2sp/Helpers/SequencedKeyList.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include "Helper.h" + +class SequencedKeyList +{ +public: + using ListType = std::list; + using MapType = std::unordered_map; + + bool Add(const ppmfc::CString& key) + { + if (m_lookup.find(key) == m_lookup.end()) + { + m_order.push_back(key); + m_lookup[key] = std::prev(m_order.end()); + return true; + } + return false; + } + + bool Remove(const ppmfc::CString& key) + { + auto it = m_lookup.find(key); + if (it != m_lookup.end()) + { + m_order.erase(it->second); + m_lookup.erase(it); + return true; + } + return false; + } + + bool Contains(const ppmfc::CString& key) const + { + return m_lookup.find(key) != m_lookup.end(); + } + + void Clear() + { + m_order.clear(); + m_lookup.clear(); + } + + size_t Size() const + { + return m_order.size(); + } + + size_t size() const noexcept + { + return m_order.size(); + } + + bool Empty() const + { + return m_order.empty(); + } + + bool empty() const noexcept + { + return m_order.empty(); + } + + const ListType& GetOrder() const + { + return m_order; + } + + auto begin() const noexcept { return m_order.begin(); } + auto end() const noexcept { return m_order.end(); } + +private: + ListType m_order; + MapType m_lookup; +}; diff --git a/FA2sp/Helpers/TheaterHelpers.h b/FA2sp/Helpers/TheaterHelpers.h index ef846dfc..450afef4 100644 --- a/FA2sp/Helpers/TheaterHelpers.h +++ b/FA2sp/Helpers/TheaterHelpers.h @@ -8,6 +8,7 @@ #include #include +#include "../Miscs/Hooks.INI.h" #include #include @@ -67,7 +68,7 @@ class TheaterHelpers if (!CMapData::Instance->MapWidthPlusHeight) return ""; - return GetSuffix(CINI::CurrentDocument->GetString("Map", "Theater")); + return GetSuffix(CINIExt::CurrentDocument->GetString("Map", "Theater")); } static bool IsAllowedTheaterName(ppmfc::CString theaterName) diff --git a/FA2sp/Miscs/CopyPaste.cpp b/FA2sp/Miscs/CopyPaste.cpp index 7be86718..4c66878b 100644 --- a/FA2sp/Miscs/CopyPaste.cpp +++ b/FA2sp/Miscs/CopyPaste.cpp @@ -101,7 +101,7 @@ void CopyPaste::Copy(const std::set& coords) auto it = structures.begin(); std::advance(it, 0); int iniIndex = CMapDataExt::StructureIndexMap[it->first]; - ppmfc::CString value = CINI::CurrentDocument->GetValueAt("Structures", iniIndex); + ppmfc::CString value = CINIExt::CurrentDocument->GetValueAt("Structures", iniIndex); auto atoms = STDHelpers::SplitString(value, 16); if (coords.X == atoi(atoms[4]) && coords.Y == atoi(atoms[3])) { @@ -114,7 +114,7 @@ void CopyPaste::Copy(const std::set& coords) auto it = structures.begin(); std::advance(it, 1); int iniIndex = CMapDataExt::StructureIndexMap[it->first]; - ppmfc::CString value = CINI::CurrentDocument->GetValueAt("Structures", iniIndex); + ppmfc::CString value = CINIExt::CurrentDocument->GetValueAt("Structures", iniIndex); auto atoms = STDHelpers::SplitString(value, 16); if (coords.X == atoi(atoms[4]) && coords.Y == atoi(atoms[3])) { @@ -127,7 +127,7 @@ void CopyPaste::Copy(const std::set& coords) auto it = structures.begin(); std::advance(it, 2); int iniIndex = CMapDataExt::StructureIndexMap[it->first]; - ppmfc::CString value = CINI::CurrentDocument->GetValueAt("Structures", iniIndex); + ppmfc::CString value = CINIExt::CurrentDocument->GetValueAt("Structures", iniIndex); auto atoms = STDHelpers::SplitString(value, 16); if (coords.X == atoi(atoms[4]) && coords.Y == atoi(atoms[3])) { @@ -137,29 +137,29 @@ void CopyPaste::Copy(const std::set& coords) } if (pCell->Aircraft > -1) { - ppmfc::CString value = CINI::CurrentDocument->GetValueAt("Aircraft", pCell->Aircraft); + ppmfc::CString value = CINIExt::CurrentDocument->GetValueAt("Aircraft", pCell->Aircraft); pushString(value.GetString(), item.AircraftData); objectMask |= ObjectRecord::RecordType::Aircraft; } if (pCell->Infantry[0] > -1) { - ppmfc::CString value = CINI::CurrentDocument->GetValueAt("Infantry", pCell->Infantry[0]); + ppmfc::CString value = CINIExt::CurrentDocument->GetValueAt("Infantry", pCell->Infantry[0]); pushString(value.GetString(), item.InfantryData_1); objectMask |= ObjectRecord::RecordType::Infantry; } if (pCell->Infantry[1] > -1) { - ppmfc::CString value = CINI::CurrentDocument->GetValueAt("Infantry", pCell->Infantry[1]); + ppmfc::CString value = CINIExt::CurrentDocument->GetValueAt("Infantry", pCell->Infantry[1]); pushString(value.GetString(), item.InfantryData_2); objectMask |= ObjectRecord::RecordType::Infantry; } if (pCell->Infantry[2] > -1) { - ppmfc::CString value = CINI::CurrentDocument->GetValueAt("Infantry", pCell->Infantry[2]); + ppmfc::CString value = CINIExt::CurrentDocument->GetValueAt("Infantry", pCell->Infantry[2]); pushString(value.GetString(), item.InfantryData_3); objectMask |= ObjectRecord::RecordType::Infantry; } if (pCell->Unit > -1) { - ppmfc::CString value = CINI::CurrentDocument->GetValueAt("Units", pCell->Unit); + ppmfc::CString value = CINIExt::CurrentDocument->GetValueAt("Units", pCell->Unit); pushString(value.GetString(), item.UnitData); objectMask |= ObjectRecord::RecordType::Unit; } diff --git a/FA2sp/Miscs/Exception.cpp b/FA2sp/Miscs/Exception.cpp index 216e227e..5f195052 100644 --- a/FA2sp/Miscs/Exception.cpp +++ b/FA2sp/Miscs/Exception.cpp @@ -1,5 +1,6 @@ #include "Exception.h" #include "SaveMap.h" +#include "Hooks.INI.h" #include "../FA2sp.h" #include "../FA2sp.Constants.h" @@ -208,7 +209,7 @@ LONG CALLBACK Exception::ExceptionFilter(PEXCEPTION_POINTERS const pExs) CINI::Temperate->Release(); CINI::Snow->Release(); CINI::Urban->Release(); - CINI::CurrentDocument->Release(); + CINIExt::CurrentDocument->Release(); CINI::Sound->Release(); CINI::Turtorial->Release(); CINI::FAData->Release(); diff --git a/FA2sp/Miscs/Hooks.INI.cpp b/FA2sp/Miscs/Hooks.INI.cpp index 30ec0120..6545ac34 100644 --- a/FA2sp/Miscs/Hooks.INI.cpp +++ b/FA2sp/Miscs/Hooks.INI.cpp @@ -3,6 +3,7 @@ #include "../Ext/CLoading/Body.h" #include "../Helpers/Helper.h" #include "../Helpers/TheaterHelpers.h" +#include "../Helpers/CINIOrderTracker.h" using std::map; using std::vector; @@ -19,12 +20,168 @@ std::unordered_map CINIManager::propertyMap; bool INIIncludes::SkipBracketFix = false; bool CINIExt::IsLoadingFAini = false; +static void NoOpRecordKey(CINI*, const ppmfc::CString&, const ppmfc::CString&) noexcept {} +static void NoOpRemoveKey(CINI*, const ppmfc::CString&, const ppmfc::CString&) noexcept {} +static void NoOpRecordSection(CINI*, const ppmfc::CString&) noexcept {} +static void NoOpRemoveSection(CINI*, const ppmfc::CString&) noexcept {} + +CINIExt::FnRecordKey_t CINIExt::FnRecordKey = &CINIOrderTracker::RecordKey; +CINIExt::FnRemoveKey_t CINIExt::FnRemoveKey = &CINIOrderTracker::RemoveKey; +CINIExt::FnRecordSection_t CINIExt::FnRecordSection = &CINIOrderTracker::RecordSection; +CINIExt::FnRemoveSection_t CINIExt::FnRemoveSection = &CINIOrderTracker::RemoveSection; + +static bool bAdaptiveSortingEnabled = true; +static bool bKeepSectionSortingEnabled = true; + +void CINIExt::SetAdaptiveSorting(bool enable) +{ + bAdaptiveSortingEnabled = enable; + if (enable) + { + FnRecordKey = &CINIOrderTracker::RecordKey; + FnRemoveKey = &CINIOrderTracker::RemoveKey; + } + else + { + FnRecordKey = &NoOpRecordKey; + FnRemoveKey = &NoOpRemoveKey; + } +} + +void CINIExt::SetKeepSectionSorting(bool enable) +{ + bKeepSectionSortingEnabled = enable; + if (enable) + { + FnRecordSection = &CINIOrderTracker::RecordSection; + FnRemoveSection = &CINIOrderTracker::RemoveSection; + } + else + { + FnRecordSection = &NoOpRecordSection; + FnRemoveSection = &NoOpRemoveSection; + } +} + +bool CINIExt::GetAdaptiveSorting() +{ + return bAdaptiveSortingEnabled; +} + +bool CINIExt::GetKeepSectionSorting() +{ + return bKeepSectionSortingEnabled; +} + +bool CINIExt::WriteString(ppmfc::CString pSection, ppmfc::CString pKey, ppmfc::CString pValue) +{ + if (pSection.IsEmpty() || pKey.IsEmpty()) + return false; + + FnRecordSection(this, pSection); + FnRecordKey(this, pSection, pKey); + return CINI::WriteString(pSection, pKey, pValue); +} + +bool CINIExt::WriteString(INISection* pSection, ppmfc::CString pKey, ppmfc::CString pValue) +{ + if (!pSection || pKey.IsEmpty()) + return false; + + if (FnRecordKey != &NoOpRecordKey) + { + for (const auto& pair : Dict) + { + if (&pair.second == pSection) + { + FnRecordKey(this, pair.first, pKey); + break; + } + } + } + return CINI::WriteString(pSection, pKey, pValue); +} + +bool CINIExt::DeleteKey(ppmfc::CString pSection, ppmfc::CString pKey) +{ + bool bDeleted = CINI::DeleteKey(pSection, pKey); + if (bDeleted) + { + FnRemoveKey(this, pSection, pKey); + } + return bDeleted; +} + +bool CINIExt::DeleteKey(INISection* pSection, ppmfc::CString pKey) +{ + if (!pSection || pKey.IsEmpty()) + return false; + + if (FnRemoveKey != &NoOpRemoveKey) + { + for (const auto& pair : Dict) + { + if (&pair.second == pSection) + { + FnRemoveKey(this, pair.first, pKey); + break; + } + } + } + return CINI::DeleteKey(pSection, pKey); +} + +bool CINIExt::DeleteSection(ppmfc::CString pSection) +{ + bool bDeleted = CINI::DeleteSection(pSection); + if (bDeleted) + { + FnRemoveSection(this, pSection); + } + return bDeleted; +} + +INISection* CINIExt::AddSection(ppmfc::CString pSectionName) +{ + auto* pSection = CINI::AddSection(pSectionName); + if (pSection) + { + FnRecordSection(this, pSectionName); + } + return pSection; +} + +INISection* CINIExt::AddOrGetSection(ppmfc::CString pSectionName) +{ + auto* pSection = CINI::AddOrGetSection(pSectionName); + FnRecordSection(this, pSectionName); + return pSection; +} + +bool CINIExt::WriteBool(ppmfc::CString pSection, ppmfc::CString pKey, bool pValue) +{ + return WriteString(pSection, pKey, pValue ? "yes" : "no"); +} + +int CINIExt::ClearAndLoad(const char* lpPath, int bTrimSpace) +{ + CINIOrderTracker::Clear(this); + return CINI::ClearAndLoad(lpPath, bTrimSpace); +} + +void CINIExt::Release() +{ + CINIOrderTracker::Clear(this); + CINI::Release(); +} + using INIPair = std::pair; void CINIExt::LoadINIExt(uint8_t* pFile, size_t fileSize, const char* lpSection, bool bClear, bool bTrimSpace, bool bAllowInclude, std::vector* parentIncludeInis) { if (bClear) { + CINIOrderTracker::Clear(this); auto itr = Dict.end(); for (size_t i = 0, sz = Dict.size(); i < sz && itr != Dict.begin(); ++i) { --itr; @@ -33,13 +190,14 @@ void CINIExt::LoadINIExt(uint8_t* pFile, size_t fileSize, const char* lpSection, } } - auto writeString = [](INISection* pSection, const FString& key, const FString& value) + auto writeString = [this](INISection* pSection, const ppmfc::CString& sectionName, const FString& key, const FString& value) { std::pair ins = std::make_pair(key, value); std::pair ret; reinterpret_cast(&pSection->GetEntities())->insert(&ret, &ins); if (!ret.second) new(&ret.first->second) ppmfc::CString(value); + FnRecordKey(this, sectionName, key); }; auto encoding = STDHelpers::GetFileEncoding(pFile, fileSize); @@ -66,7 +224,7 @@ void CINIExt::LoadINIExt(uint8_t* pFile, size_t fileSize, const char* lpSection, } bool findTargetSection = false; bool firstLine = true; - bool keepComment = this == &CINI::CurrentDocument && ExtConfigs::SaveMap_KeepComments; + bool keepComment = this == &CINIExt::CurrentDocument && ExtConfigs::SaveMap_KeepComments; std::istringstream iss(content); FString line; @@ -150,6 +308,8 @@ void CINIExt::LoadINIExt(uint8_t* pFile, size_t fileSize, const char* lpSection, pCurrentSection = AddOrGetSection(CurrentSectionName); } + FnRecordSection(this, CurrentSectionName); + if (CMapDataExt::IsLoadingMapFile && ExtConfigs::SaveMap_PreserveINISorting) { auto it = std::find(CMapDataExt::MapIniSectionSorting.begin(), CMapDataExt::MapIniSectionSorting.end(), CurrentSectionName); if (it == CMapDataExt::MapIniSectionSorting.end()) { @@ -216,7 +376,7 @@ void CINIExt::LoadINIExt(uint8_t* pFile, size_t fileSize, const char* lpSection, if (!key.empty()) { size_t currentIndex = pCurrentSection->GetEntities().size(); - writeString(pCurrentSection, key, value); + writeString(pCurrentSection, CurrentSectionName, key, value); if (keepComment) { PendingComment.Trim(); @@ -278,7 +438,7 @@ void CINIExt::LoadINIExt(uint8_t* pFile, size_t fileSize, const char* lpSection, size_t currentIndex = pSectionA->GetEntities().size(); - writeString(pSectionA, key, value); + writeString(pSectionA, main, key, value); std::pair ins = std::make_pair((ppmfc::CString)key, (int)currentIndex); @@ -298,7 +458,7 @@ void CINIExt::LoadINIExt(uint8_t* pFile, size_t fileSize, const char* lpSection, const char* includeSection = IsLoadingFAini ? "Include" : (ExtConfigs::IncludeType ? "$Include" : "#include"); auto pIncludeSection = GetSection(includeSection); if (pIncludeSection || parentIncludeInis) { - if (this == &CINI::CurrentDocument) { + if (this == &CINIExt::CurrentDocument) { INIIncludes::MapINIWarn = true; } std::set includedInis; @@ -376,7 +536,7 @@ void CINIExt::LoadINIExt(uint8_t* pFile, size_t fileSize, const char* lpSection, int index = 0; for (const auto& [key, value] : targetIndicies) { - writeString(pTargetSection, key, value); + writeString(pTargetSection, sectionName, key, value); std::pair ins = std::make_pair((ppmfc::CString)key, index++); @@ -409,7 +569,7 @@ void CINIExt::LoadINIExt(uint8_t* pFile, size_t fileSize, const char* lpSection, } } - if (CMapDataExt::IsLoadingMapFile && this == &CINI::CurrentDocument) { + if (CMapDataExt::IsLoadingMapFile && this == &CINIExt::CurrentDocument) { if (loadAsUTF8) { Logger::Debug("CINIExt::LoadINIExt(): Load map file using UTF8 encoding.\n"); CMapDataExt::IsUTF8File = true; @@ -435,13 +595,14 @@ void CINIExt::LoadINIExt(uint8_t* pFile, size_t fileSize, const char* lpSection, void CINIExt::InheritSectionRecursive(const ppmfc::CString& sectionName, std::set& visited) { - auto writeString = [](INISection* pSection, const FString& key, const FString& value) + auto writeString = [this](INISection* pSection, const ppmfc::CString& sectionName, const FString& key, const FString& value) { std::pair ins = std::make_pair(key, value); std::pair ret; reinterpret_cast(&pSection->GetEntities())->insert(&ret, &ins); if (!ret.second) new(&ret.first->second) ppmfc::CString(value); + FnRecordKey(this, sectionName, key); }; if (visited.count(sectionName)) { @@ -481,7 +642,7 @@ void CINIExt::InheritSectionRecursive(const ppmfc::CString& sectionName, { size_t currentIndex = pSection->GetEntities().size(); - writeString(pSection, key, value); + writeString(pSection, sectionName, key, value); std::pair ins = std::make_pair((ppmfc::CString)key, (int)currentIndex); @@ -680,11 +841,13 @@ DEFINE_HOOK(4536B0, CINI_WriteToFile, 8) } std::ostringstream oss; - for (auto& [sectionName, pSection] : pThis->Dict) + for (const auto& [sectionName, pSection] : CINIOrderTracker::GetSections(pThis, ExtConfigs::SaveMap_PreserveINISorting)) { oss << "[" << sectionName << "]\n"; - for (const auto& pair : pSection.GetEntities()) - oss << pair.first << "=" << pair.second << "\n"; + for (const auto& [key, value] : CINIOrderTracker::GetEntries(pThis, sectionName, pSection, ExtConfigs::SaveMap_AdaptiveSorting)) + { + oss << key << "=" << value << "\n"; + } oss << "\n"; } diff --git a/FA2sp/Miscs/Hooks.INI.h b/FA2sp/Miscs/Hooks.INI.h index c4df0237..d819728b 100644 --- a/FA2sp/Miscs/Hooks.INI.h +++ b/FA2sp/Miscs/Hooks.INI.h @@ -18,11 +18,53 @@ using std::vector; class NOVTABLE CINIExt : public CINI { public: + using FnRecordKey_t = void(*)(CINI*, const ppmfc::CString&, const ppmfc::CString&); + using FnRemoveKey_t = void(*)(CINI*, const ppmfc::CString&, const ppmfc::CString&); + using FnRecordSection_t = void(*)(CINI*, const ppmfc::CString&); + using FnRemoveSection_t = void(*)(CINI*, const ppmfc::CString&); + + static FnRecordKey_t FnRecordKey; + static FnRemoveKey_t FnRemoveKey; + static FnRecordSection_t FnRecordSection; + static FnRemoveSection_t FnRemoveSection; + + static void SetAdaptiveSorting(bool enable); + static void SetKeepSectionSorting(bool enable); + static bool GetAdaptiveSorting(); + static bool GetKeepSectionSorting(); + + bool WriteString(ppmfc::CString pSection, ppmfc::CString pKey, ppmfc::CString pValue); + bool WriteString(INISection* pSection, ppmfc::CString pKey, ppmfc::CString pValue); + bool WriteBool(ppmfc::CString pSection, ppmfc::CString pKey, bool pValue); + bool DeleteKey(ppmfc::CString pSection, ppmfc::CString pKey); + bool DeleteKey(INISection* pSection, ppmfc::CString pKey); + bool DeleteSection(ppmfc::CString pSection); + INISection* AddSection(ppmfc::CString pSectionName); + INISection* AddOrGetSection(ppmfc::CString pSectionName); + void Release(); + int ClearAndLoad(const char* lpPath, int bTrimSpace = 0); + void LoadINIExt(uint8_t* pFile, size_t fileSize, const char* lpSection, bool bClear, bool bTrimSpace, bool bAllowInclude, std::vector>* parentIncludeInis = nullptr); void LoadFASetting(const FString& path); void InheritSectionRecursive(const ppmfc::CString& sectionName, std::set& visited); static bool IsLoadingFAini; + + // ========================================================================================= + // MOTIVATION & DESIGN RATIONALE: + // FinalAlert 2's host process stores the global active map document at 0x7ACC80. + // To support adaptive key/section order tracking (CINIOrderTracker) without altering the + // untouched FA2pp submodule or hooking deep red-black tree internals, CINIExt acts as a + // zero-overhead proxy wrapper over CINI sharing the identical memory layout (sizeof == 80). + // + // CRITICAL NOTICE FOR FUTURE DEVELOPERS: + // ALWAYS operate on this instance (`CINIExt::CurrentDocument`) instead of `CINI::CurrentDocument` + // throughout all FA2sp components. + // Directly invoking `CINI::CurrentDocument` will bypass CINIExt's tracking overrides + // (`WriteString`, `DeleteKey`, `AddSection`, etc.) and break Westwood-style numeric + // order preservation when saving maps! + // ========================================================================================= + static constexpr reference const CurrentDocument{}; }; struct CINIInfo diff --git a/FA2sp/Miscs/Hooks.UserScripts.cpp b/FA2sp/Miscs/Hooks.UserScripts.cpp index e578cb24..d594d971 100644 --- a/FA2sp/Miscs/Hooks.UserScripts.cpp +++ b/FA2sp/Miscs/Hooks.UserScripts.cpp @@ -208,7 +208,7 @@ DEFINE_HOOK(516974, CUserScripts_NewFunction_SwitchID, 8) break; } MultimapHelper mmh; - mmh.AddINI(&CINI::CurrentDocument); + mmh.AddINI(&CINIExt::CurrentDocument); if (UserScriptExt::ParamCount > 4) { mmh.Clear(); auto loadfrom = UserScriptExt::GetParam(Params, 4); @@ -237,7 +237,7 @@ DEFINE_HOOK(516974, CUserScripts_NewFunction_SwitchID, 8) break; } CNewComboUInputDlg dlg; - dlg.mmh.AddINI(&CINI::CurrentDocument); + dlg.mmh.AddINI(&CINIExt::CurrentDocument); if (UserScriptExt::ParamCount > 4) { dlg.mmh.Clear(); auto loadfrom = UserScriptExt::GetParam(Params, 4); @@ -282,7 +282,7 @@ DEFINE_HOOK(516974, CUserScripts_NewFunction_SwitchID, 8) break; } MultimapHelper mmh; - mmh.AddINI(&CINI::CurrentDocument); + mmh.AddINI(&CINIExt::CurrentDocument); if (UserScriptExt::ParamCount > 3) { mmh.Clear(); auto loadfrom = UserScriptExt::GetParam(Params, 3); @@ -588,7 +588,7 @@ DEFINE_HOOK(516974, CUserScripts_NewFunction_SwitchID, 8) } } else { - auto pSection = CINI::CurrentDocument->GetSection(section); + auto pSection = CINIExt::CurrentDocument->GetSection(section); if (auto pStr = pSection->GetKeyAt(index)) { UserScriptExt::Temps.push_back(*pStr); UserScriptExt::EditVaribale = true; @@ -626,7 +626,7 @@ DEFINE_HOOK(516974, CUserScripts_NewFunction_SwitchID, 8) } } else { - auto pSection = CINI::CurrentDocument->GetSection(section); + auto pSection = CINIExt::CurrentDocument->GetSection(section); if (auto pStr = pSection->GetValueAt(index)) { UserScriptExt::Temps.push_back(*pStr); UserScriptExt::EditVaribale = true; diff --git a/FA2sp/Miscs/Palettes.cpp b/FA2sp/Miscs/Palettes.cpp index 24532ac5..99da8fff 100644 --- a/FA2sp/Miscs/Palettes.cpp +++ b/FA2sp/Miscs/Palettes.cpp @@ -1,4 +1,5 @@ #include "Palettes.h" +#include "Hooks.INI.h" #include #include @@ -277,28 +278,28 @@ LightingStruct LightingStruct::GetCurrentLighting() switch (CFinalSunDlgExt::CurrentLighting) { case 31001: - CurrentLighting.Red = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "Red", 1.0)); - CurrentLighting.Green = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "Green", 1.0)); - CurrentLighting.Blue = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "Blue", 0.5)); - CurrentLighting.Ambient = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "Ambient", 1.0)); - CurrentLighting.Ground = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "Ground", 0.008)); - CurrentLighting.Level = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "Level", 0.087)); + CurrentLighting.Red = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "Red", 1.0)); + CurrentLighting.Green = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "Green", 1.0)); + CurrentLighting.Blue = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "Blue", 0.5)); + CurrentLighting.Ambient = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "Ambient", 1.0)); + CurrentLighting.Ground = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "Ground", 0.008)); + CurrentLighting.Level = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "Level", 0.087)); return CurrentLighting; case 31002: - CurrentLighting.Red = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "IonRed", 1.0)); - CurrentLighting.Green = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "IonGreen", 1.0)); - CurrentLighting.Blue = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "IonBlue", 0.5)); - CurrentLighting.Ambient = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "IonAmbient", 1.0)); - CurrentLighting.Ground = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "IonGround", 0.008)); - CurrentLighting.Level = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "IonLevel", 0.087)); + CurrentLighting.Red = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "IonRed", 1.0)); + CurrentLighting.Green = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "IonGreen", 1.0)); + CurrentLighting.Blue = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "IonBlue", 0.5)); + CurrentLighting.Ambient = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "IonAmbient", 1.0)); + CurrentLighting.Ground = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "IonGround", 0.008)); + CurrentLighting.Level = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "IonLevel", 0.087)); return CurrentLighting; case 31003: - CurrentLighting.Red = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "DominatorRed", 1.0)); - CurrentLighting.Green = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "DominatorGreen", 1.0)); - CurrentLighting.Blue = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "DominatorBlue", 0.5)); - CurrentLighting.Ambient = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "DominatorAmbient", 1.0)); - CurrentLighting.Ground = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "DominatorGround", 0.008)); - CurrentLighting.Level = static_cast(CINI::CurrentDocument->GetDouble("Lighting", "DominatorLevel", 0.087)); + CurrentLighting.Red = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "DominatorRed", 1.0)); + CurrentLighting.Green = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "DominatorGreen", 1.0)); + CurrentLighting.Blue = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "DominatorBlue", 0.5)); + CurrentLighting.Ambient = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "DominatorAmbient", 1.0)); + CurrentLighting.Ground = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "DominatorGround", 0.008)); + CurrentLighting.Level = static_cast(CINIExt::CurrentDocument->GetDouble("Lighting", "DominatorLevel", 0.087)); return CurrentLighting; } CurrentLighting = LightingStruct::NoLighting; @@ -441,7 +442,7 @@ void LightingSourceTint::CalculateMapLamps() CMapDataExt::LightingSources.clear(); CMapDataExt::LightingBuildingTypes.clear(); const float TOLERANCE = 0.001f; - if (auto pSection = CINI::CurrentDocument->GetSection("Structures")) + if (auto pSection = CINIExt::CurrentDocument->GetSection("Structures")) { for (const auto& [key,value] : pSection->GetEntities()) { diff --git a/FA2sp/Miscs/SaveMap.cpp b/FA2sp/Miscs/SaveMap.cpp index b90d3ea9..14d8d36b 100644 --- a/FA2sp/Miscs/SaveMap.cpp +++ b/FA2sp/Miscs/SaveMap.cpp @@ -33,6 +33,7 @@ #include "../Ext/CFinalSunApp/Body.h" #include "../Helpers/Helper.h" #include "../Helpers/TheaterHelpers.h" +#include "../Helpers/CINIOrderTracker.h" std::optional SaveMapExt::SaveTime; @@ -185,7 +186,7 @@ DEFINE_HOOK(42B2EA, CFinalSunDlg_SaveMap_SkipStringDTOR, C) bool SaveMapExt::SaveMapSilent(FString filepath, bool panic) { - auto ini = &CINI::CurrentDocument; + auto ini = &CINIExt::CurrentDocument; FString buffer; FString buffer2; @@ -366,7 +367,7 @@ bool SaveMapExt::SaveMap(CINI* pINI, CFinalSunDlg* pFinalSun, FString filepath, pINI->DeleteSection("Preview"); pINI->DeleteSection("PreviewPack"); - auto& map = CINI::CurrentDocument(); + auto& map = CINIExt::CurrentDocument(); auto thisTheater = map.GetString("Map", "Theater"); auto tiledata = CMapDataExt::TileData; @@ -421,7 +422,7 @@ bool SaveMapExt::SaveMap(CINI* pINI, CFinalSunDlg* pFinalSun, FString filepath, if (mapData.IsMultiOnly() && cell.Waypoint != -1) { - auto pSection = CINI::CurrentDocument->GetSection("Waypoints"); + auto pSection = CINIExt::CurrentDocument->GetSection("Waypoints"); auto& pWP = *pSection->GetKeyAt(cell.Waypoint); if (atoi(pWP) < 8) { @@ -709,7 +710,7 @@ bool SaveMapExt::SaveMap(CINI* pINI, CFinalSunDlg* pFinalSun, FString filepath, includeIni->LoadINIExt((uint8_t*)buffer.data(), buffer.length(), nullptr, true, true, true, ¤tIncludeInis); } - auto saveSection = [&oss, &pInclude, &includeIni](INISection* pSection, FString sectionName) + auto saveSection = [&oss, &pInclude, &includeIni, pINI](INISection* pSection, FString sectionName) { bool hasInclude = includeIni && includeIni->SectionExists(sectionName); bool wroteSection = false; @@ -760,58 +761,41 @@ bool SaveMapExt::SaveMap(CINI* pINI, CFinalSunDlg* pFinalSun, FString filepath, wroteSection = true; }; - if (hasInclude) + auto writeEntry = [&](const ppmfc::CString& key, const ppmfc::CString& value) { - auto pIncludeSection = includeIni->GetSection(sectionName); - auto& keys = pIncludeSection->GetEntities(); - - for (auto& pair : pSection->GetEntities()) + if (hasInclude) { - auto itr = keys.find(pair.first); - if (itr != keys.end() && itr->second == pair.second) - continue; - - writeSectionHeaderOnce(); - - auto fkIt = CMapDataExt::MapFrontlineComments[sectionName].find(pair.first); - if (fkIt != CMapDataExt::MapFrontlineComments[sectionName].end()) + if (auto pIncludeSection = includeIni->GetSection(sectionName)) { - writeCommentBlock(fkIt->second); + auto& keys = pIncludeSection->GetEntities(); + auto itr = keys.find(key); + if (itr != keys.end() && itr->second == value) + return; } + } - oss << pair.first << "=" << pair.second; + writeSectionHeaderOnce(); - auto ikIt = CMapDataExt::MapInlineComments[sectionName].find(pair.first); - if (ikIt != CMapDataExt::MapInlineComments[sectionName].end()) - { - oss << " ; " << ikIt->second; - } - - oss << "\n"; - } - } - else - { - for (const auto& pair : pSection->GetEntities()) + auto fkIt = CMapDataExt::MapFrontlineComments[sectionName].find(key); + if (fkIt != CMapDataExt::MapFrontlineComments[sectionName].end()) { - writeSectionHeaderOnce(); + writeCommentBlock(fkIt->second); + } - auto fkIt = CMapDataExt::MapFrontlineComments[sectionName].find(pair.first); - if (fkIt != CMapDataExt::MapFrontlineComments[sectionName].end()) - { - writeCommentBlock(fkIt->second); - } + oss << key << "=" << value; - oss << pair.first << "=" << pair.second; + auto ikIt = CMapDataExt::MapInlineComments[sectionName].find(key); + if (ikIt != CMapDataExt::MapInlineComments[sectionName].end()) + { + oss << " ; " << ikIt->second; + } - auto ikIt = CMapDataExt::MapInlineComments[sectionName].find(pair.first); - if (ikIt != CMapDataExt::MapInlineComments[sectionName].end()) - { - oss << " ; " << ikIt->second; - } + oss << "\n"; + }; - oss << "\n"; - } + for (const auto& [key, value] : CINIOrderTracker::GetEntries(pINI, sectionName, pSection, ExtConfigs::SaveMap_AdaptiveSorting)) + { + writeEntry(key, value); } if (wroteSection) @@ -851,48 +835,16 @@ bool SaveMapExt::SaveMap(CINI* pINI, CFinalSunDlg* pFinalSun, FString filepath, saveSection(pSection, "PreviewPack"); } - if (!SaveMapExt::IsAutoSaving && ExtConfigs::SaveMap_PreserveINISorting) - { - for (const auto& sectionName : CMapDataExt::MapIniSectionSorting) - { - if (!strcmp(sectionName, "Preview") - || !strcmp(sectionName, "PreviewPack") - || !strcmp(sectionName, "Header") - || !strcmp(sectionName, "Digest")) - continue; - - if (const auto pSection = pINI->GetSection(sectionName)) - { - saveSection(pSection, sectionName); - } - } - for (auto& section : pINI->Dict) - { - if (!strcmp(section.first, "Preview") - || !strcmp(section.first, "PreviewPack") - || !strcmp(section.first, "Header") - || !strcmp(section.first, "Digest")) - continue; - - auto it = std::find(CMapDataExt::MapIniSectionSorting.begin(), CMapDataExt::MapIniSectionSorting.end(), section.first); - if (it == CMapDataExt::MapIniSectionSorting.end()) - { - saveSection(§ion.second, section.first); - } - } - } - else + const bool preserveSections = !SaveMapExt::IsAutoSaving && ExtConfigs::SaveMap_PreserveINISorting; + for (const auto& [sectionName, pSection] : CINIOrderTracker::GetSections(pINI, preserveSections)) { - for (auto& section : pINI->Dict) - { - if (!strcmp(section.first, "Preview") - || !strcmp(section.first, "PreviewPack") - || !strcmp(section.first, "Header") - || !strcmp(section.first, "Digest")) - continue; + if (!strcmp(sectionName, "Preview") + || !strcmp(sectionName, "PreviewPack") + || !strcmp(sectionName, "Header") + || !strcmp(sectionName, "Digest")) + continue; - saveSection(§ion.second, section.first); - } + saveSection(pSection, sectionName); } // Generate the Digest @@ -1068,7 +1020,7 @@ void SaveMapExt::RemoveEarlySaves() }; std::map m; - auto mapName = CINI::CurrentDocument->GetString("Basic", "Name", "No Name"); + auto mapName = CINIExt::CurrentDocument->GetString("Basic", "Name", "No Name"); /* * Fix : Windows file name cannot begin with space and cannot have following characters: @@ -1145,7 +1097,7 @@ void CALLBACK SaveMapExt::SaveMapCallback(HWND hwnd, UINT message, UINT iTimerID SYSTEMTIME time; GetLocalTime(&time); - auto mapName = CINI::CurrentDocument->GetString("Basic", "Name", "No Name"); + auto mapName = CINIExt::CurrentDocument->GetString("Basic", "Name", "No Name"); /* * Fix : Windows file name cannot begin with space and cannot have following characters: diff --git a/Scripts/PackageRelease.ps1 b/Scripts/PackageRelease.ps1 new file mode 100644 index 00000000..9d1bbe83 --- /dev/null +++ b/Scripts/PackageRelease.ps1 @@ -0,0 +1,169 @@ +<# +.SYNOPSIS + Packages FA2sp release zips for target editions (YR, MO, RN) both locally and in CI. + +.PARAMETER Target + The target edition to package: 'YR', 'MO', 'RN', or 'All'. Default is 'All'. + +.PARAMETER Version + The release version tag (e.g. 'v1.6.4'). Default is 'v1.6.4'. + +.PARAMETER BinDir + Directory containing compiled FA2sp.dll and FA2sp.pdb. + If not specified, auto-detects from 'bin_output', 'Release', or 'Supplementary'. + +.PARAMETER OutputDir + Destination directory for output zip files. Default is 'dist'. + +.PARAMETER SupplementaryDir + Path to the Supplementary source directory. Default is project root's 'Supplementary'. + +.EXAMPLE + # Package all targets for local testing: + ./Scripts/PackageRelease.ps1 + +.EXAMPLE + # Package only RN edition with custom version: + ./Scripts/PackageRelease.ps1 -Target RN -Version v1.6.4 +#> + +[CmdletBinding()] +param( + [Parameter(Position = 0)] + [ValidateSet("YR", "MO", "RN", "All")] + [string]$Target = "All", + + [Parameter(Position = 1)] + [string]$Version = "v1.6.4", + + [Parameter(Position = 2)] + [string]$BinDir = "", + + [Parameter(Position = 3)] + [string]$OutputDir = "dist", + + [Parameter()] + [string]$SupplementaryDir = "" +) + +$ErrorActionPreference = "Stop" + +# Resolve project root relative to script location +$ProjectRoot = (Resolve-Path "$PSScriptRoot/..").Path + +# Resolve Supplementary directory +if ([string]::IsNullOrWhiteSpace($SupplementaryDir)) { + $SupplementaryDir = Join-Path $ProjectRoot "Supplementary" +} +if (-not (Test-Path $SupplementaryDir)) { + throw "Supplementary directory not found: $SupplementaryDir" +} +$SupplementaryDir = (Resolve-Path $SupplementaryDir).Path + +# Auto-detect BinDir if not provided +if ([string]::IsNullOrWhiteSpace($BinDir)) { + $candidates = @( + (Join-Path $ProjectRoot "bin_output"), + (Join-Path $ProjectRoot "Release"), + $SupplementaryDir + ) + foreach ($cand in $candidates) { + if (Test-Path (Join-Path $cand "FA2sp.dll")) { + $BinDir = $cand + break + } + } +} +if ([string]::IsNullOrWhiteSpace($BinDir) -or -not (Test-Path (Join-Path $BinDir "FA2sp.dll"))) { + Write-Warning "FA2sp.dll not found in candidate binary paths. Packages will be built using files present in Supplementary." +} else { + $BinDir = (Resolve-Path $BinDir).Path + Write-Host "Using binary directory: $BinDir" +} + +# Resolve Output directory +if (-not [System.IO.Path]::IsPathRooted($OutputDir)) { + $OutputDir = [System.IO.Path]::GetFullPath((Join-Path (Get-Location) $OutputDir)) +} +if (-not (Test-Path $OutputDir)) { + New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null +} + +$targetsToProcess = if ($Target -eq "All") { @("YR", "MO", "RN") } else { @($Target) } + +Write-Host "========================================================" +Write-Host " Packaging FA2sp ($Version) for targets: $($targetsToProcess -join ', ')" +Write-Host " Output Directory: $OutputDir" +Write-Host "========================================================" + +foreach ($t in $targetsToProcess) { + $pkgName = "FA2sp-$t-$Version.zip" + $zipPath = Join-Path $OutputDir $pkgName + $stageDir = Join-Path $OutputDir "staging_$t" + + Write-Host "`n--> Packaging target: $t" + + if (Test-Path $stageDir) { + Remove-Item -Path $stageDir -Recurse -Force + } + + # 1. Copy Supplementary base tree + Copy-Item -Path $SupplementaryDir -Destination $stageDir -Recurse -Force + + # 2. Apply target modifications + if ($t -eq "YR") { + Remove-Item -Path "$stageDir/MentalOmega" -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item -Path "$stageDir/RevengeNow" -Recurse -Force -ErrorAction SilentlyContinue + } + elseif ($t -eq "MO") { + if (Test-Path "$stageDir/MentalOmega") { + Get-ChildItem -Path "$stageDir/MentalOmega" | ForEach-Object { + Copy-Item -Path $_.FullName -Destination $stageDir -Recurse -Force + } + } + Remove-Item -Path "$stageDir/MentalOmega" -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item -Path "$stageDir/RevengeNow" -Recurse -Force -ErrorAction SilentlyContinue + } + elseif ($t -eq "RN") { + if (Test-Path "$stageDir/RevengeNow") { + Get-ChildItem -Path "$stageDir/RevengeNow" | ForEach-Object { + Copy-Item -Path $_.FullName -Destination $stageDir -Recurse -Force + } + } + Remove-Item -Path "$stageDir/MentalOmega" -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item -Path "$stageDir/RevengeNow" -Recurse -Force -ErrorAction SilentlyContinue + } + + # 3. Inject compiled binaries if available + if (-not [string]::IsNullOrWhiteSpace($BinDir)) { + $dllPath = Join-Path $BinDir "FA2sp.dll" + $pdbPath = Join-Path $BinDir "FA2sp.pdb" + if (Test-Path $dllPath) { + Copy-Item -Force $dllPath "$stageDir/FA2sp.dll" + } + if (Test-Path $pdbPath) { + Copy-Item -Force $pdbPath "$stageDir/FA2sp.pdb" + } + } + + # 4. Clean developer / test artifacts + Remove-Item -Path "$stageDir/FA2sp.UnitTest.dll" -Force -ErrorAction SilentlyContinue + Remove-Item -Path "$stageDir/RunUnitTest.bat" -Force -ErrorAction SilentlyContinue + Remove-Item -Path "$stageDir/*.log" -Force -ErrorAction SilentlyContinue + Remove-Item -Path "$stageDir/*.xml" -Force -ErrorAction SilentlyContinue + Remove-Item -Path "$stageDir/*.isolated" -Force -ErrorAction SilentlyContinue + + # 5. Compress + if (Test-Path $zipPath) { + Remove-Item -Force $zipPath + } + Compress-Archive -Path "$stageDir/*" -DestinationPath $zipPath -Force + + # Cleanup staging directory + Remove-Item -Path $stageDir -Recurse -Force + + $zipItem = Get-Item $zipPath + Write-Host "Created: $pkgName ($([math]::Round($zipItem.Length / 1MB, 2)) MB)" +} + +Write-Host "`nAll requested targets packaged successfully." diff --git a/Scripts/RunUnitTest.bat b/Scripts/RunUnitTest.bat new file mode 100644 index 00000000..351b491a --- /dev/null +++ b/Scripts/RunUnitTest.bat @@ -0,0 +1,39 @@ +@echo off +pushd "%~dp0..\Supplementary" + +:DO_ISOLATE +if exist "FA2sp.dll" ( + if exist "FA2sp.dll.isolated" del /f /q "FA2sp.dll.isolated" >nul 2>&1 + ren "FA2sp.dll" "FA2sp.dll.isolated" >nul 2>&1 + if exist "FA2sp.dll" ( + timeout /t 1 /nobreak >nul + goto :DO_ISOLATE + ) +) + +if exist "UnitTest.log" del /f /q "UnitTest.log" +if exist "UnitTest.xml" del /f /q "UnitTest.xml" + +echo ======================================================== +echo Launching FA2sp Unit Tests via Syringe in FinalAlert2 +echo ======================================================== +Syringe.exe "FinalAlert2YR.dat" %* +set TEST_EXIT_CODE=%ERRORLEVEL% + +:DO_RESTORE +if exist "FA2sp.dll.isolated" ( + ren "FA2sp.dll.isolated" "FA2sp.dll" >nul 2>&1 + if exist "FA2sp.dll.isolated" ( + timeout /t 1 /nobreak >nul + goto :DO_RESTORE + ) +) + +if exist "UnitTest.log" ( + echo. + type UnitTest.log +) +echo. +echo Tests exited with code: %TEST_EXIT_CODE% +popd +exit /b %TEST_EXIT_CODE% diff --git a/Supplementary/FAData.ini b/Supplementary/FAData.ini index c5b9f317..9f5facda 100644 --- a/Supplementary/FAData.ini +++ b/Supplementary/FAData.ini @@ -252,6 +252,10 @@ SaveMap.KeepComments=false ; BOOLEAN ; 保存地图时维持已有的 ini 小节顺序 SaveMap.PreserveINISorting=false +; BOOLEAN ; Adaptively sort INI keys when saving maps (natural numeric order for index sections, preserve order for properties) +; BOOLEAN ; 保存地图时维持键值自适应顺序(数字索引小节按数字自然升序,常规属性小节维持原序) +SaveMap.AdaptiveSorting=false + ; Number of recent maps to remember; range 4-9 ; 记录多少个最近打开地图, 取值范围为4-9 RecentFileLimit=9 diff --git a/Supplementary/FALanguage_Chinese.ini b/Supplementary/FALanguage_Chinese.ini index 51d8f2a7..9a7a8200 100644 --- a/Supplementary/FALanguage_Chinese.ini +++ b/Supplementary/FALanguage_Chinese.ini @@ -1865,6 +1865,7 @@ SwitchConnectedTypeIsWaterCliff=(水上) Options.ExtFacings=启用32向面向 Options.ExtFacings.Drag=启用32向拖拽 Options.SaveMap.PreserveINISorting=保存时维持已有的ini小节顺序 +Options.SaveMap.AdaptiveSorting=保存地图时维持键值自适应顺序 Options.Title=偏好设置 Options.UndoRedo.RecordObjects=记录游戏对象修改历史 Options.ExtMixLoader=启用新的Mix加载方式 diff --git a/Supplementary/FALanguage_English.ini b/Supplementary/FALanguage_English.ini index 14c7e69c..439afe80 100644 --- a/Supplementary/FALanguage_English.ini +++ b/Supplementary/FALanguage_English.ini @@ -1181,6 +1181,7 @@ SwitchConnectedTypeIsWaterCliff=(Water) Options.ExtFacings=Enable 32 facings support Options.ExtFacings.Drag=Allow drag units into 32 facings Options.SaveMap.PreserveINISorting=Preserve existing INI section sorting when saving +Options.SaveMap.AdaptiveSorting=Adaptive INI key sorting when saving Options.Title=Preferences Options.UndoRedo.RecordObjects=Record editing history of objects Options.ExtMixLoader=Enable new mix loader diff --git a/Supplementary/RevengeNow/FAData.ini b/Supplementary/RevengeNow/FAData.ini new file mode 100644 index 00000000..261f7500 --- /dev/null +++ b/Supplementary/RevengeNow/FAData.ini @@ -0,0 +1,3255 @@ +; FAData.ini - Revenge Now (RN) Configured for FA2sp +; Includes RN custom sides, game modes, palettes, trigger actions, events, scripts, and templates + +[Debug] +;DisplayAllOverlay=true ; Doesn't cripple the overlay list in any way +EnableTrackLogic=true ; Enables Track Logic +;IgnoreSHPImageHeadUnused=true ; Use this *carefully* to make SHP graphics of some mods work that incorrectly have the shadow flag set +AllowTunnels=true ; Enables tunnel editing + +; Recursive inclusion is not supported + +[Include] +0=FAData_RandomPlacement.ini +1=FAData_ObjectBrowser.ini +2=FAData_Customized.ini + +[ExtConfigs] +; BOOLEAN ; Enables the redesigned ObjectBrowserView +; BOOLEAN ; 启用重构后的 ObjectBrowserView +BrowserRedraw=true + +; 0/1 ; Controls how FA2sp guesses a unit house: 0 (default) uses Prerequisite, 1 uses the first Owner +; 0/1 ; 决定 FA2sp 如何猜测单位的阵营, 0(默认)为Prerequisite, 1为第一个Owner +ObjectBrowser.GuessMode=0 + +; INTERGER ; Maximum number of sides to guess; higher values return Other (-1). Default 15 +; INTERGER ; 决定猜测阵营的最大值. 如果超过该值则返回为其他(-1), 默认为 15 +ObjectBrowser.GuessMax=15 + +; BOOLEAN ; Remove empty entries from house categories +; BOOLEAN ; 阵营分类会清除空项目 +ObjectBrowser.CleanUp=true + +; BOOLEAN ; Hide houses that cannot be used +; BOOLEAN ; 决定 FA2sp 是否屏蔽掉不能使用的所属方 +ObjectBrowser.SafeHouses=true + +; BOOLEAN ; Additionally group structures by foundation size +; BOOLEAN ; 是否会额外按照建筑大小进行分类 +ObjectBrowser.Foundation=false + +; BOOLEAN ; Use random ore in placing +; BOOLEAN ; 是否在摆放矿石时随机图像 +ObjectBrowser.Ore.RandomPlacement=false + +; BOOLEAN ; Display extra two types of ores in "Ore and Gems" +; BOOLEAN ; 是否在“金矿和水晶”中支持额外矿石类型的摆放 +ObjectBrowser.Ore.ExtraSupport=false + +; BOOLEAN ; Load EditorCategory in rules into object viewer +; BOOLEAN ; 根据单位rules中的EditorCategory在对象查看器中分类 +GridObjectViewer.LoadEditorCategory=true + +; BOOLEAN ; Load ForceSides in rules into object viewer +; BOOLEAN ; 根据单位的ForceSides在对象查看器中分类 +GridObjectViewer.LoadForceSides=true + +; BOOLEAN ; Load smudges/terrains/overlays into object viewer based on their ObjectBrowser group +; BOOLEAN ; 根据污染/地形对象/覆盖物在物品浏览器中的分组在对象查看器中分类 +GridObjectViewer.LoadObjectBrowserCategory=true + +; BOOLEAN ; Load the special civilian translation file (fa2civilian.csf) +; BOOLEAN ; 是否加载对平民单位/建筑的特殊翻译 (fa2civilian.csf) +LoadCivilianStringtable=true + +; BOOLEAN ; Reload supporting files from the map folder without reconfiguring FinalAlert.ini; default false +; BOOLEAN ; 是否从地图文件夹重新读取所有文件, 而不需要重新设置 FinalAlert.ini, 默认为 false +ReloadGameFromMapFolder=true + +; BOOLEAN ; Reload game INIs when opening map file; default false +; BOOLEAN ; 是否在打开地图时重新加载游戏ini, 默认为 false +ReloadIniWhenOpeningMap=false + +; BOOLEAN ; Load game resources from map folder when directly open map; default false +; BOOLEAN ; 双击打开地图文件时,从地图目录加载资源文件, 默认为 false +LoadGameFromMapFolder.OnInit=false + +; BOOLEAN ; Read #include sections to load additional INI files (not recommended) +; BOOLEAN ; 读取 #include 部分以获取其他 ini(不推荐) +AllowIncludes=true + +; BOOLEAN ; Load inherited INI sections (not recommended) +; BOOLEAN ; 读取继承的INI小节(不推荐) +AllowInherits=false + +; BOOLEAN ; section name of include (yes = $Include(Phobos), no = #include(Ares)) +; BOOLEAN ; include小节的名称 (yes = $Include(Phobos), no = #include(Ares)) +IncludeType=false + +; BOOLEAN ; section name of inherit (yes = $Inherit(Phobos), no = []:[](Ares)) +; BOOLEAN ; inherit小节的名称 (yes = $Inherit(Phobos), no = []:[](Ares)) +InheritType=false + +; BOOLEAN ; Allow the += syntax (not recommended) +; BOOLEAN ; 读取 += (不推荐) +AllowPlusEqual=true + +; BOOLEAN ; Read Image= from infantry, vehicle, and aircraft art(md) +; BOOLEAN ; 读取步兵, 车辆, 飞行器 art(md) 中的Image= +ArtImageSwap=false + +; BOOLEAN ; When enabled, selecting a CSF text argument opens the CSF browser +; BOOLEAN ; 启用此功能后, 当触发参数为CSF文本时, 点击下拉菜单会弹出CSF浏览器 +TutorialTexts.Viewer=true + +; BOOLEAN ; Ask for confirmation when deleting sub-entries of triggers/scripts, etc. +; BOOLEAN ; 删除删除触发/脚本等的子条目时是否需要确认 +ConfirmDelete.SubEntries=false + +; BOOLEAN ; Clone triggers/teams/taskforces/scripts/AI triggers with an incrementing suffix instead of "Clone" +; BOOLEAN ; 启用此功能后, 复制触发、小队、特遣、脚本、AI触发时,都会在名称最后新增递增编号,而不是Clone +CloneWithOrderedID=true + +; BOOLEAN ; When creating new triggers/teams/taskforces/scripts/AI triggers, always use an incremented index (max + 1) instead of searching for the first available index +; BOOLEAN ; 启用此功能后, 创建触发、小队、特遣、脚本、AI触发时,会在当前最大ID后递增,而不是寻找第一个可用ID +UseSequentialIndexing=false + +; Independent suffixes (such as -TR for trigger) will be assigned when creating triggers, teams, taskforces, scripts, and AI triggers +; 启用此功能后, 创建触发、小队、特遣、脚本、AI触发时,会分配独立的后缀(如触发后缀为-TR) +UseSeparateIndexing=false + +; BOOLEAN ; Automatically adjust dropdown widths +; BOOLEAN ; 启用后, FA2 将自动调整下拉菜单宽度 +AdjustDropdownWidth=true + +; INTERGER ; Character width factor; default 8 +; INTERGER ; 单个字符占用空间, 默认为8 +AdjustDropdownWidth.Factor=6 + +; INTERGER ; Maximum dropdown width; default 360 +; INTERGER ; 下拉菜单的最大长度, 默认为360 +AdjustDropdownWidth.Max=420 + +; BOOLEAN ; Display the disabled status of the trigger in the dropdown menu of the trigger editor; default true +; BOOLEAN ; 在触发编辑器下拉菜单显示触发的禁止状态, 默认为 true +DisplayTriggerEnableInfo=true + +; BOOLEAN ; Check whether attached trigger is itself, default true +; BOOLEAN ; 检查下级触发是否是自身, 默认为 true +AttachedTriggerIsSelfCheck=true + +; BOOLEAN ; Sort by name instead of ID in trigger/team/taskforce/script/terrain generator editors +; BOOLEAN ; 在触发编辑器、小队、特遣部队、动作脚本、地形生成器中, 是否依据名称而不是ID进行排序 +SortByLabelName=true + +; BOOLEAN ; Override sorting for teams to use names instead of IDs +; BOOLEAN ; 在排序小队时, 是否依据名称而不是ID进行排序, 覆盖 SortByLabelName 设置 +SortByLabelName.Team=true + +; BOOLEAN ; Override sorting for scripts to use names instead of IDs +; BOOLEAN ; 在排序脚本时, 是否依据名称而不是ID进行排序, 覆盖 SortByLabelName 设置 +SortByLabelName.Script=true + +; BOOLEAN ; Override sorting for triggers to use names instead of IDs +; BOOLEAN ; 在排序触发时, 是否依据名称而不是ID进行排序, 覆盖 SortByLabelName 设置 +SortByLabelName.Trigger=true + +; BOOLEAN ; Override sorting for AI triggers to use names instead of IDs +; BOOLEAN ; 在排序AI触发时, 是否依据名称而不是ID进行排序, 覆盖 SortByLabelName 设置 +SortByLabelName.AITrigger=true + +; BOOLEAN ; Override sorting for taskforces to use names instead of IDs +; BOOLEAN ; 在排序特遣部队时, 是否依据名称而不是ID进行排序, 覆盖 SortByLabelName 设置 +SortByLabelName.Taskforce=true + +; BOOLEAN ; Override sorting for tags to use names instead of IDs +; BOOLEAN ; 在排序标签时, 是否依据名称而不是ID进行排序, 覆盖 SortByLabelName 设置 +SortByLabelName.Tag=true + +; BOOLEAN ; Disable automatic search for dropdown menus; search only when pressing Enter; default false +; BOOLEAN ; 是否禁止下拉菜单的自动搜索,若禁止,需要按Enter执行搜索, 默认为 false +SearchCombobox.Disabled=false + +; BOOLEAN ; Allow automatic search for non-parameter (trigger actiontype, eventtype, etc) dropdown menus; default true +; BOOLEAN ; 是否允许非参数项(触发事件、行为种类等)下拉菜单的自动搜索, 默认为 true +SearchCombobox.AllowNonParams=true + +; BOOLEAN ; Enable auto-search when entering waypoints; default false +; BOOLEAN ; 是否在下拉菜单中输入路径点时自动弹出搜索, 默认为 false +SearchCombobox.Waypoint=false + +; BOOLEAN ; Disable Lua console safety check; default false +; BOOLEAN ; 是否禁用Lua脚本安全检查, 默认为 false +DisableLuaConsoleSafetyCheck=false + +; BOOLEAN ; Display real HP in techno dialogs; default true +; BOOLEAN ; 是否在单位属性窗口内显示真实血量, 默认为 true +DisplayRealHPinTechnoDlg=true + +; COLORREF(R,G,B) ; Outline color when copying; default 255,0,0. Range 0-255 +; COLORREF(R,G,B) ; 复制时描边的颜色, 默认为 255,0,0. 取值范围为0-255 +CopySelectionBound.Color=255,0,0 + +; COLORREF(R,G,B) ; Outline color for the hovered cell; default 60,160,60. Range 0-255 +; COLORREF(R,G,B) ; 鼠标所指单元格的描边的颜色, 默认为 60,160,60. 取值范围为0-255 +CursorSelectionBound.Color=30,30,255 + +; COLORREF(R,G,B) ; Dashed height indicator color; default 60,60,60. Range 0-255 +; COLORREF(R,G,B) ; 指示高度的虚线的颜色, 默认为 60,60,60. 取值范围为0-255 +CursorSelectionBound.HeightIndicatorColor=60,60,160 + +; BOOLEAN ; Adjust outline color by height level; default false +; BOOLEAN ; 是否根据当前高度显示不同的描边颜色, 默认为 false +CursorSelectionBound.AutoHeightColor=true + +; BOOLEAN ; Auto-detect encoding when opening INI and map files +; BOOLEAN ; 是否在打开ini和地图文件时自动推断编码, 默认为 true +UTF8Support.InferEncoding=true + +; BOOLEAN ; Always save maps using UTF-8 encoding +; BOOLEAN ; 是否总是以UTF8编码保存地图, 默认为 false +UTF8Support.AlwaysSaveAsUTF8=true + +; BOOLEAN ; Enable automatic map saves +; BOOLEAN ; 是否自动保存地图 +SaveMap.AutoSave=true + +; INTEGER ; Interval between autosaves in seconds; must be >= 30, default 300 +; INTEGER ; 应大于或等于30, 默认值为300, 决定两次自动保存间隔多少秒钟 +SaveMap.AutoSave.Interval=360 + +; INTEGER ; Number of backups to keep; -1 disables automatic cleanup. Default 10 +; INTEGER ; 保存几个副本, 设置为-1将禁用自动删除备份功能, 默认为10 +SaveMap.AutoSave.MaxCount=10 + +; BOOLEAN ; Only save maps in .map format +; BOOLEAN ; 只以.map格式保存地图 +SaveMap.OnlySaveMAP=false + +; Generate preview option when saving multiplayer maps. 0 create new, 1 reuse existing, 2 solid black. Default 0 +; 保存多人游戏地图时生成预览的默认选项.0 生产新的预览, 1 使用已存在的, 2 使用纯黑预览. 默认为0 +SaveMap.DefaultPreviewOptionMP=0 + +; Same as above for singleplayer missions +; 同上, 单人战役 +SaveMap.DefaultPreviewOptionSP=2 + +; BOOLEAN ; Produce higher fidelity RA2-style previews when generating new previews (multiplayer only) +; BOOLEAN ; 选择生成新的预览时, 生成更精细的, 原版风格的预览图 (仅支持多人游戏) +SaveMap.BetterMapPreview=true + +; BOOLEAN ; Add an encoding warning comment at the top when saving maps +; BOOLEAN ; 保存地图时在开头添加编码警告注释 +SaveMap.FileEncodingComment=true + +; BOOLEAN ; Keep existing comments when saving maps, ini editor can also save and display comments +; BOOLEAN ; 保存地图时保留注释, ini编辑器也可以保存并显示注释 +SaveMap.KeepComments=false + +; BOOLEAN ; Preserve existing INI section order when saving maps +; BOOLEAN ; 保存地图时维持已有的 ini 小节顺序 +SaveMap.PreserveINISorting=true + +; BOOLEAN ; Adaptively sort INI keys when saving maps (natural numeric order for index sections, preserve order for properties) +; BOOLEAN ; 保存地图时维持键值自适应顺序(数字索引小节按数字自然升序,常规属性小节维持原序) +SaveMap.AdaptiveSorting=true + +; Number of recent maps to remember; range 4-9 +; 记录多少个最近打开地图, 取值范围为4-9 +RecentFileLimit=9 + +; BOOLEAN ; Enable unlimited waypoints; default false (requires Phobos) +; BOOLEAN ; 启用无限路径点, 默认为 false (需要Phobos) +ExtWaypoints=false + +; BOOLEAN ; Enable unlimited variables; default false (requires Phobos) +; BOOLEAN ; 启用无限变量, 默认为 false (需要Phobos) +ExtVariables=false + +; BOOLEAN ; Enable unlimited overlays; switches NewINIFormat to 5 when overlay index exceeds 255. Default false (requires Phobos) +; BOOLEAN ; 启用无限覆盖物, 当地图中覆盖物索引超过255时, 会自动将 NewINIFormat 切换为 5, 默认为 false (需要Phobos) +ExtOverlays=false + +; BOOLEAN ; Enable 32 facing directions; increases memory usage. Default false +; BOOLEAN ; 启用32向面向, 会导致内存占用增大, 默认为 false +ExtFacings=false + +; BOOLEAN ; Allow drag placement for 32 facings by holding Ctrl; otherwise 8 facings. Default false +; BOOLEAN ; 启用32向拖拽, 如果该值为 true, 则按住 ctrl 对单位进行拖拽可选32向, 否则为8向, 默认为 false +ExtFacings.Drag=false + +; BOOLEAN ; Show a live preview while dragging facings. Default true +; BOOLEAN ; 启用拖拽面向的实时预览, 默认为 true +ExtFacings.DragPreview=true + +; BOOLEAN ; Allow scroll units into 32 facings when placing; otherwise 8 facings. Default false +; BOOLEAN ; 启用32向的摆放时滚轮调整朝向, 否则为8向, 默认为 false +ExtFacings.Scroll=false + +; BOOLEAN ; Allow vehicles and aircraft to tilt on slopes. Default false +; BOOLEAN ; 车辆和飞行器在斜坡上显示倾斜图像, 默认为 false +ExtTilts=false + +; BOOLEAN ; Enable the new MIX loader to reduce memory leaks. Default true +; BOOLEAN ; 启用新的 Mix 加载器, 可以减少内存泄漏, 默认为 true +ExtMixLoader=true + +; BOOLEAN ; Load all in-map objects during map initialization to reduce lag during use. Default false +; BOOLEAN ; 在地图初始化时加载所有地图内对象, 可以减少使用中卡顿, 默认为 false +LoadObjectsOnInit=false + +; INTEGER ; Number of undo/redo steps; default 64 +; INTEGER ; 可以进行多少次撤销/重做, 默认为64. +UndoRedoLimit=250 + +; BOOLEAN ; Holding Shift while placing terrain counts as a single history entry. Default true +; BOOLEAN ; 如果该值为 true, 则按住shift放置地形时的变动仅记录一次历史记录, 撤销会全部撤销, 默认为 true +UndoRedo.ShiftPlaceTile=true + +; BOOLEAN ; Holding placement when drawing overlays counts as a single history entry. Default true +; BOOLEAN ; 如果该值为 true, 则长按放置覆盖图时的变动仅记录一次历史记录, 撤销会全部撤销, 默认为 true +UndoRedo.HoldPlaceOverlay=true + +; BOOLEAN ; Track history for game object edits. Default true +; BOOLEAN ; 如果该值为 true, 记录游戏对象的修改历史, 默认为 true +UndoRedo.RecordObjects=true + +; BOOLEAN ; Draw a rectangle background for waypoints. Default false +; BOOLEAN ; 为路径点绘制矩形背景, 默认为 false +Waypoint.Background=true + +; COLORREF(R,G,B) ; Waypoint background color. Range 0-255 +; COLORREF(R,G,B) ; 路径点背景的颜色. 取值范围为0-255 +Waypoint.Background.Color=0,0,255 + +; COLORREF(R,G,B) ; Waypoint number color; default 0,0,255. Range 0-255 +; COLORREF(R,G,B) ; 路径点数字的颜色, 默认为 0,0,255. 取值范围为0-255 +Waypoint.Color=255,255,0 + +; X,Y offset for waypoint text, default 0,0 +; 路径点文本的X,Y坐标偏移值, 默认为 0,0. +Waypoint.Text.ExtraOffset=0,0 + +; BOOLEAN ; Skip game directory validation (including ra2.mix). Default false +; BOOLEAN ; 禁用游戏目录检测, 启用后将不会检查游戏目录是否有效 (包括ra2.mix), 默认为 false +DisableDirectoryCheck=false + +; COLORREF(R,G,B) ; Base node index color. Range 0-255 +; COLORREF(R,G,B) ; 基地节点索引号颜色. 取值范围为0-255 +BaseNodeIndex.Color=255,255,0 + +; BOOLEAN ; Draw a rectangle background for index numbers. Default false +; BOOLEAN ; 为索引号绘制矩形背景, 默认为 false +BaseNodeIndex.Background=true + +; COLORREF(R,G,B) ; Background color of basenode index. Range 0-255 +; COLORREF(R,G,B) ; 基地节点索引的背景颜色. 取值范围为0-255 +BaseNodeIndex.Background.Color=60,60,60 + +; BOOLEAN ; Move the tile browser from bottom to right pane. Default false +; BOOLEAN ; 将地形浏览器从底部移至右侧, 默认为 false +VerticalLayout=true + +; BOOLEAN ; Display tile browser as a standalone window. Default false +; BOOLEAN ; 将地形浏览器作为独立窗口显示, 默认为 false +TileSetBrowserFloating=false + +; BOOLEAN ; Display tile browser tabs in multiple rows to avoid horizontal scrolling. Default false +; BOOLEAN ; 地形浏览器标签页多行显示, 避免横向滚动, 默认为 false +TileSetBrowserMultiLineTabs=false + +; BOOLEAN ; Display object browser as a standalone window. Default false +; BOOLEAN ; 将物品浏览器作为独立窗口显示, 默认为 false +ViewObjectsFloating=false + +; BOOLEAN ; Enable dark theme. Default false +; BOOLEAN ; 启用暗色模式, 默认为 false +EnableDarkMode=false + +; BOOLEAN ; Dim the map view when dark theme is active. Default false +; BOOLEAN ; 启用暗色模式时, 将地图视图整体变暗, 默认为 false +EnableDarkMode.DimMap=false + +; BOOLEAN ; Follow the system dark theme setting; checked only at startup. Default true +; BOOLEAN ; FA2 将跟随系统暗色模式, 仅会在启动时检测, 不能实时切换, 默认为 true +AutoDarkMode=true + +; DOUBLE ; Scheduled dark theme start in 24-hour time; decimals allowed. Default -1 +; DOUBLE ; 指定进入暗色模式的时间, 24小时制, 可以填小数, 默认为 -1 +AutoDarkMode.StartTime=-1 + +; DOUBLE ; Scheduled dark theme end in 24-hour time; decimals allowed. Default -1 +; DOUBLE ; 指定离开暗色模式的时间, 24小时制, 可以填小数, 默认为 -1 +AutoDarkMode.EndTime=-1 + +; BOOLEAN ; Enable high-DPI awareness, Default true +; BOOLEAN ; 启用高DPI感知, 默认为 true +HiDPIAwareness=true + +; BOOLEAN ; Match IsoView default zoom to system scaling when high-DPI awareness is enabled, Default false +; BOOLEAN ; 启用高 DPI 感知时,使地图视图默认匹配系统缩放比例, 默认为 true +HiDPIAwareness.ScaleIsoView=true + +; COLORREF(R,G,B) ; Highlight color for multi-selection cells +; COLORREF(R,G,B) ; 多选单元格功能中选中块的颜色 +MultiSelectionColor=255,47,255 + +; FLOAT ; Opacity of the overlay color on multi-selected cells +; FLOAT ; 多选单元格附加颜色的透明度 +MultiSelect.Opacity=0.33 + +; BOOLEAN ; Show all terrain objects in the random tree dialog. Default false +; BOOLEAN ; 在绘制随机树木对话框中显示所有地形对象, 默认为 false +RandomTerrainObjects=false + +; BOOLEAN ; Allocate DirectDraw surfaces in video memory. Default true +; BOOLEAN ; 决定 FA2 是否在显存中分配 DirectDraw surface, 默认为 true +DDrawInVideoMem=true + +; BOOLEAN ; Use emulation mode for DirectDrawCreate. Default false +; BOOLEAN ; 决定 FA2 是否在 DirectDrawCreate 中使用 emulation mode, 默认为 false +DDrawEmulation=false + +; BOOLEAN ; Support displaying IsoView on non-primary screens. Slight impact on performance. Default true +; BOOLEAN ; 是否支持在副屏上显示绘图界面, 对性能有轻微影响, 默认为 true +SecondScreenSupport=true + +; DOUBLE ; Maximum render zoom (only in DDraw mode), Default 1.5 +; DOUBLE ; 绘图界面的最大缩放倍率 (仅DDraw模式有效), 默认为 1.5 +DDrawScalingMaximum=1.5 + +; BOOLEAN ; Use bilinear filtering when scaling the render surface. Improves quality at a performance cost. Default true +; BOOLEAN ; 使用双线性插值缩放绘图界面, 缩放质量更高, 但会影响性能, 默认为 true +DDrawScalingBilinear=true + +; BOOLEAN ; Only use bilinear filtering when zooming out. Default false +; BOOLEAN ; 仅在缩小界面时使用双线性插值, 默认为 false +DDrawScalingBilinear.OnlyShrink=false + +; BOOLEAN ; Draw map using graphics card, dramatically improve performance. Default true +; BOOLEAN ; 使用显卡加速绘图, 可以大幅提升性能, 默认为 true +DirectXRendering=true + +; BOOLEAN ; Select graphics rendering API, true is OpenGL 3.3, false is Direct3D 11, default true +; BOOLEAN ; 选择显卡渲染API, true 为 OpenGL 3.3, false 为 Direct3D 11, 默认为 true +OpenGLRendering=true + +; BOOLEAN ; Precisely calculate the depth relationship of game objects (slower). Default true +; BOOLEAN ; 精确计算游戏对象间的深度关系 (轻微影响性能), 默认为 true +PreciseDepthCalculation=true + +; BOOLEAN ; Enable MCP server automatically when the Lua script console starts, allowing connection to LLMs. Default false +; BOOLEAN ; Lua 脚本终端开启时自动启用 MCP 服务器, 可以连接至大语言模型. 默认为 false +MCP.Enable=false + +; INTEGER ; Port of MCP server. Default 19198 +; INTEGER ; MCP 服务器的端口, 默认为 19198 +MCP.Port=19198 + +; How to integrate (change 19198 to your custom MCP.Port): +; 集成方式 (将19198替换为你自定义的MCP.Port): +;{ +; "mcpServers": { +; "FA2spHE (RA2 map editor)": { +; "url": "http://localhost:19198" +; } +; } +;} + +; BOOLEAN ; Do not translate houses to their UIName. Default false +; BOOLEAN ; 决定 FA2 是否*不*将所属方翻译为其 UIName, 默认为 false +NoHouseNameTranslation=false + +; BOOLEAN ; After translation, also display the English house name. Requires NoHouseNameTranslation to be false. Default false +; BOOLEAN ; 决定 FA2 是否在所属方翻译后显示英文原名, 需要关闭NoHouseNameTranslation, 默认为 false +BetterHouseNameTranslation=true + +; BOOLEAN ; Show warnings instead of errors for new map validation checks. Default false +; BOOLEAN ; 如果该值为 true, 则检查地图的新功能不会显示错误, 而是显示警告, 默认为 false +ExtendedValidationNoError=false + +; BOOLEAN ; Use a 512 value when checking map INI lengths (128 for Vanilla). Default false +; BOOLEAN ; 如果该值为 true, 则检查地图INI长度时会取512, 而不是原版的128, 默认为 false +ExtendedValidationAres=false + +; BOOLEAN ; Do not render structures with HP = 0 and LeaveRubble=no. Default false +; BOOLEAN ; 如果该值为 true, 则不会渲染 HP = 0 且带有 LeaveRubble=no 的建筑, 默认为 false +HideNoRubbleBuilding=false + +; BOOLEAN ; Enforce strict NewTheater logic; structures without art are hidden. Default false +; BOOLEAN ; 如果该值为 true, 则启用严格 NewTheater 逻辑, 未找到对应图像的建筑将不会显示, 默认为 false +UseStrictNewTheater=true + +; INTEGER ; Choose logic for reading the second art letter: 0 = Vanilla (G,N,C,Y), 1 = Ares (any letter). Default 1 +; INTEGER ; 设置读取建筑图像第二位字母的逻辑, 0 = 尤复逻辑(G, N, C, Y开头), 1 = Ares逻辑(任意字母开头), 默认为 1 +NewTheaterType=1 + +; BOOLEAN ; Prevent placing overlapping structures; ignores StructureOverlappingCheckIgnores entries. Default false +; BOOLEAN ; 如果该值为 true, 则不能摆放重叠建筑, 忽略StructureOverlappingCheckIgnores中的建筑, 默认为 false +PlaceStructure.OverlappingCheck=true + +; BOOLEAN ; Auto-fill the Upgrade count. Default false +; BOOLEAN ; 如果该值为 true, 则自动填写Upgrade数量, 默认为 false +PlaceStructure.AutoUpgrade=true + +; BOOLEAN ; Apply full health when an Upgrade is present to match in-game behavior. Default false +; BOOLEAN ; 如果该值为 true, 建筑存在Upgrade时, 自动将血量设置为满血, 符合游戏内表现, 默认为 false +PlaceStructure.UpgradeStrength=true + +; BOOLEAN ; Upgrades can be directly placed onto buildings. Default false +; BOOLEAN ; 如果该值为 true, 可以直接向建筑放置加载物, 默认为 false +PlaceStructure.PlaceUpgrade=true + +; BOOLEAN ; When "Default Lighting" is selected, adjust unit RGB values. Default false +; BOOLEAN ; 选择光照沙盒的"默认光照"时, 是否调整建筑, 步兵, 载具, 飞行器的RGB值. 默认为 false +LightingPreview.MultUnitColor=false + +; BOOLEAN ; Tint the tileset browser using the lighting sandbox settings. Default false +; BOOLEAN ; 是否按照光照沙盒的光照重新渲染地形浏览器图像. 默认为 false +LightingPreview.TintTileSetBrowserView=true + +; BOOLEAN ; Make the arrangement of images in tileset browser more compact. 默认为 true +; BOOLEAN ; 紧凑排列地形浏览器的图像. 默认为 true +TileSetBrowserViewCompactArrange=true + +; BOOLEAN ; Display the localsize border (blue outline) on the minimap. Default false +; BOOLEAN ; 在小地图中显示可视地图边界(蓝色边框). 默认为 false +ShowMapBoundInMiniMap=false + +; BOOLEAN ; Place the first infantry in subcell 4 (bottom) like the game. Default false +; BOOLEAN ; 如果该值为 true, 单元格中第一个放置的步兵位于最下方,即4号subcell, 默认为 false +InfantrySubCell.GameDefault=true + +; BOOLEAN ; Select infantry by cursor position within the cell instead of cycling all. Default false +; BOOLEAN ; 如果该值为 true, 在编辑步兵时会根据鼠标在单元格中的相对位置选择步兵, 而不是遍历全部, 默认为 false +InfantrySubCell.Edit=true + +; BOOLEAN ; When only one infantry in a cell, still honor cursor position. Default false selects the only infantry regardless of cursor position +; BOOLEAN ; 如果该值为 true, 当单元格内只有一个步兵时, 仍然会判断鼠标的相对位置, false 时会始终选取该步兵, 默认为 false +InfantrySubCell.Edit.Single=false + +; BOOLEAN ; When dragging infantry, place them based on cursor position. Default false +; BOOLEAN ; 如果该值为 true, 在拖拽步兵时,会考虑鼠标的相对位置放置, 默认为 false +InfantrySubCell.Edit.Drag=true + +; BOOLEAN ; When placing infantry, place them based on cursor position. Default false +; BOOLEAN ; 如果该值为 true, 在放置步兵时,会考虑鼠标的相对位置放置, 默认为 false +InfantrySubCell.Edit.Place=true + +; BOOLEAN ; Lock infantry in the center to match InfantrySubCell.GameDefault, otherwise depend on cursor position. Default false +; BOOLEAN ; 如果该值为 true, 会固定单元格中间步兵的位置同 InfantrySubCell.GameDefault 一致, 否则根据鼠标上下位置决定. 默认为 false +InfantrySubCell.Edit.FixCenter=false + +; BOOLEAN ; Consider OccupationBits of terrain objects when placing infantry. Default false +; BOOLEAN ; 如果该值为 true, 在放置步兵时会考虑地形对象对单元格的占用, 默认为 false +InfantrySubCell.OccupationBits=true + +; STRING ; Default infantry parameterss matching the INI section +; STRING ; 摆放步兵的默认参数, 顺序与INI小节一致 +DefaultInfantryProperty=House,ID,256,X,Y,Subcell,Guard,64,None,0,-1,0,0,0 + +; STRING ; Default vehicle parameterss matching the INI section +; STRING ; 摆放车辆的默认参数, 顺序与INI小节一致 +DefaultUnitProperty=House,ID,256,X,Y,64,Guard,None,0,-1,0,-1,0,0 + +; STRING ; Default aircraft parameterss matching the INI section +; STRING ; 摆放飞行器的默认参数, 顺序与INI小节一致 +DefaultAircraftProperty=House,ID,256,X,Y,64,Guard,None,0,-1,0,0 + +; STRING ; Default buildiing parameterss matching the INI section +; STRING ; 摆放建筑的默认参数, 顺序与INI小节一致 +DefaultBuildingProperty=House,ID,256,X,Y,0,None,0,0,1,0,0,None,None,None,1,0 + +; STRING ; Supported associable map file formats +; STRING ; 支持关联的地图文件格式 +SupportedFormats=map,mpr,yrm,mmx,yro + +; BOOLEAN ; Force CString allocations on the stack for stability at minor perf cost. Default true +; BOOLEAN ; 强制CString内存在堆上分配, 可以提升稳定性, 但可能轻微降低效率, 默认为 true +StringBufferStackAllocation=true + +; BOOLEAN ; Use a stricter exception filter that catches C++ EH exceptions. Default false +; BOOLEAN ; 启用更严格的错误捕获, 会捕获 C++ EH exception, 默认为 false +StrictExceptionFilter=false + +; BOOLEAN ; Use the modern toolbar cameos. Default true +; BOOLEAN ; 是否使用新的工具栏图标, 默认为 true +UseNewToolBarCameo=true + +; BOOLEAN ; Skip the tips of the day dialog. Default false +; BOOLEAN ; 是否不显示今日提示窗口,默认为 false +SkipTipsOfTheDay=false + +; BOOLEAN ; Prevent brush tools (raise/lower/flatten) from changing brush size automatically. Default false +; BOOLEAN ; 在选择地形工具(抬升、降低、平整地形)时, 是否跳过改变笔刷大小, 默认为 false +SkipBrushSizeChangeOnTools=true + +; BOOLEAN ; Remember brush sizes for mouse commands and restore them when switching. Default false +; BOOLEAN ; 记忆各操作的笔刷大小,并在切换时自动还原, 默认为 false +RecordBrushSizeHistory=false + +; BOOLEAN ; When placing tiles, skip hidden cells. Default false +; BOOLEAN ; 在放置地形、生成海岸、抬升地表、粘贴地形时, 是否跳过被隐藏的单元格, 默认为 false +PlaceTileSkipHide=true + +; BOOLEAN ; Enable Veinhole Monster logic to display corresponding overlays in the object browser and terrain browser. Default false +; BOOLEAN ; 启用泰伯利亚藤蔓(蔓孔)逻辑, 在物品浏览器、地形浏览器中显示对应的覆盖物, 默认为 false +EnableVeinholeLogic=false + +; BOOLEAN ; Hide team/taskforce/script sections in the INI editor. Default false +; BOOLEAN ; 在INI编辑器中, 是否忽略小队、特遣、脚本小节, 默认为 false +INIEditor.IgnoreTeams=true + +; BOOLEAN ; When holding Ctrl for fill, treat LAT tiles as the same group. Default true +; BOOLEAN ; 按住ctrl填充地形时, 是否同时填充目标地形的LAT, 默认为 true +FillArea.ConsiderLAT=true + +; BOOLEAN ; When holding Ctrl for fill, treat all water tiles as as the same group. Default true +; BOOLEAN ; 按住ctrl填充地形时, 将所有水面视为一类, 默认为 true +FillArea.ConsiderWater=true + +; BOOLEAN ; When holding Ctrl for fill, treat sub tile blocks as as the same group. Default true +; BOOLEAN ; 按住ctrl填充地形时, 将整个地形的子地形块视作一类, 默认为 true +FillArea.ConsiderWholeTile=true + +; BOOLEAN ; Render object shadows in the map view. Default true +; BOOLEAN ; 是否显示所有游戏对象的阴影, 开启时可以在图层中手动关闭, 默认为 true +InGameDisplay.Shadow=true + +; BOOLEAN ; Display voxel shadow on the ground instead of in the bottom. Default false +; BOOLEAN ; 在地面上而不是体素的最底部渲染阴影, 默认为 false +InGameDisplay.Shadow.OnGround=false + +; BOOLEAN ; Show deploy/unload images of units. Default true +; BOOLEAN ; 是否对可部署的Unload状态的单位显示部署图像, 不会考虑发生了单位转换的单位, 默认为 true +InGameDisplay.Deploy=true + +; BOOLEAN ; Draw water images of units in water. Default true +; BOOLEAN ; 是否对水中的步兵或载具显示水中图像, 不会考虑发生了单位转换的单位, 默认为 true +InGameDisplay.Water=true + +; BOOLEAN ; Support Phobos damaged art images. Default true +; BOOLEAN ; 是否支持 Phobos 的单位伤残更换图像功能, 默认为 true +InGameDisplay.Damage=true + +; BOOLEAN ; Render hover unit a little higher. Default true +; BOOLEAN ; 是否将悬浮单位渲染的更高一些, 默认为 true +InGameDisplay.Hover=true + +; BOOLEAN ; Display alphaimages for terrain objects and buildings; can disable manually if needed. Default true +; BOOLEAN ; 是否显示建筑物和地形对象的 AlphaImage, 开启时可以在图层中手动关闭, 默认为 true +InGameDisplay.AlphaImage=true + +; BOOLEAN ; Render "OnBridge" unit a little higher. Default true +; BOOLEAN ; 是否将“在桥梁上”的单位渲染的更高一些, 默认为 true +InGameDisplay.Bridge=true + +; BOOLEAN ; Respect animation offsets in art when rendering. Default true +; BOOLEAN ; 是否根据素材与 art 设置调整建筑动画图层, 在少数情况下可能与游戏内不一致, 默认为 true +InGameDisplay.AnimAdjust=true + +; BOOLEAN ; Show cloaked units as transparent. Default false +; BOOLEAN ; 隐形单位是否半透明显示, 默认为 false +InGameDisplay.Cloakable=false + +; BOOLEAN ; Display remapble colors of wall overlays by nearest building. Default false +; BOOLEAN ; 根据距离最近的建筑, 显示围墙覆盖物的所属色, 默认为 false +InGameDisplay.RemapableOverlay=false + +; BOOLEAN ; Display invisible bridge overlays in numbers. Default false +; BOOLEAN ; 以数字显示不可见的桥梁覆盖物 , 默认为 false +DisplayBridgeOverlay=false + +; BOOLEAN ; Display default images for vehicles, infantry, and aircraft without images, names are: FA2DEFAULT_AIRCRAFT, FA2DEFAULT_INFANTRY, FA2DEFAULT_UNIT. Default false +; BOOLEAN ; 为没有图像的车辆、步兵与飞行器显示缺省图像, 名称为: FA2DEFAULT_AIRCRAFT, FA2DEFAULT_INFANTRY, FA2DEFAULT_UNIT, 默认为 false +UseDefaultUnitImage=true + +; BOOLEAN ; Display default images for techno attachments without images. Default false +; BOOLEAN ; 为没有图像的附加单位显示缺省图像, 默认为 false +UseDefaultUnitImage.TechnoAttachment=false + +; FLOAT ; Voxel light vector as X,Y,Z corresponding to game. Positive numbers are illuminated from the direction of the axis(↖, ↗, ↓). Default 0.05,1,0.2 +; FLOAT ; VXL渲染的光源方向, 格式为 X,Y,Z, 正数为从数轴方向照射(↖, ↗, ↓), 默认为 0.05,1,0.2 +LightingSource=0.05,1,0.2 + +; INTERGER ; Maximum value of overlay data. Default 60 +; INTERGER ; 覆盖物数据的最大值, 默认为 60 +OverlayDataLimit=256 + +; BOOLEAN ; Hide ExtraImage when Flat To Ground Mode enabled. Default false +; BOOLEAN ; 开启平面显示时, 是否隐藏地形的 ExtraImage, 默认为 false +FlatToGroundHideExtra=false + +; BOOLEAN ; Render objects outside of map bounds. Default false +; BOOLEAN ; 是否显示地图边界外的游戏对象, 默认为 false +DisplayObjectsOutside=false + +; BOOLEAN ; Show options for technos (Phobos). Default false +; BOOLEAN ; 科技类型窗口在多人地图中显示 Phobos 支持的 所属方, 默认为 false +PlayerAtXForTechnos=false + +; BOOLEAN ; Show options for triggers (Phobos). Default false +; BOOLEAN ; 触发编辑器窗口在多人地图中显示 Phobos 支持的 所属方, 默认为 false +PlayerAtXForTriggers=false + +; BOOLEAN ; Monitor map files for external edits. Default true +; BOOLEAN ; 检测地图文件是否被外部修改. 默认为 true +FileWatcher=true + +; INTERGER ; Number of distance ruler history entries. Default 5 +; INTERGER ; 距离标尺记录的历史数量, 默认为 5 +DistanceRuler.Records=5 + +; INTERGER ; Font size for money on map and property viewer. Default 18 +; INTERGER ; 资源总量与属性查看字体的大小, 默认为 18 +DisplayTextSize=18 + +; INTERGER ; Tree view cameo size; default 32 (min 16, max 64) +; INTERGER ; 物品浏览器图标的大小, 默认为 32, 最小 16, 最大 64 +TreeViewCameo.Size=32 + +; BOOLEAN ; Whether or not draw celltag icons translucently. Default false +; BOOLEAN ; 是否半透明绘制单元标记图标. 默认为 false +DrawCelltagTranslucent=true + +; BOOLEAN ; Whether or not force Neutral and Special houses to use the light gray color in skirmish. Default true +; BOOLEAN ; 是否在多人地图中, 强制 Neutral 和 Special 所属方使用浅灰色颜色. 默认为 true +ForceNeutralSpecialColor=true + +; BOOLEAN ; Map house color variants to their corresponding solid colors. Default false +; BOOLEAN ; 将单位所属色变体映射为纯色. 默认为 false +UnifyHouseColors=false + +; COLORREF(R,G,B) ; Color in waypoint flag to be replaced in "Set waypoint color". Default 0,255,0. Range 0-255 +; COLORREF(R,G,B) ; 在“设置路径点颜色”中替换的路径点旗帜颜色, 默认为 0,255,0. 取值范围为0-255 +DisplayColor.Waypoint=0,255,0 + +; COLORREF(R,G,B) ; Color in celltag icon to be replaced in "Set celltag color". Default 255,0,0. Range 0-255 +; COLORREF(R,G,B) ; 在“设置单元标记颜色”中替换的单元标记图标颜色, 默认为 255,0,0. 取值范围为0-255 +DisplayColor.Celltag=255,0,0 + +; COLORREF(R,G,B) ; Distance ruler color. Default 255,0,0. Range 0-255 +; COLORREF(R,G,B) ; 距离标尺线段的颜色, 默认为 255,0,0. 取值范围为0-255 +DistanceRuler.Color=255,0,0 + +; COLORREF(R,G,B) ; Boundary color for terrain generator. Default 255,255,0. Range 0-255 +; COLORREF(R,G,B) ; 地形生成器设置范围的颜色, 默认为 255,255,0. 取值范围为0-255 +TerrainGeneratorColor=255,255,0 + +; COLORREF(R,G,B) ; Player posistion indicator color for map renderer. Default 255,0,0. Range 0-255 +; COLORREF(R,G,B) ; 地图渲染器游戏者位置的颜色, 默认为 255,0,0. 取值范围为0-255 +PlayerLocation.Color=255,0,0 + +; INTEGER ; Maximum distance displayed by range overlays; very large values reduce performance. Default 50 +; INTEGER ; 范围属性最大显示的距离, 过大的范围会导致显示卡顿, 默认为50 +RangeBound.MaxRange=50 + +; COLORREF(R,G,B) ; Primary weapon range color. Default 0,255,255. Range 0-255 +; COLORREF(R,G,B) ; 主武器射程范围的颜色, 默认为 0,255,255. 取值范围为0-255 +WeaponRangeBound.Color=0,255,255 + +; COLORREF(R,G,B) ; Primary minimum range color. Default 0,200,200. Range 0-255 +; COLORREF(R,G,B) ; 主武器最小射程范围的颜色, 默认为 0,200,200. 取值范围为0-255 +WeaponRangeMinimumBound.Color=0,200,200 + +; COLORREF(R,G,B) ; Secondary weapon range color. Default 0,255,130. Range 0-255 +; COLORREF(R,G,B) ; 副武器射程范围的颜色, 默认为 0,255,130. 取值范围为0-255 +SecondaryWeaponRangeBound.Color=0,255,130 + +; COLORREF(R,G,B) ; Secondary minimum range color. Default 0,200,100. Range 0-255 +; COLORREF(R,G,B) ; 副武器最小射程范围的颜色, 默认为 0,200,100. 取值范围为0-255 +SecondaryWeaponRangeMinimumBound.Color=0,200,100 + +; COLORREF(R,G,B) ; Death weapon range color. Default 255,127,39. Range 0-255 +; COLORREF(R,G,B) ; 自杀武器范围的颜色, 默认为 255,127,39. 取值范围为0-255 +DeathWeaponRangeBound.Color=255,127,39 + +; COLORREF(R,G,B) ; Gap generator range color. Default 0,0,255. Range 0-255 +; COLORREF(R,G,B) ; 黑幕产生范围的颜色, 默认为 0,0,255. 取值范围为0-255 +GapRangeBound.Color=0,0,255 + +; COLORREF(R,G,B) ; Sensor array range color. Default 255,0,255. Range 0-255 +; COLORREF(R,G,B) ; 隐形检测范围的颜色, 默认为 255,0,255. 取值范围为0-255 +SensorsRangeBound.Color=255,0,255 + +; COLORREF(R,G,B) ; Cloak generator range color. Default 255,0,0. Range 0-255 +; COLORREF(R,G,B) ; 隐形范围的颜色, 默认为 255,0,0. 取值范围为0-255 +CloakRangeBound.Color=255,0,0 + +; COLORREF(R,G,B) ; Psychic sensor range color. Default 255,255,0. Range 0-255 +; COLORREF(R,G,B) ; 心灵感应范围的颜色, 默认为 255,255,0. 取值范围为0-255 +PsychicRangeBound.Color=255,255,0 + +; COLORREF(R,G,B) ; Guard area range color. Default 0,255,0. Range 0-255 +; COLORREF(R,G,B) ; 区域警戒范围的颜色, 默认为 0,255,0. 取值范围为0-255 +GuardRangeBound.Color=0,255,0 + +; COLORREF(R,G,B) ; Sight range color. Default 128,128,128. Range 0-255 +; COLORREF(R,G,B) ; 视野范围的颜色, 默认为 128,128,128. 取值范围为0-255 +SightRangeBound.Color=128,128,128 + +; COLORREF(R,G,B) ; Designator range color. Default 128,128,128. Range 0-255 +; COLORREF(R,G,B) ; 指示者范围的颜色, 默认为 34,177,76. 取值范围为0-255 +DesignatorRangeBound.Color=34,177,76 + +; COLORREF(R,G,B) ; Inhibitor range color. Default 128,128,128. Range 0-255 +; COLORREF(R,G,B) ; 抑制者范围的颜色, 默认为 204,46,49. 取值范围为0-255 +InhibitorRangeBound.Color=204,46,49 + +; BOOLEAN ; Apply elevation modifiers to weapon range preview; low accuracy. Default false +; BOOLEAN ; 计算武器射程时,是否考虑高度对射程的影响, 精确度较低, 仅供参考, 默认为 false +WeaponRangeBound.SubjectToElevation=true + +; BOOLEAN ; Draw all ranges except for weapons subject to elevation using ellipses. Default true +; BOOLEAN ; 使用椭圆绘制除了受高度影响的武器外的所有范围, 默认为 false +RangeBound.DrawEllipse=true + +; BOOLEAN ; Show outlines by default when pasting. Default false +; BOOLEAN ; 粘贴地形时, 是否默认显示描边, 默认为 false +PasteShowOutline=true + +; Mix overrides, yes = relative path to FA2 installed folder +; Can be used to override game content to show in FA2 +; First one has highest loading priority +;[ExtraMixes] + +; Encrypted packages, yes = relative path to FA2 installed folder +; Can be used to override game content to show in FA2 +; First one has highest loading priority +; How to use, please refer to documentation +;[ExtraPackages] +;data.rpck=false + +; Custom resource directories, read in registration order +; Value: yes/true = relative path to FA2 installed folder, no/false = relative path to game folder +; Can be used to override game content to show in FA2 +; First one has highest loading priority +; Priority: lower than Resources\HighPriority\, higher than the game path +;[ExtraDirectories] +;voxels=false + +; Stringtable overrides, yes = relative path to FA2 installed folder +; Can be used to override stringtables to show in FA2 +; Support .csf, .llf, .ecs, and .ini (exported by Ra2CsfToolsGUI) +;[ExtraStringtables] + +; Custom nested mix files, format: nested mix = parent mix +; Can be used to override game content to show in FA2 +; First one has highest loading priority +;[NestedMixes] + +; Ignored mix files, format: index = ignored mix +;[IgnoredMixes] + +;[Filenames] +;EVA=eva.ini +;EVAYR=evamd.ini +;Sound=sound.ini +;SoundYR=soundmd.ini +;Theme=theme.ini +;ThemeYR=thememd.ini +;AI=ai.ini +;AIYR=aimd.ini +;RulesYR=rulesmd.ini +;Rules=rules.ini +;ArtYR=artmd.ini +;Art=art.ini +;TemperateYR=temperatmd.ini +;Temperate=temperat.ini +;SnowYR=snowmd.ini +;Snow=snow.ini +;UrbanYR=urbanmd.ini +;Urban=urban.ini +;UrbanNYR=urbannmd.ini +;LunarYR=lunarmd.ini +;DesertYR=desertmd.ini +;MapselYR=mapselmd.ini +;MixExtension=md + +; Contains a list of theater names, only the +; existing 6 names are valid. If not listed +; then all default 6 theaters are used. +; The second item separated by commas +; represents the name saved in the map file +; under [Map] -> Theater=. + +[Customizations] +AnimationListID=OrderedAnimations +DefaultGameMode=standard +DefaultMinPlayers=2 +SaveMapExtensionOverride=no +SaveMapExtensionDefault=.map + + +[Sides] +0=Allied +1=Soviet +2=Yuri +3=安塔列星 +4=科技功能建筑,1 +5=地标建筑类,1 +6=大城市建筑,1 +7=中小城市建筑,1 +8=村庄建筑,1 +9=工业建筑,1 +10=小型装饰摆件,1 +11=中立特殊建筑,1 + + +[GameModes] +standard=标准作战 +teamgame=小队联盟 +freeall=混战 +cooperative=合作任务 +duel=抢地盘 +navalwar=海战 +meatgrind=生死斗 +megawealth=巨富 +manymoney=淘金热 +fortresswar=攻城拔寨 +testside=开发者乐园 +airwar=(无效)空战 +nukewar=(无效)核战 + + +[ForceUnitPalettePrefix] +0=TIBTRE + + +[ForceIsoPalettePrefix] + +;===ScriptTemplates=== +;X=UIName, Name, Count, Key 1 ,Value 1 , Key 2 ,Value 2 , ..., Key Count ,Value Count +;EMPTY will be considered as a CONST, with it FA2Copy will ignore the value + +;By prry + +[VehicleVoxelTurretsRA2Disabled] +;DISKX=-10 +;DISKY=-10 +;YTNKY=-30 +SREFX=4 +SREFY=3 + + +[IgnoreArtImage] +BFRT=1 +CAML=1 + +CAML=yes + +; hacks for shore pieces +; shore id_pos=iswater +; pos: count from left top to right, then next "row" + +; RA2 only needs one fix (for URBAN specifically) + +[IgnoreSuperAnim1] + + + +[IgnoreSuperAnim2] +NAMISL=1 +NAMISL2=1 +NAMISL3=1 +NAIRON=1 +GACSPH=1 +YAGNTC=1 +YAPPET=1 + + +[IgnoreSuperAnim3] +NAMISL=1 +NAMISL2=1 +NAMISL3=1 +NAIRON=1 +GACSPH=1 +YAGNTC=1 +YAPPET=1 + + +[IgnoreSuperAnim4] +NAMISL=1 +NAMISL2=1 +NAMISL3=1 +NAIRON=1 +GACSPH=1 +YAGNTC=1 +YAPPET=1 + +; fix for building list: + +[GTGCAN] +AIBasePlanningSide=0 + + +[YACOMD] +AIBasePlanningSide=2 + + +[YADEPT] +AIBasePlanningSide=0 + + +[NASPYB] +AIBasePlanningSide=2 + +; rename list for objects. ObjectID=StringID + +[Rename] + + +[IgnoreRA2] +0=GAARMORY +1=APACHE +2=DeathDummy +3=WEEDGUY +4=GADUMY +5=NAWAST +6=GARADR +7=CAIRSFGL +8=CALIT01E +9=CALIT01N +10=CALIT01S +11=CALIT01W +12=CALIT03E +13=CALIT03N +14=CALIT03S +15=CALIT03W +16=CALIT02L +17=CALIT02R +18=NAHPAD +19=CAEURO01 +20=CACITY01 +21=CACITY02 +22=CACITY03 +23=CACITY04 +24=CITY06 +25=CITY05 +26=CITY04 +27=CITY03 +28=CITY02 +29=CITY01 +30=CAARMR +31=TREE29 +32=TREE30 +33=CANEWY05 +34=REDLAMP +35=GRENLAMP +36=BLUELAMP +37=YELWLAMP +38=PURPLAMP +39=INORANLAMP +;40=NEGLAMP ; NEGLAMP added again, invisible in game +41=NEGRED +42=TSTLAMP +43=CAPOL01E +44=CAPOL01N +45=CAPOL01S +46=CAPOL01W +47=CASIN01E +48=CASIN01N +49=CASIN01W +50=CASIN01S +51=GAGREEN +52=GALITE +53=CARGOPLANE +; 54=CAHOSP +55=XCOMET +56=CAKRMW +57=CAFNCB +58=CAFNCW +59=CAFNCP +60=CR1 +61=CR2 +62=CR3 +63=CR4 +64=CR5 +65=CR6 +66=BURN01 +67=BURN02 +68=BURN03 +69=BURN04 +70=BURN05 +71=BURN06 +72=BURN07 +73=BURN08 +74=BURN09 +75=BURN10 +76=BURN11 +77=BURN12 +78=BURN13 +79=BURN14 +80=BURN15 +Another one :) =BURN16 +82=CASYDN01 +83=CATIME +84=CALOND02;? +;85=CAMORR01 +;86=CAMORR02 +;87=CAMORR03 +;88=CAEGYP06 +;89=CAMORR04 +;90=CAEGYP04 +;91=CAEGYP05 +;92=CALA06 + +97=DNOB;? +98=GAWRONG +99=CALA02;? +100=CAMOON01 +;101=CAEGYP01 +102=UTNK +103=YDUM +104=WWLF +105=YADOG +106=YDOG +107=SMON +108=CMON +109=YADEPT +110=CALOND06;? +;111=TREE32 + +; Yes means Image= in art ini will be ignored + +[IgnoreTerrainRA2] + +[ScriptTemplates] +DefaultName=默认 +Counts=5 +1=两点间巡逻,New patrol 2 script,5,16,0,5,3,16,0,5,3,6,1 +2=三点间巡逻,New patrol 3 script,7,16,0,5,3,16,0,5,3,16,0,5,3,6,1 +3=四点间巡逻,New patrol 4 script,9,16,0,5,3,16,0,5,3,16,0,5,3,16,0,5,3,6,1 +4=装载部队卸载攻击,New unload script,6,3,0,8,2,39,0,20,0,50,120,0,0 +5=装载入载具并攻击,New load script,3,21,EMPTY,14,EMPTY,0,0 + +;===TeamTemplates=== +;X=UIName, Name, Experience Level, Priority, Max, Group, TechLevel, Mind-control Result, Checkbox1, Checkbox2, ..., Checkbox19 +;Checkbox order matrix: +; |1 6 11 | +; |2 7 12 16| +; |3 8 13 17| +; |4 9 14 18| +; |5 10 15 19| + +;By Sandman + +[TeamTemplates] +DefaultName=默认 +Counts=3 +1=空降部队,New Paratroop Team,1,5,5,-1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0 +2=AI生产小队,New AI Production Team,1,5,10,-1,5,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0 +3=突击小队,New Offense Team,1,5,5,-1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0 + +; not NOSURFACES: +;[BuildingVoxelTurretsRA2] +;GTGCANX=-1 +;GTGCANY=0;-6 +;NASAMY=0;2 +;NALASRX=0 +;NALASRY=0;-3 +;NAFLAKY=10;10 + + +[ParamTypes] +-1=未使用,0,1;not listed in FA2 +0=未使用,0 +1=未知,0 +2=所属方,1 +3=局部变量,20 +4=时间,0 +5=积分,0 +6=数值,0 +7=小队类型,2 +8=建筑,6 +9=飞行器,5 +10=步兵,4 +11=单位,3 +12=电影,7 +13=文本,8 +14=触发,9 +15=允许,10 +16=声音,11 +17=乐曲,12 +18=语音,13 +19=步骤,0 +20=超级武器,14 +21=左,0 +22=上,0 +23=宽,0 +24=高,0 +25=动画,15 +26=粒子,16 +27=持续时间,0 +28=速度,0 +29=Voxel碎片ID号,29 +30=路径点,17 +31=木箱类型,PT_CrateTypes +32=语音提示框,19 +33=字符,21 +34=行为,9 +35=全局变量,27 +36=特定武器,14 +37=已激活,10 +38=触发标签,22 +39=科技类型,0 +40=来源,0 +41=武器,24 +42=发光行为,25 +43=事件,9 +44=阵雨,26 +45=浮点值,0 +46=科技类型,29,2 +47=建筑,28 +48=数量,0 +49=像素动画,23 +50=影片,7 +51=雷达事件,PT_RadarEventTypes + +; These value sequence are hardcoded, check dissasmbly at YR 0x7E523C +; Origianl definitinos are as follows: +; "Money" +; "Unit" +; "HealBase" +; "Cloak" +; "Explosion" +; "Napalm" +; "Squad" +; "Darkness" +; "Reveal" +; "Armor" +; "Speed" +; "Firepower" +; "ICBM" +; "Invulnerability" +; "Veteran" +; "IonStorm" +; "Gas" +; "Tiberium" +; "Pod" + +[Chinese-ParamTypes] +-1=未使用,0,1;not listed in FA2 +0=未使用,0 +1=未知,0 +2=所属方,1 +3=局部变量,20 +4=时间,0 +5=积分,0 +6=数值,0 +7=小队类型,2 +8=建筑,6 +9=飞行器,5 +10=步兵,4 +11=单位,3 +12=电影,7 +13=文本,8 +14=触发,9 +15=允许,10 +16=声音,11 +17=乐曲,12 +18=语音,13 +19=步骤,0 +20=超级武器,14 +21=左,0 +22=上,0 +23=宽,0 +24=高,0 +25=动画,15 +26=粒子,16 +27=持续时间,0 +28=速度,0 +29=Voxel碎片ID号,29 +30=路径点,17 +31=木箱类型,PT_CrateTypes +32=语音提示框,19 +33=字符,21 +34=行为,9 +35=全局变量,27 +36=特定武器,14 +37=已激活,10 +38=触发标签,22 +39=科技类型,0 +40=来源,0 +41=武器,24 +42=发光行为,25 +43=事件,9 +44=阵雨,26 +45=浮点值,0 +46=科技类型,29,2 +47=建筑,28 +48=数量,0 +49=像素动画,23 +50=影片,7 +51=雷达事件,PT_RadarEventTypes + +; These value sequence are hardcoded, check dissasmbly at YR 0x7E523C +; Origianl definitinos are as follows: +; "Money" +; "Unit" +; "HealBase" +; "Cloak" +; "Explosion" +; "Napalm" +; "Squad" +; "Darkness" +; "Reveal" +; "Armor" +; "Speed" +; "Firepower" +; "ICBM" +; "Invulnerability" +; "Veteran" +; "IonStorm" +; "Gas" +; "Tiberium" +; "Pod" + +[PT_CrateTypes] +FixedValueCount=19 +0=0 - 钱箱 +1=1 - 随机载具单位 +2=2 - 全体医疗/修复 +3=3 - 永久隐形 +4=4 - C4弹头伤害 +5=5 - 火焰爆炸伤害 +6=6 - 随机步兵小队 +7=7 - 全图黑幕 +8=8 - 显示全地图 +9=9 - 护甲强化 +10=10 - 速度提升 +11=11 - 火力提升 +12=12 - 核弹超武单次 +13=13 - 铁幕无敌(功能残缺) +14=14 - 经验升星 +15=15 - 离子风暴(无效) +16=16 - 毒气粒子 +17=17 - 小片矿石 +18=18 - 空降仓(无效) + +; Controls for radar events +; The events, in order, are: +; (1) Generic Combat Event, +; (2) Generic Noncombat Event, +; (3) Dropzone Event, +; (4) Base Under Attack Event, +; (5) Harvester Under Attack Event, +; (6) Enemy Object Sensed Event + +[PT_RadarEventTypes] +FixedValueCount=6 +0=0 - (红)通用战斗事件 +1=1 - (黄)通用非战斗事件 +2=2 - (黄)空降区事件 +3=3 - (红)基地遭受攻击 +4=4 - (红)矿车遭受攻击 +5=5 - (蓝)探测到敌方单位 + + +[ScriptParams] +0=占位,0 +1=目标,1 +2=路径点,2 +3=跳转至行#,3 +4=分离小队,4 +5=全局变量,5 +6=脚本,6 +7=小队,7 +8=所属方,8 +9=语音,9 +10=音效,10 +11=影片,11 +12=音乐,12 +13=国家,13 +14=局部变量,14 +15=朝向,15 +16=建筑,16 +17=动画,17 +18=对话气泡,18 +19=进入状态,19 +20=整数,0 +21=布尔,20 +22=目标列表,SP_TargetList + + +[Chinese-ScriptParams] +0=占位,0 +1=目标,1 +2=路径点,2 +3=跳转至行#,3 +4=分离小队,4 +5=全局变量,5 +6=脚本,6 +7=小队,7 +8=所属方,8 +9=语音,9 +10=音效,10 +11=影片,11 +12=音乐,12 +13=国家,13 +14=局部变量,14 +15=朝向,15 +16=建筑,16 +17=动画,17 +18=对话气泡,18 +19=进入状态,19 +20=整数,0 +21=布尔,20 +22=目标列表,SP_TargetList + + +[SP_TargetList] +ParseFromSection=TeamTargetTechnoTypes +ExtraValueType=0 + + +[ScriptExtras] +0=SE_TargetScanStrategy + + +[SE_TargetScanStrategy] +0=0 - 最小威胁 +1=1 - 最大威胁 +2=2 - 最近距离 +3=3 - 最远距离 + +;Index=Name,ParamType,Obsolete,YROnly,Description + +[ScriptsRA2] +0=0 - 攻击,1,0,1,攻击某类目标。 +1=1 - 攻击路径点,2,0,1,攻击指定路径点(上的物体)。 +2=2 - 暴走(无效),0,1,0,小队的半机械人成员会暴走(无效脚本)。 +3=3 - 移动到路径点,2,0,1,移动到指定路径点。 +4=4 - 移动到坐标,20,0,1,移动到坐标点 (int x+y*128)。 +5=5 - 警戒(时间计数),20,0,1,区域警戒 X 秒。(填5与计时器流逝5秒时间相同) +6=6 - 脚本转到行号 #,3,0,1,跳转到脚本的第#行,用来循环。 +7=7 - 玩家胜利,0,0,0,使玩家胜利。 +8=8 - 卸载,4,0,1,所有有载员的运载单位卸客。命令参数可以分别指定两个部分是否依然是小队成员或者要离开小队。 +9=9 - 部署,0,0,0,所有可部署单位进行部署。 +10=10 - 跟随友军单位,0,0,0,令小队跟随最近的友军单位。 +11=11 - 进入...状态,19,0,1,小队所有成员执行指定任务。 +12=12 - 设置全局变量,5,0,1,设置一个全局变量。 +13=13 - 进入空闲动画,0,0,0,让小队中的步兵单位闲置(播放闲置动画) +14=14 - 装载入运输载具,0,0,0,让所有单位进入运输载具(如果可能的话) +15=15 - 间谍进入路径点的建筑物,0,1,0,(废弃脚本) +16=16 - 巡逻至路径点,2,0,1,小队成员移动攻击到指定路径点。 +17=17 - 更改应用脚本,6,0,1,让小队使用新的脚本(参数为动作脚本ID) +18=18 - 更改小队,7,0,1,让小队更改小队类型(参数为作战小队ID) +19=19 - 惊慌,0,0,0,让小队所有单位惊慌。 +20=20 - 更改所属方,13,0,1,小队成员全部更改为所属方(参数为所属方编号) +21=21 - 分散,0,0,0,让所有单位分散。 +22=22 - 进入周围的黑幕,0,0,0,让所有单位逃到有黑幕的地方。 +23=23 - 玩家失败,0,0,0,使玩家失败。 +24=24 - 播放语音,9,0,1,播放特定的语音文件。 +25=25 - 播放音效,10,0,1,播放特定的音效文件。 +26=26 - 播放电影,11,0,1,播放特定的电影文件。 +27=27 - 播放音乐,12,0,1,播放特定的音乐文件。 +28=28 - 减少矿石,0,0,0,减少小队成员周围的矿石数量。 +29=29 - 开始生产,0,0,0,让所属方开始生产。 +30=30 - 变卖乌拉,0,0,0,让AI所属方变卖所有建筑并进行最后总攻。 +31=31 - 小队自毁,0,0,0,让该小队所有成员自毁。 +32=32 - 闪电风暴开始(延时),20,0,1,经过指定的时间后开始闪电风暴。 +33=33 - 闪电风暴结束,0,0,0,使闪电风暴结束。 +34=34 - 镜头聚焦小队,20,0,1,镜头聚焦小队(参数为速度) +35=35 - 重置地图黑幕,0,0,0,重置地图黑幕。 +36=36 - 显示全地图,0,0,0,显示全地图。 +37=37 - 删除小队成员,0,0,0,删除该小队所有成员。 +38=38 - 清除全局变量,5,0,1,清除指定全局变量。 +39=39 - 设置局部变量,14,0,1,设置指定局部变量。 +40=40 - 清除局部变量,14,0,1,清除指定局部变量。 +41=41 - 镇定,0,0,0,使所有小队成员停止惊慌。 +42=42 - 强制面向,15,0,1,强制小队成员面向一个特定的方向。 +43=43 - 等待到满载,0,0,0,等到所有运输器满载。 +44=44 - 卡车卸载,0,0,0,让所有卡车卸载箱子(即改变类型为未装载单位) +45=45 - 卡车装载,0,0,0,让所有卡车装载箱子(即改变类型为装载的单位) +46=46 - 攻击敌方建筑物,16,0,1,攻击指定类型的建筑,参数为建筑序号,额外参数可以用于指定搜索目标时的策略。 +47=47 - 移动到敌方建筑物,16,0,1,移动到指定类型的建筑,参数为建筑序号+系数(+0:攻击威胁值最小的,+65536 :攻击威胁值最大的,+131072:攻击距离最近的,+262144:攻击距离最远的)。 +48=48 - 侦察,0,0,0,小队会侦察玩家未探索的区域。 +49=49 - 成功,0,0,0,记录小队完成了任务。用于AI触发分量(权重)。在每一个AI触发后添加此行为。 +50=50 - 闪烁,20,0,1,闪烁小队一段时间(参数为闪烁的帧数) +51=51 - 播放动画,17,0,1,在每个小队单位上播放动画。 +52=52 - 对话气泡,18,0,1,在小队第一个单位上显示对话气泡。 +53=53 - 敌方基地外集合,20,0,1,在敌方基地安全距离外集合。Ares2.0起,数值参数的正负将影响Rulesmd全局中安全距离 (AISafeDistance=) 的伸长或缩短。 +54=54 - 己方基地外集合,20,0,1,在己方基地一定距离外集合。Ares2.0起,数值参数的正负将影响Rulesmd全局中「友军基地」安全距离 (AIFriendlyDistance=,不存在这条则用AISafeDistance=) 的伸长或缩短。 +55=55 - 请求超武...,20,0,1,请求小队所属方对当前小队使用超级武器,默认为铁幕。Ares2.0起,数值参数决定AI使用哪一组超武(对应SW.Group=,默认为0)。 +56=56 - 超时空传送至建筑,16,0,1,若所属方超时空传送就绪,将该小队超时空传送至特定建筑。但是此后需要额外使用攻击命令。 +57=57 - 超时空传送至目标,1,0,1,若所属方超时空传送就绪,将该小队超时空传送至目标。但是此后需要额外使用攻击命令。 +;YR Only +58=58 - 移动到己方建筑,16,0,1,移动到友方的特定建筑,额外参数可以用于指定搜索目标时的策略 +59=59 - 攻占路径点上的建筑,2,0,1,攻击占据该路径点的建筑物。AI单位通常会优先尝试驻扎(若能)。 +60=60 - 进入回收厂,0,0,0,让小队进入该所属方就近的部队回收厂。 +61=61 - 进入坦克碉堡,0,0,0,让小队的每个单位进入空的坦克碉堡。 +62=62 - 进入生化反应炉,0,0,0,让小队进入生化反应炉。 +63=63 - 进入战斗碉堡,0,0,0,让小队进入战斗碉堡。 +64=64 - 进入可驻军建筑,0,0,0,让小队进驻中立建筑物。 +;Hares Only +65=65 - 攻击特定类型的单位,22,0,1,攻击特定类型的科技单位,数值为全局TeamTargetTechnoTypes列表中的序号,从0开始 +66=66 - 驻扎路径点位置的建筑,2,0,1,派遣步兵单位驻扎路径点位置的建筑(如果有),不区分小队所属是人类或AI玩家。 +67=67 - 摄像机视角跟随,20,0,1,激活/取消摄像机追逐视角设置为该小队成员(所有成员都会标记为跟随,多成员小队慎用)。 +68=68 - 小队成员被玩家选中,20,0,1,激活/取消设置该小队成员为选中状态(所有成员都会标记为选中)。 +69=69 - 上一脚本执行成功,20,0,1,上一次脚本执行结果为成功 +70=70 - 上一脚本执行失败,20,0,1,上一次脚本执行结果为失败 +71=71 - 等待N游戏秒,20,0,1,等待N游戏秒,以保证上一条脚本执行完毕 + +[DontSaveAsWP] ; which code values indicate that the waypoint param must be saved as normal integer? +0=5 +1=9 +;2=10 +3=11 + +; events format: +; #=Description, P2 type, P1 type, TagNeeded, Obsolete,Desc2,UsedInTS,UsedInRA2,ID,[NeedsYR optional] +; negative number for type currently not supported (only Actions do support yet)! +; NOTE: Actions not listed in [Events] but in [EventsRA2] will be ignored. Use the "used in RA2" to support them. +; NOTE: To overwrite TS events with RA2 special ones, add the new event to the [EventsRA2] section. +; NOTE: ID must be the same like # + +[Chinese-ScriptsRA2] +0=0 - 攻击,1,0,1,攻击某类目标。 +1=1 - 攻击路径点,2,0,1,攻击指定路径点(上的物体)。 +2=2 - 暴走(无效),0,1,0,小队的半机械人成员会暴走(无效脚本)。 +3=3 - 移动到路径点,2,0,1,移动到指定路径点。 +4=4 - 移动到坐标,20,0,1,移动到坐标点 (int x+y*128)。 +5=5 - 警戒(时间计数),20,0,1,区域警戒 X 秒。(填5与计时器流逝5秒时间相同) +6=6 - 脚本转到行号 #,3,0,1,跳转到脚本的第#行,用来循环。 +7=7 - 玩家胜利,0,0,0,使玩家胜利。 +8=8 - 卸载,4,0,1,所有有载员的运载单位卸客。命令参数可以分别指定两个部分是否依然是小队成员或者要离开小队。 +9=9 - 部署,0,0,0,所有可部署单位进行部署。 +10=10 - 跟随友军单位,0,0,0,令小队跟随最近的友军单位。 +11=11 - 进入...状态,19,0,1,小队所有成员执行指定任务。 +12=12 - 设置全局变量,5,0,1,设置一个全局变量。 +13=13 - 进入空闲动画,0,0,0,让小队中的步兵单位闲置(播放闲置动画) +14=14 - 装载入运输载具,0,0,0,让所有单位进入运输载具(如果可能的话) +15=15 - 间谍进入路径点的建筑物,0,1,0,(废弃脚本) +16=16 - 巡逻至路径点,2,0,1,小队成员移动攻击到指定路径点。 +17=17 - 更改应用脚本,6,0,1,让小队使用新的脚本(参数为动作脚本ID) +18=18 - 更改小队,7,0,1,让小队更改小队类型(参数为作战小队ID) +19=19 - 惊慌,0,0,0,让小队所有单位惊慌。 +20=20 - 更改所属方,13,0,1,小队成员全部更改为所属方(参数为所属方编号) +21=21 - 分散,0,0,0,让所有单位分散。 +22=22 - 进入周围的黑幕,0,0,0,让所有单位逃到有黑幕的地方。 +23=23 - 玩家失败,0,0,0,使玩家失败。 +24=24 - 播放语音,9,0,1,播放特定的语音文件。 +25=25 - 播放音效,10,0,1,播放特定的音效文件。 +26=26 - 播放电影,11,0,1,播放特定的电影文件。 +27=27 - 播放音乐,12,0,1,播放特定的音乐文件。 +28=28 - 减少矿石,0,0,0,减少小队成员周围的矿石数量。 +29=29 - 开始生产,0,0,0,让所属方开始生产。 +30=30 - 变卖乌拉,0,0,0,让AI所属方变卖所有建筑并进行最后总攻。 +31=31 - 小队自毁,0,0,0,让该小队所有成员自毁。 +32=32 - 闪电风暴开始(延时),20,0,1,经过指定的时间后开始闪电风暴。 +33=33 - 闪电风暴结束,0,0,0,使闪电风暴结束。 +34=34 - 镜头聚焦小队,20,0,1,镜头聚焦小队(参数为速度) +35=35 - 重置地图黑幕,0,0,0,重置地图黑幕。 +36=36 - 显示全地图,0,0,0,显示全地图。 +37=37 - 删除小队成员,0,0,0,删除该小队所有成员。 +38=38 - 清除全局变量,5,0,1,清除指定全局变量。 +39=39 - 设置局部变量,14,0,1,设置指定局部变量。 +40=40 - 清除局部变量,14,0,1,清除指定局部变量。 +41=41 - 镇定,0,0,0,使所有小队成员停止惊慌。 +42=42 - 强制面向,15,0,1,强制小队成员面向一个特定的方向。 +43=43 - 等待到满载,0,0,0,等到所有运输器满载。 +44=44 - 卡车卸载,0,0,0,让所有卡车卸载箱子(即改变类型为未装载单位) +45=45 - 卡车装载,0,0,0,让所有卡车装载箱子(即改变类型为装载的单位) +46=46 - 攻击敌方建筑物,16,0,1,攻击指定类型的建筑,参数为建筑序号,额外参数可以用于指定搜索目标时的策略。 +47=47 - 移动到敌方建筑物,16,0,1,移动到指定类型的建筑,参数为建筑序号+系数(+0:攻击威胁值最小的,+65536 :攻击威胁值最大的,+131072:攻击距离最近的,+262144:攻击距离最远的)。 +48=48 - 侦察,0,0,0,小队会侦察玩家未探索的区域。 +49=49 - 成功,0,0,0,记录小队完成了任务。用于AI触发分量(权重)。在每一个AI触发后添加此行为。 +50=50 - 闪烁,20,0,1,闪烁小队一段时间(参数为闪烁的帧数) +51=51 - 播放动画,17,0,1,在每个小队单位上播放动画。 +52=52 - 对话气泡,18,0,1,在小队第一个单位上显示对话气泡。 +53=53 - 敌方基地外集合,20,0,1,在敌方基地安全距离外集合。Ares2.0起,数值参数的正负将影响Rulesmd全局中安全距离 (AISafeDistance=) 的伸长或缩短。 +54=54 - 己方基地外集合,20,0,1,在己方基地一定距离外集合。Ares2.0起,数值参数的正负将影响Rulesmd全局中「友军基地」安全距离 (AIFriendlyDistance=,不存在这条则用AISafeDistance=) 的伸长或缩短。 +55=55 - 请求超武...,20,0,1,请求小队所属方对当前小队使用超级武器,默认为铁幕。Ares2.0起,数值参数决定AI使用哪一组超武(对应SW.Group=,默认为0)。 +56=56 - 超时空传送至建筑,16,0,1,若所属方超时空传送就绪,将该小队超时空传送至特定建筑。但是此后需要额外使用攻击命令。 +57=57 - 超时空传送至目标,1,0,1,若所属方超时空传送就绪,将该小队超时空传送至目标。但是此后需要额外使用攻击命令。 +;YR Only +58=58 - 移动到己方建筑,16,0,1,移动到友方的特定建筑,额外参数可以用于指定搜索目标时的策略 +59=59 - 攻占路径点上的建筑,2,0,1,攻击占据该路径点的建筑物。AI单位通常会优先尝试驻扎(若能)。 +60=60 - 进入回收厂,0,0,0,让小队进入该所属方就近的部队回收厂。 +61=61 - 进入坦克碉堡,0,0,0,让小队的每个单位进入空的坦克碉堡。 +62=62 - 进入生化反应炉,0,0,0,让小队进入生化反应炉。 +63=63 - 进入战斗碉堡,0,0,0,让小队进入战斗碉堡。 +64=64 - 进入可驻军建筑,0,0,0,让小队进驻中立建筑物。 +;Hares Only +65=65 - 攻击特定类型的单位,22,0,1,攻击特定类型的科技单位,数值为全局TeamTargetTechnoTypes列表中的序号,从0开始 +66=66 - 驻扎路径点位置的建筑,2,0,1,派遣步兵单位驻扎路径点位置的建筑(如果有),不区分小队所属是人类或AI玩家。 +67=67 - 摄像机视角跟随,20,0,1,激活/取消摄像机追逐视角设置为该小队成员(所有成员都会标记为跟随,多成员小队慎用)。 +68=68 - 小队成员被玩家选中,20,0,1,激活/取消设置该小队成员为选中状态(所有成员都会标记为选中)。 +69=69 - 上一脚本执行成功,20,0,1,上一次脚本执行结果为成功 +70=70 - 上一脚本执行失败,20,0,1,上一次脚本执行结果为失败 +71=71 - 等待N游戏秒,20,0,1,等待N游戏秒,以保证上一条脚本执行完毕 + +[DontSaveAsWP] ; which code values indicate that the waypoint param must be saved as normal integer? +0=5 +1=9 +;2=10 +3=11 + +; events format: +; #=Description, P2 type, P1 type, TagNeeded, Obsolete,Desc2,UsedInTS,UsedInRA2,ID,[NeedsYR optional] +; negative number for type currently not supported (only Actions do support yet)! +; NOTE: Actions not listed in [Events] but in [EventsRA2] will be ignored. Use the "used in RA2" to support them. +; NOTE: To overwrite TS events with RA2 special ones, add the new event to the [EventsRA2] section. +; NOTE: ID must be the same like # + +[EventsRA2] +0=-无事件-,0,0,0,0,这是一个空的事件,无意义。请选择需要的事件。,0,1,0 +1=进入事件...,0,2,0,0,当步兵或车辆进入关联的对象时,事件被触发。此触发可关联到一所建筑或一个单元(使用“放置于地图上”按钮)。可用于工程师、间谍进入建筑,但空中单位不会触发此条件。,0,1,1 +2=侦察开始(无用),0,0,0,0,当间谍进入关联的建筑,开始侦察。,0,0,2 +3=偷窃行动(无用),0,2,0,0,当一名小偷从特定的建筑中偷窃金钱时此事件被触发。,0,0,3 +4=被游戏者发现,0,0,0,0,当关联的对象被游戏者发现时触发此事件,被发现意味着要显示附近的地图区域。,0,1,4 +5=作战方被发现...,0,2,0,0,当特定的某一作战方的任一单位或建筑被游戏者发现时触发此事件。,0,1,5 +6=受到任一作战方攻击,0,0,0,0,当单位受到一些方式的攻击时,触发此事件。间接损伤或友军开火不包括在内。,0,1,6 +7=被任一作战方摧毁,0,0,0,0,当关联的对象被摧毁时触发些事件。间接或友军开火造成被毁不包括在内。,0,1,7 +8=任何事件,0,0,0,0,当单独%1使用时,立即强制触发此事件。此动作跟流逝时间 数值1 相似。,0,1,8 +9=被摧毁%1 单位%1 全部...,0,2,0,0,当特定作战方的所有单位被摧毁时触发此事件。典型使用在游戏的结束条件中。,0,1,9 +10=被摧毁%1 建筑%1 全部...,0,2,0,0,当特定作战方的所有建筑被摧毁时触发此事件。典型使用在游戏的结束条件中。,0,1,10 +11=被摧毁%1 全部...,0,2,0,0,当特定作战方的所有对象被摧毁时触发此事件。这是常规的游戏结束触发事件(全部摧毁)。,0,1,11 +12=金钱超过...,0,6,0,0,当与事件关联的作战方的金钱超过特定值触发此事件。,0,1,12 +13=流逝时间...,0,6,0,0,当流逝的时间达到特定值触发此事件。当触发器建立时该时间初始化,当触发器持续不变或被触发时,计时器复位。,0,1,13 +14=任务时间已到,0,0,0,0,当全局的任务时间(显示在屏幕右下角)倒计时为零时触发此事件。,0,1,14 +15=被摧毁%1 建筑%1 (数量为#)...,0,6,0,0,当特定数量的%1建筑(由此触发事件特定作战方%1所有)被摧毁。,0,1,15 +16=被摧毁%1 单位%1 (数量为#)...,0,6,0,0,当特定数量的%1单位(由此触发事件特定作战方%1所有)被摧毁。,0,1,16 +17=不再有工厂,0,0,0,0,当此触发事件特定的的作战方不会再有带有Factory=的工厂(建造场,车间,空指部,兵营等)时触发此事件。一般作为 9卖掉全部建筑 的条件。,0,1,17 +18=平民撤离(无用),0,0,0,0,当平民撤离时(离开地图)触发此事件。,0,0,18 +19=建造特定类型的建筑...,0,8,0,0,当触发事件的作战方建造特定的%1类型的建筑时触发此事件。,0,1,19 +20=生产特定类型的车辆...,0,11,0,0,当触发事件的作战方生产特定的科技类型的车辆时触发此事件。,0,1,20 +21=生产特定类型的步兵...,0,10,0,0,当触发事件的作战方生产特定的步兵类单位时触发此事件。,0,1,21 +22=生产特定类型的飞机...,0,9,0,0,当触发事件的作战方生产特定的%1飞行类单位时此事件被触发。,0,1,22 +23=离开地图(小队)...,-1,7,0,0,当特定的小队离开地图时事件触发。如果小队被摧毁则不会触发事件。如果小队除一个成员名其他已被全部摧毁,而这一成员离开了地图,那么事件将被触发。,0,1,23 +24=进入某区域...,0,2,0,0,当特定作战方的一单位进入上述这一触发事件定位区域,事件被触发。此触发事件必须被定位于一个单元中,只能是一个单元。,0,1,24 +25=越过水平线...,0,2,0,0,当特定作战方的一单位越过此触发事件特定的区域水平线时,事件触发。此触发事件必须放置于一个单元内。,0,1,25 +26=越过垂直线...,0,2,0,0,当特定作战方的一单位越过此触发事件特定的区域垂直线时,事件触发。此触发事件必须放置于一个单元内。,0,1,26 +27=全局开启...,0,35,0,0,当特定的全局变量开启[1]时,事件触发。,0,1,27 +28=全局关闭...,0,35,0,0,当特定的全局变量关闭[0]时,事件触发。,0,1,28 +29=被任何事物摧毁(不包括间谍渗透),0,0,0,0,当关联对象被摧毁%1时,事件触发。不包括渗透入建筑或单位。,0,1,29 +30=电力不足...,0,2,0,0,当特定作战方停电时,事件触发。,0,1,30 +31=桥梁被摧毁,0,0,0,0,当特定的桥梁当出现缺口不能通行时,事件触发。此触发必须被用单元标记放置在目标桥梁的下方(只能放置一个单元标记)。,0,1,31 +32=建筑存在...,0,8,0,0,当此触发事件作战方有特定建筑存在于地图上时,事件触发。此建筑可以是以前就有的或由基地建造的。,0,1,32 +33=被游戏者选定,0,2,0,0,当单位被游戏者选定时,事件触发。仅使用于单位任务中。,0,1,33 +34=到达路径点附近...,48,30,0,0,当对象到达特定的路径点附近时,事件触发。参数表示距离路径点的范围半径,0,1,34 +35=敌人进入局部照明区...,0,0,0,0,当一个敌人单位进入关联建筑物投射的局部照明区时,事件触发。,0,1,35 +36=局部开启...,0,3,0,0,当特定的局部变量开启(1)时,事件触发。,0,1,36 +37=局部关闭...,0,3,0,0,当特定的局部变量关闭(0)时,事件触发。,0,1,37 +38=首次受损(仅指战斗),0,0,0,0,仅指作战时初次受到损伤时,事件触发。,0,1,38 +39=一半生命值(仅指战斗),0,0,0,0,仅指作战时受到损伤而只有一半生命值时,事件触发。,0,1,39 +40=四分之一生命值(仅指战斗),0,0,0,0,仅指作战时受到损伤而只有四分之一生命值时,事件触发。,0,1,40 +41=首次受损(任何来源),0,0,0,0,当受到任何来源的攻击而初次受损时,事件触发。,0,1,41 +42=一半生命值(任何来源),0,0,0,0,当受到任何来源的攻击而只有一半生命值时,事件触发。,0,1,42 +43=四分之一生命值(任何来源),0,0,0,0,当受到任何来源的攻击而只有四分之一生命值时,事件触发。,0,1,43 +44=受到攻击(作战方)...,0,2,0,0,当遭到特定作战方的某些单位的攻击时,事件触发。,0,1,44 +45=环境照明 <=(小于)...,0,6,0,0,当环境照明亮度低于或等于某一确定值时,事件触发。可用值介于 0 到 100之间。,0,1,45 +46=环境照明 >=(大于)...,0,6,0,0,当环境照明亮度超过或等于某一确定值时,事件触发。可用值介于 0 到 100之间。,0,1,46 +47=流逝的情节时间...,0,6,0,0,从剧情开始计算游戏的流逝时间。,0,1,47 +48=被任何事物摧毁,0,0,0,0,当被任何事物摧毁时,事件触发。此触发不能关联到航母上,否则其子机被摧毁后再摧毁航母,事件不会触发。但先摧毁航母,事件可以触发。,0,1,48 +49=关联对象拾得木箱,0,0,0,0,当此事件关联对象拾得木箱时,事件触发。,0,1,49 +50=任何单位拾得木箱,0,0,0,0,当任何单位拾得木箱时,事件触发。,0,1,50 +51=随机延时...,0,6,0,0,进行随机延时,时间值介于特定值的 50% 到 150%。联机地图使用该条件容易因为各玩家时间不对应造成“重新连线错误”。。,0,1,51 +52=金钱低于...,0,6,0,0,当此事件作战方金钱值低于特定值时,事件触发。,0,1,52 +53=间谍伪装进入(无用)...,0,2,0,0,当间谍伪装为特定作战方单位进入时,事件触发。,0,0,53 +54=间谍伪装步兵进入(无用)...,0,10,0,0,当间谍伪装为特定步兵类单位进入时,事件触发。,0,0,54 +55=被摧毁%1 单位%1 海军...,0,2,0,0,当特定作战方的所有海军单位被摧毁时,事件触发。典型应用于游戏结束条件中。,0,1,55 +56=被摧毁%1 单位%1 陆军...,0,2,0,0,当特定作战方的所有陆军单位被摧毁时,事件触发。典型应用于游戏结束条件中。,0,1,56 +57=建筑不再存在,0,8,0,0,当特定的建筑(归属于此事件作战方)不再存在于地图上时,事件触发。,0,1,57 + +; YR +58=充足能量供应...,0,2,0,0,当特定参战方的电力充足时,事件触发。,0,1,58,1 +59=进入或飞越...,0,2,0,0,当单位%1 步兵%1 或飞行器移动进入或飞越此单元时,事件触发。属于进入事件的拓展版,但不能关联建筑。,0,1,59,1 + +; 60 and 61 are tricky! They use code + 2 params... param type 46 includes the code 2, which is put in front of the 2 parameters +60=科技类型存在,46,48,0,0,地图上该科技存在的(无论所属方)数量大于等于设定值,则触发。此触发计算“正在建造中的物体”。,0,1,60,1 +61=科技类型不存在,46,48,0,0,如果地图上(包括进入载具和建筑的)此类科技的数量小于设定值,则触发,数值设为0没有意义。此触发计算“正在建造中的物体”。,0,1,61,1 +86=任务倒计时少于...,0,6,0,0,当任务计时器时间秒数小于等于特定值触发此事件。当计时器重设时该事件将会重新评估。,0,1,13 + + +; action format: +; #=Briefing, Control Code, P1 type, P2 type, P3 type, P4Type, P5Type, uses waypoint, uses tag, Obsolete, Description, used in TS, used in RA2, ID, [YR only - optional] +; if using negative number for type, param will be set to the absolute value of this number +; NOTE: Actions not listed in [Actions] but in [ActionsRA2] will be ignored. Use the "used in RA2" to support them. +; NOTE: To overwrite TS actions with RA2 special ones, add the new action to the [ActionsRA2] section. +; NOTE: ID must be the same like # + +[Chinese-EventsRA2] +0=-无事件-,0,0,0,0,这是一个空的事件,无意义。请选择需要的事件。,0,1,0 +1=进入事件...,0,2,0,0,当步兵或车辆进入关联的对象时,事件被触发。此触发可关联到一所建筑或一个单元(使用“放置于地图上”按钮)。可用于工程师、间谍进入建筑,但空中单位不会触发此条件。,0,1,1 +2=侦察开始(无用),0,0,0,0,当间谍进入关联的建筑,开始侦察。,0,0,2 +3=偷窃行动(无用),0,2,0,0,当一名小偷从特定的建筑中偷窃金钱时此事件被触发。,0,0,3 +4=被游戏者发现,0,0,0,0,当关联的对象被游戏者发现时触发此事件,被发现意味着要显示附近的地图区域。,0,1,4 +5=作战方被发现...,0,2,0,0,当特定的某一作战方的任一单位或建筑被游戏者发现时触发此事件。,0,1,5 +6=受到任一作战方攻击,0,0,0,0,当单位受到一些方式的攻击时,触发此事件。间接损伤或友军开火不包括在内。,0,1,6 +7=被任一作战方摧毁,0,0,0,0,当关联的对象被摧毁时触发些事件。间接或友军开火造成被毁不包括在内。,0,1,7 +8=任何事件,0,0,0,0,当单独%1使用时,立即强制触发此事件。此动作跟流逝时间 数值1 相似。,0,1,8 +9=被摧毁%1 单位%1 全部...,0,2,0,0,当特定作战方的所有单位被摧毁时触发此事件。典型使用在游戏的结束条件中。,0,1,9 +10=被摧毁%1 建筑%1 全部...,0,2,0,0,当特定作战方的所有建筑被摧毁时触发此事件。典型使用在游戏的结束条件中。,0,1,10 +11=被摧毁%1 全部...,0,2,0,0,当特定作战方的所有对象被摧毁时触发此事件。这是常规的游戏结束触发事件(全部摧毁)。,0,1,11 +12=金钱超过...,0,6,0,0,当与事件关联的作战方的金钱超过特定值触发此事件。,0,1,12 +13=流逝时间...,0,6,0,0,当流逝的时间达到特定值触发此事件。当触发器建立时该时间初始化,当触发器持续不变或被触发时,计时器复位。,0,1,13 +14=任务时间已到,0,0,0,0,当全局的任务时间(显示在屏幕右下角)倒计时为零时触发此事件。,0,1,14 +15=被摧毁%1 建筑%1 (数量为#)...,0,6,0,0,当特定数量的%1建筑(由此触发事件特定作战方%1所有)被摧毁。,0,1,15 +16=被摧毁%1 单位%1 (数量为#)...,0,6,0,0,当特定数量的%1单位(由此触发事件特定作战方%1所有)被摧毁。,0,1,16 +17=不再有工厂,0,0,0,0,当此触发事件特定的的作战方不会再有带有Factory=的工厂(建造场,车间,空指部,兵营等)时触发此事件。一般作为 9卖掉全部建筑 的条件。,0,1,17 +18=平民撤离(无用),0,0,0,0,当平民撤离时(离开地图)触发此事件。,0,0,18 +19=建造特定类型的建筑...,0,8,0,0,当触发事件的作战方建造特定的%1类型的建筑时触发此事件。,0,1,19 +20=生产特定类型的车辆...,0,11,0,0,当触发事件的作战方生产特定的科技类型的车辆时触发此事件。,0,1,20 +21=生产特定类型的步兵...,0,10,0,0,当触发事件的作战方生产特定的步兵类单位时触发此事件。,0,1,21 +22=生产特定类型的飞机...,0,9,0,0,当触发事件的作战方生产特定的%1飞行类单位时此事件被触发。,0,1,22 +23=离开地图(小队)...,-1,7,0,0,当特定的小队离开地图时事件触发。如果小队被摧毁则不会触发事件。如果小队除一个成员名其他已被全部摧毁,而这一成员离开了地图,那么事件将被触发。,0,1,23 +24=进入某区域...,0,2,0,0,当特定作战方的一单位进入上述这一触发事件定位区域,事件被触发。此触发事件必须被定位于一个单元中,只能是一个单元。,0,1,24 +25=越过水平线...,0,2,0,0,当特定作战方的一单位越过此触发事件特定的区域水平线时,事件触发。此触发事件必须放置于一个单元内。,0,1,25 +26=越过垂直线...,0,2,0,0,当特定作战方的一单位越过此触发事件特定的区域垂直线时,事件触发。此触发事件必须放置于一个单元内。,0,1,26 +27=全局开启...,0,35,0,0,当特定的全局变量开启[1]时,事件触发。,0,1,27 +28=全局关闭...,0,35,0,0,当特定的全局变量关闭[0]时,事件触发。,0,1,28 +29=被任何事物摧毁(不包括间谍渗透),0,0,0,0,当关联对象被摧毁%1时,事件触发。不包括渗透入建筑或单位。,0,1,29 +30=电力不足...,0,2,0,0,当特定作战方停电时,事件触发。,0,1,30 +31=桥梁被摧毁,0,0,0,0,当特定的桥梁当出现缺口不能通行时,事件触发。此触发必须被用单元标记放置在目标桥梁的下方(只能放置一个单元标记)。,0,1,31 +32=建筑存在...,0,8,0,0,当此触发事件作战方有特定建筑存在于地图上时,事件触发。此建筑可以是以前就有的或由基地建造的。,0,1,32 +33=被游戏者选定,0,2,0,0,当单位被游戏者选定时,事件触发。仅使用于单位任务中。,0,1,33 +34=到达路径点附近...,48,30,0,0,当对象到达特定的路径点附近时,事件触发。参数表示距离路径点的范围半径,0,1,34 +35=敌人进入局部照明区...,0,0,0,0,当一个敌人单位进入关联建筑物投射的局部照明区时,事件触发。,0,1,35 +36=局部开启...,0,3,0,0,当特定的局部变量开启(1)时,事件触发。,0,1,36 +37=局部关闭...,0,3,0,0,当特定的局部变量关闭(0)时,事件触发。,0,1,37 +38=首次受损(仅指战斗),0,0,0,0,仅指作战时初次受到损伤时,事件触发。,0,1,38 +39=一半生命值(仅指战斗),0,0,0,0,仅指作战时受到损伤而只有一半生命值时,事件触发。,0,1,39 +40=四分之一生命值(仅指战斗),0,0,0,0,仅指作战时受到损伤而只有四分之一生命值时,事件触发。,0,1,40 +41=首次受损(任何来源),0,0,0,0,当受到任何来源的攻击而初次受损时,事件触发。,0,1,41 +42=一半生命值(任何来源),0,0,0,0,当受到任何来源的攻击而只有一半生命值时,事件触发。,0,1,42 +43=四分之一生命值(任何来源),0,0,0,0,当受到任何来源的攻击而只有四分之一生命值时,事件触发。,0,1,43 +44=受到攻击(作战方)...,0,2,0,0,当遭到特定作战方的某些单位的攻击时,事件触发。,0,1,44 +45=环境照明 <=(小于)...,0,6,0,0,当环境照明亮度低于或等于某一确定值时,事件触发。可用值介于 0 到 100之间。,0,1,45 +46=环境照明 >=(大于)...,0,6,0,0,当环境照明亮度超过或等于某一确定值时,事件触发。可用值介于 0 到 100之间。,0,1,46 +47=流逝的情节时间...,0,6,0,0,从剧情开始计算游戏的流逝时间。,0,1,47 +48=被任何事物摧毁,0,0,0,0,当被任何事物摧毁时,事件触发。此触发不能关联到航母上,否则其子机被摧毁后再摧毁航母,事件不会触发。但先摧毁航母,事件可以触发。,0,1,48 +49=关联对象拾得木箱,0,0,0,0,当此事件关联对象拾得木箱时,事件触发。,0,1,49 +50=任何单位拾得木箱,0,0,0,0,当任何单位拾得木箱时,事件触发。,0,1,50 +51=随机延时...,0,6,0,0,进行随机延时,时间值介于特定值的 50% 到 150%。联机地图使用该条件容易因为各玩家时间不对应造成“重新连线错误”。。,0,1,51 +52=金钱低于...,0,6,0,0,当此事件作战方金钱值低于特定值时,事件触发。,0,1,52 +53=间谍伪装进入(无用)...,0,2,0,0,当间谍伪装为特定作战方单位进入时,事件触发。,0,0,53 +54=间谍伪装步兵进入(无用)...,0,10,0,0,当间谍伪装为特定步兵类单位进入时,事件触发。,0,0,54 +55=被摧毁%1 单位%1 海军...,0,2,0,0,当特定作战方的所有海军单位被摧毁时,事件触发。典型应用于游戏结束条件中。,0,1,55 +56=被摧毁%1 单位%1 陆军...,0,2,0,0,当特定作战方的所有陆军单位被摧毁时,事件触发。典型应用于游戏结束条件中。,0,1,56 +57=建筑不再存在,0,8,0,0,当特定的建筑(归属于此事件作战方)不再存在于地图上时,事件触发。,0,1,57 + +; YR +58=充足能量供应...,0,2,0,0,当特定参战方的电力充足时,事件触发。,0,1,58,1 +59=进入或飞越...,0,2,0,0,当单位%1 步兵%1 或飞行器移动进入或飞越此单元时,事件触发。属于进入事件的拓展版,但不能关联建筑。,0,1,59,1 + +; 60 and 61 are tricky! They use code + 2 params... param type 46 includes the code 2, which is put in front of the 2 parameters +60=科技类型存在,46,48,0,0,地图上该科技存在的(无论所属方)数量大于等于设定值,则触发。此触发计算“正在建造中的物体”。,0,1,60,1 +61=科技类型不存在,46,48,0,0,如果地图上(包括进入载具和建筑的)此类科技的数量小于设定值,则触发,数值设为0没有意义。此触发计算“正在建造中的物体”。,0,1,61,1 +86=任务倒计时少于...,0,6,0,0,当任务计时器时间秒数小于等于特定值触发此事件。当计时器重设时该事件将会重新评估。,0,1,13 + + +; action format: +; #=Briefing, Control Code, P1 type, P2 type, P3 type, P4Type, P5Type, uses waypoint, uses tag, Obsolete, Description, used in TS, used in RA2, ID, [YR only - optional] +; if using negative number for type, param will be set to the absolute value of this number +; NOTE: Actions not listed in [Actions] but in [ActionsRA2] will be ignored. Use the "used in RA2" to support them. +; NOTE: To overwrite TS actions with RA2 special ones, add the new action to the [ActionsRA2] section. +; NOTE: ID must be the same like # + +[ActionsRA2] +0=-无行为-,0,0,0,0,0,0,0,0,0,这是一个空的行为。就是什么也不做,相当于没有行为。为什么要用?赶紧换成一个有用的行为啦╮(╯▽╰)╭,0,1,0 +1=胜利者是...,0,2,0,0,0,0,0,0,0,胜利者必须是特定的某一方,那么游戏就会立即结束。例如%1 游戏者这一方是特定的。多人任务时,此行为会导致所有玩家失败。,0,1,1 +2=失败者是...,0,2,0,0,0,0,0,0,0,失败者必须是特定的某一方,那么游戏就会立即结束。失败者指定的某一方,游戏立即结束。例如%1 游戏者这一方是特定的。如果设为非玩家的国家,会造成任务胜利,典型例子是改动过国家顺序的MOD。多人任务时,此行为会导致所有玩家失败。,0,1,2 +3=生产开始...,0,2,0,0,0,0,0,0,0,特定作战方将开始生产单位和建筑,只对电脑方有效。,0,1,3 +4=建立小队...,-1,7,6,0,0,0,0,0,0,本触发事件的作战方(归属于作战小队设置的作战方)将会建立一个作战小队实例,但小队成员不会自动生成。小队属性不同效果不同:小队有自动建造则使用工厂建造小队(对人类玩家无效)。小队有补充新兵则召集地图上允许重组的单位,或将有可为小队重组成员的小队拆散再组建(对人类玩家有效)。小队实例的创建遵循小队类型的最大值设置。,0,1,4 +5=解散小队...,-1,7,0,0,0,0,0,0,0,将特定小队类型的全部实例销毁。小队的成员将被解散,停止执行脚本,可做为新兵加入其他小队中。,0,1,5 +6=全部搜索...,0,2,0,0,0,0,0,0,0,军队全部单位%1(归属于特定的%1作战方)进入搜索模式(Hunt)。他们将搜索并消灭敌人。设定在rules里的[Hunt]段,默认为不反击。,0,1,6 +7=援军(小队)...,-1,7,6,0,0,0,0,0,0,建立一个特定的援军小队。效果为直接刷出来,刷兵路径点为小队设置中的路径点。,0,1,7 +8=降落区闪动(路径点)...,0,0,0,0,0,0,1,0,0,TS残留功能,在特定的路径点出现一个空降舱降落区域动画,该动画为循环播放。此区域附近的地图也将会显示。,0,1,8 +9=卖掉全部建筑...,0,2,0,0,0,0,0,0,0,卖掉所有的特定建筑(为兑取现金)。典型应用在游戏中电脑玩家发动的最后攻击中。,0,1,9 +10=播放影片...,0,50,0,0,0,0,0,0,0,显示特定的影片(全屏幕)。游戏在此期间将会暂停,在播放完成后自动恢复正常。,0,1,10 +11=文本触发事件...,-4,13,2,6,0,0,0,0,0,显示字符文件的文本标志<标签>。文本在csf文件中,需要用csf编辑器编辑。以NOSTR:开头可以直接显示内容,但空格会丢失,且对多语言支持不佳;另外所属方参数仅在多人模式有意义。,0,1,11 +12=摧毁触发事件...,-2,14,0,0,0,0,0,0,0,摧毁所有特定触发事件类型的当前实例。不会影响未来的触发实例(包括正在建立中的)。,0,1,12 +13=(无效)自动创建开始...,0,2,0,0,0,0,0,0,0,特定作战方开始自动创建小队,如当建筑被攻击时自动调用空闲单位去灭掉侵略者。本事件适合电脑方开始自动建造。,0,1,13 +14=更改所属...,0,2,0,0,0,0,0,0,0,更改单位或建筑归属到特定作战方。,0,1,14 +15=允许胜利,0,0,0,0,0,0,0,0,0,清除一个“障碍”后允许游戏者胜利。障碍的数量等于已建立而拥有此行为的触发数。如果这个行为效果不好,请使用触发链和 行为1 胜利者是...。,0,1,15 +16=显示全部地图,0,0,0,0,0,0,0,0,0,为游戏者显示全部地图(清除黑幕)。联机地图使用该行为会造成“重新连线错误”。,0,1,16 +17=显示路径点周围区域...,0,30,2,0,0,0,0,0,0,为游戏者显示特定路径定一定范围内的地图区域。,0,1,17 +18=显示路径点的单元区域...,0,30,0,0,0,0,0,0,0,显示全部与特定路径点共享相同区域的单元。该行为会产生一些附带效应,使用时要谨慎!,0,1,18 +19=播放声音...,-7,16,2,0,0,0,0,0,0,播放特定的音效,定义在sound(md).ini。,0,1,19 +20=播放音乐...,-8,17,0,0,0,0,0,0,0,播放特定的音乐曲目,定义在theme(md).ini中。,0,1,20 +21=播放语音...,-6,18,2,0,0,0,0,0,0,播放特定的语音,定义在eva(md).ini中。,0,1,21 +22=强制触发事件...,-2,14,0,0,0,0,0,0,0,不管特定触发的条件,强制执行特定触发的行为。此行为有BUG,特定触发会连续触发6次,需要在特定触发中使用 行为12 摧毁触发事件 摧毁其本身来阻止其触发多次。,0,1,22 +23=计时开始,0,0,0,0,0,0,0,0,0,启动全局任务计时器,存在于游戏界面右下角。要开启一个正常的计时器,需要在一个触发内依次设置 27 计时设置、23 计时开始、103 计时器文本 三个行为。,0,1,23 +24=计时停止,0,0,0,0,0,0,0,0,0,停止全局任务计时器,此时文本会消失。,0,1,24 +25=计时增加...,0,6,0,0,0,0,0,0,0,按特定的时间增加全局任务计时器。,0,1,25 +26=计时缩短...,0,6,0,0,0,0,0,0,0,按特定的时间缩短全局任务计时器。但特定的时间值不能小于零。,0,1,26 +27=计时设置...,0,6,0,0,0,0,0,0,0,将全局任务计时器设置为特定的时间值。要开启一个正常的计时器,需要在一个触发内依次设置 27 计时设置、23 计时开始、103 计时器文本 三个行为。,0,1,27 +28=全局开启...,0,35,0,0,0,0,0,0,0,开启全局变量。全局变量被命名于 rules 文件的[VariableNames]段。全局变量可以是关闭[0]或开启[1]。,0,1,28 +29=全局关闭...,0,35,0,0,0,0,0,0,0,关闭全局变量。全局变量被命名于 rules 文件的[VariableNames]段。全局变量可以是关闭[0]或开启[1]。,0,1,29 +30=自动建设基地...,0,37,0,0,0,0,0,0,0,初始化计算机遭遇战模式的建设控制,可以是[ON]或[OFF]状态。当设为[ON]%1时,在单人任务中模式/遭遇战模式计算机将接管控制(要确保有建造厂)。,0,1,30 +31=逐单元延伸黑幕,0,0,0,0,0,0,0,0,0,一步一步(按单元)增大地图的黑幕(不可见)区域,需要相应的延伸黑幕INI设置才能使用。,0,1,31 +32=摧毁关联物体,0,0,0,0,0,0,0,0,0,摧毁该触发关联的所有建筑%1 桥梁%1 或单位。如果是桥梁,那么此触发必须被用单元标记放置在目标桥梁的下方(只能放置一个单元标记)。,0,1,32 +33=添加一次特定的超级武器...,0,36,0,0,0,0,0,0,0,为拥有此触发的参战方添加一次(只一次)特定的超级武器。,0,1,33 +34=永久添加特定的超级武器...,0,36,0,0,0,0,0,0,0,为拥有此触发的参战方添加永久的超级武器。,0,1,34 +35=超武首选目标为特定建筑...,0,8,0,0,0,0,0,0,0,指定拥有此触发的参战方超级武器攻击时的首选目标,其参数为[BuildingTypes]里的ID。,0,1,35 +36=全部更改归属...,0,2,0,0,0,0,0,0,0,此触发的参战方的所有对象更改归属到特定参战方。,0,1,36 +37=结盟...,0,2,0,0,0,0,0,0,0,使此触发的参战方与特定参战方结盟,这个结盟是双向的。,0,1,37 +38=成为敌人...,0,2,0,0,0,0,0,0,0,使此触发的参战方与特定参战方不结盟(宣战),这个宣战是双向的。被宣战的参战方若是电脑,其会判定为存在敌人,进攻用AI触发会被激活。,0,1,38 +39=更改视野等级(无用)...,0,6,0,0,0,0,0,0,0,更改游戏者雷达图的视野等级。使用 1 为超常视野%1 2 为缩小视野。,0,0,39 +40=调整玩家视野...,0,0,21,22,23,24,0,0,0,调整玩家的地图可到达范围。参数:左%1上%1宽%1高,在这里左%1上限制左上角位置, 宽%1高限制右下角。,0,1,40 +41=播放动画在...,0,25,6,0,0,0,1,0,0,在特定的单元播放特定的动画。这里填的动画ID并不一定是Rules里指定的,而是实际加载进游戏内存的顺序。如果有跳号或者不是从0开始的,请使用第三方工具获得正确序号或者确保从Rules读取的序号是修正过的。此外,这里的数值1表示如果原地存在一个类型相同的动画则跳过创建;为2表示删除前一个存在的同类型动画。为6表示重建动画。,0,1,41 +42=爆炸在...,0,41,0,0,0,0,1,0,0,使用特定的武器,在特定的单元%1 产生一个爆炸。用数字指定特定的武器,在Ares中遵循[WeaponTypes]列表的注册顺序,不在列表内的武器无法直接知道注册顺序。,0,1,42 +43=在指定路径点创建一个Voxel碎片,0,29,0,0,0,0,1,0,0,在指定地点创造一个Voxel碎片,ID号在rules的[VoxelAnims]下有定义。其中8是陨石。,0,1,43 +44=离子风暴开始(无用),0,6,0,0,0,0,0,0,0,TS残留功能,启动离子风暴,按特定的游戏帧。,0,0,44 +45=离子风暴结束(无用),0,0,0,0,0,0,0,0,0,TS残留功能,结束离子风暴进程。,0,0,45 +46=禁止用户输入,0,0,0,0,0,0,0,0,0,禁止用户输入,失去鼠标无法控制。,0,1,46 +47=允许用户输入,0,0,0,0,0,0,0,0,0,允许用户输入,取消动作46的效果。,0,1,47 +48=移动并居中视野...,0,28,0,0,0,0,1,0,0,将战术视野移动到特定的路径点,速度为1-4的一个值,太大会造成卡屏,无法移动视野。,0,1,48 +49=放大视野,0,0,0,0,0,0,0,0,0,放大战术视野,减少分辨率,同时不能输入。,0,1,49 +50=缩小视野,0,0,0,0,0,0,0,0,0,取消49放大视野的作用,缩小战术视野,增加分辨率,回复输入。,0,1,50 +51=重置全部地图的黑幕(未探测区),0,0,0,0,0,0,0,0,0,遮蔽全部地图,0,1,51 +52=更改照明状态(无用),0,42,0,0,0,0,0,0,0,更改某建筑的局部照明方式。此触发与某一可生产局部照明的建筑相关联。,0,0,52 +53=允许事件触发,-2,14,0,0,0,0,0,0,0,允许被勾选为禁止或触发-54设为禁止的触发运行。,0,1,53 +54=禁止事件触发,-2,14,0,0,0,0,0,0,0,禁止可以正常或被触发-53设为可以运行的触发停止运行。,0,1,54 +55=建立雷达事件,0,51,2,0,0,0,1,0,0,在特定的路径点建立雷达事件。0、3、4红圈 1、2黄圈 5篮圈。,0,1,55 +56=开启局部变量...,0,3,0,0,0,0,0,0,0,开启局部变量。局部变量可以是 关闭[0] 或 开启[1]。,0,1,56 +57=关闭局部变量...,0,3,0,0,0,0,0,0,0,关闭局部变量。局部变量可以是 关闭[0] 或 开启[1]。,0,1,57 +58=流星雨在...,0,44,0,0,0,0,1,0,0,在特定的路径点建立显示一个流星雨事件。,0,0,58 +59=在特定路径点减少矿石,0,30,0,0,0,0,0,0,0,减少特定路径点周围的矿石。,0,1,59 +60=变卖关联建筑,0,0,0,0,0,0,0,0,0,卖掉与此触发关联的建筑。,0,1,60 +61=关闭关联的建筑,0,0,0,0,0,0,0,0,0,关闭与此触发关联的建筑的供能,建筑将强制失去电力供应。效果与建筑中耗能=0相同。,0,1,61 +62=启动关联的建筑,0,0,0,0,0,0,0,0,0,启动与此触发关联的建筑的供能,取消行为 61关闭关联的建筑 的效果。效果与建筑中耗能=1相同。,0,1,62 +63=给予100损伤在...,0,30,0,0,0,0,0,0,0,在特定的位置给予 100 点爆炸伤害,不会播放任何动画。,0,1,63 +64=闪光(较小)(无用)...,0,30,0,0,0,0,0,0,0,在特定区域显示一个较小的闪光。,0,0,64 +65=闪光(中等)(无用)...,0,30,0,0,0,0,0,0,0,在特定区域显示一个中等的闪光。,0,0,65 +66=闪光(较大)(无用)...,0,30,0,0,0,0,0,0,0,在特定区域显示一个较大的闪光。,0,0,66 +67=宣告胜利,0,0,0,0,0,0,0,0,0,宣告游戏者胜利,但不会显示“任务完成”的图示。,0,1,67 +68=宣告失败,0,0,0,0,0,0,0,0,0,宣告游戏者失败,但不会显示“任务失败”的图示。,0,1,68 +69=游戏任务强制胜利,0,0,0,0,0,0,0,0,0,强制结束游戏任务,显示“任务完成”的图示。,0,1,69 +70=销毁标签及相关触发...,-3,38,0,0,0,0,0,0,0,销毁指定标签所有实例和所有关联的触发。,0,1,70 +71=设置环境步幅...,0,45,0,0,0,0,0,0,0,设置环境亮度淡入步幅值,一般使用1020000000。行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,71 +72=设置环境速率...,0,45,0,0,0,0,0,0,0,设置环境亮度淡入速率,一般使用1036000000。行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,72 +73=设置环境亮度...,0,6,0,0,0,0,0,0,0,用新的环境亮度等级淡入替代原亮度,使用一个整数(0-100)代表光照设置里的(0.01-1)行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,73 +74=AI 触发开始...,0,2,0,0,0,0,0,0,0,启动特定参战方的 AI 触发。,0,1,74 +75=AI 触发停止...,0,2,0,0,0,0,0,0,0,停止特定参战方的 AI 触发。,0,1,75 +76=AI 触发小队比例...,0,6,0,0,0,0,0,0,0,AI 进行 AI 触发小队建造的比例,数值越大越倾向于使用局部的AI触发(在地图内),越小越倾向于使用全局的AI触发(在ai(md).ini中)。100 = 全部 局部AI触发小队%1 0 = 全部 全局AI触发小队。,0,1,76 +77=飞行作战小队比例...,0,6,0,0,0,0,0,0,0,AI 进行飞行小队建造的比例(100 = 全部小队%1 0 = 全部随机)。,0,1,77 +78=步兵作战小队比例...,0,6,0,0,0,0,0,0,0,AI 进行步兵小队建造的比例(100 = 全部小队%1 0 = 全部随机)。,0,1,78 +79=作战小队单位比例...,0,6,0,0,0,0,0,0,0,AI 进行小队单位建造的比例(100 = 全部小队%1 0 = 全部随机)。,0,1,79 +80=援军(小队)[在路径点]...,-1,7,6,0,0,0,1,0,0,在特定的路径点区域建造援军作战小队。效果为直接刷出来,刷兵路径点为行为设置中的路径点。,0,1,80 +81=自我唤醒,0,0,0,0,0,0,0,0,0,唤醒所有休息(sleep)或无指令(harmless)单位进行防卫模式。,0,1,81 +82=唤醒所有休息单位,0,0,0,0,0,0,0,0,0,唤醒所有休息(sleep)的单位模式。,0,1,82 +83=唤醒所有无指令单位,0,0,0,0,0,0,0,0,0,唤醒所有无指令(harmless)的单位模式。,0,1,83 +84=唤醒某小组单位...,0,6,0,0,0,0,0,0,0,唤醒有该小组编号的无指令(harmless)单位。,0,1,84 +85=矿脉生长...,0,37,0,0,0,0,0,0,0,控制矿脉是否生长。,0,1,85 +86=矿石生长...,0,37,0,0,0,0,0,0,0,控制矿石是否生长。,0,1,86 +87=冰层延伸(无用)...,0,37,0,0,0,0,0,0,0,控制结冰面积是否增长。,0,0,87 +88=粒子动画...,0,26,0,0,0,0,1,0,0,在特定区域显示粒子动画。,0,1,88 +89=删除粒子动画...,0,0,0,0,0,0,1,0,0,删除特定区域的粒子动画。,0,1,89 +90=闪电打击...,0,0,0,0,0,0,1,0,0,单个的离子风暴闪电打击。目前只有在爱伦斯(Alliance)编辑器上才可以正常使用这个功能。,0,1,90 +91=Go Berzerk(无用),0,0,0,0,0,0,0,0,0,Attached object (cyborg) goes berzerk.,0,0,91 +92=启动烈火风暴防御系统(无用),0,0,0,0,0,0,0,0,0,启动某一作战方的烈火风暴防御系统。,0,0,92 +93=关闭烈火风暴防御系统(无用),0,0,0,0,0,0,0,0,0,关闭某一作战方的烈火风暴防御系统。,0,0,93 +94=离子炮打击(无用),0,0,0,0,0,0,1,0,0,在特定的路径点实施离子炮打击。,0,0,94 +95=核弹打击...,0,0,0,0,0,0,1,0,0,在特定的路径点边缘实施核弹打击。,0,1,95 +96=化学导弹打击(无用),0,0,0,0,0,0,1,0,0,在特定的路径点实施化学导弹打击。,0,0,96 +97=Toggle Train Cargo(无用),0,0,0,0,0,0,0,0,0,TS残留功能,列车卸货,切换状态,0,0,97 +98=播入音效(随机)...,-7,16,0,0,0,0,0,0,0,在随机的路径点播放音效。这个动作不需要设置重复触发,启动以后即在随机路径点不停播放这个音效,间隔时间随机。,0,1,98 +99=播放音效...,-7,16,0,0,0,0,1,0,0,在特定的路径点播放特定的音效。这个动作不需要设置重复触发,启动以后即在特定路径点不停播放这个音效,间隔时间随机。,0,1,99 +100=在雷达窗口播放影片...,0,50,0,0,0,0,0,0,0,显示特定的开场影片(在小屏幕)。游戏者仍能对步兵和单位进行控制。,0,1,100 +101=重置特定路径点的黑幕(未探测区)...,0,30,0,0,0,0,0,0,0,用黑幕覆盖特定的路径点。,0,1,101 +102=闪电风暴打击...,0,0,0,0,0,0,1,0,0,此触发的所有者在特定的路径点实施闪电风暴打击。,0,1,102 +103=计时器文本...,-4,13,6,0,0,0,0,0,0,计时器显示什么文本标签(如文本是在csf文件中)。要开启一个正常的计时器,需要在一个触发内依次设置 27 计时设置、23 计时开始、103 计时器文本 三个行为。,0,1,103 +104=闪亮小队...,-5,7,0,0,0,0,1,0,0,用特定的帧数闪亮特定的小队,填600以上效果较好。,0,1,104 +105=显示对话注释框...(无用),-1,7,0,0,0,0,0,0,0,TS残留功能,在特定小队的上面显示对话注释框。,0,0,105 +106=设置科技等级(Techno)[Level]... ,-9,46,0,0,0,0,1,0,0,设置特定的单位或建筑到特定的科技等级。,0,1,106 +107=超时空传送援军(小队)[在路径点]... ,-1,7,0,0,0,0,1,0,0,传送援军小队到特定的路径点区域,并制造一个超时空穿送的效果,而超时空传送显示的动画需要与事件41连用。,0,1,107 +108=建立小木箱...,0,31,0,0,0,0,1,0,0,在特定的路径点建立一个特定内容的工具木箱。0金钱(大的)%1 1获得随机单位%1 2医疗%1 3获得隐形能力%1 4爆炸%1 5爆炸(大的)%1 6金钱%1 7黑幕重置%1 8清除黑幕%1 9护甲加强%1 10速度加强%1 11攻击加强%1 12获得核弹一枚%1 13空箱子%1 14升级%1 15空箱子%1 16瓦斯气体%1 17一片矿。,0,1,108 +109=铁幕装置作用于...,0,4,0,0,0,0,1,0,0,将铁幕装置作用于特定的路径点。,0,1,109 +110=游戏暂停(秒)...,0,6,0,0,0,0,0,0,0,使游戏暂停特定的秒数。,0,1,110 +111=驱逐占据者,0,0,0,0,0,0,0,0,0,本行为将会从城市关联的建筑中驱逐占据的作战单位。要使用本行为,必须在每一个建筑上关联一个单独的标签,重复的标签无用。默认每一个触发对应一个标签,你可以使用 编辑→标签编辑 来增加这个触发的标签。,0,1,111 +112=立即居中当前视野到特定路径点...,0,0,0,0,0,0,1,0,0,立即将战术视野移动到特定的路径点。,0,1,112 +113=欢呼,0,2,0,0,0,0,0,0,0,使所有步兵单位进行欢呼动作。,0,1,113 +114=设置建造栏到(0-3)...,0,6,0,0,0,0,0,0,0,强制使建造栏跳转到特定的界面。0建筑界面%1 1防御界面%1 2步兵界面%1 3车辆界面。,0,1,114 +115=闪动建造栏中的单位,-9,46,0,0,0,0,1,0,0,闪动建造栏中的单位,数值为闪烁时间,一般为120比较合适。,0,1,115 +116=停止特定路径点的音效...,0,30,0,0,0,0,1,0,0,这个触发可以停止在某个路径点播放的音效。,0,1,116 +117=播放开场影片(游戏暂停)...,0,50,0,0,0,0,0,0,0,显示特定的游戏开场影片。影片播放时,游戏暂停。播放影片期间游戏者不可进行操作。,0,1,117 +118=清除全部污染,0,0,0,0,0,0,0,0,0,将从地图上删除所有污染对象。,0,1,118 +119=将特定作战方全部摧毁...,0,2,6,0,0,0,0,0,0,消灭特定作战方的所有建筑、单位等。数值为1时表示直接移除单位不产生任何破坏效果。,0,1,119 +120=将特定方建筑全部摧毁...,0,2,0,0,0,0,0,0,0,摧毁特定作战方的所有建筑。,0,1,120 +121=将特定方陆军单位全部摧毁...,0,2,0,0,0,0,0,0,0,消灭特定作战方的所有陆军单位。,0,1,121 +122=将特定方海军单位全部摧毁...,0,2,0,0,0,0,0,0,0,消灭特定作战方的所有海军单位。,0,1,122 +123=心灵控制基地...,0,2,0,0,0,0,0,0,0,此触发的所有者将会通过心灵控制敌方所有建筑。,0,1,123 +124=解除基地的心灵控制...,0,2,0,0,0,0,0,0,0,此触发的所有者将重新取得被敌方心灵控制的所有建筑的作战控制权。,0,1,124 +125=将建筑建于...,-10,47,6,0,0,0,1,0,0,此触发的所有者将在本路径点获得该类型的建筑,如果目标路径点有建筑则不会建造,如果目标路径点有单位则会强行弹开单位。此动作的数值参数用于控制是否禁止建造动画,为1则不播放。,0,1,125 +126=恢复至游戏初始建筑单位状态...,0,2,0,0,0,0,0,0,0,此作战方游戏初始的所有建筑和单位将会重建。会引起单位冲击和覆盖图被清除。,0,1,126 +127=超时空事件效果 ...,0,6,0,0,0,0,0,0,0,将会用多帧画面全屏幕平铺显示超时空穿梭事件效果,数值一般为60,填太大会游戏卡死。,0,1,127 +128=全部超时空传送到...,0,0,0,0,0,0,1,0,0,此触发的作战方所有单位将被传送到特定的路径点。,0,1,128 +129=设置超武充能完成度,-11,20,0,0,0,0,1,0,0,此触发的所有者如果有超级武器,那么此超级武器的充能状态会被设置为“完成了”百分之几 。使用一个整数(0-100)。,0,1,129 + +; YR +130=恢复初始建筑...,0,2,0,0,0,0,0,0,0,该参战方游戏初始的所有建筑将会被修复或重建。 会引起单位冲击和覆盖图清除。,0,1,130,1 +131=闪亮特定类型的建筑...,-9,47,0,0,0,0,1,0,0,该触发所属的所有特定类型的建筑将闪亮特定的时间。,0,1,131,1 +132=设置超武充能时间,-11,20,0,0,0,0,1,0,0,设置任何数值,设置触发所属方超武至充能完毕的剩余时间,使用画面帧数(1游戏秒=15帧),将会覆盖ini设置的RechargeTime,0,1,132,1 +133=复位超级武器充能时间,0,20,0,0,0,0,0,0,0,重置触发所属方超级武器充能时间,使用ini设置的RechargeTime。,0,1,133,1 +134=超级武器复位,0,36,0,0,0,0,0,0,0,就与间谍进入超级武器一样,超级武器将会复位(重新开始倒计时)。,0,1,134,1 +135=超武首选目标路径点...,0,0,0,0,0,0,1,0,0,该触发的所有者会将超级武器的攻击目标指向此路径点。,0,1,135,1 +136=超武首选目标路径点清除,0,0,0,0,0,0,0,0,0,该触发的所有者将返回,正常确定其超级武器的攻击目标。,0,1,136,1 +137=基地中心单元设置,0,0,0,0,0,0,1,0,0,此触发的所有者将认为此路径点为其基地的中心单元,脚本里“集合在基地附近”将会集合在这个路径点附近。,0,1,137,1 +138=基地中心单元清除,0,0,0,0,0,0,0,0,0,此触发的所有者将返回,并去计算其中心位置。,0,1,138,1 +139=雷达信息中断,0,6,0,0,0,0,0,0,0,此触发的所有者的雷达将中断一段时间,填太小会没有效果。,0,1,139,1 +140=设置防御目标单元,0,0,0,0,0,0,1,0,0,此触发的所有者将自动对特定的路径点区域进行防御行动。,0,1,140,1 +141=清除防御目标单元,0,0,0,0,0,0,0,0,0,此触发的所有者将返回到正常情况下的防御目标区域。,0,1,141,1 +142=重绘红色,0,6,0,0,0,0,0,0,0,重绘屏幕的红色部分。采用百分比,这里的50代表光照里的0.5。行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,142,1 +143=重绘绿色,0,6,0,0,0,0,0,0,0,重绘屏幕的绿色部分。采用百分比,这里的50代表光照里的0.5。行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,143,1 +144=重绘蓝色,0,6,0,0,0,0,0,0,0,重绘屏幕的蓝色部分。采用百分比,这里的50代表光照里的0.5。行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,144,1 +145=战术视野跳转至基地,0,0,0,0,0,0,0,0,0,和按下“H”键效果相同。,0,1,145,1 +;bellow only added in hares +146=使步兵单位进入惊恐状态,0,2,0,0,0,0,0,0,0,使指定作战方所有的步兵单位进入惊恐动作状态(如果支持),0,1,146,1 +147=在战场区域全屏铺展绘制动画,0,25,6,0,0,0,0,0,0,在屏幕固定位置平铺绘制多帧图像,0,1,147,1 +148=允许/禁止所属方共享视野,0,2,15,0,0,0,0,0,0,允许/禁止指定所属方共享视野给友方,0,1,148,1 +149=设置触发随机分量,-2,14,6,0,0,0,0,0,0,设置指定触发的随机分量,与行为150配合使用;负数不会更改触发的分量,可用于行为150罗列事件使用。,0,1,149,1 +150=随机允许下列触发,0,0,0,0,0,0,0,0,0,将该行为(从属于当前触发事件)以下的多个行为所指定的触发按照分量随机抽取,并将其禁止状态改为允许,与行为149配合使用。,0,1,150,1 +151=建筑炮塔转动,0,6,15,0,0,0,0,0,0,将触发绑定的建筑转动其炮塔特定方向,如果布尔值为真,则表示这个转动量是0-255之间的绝对方向数,0,1,151,1 +152=全部归入小队...,-1,7,2,0,0,0,0,0,0,作战方%1全部单位被强制招募进指定小队类型。这些单位将被这同一个小队实例管理。,0,1,152 +153=控制存档按钮可用性,0,15,0,0,0,0,0,0,0,控制存档按钮是否为允许/禁止状态,0,1,153,1 +154=设置参战方地图边界,0,6,0,0,0,0,0,0,0,设置参战方的地图边界,仅能为0-4之间的一个枚举值,0,1,154,1 + + +[Chinese-ActionsRA2] +0=-无行为-,0,0,0,0,0,0,0,0,0,这是一个空的行为。就是什么也不做,相当于没有行为。为什么要用?赶紧换成一个有用的行为啦╮(╯▽╰)╭,0,1,0 +1=胜利者是...,0,2,0,0,0,0,0,0,0,胜利者必须是特定的某一方,那么游戏就会立即结束。例如%1 游戏者这一方是特定的。多人任务时,此行为会导致所有玩家失败。,0,1,1 +2=失败者是...,0,2,0,0,0,0,0,0,0,失败者必须是特定的某一方,那么游戏就会立即结束。失败者指定的某一方,游戏立即结束。例如%1 游戏者这一方是特定的。如果设为非玩家的国家,会造成任务胜利,典型例子是改动过国家顺序的MOD。多人任务时,此行为会导致所有玩家失败。,0,1,2 +3=生产开始...,0,2,0,0,0,0,0,0,0,特定作战方将开始生产单位和建筑,只对电脑方有效。,0,1,3 +4=建立小队...,-1,7,6,0,0,0,0,0,0,本触发事件的作战方(归属于作战小队设置的作战方)将会建立一个作战小队实例,但小队成员不会自动生成。小队属性不同效果不同:小队有自动建造则使用工厂建造小队(对人类玩家无效)。小队有补充新兵则召集地图上允许重组的单位,或将有可为小队重组成员的小队拆散再组建(对人类玩家有效)。小队实例的创建遵循小队类型的最大值设置。,0,1,4 +5=解散小队...,-1,7,0,0,0,0,0,0,0,将特定小队类型的全部实例销毁。小队的成员将被解散,停止执行脚本,可做为新兵加入其他小队中。,0,1,5 +6=全部搜索...,0,2,0,0,0,0,0,0,0,军队全部单位%1(归属于特定的%1作战方)进入搜索模式(Hunt)。他们将搜索并消灭敌人。设定在rules里的[Hunt]段,默认为不反击。,0,1,6 +7=援军(小队)...,-1,7,6,0,0,0,0,0,0,建立一个特定的援军小队。效果为直接刷出来,刷兵路径点为小队设置中的路径点。,0,1,7 +8=降落区闪动(路径点)...,0,0,0,0,0,0,1,0,0,TS残留功能,在特定的路径点出现一个空降舱降落区域动画,该动画为循环播放。此区域附近的地图也将会显示。,0,1,8 +9=卖掉全部建筑...,0,2,0,0,0,0,0,0,0,卖掉所有的特定建筑(为兑取现金)。典型应用在游戏中电脑玩家发动的最后攻击中。,0,1,9 +10=播放影片...,0,50,0,0,0,0,0,0,0,显示特定的影片(全屏幕)。游戏在此期间将会暂停,在播放完成后自动恢复正常。,0,1,10 +11=文本触发事件...,-4,13,2,6,0,0,0,0,0,显示字符文件的文本标志<标签>。文本在csf文件中,需要用csf编辑器编辑。以NOSTR:开头可以直接显示内容,但空格会丢失,且对多语言支持不佳;另外所属方参数仅在多人模式有意义。,0,1,11 +12=摧毁触发事件...,-2,14,0,0,0,0,0,0,0,摧毁所有特定触发事件类型的当前实例。不会影响未来的触发实例(包括正在建立中的)。,0,1,12 +13=(无效)自动创建开始...,0,2,0,0,0,0,0,0,0,特定作战方开始自动创建小队,如当建筑被攻击时自动调用空闲单位去灭掉侵略者。本事件适合电脑方开始自动建造。,0,1,13 +14=更改所属...,0,2,0,0,0,0,0,0,0,更改单位或建筑归属到特定作战方。,0,1,14 +15=允许胜利,0,0,0,0,0,0,0,0,0,清除一个“障碍”后允许游戏者胜利。障碍的数量等于已建立而拥有此行为的触发数。如果这个行为效果不好,请使用触发链和 行为1 胜利者是...。,0,1,15 +16=显示全部地图,0,0,0,0,0,0,0,0,0,为游戏者显示全部地图(清除黑幕)。联机地图使用该行为会造成“重新连线错误”。,0,1,16 +17=显示路径点周围区域...,0,30,2,0,0,0,0,0,0,为游戏者显示特定路径定一定范围内的地图区域。,0,1,17 +18=显示路径点的单元区域...,0,30,0,0,0,0,0,0,0,显示全部与特定路径点共享相同区域的单元。该行为会产生一些附带效应,使用时要谨慎!,0,1,18 +19=播放声音...,-7,16,2,0,0,0,0,0,0,播放特定的音效,定义在sound(md).ini。,0,1,19 +20=播放音乐...,-8,17,0,0,0,0,0,0,0,播放特定的音乐曲目,定义在theme(md).ini中。,0,1,20 +21=播放语音...,-6,18,2,0,0,0,0,0,0,播放特定的语音,定义在eva(md).ini中。,0,1,21 +22=强制触发事件...,-2,14,0,0,0,0,0,0,0,不管特定触发的条件,强制执行特定触发的行为。此行为有BUG,特定触发会连续触发6次,需要在特定触发中使用 行为12 摧毁触发事件 摧毁其本身来阻止其触发多次。,0,1,22 +23=计时开始,0,0,0,0,0,0,0,0,0,启动全局任务计时器,存在于游戏界面右下角。要开启一个正常的计时器,需要在一个触发内依次设置 27 计时设置、23 计时开始、103 计时器文本 三个行为。,0,1,23 +24=计时停止,0,0,0,0,0,0,0,0,0,停止全局任务计时器,此时文本会消失。,0,1,24 +25=计时增加...,0,6,0,0,0,0,0,0,0,按特定的时间增加全局任务计时器。,0,1,25 +26=计时缩短...,0,6,0,0,0,0,0,0,0,按特定的时间缩短全局任务计时器。但特定的时间值不能小于零。,0,1,26 +27=计时设置...,0,6,0,0,0,0,0,0,0,将全局任务计时器设置为特定的时间值。要开启一个正常的计时器,需要在一个触发内依次设置 27 计时设置、23 计时开始、103 计时器文本 三个行为。,0,1,27 +28=全局开启...,0,35,0,0,0,0,0,0,0,开启全局变量。全局变量被命名于 rules 文件的[VariableNames]段。全局变量可以是关闭[0]或开启[1]。,0,1,28 +29=全局关闭...,0,35,0,0,0,0,0,0,0,关闭全局变量。全局变量被命名于 rules 文件的[VariableNames]段。全局变量可以是关闭[0]或开启[1]。,0,1,29 +30=自动建设基地...,0,37,0,0,0,0,0,0,0,初始化计算机遭遇战模式的建设控制,可以是[ON]或[OFF]状态。当设为[ON]%1时,在单人任务中模式/遭遇战模式计算机将接管控制(要确保有建造厂)。,0,1,30 +31=逐单元延伸黑幕,0,0,0,0,0,0,0,0,0,一步一步(按单元)增大地图的黑幕(不可见)区域,需要相应的延伸黑幕INI设置才能使用。,0,1,31 +32=摧毁关联物体,0,0,0,0,0,0,0,0,0,摧毁该触发关联的所有建筑%1 桥梁%1 或单位。如果是桥梁,那么此触发必须被用单元标记放置在目标桥梁的下方(只能放置一个单元标记)。,0,1,32 +33=添加一次特定的超级武器...,0,36,0,0,0,0,0,0,0,为拥有此触发的参战方添加一次(只一次)特定的超级武器。,0,1,33 +34=永久添加特定的超级武器...,0,36,0,0,0,0,0,0,0,为拥有此触发的参战方添加永久的超级武器。,0,1,34 +35=超武首选目标为特定建筑...,0,8,0,0,0,0,0,0,0,指定拥有此触发的参战方超级武器攻击时的首选目标,其参数为[BuildingTypes]里的ID。,0,1,35 +36=全部更改归属...,0,2,0,0,0,0,0,0,0,此触发的参战方的所有对象更改归属到特定参战方。,0,1,36 +37=结盟...,0,2,0,0,0,0,0,0,0,使此触发的参战方与特定参战方结盟,这个结盟是双向的。,0,1,37 +38=成为敌人...,0,2,0,0,0,0,0,0,0,使此触发的参战方与特定参战方不结盟(宣战),这个宣战是双向的。被宣战的参战方若是电脑,其会判定为存在敌人,进攻用AI触发会被激活。,0,1,38 +39=更改视野等级(无用)...,0,6,0,0,0,0,0,0,0,更改游戏者雷达图的视野等级。使用 1 为超常视野%1 2 为缩小视野。,0,0,39 +40=调整玩家视野...,0,0,21,22,23,24,0,0,0,调整玩家的地图可到达范围。参数:左%1上%1宽%1高,在这里左%1上限制左上角位置, 宽%1高限制右下角。,0,1,40 +41=播放动画在...,0,25,6,0,0,0,1,0,0,在特定的单元播放特定的动画。这里填的动画ID并不一定是Rules里指定的,而是实际加载进游戏内存的顺序。如果有跳号或者不是从0开始的,请使用第三方工具获得正确序号或者确保从Rules读取的序号是修正过的。此外,这里的数值1表示如果原地存在一个类型相同的动画则跳过创建;为2表示删除前一个存在的同类型动画。为6表示重建动画。,0,1,41 +42=爆炸在...,0,41,0,0,0,0,1,0,0,使用特定的武器,在特定的单元%1 产生一个爆炸。用数字指定特定的武器,在Ares中遵循[WeaponTypes]列表的注册顺序,不在列表内的武器无法直接知道注册顺序。,0,1,42 +43=在指定路径点创建一个Voxel碎片,0,29,0,0,0,0,1,0,0,在指定地点创造一个Voxel碎片,ID号在rules的[VoxelAnims]下有定义。其中8是陨石。,0,1,43 +44=离子风暴开始(无用),0,6,0,0,0,0,0,0,0,TS残留功能,启动离子风暴,按特定的游戏帧。,0,0,44 +45=离子风暴结束(无用),0,0,0,0,0,0,0,0,0,TS残留功能,结束离子风暴进程。,0,0,45 +46=禁止用户输入,0,0,0,0,0,0,0,0,0,禁止用户输入,失去鼠标无法控制。,0,1,46 +47=允许用户输入,0,0,0,0,0,0,0,0,0,允许用户输入,取消动作46的效果。,0,1,47 +48=移动并居中视野...,0,28,0,0,0,0,1,0,0,将战术视野移动到特定的路径点,速度为1-4的一个值,太大会造成卡屏,无法移动视野。,0,1,48 +49=放大视野,0,0,0,0,0,0,0,0,0,放大战术视野,减少分辨率,同时不能输入。,0,1,49 +50=缩小视野,0,0,0,0,0,0,0,0,0,取消49放大视野的作用,缩小战术视野,增加分辨率,回复输入。,0,1,50 +51=重置全部地图的黑幕(未探测区),0,0,0,0,0,0,0,0,0,遮蔽全部地图,0,1,51 +52=更改照明状态(无用),0,42,0,0,0,0,0,0,0,更改某建筑的局部照明方式。此触发与某一可生产局部照明的建筑相关联。,0,0,52 +53=允许事件触发,-2,14,0,0,0,0,0,0,0,允许被勾选为禁止或触发-54设为禁止的触发运行。,0,1,53 +54=禁止事件触发,-2,14,0,0,0,0,0,0,0,禁止可以正常或被触发-53设为可以运行的触发停止运行。,0,1,54 +55=建立雷达事件,0,51,2,0,0,0,1,0,0,在特定的路径点建立雷达事件。0、3、4红圈 1、2黄圈 5篮圈。,0,1,55 +56=开启局部变量...,0,3,0,0,0,0,0,0,0,开启局部变量。局部变量可以是 关闭[0] 或 开启[1]。,0,1,56 +57=关闭局部变量...,0,3,0,0,0,0,0,0,0,关闭局部变量。局部变量可以是 关闭[0] 或 开启[1]。,0,1,57 +58=流星雨在...,0,44,0,0,0,0,1,0,0,在特定的路径点建立显示一个流星雨事件。,0,0,58 +59=在特定路径点减少矿石,0,30,0,0,0,0,0,0,0,减少特定路径点周围的矿石。,0,1,59 +60=变卖关联建筑,0,0,0,0,0,0,0,0,0,卖掉与此触发关联的建筑。,0,1,60 +61=关闭关联的建筑,0,0,0,0,0,0,0,0,0,关闭与此触发关联的建筑的供能,建筑将强制失去电力供应。效果与建筑中耗能=0相同。,0,1,61 +62=启动关联的建筑,0,0,0,0,0,0,0,0,0,启动与此触发关联的建筑的供能,取消行为 61关闭关联的建筑 的效果。效果与建筑中耗能=1相同。,0,1,62 +63=给予100损伤在...,0,30,0,0,0,0,0,0,0,在特定的位置给予 100 点爆炸伤害,不会播放任何动画。,0,1,63 +64=闪光(较小)(无用)...,0,30,0,0,0,0,0,0,0,在特定区域显示一个较小的闪光。,0,0,64 +65=闪光(中等)(无用)...,0,30,0,0,0,0,0,0,0,在特定区域显示一个中等的闪光。,0,0,65 +66=闪光(较大)(无用)...,0,30,0,0,0,0,0,0,0,在特定区域显示一个较大的闪光。,0,0,66 +67=宣告胜利,0,0,0,0,0,0,0,0,0,宣告游戏者胜利,但不会显示“任务完成”的图示。,0,1,67 +68=宣告失败,0,0,0,0,0,0,0,0,0,宣告游戏者失败,但不会显示“任务失败”的图示。,0,1,68 +69=游戏任务强制胜利,0,0,0,0,0,0,0,0,0,强制结束游戏任务,显示“任务完成”的图示。,0,1,69 +70=销毁标签及相关触发...,-3,38,0,0,0,0,0,0,0,销毁指定标签所有实例和所有关联的触发。,0,1,70 +71=设置环境步幅...,0,45,0,0,0,0,0,0,0,设置环境亮度淡入步幅值,一般使用1020000000。行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,71 +72=设置环境速率...,0,45,0,0,0,0,0,0,0,设置环境亮度淡入速率,一般使用1036000000。行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,72 +73=设置环境亮度...,0,6,0,0,0,0,0,0,0,用新的环境亮度等级淡入替代原亮度,使用一个整数(0-100)代表光照设置里的(0.01-1)行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,73 +74=AI 触发开始...,0,2,0,0,0,0,0,0,0,启动特定参战方的 AI 触发。,0,1,74 +75=AI 触发停止...,0,2,0,0,0,0,0,0,0,停止特定参战方的 AI 触发。,0,1,75 +76=AI 触发小队比例...,0,6,0,0,0,0,0,0,0,AI 进行 AI 触发小队建造的比例,数值越大越倾向于使用局部的AI触发(在地图内),越小越倾向于使用全局的AI触发(在ai(md).ini中)。100 = 全部 局部AI触发小队%1 0 = 全部 全局AI触发小队。,0,1,76 +77=飞行作战小队比例...,0,6,0,0,0,0,0,0,0,AI 进行飞行小队建造的比例(100 = 全部小队%1 0 = 全部随机)。,0,1,77 +78=步兵作战小队比例...,0,6,0,0,0,0,0,0,0,AI 进行步兵小队建造的比例(100 = 全部小队%1 0 = 全部随机)。,0,1,78 +79=作战小队单位比例...,0,6,0,0,0,0,0,0,0,AI 进行小队单位建造的比例(100 = 全部小队%1 0 = 全部随机)。,0,1,79 +80=援军(小队)[在路径点]...,-1,7,6,0,0,0,1,0,0,在特定的路径点区域建造援军作战小队。效果为直接刷出来,刷兵路径点为行为设置中的路径点。,0,1,80 +81=自我唤醒,0,0,0,0,0,0,0,0,0,唤醒所有休息(sleep)或无指令(harmless)单位进行防卫模式。,0,1,81 +82=唤醒所有休息单位,0,0,0,0,0,0,0,0,0,唤醒所有休息(sleep)的单位模式。,0,1,82 +83=唤醒所有无指令单位,0,0,0,0,0,0,0,0,0,唤醒所有无指令(harmless)的单位模式。,0,1,83 +84=唤醒某小组单位...,0,6,0,0,0,0,0,0,0,唤醒有该小组编号的无指令(harmless)单位。,0,1,84 +85=矿脉生长...,0,37,0,0,0,0,0,0,0,控制矿脉是否生长。,0,1,85 +86=矿石生长...,0,37,0,0,0,0,0,0,0,控制矿石是否生长。,0,1,86 +87=冰层延伸(无用)...,0,37,0,0,0,0,0,0,0,控制结冰面积是否增长。,0,0,87 +88=粒子动画...,0,26,0,0,0,0,1,0,0,在特定区域显示粒子动画。,0,1,88 +89=删除粒子动画...,0,0,0,0,0,0,1,0,0,删除特定区域的粒子动画。,0,1,89 +90=闪电打击...,0,0,0,0,0,0,1,0,0,单个的离子风暴闪电打击。目前只有在爱伦斯(Alliance)编辑器上才可以正常使用这个功能。,0,1,90 +91=Go Berzerk(无用),0,0,0,0,0,0,0,0,0,Attached object (cyborg) goes berzerk.,0,0,91 +92=启动烈火风暴防御系统(无用),0,0,0,0,0,0,0,0,0,启动某一作战方的烈火风暴防御系统。,0,0,92 +93=关闭烈火风暴防御系统(无用),0,0,0,0,0,0,0,0,0,关闭某一作战方的烈火风暴防御系统。,0,0,93 +94=离子炮打击(无用),0,0,0,0,0,0,1,0,0,在特定的路径点实施离子炮打击。,0,0,94 +95=核弹打击...,0,0,0,0,0,0,1,0,0,在特定的路径点边缘实施核弹打击。,0,1,95 +96=化学导弹打击(无用),0,0,0,0,0,0,1,0,0,在特定的路径点实施化学导弹打击。,0,0,96 +97=Toggle Train Cargo(无用),0,0,0,0,0,0,0,0,0,TS残留功能,列车卸货,切换状态,0,0,97 +98=播入音效(随机)...,-7,16,0,0,0,0,0,0,0,在随机的路径点播放音效。这个动作不需要设置重复触发,启动以后即在随机路径点不停播放这个音效,间隔时间随机。,0,1,98 +99=播放音效...,-7,16,0,0,0,0,1,0,0,在特定的路径点播放特定的音效。这个动作不需要设置重复触发,启动以后即在特定路径点不停播放这个音效,间隔时间随机。,0,1,99 +100=在雷达窗口播放影片...,0,50,0,0,0,0,0,0,0,显示特定的开场影片(在小屏幕)。游戏者仍能对步兵和单位进行控制。,0,1,100 +101=重置特定路径点的黑幕(未探测区)...,0,30,0,0,0,0,0,0,0,用黑幕覆盖特定的路径点。,0,1,101 +102=闪电风暴打击...,0,0,0,0,0,0,1,0,0,此触发的所有者在特定的路径点实施闪电风暴打击。,0,1,102 +103=计时器文本...,-4,13,6,0,0,0,0,0,0,计时器显示什么文本标签(如文本是在csf文件中)。要开启一个正常的计时器,需要在一个触发内依次设置 27 计时设置、23 计时开始、103 计时器文本 三个行为。,0,1,103 +104=闪亮小队...,-5,7,0,0,0,0,1,0,0,用特定的帧数闪亮特定的小队,填600以上效果较好。,0,1,104 +105=显示对话注释框...(无用),-1,7,0,0,0,0,0,0,0,TS残留功能,在特定小队的上面显示对话注释框。,0,0,105 +106=设置科技等级(Techno)[Level]... ,-9,46,0,0,0,0,1,0,0,设置特定的单位或建筑到特定的科技等级。,0,1,106 +107=超时空传送援军(小队)[在路径点]... ,-1,7,0,0,0,0,1,0,0,传送援军小队到特定的路径点区域,并制造一个超时空穿送的效果,而超时空传送显示的动画需要与事件41连用。,0,1,107 +108=建立小木箱...,0,31,0,0,0,0,1,0,0,在特定的路径点建立一个特定内容的工具木箱。0金钱(大的)%1 1获得随机单位%1 2医疗%1 3获得隐形能力%1 4爆炸%1 5爆炸(大的)%1 6金钱%1 7黑幕重置%1 8清除黑幕%1 9护甲加强%1 10速度加强%1 11攻击加强%1 12获得核弹一枚%1 13空箱子%1 14升级%1 15空箱子%1 16瓦斯气体%1 17一片矿。,0,1,108 +109=铁幕装置作用于...,0,4,0,0,0,0,1,0,0,将铁幕装置作用于特定的路径点。,0,1,109 +110=游戏暂停(秒)...,0,6,0,0,0,0,0,0,0,使游戏暂停特定的秒数。,0,1,110 +111=驱逐占据者,0,0,0,0,0,0,0,0,0,本行为将会从城市关联的建筑中驱逐占据的作战单位。要使用本行为,必须在每一个建筑上关联一个单独的标签,重复的标签无用。默认每一个触发对应一个标签,你可以使用 编辑→标签编辑 来增加这个触发的标签。,0,1,111 +112=立即居中当前视野到特定路径点...,0,0,0,0,0,0,1,0,0,立即将战术视野移动到特定的路径点。,0,1,112 +113=欢呼,0,2,0,0,0,0,0,0,0,使所有步兵单位进行欢呼动作。,0,1,113 +114=设置建造栏到(0-3)...,0,6,0,0,0,0,0,0,0,强制使建造栏跳转到特定的界面。0建筑界面%1 1防御界面%1 2步兵界面%1 3车辆界面。,0,1,114 +115=闪动建造栏中的单位,-9,46,0,0,0,0,1,0,0,闪动建造栏中的单位,数值为闪烁时间,一般为120比较合适。,0,1,115 +116=停止特定路径点的音效...,0,30,0,0,0,0,1,0,0,这个触发可以停止在某个路径点播放的音效。,0,1,116 +117=播放开场影片(游戏暂停)...,0,50,0,0,0,0,0,0,0,显示特定的游戏开场影片。影片播放时,游戏暂停。播放影片期间游戏者不可进行操作。,0,1,117 +118=清除全部污染,0,0,0,0,0,0,0,0,0,将从地图上删除所有污染对象。,0,1,118 +119=将特定作战方全部摧毁...,0,2,6,0,0,0,0,0,0,消灭特定作战方的所有建筑、单位等。数值为1时表示直接移除单位不产生任何破坏效果。,0,1,119 +120=将特定方建筑全部摧毁...,0,2,0,0,0,0,0,0,0,摧毁特定作战方的所有建筑。,0,1,120 +121=将特定方陆军单位全部摧毁...,0,2,0,0,0,0,0,0,0,消灭特定作战方的所有陆军单位。,0,1,121 +122=将特定方海军单位全部摧毁...,0,2,0,0,0,0,0,0,0,消灭特定作战方的所有海军单位。,0,1,122 +123=心灵控制基地...,0,2,0,0,0,0,0,0,0,此触发的所有者将会通过心灵控制敌方所有建筑。,0,1,123 +124=解除基地的心灵控制...,0,2,0,0,0,0,0,0,0,此触发的所有者将重新取得被敌方心灵控制的所有建筑的作战控制权。,0,1,124 +125=将建筑建于...,-10,47,6,0,0,0,1,0,0,此触发的所有者将在本路径点获得该类型的建筑,如果目标路径点有建筑则不会建造,如果目标路径点有单位则会强行弹开单位。此动作的数值参数用于控制是否禁止建造动画,为1则不播放。,0,1,125 +126=恢复至游戏初始建筑单位状态...,0,2,0,0,0,0,0,0,0,此作战方游戏初始的所有建筑和单位将会重建。会引起单位冲击和覆盖图被清除。,0,1,126 +127=超时空事件效果 ...,0,6,0,0,0,0,0,0,0,将会用多帧画面全屏幕平铺显示超时空穿梭事件效果,数值一般为60,填太大会游戏卡死。,0,1,127 +128=全部超时空传送到...,0,0,0,0,0,0,1,0,0,此触发的作战方所有单位将被传送到特定的路径点。,0,1,128 +129=设置超武充能完成度,-11,20,0,0,0,0,1,0,0,此触发的所有者如果有超级武器,那么此超级武器的充能状态会被设置为“完成了”百分之几 。使用一个整数(0-100)。,0,1,129 + +; YR +130=恢复初始建筑...,0,2,0,0,0,0,0,0,0,该参战方游戏初始的所有建筑将会被修复或重建。 会引起单位冲击和覆盖图清除。,0,1,130,1 +131=闪亮特定类型的建筑...,-9,47,0,0,0,0,1,0,0,该触发所属的所有特定类型的建筑将闪亮特定的时间。,0,1,131,1 +132=设置超武充能时间,-11,20,0,0,0,0,1,0,0,设置任何数值,设置触发所属方超武至充能完毕的剩余时间,使用画面帧数(1游戏秒=15帧),将会覆盖ini设置的RechargeTime,0,1,132,1 +133=复位超级武器充能时间,0,20,0,0,0,0,0,0,0,重置触发所属方超级武器充能时间,使用ini设置的RechargeTime。,0,1,133,1 +134=超级武器复位,0,36,0,0,0,0,0,0,0,就与间谍进入超级武器一样,超级武器将会复位(重新开始倒计时)。,0,1,134,1 +135=超武首选目标路径点...,0,0,0,0,0,0,1,0,0,该触发的所有者会将超级武器的攻击目标指向此路径点。,0,1,135,1 +136=超武首选目标路径点清除,0,0,0,0,0,0,0,0,0,该触发的所有者将返回,正常确定其超级武器的攻击目标。,0,1,136,1 +137=基地中心单元设置,0,0,0,0,0,0,1,0,0,此触发的所有者将认为此路径点为其基地的中心单元,脚本里“集合在基地附近”将会集合在这个路径点附近。,0,1,137,1 +138=基地中心单元清除,0,0,0,0,0,0,0,0,0,此触发的所有者将返回,并去计算其中心位置。,0,1,138,1 +139=雷达信息中断,0,6,0,0,0,0,0,0,0,此触发的所有者的雷达将中断一段时间,填太小会没有效果。,0,1,139,1 +140=设置防御目标单元,0,0,0,0,0,0,1,0,0,此触发的所有者将自动对特定的路径点区域进行防御行动。,0,1,140,1 +141=清除防御目标单元,0,0,0,0,0,0,0,0,0,此触发的所有者将返回到正常情况下的防御目标区域。,0,1,141,1 +142=重绘红色,0,6,0,0,0,0,0,0,0,重绘屏幕的红色部分。采用百分比,这里的50代表光照里的0.5。行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,142,1 +143=重绘绿色,0,6,0,0,0,0,0,0,0,重绘屏幕的绿色部分。采用百分比,这里的50代表光照里的0.5。行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,143,1 +144=重绘蓝色,0,6,0,0,0,0,0,0,0,重绘屏幕的蓝色部分。采用百分比,这里的50代表光照里的0.5。行为71~73连用+行为142~144连用可以做出变更光照效果。,0,1,144,1 +145=战术视野跳转至基地,0,0,0,0,0,0,0,0,0,和按下“H”键效果相同。,0,1,145,1 +;bellow only added in hares +146=使步兵单位进入惊恐状态,0,2,0,0,0,0,0,0,0,使指定作战方所有的步兵单位进入惊恐动作状态(如果支持),0,1,146,1 +147=在战场区域全屏铺展绘制动画,0,25,6,0,0,0,0,0,0,在屏幕固定位置平铺绘制多帧图像,0,1,147,1 +148=允许/禁止所属方共享视野,0,2,15,0,0,0,0,0,0,允许/禁止指定所属方共享视野给友方,0,1,148,1 +149=设置触发随机分量,-2,14,6,0,0,0,0,0,0,设置指定触发的随机分量,与行为150配合使用;负数不会更改触发的分量,可用于行为150罗列事件使用。,0,1,149,1 +150=随机允许下列触发,0,0,0,0,0,0,0,0,0,将该行为(从属于当前触发事件)以下的多个行为所指定的触发按照分量随机抽取,并将其禁止状态改为允许,与行为149配合使用。,0,1,150,1 +151=建筑炮塔转动,0,6,15,0,0,0,0,0,0,将触发绑定的建筑转动其炮塔特定方向,如果布尔值为真,则表示这个转动量是0-255之间的绝对方向数,0,1,151,1 +152=全部归入小队...,-1,7,2,0,0,0,0,0,0,作战方%1全部单位被强制招募进指定小队类型。这些单位将被这同一个小队实例管理。,0,1,152 +153=控制存档按钮可用性,0,15,0,0,0,0,0,0,0,控制存档按钮是否为允许/禁止状态,0,1,153,1 +154=设置参战方地图边界,0,6,0,0,0,0,0,0,0,设置参战方的地图边界,仅能为0-4之间的一个枚举值,0,1,154,1 + + +[NewParamTypes] +500=AttackTargets,0,0,0,0 +501=BuildingTypes,2,1,1,0 +502=BuildingwithProperty,0,0,0,0 +503=VariableNames,1,0,0,0 +504=VariableNames,3,0,0,0 +505=UnloadOptions,0,0,0,0 +506=ScriptTypes,7,1,1,0 +507=TeamTypes,7,1,1,0 +508=Houses,3,1,0,0 +509=DialogList,8,1,0,0 +510=SoundList,5,1,0,0 +511=Movies,4,1,0,0 +512=Themes,6,1,0,0 +513=Countries,2,1,1,0 +514=Facing,0,0,0,0 +515=Animations,1,1,1,0 +516=TalkBubble,0,0,0,0 +517=EnterStatus,0,0,0,0 +518=Boolean,0,0,0,0 +519=CameraSpeed,0,0,0,0 +520=AITargetTypes,1,1,0,0 +521=AITargetCategories,0,0,0,0 +522=AIScriptsList,1,1,0,0 +523=MoveCompletion,0,0,0,0 +524=IdleAnim,0,0,0,0 +525=AircraftTypes,2,1,1,0 +526=InfantryTypes,2,1,1,0 +527=VehicleTypes,1,1,1,0 +528=DialogList,8,0,0,1 +529=SoundList,5,0,0,1 +530=Themes,6,0,0,1 +531=SuperWeaponTypes,2,1,1,0 +532=BuildingTypes,2,0,1,1 +533=TeamTypes,3,0,1,1 +534=ParticleSystems,2,1,0,0 +535=TalkBubble,0,1,0,0 +536=VoxelAnims,2,1,0,0 +537=CrateTypes,0,0,0,0 +538=SidebarNum,0,0,0,0 +539=WeaponTypes,1,1,0,0 +540=SpotlightOptions,0,0,0,0 +541=RadarEvents,0,0,0,0 +542=Operator,0,0,0,0 +543=EVAType,0,0,0,0 +544=SuperWeaponTypes,2,0,1,1 +545=BannerTypes,2,0,0,1 +546=AttachEffectTypes,2,0,0,1 +547=PlayBuildup,0,0,0,0 +548=BuildingTypes,2,0,1,1 +549=BuildingwithPropertyID,0,0,0,1 +550=ScriptTypes,7,0,1,1 +551=TeamTypes,7,0,1,1 +552=DialogList,8,0,0,1 +553=SoundList,5,0,0,1 +554=Movies,4,0,0,1 +555=Themes,6,0,0,1 +556=Countries,2,0,1,1 +557=AircraftTypes,2,0,1,1 +558=InfantryTypes,2,0,1,1 +559=VehicleTypes,2,0,1,1 +560=SuperWeaponTypes,2,0,1,1 +561=Animations,2,0,1,1 +562=WeaponTypes,2,0,1,1 +563=VoxelAnims,2,0,1,1 +564=ParticleSystems,2,0,1,1 +565=RadarMode,0,0,0,0 +566=ForceOnlyTargetHouseEnemy,0,0,0,0 +567=DropCrateBehaviour,0,0,0,0 + +; Script actions that use strings as params + +[ParamAffectedParams] +0=Action,501,3,0,0,3,1,35 +1=Action,502,3,0,0,3,1,35 +2=Action,503,1,0,0,3,1,35 +3=Action,504,3,0,0,3,1,35 +4=Action,504,4,2,0,3,1,35 + +; Over 500 values can be used +; for additional custom dropdowns listing of a section's values. +; Format: +; Index = SectionName, LoadFrom, StrictOrder, ShowUIName, [UseValue] +; Index: the positive of 2nd parameter in [ScriptParams] +; SectionName: section name string +; LoadFrom: which file to load the section from. +; 0 FAData, 1 Rules, 2 Rules + Map, 3 Map, 4 Art, 5 Sound, 6 Theme, 7 AI + Map, 8 EVA, 9 CurrentTheater. +; StrictOrder: Use registry index instead of key value. +; ShowUIName: show UIName, if failed, Name. +; UseValue: use value instead of its key. + +[Chinese-AITriggerSides] +0=所有阵营 +1=盟军 +2=苏军 +3=尤里 + + +[Chinese-AITriggerConditionTypes] +-1=恒成立 +0=敌方拥有 X 单位 <比较方式> N 个 +1=我方拥有 X 单位 <比较方式> N 个 +2=敌方电力条为黄色 +3=敌方电力条为红色 +4=敌方拥有金钱 <比较方式> N +5=铁幕将准备就绪 +6=超时空传送将准备就绪 +7=中立方拥有 X 单位 <比较方式> N 个 + +; events format: +; 0 1 2 3 4 5 6 7 8 9 +; #=Description, P1 type, P2 type, TagNeeded, Obsolete,Desc2,UsedInTS,UsedInRA2,ID,[NeedsYR optional] +; negative number for type currently not supported (only Actions do support yet)! +; NOTE: Actions not listed in [Events] but in [EventsRA2] will be ignored. Use the "used in RA2" to support them. +; NOTE: To overwrite TS events with RA2 special ones, add the new event to the [EventsRA2] section. +; NOTE: ID must be the same like # + +[Chinese-AttackTargets] +0=任意目标 +1=任意目标(同0) +2=建筑物 +3=矿车矿场 +4=步兵 +5=车辆 +6=生产建筑 +7=防御建筑 +8=基地威胁 +9=电厂 +10=驻军建筑 +11=科技建筑 + + +[Chinese-Operator] +0=赋值 (=) A=B +1=加 (+) A=A+B +2=减 (-) A=A-B +3=乘 (*) A=A*B +4=除 (/) A=A/B +5=求余 (%) A=A%B +6=左移 (<<) A=A<>) A=A>>B +8=反转 (~) A=~A +9=异或 A=A⊕B +10=或 A=A|B +11=与 A=A&B + + +[Chinese-RadarEvents] +0=战斗 +1=非战斗 +2=空降区 +3=基地受击 +4=矿车受击 +5=发现敌军 +6=单位生产 +7=单位阵亡 +8=单位维修 +9=建筑被渗透 +10=建筑被占领 +11=信标放置 +12=发现超武 +13=超武启动 +14=桥梁维修 +15=放弃驻扎 +16=友军受击 + + +[Chinese-EVAType] +-1=禁用 +0=盟军(伊娃) +1=苏军(索菲亚) +2=尤里 + + +[Chinese-SidebarNum] +0=建筑栏 +1=防御栏 +2=步兵栏 +3=战车栏 + + +[Chinese-CrateTypes] +0=大量金钱(固定值) +1=随机单位 +2=医疗 +3=隐形 +4=爆炸 +5=大型爆炸 +6=金钱(随机) +7=重置黑幕 +8=显示全图 +9=防御力强化 +10=速度强化 +11=火力强化 +12=一次核弹 +13=空 +14=升级 +15=空 +16=毒气 +17=一片矿 + + +[Chinese-SpotlightOptions] +0=无聚光灯 +1=使用rules设置 +2=圆圈 +3=跟随敌对目标 + + + +[Chinese-CameraSpeed] +0=非常慢 +1=慢 +2=正常 +3=快 +4=非常快 + + +[Chinese-Boolean] +0=否 +1=是 + + +[Chinese-EnterStatus] +0=※Sleep (休眠,不还击) +1=Attack nearest enemy +2=Move +3=QMove +4=Retreat (撤离地图) +5=※Guard (原地警戒) +6=Sticky (固守,不主动攻击) +7=Enter object +8=Capture object +9=Move into & get eaten +10=Harvest (采矿) +11=※Area Guard (区域警戒) +12=Return (to refinery) +13=Stop +14=Ambush (wait until discovered) +15=※Hunt (游猎) +16=※Unload (卸载或部署) +17=Sabotage (move in & destroy) +18=Construction +19=Deconstruction +20=Repair +21=Rescue +22=Missile +23=※Harmless (无威胁) +24=Open +25=Patrol +26=Paradrop approach drop zone +27=Paradrop overlay drop zone +28=Wait +29=Attack again +30=Spyplane approach (YR) +31=Spyplane overfly (YR) + + +[Chinese-Facing] +0=↗ +1=→ +2=↘ +3=↓ +4=↙ +5=← +6=↖ +7=↑ + + +[Chinese-UnloadOptions] +0=保留载具,保留成员 +1=保留载具,丢弃成员 +2=丢弃载具,保留成员 +3=丢弃载具,丢弃成员 + + +[Chinese-BuildingwithProperty] +0=最小威胁 +1=最大威胁 +2=最近 +3=最远 + + +[Chinese-BuildingwithPropertyID] +0=LeastThreat - 最小威胁 +1=HighestThreat - 最大威胁 +2=Nearest - 最近 +3=Farthest - 最远 + +;从1开始索引的talkbubl.shp帧数,你可以添加更多内容,色盘为palette.pal +;持续时间在[General]TalkBubbleTime=中设定,值为现实秒,默认大约为5秒钟 + +[Chinese-TalkBubble] +1=星号(*) +2=问号(?) +3=感叹号(!) + + +[Chinese-IdleAnim] +0=Idle1 +1=Idle2 + + +[Chinese-MoveCompletion] +0=小队队长抵达 +1=任一成员抵达 +2=所有成员抵达 + + +[Chinese-PlayBuildup] +0=播放 +1=不播放 + + +[Chinese-RadarMode] +0=默认,需要有足够电力供应与雷达建筑 +1=无偿雷达 +2=永久开启,无视雷达干扰效果 +3=永久关闭 + + +[Chinese-ForceOnlyTargetHouseEnemy] +-1=默认值,不强制更改 OnlyTargetHouseEnemy 标签全局值 +0=强制 OnlyTargetHouseEnemy 为 false +1=强制 OnlyTargetHouseEnemy 为 true +2=强制随机布尔值 + + +[Chinese-DropCrateBehaviour] +-1=使用单位的默认设置 +0=清除掉落箱子类型 +1=覆盖掉落箱子类型 + +[Theaters] +0=TEMPERATE,TEMPERATE +1=SNOW,SNOW +2=URBAN,URBAN +3=NEWURBAN,NEWURBAN +4=LUNAR,LUNAR +5=DESERT,DESERT + +; Letter abbreviations for different theaters +; Only works in file loading +; First = file suffix (xxxx.~~~, iso~~~.pal), must be 3 letters +; Second = new theater letter (G~CNST) + +[TheaterSuffixes] +TEMPERATE=TEM,T +SNOW=SNO,A +URBAN=URB,U +NEWURBAN=UBN,N +LUNAR=LUN,L +DESERT=DES,D + +; Order: Theater, SmoothTileSet, LATTileSet[, Skip LAT Tiles, ClearTileSet] + +[LATGroups] +0=SNOW,TXSnowGrass,TXSnowGrassLAT,TXSnowGrassSkipLAT +1=SNOW,TXSnowPave,TXSnowPaveLAT,TXSnowPaveSkipLAT +2=TEMPERATE,TXTemperatePave,TXTemperatePaveLAT,TXTemperatePaveSkipLAT +;newurban is disabled by default, for the vanilla pave (114) has no LAT. +;3=NEWURBAN,TXNewUrbanPave,TXNewUrbanPaveLAT,TXNewUrbanPaveSkipLAT +4=DESERT,TXDesertPave,TXDesertPaveLAT,TXDesertPaveSkipLAT +; TX remove 33 from lat system +5=LUNAR,TXLunarSand,TXTXLunarSandLAT + +; FA2 will first read this, and then [General] +; in theater INI (not include Skip LAT Tiles). + +[LATSettings] +TXSnowGrass=127 +TXSnowGrassLAT=131 +TXSnowPave=97 +TXSnowPaveLAT=130 +TXTemperatePave=93 +TXTemperatePaveLAT=94 +TXNewUrbanPave=114 +TXNewUrbanPaveLAT=178 +TXDesertPave=107 +TXDesertPaveLAT=108 +TXLunarSand=33 +TXTXLunarSandLAT=34 +TXSnowGrassSkipLAT=20,44,69,71,103,104,105,128,129,132,133,134,135,142,143,147,148,149,150,162,169,171,172 +TXSnowPaveSkipLAT=20,44,69,71,87,88,98,102,103,104,105,110,111,113,116,117,120,121,124,128,129,132,133,134,135,136,142,143,147,148,149,150,162,169,171,172 +TXTemperatePaveSkipLAT=20,38,40,47,82,83,97,98,102 +TXNewUrbanPaveSkipLAT=10,15,16,17,18,19,20,23,25,35,38,40,47,76,78,80,83,85,87,102,104,106,107,117,120,121,133,137,138,140,146,148,152,155,156,171,172,174,181,190,191,192,195,203,123,124,130,131,132,134,139,144,145,170 +TXDesertPaveSkipLAT=20,38,40,47,97,98,111,112,118 + +; custom brush sizes +; format: X length + x + Y length +; it is not recommended to change first and second value + +[BrushSizes] +0=1x1 +1=2x2 +2=3x3 +3=4x4 +4=5x5 +5=7x7 +6=10x10 +7=1x2 +8=2x1 +9=1x3 +10=3x1 +11=1x5 +12=5x1 +13=1x10 +14=10x1 + +; All tilesets in Morphables should have Morphable=true. +; If you want to specify a certain tile in the tileset, +; use # as a delimiter, e.g. Morphables=114#0,114#1 +; if there's no #, all tiles in the tileset will count +; You don't need to write existing RampBase here, +; only new ramps need to be added here. The Ramps +; and Morphables should have the same length of +; tilesets, and those tilesets should be one-to-one +; correspondence. +; AddTiles = some tilesets added to the Object Browser +; -> Ground, not necessarily be morphable. +; BigWaterIndices = tile indices of big (2x2) water +; default: first 6 tiles of [General]->WaterSet +; SmallWaterIndices = tile indices of small (1x1) water +; default: No. 9~13 tiles of [General]->WaterSet +; IgnoredTilesets = Tilesets not adding to the Object +; Browser -> Ground. Should be keys in [General] of +; theater INIs, e.g. WaterSet + +[TemperateInfo] +AddTiles=93 + + +[SnowInfo] +Morphables=97 +Ramps=98 +Cliffs2=75 +CliffsWater2=79 +AddTiles=127,135 + + +[UrbanInfo] +Morphables=13 +Ramps=140 + + +[NewUrbanInfo] +Morphables=114,123 +Ramps=117,193 +Cliffs2=110 +CliffsWater2=112 +AddTiles=46,124,157 +IgnoredTilesets=PaveTile + + +[DesertInfo] +AddTiles=107 + + +[LunarInfo] +AddTiles=33,91 +IgnoredTilesets=WaterSet + +;[OverlayDisplayLimit] +;243=48 ; This means FA2 won't display overlay 243's frames after 48 + +; BuildingTypes to be ignored during overlap detection. For Immune or InvisibleInGame cases. + + +[StructureOverlappingCheckIgnores] +0=TSTLAMP +1=GALITE +2=NEGLAMP +3=INGALITE +4=REDLAMP +5=NEGRED +6=GRENLAMP +7=BLUELAMP +8=YELWLAMP +9=INYELWLAMP +10=PURPLAMP +11=INPURPLAMP +12=INORANLAMP +13=INGRNLMP +14=INREDLMP +15=INBLULMP +16=TEMMORLAMP +17=TEMDAYLAMP +18=TEMDUSLAMP +19=TEMNITLAMP +20=SNOMORLAMP +21=SNODAYLAMP +22=SNODUSLAMP +23=SNONITLAMP + + +[Chinese-TileManagerDataTEM] +普通地形/LAT=lat +悬崖=cliff +水面=water +水岸=shore +斜坡=ramp|slope +城市地形=pave +公路/小路/人行道=road|highway +地表装饰=feature|farm +桥=bridge +铁路=rail|train +隧道=tunnel|tube +瀑布=waterfall +交界处/地形修复=fix + + +[Chinese-TileManagerDataSNO] +普通地形/LAT=lat +悬崖=cliff +水面=water +水岸=shore +斜坡=ramp|slope +城市地形=pave +公路/小路/人行道=road|highway +地表装饰=feature|farm +桥=bridge +铁路=rail|train +隧道=tunnel|tube +瀑布=waterfall +交界处/地形修复=fix + + +[Chinese-TileManagerDataURB] +普通地形/LAT=lat +悬崖=cliff +水面=water +水岸=shore +斜坡=ramp|slope +城市地形=pave +公路/小路/人行道=road|highway +地表装饰=feature|farm +桥=bridge +铁路=rail|train +隧道=tunnel|tube +瀑布=waterfall +交界处/地形修复=fix + + +[Chinese-TileManagerDataUBN] +普通地形/LAT=lat +悬崖=cliff +水面=water +水岸=shore +斜坡=ramp|slope +城市地形=pave +公路/小路/人行道=road|highway +地表装饰=feature|farm +桥=bridge +铁路=rail|train +隧道=tunnel|tube +瀑布=waterfall +交界处/地形修复=fix + + +[Chinese-TileManagerDataDES] +普通地形/LAT=lat +悬崖=cliff +水面=water +水岸=shore +斜坡=ramp|slope +城市地形=pave +公路/小路/人行道=road|highway +地表装饰=feature|farm +桥=bridge +铁路=rail|train +隧道=tunnel|tube +瀑布=waterfall +交界处/地形修复=fix + + +[Chinese-TileManagerDataLUN] +普通地形/LAT=lat +悬崖=cliff +水面=water +水岸=shore +斜坡=ramp|slope +城市地形=pave +公路/小路/人行道=road|highway +地表装饰=feature|farm +桥=bridge +铁路=rail|train +隧道=tunnel|tube +瀑布=waterfall +交界处/地形修复=fix + + +[English-TileManagerDataTEM] +Cliff=cliff +Water=water +Ramp=ramp|slope +Bridge=bridge +Road=road|highway +Feature=feature|farm +Railway=rail|train +Tunnel=tunnel|tube +Ramp=ramp|slope +Shore=shore +Pavement=pave +Fix=fix +LAT=lat + + +[English-TileManagerDataSNO] +Cliff=cliff +Water=water +Ramp=ramp|slope +Bridge=bridge +Road=road|highway +Feature=feature|farm +Railway=rail|train +Tunnel=tunnel|tube +Ramp=ramp|slope +Shore=shore +Pavement=pave +Fix=fix +LAT=lat + + +[English-TileManagerDataURB] +Cliff=cliff +Water=water +Ramp=ramp|slope +Bridge=bridge +Road=road|highway +Feature=feature|farm +Railway=rail|train +Tunnel=tunnel|tube +Ramp=ramp|slope +Shore=shore +Pavement=pave +Fix=fix +LAT=lat + + +[English-TileManagerDataUBN] +Cliff=cliff +Water=water +Ramp=ramp|slope +Bridge=bridge +Road=road|highway +Feature=feature|farm +Railway=rail|train +Tunnel=tunnel|tube +Ramp=ramp|slope +Shore=shore +Pavement=pave +Fix=fix +LAT=lat + + +[English-TileManagerDataDES] +Cliff=cliff +Water=water +Ramp=ramp|slope +Bridge=bridge +Road=road|highway +Feature=feature|farm +Railway=rail|train +Tunnel=tunnel|tube +Ramp=ramp|slope +Shore=shore +Pavement=pave +Fix=fix +LAT=lat + + +[English-TileManagerDataLUN] +Cliff=cliff +Water=water +Ramp=ramp|slope +Bridge=bridge +Road=road|highway +Feature=feature|farm +Railway=rail|train +Tunnel=tunnel|tube +Ramp=ramp|slope +Shore=shore +Pavement=pave +Fix=fix +LAT=lat + +; override the same section in rules +;[Colors] +;Gold=43,239,255 + +; Comment these sections for FA2sp DLL drawing +; voxel turret locations +;[BuildingVoxelTurretsRA2] +;GTGCANX=00 +;GTGCANY=44;-6 +;NASAMY=-3;2 +;NALASRX=0 +;NALASRY=10;-3 +;NAFLAKY=6;10 ; flak needs additional correction below for every direction +;NAFLAKX=-5; +;NAFLAKX0=5 +;NAFLAKX2=-4; +;NAFLAKY2=5 +;NAFLAKX3=-5; +;NAFLAKY3=10 +;NAFLAKX4=5 +;NAFLAKY4=14 +;NAFLAKX5=10 +;NAFLAKY5=8 +;NAFLAKX6=15 +;NAFLAKY6=5 +;NAFLAKX7=12 +;NAFLAKY7=1 +;CAOUTPX=-30 +;CAOUTPY=12 +;YAREFNX=-30 +;YAREFNY=30 +;YAGGUNY=7 +;YAGGUNX=-5 +;YAGGUNX7=5; gattling gun needs some correction +;YAGGUNY7=-3 +;YAGGUNX6=11 +;YAGGUNY6=3 +;YAGGUNX5=10 +;YAGGUNY5=3 +;YAGGUNX4=3 +;YAGGUNY4=2 +;YAGGUNX0=5 + +; voxel barrel locations +;[BuildingVoxelBarrelsRA2] +;GTGCANX0=0 +;GTGCANY0=30;-6 +;GTGCANX1=-18 +;GTGCANY1=35;-6 +;GTGCANX2=-28 +;GTGCANY2=50;-6 +;GTGCANX3=-27 +;GTGCANY3=67;-6 +;GTGCANX4=00 +;GTGCANY4=73;-6 +;GTGCANX5=23 +;GTGCANY5=60;-6 +;GTGCANX6=30 +;GTGCANY6=48;-6 +;GTGCANX7=20 +;GTGCANY7=35;-6 + + +; not NOSURFACES: +;[BuildingVoxelTurretsRA2] +;GTGCANX=-1 +;GTGCANY=0;-6 +;NASAMY=0;2 +;NALASRX=0 +;NALASRY=0;-3 +;NAFLAKY=10;10 + +;[BuildingVoxelTurrets] + +;[VehicleVoxelTurretsRA2] +;DISKX=-10 +;DISKY=-10 +;YTNKY=-30 +;SREFX=4 +;SREFY=3 + +;[VehicleVoxelTurrets] + +;obsolete +;[IgnoreArtImage] +;BFRT=1 +;CAML=1 + +;[IgnoreActiveAnim2] + + +[AutoShoreTypes] +0=TEMPERATE,Sand Shorelines,ShorePieces,GreenTile +1=SNOW,Ice Shorelines,ShorePieces,GreenTile +2=URBAN,Grass Shorelines,ShorePieces,GreenTile +3=NEWURBAN,Sand Shorelines,ShorePieces,GreenTile +4=NEWURBAN,*Grass Dark Shorelines*,129,124 +5=DESERT,Sand Shorelines,ShorePieces,GreenTile + +; hacks for shore pieces +; shore id_pos=iswater +; pos: count from left top to right, then next "row" +; RA2 only needs one fix (for URBAN specifically) + +[ShoreTerrainRA2] +12_4=1 +12_5=1 + +; tilesets that may be modified using the shore or a similar logic +; 1 = affected, 0 = not affected +; in theater specific sections, format: tileIndex = 1/0 +; [SoftTileSetsTEM] + +[ShoreTerrainTS] +0_0=0 +0_1=0 +0_2=1 +0_3=1 +1_0=0 +1_1=0 +2_0=0 +2_1=0 +3_0=0 +4_0=0 +4_1=0 +4_3=0 +5_0=0 +5_1=0 +5_2=0 +6_0=0 +7_0=0 +8_0=0 +8_2=0 +9_0=0 +9_2=0 +10_0=0 +10_2=0 +11_0=0 +12_0=0 +12_1=0 +12_3=0 +13_0=0 +13_3=0 +13_4=0 +14_2=0 +15_2=0 +16_2=0 +16_3=0 +17_2=0 +17_3=0 +18_2=0 +18_3=0 +19_1=0 +20_3=0 +20_4=0 +20_5=0 +21_2=0 +21_4=0 +21_5=0 +22_3=0 +23_3=0 +24_1=0 +24_3=0 +25_1=0 +25_3=0 +26_1=0 +26_3=0 +27_1=0 +28_1=0 +28_2=0 +28_5=0 +29_2=0 +29_4=0 +29_5=0 +30_1=0 +31_1=0 +32_0=0 +32_1=0 +32_2=0 +33_0=0 +33_1=0 +33_2=0 +34_0=0 +34_2=0 +34_3=0 +35_0=0 +35_2=0 +35_3=0 +36_1=0 +36_2=0 +36_3=0 +37_1=0 +37_2=0 +37_3=0 +38_0=0 +38_1=0 +38_3=0 +39_0=0 +39_1=0 +39_3=0 + +; tilesets that may be modified using the shore or a similar logic + +[SoftTileSets] +ClearTile=1 +RoughTile=1 +ClearToRoughLat=1 +SandTile=1 +ClearToSandLat=1 +GreenTile=1 +ClearToGreenLat=1 +PaveTile=1 +MiscPaveTile=1 +ClearToPaveLat=1 +RoughGround=0; no +WaterSet=1 +ShorePieces=1 + + +[SoftTileSetsTEM] +81=0 + +; tilesets that will redraw extra image +; [RedrawExtraTileSetsTEM] + +; Normally, tile extra images are included when computing redraw relations. +; Tilesets specified in this ini section will disregard their extra images. +; [NoExtraHeightRedrawTileSetsTEM] + +; corner strings are: cornerleft, cornerright, cornerbottom, cornertop + + +[CliffBackData] +vertic_diag_c=2 +;vertic_diag_2=0 +vertic_diag_0=2 +vertic_diag_1=3 +;vertic_diag_3=1 ; unused at moment +vertic_diag_cornertop_c=1 +;vertic_diag_cornertop_1=0 +vertic_diag_cornertop_0=1 +horiz_c=4 +horiz_0=22 +horiz_1=23 +horiz_2=24 +horiz_3=25 ;2x1, not 2x2, unused at moment +horiz_cornerbottom_c=2 +horiz_cornerbottom_0=28 +horiz_cornerbottom_1=29 +horiz_cornertop_c=0 +;horiz_cornertop_0=31 +vertic_c=4 +vertic_0=34 +vertic_1=35 +vertic_2=36 +vertic_3=37 ;2x1, not 2x2, unused at moment +vertic_cornerright_c=2 +vertic_cornerright_0=29 +vertic_cornerright_1=28 + + +[CliffBackDataAlt] +vertic_diag_c=2 +;vertic_diag_2=0 +vertic_diag_0=2 +vertic_diag_1=3 +;vertic_diag_3=1 ; unused at moment +vertic_diag_cornertop_c=1 +;vertic_diag_cornertop_1=0 +vertic_diag_cornertop_0=1 +horiz_c=4 +horiz_0=22 +horiz_1=23 +horiz_2=24 +horiz_3=25 ;2x1, not 2x2, unused at moment +horiz_cornerbottom_c=2 +horiz_cornerbottom_0=28 +horiz_cornerbottom_1=29 +horiz_cornertop_c=0 +;horiz_cornertop_0=31 +vertic_c=4 +vertic_0=34 +vertic_1=35 +vertic_2=36 +vertic_3=37 ;2x1, not 2x2, unused at moment +vertic_cornerright_c=2 +vertic_cornerright_0=29 +vertic_cornerright_1=28 + + +[CliffBackDataURBAN] +vertic_diag_c=1 +;vertic_diag_4=0 +vertic_diag_0=2 +;vertic_diag_1=3; other cliff type +;vertic_diag_3=1 ; unused at moment +vertic_diag_cornertop_c=2 +vertic_diag_cornertop_0=0 +vertic_diag_cornertop_1=1 +horiz_c=2 +horiz_0=22 +;horiz_3=23 +;horiz_2=24 +horiz_1=25 ;2x1, not 2x2, unused at moment +horiz_cornerbottom_c=2 +horiz_cornerbottom_0=28 +horiz_cornerbottom_1=29 +vertic_c=4 +vertic_0=34 +vertic_1=35 +vertic_2=36 +vertic_3=37 ;2x1, not 2x2, unused at moment +vertic_cornerright_c=2 +vertic_cornerright_0=29 +vertic_cornerright_1=28 + + +[CliffBackDataNEWURBAN] +vertic_diag_c=1 +;vertic_diag_4=0 +vertic_diag_0=2 +;vertic_diag_1=3; other cliff type +;vertic_diag_3=1 ; unused at moment +vertic_diag_cornertop_c=2 +vertic_diag_cornertop_0=0 +vertic_diag_cornertop_1=1 +horiz_c=2 +horiz_0=22 +;horiz_3=23 +;horiz_2=24 +horiz_1=25 ;2x1, not 2x2, unused at moment +horiz_cornerbottom_c=2 +horiz_cornerbottom_0=28 +horiz_cornerbottom_1=29 +vertic_c=4 +vertic_0=34 +vertic_1=35 +vertic_2=36 +vertic_3=37 ;2x1, not 2x2, unused at moment +vertic_cornerright_c=2 +vertic_cornerright_0=29 +vertic_cornerright_1=28 + + +[CliffFrontData] +vertic_diag_c=2 +vertic_diag_0=18 +vertic_diag_1=19 +;vertic_diag_2=20 +;vertic_diag_3=21 ; unused at moment +vertic_diag_cornerleft_c=1 +vertic_diag_cornerleft_0=20 +;vertic_diag_cornerleft_1=21 +vertic_diag_cornerright_c=2 +vertic_diag_cornerright_0=32 +vertic_diag_cornerright_1=33 +horiz_c=4 +horiz_0=4 +horiz_1=5 +horiz_2=6 +horiz_3=7 ;2x1, not 2x2, unused at moment +horiz_cornertop_c=1 +horiz_cornertop_0=1 +horiz_diag_c=6 +horiz_diag_0=8 +horiz_diag_1=9 +horiz_diag_2=10 +horiz_diag_3=11 ; unused at moment +horiz_diag_4=12 ; unused at moment +horiz_diag_5=13 ; unused at moment +vertic_c=4 +vertic_0=14 +vertic_1=15 +vertic_2=16 +vertic_3=17 ;2x1, not 2x2, unused at moment +vertic_cornerleft_c=1 +vertic_cornerleft_0=21 + + +[CliffFrontDataAlt] +vertic_diag_c=2 +vertic_diag_0=18 +vertic_diag_1=19 +;vertic_diag_2=20 +;vertic_diag_3=21 ; unused at moment +vertic_diag_cornerleft_c=1 +vertic_diag_cornerleft_0=20 +;vertic_diag_cornerleft_1=21 +vertic_diag_cornerright_c=2 +vertic_diag_cornerright_0=32 +vertic_diag_cornerright_1=33 +horiz_c=4 +horiz_0=4 +horiz_1=5 +horiz_2=6 +horiz_3=7 ;2x1, not 2x2, unused at moment +horiz_cornertop_c=1 +horiz_cornertop_0=1 +horiz_diag_c=6 +horiz_diag_0=8 +horiz_diag_1=9 +horiz_diag_2=10 +horiz_diag_3=11 ; unused at moment +horiz_diag_4=12 ; unused at moment +horiz_diag_5=13 ; unused at moment +vertic_c=4 +vertic_0=14 +vertic_1=15 +vertic_2=16 +vertic_3=17 ;2x1, not 2x2, unused at moment +vertic_cornerleft_c=1 +vertic_cornerleft_0=21 + + +[CliffFrontDataURBAN] +vertic_diag_c=1 +vertic_diag_0=18 +;vertic_diag_2=19 ; other cliff type +;vertic_diag_3=20 ; other cliff type +;vertic_diag_1=21 ; unused at moment +vertic_diag_cornerleft_c=1 +vertic_diag_cornerleft_0=20 +;vertic_diag_cornerleft_1=21 +vertic_diag_cornerright_c=2 +vertic_diag_cornerright_0=32 +vertic_diag_cornerright_1=33 +horiz_c=2 +horiz_0=4 +;horiz_3=5 ; other cliff type +;horiz_2=6 ; other cliff type +horiz_1=7 ;2x1, not 2x2, unused at moment +horiz_cornertop_c=1 +horiz_cornertop_0=1 +horiz_diag_c=2 +horiz_diag_0=8 +;horiz_diag_4=9 ; other cliff type +;horiz_diag_2=10 ; other cliff type +horiz_diag_1=11 ; unused at moment +;horiz_diag_3=12 ; unused at moment +;horiz_diag_5=13 ; unused at moment +vertic_c=2 +vertic_0=14 +;vertic_3=15 ; other cliff type +;vertic_2=16 ; other cliff type +vertic_1=17 ;2x1, not 2x2, unused at moment +vertic_cornerleft_c=1 +vertic_cornerleft_0=21 + + +[CliffFrontDataNEWURBAN] +vertic_diag_c=1 +vertic_diag_0=18 +;vertic_diag_2=19 ; other cliff type +;vertic_diag_3=20 ; other cliff type +;vertic_diag_1=21 ; unused at moment +vertic_diag_cornerleft_c=1 +vertic_diag_cornerleft_0=20 +;vertic_diag_cornerleft_1=21 +vertic_diag_cornerright_c=2 +vertic_diag_cornerright_0=32 +vertic_diag_cornerright_1=33 +horiz_c=2 +horiz_0=4 +;horiz_3=5 ; other cliff type +;horiz_2=6 ; other cliff type +horiz_1=7 ;2x1, not 2x2, unused at moment +horiz_cornertop_c=1 +horiz_cornertop_0=1 +horiz_diag_c=2 +horiz_diag_0=8 +;horiz_diag_4=9 ; other cliff type +;horiz_diag_2=10 ; other cliff type +horiz_diag_1=11 ; unused at moment +;horiz_diag_3=12 ; unused at moment +;horiz_diag_5=13 ; unused at moment +vertic_c=2 +vertic_0=14 +;vertic_3=15 ; other cliff type +;vertic_2=16 ; other cliff type +vertic_1=17 ;2x1, not 2x2, unused at moment +vertic_cornerleft_c=1 +vertic_cornerleft_0=21 + + +[BridgesTS] + + +[BridgesRA2] + + +[SpecialOverlayTS] + + +[SpecialOverlayRA2] + + + diff --git a/Supplementary/RevengeNow/README.md b/Supplementary/RevengeNow/README.md new file mode 100644 index 00000000..d23920c1 --- /dev/null +++ b/Supplementary/RevengeNow/README.md @@ -0,0 +1,24 @@ +# Revenge Now (RN) 配置套件 / Revenge Now Configuration Suite + +本目录提供面向 **Revenge Now (复仇时刻)** 项目的专属配置套件,旨在与 [`YR_RN_Mission_Editor`](file:///D:/Developments/RevengeNow/Codes/YR_RN_Mission_Editor) 地图编辑器行为全面对齐。 + +--- + +## 包含文件清单 + +1. **`FAData.ini`**: + - 融合了 `YR_RN_Mission_Editor` 的完整 RN 定制内容与 FA2sp 现代化框架体系: + - **RN 专属触发系统**:完整保留 RN 的触发动作(`[ActionsRA2]` / `[Chinese-ActionsRA2]` 共 157 项,含 Ares 动作 150~154 及参数微调)、事件(`[EventsRA2]` / `[Chinese-EventsRA2]`)、脚本(`[ScriptsRA2]` / `[Chinese-ScriptsRA2]`)、参数定义(`[ParamTypes]`、`[ScriptParams]`、`[PT_CrateTypes]` 等)以及 `[ScriptTemplates]`、`[TeamTemplates]`。 + - **RN 阵营与模式**:保留 `[Sides]`(12 个作战方定义)、`[GameModes]`、`[ForceUnitPalettePrefix]` 等。 + - **FA2sp 现代兼容层**:保留 `[Include]` 引用链(加载 `FAData_ObjectBrowser.ini` 保证物件浏览器中文分类与完整条目正常显示)与 `[ExtConfigs]`、现代剧场与图块管理定义。 + - 启用了面向 RN 对齐保存所需的保序与编码配置: + - `SaveMap.PreserveINISorting=true`:保存地图时保持所有 INI 小节原有的出现顺序。 + - `SaveMap.AdaptiveSorting=true`:保存地图时自适应保持键值排布(数字索引小节自然升序,常规属性小节维持原序)。 + - `SaveMap.KeepComments=false`:不保留注释(符合 RN 要求)。 + - `UTF8Support.AlwaysSaveAsUTF8=true`:总是以 UTF-8 编码保存地图(符合 RN 要求)。 + +--- + +## 使用方法 + +将本目录中的 `FAData.ini` 放置到 FA2 程序运行根目录(或覆盖使用),启动后保存地图即可保证 INI 文件结构、小节顺序与键值顺序与 `YR_RN_Mission_Editor` 完全一致。 diff --git a/agents.md b/agents.md new file mode 100644 index 00000000..0e915def --- /dev/null +++ b/agents.md @@ -0,0 +1,68 @@ +# Agent Guidelines & Development Standards + +## 1. Mandatory Verification Rule +> [!IMPORTANT] +> **Always verify that the solution compiles with 0 errors and all unit tests PASS before submitting changes, finishing tasks, or committing code, unless the user explicitly requests to skip the verification step.** + +## 2. Project Architecture & Context +- **Project**: FA2sp (FinalAlert 2 Special / Modernized Yuri's Revenge Map Editor extension). +- **Host Target**: `FinalAlert2YR.dat` (32-bit x86 Windows executable located in `Supplementary/`). +- **Platform**: Windows `Win32` (`x86`) only. +- **Toolchain**: Visual Studio 2022 (MSVC v143, C++20). +- **Submodules**: + - `FA2pp` at `FA2pp/` (**CRITICAL**: Must remain 100% clean and untouched; do not commit into this submodule unless explicitly instructed). + - `MFC42` at `MFC42/`. + - `googletest` at `googletest/` (tag `v1.15.2`). + - `lexilla`, `scintilla`. +- **Git Remote Constraints**: + - **NEVER** push local commits to remote repositories (`git push`) unless the user explicitly requests it. + +## 3. Unit Test (UT) Architecture +- **In-Process Test Architecture**: + - Unit tests are located in `FA2sp.UnitTest/` and built into `Supplementary/FA2sp.UnitTest.dll`. + - The test DLL hooks `CFinalSunApp::InitInstance` (address `0x41FAD0`) in `FinalAlert2YR.dat` via Syringe. + - Upon entry, it redirects stdout/stderr to `Supplementary/UnitTest.log` and `Supplementary/UnitTest.xml`, forwards CLI arguments to GoogleTest (`testing::InitGoogleTest`), runs all tests via `RUN_ALL_TESTS()`, closes output streams, and terminates cleanly via `ExitProcess(res)` before any host GUI windows are created. +- **Strict Isolation Mechanism**: + - During tests, `FA2sp.dll` must NOT be loaded alongside `FA2sp.UnitTest.dll` to prevent hook collisions. + - The runner script `Supplementary/RunUnitTest.bat` automatically isolates `FA2sp.dll` (temporarily renaming it to `FA2sp.dll.isolated`) before launching Syringe, and restores it unconditionally upon test completion. + +## 4. How to Build & Run Tests + +### Build +From project root: +```bat +msbuild FA2sp.sln /m /p:Configuration=Release /p:Platform=x86 +``` +Or build the UnitTest project alone: +```bat +msbuild FA2sp.UnitTest/FA2sp.UnitTest.vcxproj /p:Configuration=Release /p:Platform=Win32 /m +``` + +### Run Unit Tests +From project root: +```bat +Scripts\RunUnitTest.bat +``` +Or with GoogleTest Filters / Options: +```bat +Scripts\RunUnitTest.bat --gtest_filter=SequencedKeyListTest.* +Scripts\RunUnitTest.bat --gtest_filter=CINIOrderTrackerTest.* +Scripts\RunUnitTest.bat --gtest_repeat=5 +``` + +### Inspect Test Results +- Console output: printed directly by `Scripts\RunUnitTest.bat`. +- Detailed log: `Supplementary/UnitTest.log`. +- XML report: `Supplementary/UnitTest.xml`. +- Injector log: `Supplementary/syringe.log`. +- Exit code: 0 indicates success, non-zero indicates test failure. + +## 5. Local Release Simulation & Packaging +Package release zip archives for YR, MO, and RN locally using `Scripts/PackageRelease.ps1`: +```powershell +# Package all editions (YR, MO, RN) into dist/ +./Scripts/PackageRelease.ps1 + +# Package a single edition with specific version: +./Scripts/PackageRelease.ps1 -Target RN -Version v1.6.4 +``` diff --git a/doc/agents/plans/ini_key_sorting_alignment_plan.md b/doc/agents/plans/ini_key_sorting_alignment_plan.md new file mode 100644 index 00000000..4d16d323 --- /dev/null +++ b/doc/agents/plans/ini_key_sorting_alignment_plan.md @@ -0,0 +1,195 @@ +# INI 键值与小节顺序对齐实施方案与完成报告 (Alignment Plan & Architecture Report) + +> **目标**:在 `FA2Copy`(分支 `enhance/rn-version-alignment`)中实现 INI 保存时保持原始小节顺序与小节内原始键值对顺序(支持自适应纯数/多段数字自然排序),实现与 [`YR_RN_Mission_Editor`](file:///D:/Developments/RevengeNow/Codes/YR_RN_Mission_Editor) 行为全面对齐;提供模块化配置项 `SaveMap.AdaptiveSorting` / `SaveMap.PreserveINISorting`,以 **CINIExt 动态函数指针零开销切换机制** 包办所有 INI 变更操作;并建立完整的单元测试体系 (UT)、持续集成流水线 (CI) 与多版本发包分拣脚本。 + +--- + +## 一、方案核心架构设计 + +### 1.1 零破坏解耦设计 (Decoupled Sidecar Design) +由于旧版 32 位 `FinalSun.exe` / `FinalAlert2YR.dat` 核心逻辑与 `CINI` / `INISection` 的二进制结构存在紧耦合(IDA 逆向已证实 `EntitiesDictionary` 与 `IndicesDictionary` 具有固定内存偏移),任何直接修改 `sizeof(INISection)` 或改变内部 `std::map` 类型的改动都会引发主程序崩溃。 + +因此采用**外置侧车追踪器 (`CINIOrderTracker`)**: +- 内部核心数据结构为 **`SequencedKeyList`**,采用 **$O(1)$ Multi-Index 双索引模型**: + $$\text{SequencedKeyList} = \text{std::list} \;+\; \text{std::unordered\_map}$$ +- **复杂度表现**: + - **插入 (Add)**:$O(1)$,若已存在则忽略,不存在则尾插并记录迭代器。 + - **删除 (Remove)**:$O(1)$,通过哈希表直接定位链表迭代器擦除,避免 `std::vector` 的 $O(N)$ 元素搬移开销。 + - **查询/存在性检查 (Contains)**:$O(1)$。 + - **遍历 (Iteration)**:严格按照自然插入顺序顺序遍历。 + +### 1.2 自适应键排序 (Adaptive Sorting) +针对红警地图逻辑中具有严苛顺序要求的特殊 Section(例如脚本动作序号 `0, 1, 2...`、单位结构列表 `[Units]`, `[Structures]`、触发 `[TeamTypes]`): +- **文本键(非数字)**:严格保持文件读取与创建时的插入自然队列顺序(FIFO 语义)。 +- **纯数字 / 多段数字键**:在删除再新增时,采用 Westwood 格式自然数值排序器(`WestwoodNumericKeyCompare`),避免将重新添加的编号键挤压到 Section 末尾导致地图解析逻辑错乱。 + +### 1.3 CINIExt 代理与动态函数指针零开销切换机制 (Dynamic Function Pointer Dispatch) +为了杜绝在业务代码或单元测试中到处“手动补充调用 `RecordKey` / `RemoveKey`”的别扭耦合,同时避免在热点写入路径上产生不必要的分支判断: +1. **全局静态单例包装**: + - 声明 `CINIExt` 继承自 `CINI`(`sizeof(CINIExt) == sizeof(CINI) == 80` 字节,零新增成员变量,确保直接重映射 `0x7ACC80` 时不会踩踏相邻结构 `CMapData::Size`)。 + - 提供 `CINIExt::CurrentDocument` 全局引用,并在注释中严格规范所有开发者统一使用该实例。 +2. **函数指针动态切换**: + - 内部静态函数指针:`FnRecordKey`、`FnRemoveKey`、`FnRecordSection`、`FnRemoveSection`。 + - 当 `AdaptiveSorting` / `PreserveINISorting` 启用时:函数指针指向真实的 `CINIOrderTracker::RecordKey` / `RecordSection`。 + - 当未启用时:函数指针在 `SetAdaptiveSorting(false)` 时直接切换为内部 `static void NoOp(...) noexcept {}` 空函数。 + - **极致性能**:关闭功能时无任何分支条件判断 (`if`),直接调用空函数内联或直接返回,开销趋近于 0。 + +--- + +## 二、单元测试 (UT) 架构与用例矩阵 + +### 2.1 In-Process 进程内测试架构 +传统单元测试难以覆盖与 `FinalAlert2YR.dat` 二进制宿主高度绑定的 INI 内存与钩子逻辑。为此设计并实现了进程内挂钩测试架构: +- **测试工程**:位于 `FA2sp.UnitTest/`,编译生成 32 位动态链接库 `Supplementary/FA2sp.UnitTest.dll`。 +- **Syringe 动态挂钩**: + - 测试 DLL 利用 Syringe 挂钩宿主程序入口 `CFinalSunApp::InitInstance`(地址 `0x41FAD0`)。 + - 入口劫持:在宿主创建任何 MFC GUI 窗口之前接管控制权。 +- **输出重定向与干净退出**: + - 将 stdout / stderr 重定向到 `Supplementary/UnitTest.log`,并通过 GoogleTest 导出 `Supplementary/UnitTest.xml`。 + - 将命令行参数转发给 `testing::InitGoogleTest(&argc, argv)`,执行 `RUN_ALL_TESTS()`。 + - 测试执行完毕后刷新缓冲区并直接调用 `ExitProcess(res)` 退出,杜绝弹出主界面阻塞 CI 无人值守环境。 + +### 2.2 运行隔离机制 (Strict Isolation Mechanism) +- 运行测试时,宿主目录下如果同时存在生产 DLL `FA2sp.dll`,会导致 Hook 符号与 Syringe 注入表发生冲突。 +- 启动脚本 [`Scripts/RunUnitTest.bat`](file:///D:/Developments/RevengeNow/Codes/FA2Copy/Scripts/RunUnitTest.bat) 实现了自动化隔离: + 1. 检查并将 `FA2sp.dll` 临时重命名为 `FA2sp.dll.isolated`; + 2. 调用 `Syringe.exe FinalAlert2YR.dat` 注入 `FA2sp.UnitTest.dll` 执行测试; + 3. 捕获退出码; + 4. 无论测试成功还是异常退出,均在 `finally` 阶段无条件还原 `FA2sp.dll`; + 5. 返回真实退出码,确保自动化流程捕获断言失败。 + +### 2.3 单元测试覆盖矩阵 (21 Tests) +在 [`FA2sp.UnitTest/CINIOrderTrackerTest.cpp`](file:///D:/Developments/RevengeNow/Codes/FA2Copy/FA2sp.UnitTest/CINIOrderTrackerTest.cpp) 中构建了 21 个自动化测试用例,覆盖所有排列组合与边界情况: + +| 测试套件 | 测试用例名称 | 核心验证内容 | +| :--- | :--- | :--- | +| **SequencedKeyListTest** | `PreservesInsertionOrder` | 验证多键按插入顺序依次存储与输出 | +| | `IgnoresDuplicateAdditions` | 重复插入相同键时忽略,不改变原有链表位置 | +| | `ReAddAfterRemovePushedToTailQueueSemantics` | 模拟 FIFO 队列语义:删除后再插入排至队尾 | +| | `RemoveHeadAndTail` | 链表头节点与尾节点删除的边界处理 | +| | `ClearResetsState` | 清空链表与哈希表,状态完全重置 | +| **CINIOrderTrackerTest** | `SectionOrderTrackingAndQueueSemantics` | Section 维度的插入保序与删除再加尾部追加语义 | +| | `KeyOrderTrackingAndQueueSemantics` | Section 内部 Key 维度的插入保序与顺序追踪 | +| | `RealCINICrudKeyOperations` | 结合真实 `CINIExt` 的 `WriteString` / `DeleteKey` 透明追踪 | +| | `RealCINICrudSectionOperations` | 结合真实 `CINIExt` 的 `AddSection` / `DeleteSection` 透明追踪 | +| | `RealCINIAutoTrackingUntrackedItems` | 未显式通过 `RecordKey` 录入的新键在遍历时自动捕获 | +| | `RealCINIDeletedItemsOmittedDuringIterationEvenWithoutUntrack` | 被 CINI 删除的键即使 Tracker 未同步也会在迭代时自动剔除 | +| | `RealCINIPreserveOrderTrueVsNaturalMapOrder` | 对比测试:开启保序输出 vs 未开启时 `std::map` 字母序输出 | +| | `RealCINIComplexMapLifecycleSimulation` | 模拟复杂地图加载、增删修改、保存重构全生命周期 | +| | `RealCINISameKeyDeletedAndReaddedHeadMiddleTailCombinations` | 单 Section 内头、中、尾不同位置 Key 删除再添加组合 | +| | `RealCINISameSectionDeletedAndReaddedHeadMiddleTailCombinations` | 多个 Section 头、中、尾不同位置删除再添加组合 | +| | `RealCINIInterleavedKeyAndSectionDeleteReaddStressTest` | 节与键高频交替增删的极端压力场景 | +| | `AdaptiveSortingNumericKeysPreserveNaturalOrderOnDeleteAndReadd` | **核心自适应排序**:删除中间数字键(如 1)再添加时,自动维持 `0, 1, 2, 3` 自然数字升序而非排到末尾 | +| | `AdaptiveSortingWestwoodNumericComparatorMultiDigit` | **Westwood 自然数值比较**:验证多位数升序排序(如 `0..9, 10, 11` 正确排在 9 之后,而非按 ASCII 字符排在 1 之后) | +| | `AdaptiveSortingMixedSectionPreservesTextHeaderAndOrdersNumericArray` | **混合小节测试**:头部元数据文本项维持原序,尾部数字动作阵列自适应升序 | +| | `AdaptiveSortingUntrackedKeyWithoutManualRecordKeyAutoSorted` | 真实地图加载场景下,直接载入未追踪文本段同样按规则正确排布 | +| | `DynamicFunctionPointerSwitchingAdaptiveSortingToggle` | **动态函数指针切换验证**:关闭自适应排序时指向 `NoOp`(零开销无记录),开启后实时挂接追踪 | + +--- + +## 三、持续集成 (CI) 工作流改动 + +CI 架构重构为基于 GitHub Actions 的模块化、多触发点流水线体系: + +```mermaid +flowchart TD + PR[Pull Request] --> MSBuildWF[msbuild.yml] + Merge[Master Push / Merged PR] --> ReleaseWF[release.yml] + Dispatch[workflow_dispatch 手动触发] --> ReleaseWF + + MSBuildWF --> Reusable[reusable-build-and-test.yml] + ReleaseWF --> Reusable + + subgraph ReusableJob ["reusable-build-and-test.yml (windows-2022)"] + Checkout[递归拉取代码与子模块 lfs] --> NuGet[还原 NuGet 依赖] + NuGet --> Build[MSBuild 编译 FA2sp.sln x86 Release] + Build --> DeployBin[部署产物至 Supplementary] + DeployBin --> RunUT[执行 Scripts/RunUnitTest.bat] + RunUT --> UploadLogs[上传 UT 日志与 XML 报告] + UploadLogs --> UploadArtifacts[上传构建产物 FA2sp.dll / pdb] + end + + ReusableJob --> PackageMatrix["package 任务 (矩阵: YR, MO, RN)"] + PackageMatrix --> DownloadBin[下载编译构建产物] + DownloadBin --> ScriptPkg["./Scripts/PackageRelease.ps1"] + ScriptPkg --> GHRelease[发布至 GitHub Releases] +``` + +### 3.1 可复用构建与测试工作流 ([`reusable-build-and-test.yml`](file:///D:/Developments/RevengeNow/Codes/FA2Copy/.github/workflows/reusable-build-and-test.yml)) +- **运行环境**:`windows-2022` 虚拟机。 +- **子模块与大文件处理**:配置 `submodules: recursive` 与 `lfs: true`,确保 `FA2pp`、`MFC42`、`googletest`、`lexilla`、`scintilla` 完整克隆。 +- **依赖还原与并发编译**: + - 自动通过 `nuget restore FA2sp.sln` 还原项目第三方依赖(如 `lua`、`openssl`)。 + - 执行 `msbuild /m /p:Configuration=Release /p:Platform=x86 FA2sp.sln`,一次性并发编译主工程与 UT 工程。 +- **自动化测试执行与日志保全**: + - 无缝调用 `Scripts\RunUnitTest.bat` 运行测试套件。 + - 采用 `if: always()` 策略,无论测试成功还是失败,均完整收集并上传 `UnitTest.log`、`UnitTest.xml` 及 `syringe.log`(保留 7 天),便于远端复现和排查断言错误。 + +### 3.2 PR 校验流水线 ([`msbuild.yml`](file:///D:/Developments/RevengeNow/Codes/FA2Copy/.github/workflows/msbuild.yml)) +- 监听所有分支的 PR 创建与更新(过滤图片和文档变动),调用 `reusable-build-and-test.yml` 进行严格的零警告、全绿测试卡点验证,防止任何破坏性改动合并入主干。 + +### 3.3 自动发版流水线 ([`release.yml`](file:///D:/Developments/RevengeNow/Codes/FA2Copy/.github/workflows/release.yml)) +- 监听 `master` 分支的 PR 合并或手动调度。 +- 自动计算流水号版本(如 `v1.6.x`)或接受自定义 Tag。 +- 启动 `matrix: [YR, MO, RN]` 矩阵任务,并行调用发包脚本分发三种针对性架构的 ZIP 包。 + +--- + +## 四、Release 脚本分拣与打包思路 + +打包脚本 [`Scripts/PackageRelease.ps1`](file:///D:/Developments/RevengeNow/Codes/FA2Copy/Scripts/PackageRelease.ps1) 承担了本地发布模拟与 CI 矩阵打包的核心职能。其设计理念为:**分层覆盖 (Layered Overlay) + 白名单/黑名单清洗 (Sanitization)**。 + +### 4.1 分层覆盖分拣流程 +每个发行版(Edition)的打包流水线严格遵循以下阶段: + +```mermaid +flowchart LR + Base[Supplementary/ 基础公共文件] --> Staging[临时暂存区 staging_TARGET] + Custom[Supplementary/TARGET/ 专属配置] -->|覆盖写入| Staging + Bin[bin_output/ 最新 FA2sp.dll / pdb] -->|注入| Staging + Clean[清洗剔除: UnitTest.dll, *.log, *.xml] --> Staging + Staging --> Compress[Compress-Archive 生成 FA2sp-TARGET-Version.zip] +``` + +1. **第一层:基底拷贝 (Base Tree)** + - 将 `Supplementary/` 下的所有通用编辑器运行资源(`FinalAlert2YR.dat`、核心 CSF 语言包、公共图块、Syringe 运行库等)全量克隆到暂存区 `staging_$t`。 +2. **第二层:版本差异化分拣与覆盖 (Edition Customization)** + - **`YR` (原版尤里复仇)**: + - 直接删除暂存区中的 `MentalOmega/` 与 `RevengeNow/` 目录。 + - 保留默认的 `FAData.ini` 及原生 YR 触发/小队/动作配置。 + - **`MO` (心灵终结 Mental Omega)**: + - 递归读取 `staging/MentalOmega/` 下的文件,逐项覆盖到暂存区根目录(如 MO 专属 `FAData.ini`、CSF 与定制库)。 + - 删除多余的 `MentalOmega/` 与 `RevengeNow/` 子目录。 + - **`RN` (复仇时刻 Revenge Now)**: + - 递归读取 `staging/RevengeNow/` 下的文件,逐项覆盖到暂存区根目录。 + - 注入 RN 专属配置: + - `SaveMap.PreserveINISorting=true`(保持小节物理出现顺序); + - `SaveMap.AdaptiveSorting=true`(自适应数字键递增排序与文本键保序); + - `SaveMap.KeepComments=false`(不保留注释); + - `UTF8Support.AlwaysSaveAsUTF8=true`(**总是以 UTF-8 编码保存地图**); + - 157 项定制 Ares 动作与完整 RN 阵营定义。 + - 删除多余的 `MentalOmega/` 与 `RevengeNow/` 子目录。 +3. **第三层:编译二进制注入 (Binary Injection)** + - 自动探测 `bin_output/` 或 `Release/` 目录,将最新编译的 `FA2sp.dll` 与符号文件 `FA2sp.pdb` 复制到暂存区。 +4. **第四层:研发与测试产物深度清洗 (Sanitization)** + - 严格剔除开发/CI 产生的中间文件,防止污染用户包: + - 删除 `FA2sp.UnitTest.dll`; + - 删除 `RunUnitTest.bat`; + - 删除所有日志 `*.log`、报告 `*.xml`; + - 删除隔离标记文件 `*.isolated`。 +5. **第五层:归档与统计** + - 使用 PowerShell 原生 `Compress-Archive` 输出标准发布包:`FA2sp-{Target}-{Version}.zip`。 + - 打印生成的归档大小并清理暂存工作区。 + +--- + +## 五、开发者规范与注意事项 + +1. **全局地图对象操作守则**: + - 在 `FA2sp` 所有模块与对话框窗口中,**必须始终操作 `CINIExt::CurrentDocument`**,严禁直接使用底层 `CINI::CurrentDocument`。 + - 直接访问 `CINI::CurrentDocument` 会绕过 `CINIExt` 的虚函数与成员拦截(`WriteString`、`DeleteKey`、`AddSection` 等),导致 `CINIOrderTracker` 追踪断裂。 +2. **子模块保护红线**: + - `FA2pp/` 为第三方子模块,**绝对不得做任何就地修改或提交**。 +3. **提交与推送规范**: + - 所有变更必须在本地完成 `FA2sp.sln` 编译通过(0 警告、0 错误)与 `Scripts\RunUnitTest.bat` 21 项测试全绿通过后方可提交。 + - 未经用户显式授权,**严禁执行 `git push`**。 diff --git a/googletest b/googletest new file mode 160000 index 00000000..b514bdc8 --- /dev/null +++ b/googletest @@ -0,0 +1 @@ +Subproject commit b514bdc898e2951020cbdca1304b75f5950d1f59