Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added 1.6/Assemblies/0Harmony.dll
Binary file not shown.
Binary file added 1.6/Assemblies/0Harmony.pdb
Binary file not shown.
Binary file modified 1.6/Assemblies/Better Work Tab.dll
Binary file not shown.
Binary file added 1.6/Assemblies/Better Work Tab.pdb
Binary file not shown.
2 changes: 2 additions & 0 deletions Languages/English/Keyed/RuleBuilder.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
<BWT_Priority_High_Desc>High priority. This pawn will be assigned before normal priority pawns.</BWT_Priority_High_Desc>
<BWT_Priority_Normal_Desc>Normal priority. Standard assignment.</BWT_Priority_Normal_Desc>
<BWT_Priority_Low_Desc>Low priority. This pawn will be assigned last.</BWT_Priority_Low_Desc>
<BWT_Priority_Extended>Priority ({0})</BWT_Priority_Extended>
<BWT_Priority_Extended_Desc>Extended priority {0}. Higher numbers are lower priority and run after smaller non-zero values.</BWT_Priority_Extended_Desc>

<!-- Condition categories -->
<BWT_Category_Skill>Skill-Based</BWT_Category_Skill>
Expand Down
161 changes: 161 additions & 0 deletions Source/API/PriorityApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
using System;
using Better_Work_Tab.Features.RaisedPriorityMaximum;

namespace Better_Work_Tab.API
{
/// <summary>
/// Immutable snapshot of Better Work Tab's current priority configuration.
/// Designed to be easy to consume over reflection so other mods do not need
/// a hard assembly reference just to read max-priority settings.
/// </summary>
public readonly struct PriorityApiSnapshot
{
public readonly int ApiVersion;
public readonly int MaxPriority;
public readonly int DefaultEnabledPriority;

public PriorityApiSnapshot(int apiVersion, int maxPriority, int defaultEnabledPriority)
{
ApiVersion = apiVersion;
MaxPriority = maxPriority;
DefaultEnabledPriority = defaultEnabledPriority;
}
}

/// <summary>
/// Public compatibility surface for Better Work Tab's extended manual priorities.
/// Other mods should prefer this API over hardcoding the vanilla max priority of 4.
///
/// Reflection integration:
/// - Assembly: "Better Work Tab"
/// - Type: "Better_Work_Tab.API.PriorityApi"
/// - Read methods such as <see cref="GetMaxPriority"/> or <see cref="GetSnapshot"/>
///
/// Example reflection flow for mods that do not want a hard dependency:
/// 1. Find the loaded assembly named "Better Work Tab".
/// 2. Resolve type "Better_Work_Tab.API.PriorityApi".
/// 3. Invoke static method "GetMaxPriority" or "GetSnapshot".
/// </summary>
public static class PriorityApi
{
/// <summary>
/// Stable API version for reflection-based callers.
/// Increment only when the public contract changes incompatibly.
/// </summary>
public const int ApiVersion = 1;

/// <summary>
/// Stable assembly name for reflection-based lookups.
/// </summary>
public const string AssemblyName = "Better Work Tab";

/// <summary>
/// Stable fully-qualified type name for reflection-based lookups.
/// </summary>
public const string TypeName = "Better_Work_Tab.API.PriorityApi";

/// <summary>
/// Returns the configured upper bound for manual priorities.
/// Guaranteed to return at least 1.
/// </summary>
public static int GetMaxPriority()
{
try
{
return MaxPriorityLogic.GetMaxPriority();
}
catch
{
return Math.Max(1, DefaultSettings.maxPriority);
}
}

/// <summary>
/// Returns the default priority used when enabling a work type outside manual-priority mode.
/// </summary>
public static int GetDefaultEnabledPriority()
{
try
{
return MaxPriorityLogic.GetDefaultEnabledPriority();
}
catch
{
return Clamp(3, 1, GetMaxPriority());
}
}

/// <summary>
/// Maps an extended stored priority back into RimWorld's vanilla-style 1..4 display buckets.
/// Returns 0 for disabled priorities.
/// </summary>
public static int MapPriorityToVanillaDisplay(int priority)
{
try
{
return MaxPriorityLogic.MapPriorityToVanillaDisplay(priority);
}
catch
{
if (priority <= 0)
{
return 0;
}

int maxPriority = GetMaxPriority();
if (maxPriority <= 1)
{
return 1;
}

float remapped = Spine.Utils.SpineUtils.Remap(priority, 1, maxPriority, 1, 4);
return Clamp((int)Math.Round(remapped), 1, 4);
}
}

/// <summary>
/// True when the priority value represents "disabled" for a work type.
/// </summary>
public static bool IsDisabledPriority(int priority)
{
return priority <= 0;
}

/// <summary>
/// Returns a small versioned snapshot for reflection-friendly integration.
/// This is the preferred call for mods that want one round-trip instead of multiple method calls.
/// </summary>
public static PriorityApiSnapshot GetSnapshot()
{
return new PriorityApiSnapshot(
ApiVersion,
GetMaxPriority(),
GetDefaultEnabledPriority());
}

/// <summary>
/// Safe reflection-friendly wrapper that never throws.
/// Returns false only if the API could not produce a valid snapshot.
/// </summary>
public static bool TryGetSnapshot(out PriorityApiSnapshot snapshot)
{
try
{
snapshot = GetSnapshot();
return true;
}
catch
{
snapshot = new PriorityApiSnapshot(ApiVersion, Math.Max(1, DefaultSettings.maxPriority), 3);
return false;
}
}

private static int Clamp(int value, int min, int max)
{
if (value < min) return min;
if (value > max) return max;
return value;
}
}
}
45 changes: 45 additions & 0 deletions Source/API/PriorityApi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Better Work Tab Priority API

