acdream/src/AcDream.App/AcDream.App.csproj
Erik 54005a864c feat(mosstank): cast back-to-back, and a settings view for the thresholds
Three things from the first working buff pass.

**Pacing.** Casts were three seconds apart because of a fixed interval I added
before there was a real busy signal. There is one now -- the shared busy count,
incremented by the cast and decremented by the server's UseDone -- so the
interval is gone and the server is the only throttle. MossTank casts the moment
the previous action is acknowledged, which is the spam behaviour VTank has. The
stall timeout moved 25s -> 30s so a slow server does not read as a stall.

**Stamina to Mana was surprising.** VTank does convert vitals by default
(Recharge-*-Mana), so the behaviour is right, but a buff pass quietly spending
your stamina is not something to discover by watching. It is now a setting,
with its own thresholds, and can be turned off outright.

**Settings view.** Spell difficulty margin, rebuff time, the three vital
thresholds, and three toggles. Two details worth recording:

* The difficulty margin is SIGNED, per the VTank wiki: "a positive number
  raises the skill necessary to cast spells, a negative number lowers it. To
  attempt higher spells at a low level use a negative number." So the range
  spans -100..+100 rather than starting at zero.
* It is a second registered panel with a complementary visible binding rather
  than a tab control. Two panels and an Action need no new markup vocabulary,
  and only one is ever on screen.

Adjuster buttons rather than typed fields: buttons are proven in plugin markup,
while an editable UiField would need keyboard routing plumbed through to plugin
panels first. Worth doing, but not as a side quest inside this change.

Also fixed: the App copy target names plugin files explicitly, so the new
markup would have been left out of the plugin directory and the settings panel
would have failed to load at runtime with the build perfectly green. Caught by
listing the output rather than trusting the build.

Solution builds clean; 14,438 tests pass on the standard hermetic lane filter,
0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 18:51:15 +02:00

