diff --git a/SKSE/Plugins/ArtifactTrackerFunctions.dll b/SKSE/Plugins/ArtifactTrackerFunctions.dll new file mode 100644 index 0000000..3a49b5a Binary files /dev/null and b/SKSE/Plugins/ArtifactTrackerFunctions.dll differ diff --git a/Scripts/ETR_CellStorageScript.pex b/Scripts/ETR_CellStorageScript.pex deleted file mode 100644 index d7f75eb..0000000 Binary files a/Scripts/ETR_CellStorageScript.pex and /dev/null differ diff --git a/Scripts/ETR_Functions.pex b/Scripts/ETR_Functions.pex new file mode 100644 index 0000000..0a56104 Binary files /dev/null and b/Scripts/ETR_Functions.pex differ diff --git a/Scripts/ETR_TrackFoundItems.pex b/Scripts/ETR_TrackFoundItems.pex index 58cc78b..e17fe19 100644 Binary files a/Scripts/ETR_TrackFoundItems.pex and b/Scripts/ETR_TrackFoundItems.pex differ diff --git a/Scripts/ETR_TrackNewItems.pex b/Scripts/ETR_TrackNewItems.pex index 885b796..86ed3f6 100644 Binary files a/Scripts/ETR_TrackNewItems.pex and b/Scripts/ETR_TrackNewItems.pex differ diff --git a/Scripts/ETR_TrackStoredItems.pex b/Scripts/ETR_TrackStoredItems.pex index 9320147..a4f1096 100644 Binary files a/Scripts/ETR_TrackStoredItems.pex and b/Scripts/ETR_TrackStoredItems.pex differ diff --git a/Source/DLL/.clang-format b/Source/DLL/.clang-format new file mode 100644 index 0000000..a74ee9e --- /dev/null +++ b/Source/DLL/.clang-format @@ -0,0 +1,101 @@ +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: 'false' +AlignConsecutiveBitFields: 'false' +AlignConsecutiveDeclarations: 'false' +AlignConsecutiveMacros: 'false' +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: 'true' +AllowAllArgumentsOnNextLine: 'false' +AllowAllConstructorInitializersOnNextLine: 'false' +AllowAllParametersOfDeclarationOnNextLine: 'false' +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: 'false' +AllowShortEnumsOnASingleLine: 'true' +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: 'true' +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: 'true' +AlwaysBreakTemplateDeclarations: 'Yes' +BinPackArguments: 'true' +BinPackParameters: 'true' +BitFieldColonSpacing: After +BraceWrapping: + AfterCaseLabel: 'true' + AfterClass: 'true' + AfterControlStatement: 'false' + AfterEnum: 'true' + AfterFunction: 'true' + AfterNamespace: 'true' + AfterStruct: 'true' + AfterUnion: 'true' + AfterExternBlock: 'true' + BeforeCatch: 'false' + BeforeElse: 'false' + BeforeLambdaBody: 'false' + BeforeWhile: 'false' + IndentBraces: 'false' + SplitEmptyFunction: 'false' + SplitEmptyRecord: 'false' + SplitEmptyNamespace: 'false' +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: 'false' +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: 'true' +ColumnLimit: 0 +CompactNamespaces: 'false' +ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: 'false' +DeriveLineEnding: 'true' +DerivePointerAlignment: 'false' +DisableFormat: 'false' +FixNamespaceComments: 'false' +IncludeBlocks: Preserve +IndentCaseBlocks: 'true' +IndentCaseLabels: 'false' +IndentExternBlock: Indent +IndentGotoLabels: 'false' +IndentPPDirectives: AfterHash +IndentWidth: 4 +IndentWrappedFunctionNames: 'true' +KeepEmptyLinesAtTheStartOfBlocks: 'false' +Language: Cpp +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +PointerAlignment: Left +ReflowComments : 'false' +SortIncludes: 'true' +SortUsingDeclarations: 'true' +SpaceAfterCStyleCast: 'false' +SpaceAfterLogicalNot: 'false' +SpaceAfterTemplateKeyword: 'true' +SpaceBeforeAssignmentOperators: 'true' +SpaceBeforeCpp11BracedList: 'false' +SpaceBeforeCtorInitializerColon: 'true' +SpaceBeforeInheritanceColon: 'true' +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: 'true' +SpaceBeforeSquareBrackets: 'false' +SpaceInEmptyBlock: 'false' +SpaceInEmptyParentheses: 'false' +SpacesBeforeTrailingComments: 2 +SpacesInAngles: 'false' +SpacesInCStyleCastParentheses: 'false' +SpacesInConditionalStatement: 'false' +SpacesInContainerLiterals: 'true' +SpacesInParentheses: 'false' +SpacesInSquareBrackets: 'false' +Standard: Latest +TabWidth: 4 +UseCRLF: 'true' +UseTab: AlignWithSpaces + +... diff --git a/Source/DLL/.gitignore b/Source/DLL/.gitignore new file mode 100644 index 0000000..6214d6e --- /dev/null +++ b/Source/DLL/.gitignore @@ -0,0 +1,539 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/clion,visualstudio,visualstudiocode,cmake +# Edit at https://www.toptal.com/developers/gitignore?templates=clion,visualstudio,visualstudiocode,cmake + +### CLion ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf +.vscode/ + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### CLion Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# Local History for Visual Studio Code +.history/ + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Support for Project snippet scope +!.vscode/*.code-snippets + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Nuget personal access tokens and Credentials +# nuget.config + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools + +# Local History for Visual Studio Code + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +.idea/ +*.sln.iml + +### VisualStudio Patch ### +# Additional files built by Visual Studio + +# End of https://www.toptal.com/developers/gitignore/api/clion,visualstudio,visualstudiocode,cmake + +build/ + +contrib/Distribution/**/*.dll +contrib/Distribution/**/*.pdb +contrib/Distribution/**/*.pex diff --git a/Source/DLL/CMakeLists.txt b/Source/DLL/CMakeLists.txt new file mode 100644 index 0000000..138a6ed --- /dev/null +++ b/Source/DLL/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.21) +message("Using toolchain file ${CMAKE_TOOLCHAIN_FILE}.") + +######################################################################################################################## +## Define project +######################################################################################################################## +project( + ArtifactTrackerFunctions + VERSION 1.0.0 + DESCRIPTION "Eddoursul's Artifact Tracker" + LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) + +include(GNUInstallDirs) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/version.rc + @ONLY) + +set(sources + src/Main.cpp + src/Papyrus.cpp + + ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + +source_group( + TREE ${CMAKE_CURRENT_SOURCE_DIR} + FILES + ${headers} + ${sources}) + +######################################################################################################################## +## Configure target DLL +######################################################################################################################## +find_package(CommonLibSSE CONFIG REQUIRED) + +add_commonlibsse_plugin(${PROJECT_NAME} SOURCES ${headers} ${sources}) +add_library("${PROJECT_NAME}::${PROJECT_NAME}" ALIAS "${PROJECT_NAME}") + +target_include_directories(${PROJECT_NAME} + PRIVATE + $ + $ + $) + +target_include_directories(${PROJECT_NAME} + PUBLIC + $) + +target_precompile_headers(${PROJECT_NAME} + PRIVATE + src/PCH.h) + +install(TARGETS ${PROJECT_NAME} + DESTINATION "${CMAKE_INSTALL_LIBDIR}") + + + +######################################################################################################################## +## Automatic plugin deployment +######################################################################################################################## + +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_SOURCE_DIR}/../../SKSE/Plugins/") +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ "${CMAKE_CURRENT_SOURCE_DIR}/../../SKSE/Plugins/") diff --git a/Source/DLL/CMakePresets.json b/Source/DLL/CMakePresets.json new file mode 100644 index 0000000..566eb6b --- /dev/null +++ b/Source/DLL/CMakePresets.json @@ -0,0 +1,252 @@ +{ + "version": 2, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_FLAGS": "$env{COMMONLIBSSE_COMPILER} $env{COMMONLIBSSE_PLATFORM} $env{COMMONLIBSSE_TEXT}" + } + }, + { + "name": "vcpkg", + "hidden": true, + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "VCPKG_TARGET_TRIPLET": "x64-windows-skse", + "VCPKG_HOST_TRIPLET": "x64-windows-skse", + "VCPKG_OVERLAY_TRIPLETS": "${sourceDir}/cmake", + "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>DLL" + } + }, + { + "name": "win32", + "hidden": true, + "environment": { + "COMMONLIBSSE_PLATFORM": "-DWIN32_LEAN_AND_MEAN -DNOMINMAX" + } + }, + { + "name": "win32-unicode", + "hidden": true, + "inherits": "win32", + "environment": { + "COMMONLIBSSE_TEXT": "-DUNICODE -D_UNICODE" + } + }, + { + "name": "x64", + "hidden": true, + "architecture": { + "value": "x64", + "strategy": "external" + } + }, + { + "name": "msvc", + "hidden": true, + "environment": { + "COMMONLIBSSE_COMPILER": "/permissive- /Zc:preprocessor /EHsc $penv{CXXFLAGS}" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-msvc-x64", + "enableMicrosoftCodeAnalysis": true, + "enableClangTidyCodeAnalysis": true + } + } + }, + { + "name": "clang-cl", + "hidden": true, + "cacheVariables": { + "CMAKE_C_COMPILER": "clang-cl", + "CMAKE_CXX_COMPILER": "clang-cl" + }, + "environment": { + "COMMONLIBSSE_COMPILER": "/permissive- /EHsc -Wno-overloaded-virtual -Wno-delete-non-abstract-non-virtual-dtor -D__cpp_lib_char8_t -D__cpp_consteval -D__cpp_lib_format $penv{CXXFLAGS}" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-clang-x64", + "enableMicrosoftCodeAnalysis": true, + "enableClangTidyCodeAnalysis": true + } + } + }, + { + "name": "build-tests", + "displayName": "Build Tests", + "hidden": true, + "description": "Include test suites in the build.", + "cacheVariables": { + "BUILD_TESTS": { + "type": "STRING", + "value": "ON" + } + } + }, + { + "name": "build-release-msvc", + "inherits": [ + "base", + "vcpkg", + "win32-unicode", + "x64", + "build-tests", + "msvc" + ], + "displayName": "Release", + "description": "Optimized release build.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/release-msvc", + "cacheVariables": { + "CMAKE_BUILD_TYPE": { + "type": "STRING", + "value": "Release" + } + } + }, + { + "name": "build-debug-msvc", + "inherits": [ + "base", + "vcpkg", + "win32-unicode", + "x64", + "build-tests", + "msvc" + ], + "displayName": "Debug", + "description": "Debug build for testing.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/debug-msvc", + "cacheVariables": { + "CMAKE_BUILD_TYPE": { + "type": "STRING", + "value": "Debug" + } + } + }, + { + "name": "build-debug-clang-cl", + "inherits": [ + "base", + "vcpkg", + "win32-unicode", + "x64", + "build-tests", + "clang-cl" + ], + "displayName": "Debug", + "description": "Debug build for testing.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/debug-clang", + "cacheVariables": { + "CMAKE_BUILD_TYPE": { + "type": "STRING", + "value": "Debug" + } + } + }, + { + "name": "build-release-clang-cl", + "inherits": [ + "base", + "vcpkg", + "win32-unicode", + "x64", + "build-tests", + "clang-cl" + ], + "displayName": "Release", + "description": "Optimized release build.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/release-clang", + "cacheVariables": { + "CMAKE_BUILD_TYPE": { + "type": "STRING", + "value": "Release" + } + } + } + ], + "buildPresets": [ + { + "name": "release-msvc", + "displayName": "Release (MSVC)", + "configurePreset": "build-release-msvc", + "description": "Optimized release build." + }, + { + "name": "debug-msvc", + "displayName": "Debug (MSVC)", + "configurePreset": "build-debug-msvc", + "description": "Debug build for testing." + }, + { + "name": "release-clang-cl", + "displayName": "Release (Clang)", + "configurePreset": "build-release-clang-cl", + "description": "Optimized release build." + }, + { + "name": "debug-clang-cl", + "displayName": "Debug (Clang)", + "configurePreset": "build-debug-clang-cl", + "description": "Debug build for testing." + } + ], + "testPresets": [ + { + "name": "tests-all", + "displayName": "All Tests", + "configurePreset": "build-debug-msvc", + "output": { + "outputOnFailure": true + }, + "execution": { + "noTestsAction": "error", + "stopOnFailure": false + } + }, + { + "name": "tests-unit", + "displayName": "Unit Tests", + "description": "Runs tests that do not require any Skyrim module loaded into the process.", + "inherits": "tests-all", + "filter": { + "exclude": { + "label": "[integration],[e2e]" + } + } + }, + { + "name": "tests-integration", + "displayName": "Integration Tests", + "description": "Runs tests that interact with a Skyrim module at rest (do not require the Skyrim module to have run any main function).", + "inherits": "tests-all", + "filter": { + "include": { + "label": "[integration]" + } + } + }, + { + "name": "tests-e2e", + "displayName": "End-to-End Tests", + "description": "Runs test that depend on a fully running Skyrim engine in the process.", + "inherits": "tests-all", + "filter": { + "include": { + "label": "[e2e]" + } + } + } + ] +} diff --git a/Source/DLL/cmake/version.rc.in b/Source/DLL/cmake/version.rc.in new file mode 100644 index 0000000..c0d89e4 --- /dev/null +++ b/Source/DLL/cmake/version.rc.in @@ -0,0 +1,32 @@ +#include + +1 VERSIONINFO + FILEVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 + PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "@PROJECT_DESCRIPTION@" + VALUE "FileVersion", "@PROJECT_VERSION@" + VALUE "InternalName", "@PROJECT_NAME@" + VALUE "LegalCopyright", "MIT License" + VALUE "ProductName", "@PROJECT_FRIENDLY_NAME@" + VALUE "ProductVersion", "@PROJECT_VERSION@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Source/DLL/cmake/x64-windows-skse.cmake b/Source/DLL/cmake/x64-windows-skse.cmake new file mode 100644 index 0000000..2925afa --- /dev/null +++ b/Source/DLL/cmake/x64-windows-skse.cmake @@ -0,0 +1,8 @@ +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) + +if (${PORT} MATCHES "fully-dynamic-game-engine|skse|qt*") + set(VCPKG_LIBRARY_LINKAGE dynamic) +else () + set(VCPKG_LIBRARY_LINKAGE static) +endif () diff --git a/Source/DLL/src/Functions/ArtifactTracker.h b/Source/DLL/src/Functions/ArtifactTracker.h new file mode 100644 index 0000000..d30461c --- /dev/null +++ b/Source/DLL/src/Functions/ArtifactTracker.h @@ -0,0 +1,180 @@ +#pragma once + +namespace Papyrus::ArtifactTracker +{ + inline bool is_artifact(RE::TESForm* a_form, RE::TESForm* a_excludeForm = NULL) + { + const auto formType = a_form->GetFormType(); + + if (formType != RE::FormType::Armor && formType != RE::FormType::Weapon && formType != RE::FormType::Book && formType != RE::FormType::Misc) { + return false; + } + + if (Papyrus::ObjectReference::inv_util::is_excluded(a_form, a_excludeForm)) { + return false; + } + + return true; + } + + inline RE::TESObjectREFR* GetCellStorage(RE::StaticFunctionTag*, + RE::TESObjectREFR* a_ref, + RE::BGSListForm* a_refList, + RE::TESBoundObject* a_objectToCreate, + bool a_autoCreate = true) + { + RE::TESObjectREFR* result = NULL; + + if (!a_ref || !a_refList || !a_objectToCreate) { + return result; + } + + RE::TESObjectCELL* cell = a_ref->GetParentCell(); + + a_refList->ForEachForm([&result, &cell, &a_objectToCreate](RE::TESForm& a_exform) { + const auto ref = a_exform.As(); + + if (ref && ref->GetParentCell() == cell && ref->GetBaseObject()->formID == a_objectToCreate->formID) { + result = ref; + return false; + } + + return true; + }); + + if (!result && a_autoCreate) { + result = a_ref->PlaceObjectAtMe(a_objectToCreate, true).get(); + result->Disable(); + a_refList->AddForm(result); + } + + return result; + } + + inline bool HasRefInCell(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, + RE::TESBoundObject* a_item, + RE::TESObjectCELL* a_cell = NULL, + bool a_checkInContainers = true, + RE::BGSListForm* a_excludeList = NULL) + { + bool bResult = false; + + if (!a_cell) { + a_cell = RE::PlayerCharacter::GetSingleton()->GetParentCell(); + } + + a_cell->ForEachReference([&](RE::TESObjectREFR& a_ref) { + const auto baseObj = a_ref.GetBaseObject(); + + if (a_item->formID == baseObj->formID) { + if (a_ref.IsDisabled() || a_ref.IsMarkedForDeletion()) { + return true; + } + if (a_excludeList && a_excludeList->HasForm(a_ref.formID)) { + return true; + } + bResult = true; + return false; + } else if (a_checkInContainers) { + if (baseObj->formType == RE::FormType::Container || (baseObj->formType == RE::FormType::NPC && !a_ref.IsDisabled() && baseObj->As()->GetRace()->formID == 0x0010760A)) { + if (a_excludeList && a_excludeList->HasForm(a_ref.formID)) { + return true; + } + const auto inv = a_ref.GetInventory([&](RE::TESBoundObject& a_object) -> bool { + return a_item->formID == a_object.formID; + }); + const auto it = inv.find(a_item); + if (it != inv.end() ? it->second.first : 0) { + bResult = true; + return false; + } + } + } + + return true; + }); + + return bResult; + } + + inline void SyncCellStorage(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, + RE::TESObjectREFR* a_cellStorage, + RE::TESForm* a_excludeForm = NULL) + { + if (!a_cellStorage) { + return; + } + + std::unordered_map cellItems; + + const auto storageBase = a_cellStorage->GetBaseObject(); + const auto cell = a_cellStorage->GetParentCell(); + const auto inv = a_cellStorage->GetInventory(); + + for (const auto a_ref : cell->references) { + const auto baseObj = a_ref->GetBaseObject(); + + if (baseObj->formType == RE::FormType::Container || (baseObj->formType == RE::FormType::NPC && !a_ref->IsDisabled() && baseObj->As()->GetRace()->formID == 0x0010760A)) { + if (a_ref->GetBaseObject()->formID == storageBase->formID) { + continue; + } + + const auto contInv = a_ref->GetInventory(); + + for (const auto& [item, data] : contInv) { + if (cellItems.contains(item->formID)) { + continue; + } + const auto& [count, entry] = data; + if (count > 0) { + cellItems[item->formID] = true; + if (inv.find(item) == inv.end()) { + if (is_artifact(item, a_excludeForm)) { + a_cellStorage->AddObjectToContainer(item, nullptr, 1, nullptr); + } + } + } + } + + continue; + } + + if (a_ref->IsDisabled() || a_ref->IsMarkedForDeletion()) { + continue; + } + + if (cellItems.contains(baseObj->formID)) { + continue; + } + + if (!is_artifact(baseObj, a_excludeForm)) { + continue; + } + + cellItems[baseObj->formID] = true; + + if (inv.find(baseObj) == inv.end()) { + a_cellStorage->AddObjectToContainer(baseObj, nullptr, 1, nullptr); + } + } + + for (const auto& [item, data] : inv) { + const auto& [count, entry] = data; + if (count > 0 && !cellItems.contains(item->formID)) { + a_cellStorage->RemoveItem(item, count, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr); + } + } + + cellItems.clear(); + } + + inline void Bind(VM& a_vm) + { + BIND(GetCellStorage); + logger::info("Registered GetCellStorage"sv); + BIND(HasRefInCell); + logger::info("Registered HasRefInCell"sv); + BIND(SyncCellStorage); + logger::info("Registered SyncCellStorage"sv); + } +} diff --git a/Source/DLL/src/Functions/ObjectReference.h b/Source/DLL/src/Functions/ObjectReference.h new file mode 100644 index 0000000..5958db9 --- /dev/null +++ b/Source/DLL/src/Functions/ObjectReference.h @@ -0,0 +1,270 @@ +#pragma once + +namespace Papyrus::ObjectReference +{ + namespace inv_util + { + inline bool can_be_taken(const std::unique_ptr& a_entry, bool a_noEquipped, bool a_noFavourited, bool a_noQuestItem) + { + if (a_noEquipped && a_entry->IsWorn()) { + return false; + } + if (a_noFavourited && a_entry->IsFavorited()) { + return false; + } + if (a_noQuestItem && a_entry->IsQuestObject()) { + return false; + } + if (a_entry->object->formID == 0x000001F4) { // Unarmed + return false; + } + return true; + } + + inline bool is_excluded(RE::TESForm* a_form, RE::TESForm* a_excludeForm = NULL) + { + if (!a_excludeForm) { + return false; + } + + const RE::BGSListForm* list = a_excludeForm->As(); + + if (list) { + if (list->HasForm(a_form)) { + return true; + } else { + bool isExcluded = false; + + list->ForEachForm([&](RE::TESForm& a_exform) { + const auto exlist = a_exform.As(); + if (exlist) { + if (exlist->forms.size() > 0) { + if (exlist->forms[0]->Is(RE::FormType::Keyword) ? a_form->HasKeywordInList(exlist, false) : exlist->HasForm(a_form)) { + isExcluded = true; + return false; + } + } + } else { + const auto exkeyword = a_exform.As(); + if (exkeyword && a_form->As()->HasKeyword(exkeyword)) { + isExcluded = true; + return false; + } + } + return true; + }); + + return isExcluded; + } + } + + const RE::BGSKeyword* keyword = a_excludeForm->As(); + + if (keyword) { + if (a_form->As()->HasKeyword(keyword)) { + return true; + } + } + + return false; + } + + inline RE::ITEM_REMOVE_REASON get_remove_reason(RE::TESObjectREFR* atargetRef) + { + RE::ITEM_REMOVE_REASON iReason = RE::ITEM_REMOVE_REASON::kStoreInContainer; + + if (atargetRef->As() && atargetRef->As()->IsPlayerTeammate()) { + iReason = RE::ITEM_REMOVE_REASON::kStoreInTeammate; + } + + return iReason; + } + } + + inline std::int32_t AddAllFormsToList(RE::StaticFunctionTag*, + RE::BGSListForm* a_targetList, + std::int32_t a_formType, + RE::TESForm* a_keywordOrList = NULL, + RE::TESForm* a_excludeForm = NULL, + bool a_onlyPlayable = true) + { + const auto formType = static_cast(a_formType); + + const auto keyword = a_keywordOrList ? a_keywordOrList->As() : nullptr; + const auto keywordList = a_keywordOrList ? a_keywordOrList->As() : nullptr; + + const auto dataHandler = RE::TESDataHandler::GetSingleton(); + + if (!dataHandler) { + return a_targetList->forms.size(); + } + + for (const auto& form : dataHandler->GetFormArray(formType)) { + if (!form) { + continue; + } + if (a_onlyPlayable && !form->GetPlayable()) { + continue; + } + if ((keyword && !form->As()->HasKeyword(keyword)) || (keywordList && !form->HasKeywordInList(keywordList, false))) { + continue; + } + if (a_excludeForm && inv_util::is_excluded(form, a_excludeForm)) { + continue; + } + a_targetList->AddForm(form); + } + + return a_targetList->forms.size(); + } + + inline std::int32_t AddItemsOfTypeAndKeywordToList(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, + RE::TESForm* a_refOrList, + RE::BGSListForm* a_targetList, + std::uint32_t a_formType = 0, + RE::TESForm* a_keywordOrList = NULL, + RE::TESForm* a_excludeForm = NULL, + bool a_noEquipped = TRUE, + bool a_noFavourited = TRUE, + bool a_noQuestItem = TRUE) + { + if (!a_refOrList) { + a_vm->TraceStack("a_refOrList in AddItemsOfTypeAndKeywordToList is None", a_stackID); + return 0; + } + if (!a_targetList) { + a_vm->TraceStack("a_targetList in AddItemsOfTypeAndKeywordToList is None", a_stackID); + return 0; + } + + if (a_refOrList->Is(RE::FormType::FormList)) { + a_refOrList->As()->ForEachForm([&](RE::TESForm& a_exform) { + const auto refrItem = a_exform.As(); + if (refrItem) { + AddItemsOfTypeAndKeywordToList(a_vm, a_stackID, {}, refrItem, a_targetList, a_formType, a_keywordOrList, a_excludeForm, a_noEquipped, a_noFavourited, a_noQuestItem); + } + return true; + }); + return a_targetList->forms.size(); + } + + const auto containerRef = a_refOrList->As(); + + if (!containerRef) { + a_vm->TraceStack("containerRef in AddItemsOfTypeAndKeywordToList is not a reference", a_stackID); + return 0; + } + + const auto formType = static_cast(a_formType); + const bool bNoType = formType == RE::FormType::None; + + const auto keyword = a_keywordOrList ? a_keywordOrList->As() : nullptr; + const auto keywordList = a_keywordOrList ? a_keywordOrList->As() : nullptr; + + auto inv = containerRef->GetInventory([&](RE::TESBoundObject& a_exform) { + return (bNoType || a_exform.formType == formType) + && ( + (!keyword && !keywordList) + || (keyword && a_exform.As()->HasKeyword(keyword)) + || (keywordList && a_exform.HasKeywordInList(keywordList, false))) + && (!a_excludeForm || !inv_util::is_excluded(&a_exform, a_excludeForm)) + && a_exform.GetPlayable() && a_exform.formID != 0x000001F4; + }); + + if (containerRef->IsPlayerRef()) { + for (const auto& [item, data] : inv) { + const auto& [count, entry] = data; + if (count > 0 && inv_util::can_be_taken(entry, a_noEquipped, a_noFavourited, a_noQuestItem)) { + a_targetList->AddForm(item); + } + } + } else { + for (const auto& [item, data] : inv) { + const auto& [count, entry] = data; + if (count > 0) { + a_targetList->AddForm(item); + } + } + } + + return a_targetList->forms.size(); + } + + inline std::uint32_t GetItemCountInList(RE::StaticFunctionTag*, + RE::BGSListForm* a_containerList, + RE::TESBoundObject* a_form) + { + if (!a_containerList || !a_form) { + return 0; + } + + std::uint32_t iResult = 0; + + a_containerList->ForEachForm([&](RE::TESForm& a_container) { + const auto refrItem = a_container.As(); + if (refrItem) { + const auto inv = refrItem->GetInventory([&](RE::TESBoundObject& a_object) -> bool { + return a_form->formID == a_object.formID; + }); + const auto it = inv.find(a_form); + iResult += it != inv.end() ? it->second.first : 0; + } + return true; + }); + + return iResult; + } + + inline std::uint32_t GetItemCountInActors(RE::StaticFunctionTag*, + std::vector a_refArray, // accepts ObjectReference[] and Actor[] + RE::TESBoundObject* a_form) + { + if (a_refArray.size() <= 0 || !a_form) { + return 0; + } + + std::uint32_t iResult = 0; + + for (RE::Actor* actorItem : a_refArray) { + if (actorItem) { + const auto inv = actorItem->GetInventory([&](RE::TESBoundObject& a_object) -> bool { + return a_form->formID == a_object.formID; + }); + const auto it = inv.find(a_form); + iResult += it != inv.end() ? it->second.first : 0; + } + } + + return iResult; + } + + // From po3's Papyrus Extender + inline std::vector GetPlayerFollowers(RE::StaticFunctionTag*) + { + std::vector result; + + if (const auto processLists = RE::ProcessLists::GetSingleton(); processLists) { + for (auto& actorHandle : processLists->highActorHandles) { + if (auto actor = actorHandle.get(); actor && actor->IsPlayerTeammate()) { + result.push_back(actor.get()); + } + } + } + + return result; + } + + inline void Bind(VM& a_vm) + { + BIND(AddAllFormsToList); + logger::info("Registered AddAllFormsToList"sv); + BIND(AddItemsOfTypeAndKeywordToList); + logger::info("Registered AddItemsOfTypeAndKeywordToList"sv); + BIND(GetItemCountInList); + logger::info("Registered GetItemCountInList"sv); + BIND(GetItemCountInActors); + logger::info("Registered GetItemCountInActors"sv); + BIND(GetPlayerFollowers); + logger::info("Registered GetPlayerFollowers"sv); + } +} diff --git a/Source/DLL/src/Main.cpp b/Source/DLL/src/Main.cpp new file mode 100644 index 0000000..2abc0f6 --- /dev/null +++ b/Source/DLL/src/Main.cpp @@ -0,0 +1,40 @@ +#include "Papyrus.h" + +using namespace RE::BSScript; +using namespace SKSE; +using namespace SKSE::log; +using namespace SKSE::stl; + +namespace { + void InitializeLogging() { + auto path = logger::log_directory(); + if (!path) { + stl::report_and_fail("Failed to find standard logging directory"sv); + } + + *path /= "ArtifactTrackerFunctions.log"sv; + auto sink = std::make_shared(path->string(), true); + + auto log = std::make_shared("global log"s, std::move(sink)); + + log->set_level(spdlog::level::info); + log->flush_on(spdlog::level::info); + + spdlog::set_default_logger(std::move(log)); + spdlog::set_pattern("[%l] %v"s); + } +} + +SKSEPluginLoad(const LoadInterface* skse) { + InitializeLogging(); + + auto* plugin = PluginDeclaration::GetSingleton(); + auto version = plugin->GetVersion(); + log::info("{} {} is loading...", plugin->GetName(), version); + + Init(skse); + SKSE::GetPapyrusInterface()->Register(Papyrus::Bind); + + log::info("{} has finished loading.", plugin->GetName()); + return true; +} diff --git a/Source/DLL/src/PCH.h b/Source/DLL/src/PCH.h new file mode 100644 index 0000000..165cd7e --- /dev/null +++ b/Source/DLL/src/PCH.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#undef cdecl // Workaround for Clang 14 CMake configure error. + +#include +#include + +// Compatible declarations with other sample projects. +#define DLLEXPORT __declspec(dllexport) + +using namespace std::literals; +using namespace REL::literals; + +namespace logger = SKSE::log; + +namespace util { + using SKSE::stl::report_and_fail; +} diff --git a/Source/DLL/src/Papyrus.cpp b/Source/DLL/src/Papyrus.cpp new file mode 100644 index 0000000..b43d919 --- /dev/null +++ b/Source/DLL/src/Papyrus.cpp @@ -0,0 +1,22 @@ +#include "Papyrus.h" + +#include "Functions/ObjectReference.h" +#include "Functions/ArtifactTracker.h" + +namespace Papyrus +{ + bool Bind(VM* a_vm) + { + if (!a_vm) { + logger::critical("couldn't get VM State"sv); + return false; + } + + logger::info("{:*^30}", "FUNCTIONS"sv); + + ObjectReference::Bind(*a_vm); + ArtifactTracker::Bind(*a_vm); + + return true; + } +} diff --git a/Source/DLL/src/Papyrus.h b/Source/DLL/src/Papyrus.h new file mode 100644 index 0000000..3a4e060 --- /dev/null +++ b/Source/DLL/src/Papyrus.h @@ -0,0 +1,15 @@ +#pragma once + +#define BIND(a_method, ...) a_vm.RegisterFunction(#a_method##sv, script, a_method __VA_OPT__(, ) __VA_ARGS__) + +#include + +namespace Papyrus { + using VM = RE::BSScript::Internal::VirtualMachine; + using StackID = RE::VMStackID; + using Severity = RE::BSScript::ErrorLogger::Severity; + + inline constexpr auto script = "EddsArtifactTracker"sv; + + bool Bind(VM* a_vm); +} diff --git a/Source/DLL/vcpkg-configuration.json b/Source/DLL/vcpkg-configuration.json new file mode 100644 index 0000000..337bdfa --- /dev/null +++ b/Source/DLL/vcpkg-configuration.json @@ -0,0 +1,15 @@ +{ + "registries": [ + { + "kind": "git", + "repository": "https://gitlab.com/colorglass/vcpkg-colorglass", + "baseline": "5a11d06fd1b2d7cd6339d6aea48d450309e89cc1", + "packages": [ + "commonlibsse-ng", + "gluino", + "script-extender-common", + "skse" + ] + } + ] +} diff --git a/Source/DLL/vcpkg.json b/Source/DLL/vcpkg.json new file mode 100644 index 0000000..2a63dce --- /dev/null +++ b/Source/DLL/vcpkg.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", + "name": "artifact-tracker", + "version-string": "1.0.0", + "port-version": 0, + "description": "Artifact Tracker functions", + "homepage": "https://eddoursul.win", + "license": "MIT", + "features": { + "plugin": { + "description": "Build the SKSE plugin.", + "dependencies": [ + "commonlibsse-ng" + ] + } + }, + "default-features": [ + "plugin" + ] +} diff --git a/Source/Scripts/ETR_CellStorageScript.psc b/Source/Scripts/ETR_CellStorageScript.psc deleted file mode 100644 index cf850a0..0000000 --- a/Source/Scripts/ETR_CellStorageScript.psc +++ /dev/null @@ -1 +0,0 @@ -Scriptname ETR_CellStorageScript extends ObjectReference diff --git a/Source/Scripts/ETR_Functions.psc b/Source/Scripts/ETR_Functions.psc new file mode 100644 index 0000000..3e4faa3 --- /dev/null +++ b/Source/Scripts/ETR_Functions.psc @@ -0,0 +1,25 @@ +Scriptname ETR_Functions Hidden + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ObjectReference functions +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +int function AddAllFormsToList(FormList targetList, int formType, Form keywordOrList = None, Form excludeForm = None, bool onlyPlayable = true) native global + +int function AddItemsOfTypeAndKeywordToList(Form refOrList, FormList targetList, int formType = 0, Form keywordOrList = None, Form excludeForm = None, bool noEquipped = true, bool noFavourited = true, bool noQuestItem = true) native global + +int function GetItemCountInList(FormList refList, Form baseForm) native global + +int function GetItemCountInActors(Actor[] refArray, Form baseForm) native global + +Actor[] function GetPlayerFollowers() native global + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Artifact Tracker +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +ObjectReference function GetCellStorage(ObjectReference ref, FormList refList, Form refToCreate, bool autoCreate = true) native global + +bool function HasRefInCell(Form item, Cell currentCell = None, bool checkContainers = true, FormList excludeList = None) native global + +function SyncCellStorage(ObjectReference cellStorage, Form excludeForm = None) native global diff --git a/Source/Scripts/ETR_TrackFoundItems.psc b/Source/Scripts/ETR_TrackFoundItems.psc index 3c338d5..273f2f3 100644 --- a/Source/Scripts/ETR_TrackFoundItems.psc +++ b/Source/Scripts/ETR_TrackFoundItems.psc @@ -35,7 +35,7 @@ Event OnLocationChange(Location akOldLoc, Location akNewLoc) bAtHome = akNewLoc && akNewLoc.HasKeyword(LocTypePlayerHouse) int iCurrentFollowers = 0; - Actor[] aFollowers = Eddoursul_Functions.GetPlayerFollowers() + Actor[] aFollowers = ETR_Functions.GetPlayerFollowers() int i = aFollowers.length while i > 0 i -= 1 @@ -69,36 +69,36 @@ Event OnUpdate() if bSyncStorage bSyncStorage = false - ObjectReference cellStorage = Eddoursul_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer) - Eddoursul_Functions.SyncCellStorage(cellStorage, ETR_ExcludeFromCellItems) + ObjectReference cellStorage = ETR_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer) + ETR_Functions.SyncCellStorage(cellStorage, ETR_ExcludeFromCellItems) endif - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 26, None, ETR_ExcludeFromCellItems) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 41, None, ETR_ExcludeFromCellItems) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 27, None, ETR_ExcludeFromCellItems) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 32, None, ETR_ExcludeFromCellItems) + ETR_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 26, None, ETR_ExcludeFromCellItems) + ETR_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 41, None, ETR_ExcludeFromCellItems) + ETR_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 27, None, ETR_ExcludeFromCellItems) + ETR_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 32, None, ETR_ExcludeFromCellItems) ETR_ItemsFound.Revert() - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) - aFollowers = Eddoursul_Functions.GetPlayerFollowers() + aFollowers = ETR_Functions.GetPlayerFollowers() i = aFollowers.length while i > 0 i -= 1 - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) endwhile ETR_ItemsNew.Revert() - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 26, None, ETR_ExcludeFromNew) ; armor - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 41, None, ETR_ExcludeFromNew) ; weapons - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 27, None, ETR_ExcludeFromNew) ; books - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 32, None, ETR_ExcludeFromNew) ; misc items + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 26, None, ETR_ExcludeFromNew) ; armor + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 41, None, ETR_ExcludeFromNew) ; weapons + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 27, None, ETR_ExcludeFromNew) ; books + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 32, None, ETR_ExcludeFromNew) ; misc items return @@ -110,26 +110,26 @@ Event OnUpdate() Debug.Notification("Team changed, updating lists") ETR_ItemsFound.Revert() - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) - aFollowers = Eddoursul_Functions.GetPlayerFollowers() + aFollowers = ETR_Functions.GetPlayerFollowers() i = aFollowers.length while i > 0 i -= 1 - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) endwhile ETR_ItemsNew.Revert() - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 26, None, ETR_ExcludeFromNew) ; armor - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 41, None, ETR_ExcludeFromNew) ; weapons - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 27, None, ETR_ExcludeFromNew) ; books - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 32, None, ETR_ExcludeFromNew) ; misc items + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 26, None, ETR_ExcludeFromNew) ; armor + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 41, None, ETR_ExcludeFromNew) ; weapons + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 27, None, ETR_ExcludeFromNew) ; books + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 32, None, ETR_ExcludeFromNew) ; misc items endif @@ -145,8 +145,8 @@ event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemRefe elseif bAtHome && akItemReference ETR_ItemsFound.RemoveAddedForm(akBaseItem) ETR_ItemsStored.AddForm(akBaseItem) - Eddoursul_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer).AddItem(akBaseItem, 1, true) - elseif PlayerRef.GetItemCount(akBaseItem) == 0 && Eddoursul_Functions.GetItemCountInActors(Eddoursul_Functions.GetPlayerFollowers(), akBaseItem) == 0 + ETR_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer).AddItem(akBaseItem, 1, true) + elseif PlayerRef.GetItemCount(akBaseItem) == 0 && ETR_Functions.GetItemCountInActors(ETR_Functions.GetPlayerFollowers(), akBaseItem) == 0 ETR_ItemsFound.RemoveAddedForm(akBaseItem) ETR_ItemsNew.AddForm(akBaseItem) endif diff --git a/Source/Scripts/ETR_TrackNewItems.psc b/Source/Scripts/ETR_TrackNewItems.psc index 2ce602e..3e4a6a8 100644 --- a/Source/Scripts/ETR_TrackNewItems.psc +++ b/Source/Scripts/ETR_TrackNewItems.psc @@ -39,34 +39,34 @@ event OnPlayerLoadGame() ; Rebuild all lists to avoid discrepancies, stale data, and broken records ETR_ItemsStored.Revert() - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 26, None, ETR_ExcludeFromNew) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 41, None, ETR_ExcludeFromNew) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 27, None, ETR_ExcludeFromNew) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 32, None, ETR_ExcludeFromNew) + ETR_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 26, None, ETR_ExcludeFromNew) + ETR_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 41, None, ETR_ExcludeFromNew) + ETR_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 27, None, ETR_ExcludeFromNew) + ETR_Functions.AddItemsOfTypeAndKeywordToList(ETR_PersistentStorageList, ETR_ItemsStored, 32, None, ETR_ExcludeFromNew) ETR_ItemsFound.Revert() - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(PlayerRef, ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) - Actor[] aFollowers = Eddoursul_Functions.GetPlayerFollowers() + Actor[] aFollowers = ETR_Functions.GetPlayerFollowers() int i = aFollowers.length while i > 0 i -= 1 if ! aFollowers[i].IsDead() && ! aFollowers[i].IsDisabled() - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) - Eddoursul_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 26, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 41, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 27, None, ETR_ExcludeFromNew, false, false, false) + ETR_Functions.AddItemsOfTypeAndKeywordToList(aFollowers[i], ETR_ItemsFound, 32, None, ETR_ExcludeFromNew, false, false, false) endif endwhile ETR_ItemsNew.Revert() - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 26, None, ETR_ExcludeFromNew) ; armor - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 41, None, ETR_ExcludeFromNew) ; weapons - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 27, None, ETR_ExcludeFromNew) ; books - Eddoursul_Functions.AddAllFormsToList(ETR_ItemsNew, 32, None, ETR_ExcludeFromNew) ; misc items + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 26, None, ETR_ExcludeFromNew) ; armor + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 41, None, ETR_ExcludeFromNew) ; weapons + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 27, None, ETR_ExcludeFromNew) ; books + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 32, None, ETR_ExcludeFromNew) ; misc items endevent diff --git a/Source/Scripts/ETR_TrackStoredItems.psc b/Source/Scripts/ETR_TrackStoredItems.psc index 6ce1851..cc0f79c 100644 --- a/Source/Scripts/ETR_TrackStoredItems.psc +++ b/Source/Scripts/ETR_TrackStoredItems.psc @@ -48,9 +48,9 @@ event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemRefe endif - if ((akDestContainer && akDestContainer.GetParentCell() == PlayerRef.GetParentCell()) || (! akDestContainer && Eddoursul_Functions.HasRefInCell(akBaseItem, None, true, ETR_PersistentStorageList))) + if ((akDestContainer && akDestContainer.GetParentCell() == PlayerRef.GetParentCell()) || (! akDestContainer && ETR_Functions.HasRefInCell(akBaseItem, None, true, ETR_PersistentStorageList))) - ObjectReference cellStorage = Eddoursul_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer) + ObjectReference cellStorage = ETR_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer) if cellStorage.GetItemCount(akBaseItem) == 0 cellStorage.AddItem(akBaseItem, 1, true) @@ -65,19 +65,19 @@ endevent event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer) if akSourceContainer && !(akSourceContainer as Actor) && ETR_PersistentStorageList.HasForm(akSourceContainer) - if Eddoursul_Functions.GetItemCountInList(ETR_PersistentStorageList, akBaseItem) == 0 + if ETR_Functions.GetItemCountInList(ETR_PersistentStorageList, akBaseItem) == 0 ETR_ItemsStored.RemoveAddedForm(akBaseItem) ETR_ItemsFound.AddForm(akBaseItem) endif elseif bAtHome - ObjectReference cellStorage = Eddoursul_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer) + ObjectReference cellStorage = ETR_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer) int iCount = cellStorage.GetItemCount(akBaseItem) - if iCount > 0 && ! Eddoursul_Functions.HasRefInCell(akBaseItem, None, true, ETR_PersistentStorageList) + if iCount > 0 && ! ETR_Functions.HasRefInCell(akBaseItem, None, true, ETR_PersistentStorageList) cellStorage.RemoveItem(akBaseItem, iCount, true) - if Eddoursul_Functions.GetItemCountInList(ETR_PersistentStorageList, akBaseItem) == 0 + if ETR_Functions.GetItemCountInList(ETR_PersistentStorageList, akBaseItem) == 0 ETR_ItemsStored.RemoveAddedForm(akBaseItem) ETR_ItemsFound.AddForm(akBaseItem) endif