This API exists so other mods can read Better Work Tab's extended priority settings without hardcoding the vanilla max priority of `4`.

## Goals

- No hard dependency required
- Stable reflection target
- Small read-only surface
- Versioned for future extension

## Reflection Contract

- Assembly name: `Better Work Tab`
- Type name: `Better_Work_Tab.API.PriorityApi`
- API version field: `ApiVersion`

## Public Methods

- `GetMaxPriority()`
- `GetDefaultEnabledPriority()`
- `MapPriorityToVanillaDisplay(int priority)`
- `IsDisabledPriority(int priority)`
- `GetSnapshot()`
- `TryGetSnapshot(out PriorityApiSnapshot snapshot)`

## Recommended Integration

If you do not want a compile-time reference to Better Work Tab:

1. Find the loaded assembly named `Better Work Tab`
2. Resolve `Better_Work_Tab.API.PriorityApi`
3. Prefer calling `GetSnapshot()` or `TryGetSnapshot(...)`

`GetSnapshot()` returns a versioned struct with:

- `ApiVersion`
- `MaxPriority`
- `DefaultEnabledPriority`

## Notes

- `0` means disabled
- Higher numbers are lower priority
- `MapPriorityToVanillaDisplay(...)` intentionally compresses extended priorities back into vanilla `1..4` display buckets for compatibility-oriented UI
22 changes: 22 additions & 0 deletions Source/Better Work Tab.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<Compile Include="Features\HighlightDrawer.cs" />
<Compile Include="Features\HighlightState.cs" />
<Compile Include="Features\Caching\BedCountCache.cs" />
<Compile Include="Features\Patches\MaxPriorityPatches.cs" />
<Compile Include="Features\Patches\Patch_PawnTable_RecacheIfDirty.cs" />
<Compile Include="Features\Patches\Patch_Pawn_WorkSettings_SetPriority.cs" />
<Compile Include="Features\Patches\Patch_WorkPriority_DoCell_Unified.cs" />
Expand Down Expand Up @@ -110,7 +111,27 @@
<Compile Include="Spine\DragDrop\Util\ListDragHelpers.cs" />
<Compile Include="Spine\DragDrop\ListDragSession.cs" />
<Compile Include="Spine\DragDrop\Util\ListDragVisuals.cs" />
<Compile Include="Spine\harmony\HarmonyBootstrap.cs" />
<Compile Include="Spine\harmony\HarmonyCompat.cs" />
<Compile Include="Spine\harmony\HarmonyHelper.cs" />
<Compile Include="Spine\harmony\HarmonyUtil.cs" />
<Compile Include="Spine\harmony\PatchRegistry.cs" />
<Compile Include="Spine\harmony\Transpilers\AdvancedExtensions.cs" />
<Compile Include="Spine\harmony\Transpilers\CartographerExtensions.cs" />
<Compile Include="Spine\harmony\Transpilers\ControlFlowExtensions.cs" />
<Compile Include="Spine\harmony\Transpilers\CooperativePatcher.cs" />
<Compile Include="Spine\harmony\Transpilers\FluentTranspiler.cs" />
<Compile Include="Spine\harmony\Transpilers\FluentTranspilerPatterns.cs" />
<Compile Include="Spine\harmony\Transpilers\FluentTranspiler_Features.cs" />
<Compile Include="Spine\harmony\Transpilers\IntentAPI.cs" />
<Compile Include="Spine\harmony\Transpilers\ShelteredPatterns.cs" />
<Compile Include="Spine\harmony\Transpilers\StackSentinel.cs" />
<Compile Include="Spine\harmony\Transpilers\TranspilerDebugger.cs" />
<Compile Include="Spine\harmony\Transpilers\TranspilerSafetyPolicy.cs" />
<Compile Include="Spine\harmony\Transpilers\TranspilerTestHarness.cs" />
<Compile Include="Spine\harmony\Transpilers\UnityPatterns.cs" />
<Compile Include="Spine\Profiling\SpineTiming.cs" />
<Compile Include="Spine\Utils\SpineUtils.cs" />
<Compile Include="Spine\UI\TextColorHelper.cs" />
<Compile Include="Spine\UI\SettingsFramework\SettingDefinition.cs" />
<Compile Include="Spine\UI\SettingsFramework\SettingType.cs" />
Expand Down Expand Up @@ -204,6 +225,7 @@
<Publicize Include="Assembly-CSharp:RimWorld.Pawn_WorkSettings.priorities" />
<Publicize Include="Assembly-CSharp" MemberPattern="^RimWorld\.FloatMenuOptionProvider_WorkGivers\..*|RimWorld\.PawnTable\..*" />
<Publicize Include="Assembly-CSharp:RimWorld.Pawn_WorkSettings.CacheWorkGiversInOrder" />
<Publicize Include="Assembly-CSharp:RimWorld.PawnColumnWorker_WorkPriority.HeaderClicked" />
</ItemGroup>
<ItemGroup>
<Folder Include="Util\" />
Expand Down
86 changes: 80 additions & 6 deletions Source/BetterWorkTabSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,11 @@ public enum ColorScheme { RimWorldDefault, Colorblind_Deuteranopia, Colorblind_P
new WorkAssignmentParameters("Always Assigns", 3, isNaturalAlwaysAssign: true),
}, resetBeforeApplying: true, isDefault: true),

