Initial commit. Integrates with client, saves refs and merch.
This commit is contained in:
commit
36abd362cd
95
.clang-format
Normal file
95
.clang-format
Normal file
@ -0,0 +1,95 @@
|
||||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: 'false'
|
||||
AlignConsecutiveDeclarations: 'false'
|
||||
AlignConsecutiveMacros: 'false'
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: 'true'
|
||||
AlignTrailingComments: 'true'
|
||||
AllowAllArgumentsOnNextLine: 'false'
|
||||
AllowAllConstructorInitializersOnNextLine: 'false'
|
||||
AllowAllParametersOfDeclarationOnNextLine: 'false'
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortCaseLabelsOnASingleLine: 'false'
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: 'true'
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: 'true'
|
||||
AlwaysBreakTemplateDeclarations: 'Yes'
|
||||
BinPackArguments: 'true'
|
||||
BinPackParameters: 'true'
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: 'true'
|
||||
AfterClass: 'true'
|
||||
AfterControlStatement: 'false'
|
||||
AfterEnum: 'true'
|
||||
AfterExternBlock: 'true'
|
||||
AfterFunction: 'true'
|
||||
AfterNamespace: 'true'
|
||||
AfterStruct: 'true'
|
||||
AfterUnion: 'true'
|
||||
BeforeCatch: 'false'
|
||||
BeforeElse: 'false'
|
||||
IndentBraces: 'false'
|
||||
SplitEmptyFunction: 'false'
|
||||
SplitEmptyNamespace: 'false'
|
||||
SplitEmptyRecord: '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'
|
||||
IndentGotoLabels: 'false'
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: 'true'
|
||||
KeepEmptyLinesAtTheStartOfBlocks: 'false'
|
||||
Language: Cpp
|
||||
MaxEmptyLinesToKeep: 2
|
||||
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: c++17
|
||||
TabWidth: 4
|
||||
UseCRLF: 'true'
|
||||
UseTab: Always
|
||||
|
||||
...
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
334
.gitignore
vendored
Normal file
334
.gitignore
vendored
Normal file
@ -0,0 +1,334 @@
|
||||
## 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
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# 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
|
||||
|
||||
# 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/
|
||||
|
||||
# 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
|
||||
*.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
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# 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
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# 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/
|
41
BazaarRealmPlugin.sln
Normal file
41
BazaarRealmPlugin.sln
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30223.230
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BazaarRealmPlugin", "BazaarRealmPlugin.vcxproj", "{27055442-2141-4A9B-ADD4-6298178CD606}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonLibSSE", "%CommonLibSSEPath%\CommonLibSSE.vcxproj", "{3E4B6EF3-EC6E-46CF-9003-EEB57A258B82}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{27055442-2141-4A9B-ADD4-6298178CD606}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{27055442-2141-4A9B-ADD4-6298178CD606}.Debug|x64.Build.0 = Debug|x64
|
||||
{27055442-2141-4A9B-ADD4-6298178CD606}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{27055442-2141-4A9B-ADD4-6298178CD606}.Debug|x86.Build.0 = Debug|Win32
|
||||
{27055442-2141-4A9B-ADD4-6298178CD606}.Release|x64.ActiveCfg = Release|x64
|
||||
{27055442-2141-4A9B-ADD4-6298178CD606}.Release|x64.Build.0 = Release|x64
|
||||
{27055442-2141-4A9B-ADD4-6298178CD606}.Release|x86.ActiveCfg = Release|Win32
|
||||
{27055442-2141-4A9B-ADD4-6298178CD606}.Release|x86.Build.0 = Release|Win32
|
||||
{3E4B6EF3-EC6E-46CF-9003-EEB57A258B82}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{3E4B6EF3-EC6E-46CF-9003-EEB57A258B82}.Debug|x64.Build.0 = Debug|x64
|
||||
{3E4B6EF3-EC6E-46CF-9003-EEB57A258B82}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{3E4B6EF3-EC6E-46CF-9003-EEB57A258B82}.Debug|x86.Build.0 = Debug|Win32
|
||||
{3E4B6EF3-EC6E-46CF-9003-EEB57A258B82}.Release|x64.ActiveCfg = Release|x64
|
||||
{3E4B6EF3-EC6E-46CF-9003-EEB57A258B82}.Release|x64.Build.0 = Release|x64
|
||||
{3E4B6EF3-EC6E-46CF-9003-EEB57A258B82}.Release|x86.ActiveCfg = Release|Win32
|
||||
{3E4B6EF3-EC6E-46CF-9003-EEB57A258B82}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {85DD0553-0922-4F81-80CF-7DB29ADD49F0}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
262
BazaarRealmPlugin.vcxproj
Normal file
262
BazaarRealmPlugin.vcxproj
Normal file
@ -0,0 +1,262 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{27055442-2141-4a9b-add4-6298178cd606}</ProjectGuid>
|
||||
<RootNamespace>BazaarRealm</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>BazaarRealmPlugin</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<VcpkgTriplet>$(VcpkgUserTriplet)-custom</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<VcpkgTriplet>$(VcpkgUserTriplet)-custom</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<VcpkgTriplet>$(VcpkgUserTriplet)-custom</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<VcpkgTriplet>$(VcpkgUserTriplet)-custom</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<EnforceTypeConversionRules>true</EnforceTypeConversionRules>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>PCH.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)\src;$(CommonLibSSEPath)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<ForcedIncludeFiles>PCH.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
|
||||
<CompileAs>CompileAsCpp</CompileAs>
|
||||
<UseFullPaths>false</UseFullPaths>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalOptions>/experimental:external /external:anglebrackets /external:W0 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy "$(TargetPath)" "$(Skyrim64Path)\Data\SKSE\Plugins\$(TargetFileName)" /Y</Command>
|
||||
<Message>Copy the compiled dll to the Skyrim directory</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<EnforceTypeConversionRules>true</EnforceTypeConversionRules>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>PCH.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)\src;$(CommonLibSSEPath)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<ForcedIncludeFiles>PCH.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
|
||||
<CompileAs>CompileAsCpp</CompileAs>
|
||||
<UseFullPaths>false</UseFullPaths>
|
||||
<AdditionalOptions>/experimental:external /external:anglebrackets /external:W0 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy "$(TargetPath)" "$(Skyrim64Path)\Data\SKSE\Plugins\$(TargetFileName)" /Y</Command>
|
||||
<Message>Copy the compiled dll to the Skyrim directory</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<EnforceTypeConversionRules>true</EnforceTypeConversionRules>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>PCH.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)src;$(CommonLibSSEPath)\include;$(BazaarRealmClientPath);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<ForcedIncludeFiles>PCH.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
|
||||
<CompileAs>CompileAsCpp</CompileAs>
|
||||
<UseFullPaths>false</UseFullPaths>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalOptions>/experimental:external /external:anglebrackets /external:W0 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>$(BazaarRealmClientPath)\target\debug\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>ShopkeeperClient.dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy "$(TargetPath)" "$(Skyrim64Path)\Data\SKSE\Plugins\$(TargetFileName)" /Y</Command>
|
||||
<Message>Copy the compiled dll to the Skyrim directory</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<EnforceTypeConversionRules>true</EnforceTypeConversionRules>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>PCH.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)src;$(CommonLibSSEPath)\include;$(BazaarRealmClientPath);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<ForcedIncludeFiles>PCH.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
|
||||
<CompileAs>CompileAsCpp</CompileAs>
|
||||
<UseFullPaths>false</UseFullPaths>
|
||||
<AdditionalOptions>/experimental:external /external:anglebrackets /external:W0 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>$(BazaarRealmClientPath)\target\debug\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>ShopkeeperClient.dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy "$(TargetPath)" "$(Skyrim64Path)\Data\SKSE\Plugins\$(TargetFileName)" /Y</Command>
|
||||
<Message>Copy the compiled dll to the Skyrim directory</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\BRClient.cpp" />
|
||||
<ClCompile Include="src\BRInteriorRefList.cpp" />
|
||||
<ClCompile Include="src\BRMerchandiseList.cpp" />
|
||||
<ClCompile Include="src\BROwner.cpp" />
|
||||
<ClCompile Include="src\BRShop.cpp" />
|
||||
<ClCompile Include="src\main.cpp" />
|
||||
<ClCompile Include="src\PCH.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</MultiProcessorCompilation>
|
||||
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</MultiProcessorCompilation>
|
||||
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</MultiProcessorCompilation>
|
||||
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\BRClient.h" />
|
||||
<ClInclude Include="src\BRInteriorRefList.h" />
|
||||
<ClInclude Include="src\BRMerchandiseList.h" />
|
||||
<ClInclude Include="src\BROwner.h" />
|
||||
<ClInclude Include="src\BRShop.h" />
|
||||
<ClInclude Include="src\NativeFunctions.h" />
|
||||
<ClInclude Include="src\PCH.h" />
|
||||
<ClInclude Include="src\version.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(CommonLibSSEPath)\CommonLibSSE.vcxproj">
|
||||
<Project>{3e4b6ef3-ec6e-46cf-9003-eeb57a258b82}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include=".clang-format" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="version.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
63
BazaarRealmPlugin.vcxproj.filters
Normal file
63
BazaarRealmPlugin.vcxproj.filters
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="src">
|
||||
<UniqueIdentifier>{e487ea2f-dbf7-49c6-bd70-00519f35c76c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\main.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\PCH.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\BRClient.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\BROwner.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\BRShop.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\BRInteriorRefList.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\BRMerchandiseList.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\PCH.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\version.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\BRClient.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\BROwner.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\BRShop.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\BRInteriorRefList.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\BRMerchandiseList.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\NativeFunctions.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include=".clang-format" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="version.rc" />
|
||||
</ItemGroup>
|
||||
</Project>
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Tyler Hallada
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
17
README.md
Normal file
17
README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# BazaarRealmPlugin
|
||||
|
||||
The SKSE plugin for the Bazaar Realm Skyrim mod.
|
||||
|
||||
## Building
|
||||
|
||||
[Follow the CommonLibSSE set-up
|
||||
instructions](https://github.com/Ryan-rsm-McKenzie/CommonLibSSE/wiki/Getting-Started#commonlibsse).
|
||||
Instead of cloning the example plugin, clone this repo instead.
|
||||
|
||||
This plugin relies on a separate DLL plugin called `BazaarRealmClient`. To build
|
||||
this plugin, it needs to be linked to it at compile-time. Checkout
|
||||
`BazaarRealmClient` in a place of your choosing, build it, and then set the
|
||||
environment variable `BazaarRealmClientPath` to the path you chose.
|
||||
|
||||
Building the plugin in Visual Studio will automatically place the built plugin
|
||||
DLL in the `Skyrim Special Edition\Data\SKSE\Plugins\` folder.
|
25
src/BRClient.cpp
Normal file
25
src/BRClient.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include "bindings.h"
|
||||
|
||||
void Init(RE::StaticFunctionTag*)
|
||||
{
|
||||
logger::info("Entered Init");
|
||||
init();
|
||||
logger::info("Init done");
|
||||
}
|
||||
|
||||
std::string GenerateApiKey(RE::StaticFunctionTag*)
|
||||
{
|
||||
logger::info("Entered GenerateApiKey");
|
||||
char *api_key = generate_api_key();
|
||||
logger::info(FMT_STRING("GenerateApiKey api_key: {}"), api_key);
|
||||
return api_key;
|
||||
}
|
||||
|
||||
bool StatusCheck(RE::StaticFunctionTag*, RE::BSFixedString api_url)
|
||||
{
|
||||
logger::info("Entered StatusCheck");
|
||||
logger::info(FMT_STRING("StatusCheck api_url: {}"), api_url);
|
||||
bool result = status_check(api_url.c_str());
|
||||
logger::info(FMT_STRING("StatusCheck result: {}"), result ? "true" : "false");
|
||||
return result;
|
||||
}
|
5
src/BRClient.h
Normal file
5
src/BRClient.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
void Init(RE::StaticFunctionTag*);
|
||||
std::string GenerateApiKey(RE::StaticFunctionTag*);
|
||||
bool StatusCheck(RE::StaticFunctionTag*, RE::BSFixedString api_url);
|
278
src/BRInteriorRefList.cpp
Normal file
278
src/BRInteriorRefList.cpp
Normal file
@ -0,0 +1,278 @@
|
||||
#include "bindings.h"
|
||||
|
||||
#include "NativeFunctions.h"
|
||||
|
||||
int CreateInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectCELL * cell)
|
||||
{
|
||||
logger::info("Entered CreateInteriorRefList");
|
||||
if (!cell)
|
||||
return -1;
|
||||
|
||||
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
|
||||
std::vector<RefRecord> ref_records;
|
||||
for (auto entry = cell->references.begin(); entry != cell->references.end(); ++entry)
|
||||
{
|
||||
// TODO: skip saving the ref if it is part of mod file
|
||||
// OR: continue to save it, but with a special flag (useful in order to allow repositioning default statics)
|
||||
// Then, during Load, skip PlaceAtMe, lookup the ref by id and then reposition it
|
||||
RE::TESObjectREFR * ref = (*entry).get();
|
||||
const char * name = ref->GetName();
|
||||
logger::info(FMT_STRING("CreateInteriorRefList ref: {}"), name);
|
||||
const RE::TESBoundObject * base = ref->GetBaseObject();
|
||||
if (base) {
|
||||
RE::FormID base_form_id = base->GetFormID();
|
||||
if (base_form_id == 7) {
|
||||
// skip saving player ref
|
||||
continue;
|
||||
}
|
||||
const char * form_name = base->GetName();
|
||||
const RE::FormType form_type = base->GetFormType();
|
||||
logger::info(FMT_STRING("CreateInteriorRefList form: {} ({:x})"), form_name, (uint32_t)form_type);
|
||||
float position_x = ref->GetPositionX();
|
||||
float position_y = ref->GetPositionY();
|
||||
float position_z = ref->GetPositionZ();
|
||||
float angle_x = ref->GetAngleX();
|
||||
float angle_y = ref->GetAngleY();
|
||||
float angle_z = ref->GetAngleZ();
|
||||
RE::NiNPShortPoint3 boundMin = base->boundData.boundMin;
|
||||
RE::NiNPShortPoint3 boundMax = base->boundData.boundMax;
|
||||
uint16_t bound_x = boundMax.x > boundMin.x ? boundMax.x - boundMin.x : boundMin.x - boundMax.x;
|
||||
uint16_t bound_y = boundMax.y > boundMin.y ? boundMax.y - boundMin.y : boundMin.y - boundMax.y;
|
||||
uint16_t bound_z = boundMax.z > boundMin.z ? boundMax.z - boundMin.z : boundMin.z - boundMax.z;
|
||||
logger::info(FMT_STRING("CreateInteriorRefList bounds: width: {:d}, length: {:d}, height: {:d}"), bound_x, bound_y, bound_z);
|
||||
uint16_t scale = ref->refScale;
|
||||
logger::info(FMT_STRING("CreateInteriorRefList position: {:.2f}, {:.2f}, {:.2f} angle: {:.2f}, {:.2f}, {:.2f} scale: {:d}"), position_x, position_y, position_z, angle_x, angle_y, angle_z, scale);
|
||||
logger::info(FMT_STRING("CreateInteriorRefList deleted: {:d}, wants delete: {:d}"), ref->IsMarkedForDeletion(), ref->inGameFormFlags.all(RE::TESObjectREFR::InGameFormFlag::kWantsDelete));
|
||||
|
||||
RE::TESFile * base_file = base->GetFile(0);
|
||||
char * base_file_name = base_file->fileName;
|
||||
bool is_light = base_file->recordFlags.all(RE::TESFile::RecordFlag::kSmallFile);
|
||||
uint32_t base_local_form_id = is_light ? base_form_id & 0xfff : base_form_id & 0xFFFFFF;
|
||||
//_MESSAGE("FILE: %s isLight: %d formID: 0x%x localFormId: 0x%x", file_name, is_light, form_id, local_form_id);
|
||||
RE::FormID ref_form_id = ref->GetFormID();
|
||||
uint16_t ref_mod_index = ref_form_id >> 24;
|
||||
char * ref_file_name = nullptr;
|
||||
uint32_t ref_local_form_id = ref_form_id & 0xFFFFFF;
|
||||
if (ref_mod_index != 255) { // not a temp ref
|
||||
if (ref_mod_index == 254) { // is a light mod
|
||||
ref_local_form_id = ref_form_id & 0xfff;
|
||||
ref_mod_index = (ref_form_id >> 12) & 0xfff;
|
||||
const RE::TESFile * ref_file = data_handler->LookupLoadedLightModByIndex(ref_mod_index);
|
||||
ref_file_name = _strdup(ref_file->fileName);
|
||||
}
|
||||
else {
|
||||
const RE::TESFile * ref_file = data_handler->LookupLoadedModByIndex(ref_mod_index);
|
||||
ref_file_name = _strdup(ref_file->fileName);
|
||||
}
|
||||
}
|
||||
|
||||
ref_records.push_back({
|
||||
base_file_name,
|
||||
base_local_form_id,
|
||||
ref_file_name,
|
||||
ref_local_form_id,
|
||||
position_x,
|
||||
position_y,
|
||||
position_z,
|
||||
angle_x,
|
||||
angle_y,
|
||||
angle_z,
|
||||
scale,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int interior_ref_list_id = create_interior_ref_list(api_url.c_str(), api_key.c_str(), shop_id, &ref_records[0], ref_records.size());
|
||||
logger::info(FMT_STRING("CreateInteriorRefList result: {}"), interior_ref_list_id);
|
||||
return interior_ref_list_id;
|
||||
}
|
||||
|
||||
bool ClearCell(RE::StaticFunctionTag*, RE::TESObjectCELL* cell_ignored)
|
||||
{
|
||||
logger::info("Entered ClearCell");
|
||||
|
||||
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
|
||||
using func_t = bool(RE::TESObjectREFR* a_thisObj, void* a_param1, void* a_param2, double& a_result);
|
||||
RE::TESObjectCELL * cell = RE::TESObjectCELL::LookupByEditorID<RE::TESObjectCELL>("BREmpty");
|
||||
logger::info(FMT_STRING("ClearCell lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID());
|
||||
if (!cell) {
|
||||
logger::error("ClearCell cell is null!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Destroy existing references
|
||||
for (auto entry = cell->references.begin(); entry != cell->references.end(); ++entry)
|
||||
{
|
||||
RE::TESObjectREFR * ref = (*entry).get();
|
||||
RE::FormID form_id = ref->GetFormID();
|
||||
logger::info(FMT_STRING("ClearCell ref form_id: {:x}"), (uint32_t)form_id);
|
||||
|
||||
int mod_index = form_id >> 24;
|
||||
if (mod_index != 255) {
|
||||
RE::TESFile * file = ref->GetDescriptionOwnerFile();
|
||||
if (file) {
|
||||
bool is_light = file->recordFlags.all(RE::TESFile::RecordFlag::kSmallFile);
|
||||
uint32_t local_form_id = is_light ? ref->GetFormID() & 0xfff : form_id & 0xFFFFFF;
|
||||
if (!data_handler->LookupForm<RE::TESObjectREFR>(local_form_id, file->fileName)) {
|
||||
logger::info(FMT_STRING("ClearCell ref was not in mod file! {:x} {}"), local_form_id, ref->GetName());
|
||||
}
|
||||
else {
|
||||
logger::info(FMT_STRING("ClearCell ref in mod file {:x} {}"), local_form_id, ref->GetName());
|
||||
}
|
||||
} else {
|
||||
logger::info(FMT_STRING("ClearCell ref not in ANY file! {:x} {}"), (uint32_t)form_id, ref->GetName());
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger::info(FMT_STRING("ClearCell ref is a temp ref, deleting {:x} {}"), (uint32_t)form_id, ref->GetName());
|
||||
ref->Disable(); // disabling first is required to prevent CTD on unloading cell
|
||||
ref->SetDelete(true);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: store the FFIResult in a HashMap cache (with ids assigned from incrementing a global int) and return its key in this function
|
||||
// * bool BRResult.IsOk(int resultId) ; if false, then only GetErr() shall be accessed
|
||||
// * bool BRResult.IsErr(int resultId) ; maybe not needed
|
||||
// * string BRResult.GetErr(int resultId) ; always a string
|
||||
// * string BRResult.GetType(int resultId) ; returns type of Ok value encoded as string
|
||||
// * BRResult.Drop(int resultId) ; once we are done using the data from the result or displaying an error, delete it from the cache and free the memory
|
||||
|
||||
// These will panic if a) the result is an error or b) the type of the result value different than what was requested
|
||||
// That will cause CTDs but there's not much else to do, silently causing undefined behavior would be worse
|
||||
// Every step of this API has an explicit check you must make before continuing. If you break that contract, that's on you, the scripter.
|
||||
// * string BRResult.GetString(int resultId)
|
||||
// * int BRResult.GetInt(int resultId)
|
||||
// * bool BRResult.GetBool(int resultId)
|
||||
// * int BRResult.GetResponseId(int resultId)
|
||||
|
||||
// For BRResponse (maybe just completely get rid of this):
|
||||
// * int BRResponse.GetStatus(int responseId) ; if >= 300 then none of the other functions should be accessed
|
||||
// * bool BRResponse.IsFromCache(int responseId)
|
||||
// * string BRResponse.GetType(int responseId) ; may return things like "string" or "shop"
|
||||
// * string BRResponse.GetString(int responseId) ; is this going to be needed?
|
||||
// * int BRResponse.GetShopId(int responseId) ; for a get_shop request
|
||||
// * int BRResponse.GetInteriorRefListId(int responseId) ; for a get_interior_ref_list request
|
||||
// * BRResponse.Drop()
|
||||
|
||||
// The following will return values from a HashMap cache on the rust-side where entries are not evicted unless explicitly told to by papyrus.
|
||||
// These methods will panic if there isn't an entry in the cache for the id. This can happen if a request was not made beforehand and verified to be non-err result with a non-error response.
|
||||
// For BRShop
|
||||
// * string BRShop.GetName(int shopId)
|
||||
// * int BRShop.GetOwnerId(int shopId)
|
||||
// * BRShop.Drop()
|
||||
|
||||
// For BROwner
|
||||
// * string BROwner.GetName(int ownerId)
|
||||
// * BROwner.Drop()
|
||||
|
||||
// Should split this up into GetInteriorRefList and LoadInteriorRefList.
|
||||
// Get: makes the request to the api and stores the Result in a rust cache, returns id of Result
|
||||
// Load: given id of interior ref list, gets the data from the rust cache, does the PlaceAtMe loop and returns an id of another Result
|
||||
bool LoadInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t interior_ref_list_id, RE::TESObjectCELL* cell_ignored, RE::TESObjectREFR* target_ref)
|
||||
{
|
||||
logger::info("Entered LoadInteriorRefList");
|
||||
|
||||
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
|
||||
RE::BSScript::Internal::VirtualMachine * a_vm = RE::BSScript::Internal::VirtualMachine::GetSingleton();
|
||||
using func_t = decltype(&PlaceAtMe);
|
||||
REL::Relocation<func_t> PlaceAtMe_Native{ REL::ID(55672) };
|
||||
using func_t2 = decltype(&MoveTo);
|
||||
REL::Relocation<func_t2> MoveTo_Native(RE::Offset::TESObjectREFR::MoveTo);
|
||||
|
||||
// testing returning a script object
|
||||
// RE::BSTSmartPointer<RE::BSScript::Object> ret = RE::BSTSmartPointer<RE::BSScript::Object>::BSTSmartPointer();
|
||||
// a_vm->CreateObject(RE::BSFixedString("BRResult"), ret);
|
||||
// RE::BSScript::Variable var = RE::BSScript::Variable::Variable(RE::BSScript::TypeInfo::TypeInfo(RE::BSScript::TypeInfo::RawType::kBool));
|
||||
// var.SetBool(false);
|
||||
// a_vm->SetPropertyValue(ret, "value", var);
|
||||
// RE::BSScript::Variable ret_var = RE::BSScript::Variable::Variable(RE::BSScript::TypeInfo::TypeInfo(RE::BSScript::TypeInfo::RawType::kObject));
|
||||
// ret_var.SetObject(ret);
|
||||
// return ret_var;
|
||||
|
||||
RE::TESObjectCELL * cell = RE::TESObjectCELL::LookupByEditorID<RE::TESObjectCELL>("BREmpty");
|
||||
logger::info(FMT_STRING("LoadInteriorRefList lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID());
|
||||
if (!cell) {
|
||||
logger::error("LoadInteriorRefList cell is null!");
|
||||
}
|
||||
|
||||
//RE::TESObjectREFR * x_marker = data_handler->LookupForm<RE::TESObjectREFR>(6628, "BazaarRealm.esp");
|
||||
if (target_ref) {
|
||||
FFIResult<RefRecordVec> result = get_interior_ref_list(api_url.c_str(), api_key.c_str(), interior_ref_list_id);
|
||||
if (result.IsOk()) {
|
||||
logger::info("LoadInteriorRefList get_interior_ref_list result: OK");
|
||||
RefRecordVec vec = result.AsOk();
|
||||
logger::info(FMT_STRING("LoadInteriorRefList vec len: {:d}, cap: {:d}"), vec.len, vec.cap);
|
||||
|
||||
for (int i = 0; i < vec.len; i++) {
|
||||
RefRecord ref = vec.ptr[i];
|
||||
logger::info(FMT_STRING("LoadInteriorRefList ref base_mod_name: {}, base_local_form_id: {:x}"), ref.base_mod_name, ref.base_local_form_id);
|
||||
logger::info(FMT_STRING("LoadInteriorRefList ref position {:.2f} {:.2f} {:.2f}, angle: {:.2f} {:.2f} {:.2f}, scale: {:d}"), ref.position_x, ref.position_y, ref.position_z, ref.angle_x, ref.angle_y, ref.angle_z, ref.scale);
|
||||
if (ref.base_local_form_id == 7) {
|
||||
logger::info("LoadInteriorRefList skipping player ref");
|
||||
continue;
|
||||
}
|
||||
|
||||
RE::NiPoint3 position = RE::NiPoint3::NiPoint3(ref.position_x, ref.position_y, ref.position_z);
|
||||
RE::NiPoint3 angle = RE::NiPoint3::NiPoint3(ref.angle_x, ref.angle_y, ref.angle_z);
|
||||
RE::TESObjectREFR * game_ref = nullptr;
|
||||
if (ref.ref_mod_name) { // non-temp ref, try to lookup and reposition
|
||||
game_ref = data_handler->LookupForm<RE::TESObjectREFR>(ref.ref_local_form_id, ref.ref_mod_name);
|
||||
if (game_ref) {
|
||||
logger::info(FMT_STRING("LoadInteriorRefList lookup ref name: {}, form_id: {:x}"), game_ref->GetName(), (uint32_t)game_ref->GetFormID());
|
||||
|
||||
// Failed experiment at trying to call the ObjectReference.Enable() papyrus native function
|
||||
//RE::BSTSmartPointer<RE::BSScript::Object> object;
|
||||
//RE::BSTSmartPointer<RE::BSScript::IStackCallbackFunctor> callback = RE::BSTSmartPointer<RE::BSScript::IStackCallbackFunctor>::BSTSmartPointer(RE::BSScript::IStackCallbackFunctor());
|
||||
//a_vm->CreateObject(RE::BSFixedString("ObjectReference"), game_ref, object);
|
||||
//_MESSAGE("Created Object isConstructed: %d, isInitialized: %d, isValid: %d, type: %s", object->IsConstructed(), object->IsInitialized(), object->IsValid(), object->GetTypeInfo()->GetName());
|
||||
//RE::BSTHashMap<RE::VMStackID, RE::BSTSmartPointer<RE::BSScript::Stack>> runningStacks = a_vm->allRunningStacks;
|
||||
//_MESSAGE("allRunningStacks size: %d", runningStacks.size());
|
||||
//for (auto entry = runningStacks.begin(); entry != runningStacks.end(); ++entry) {
|
||||
//_MESSAGE("allRunningStacks %d", entry->first);
|
||||
//}
|
||||
//a_vm->DispatchMethodCall(object, RE::BSFixedString("Enable"), RE::MakeFunctionArguments<>(), RE::BSScript::IStackCallbackFunctor::IStackCallbackFunctor())
|
||||
}
|
||||
else {
|
||||
logger::info(FMT_STRING("LoadInteriorRefList lookup ref not found, ref_mod_name: {}, ref_local_form_id: {:x}"), ref.ref_mod_name, ref.ref_local_form_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!game_ref) { // temporary reference or non-temp ref not found, recreate from base form
|
||||
RE::TESForm * form = data_handler->LookupForm(ref.base_local_form_id, ref.base_mod_name);
|
||||
if (!form) { // form is not found, might be in an uninstalled mod
|
||||
logger::warn(FMT_STRING("LoadInteriorRefList not spawning ref for form that could not be found in installed mods, base_mod_name: {}, base_local_form_id: {:d}"), ref.base_mod_name, ref.base_local_form_id);
|
||||
continue;
|
||||
}
|
||||
logger::info(FMT_STRING("LoadInteriorRefList lookup form name: {}, form_id: {:x}"), form->GetName(), (uint32_t)form->GetFormID());
|
||||
game_ref = PlaceAtMe_Native(a_vm, 0, target_ref, form, 1, false, false);
|
||||
}
|
||||
|
||||
MoveTo_Native(game_ref, game_ref->CreateRefHandle(), cell, cell->worldSpace, position, angle);
|
||||
game_ref->data.angle = angle; // set angle directly to fix bug with MoveTo in an unloaded target cell
|
||||
}
|
||||
}
|
||||
else {
|
||||
const char * error = result.AsErr();
|
||||
logger::error(FMT_STRING("LoadInteriorRefList get_interior_ref_list error: {}"), error);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
logger::error("LoadInteriorRefList target_ref is null!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//RE::TESForm * gold = RE::TESForm::LookupByID(15);
|
||||
//_MESSAGE("Gold form name: %s", gold->GetName());
|
||||
//_MESSAGE("Gold form id: %d", gold->GetFormID());
|
||||
//using func_t = decltype(&PlaceAtMe);
|
||||
//REL::Offset<func_t> func{ REL::ID(55672) };
|
||||
//RE::TESObjectREFR * new_ref = func(RE::BSScript::Internal::VirtualMachine::GetSingleton(), 1, player, gold, 50, false, false);
|
||||
//_MESSAGE("New ref initially disabled: %d", new_ref->IsDisabled());
|
||||
//_MESSAGE("New ref persistent: %d", new_ref->loadedData->flags & RE::TESObjectREFR::RecordFlags::kPersistent);
|
||||
return true;
|
||||
}
|
5
src/BRInteriorRefList.h
Normal file
5
src/BRInteriorRefList.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
int CreateInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectCELL * cell);
|
||||
bool ClearCell(RE::StaticFunctionTag*, RE::TESObjectCELL* cell_ignored);
|
||||
bool LoadInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t interior_ref_list_id, RE::TESObjectCELL* cell_ignored, RE::TESObjectREFR* target_ref);
|
624
src/BRMerchandiseList.cpp
Normal file
624
src/BRMerchandiseList.cpp
Normal file
@ -0,0 +1,624 @@
|
||||
#include "bindings.h"
|
||||
|
||||
#include "NativeFunctions.h"
|
||||
|
||||
// TODO: replace "placeholder" with "buy_activator" and "ref" with "item_ref"
|
||||
bool ClearMerchandiseImpl(RE::TESObjectREFR* merchant_chest, RE::TESObjectREFR* merchant_shelf, RE::TESForm* placeholder_static, RE::BGSKeyword * shelf_keyword, RE::BGSKeyword * item_keyword)
|
||||
{
|
||||
logger::info("Entered ClearMerchandise");
|
||||
|
||||
if (merchant_chest && merchant_shelf) {
|
||||
RE::TESObjectCELL * cell = merchant_shelf->GetParentCell();
|
||||
RE::FormID placeholder_form_id = placeholder_static->GetFormID();
|
||||
RE::FormID shelf_form_id = merchant_shelf->GetFormID();
|
||||
for (auto entry = cell->references.begin(); entry != cell->references.end();)
|
||||
{
|
||||
RE::TESObjectREFR * ref = (*entry).get();
|
||||
logger::info(FMT_STRING("ClearMerchandise ref form_id: {:x}, disabled: {:d}, marked for deletion: {:d}, deleted: {:d}"), (uint32_t)ref->GetFormID(), ref->IsDisabled(), ref->IsMarkedForDeletion(), ref->IsDeleted());
|
||||
RE::TESBoundObject * base = ref->GetBaseObject();
|
||||
if (base) {
|
||||
RE::FormID form_id = base->GetFormID();
|
||||
if (form_id == placeholder_form_id) {
|
||||
logger::info("ClearMerchandise found placeholder ref");
|
||||
RE::TESObjectREFR * shelf_linked_ref = ref->GetLinkedRef(shelf_keyword);
|
||||
if (shelf_linked_ref && shelf_linked_ref->GetFormID() == shelf_form_id) {
|
||||
logger::info("ClearMerchandise placeholder ref is linked with cleared shelf");
|
||||
RE::TESObjectREFR * linked_ref = ref->GetLinkedRef(item_keyword);
|
||||
if (linked_ref) {
|
||||
logger::info(FMT_STRING("ClearMerchandise deleting ref linked to placeholder ref: {:x}"), (uint32_t)linked_ref->GetFormID());
|
||||
// TODO: should I use the MemoryManager to free these references?
|
||||
// linked_ref->Load3D(false);
|
||||
// linked_ref->SetPosition(linked_ref->GetPosition() -= RE::NiPoint3(-10000, -10000, -10000));
|
||||
// linked_ref->Update3DPosition(false);
|
||||
// linked_ref->Set3D(ref->GetCurrent3D());
|
||||
// linked_ref->Release3DRelatedData();
|
||||
linked_ref->Disable(); // disabling first is required to prevent CTD on unloading cell
|
||||
linked_ref->SetDelete(true);
|
||||
// linked_ref->DeleteThis(); // does this do anything?
|
||||
}
|
||||
logger::info(FMT_STRING("ClearMerchandise deleting existing placeholder ref: {:x}"), (uint32_t)ref->GetFormID());
|
||||
// ref->Load3D(false);
|
||||
// ref->SetPosition(ref->GetPosition() -= RE::NiPoint3(-10000, -10000, -10000));
|
||||
// ref->Update3DPosition(false);
|
||||
// ref->Release3DRelatedData();
|
||||
ref->Disable(); // disabling first is required to prevent CTD on unloading cell
|
||||
ref->SetDelete(true);
|
||||
// ref->DeleteThis(); // does this do anything?
|
||||
cell->references.erase(*entry++); // prevents slowdowns after many runs of ClearMerchandise
|
||||
}
|
||||
else {
|
||||
++entry;
|
||||
}
|
||||
}
|
||||
else {
|
||||
++entry;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ref->IsDisabled() && ref->IsMarkedForDeletion() && ref->IsDeleted()) {
|
||||
logger::info("ClearMerchandise ref is probably an item from old LoadMerchandise, clearing from cell now");
|
||||
cell->references.erase(*entry++);
|
||||
}
|
||||
else {
|
||||
logger::info("ClearMerchandise ref has no base, skipping");
|
||||
++entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger::error("ClearMerchandise merchant_chest or merchant_shelf is null!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadMerchandiseImpl(
|
||||
RE::BSFixedString api_url,
|
||||
RE::BSFixedString api_key,
|
||||
uint32_t merchandise_list_id,
|
||||
RE::TESObjectREFR* merchant_shelf,
|
||||
RE::TESForm* placeholder_static,
|
||||
RE::BGSKeyword* shelf_keyword,
|
||||
RE::BGSKeyword* chest_keyword,
|
||||
RE::BGSKeyword* item_keyword,
|
||||
RE::BGSKeyword* toggle_keyword,
|
||||
RE::BGSKeyword* next_keyword,
|
||||
RE::BGSKeyword* prev_keyword,
|
||||
int page)
|
||||
{
|
||||
logger::info("Entered LoadMerchandise");
|
||||
logger::info(FMT_STRING("LoadMerchandise page: {:d}"), page);
|
||||
|
||||
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
|
||||
RE::BSScript::Internal::VirtualMachine * a_vm = RE::BSScript::Internal::VirtualMachine::GetSingleton();
|
||||
using func_t = decltype(&PlaceAtMe);
|
||||
REL::Relocation<func_t> PlaceAtMe_Native{ REL::ID(55672) };
|
||||
using func_t2 = decltype(&MoveTo);
|
||||
REL::Relocation<func_t2> MoveTo_Native(RE::Offset::TESObjectREFR::MoveTo);
|
||||
REL::ID extra_linked_ref_vtbl(static_cast<std::uint64_t>(229564));
|
||||
if (!merchant_shelf) {
|
||||
logger::error("LoadMerchandise merchant_shelf is null!");
|
||||
return false;
|
||||
}
|
||||
RE::TESObjectREFR * merchant_chest = merchant_shelf->GetLinkedRef(chest_keyword);
|
||||
if (!merchant_chest) {
|
||||
logger::error("LoadMerchandise merchant_chest is null!");
|
||||
return false;
|
||||
}
|
||||
|
||||
FFIResult<MerchRecordVec> result = get_merchandise_list(api_url.c_str(), api_key.c_str(), merchandise_list_id);
|
||||
if (result.IsOk()) {
|
||||
logger::info("LoadMerchandise get_merchandise_list result OK");
|
||||
MerchRecordVec vec = result.AsOk();
|
||||
logger::info(FMT_STRING("LoadMerchandise vec len: {:d}, cap: {:d}"), vec.len, vec.cap);
|
||||
int max_page = std::ceil((float)(vec.len - 1) / (float)9);
|
||||
|
||||
if (vec.len > 0 && page > max_page) {
|
||||
logger::info(FMT_STRING("LoadMerchandise page {:d} is greater than max_page {:d}, doing nothing"), page, max_page);
|
||||
return true;
|
||||
}
|
||||
|
||||
ClearMerchandiseImpl(merchant_chest, merchant_shelf, placeholder_static, shelf_keyword, item_keyword);
|
||||
|
||||
logger::info(FMT_STRING("LoadMerchandise current shelf page is: {:d}"), merchant_shelf->extraList.GetCount());
|
||||
for (int i = 0; i < vec.len; i++) {
|
||||
MerchRecord rec = vec.ptr[i];
|
||||
logger::info(FMT_STRING("LoadMerchandise item: {:d}"), i);
|
||||
if (i < (page - 1) * 9 || i >= (page - 1) * 9 + 9) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RE::TESForm * form = data_handler->LookupForm(rec.local_form_id, rec.mod_name);
|
||||
if (!form) { // form is not found, might be in an uninstalled mod
|
||||
logger::warn(FMT_STRING("LoadMerchandise not spawning ref for form that could not be found in installed mods: {} {:d}"), rec.mod_name, rec.local_form_id);
|
||||
continue;
|
||||
}
|
||||
logger::info(FMT_STRING("LoadMerchandise lookup form name: {}, form_id: {:x}, form_type: {:x}"), form->GetName(), (uint32_t)form->GetFormID(), (uint32_t)form->GetFormType());
|
||||
RE::TESObjectREFR * ref = PlaceAtMe_Native(a_vm, 0, merchant_shelf, form, 1, false, false);
|
||||
|
||||
RE::TESBoundObject * base = ref->GetBaseObject();
|
||||
RE::NiNPShortPoint3 boundMin = base->boundData.boundMin;
|
||||
RE::NiNPShortPoint3 boundMax = base->boundData.boundMax;
|
||||
uint16_t bound_x = boundMax.x > boundMin.x ? boundMax.x - boundMin.x : boundMin.x - boundMax.x;
|
||||
uint16_t bound_y = boundMax.y > boundMin.y ? boundMax.y - boundMin.y : boundMin.y - boundMax.y;
|
||||
uint16_t bound_z = boundMax.z > boundMin.z ? boundMax.z - boundMin.z : boundMin.z - boundMax.z;
|
||||
logger::info(FMT_STRING("LoadMerchandise ref bounds width: {:d}, length: {:d}, height: {:d}"), bound_x, bound_y, bound_z);
|
||||
|
||||
RE::TESObjectREFR * placeholder_ref = PlaceAtMe_Native(a_vm, 0, merchant_shelf, placeholder_static, 1, false, false);
|
||||
|
||||
RE::NiPoint3 bound_min = ref->GetBoundMin();
|
||||
RE::NiPoint3 bound_max = ref->GetBoundMax();
|
||||
logger::info(FMT_STRING("LoadMerchandise ref bounds min: {:.2f} {:.2f} {:.2f}, max: {:.2f} {:.2f} {:.2f}"), bound_min.x, bound_min.y, bound_min.z, bound_max.x, bound_max.y, bound_max.z);
|
||||
|
||||
RE::ExtraLinkedRef * extra_linked_ref = (RE::ExtraLinkedRef*)RE::BSExtraData::Create(sizeof(RE::ExtraLinkedRef), extra_linked_ref_vtbl.address());
|
||||
// RE::BGSKeywordForm * place_keyword1 = data_handler->LookupForm<RE::BGSKeywordForm>(595228, "BazaarRealm.esm");
|
||||
extra_linked_ref->linkedRefs.push_back({shelf_keyword, merchant_shelf});
|
||||
placeholder_ref->extraList.Add(extra_linked_ref);
|
||||
// _MESSAGE("PLACEHOLDER LINKED REF: %s", placeholder_ref->GetLinkedRef(nullptr)->GetName());
|
||||
|
||||
// This extra count stored on the placeholder_ref indicates the quanity of the merchandise item it is linked to
|
||||
RE::ExtraCount * extra_page_num = (RE::ExtraCount*)RE::BSExtraData::Create(sizeof(RE::ExtraCount), RE::Offset::ExtraCount::Vtbl.address());
|
||||
extra_page_num->count = rec.quantity;
|
||||
placeholder_ref->extraList.Add(extra_page_num);
|
||||
|
||||
float scale = 1;
|
||||
int max_over_bound = 0;
|
||||
if (max_over_bound < bound_x - 34) {
|
||||
max_over_bound = bound_x - 34;
|
||||
}
|
||||
if (max_over_bound < bound_y - 34) {
|
||||
max_over_bound = bound_y - 34;
|
||||
}
|
||||
if (max_over_bound < bound_z - 34) {
|
||||
max_over_bound = bound_z - 34;
|
||||
}
|
||||
if (max_over_bound > 0) {
|
||||
scale = ((float)34 / (float)(max_over_bound + 34)) * (float)100;
|
||||
logger::info(FMT_STRING("LoadMerchandise new scale: {:.2f} {:d} (max_over_bound: {:d}"), scale, static_cast<uint16_t>(scale), max_over_bound);
|
||||
ref->refScale = static_cast<uint16_t>(scale);
|
||||
placeholder_ref->refScale = static_cast<uint16_t>(scale);
|
||||
}
|
||||
|
||||
RE::NiPoint3 shelf_position = merchant_shelf->data.location;
|
||||
RE::NiPoint3 shelf_angle = merchant_shelf->data.angle;
|
||||
RE::NiPoint3 ref_angle = RE::NiPoint3(shelf_angle.x, shelf_angle.y, shelf_angle.z - 3.14);
|
||||
RE::NiPoint3 ref_position;
|
||||
int x_imbalance = (((bound_min.x * -1) - bound_max.x) * (scale / 100)) / 2;
|
||||
int y_imbalance = (((bound_min.y * -1) - bound_max.y) * (scale / 100)) / 2;
|
||||
// adjusts z-height so item doesn't spawn underneath it's shelf
|
||||
int z_imbalance = (bound_min.z * -1) - bound_max.z;
|
||||
if (z_imbalance < 0) {
|
||||
z_imbalance = 0;
|
||||
}
|
||||
// TODO: make page size and buy_activator positions configurable per "shelf" type (where is config stored?)
|
||||
if (i % 9 == 0) {
|
||||
ref_position = RE::NiPoint3(shelf_position.x + 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 110 + z_imbalance);
|
||||
}
|
||||
else if (i % 9 == 1) {
|
||||
ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 110 + z_imbalance);
|
||||
}
|
||||
else if (i % 9 == 2) {
|
||||
ref_position = RE::NiPoint3(shelf_position.x - 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 110 + z_imbalance);
|
||||
}
|
||||
else if (i % 9 == 3) {
|
||||
ref_position = RE::NiPoint3(shelf_position.x + 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 65 + z_imbalance);
|
||||
}
|
||||
else if (i % 9 == 4) {
|
||||
ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 65 + z_imbalance);
|
||||
}
|
||||
else if (i % 9 == 5) {
|
||||
ref_position = RE::NiPoint3(shelf_position.x - 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 65 + z_imbalance);
|
||||
}
|
||||
else if (i % 9 == 6) {
|
||||
ref_position = RE::NiPoint3(shelf_position.x + 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 20 + z_imbalance);
|
||||
}
|
||||
else if (i % 9 == 7) {
|
||||
ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 20 + z_imbalance);
|
||||
}
|
||||
else if (i % 9 == 8) {
|
||||
ref_position = RE::NiPoint3(shelf_position.x - 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 20 + z_imbalance);
|
||||
}
|
||||
RE::TESObjectCELL * cell = merchant_shelf->GetParentCell();
|
||||
MoveTo_Native(ref, ref->CreateRefHandle(), cell, cell->worldSpace, ref_position - RE::NiPoint3(10000, 10000, 10000), ref_angle);
|
||||
MoveTo_Native(placeholder_ref, placeholder_ref->CreateRefHandle(), cell, cell->worldSpace, ref_position, ref_angle);
|
||||
// ref->Load3D(false);
|
||||
// Note: passing false to this method occasionally causes the game to crash due to access violation
|
||||
// RE::NiAVObject * obj_3d = ref->Load3D(true);
|
||||
// None of this works, havok is still applied, which isn't that bad really
|
||||
// _MESSAGE("objectReference: %x, GetBaseObject: %x", *ref->data.objectReference, *ref->GetBaseObject());
|
||||
// RE::NiAVObject * cloned_obj_3d = ref->data.objectReference->Clone3D(placeholder_ref, false);
|
||||
// _MESSAGE("loaded 3d: %x, cloned 3d: %x", *obj_3d, *cloned_obj_3d);
|
||||
// Need to Load3D() before calling this:
|
||||
// ref->SetMotionType(RE::TESObjectREFR::MotionType::kKeyframed);
|
||||
// obj_3d->SetMotionType(static_cast<uint32_t>(RE::TESObjectREFR::MotionType::kKeyframed));
|
||||
// obj_3d->SetMotionType(5);
|
||||
// Fails if loadedData is nullptr (if Load3D is not called first):
|
||||
// ref->loadedData->flags = ref->loadedData->flags | RE::TESObjectREFR::RecordFlags::kDontHavokSettle | RE::TESObjectREFR::RecordFlags::kCollisionsDisabled | RE::TESObjectREFR::RecordFlags::kCollisionsDisabled;
|
||||
// ref->InitHavok();
|
||||
/// ref->DetachHavok(obj_3d);
|
||||
// ref->SetCollision(false);
|
||||
// ref->ClampToGround();
|
||||
// placeholder_ref->Load3D(false);
|
||||
// RE::NiPointer<RE::NiAVObject> placeholder_3d_data = placeholder_ref->loadedData->data3D;
|
||||
// _MESSAGE("PLACEHOLDER 3D (pre-set-3d): %x", placeholder_3d_data.get());
|
||||
// placeholder_ref->Set3D(obj_3d); // steal the 3D model from the item ref
|
||||
|
||||
// RE::ExtraLight * x_light = ref->extraList.GetByType<RE::ExtraLight>();
|
||||
// if (x_light) {
|
||||
// _MESSAGE("ExtraLight exists on ref: %x", x_light);
|
||||
// ref->extraList.RemoveByType(RE::ExtraDataType::kLight);
|
||||
// x_light = ref->extraList.GetByType<RE::ExtraLight>();
|
||||
// if (!x_light) {
|
||||
// _MESSAGE("ExtraLight removed");
|
||||
// }
|
||||
// else {
|
||||
// _MESSAGE("After removing ExtraLight: %x", x_light);
|
||||
// }
|
||||
// }
|
||||
|
||||
// x_light = placeholder_ref->extraList.GetByType<RE::ExtraLight>();
|
||||
// if (x_light) {
|
||||
// _MESSAGE("ExtraLight exists on placeholder_ref: %x", x_light);
|
||||
// placeholder_ref->extraList.RemoveByType(RE::ExtraDataType::kLight);
|
||||
// x_light = placeholder_ref->extraList.GetByType<RE::ExtraLight>();
|
||||
// if (!x_light) {
|
||||
// _MESSAGE("ExtraLight removed");
|
||||
// }
|
||||
// else {
|
||||
// _MESSAGE("After removing ExtraLight: %x", x_light);
|
||||
// }
|
||||
// }
|
||||
RE::BSFixedString name = RE::BSFixedString::BSFixedString(ref->GetName());
|
||||
placeholder_ref->SetDisplayName(name, true);
|
||||
// placeholder_ref->SetObjectReference(base);
|
||||
placeholder_ref->extraList.SetOwner(base); // I'm abusing "owner" to link the activator with the Form that should be bought once activated
|
||||
|
||||
// Do I still need to set this flag? I could maybe use the deleted flag instead
|
||||
// uint32_t phantom_ref_flag = 1 << 9; // this is my own made up ExtraFlags::Flag that marks the reference we stole the 3D from as needing to be deleted at the start of the next LoadMerchandise
|
||||
// RE::ExtraFlags * x_flags = ref->extraList.GetByType<RE::ExtraFlags>();
|
||||
// RE::ExtraFlags::Flag new_flags;
|
||||
// if (x_flags) {
|
||||
// _MESSAGE("REF XFLAGS pre-set: %x", x_flags->flags);
|
||||
// new_flags = (RE::ExtraFlags::Flag)((uint32_t)(x_flags->flags) | phantom_ref_flag);
|
||||
// }
|
||||
// else {
|
||||
// new_flags = (RE::ExtraFlags::Flag)phantom_ref_flag;
|
||||
// }
|
||||
//ref->extraList.SetExtraFlags(new_flags, true);
|
||||
// x_flags = ref->extraList.GetByType<RE::ExtraFlags>();
|
||||
extra_linked_ref->linkedRefs.push_back({item_keyword, ref});
|
||||
// _MESSAGE("REF XFLAGS post-set: %x", x_flags->flags);
|
||||
|
||||
// Test deleting ref that owns 3d
|
||||
// ref->Disable(); // disabling first is required to prevent CTD on unloading cell
|
||||
// ref->SetDelete(true);
|
||||
// ref->Predestroy();
|
||||
// ref->formFlags |= RE::TESObjectREFR::RecordFlags::kDeleted;
|
||||
// ref->AddChange(RE::TESObjectREFR::ChangeFlags::kItemExtraData);
|
||||
// ref->AddChange(RE::TESObjectREFR::ChangeFlags::kGameOnlyExtra);
|
||||
// ref->AddChange(RE::TESObjectREFR::ChangeFlags::kCreatedOnlyExtra);
|
||||
|
||||
// placeholder_ref->inGameFormFlags |= RE::TESObjectREFR::InGameFormFlag::kWantsDelete;
|
||||
// placeholder_ref->AddChange(RE::TESObjectREFR::ChangeFlags::kGameOnlyExtra);
|
||||
}
|
||||
|
||||
// I'm abusing the ExtraCount ExtraData type for storing the current page number state of the shelf
|
||||
RE::ExtraCount * extra_page_num = merchant_shelf->extraList.GetByType<RE::ExtraCount>();
|
||||
if (!extra_page_num) {
|
||||
extra_page_num = (RE::ExtraCount*)RE::BSExtraData::Create(sizeof(RE::ExtraCount), RE::Offset::ExtraCount::Vtbl.address());
|
||||
merchant_shelf->extraList.Add(extra_page_num);
|
||||
}
|
||||
extra_page_num->count = page;
|
||||
logger::info(FMT_STRING("LoadMerchandise set shelf page to: {:d}"), merchant_shelf->extraList.GetCount());
|
||||
|
||||
// I'm abusing the ExtraCannotWear ExtraData type as a boolean marker which stores whether the shelf is in a loaded or cleared state
|
||||
// The presense of ExtraCannotWear == loaded, its absence == cleared
|
||||
// Please don't try to wear the shelves :)
|
||||
RE::ExtraCannotWear * extra_is_loaded = merchant_shelf->extraList.GetByType<RE::ExtraCannotWear>();
|
||||
if (!extra_is_loaded) {
|
||||
extra_is_loaded = (RE::ExtraCannotWear*)RE::BSExtraData::Create(sizeof(RE::ExtraCannotWear), RE::Offset::ExtraCannotWear::Vtbl.address());
|
||||
merchant_shelf->extraList.Add(extra_is_loaded);
|
||||
}
|
||||
logger::info(FMT_STRING("LoadMerchandise set loaded: {:d}"), merchant_shelf->extraList.GetByType<RE::ExtraCannotWear>() != nullptr);
|
||||
|
||||
RE::TESObjectREFR * toggle_ref = merchant_shelf->GetLinkedRef(toggle_keyword);
|
||||
if (!toggle_ref) {
|
||||
logger::error("LoadMerchandise toggle_ref is null!");
|
||||
return false;
|
||||
}
|
||||
RE::TESObjectREFR * next_ref = merchant_shelf->GetLinkedRef(next_keyword);
|
||||
if (!next_ref) {
|
||||
logger::error("LoadMerchandise next_ref is null!");
|
||||
return false;
|
||||
}
|
||||
RE::TESObjectREFR * prev_ref = merchant_shelf->GetLinkedRef(prev_keyword);
|
||||
if (!prev_ref) {
|
||||
logger::error("LoadMerchandise prev_ref is null!");
|
||||
return false;
|
||||
}
|
||||
toggle_ref->SetDisplayName("Clear merchandise", true);
|
||||
if (page == max_page) {
|
||||
next_ref->SetDisplayName("(No next page)", true);
|
||||
}
|
||||
else {
|
||||
next_ref->SetDisplayName(fmt::format("Advance to page %d", page + 1).c_str(), true);
|
||||
}
|
||||
if (page == 1) {
|
||||
prev_ref->SetDisplayName("(No previous page)", true);
|
||||
}
|
||||
else {
|
||||
prev_ref->SetDisplayName(fmt::format("Back to page %d", page - 1).c_str(), true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const char * error = result.AsErr();
|
||||
logger::error(FMT_STRING("LoadMerchandise get_merchandise_list error: {}"), error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ToggleMerchandise(
|
||||
RE::StaticFunctionTag*,
|
||||
RE::BSFixedString api_url,
|
||||
RE::BSFixedString api_key,
|
||||
uint32_t merchandise_list_id,
|
||||
RE::TESObjectREFR* merchant_shelf,
|
||||
RE::TESForm* placeholder_static,
|
||||
RE::BGSKeyword* shelf_keyword,
|
||||
RE::BGSKeyword* chest_keyword,
|
||||
RE::BGSKeyword* item_keyword,
|
||||
RE::BGSKeyword* toggle_keyword,
|
||||
RE::BGSKeyword* next_keyword,
|
||||
RE::BGSKeyword* prev_keyword)
|
||||
{
|
||||
if (!merchant_shelf) {
|
||||
logger::error("ToggleMerchandise merchant_shelf is null!");
|
||||
return false;
|
||||
}
|
||||
// I'm abusing the ExtraCannotWear ExtraData type as a boolean marker which stores whether the shelf is in a loaded or cleared state
|
||||
// The presense of ExtraCannotWear == loaded, its absence == cleared
|
||||
// Please don't try to wear the shelves :)
|
||||
RE::ExtraCannotWear * extra_is_loaded = merchant_shelf->extraList.GetByType<RE::ExtraCannotWear>();
|
||||
if (extra_is_loaded) {
|
||||
// Clear merchandise
|
||||
RE::TESObjectREFR * merchant_chest = merchant_shelf->GetLinkedRef(chest_keyword);
|
||||
if (!ClearMerchandiseImpl(merchant_chest, merchant_shelf, placeholder_static, shelf_keyword, item_keyword)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset shelf page to 1 and set state to cleared
|
||||
merchant_shelf->extraList.RemoveByType(RE::ExtraDataType::kCount);
|
||||
merchant_shelf->extraList.RemoveByType(RE::ExtraDataType::kCannotWear);
|
||||
|
||||
RE::TESObjectREFR * toggle_ref = merchant_shelf->GetLinkedRef(toggle_keyword);
|
||||
if (!toggle_ref) {
|
||||
logger::error("ToggleMerchandise toggle_ref is null!");
|
||||
return false;
|
||||
}
|
||||
RE::TESObjectREFR * next_ref = merchant_shelf->GetLinkedRef(next_keyword);
|
||||
if (!next_ref) {
|
||||
logger::error("ToggleMerchandise next_ref is null!");
|
||||
return false;
|
||||
}
|
||||
RE::TESObjectREFR * prev_ref = merchant_shelf->GetLinkedRef(prev_keyword);
|
||||
if (!prev_ref) {
|
||||
logger::error("ToggleMerchandise prev_ref is null!");
|
||||
return false;
|
||||
}
|
||||
toggle_ref->SetDisplayName("Load merchandise", true);
|
||||
next_ref->SetDisplayName("Load merchandise", true);
|
||||
prev_ref->SetDisplayName("Load merchandise", true);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// Load merchandise
|
||||
int page = merchant_shelf->extraList.GetCount();
|
||||
return LoadMerchandiseImpl(api_url, api_key, merchandise_list_id, merchant_shelf, placeholder_static, shelf_keyword, chest_keyword, item_keyword, toggle_keyword, next_keyword, prev_keyword, page);
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadNextMerchandise(
|
||||
RE::StaticFunctionTag*,
|
||||
RE::BSFixedString api_url,
|
||||
RE::BSFixedString api_key,
|
||||
uint32_t merchandise_list_id,
|
||||
RE::TESObjectREFR* merchant_shelf,
|
||||
RE::TESForm* placeholder_static,
|
||||
RE::BGSKeyword* shelf_keyword,
|
||||
RE::BGSKeyword* chest_keyword,
|
||||
RE::BGSKeyword* item_keyword,
|
||||
RE::BGSKeyword* toggle_keyword,
|
||||
RE::BGSKeyword* next_keyword,
|
||||
RE::BGSKeyword* prev_keyword)
|
||||
{
|
||||
if (!merchant_shelf) {
|
||||
logger::error("LoadNextMerchandise merchant_shelf is null!");
|
||||
return false;
|
||||
}
|
||||
int page = merchant_shelf->extraList.GetCount();
|
||||
|
||||
RE::ExtraCannotWear * extra_is_loaded = merchant_shelf->extraList.GetByType<RE::ExtraCannotWear>();
|
||||
if (extra_is_loaded) {
|
||||
// Only advance the page if shelf is in loaded state, else just load the (first) page
|
||||
page = page + 1;
|
||||
}
|
||||
|
||||
return LoadMerchandiseImpl(api_url, api_key, merchandise_list_id, merchant_shelf, placeholder_static, shelf_keyword, chest_keyword, item_keyword, toggle_keyword, next_keyword, prev_keyword, page);
|
||||
}
|
||||
|
||||
bool LoadPrevMerchandise(
|
||||
RE::StaticFunctionTag*,
|
||||
RE::BSFixedString api_url,
|
||||
RE::BSFixedString api_key,
|
||||
uint32_t merchandise_list_id,
|
||||
RE::TESObjectREFR* merchant_shelf,
|
||||
RE::TESForm* placeholder_static,
|
||||
RE::BGSKeyword* shelf_keyword,
|
||||
RE::BGSKeyword* chest_keyword,
|
||||
RE::BGSKeyword* item_keyword,
|
||||
RE::BGSKeyword* toggle_keyword,
|
||||
RE::BGSKeyword* next_keyword,
|
||||
RE::BGSKeyword* prev_keyword)
|
||||
{
|
||||
if (!merchant_shelf) {
|
||||
logger::error("LoadPrevMerchandise merchant_shelf is null!");
|
||||
return false;
|
||||
}
|
||||
int page = merchant_shelf->extraList.GetCount();
|
||||
|
||||
if (page == 1) { // no-op on first page
|
||||
return true;
|
||||
}
|
||||
|
||||
RE::ExtraCannotWear * extra_is_loaded = merchant_shelf->extraList.GetByType<RE::ExtraCannotWear>();
|
||||
if (extra_is_loaded) {
|
||||
// Only advance the page if shelf is in loaded state, else just load the (first) page
|
||||
page = page - 1;
|
||||
}
|
||||
|
||||
return LoadMerchandiseImpl(api_url, api_key, merchandise_list_id, merchant_shelf, placeholder_static, shelf_keyword, chest_keyword, item_keyword, toggle_keyword, next_keyword, prev_keyword, page);
|
||||
}
|
||||
|
||||
bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf, RE::TESForm* placeholder_static, RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* item_keyword) {
|
||||
logger::info("Entered ReplaceMerch3D");
|
||||
|
||||
if (!merchant_shelf) {
|
||||
logger::error("LoadMerchandise merchant_shelf is null!");
|
||||
return false;
|
||||
}
|
||||
RE::TESObjectCELL * cell = merchant_shelf->GetParentCell();
|
||||
RE::FormID placeholder_form_id = placeholder_static->GetFormID();
|
||||
RE::FormID shelf_form_id = merchant_shelf->GetFormID();
|
||||
for (auto entry = cell->references.begin(); entry != cell->references.end(); ++entry)
|
||||
{
|
||||
RE::TESObjectREFR * ref = (*entry).get();
|
||||
RE::TESBoundObject * base = ref->GetBaseObject();
|
||||
if (base) {
|
||||
if (base->GetFormID() == placeholder_form_id) {
|
||||
logger::info(FMT_STRING("ReplaceMerch3D REF is a placeholder ref: {:x}"), (uint32_t)ref->GetFormID());
|
||||
RE::TESObjectREFR * shelf_linked_ref = ref->GetLinkedRef(shelf_keyword);
|
||||
if (shelf_linked_ref && shelf_linked_ref->GetFormID() == shelf_form_id) {
|
||||
logger::info("ReplaceMerch3D placeholder ref is linked with loaded shelf");
|
||||
RE::TESObjectREFR * linked_ref = ref->GetLinkedRef(item_keyword);
|
||||
if (linked_ref) {
|
||||
logger::info(FMT_STRING("ReplaceMerch3D placeholder has linked item ref: {:x}"), (uint32_t)linked_ref->GetFormID());
|
||||
if (linked_ref->Is3DLoaded()) {
|
||||
logger::info("ReplaceMerch3D replaceing placeholder 3D with linked item 3D");
|
||||
ref->Set3D(linked_ref->GetCurrent3D());
|
||||
}
|
||||
else {
|
||||
logger::info("ReplaceMerch3D linked item ref 3D is not loaded yet, returning false");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger::info("ReplaceMerch3D ref has no base, skipping");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
RE::TESForm * BuyMerchandise(RE::StaticFunctionTag*, RE::TESObjectREFR * merchandise_placeholder) {
|
||||
logger::info("Entered BuyMerchandise");
|
||||
logger::info(FMT_STRING("BuyMerchandise activated ref: {}"), merchandise_placeholder->GetName());
|
||||
RE::TESForm * owner = merchandise_placeholder->GetOwner();
|
||||
logger::info(FMT_STRING("BuyMerchandise owner: {}"), owner->GetName());
|
||||
logger::info(FMT_STRING("BuyMerchandise count: {:d}"), merchandise_placeholder->extraList.GetCount());
|
||||
// TODO: do add item here
|
||||
// player->AddObjectToContainer(owner, *RE::ExtraDataList::ExtraDataList(), 1, merchandise_placeholder)
|
||||
return owner;
|
||||
}
|
||||
|
||||
// Return code:
|
||||
// -2: No changes to save, no create request was made
|
||||
// -1: Error occured
|
||||
// >= 0: ID of created MerchandiseList returned by API
|
||||
int CreateMerchandiseList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectREFR* merchant_chest)
|
||||
{
|
||||
logger::info("Entered CreateMerchandiseList");
|
||||
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
|
||||
std::vector<MerchRecord> merch_records;
|
||||
if (!merchant_chest) {
|
||||
logger::error("CreateMerchandiseList merchant_chest is null!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
RE::InventoryChanges * inventory_changes = merchant_chest->GetInventoryChanges();
|
||||
if (inventory_changes == nullptr) {
|
||||
logger::info("CreateMerchandiseList container empty, nothing to save");
|
||||
return -2;
|
||||
}
|
||||
|
||||
RE::BSSimpleList<RE::InventoryEntryData*>* entries = inventory_changes->entryList;
|
||||
int count = 0;
|
||||
for (auto entry = entries->begin(); entry != entries->end(); ++entry) {
|
||||
logger::info(FMT_STRING("CreateMerchandiseList container iterator count: {:d}"), count);
|
||||
RE::InventoryEntryData * entry_data = *entry;
|
||||
RE::TESBoundObject * base = entry_data->GetObject();
|
||||
if (base) {
|
||||
RE::BSSimpleList<RE::ExtraDataList*> * x_lists = entry_data->extraLists;
|
||||
if (x_lists) {
|
||||
const char * entry_name = entry_data->extraLists->front()->GetDisplayName(base);
|
||||
logger::info(FMT_STRING("CreateMerchandiseList container item display_name: {}"), entry_name);
|
||||
int x_list_count = 0;
|
||||
for (auto x_list = entry_data->extraLists->begin(); x_list != entry_data->extraLists->end(); ++x_list) {
|
||||
logger::info(FMT_STRING("CreateMerchandiseList container item x_list: {:d}"), x_list_count);
|
||||
int x_data_count = 0;
|
||||
for (auto x_data = (*x_list)->begin(); x_data != (*x_list)->end(); ++x_data) {
|
||||
logger::info(FMT_STRING("CreateMerchandiseList container item x_data: {:d}, type: {:x}"), x_data_count, (uint32_t)x_data->GetType());
|
||||
x_data_count++;
|
||||
}
|
||||
|
||||
RE::ExtraEnchantment * enchantment = (RE::ExtraEnchantment*)(*x_list)->GetByType(RE::ExtraDataType::kEnchantment);
|
||||
if (enchantment) {
|
||||
logger::info(FMT_STRING("CreateMerchandiseList container item enchantment charge: {:d}"), enchantment->charge);
|
||||
}
|
||||
RE::ExtraScale * scale = (RE::ExtraScale*)(*x_list)->GetByType(RE::ExtraDataType::kScale);
|
||||
if (scale) {
|
||||
logger::info(FMT_STRING("CreateMerchandiseList container item scale: {:.2f}"), scale->scale);
|
||||
}
|
||||
x_list_count++;
|
||||
}
|
||||
}
|
||||
const char * name = base->GetName();
|
||||
uint32_t form_type = (uint32_t)base->GetFormType();
|
||||
uint32_t quantity = entry_data->countDelta;
|
||||
RE::FormID form_id = base->GetFormID();
|
||||
logger::info(FMT_STRING("CreateMerchandiseList quantity: {:d}"), quantity);
|
||||
logger::info(FMT_STRING("CreateMerchandiseList base form_id: {:x}, name: {}, type: {:x}"), (uint32_t)form_id, name, (uint32_t)form_type);
|
||||
|
||||
RE::TESFile * file = base->GetFile(0);
|
||||
const char * mod_name = file->fileName;
|
||||
bool is_light = file->recordFlags.all(RE::TESFile::RecordFlag::kSmallFile);
|
||||
uint32_t local_form_id = is_light ? form_id & 0xfff : form_id & 0xFFFFFF;
|
||||
//_MESSAGE("FILE: %s isLight: %d formID: 0x%x localFormId: 0x%x", file_name, is_light, form_id, local_form_id);
|
||||
|
||||
logger::info(FMT_STRING("CreateMerchandiseList base form file_name: {}, local_form_id"), mod_name, local_form_id);
|
||||
|
||||
// TODO: implement is_food
|
||||
uint8_t is_food = 0;
|
||||
// TODO: implement price
|
||||
uint32_t price = 0;
|
||||
|
||||
merch_records.push_back({
|
||||
mod_name,
|
||||
local_form_id,
|
||||
name,
|
||||
quantity,
|
||||
form_type,
|
||||
is_food,
|
||||
price,
|
||||
});
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
int merchandise_list_id = create_merchandise_list(api_url.c_str(), api_key.c_str(), shop_id, &merch_records[0], merch_records.size());
|
||||
logger::info(FMT_STRING("CreateMerchandiseList create_merchandise_list result: {:d}"), merchandise_list_id);
|
||||
return merchandise_list_id;
|
||||
}
|
45
src/BRMerchandiseList.h
Normal file
45
src/BRMerchandiseList.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
bool ClearMerchandiseImpl(RE::TESObjectREFR* merchant_chest, RE::TESObjectREFR* merchant_shelf, RE::TESForm* placeholder_static, RE::BGSKeyword * shelf_keyword, RE::BGSKeyword * item_keyword);
|
||||
bool ToggleMerchandise(
|
||||
RE::StaticFunctionTag*,
|
||||
RE::BSFixedString api_url,
|
||||
RE::BSFixedString api_key,
|
||||
uint32_t merchandise_list_id,
|
||||
RE::TESObjectREFR* merchant_shelf,
|
||||
RE::TESForm* placeholder_static,
|
||||
RE::BGSKeyword* shelf_keyword,
|
||||
RE::BGSKeyword* chest_keyword,
|
||||
RE::BGSKeyword* item_keyword,
|
||||
RE::BGSKeyword* toggle_keyword,
|
||||
RE::BGSKeyword* next_keyword,
|
||||
RE::BGSKeyword* prev_keyword);
|
||||
bool LoadNextMerchandise(
|
||||
RE::StaticFunctionTag*,
|
||||
RE::BSFixedString api_url,
|
||||
RE::BSFixedString api_key,
|
||||
uint32_t merchandise_list_id,
|
||||
RE::TESObjectREFR* merchant_shelf,
|
||||
RE::TESForm* placeholder_static,
|
||||
RE::BGSKeyword* shelf_keyword,
|
||||
RE::BGSKeyword* chest_keyword,
|
||||
RE::BGSKeyword* item_keyword,
|
||||
RE::BGSKeyword* toggle_keyword,
|
||||
RE::BGSKeyword* next_keyword,
|
||||
RE::BGSKeyword* prev_keyword);
|
||||
bool LoadPrevMerchandise(
|
||||
RE::StaticFunctionTag*,
|
||||
RE::BSFixedString api_url,
|
||||
RE::BSFixedString api_key,
|
||||
uint32_t merchandise_list_id,
|
||||
RE::TESObjectREFR* merchant_shelf,
|
||||
RE::TESForm* placeholder_static,
|
||||
RE::BGSKeyword* shelf_keyword,
|
||||
RE::BGSKeyword* chest_keyword,
|
||||
RE::BGSKeyword* item_keyword,
|
||||
RE::BGSKeyword* toggle_keyword,
|
||||
RE::BGSKeyword* next_keyword,
|
||||
RE::BGSKeyword* prev_keyword);
|
||||
bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf, RE::TESForm* placeholder_static, RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* item_keyword);
|
||||
RE::TESForm * BuyMerchandise(RE::StaticFunctionTag*, RE::TESObjectREFR * merchandise_placeholder);
|
||||
int CreateMerchandiseList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectREFR* merchant_chest);
|
13
src/BROwner.cpp
Normal file
13
src/BROwner.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
#include "bindings.h"
|
||||
|
||||
int CreateOwner(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, RE::BSFixedString name, uint32_t mod_version)
|
||||
{
|
||||
logger::info("Entered CreateOwner");
|
||||
logger::info(FMT_STRING("CreateOwner api_url: {}"), api_url);
|
||||
logger::info(FMT_STRING("CreateOwner api_key: {}"), api_key);
|
||||
logger::info(FMT_STRING("CreateOwner name: {}"), name);
|
||||
logger::info(FMT_STRING("CreateOwner mod_version: {}"), mod_version);
|
||||
int owner_id = create_owner(api_url.c_str(), api_key.c_str(), name.c_str(), mod_version);
|
||||
logger::info(FMT_STRING("CreateOwner result: {}"), owner_id);
|
||||
return owner_id;
|
||||
}
|
3
src/BROwner.h
Normal file
3
src/BROwner.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int CreateOwner(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, RE::BSFixedString name, uint32_t mod_version);
|
13
src/BRShop.cpp
Normal file
13
src/BRShop.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
#include "bindings.h"
|
||||
|
||||
int CreateShop(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, RE::BSFixedString name, RE::BSFixedString description)
|
||||
{
|
||||
logger::info("Entered CreateShop");
|
||||
logger::info(FMT_STRING("CreateShop api_url: {}"), api_url);
|
||||
logger::info(FMT_STRING("CreateShop api_key: {}"), api_key);
|
||||
logger::info(FMT_STRING("CreateShop name: {}"), name);
|
||||
logger::info(FMT_STRING("CreateShop description: {}"), description);
|
||||
int shop_id = create_shop(api_url.c_str(), api_key.c_str(), name.c_str(), description.c_str());
|
||||
logger::info(FMT_STRING("CreateShop result: {}"), shop_id);
|
||||
return shop_id;
|
||||
}
|
3
src/BRShop.h
Normal file
3
src/BRShop.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int CreateShop(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, RE::BSFixedString name, RE::BSFixedString description);
|
4
src/NativeFunctions.h
Normal file
4
src/NativeFunctions.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
RE::TESObjectREFR* PlaceAtMe(RE::BSScript::IVirtualMachine* a_vm, int something, RE::TESObjectREFR* ref, RE::TESForm* form, int count, bool forcePersist, bool initiallyDisabled);
|
||||
void MoveTo(RE::TESObjectREFR * ref, const RE::ObjectRefHandle& a_targetHandle, RE::TESObjectCELL* a_targetCell, RE::TESWorldSpace* a_selfWorldSpace, const RE::NiPoint3& a_position, const RE::NiPoint3& a_rotation);
|
1
src/PCH.cpp
Normal file
1
src/PCH.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "PCH.h"
|
16
src/PCH.h
Normal file
16
src/PCH.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "RE/Skyrim.h"
|
||||
#include "SKSE/SKSE.h"
|
||||
|
||||
#ifdef NDEBUG
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
#else
|
||||
#include <spdlog/sinks/msvc_sink.h>
|
||||
#endif
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace logger = SKSE::log;
|
||||
|
||||
#define DLLEXPORT __declspec(dllexport)
|
88
src/main.cpp
Normal file
88
src/main.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
#include "version.h"
|
||||
#include "bindings.h"
|
||||
|
||||
#include "BRClient.h"
|
||||
#include "BROwner.h"
|
||||
#include "BRShop.h"
|
||||
#include "BRInteriorRefList.h"
|
||||
#include "BRMerchandiseList.h"
|
||||
|
||||
bool RegisterFuncs(RE::BSScript::IVirtualMachine* a_vm)
|
||||
{
|
||||
a_vm->RegisterFunction("Init", "BRClient", Init);
|
||||
a_vm->RegisterFunction("StatusCheck", "BRClient", StatusCheck);
|
||||
a_vm->RegisterFunction("GenerateApiKey", "BRClient", GenerateApiKey);
|
||||
a_vm->RegisterFunction("Create", "BROwner", CreateOwner);
|
||||
a_vm->RegisterFunction("Create", "BRShop", CreateShop);
|
||||
a_vm->RegisterFunction("Create", "BRInteriorRefList", CreateInteriorRefList);
|
||||
a_vm->RegisterFunction("ClearCell", "BRInteriorRefList", ClearCell);
|
||||
a_vm->RegisterFunction("Load", "BRInteriorRefList", LoadInteriorRefList);
|
||||
a_vm->RegisterFunction("Toggle", "BRMerchandiseList", ToggleMerchandise);
|
||||
a_vm->RegisterFunction("NextPage", "BRMerchandiseList", LoadNextMerchandise);
|
||||
a_vm->RegisterFunction("PrevPage", "BRMerchandiseList", LoadPrevMerchandise);
|
||||
a_vm->RegisterFunction("Buy", "BRMerchandiseList", BuyMerchandise);
|
||||
a_vm->RegisterFunction("Replace3D", "BRMerchandiseList", ReplaceMerch3D);
|
||||
a_vm->RegisterFunction("Create", "BRMerchandiseList", CreateMerchandiseList);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" DLLEXPORT bool SKSEAPI SKSEPlugin_Query(const SKSE::QueryInterface* a_skse, SKSE::PluginInfo* a_info)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
auto sink = std::make_shared<spdlog::sinks::msvc_sink_mt>();
|
||||
#else
|
||||
auto path = logger::log_directory();
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*path /= "BazaarRealmPlugin.log"sv;
|
||||
auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(path->string(), true);
|
||||
#endif
|
||||
|
||||
auto log = std::make_shared<spdlog::logger>("global log"s, std::move(sink));
|
||||
|
||||
#ifndef NDEBUG
|
||||
log->set_level(spdlog::level::trace);
|
||||
#else
|
||||
log->set_level(spdlog::level::info);
|
||||
log->flush_on(spdlog::level::warn);
|
||||
#endif
|
||||
|
||||
spdlog::set_default_logger(std::move(log));
|
||||
spdlog::set_pattern("%g(%#): [%^%l%$] %v"s);
|
||||
|
||||
logger::info(FMT_STRING("BazaarRealmPlugin v{}"), MYFP_VERSION_VERSTRING);
|
||||
|
||||
a_info->infoVersion = SKSE::PluginInfo::kVersion;
|
||||
a_info->name = "BazaarRealmPlugin";
|
||||
a_info->version = MYFP_VERSION_MAJOR;
|
||||
|
||||
if (a_skse->IsEditor()) {
|
||||
logger::critical("Loaded in editor, marking as incompatible"sv);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto ver = a_skse->RuntimeVersion();
|
||||
if (ver < SKSE::RUNTIME_1_5_39) {
|
||||
logger::critical(FMT_STRING("Unsupported runtime version {}"), ver.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
extern "C" DLLEXPORT bool SKSEAPI SKSEPlugin_Load(const SKSE::LoadInterface* a_skse)
|
||||
{
|
||||
logger::info("BazaarRealmPlugin loaded");
|
||||
|
||||
SKSE::Init(a_skse);
|
||||
|
||||
auto papyrus = SKSE::GetPapyrusInterface();
|
||||
if (!papyrus->Register(RegisterFuncs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
15
src/version.h
Normal file
15
src/version.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef MYFP_VERSION_INCLUDED
|
||||
#define MYFP_VERSION_INCLUDED
|
||||
|
||||
#define MAKE_STR_HELPER(a_str) #a_str
|
||||
#define MAKE_STR(a_str) MAKE_STR_HELPER(a_str)
|
||||
|
||||
#define MYFP_VERSION_MAJOR 1
|
||||
#define MYFP_VERSION_MINOR 0
|
||||
#define MYFP_VERSION_PATCH 0
|
||||
#define MYFP_VERSION_BETA 0
|
||||
#define MYFP_VERSION_VERSTRING \
|
||||
MAKE_STR(MYFP_VERSION_MAJOR) \
|
||||
"." MAKE_STR(MYFP_VERSION_MINOR) "." MAKE_STR(MYFP_VERSION_PATCH) "." MAKE_STR(MYFP_VERSION_BETA)
|
||||
|
||||
#endif
|
33
version.rc
Normal file
33
version.rc
Normal file
@ -0,0 +1,33 @@
|
||||
#include "src\version.h"
|
||||
#include <winres.h>
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION MYFP_VERSION_MAJOR, MYFP_VERSION_MINOR, MYFP_VERSION_PATCH, MYFP_VERSION_BETA
|
||||
PRODUCTVERSION MYFP_VERSION_MAJOR, MYFP_VERSION_MINOR, MYFP_VERSION_PATCH, MYFP_VERSION_BETA
|
||||
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", "MyFirstPlugin"
|
||||
VALUE "FileVersion", MYFP_VERSION_VERSTRING
|
||||
VALUE "InternalName", "MyFirstPlugin"
|
||||
VALUE "LegalCopyright", "MIT License"
|
||||
VALUE "ProductName", "MyFirstPlugin"
|
||||
VALUE "ProductVersion", MYFP_VERSION_VERSTRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
Loading…
Reference in New Issue
Block a user