198 lines
12 KiB
XML

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<RootNamespace>AcDream.App</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- Windows shell/Explorer icon for the client executable. The runtime
window icon is a separate mechanism (WindowIconLoader) because the exe
icon is baked into the PE and never reaches GLFW. -->
<ApplicationIcon>..\..\assets\icons\acdream-client.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="AcDream.Core.Tests" />
<InternalsVisibleTo Include="AcDream.App.Tests" />
</ItemGroup>
<ItemGroup>
<!-- T3 originally cited: "extracted GL infra (ManagedGLUniformBuffer,
OpenGLGraphicsDevice) directly implement IUniformBuffer from
Chorizite.Core". Campaign V slice V11 deleted both classes along with
the rest of the raw-GL arm, and neither IUniformBuffer nor any other
Chorizite.Core.Render type is implemented by acdream code any more —
but the package audit is NOT clean: TextureAtlasManager.cs,
WorldTextureArray.cs, ObjectMeshManager.cs, TextureFormatExtensions.cs
and others consume Chorizite.Core.Render.Enums.TextureFormat/BufferUsage
and Chorizite.Core.Lib.BoundingBox directly and extensively, entirely
independent of the deleted GL classes. The package stays; NuGet
PackageReferences are not forwarded from ProjectReferences so we must
declare it explicitly here. -->
<PackageReference Include="Chorizite.Core" />
<!-- Phase O-T7: BCnEncoder.Net.ImageSharp + SixLabors.ImageSharp were previously
transitive via the WorldBuilder project reference. Now direct deps of
ObjectMeshManager.cs (extracted in T2). -->
<PackageReference Include="BCnEncoder.Net.ImageSharp" />
<PackageReference Include="SixLabors.ImageSharp" />
<!-- Slice F: contained render-projection storage. Arch types are confined to
Rendering/Scene/Arch and never cross an acdream-owned interface. -->
<PackageReference Include="Arch" />
<!-- Campaign V slice V5: dark Vulkan bring-up. Pinned to the same 2.23.0 as
the rest of the Silk family so the shared Silk.NET.Core does not fork. -->
<PackageReference Include="Silk.NET.Vulkan" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
<!-- Campaign V slice V6: VK_EXT_debug_utils object naming and command labels.
Optional at runtime (VulkanDebugNames.Disabled when absent), but the
entry points have to be loadable for the dev-tools path to name anything. -->
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
<PackageReference Include="Silk.NET.Windowing" />
<PackageReference Include="Silk.NET.Input" />
<PackageReference Include="Silk.NET.OpenAL" />
<PackageReference Include="Silk.NET.OpenAL.Extensions.Creative" />
<PackageReference Include="Silk.NET.OpenAL.Extensions.EXT" />
<PackageReference Include="Silk.NET.OpenAL.Soft.Native" />
<PackageReference Include="Serilog" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="StbTrueTypeSharp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AcDream.Runtime\AcDream.Runtime.csproj" />
<ProjectReference Include="..\AcDream.Core\AcDream.Core.csproj" />
<ProjectReference Include="..\AcDream.Core.Net\AcDream.Core.Net.csproj" />
<ProjectReference Include="..\AcDream.Content\AcDream.Content.csproj" />
<!-- Campaign LA LA0 review finding 6: App consumes AcDream.Platform
types directly (GraphicalHostPlatformServices, Program, GameWindow),
so the reference is declared explicitly per this file's convention
rather than ridden transitively through Runtime. -->
<ProjectReference Include="..\AcDream.Platform\AcDream.Platform.csproj" />
<ProjectReference Include="..\AcDream.UI.Abstractions\AcDream.UI.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Rendering\Shaders\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<!-- Campaign V slice V6c: the committed SPIR-V the Vulkan backend loads at
startup, plus the manifest the freshness test re-hashes. Regenerated by
tools/compile-shaders.ps1; never compiled at runtime (plan §4.6). -->
<None Update="Rendering\Shaders\spv\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<!-- Phase D.2b: KSML-style panel markup assets (vitals.xml etc.) ship
next to the binary so MarkupDocument.Build can load them at runtime. -->
<None Include="UI\assets\**\*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<!-- Runtime window icon (WindowIconLoader). Embedded rather than copied:
a window icon has no sensible fallback if the file goes missing, and
embedding survives single-file publish. Included from assets/icons
with an explicit LogicalName so the art has ONE source of truth and is
not duplicated into this project's tree. -->
<EmbeddedResource Include="..\..\assets\icons\acdream-client-16.png"
LogicalName="AcDream.App.Rendering.Icons.acdream-client-16.png" />
<EmbeddedResource Include="..\..\assets\icons\acdream-client-32.png"
LogicalName="AcDream.App.Rendering.Icons.acdream-client-32.png" />
<EmbeddedResource Include="..\..\assets\icons\acdream-client-48.png"
LogicalName="AcDream.App.Rendering.Icons.acdream-client-48.png" />
<EmbeddedResource Include="..\..\assets\icons\acdream-client-256.png"
LogicalName="AcDream.App.Rendering.Icons.acdream-client-256.png" />
</ItemGroup>
<ItemGroup>
<!-- Build the smoke plugin first and copy it into plugins/AcDream.Plugins.Smoke/ -->
<ProjectReference Include="..\AcDream.Plugins.Smoke\AcDream.Plugins.Smoke.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
</ProjectReference>
</ItemGroup>
<Target
Name="CopySmokePluginToBuildOutput"
AfterTargets="Build"
Condition="'$(IsCrossTargetingBuild)' != 'true'">
<PropertyGroup>
<_SmokePluginSourceDir>$(MSBuildProjectDirectory)/../AcDream.Plugins.Smoke/bin/$(Configuration)/$(TargetFramework)</_SmokePluginSourceDir>
<_SmokePluginSourceDir Condition="'$(RuntimeIdentifier)' != ''">$(_SmokePluginSourceDir)/$(RuntimeIdentifier)</_SmokePluginSourceDir>
<_SmokePluginDestDir>$(OutputPath)plugins/AcDream.Plugins.Smoke</_SmokePluginDestDir>
</PropertyGroup>
<MakeDir Directories="$(_SmokePluginDestDir)" />
<Copy
SourceFiles="$(_SmokePluginSourceDir)/AcDream.Plugins.Smoke.dll"
DestinationFolder="$(_SmokePluginDestDir)"
SkipUnchangedFiles="true" />
<WriteLinesToFile
File="$(_SmokePluginDestDir)/plugin.json"
Overwrite="true"
Lines="{ &quot;id&quot;: &quot;acdream.smoke&quot;, &quot;displayName&quot;: &quot;Smoke Plugin&quot;, &quot;version&quot;: &quot;0.1.0&quot;, &quot;entryDll&quot;: &quot;AcDream.Plugins.Smoke.dll&quot;, &quot;apiVersion&quot;: 1 }" />
</Target>
<Target
Name="CopySmokePluginToPublishOutput"
AfterTargets="Publish"
Condition="'$(IsCrossTargetingBuild)' != 'true'">
<PropertyGroup>
<_SmokePluginPublishSourceDir>$(MSBuildProjectDirectory)/../AcDream.Plugins.Smoke/bin/$(Configuration)/$(TargetFramework)</_SmokePluginPublishSourceDir>
<_SmokePluginPublishSourceDir Condition="'$(RuntimeIdentifier)' != ''">$(_SmokePluginPublishSourceDir)/$(RuntimeIdentifier)</_SmokePluginPublishSourceDir>
<_SmokePluginPublishDestDir>$(PublishDir)plugins/AcDream.Plugins.Smoke</_SmokePluginPublishDestDir>
</PropertyGroup>
<MakeDir Directories="$(_SmokePluginPublishDestDir)" />
<Copy
SourceFiles="$(_SmokePluginPublishSourceDir)/AcDream.Plugins.Smoke.dll"
DestinationFolder="$(_SmokePluginPublishDestDir)"
SkipUnchangedFiles="true" />
<WriteLinesToFile
File="$(_SmokePluginPublishDestDir)/plugin.json"
Overwrite="true"
Lines="{ &quot;id&quot;: &quot;acdream.smoke&quot;, &quot;displayName&quot;: &quot;Smoke Plugin&quot;, &quot;version&quot;: &quot;0.1.0&quot;, &quot;entryDll&quot;: &quot;AcDream.Plugins.Smoke.dll&quot;, &quot;apiVersion&quot;: 1 }" />
</Target>
<!-- MossTank ships the same way as the smoke plugin, plus its panel markup:
MossTankPlugin resolves mosstank.xml relative to its own assembly, so the
two files must land in the same plugin directory. -->
<ItemGroup>
<ProjectReference Include="..\AcDream.Plugins.MossTank\AcDream.Plugins.MossTank.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
</ProjectReference>
</ItemGroup>
<Target
Name="CopyMossTankPluginToBuildOutput"
AfterTargets="Build"
Condition="'$(IsCrossTargetingBuild)' != 'true'">
<PropertyGroup>
<_MossTankSourceDir>$(MSBuildProjectDirectory)/../AcDream.Plugins.MossTank/bin/$(Configuration)/$(TargetFramework)</_MossTankSourceDir>
<_MossTankSourceDir Condition="'$(RuntimeIdentifier)' != ''">$(_MossTankSourceDir)/$(RuntimeIdentifier)</_MossTankSourceDir>
<_MossTankDestDir>$(OutputPath)plugins/AcDream.Plugins.MossTank</_MossTankDestDir>
</PropertyGroup>
<MakeDir Directories="$(_MossTankDestDir)" />
<Copy
SourceFiles="$(_MossTankSourceDir)/AcDream.Plugins.MossTank.dll;$(_MossTankSourceDir)/mosstank.xml;$(_MossTankSourceDir)/mosstank-settings.xml"
DestinationFolder="$(_MossTankDestDir)"
SkipUnchangedFiles="true" />
<WriteLinesToFile
File="$(_MossTankDestDir)/plugin.json"
Overwrite="true"
Lines="{ &quot;id&quot;: &quot;acdream.mosstank&quot;, &quot;displayName&quot;: &quot;MossTank&quot;, &quot;version&quot;: &quot;0.1.0&quot;, &quot;entryDll&quot;: &quot;AcDream.Plugins.MossTank.dll&quot;, &quot;apiVersion&quot;: 1 }" />
</Target>
<Target
Name="CopyMossTankPluginToPublishOutput"
AfterTargets="Publish"
Condition="'$(IsCrossTargetingBuild)' != 'true'">
<PropertyGroup>
<_MossTankPublishSourceDir>$(MSBuildProjectDirectory)/../AcDream.Plugins.MossTank/bin/$(Configuration)/$(TargetFramework)</_MossTankPublishSourceDir>
<_MossTankPublishSourceDir Condition="'$(RuntimeIdentifier)' != ''">$(_MossTankPublishSourceDir)/$(RuntimeIdentifier)</_MossTankPublishSourceDir>
<_MossTankPublishDestDir>$(PublishDir)plugins/AcDream.Plugins.MossTank</_MossTankPublishDestDir>
</PropertyGroup>
<MakeDir Directories="$(_MossTankPublishDestDir)" />
<Copy
SourceFiles="$(_MossTankPublishSourceDir)/AcDream.Plugins.MossTank.dll;$(_MossTankPublishSourceDir)/mosstank.xml;$(_MossTankPublishSourceDir)/mosstank-settings.xml"
DestinationFolder="$(_MossTankPublishDestDir)"
SkipUnchangedFiles="true" />
<WriteLinesToFile
File="$(_MossTankPublishDestDir)/plugin.json"
Overwrite="true"
Lines="{ &quot;id&quot;: &quot;acdream.mosstank&quot;, &quot;displayName&quot;: &quot;MossTank&quot;, &quot;version&quot;: &quot;0.1.0&quot;, &quot;entryDll&quot;: &quot;AcDream.Plugins.MossTank.dll&quot;, &quot;apiVersion&quot;: 1 }" />
</Target>
</Project>