new WorkAssignmentRuleset("Vanilla New Pawn", new List<WorkAssignmentParameters>()
{
new WorkAssignmentParameters("Top 6", 3, isTopXSkill: 6),
new WorkAssignmentParameters("Always Assigns", 3, isNaturalAlwaysAssign: true),
}, resetBeforeApplying: false, isDefault: true),
//new WorkAssignmentRuleset("Vanilla New Pawn", new List<WorkAssignmentParameters>()
//{
// new WorkAssignmentParameters("Top 6", 3, isTopXSkill: 6),
// new WorkAssignmentParameters("Always Assigns", 3, isNaturalAlwaysAssign: true),
//}, resetBeforeApplying: false, isDefault: true),

new WorkAssignmentRuleset("BWT Default", new List<WorkAssignmentParameters>()
{
Expand Down Expand Up @@ -239,6 +239,11 @@ public enum ColorScheme { RimWorldDefault, Colorblind_Deuteranopia, Colorblind_P
// UI mode settings for work tab visibility
public static BetterWorkTabSettings.ShowUIMode ShowUIMode_ShowSmallSkillNumbers = BetterWorkTabSettings.ShowUIMode.Unshifted;
public static BetterWorkTabSettings.ShowUIMode ShowUIMode_ShowPawnForSkillSquare = BetterWorkTabSettings.ShowUIMode.Shifted;

public static int maxPriority = 9;
public static int priorityColorPercentage_Green = 10;
public static int priorityColorPercentage_Yellow = 50;
public static int priorityColorPercentage_Tan = 75;
}

// Contains all configurable settings for Better Work Tab mod
Expand Down Expand Up @@ -416,11 +421,20 @@ public Color Color_SimilarWorktypeMouseOver
public bool mpSyncRulesets = true;
public enum MpConflictMode { PlayerPriority, HostPriority, AskPlayer }
public MpConflictMode mpConflictMode = MpConflictMode.PlayerPriority;

// Max Priority Int Settings
public const int MAX_PRIORITY_HARD_LIMIT = 99;

public int maxPriorityInt = DefaultSettings.maxPriority;
public int priorityColorPercentage_Green = 10;
public int priorityColorPercentage_Yellow = 50;
public int priorityColorPercentage_Tan = 75;

public bool mpShowOtherPlayersHover = DefaultSettings.mpShowOtherPlayersHover;
public bool mpAllowOthersToRequestLayout = DefaultSettings.mpAllowOthersToRequestLayout;
public bool mpAllowPresenceBroadcast = DefaultSettings.mpAllowPresenceBroadcast;
public bool mpShowLinkedIndicator = DefaultSettings.mpShowLinkedIndicator;

/// <summary>
/// Unique identifier for this BWT installation in multiplayer
/// Auto-generated but user-configurable
Expand Down Expand Up @@ -652,9 +666,69 @@ public override void ExposeData()
Scribe_Values.Look(ref cjkVerticalKerning, "cjkVerticalKerning", 0.75f);
Scribe_Values.Look(ref angledHeaderColor, "angledHeaderColor", DefaultSettings.Color_AngledHeaderText);
Scribe_Values.Look(ref autoEnableManualPriorities, "autoEnableManualPriorities", DefaultSettings.autoEnableManualPriorities);
Scribe_Values.Look(ref maxPriorityInt, "maxPriorityInt", DefaultSettings.maxPriority);
Scribe_Values.Look(ref priorityColorPercentage_Green, "priorityColorPercentage_Green", DefaultSettings.priorityColorPercentage_Green);
Scribe_Values.Look(ref priorityColorPercentage_Yellow , "priorityColorPercentage_Yellow", DefaultSettings.priorityColorPercentage_Yellow );
Scribe_Values.Look(ref priorityColorPercentage_Tan , "priorityColorPercentage_Tan", DefaultSettings.priorityColorPercentage_Tan );

if (hiddenWorktypes == null) hiddenWorktypes = new List<string>();

// Colors
Scribe_Values.Look(ref Color_CursorHighlight, "Color_CursorHighlight", DefaultSettings.Color_CursorHighlight);
Scribe_Values.Look(ref Color_FloatMenuHighlight, "Color_FloatMenuHighlight", DefaultSettings.Color_FloatMenuHighlight);
Scribe_Values.Look(ref Color_CustomMouseHighlight, "Color_CustomMouseHighlight", DefaultSettings.Color_CustomMouseHighlight);
Scribe_Values.Look(ref Color_CustomSimilarWorktypeHighlight, "Color_CustomSimilarWorktypeHighlight", DefaultSettings.Color_CustomSimilarWorktypeHighlight);
Scribe_Values.Look(ref Color_IncapableBecauseOfCapacities, "Color_IncapableBecauseOfCapacities", DefaultSettings.Color_IncapableBecauseOfCapacities);
Scribe_Values.Look(ref Color_BestPawnForSkillSquare, "Color_BestPawnForSkillSquare", DefaultSettings.Color_BestPawnForSkillSquare);
Scribe_Values.Look(ref Color_VeryLowSkill, "Color_VeryLowSkill", DefaultSettings.Color_VeryLowSkill);
Scribe_Values.Look(ref Color_LowSkill, "Color_LowSkill", DefaultSettings.Color_LowSkill);
Scribe_Values.Look(ref Color_GoodLowSkill, "Color_GoodLowSkill", DefaultSettings.Color_GoodLowSkill);
Scribe_Values.Look(ref Color_ExcellentSkill, "Color_ExcellentSkill", DefaultSettings.Color_ExcellentSkill);
Scribe_Values.Look(ref Color_RowHoverHighlight, "Color_RowHoverHighlight", DefaultSettings.Color_RowHoverHighlight);
Scribe_Values.Look(ref Color_ColumnHoverHighlight, "Color_ColumnHoverHighlight", DefaultSettings.Color_ColumnHoverHighlight);
Scribe_Values.Look(ref Color_SelectedPawnHighlight, "Color_SelectedPawnHighlight", DefaultSettings.Color_SelectedPawnHighlight);
Scribe_Values.Look(ref Color_HeaderText, "Color_HeaderText", DefaultSettings.Color_HeaderText);
Scribe_Values.Look(ref Color_DividerText, "Color_DividerText", DefaultSettings.Color_DividerText);
Scribe_Values.Look(ref Color_Borders, "Color_Borders", DefaultSettings.Color_Borders);

// UI modes
Scribe_Values.Look(ref ShowUIMode_ShowSmallSkillNumbers, "ShowUIMode_ShowSmallSkillNumbers", DefaultSettings.ShowUIMode_ShowSmallSkillNumbers);
Scribe_Values.Look(ref ShowUIMode_ShowPawnForSkillSquare, "ShowUIMode_ShowPawnForSkillSquare", DefaultSettings.ShowUIMode_ShowPawnForSkillSquare);
Scribe_Values.Look(ref hoverEffectScope, "hoverEffectScope", DefaultSettings.hoverEffectScope);

// Future behavior templates
Scribe_Values.Look(ref confirmRulesetApplication, "confirmRulesetApplication", DefaultSettings.confirmRulesetApplication);
Scribe_Values.Look(ref dragStartThreshold, "dragStartThreshold", DefaultSettings.dragStartThreshold);
Scribe_Values.Look(ref scrollSpeed, "scrollSpeed", DefaultSettings.scrollSpeed);
Scribe_Values.Look(ref showAutoAssignConfirmation, "showAutoAssignConfirmation", DefaultSettings.showAutoAssignConfirmation);
Scribe_Values.Look(ref resetWorkBeforeAutoAssign, "resetWorkBeforeAutoAssign", DefaultSettings.resetWorkBeforeAutoAssign);
Scribe_Values.Look(ref showAutoAssignVisualFeedback, "showAutoAssignVisualFeedback", DefaultSettings.showAutoAssignVisualFeedback);
Scribe_Values.Look(ref defaultAutoAssignRuleset, "defaultAutoAssignRuleset", "BWT Default");
Scribe_Values.Look(ref showWorkloadButtonFooter, "showWorkloadButtonFooter", DefaultSettings.showWorkloadButtonFooter);
Scribe_Values.Look(ref enableWorkloadSaving, "enableWorkloadSaving", DefaultSettings.enableWorkloadSaving);
Scribe_Values.Look(ref enableWorkloadLoading, "enableWorkloadLoading", DefaultSettings.enableWorkloadLoading);
Scribe_Values.Look(ref persistDividersInWorkloads, "persistDividersInWorkloads", DefaultSettings.persistDividersInWorkloads);
Scribe_Values.Look(ref alwaysShowConditionEditors, "alwaysShowConditionEditors", DefaultSettings.alwaysShowConditionEditors);
Scribe_Values.Look(ref cacheBedCounts, "cacheBedCounts", true);
Scribe_Values.Look(ref cacheSkillLevels, "cacheSkillLevels", true);
Scribe_Values.Look(ref cacheRowDescriptors, "cacheRowDescriptors", true);
Scribe_Values.Look(ref cacheIncapabilityChecks, "cacheIncapabilityChecks", true);
Scribe_Values.Look(ref useElementPooling, "useElementPooling", true);
Scribe_Values.Look(ref viewportCulling, "viewportCulling", true);
Scribe_Values.Look(ref enableProfiler, "enableProfiler", false);
Scribe_Values.Look(ref logDebugToFile, "logDebugToFile", false);
Scribe_Values.Look(ref mpSyncColumnOrder, "mpSyncColumnOrder", true);
Scribe_Values.Look(ref mpSyncWorkloads, "mpSyncWorkloads", true);
Scribe_Values.Look(ref mpSyncRulesets, "mpSyncRulesets", true);
Scribe_Values.Look(ref mpConflictMode, "mpConflictMode", MpConflictMode.PlayerPriority);
Scribe_Values.Look(ref rulesetViewMode, "rulesetViewMode", RulesetViewMode.Regular);

//Scribe_Values.Look(ref maxPriorityInt, "maxPriorityInt", 4);

// Divider settings
Scribe_Values.Look(ref dividerHeight, "dividerHeight", DefaultSettings.dividerHeight);

// Load rulesets from save file
Scribe_Collections.Look(ref SavedRulesets, "SavedRulesets", LookMode.Deep);

// Reinitialize rulesets after load (restores defaults if missing)
Expand Down
Loading