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 modified 1.6/Assemblies/RunAndGun.dll
Binary file not shown.
2 changes: 2 additions & 0 deletions Languages/English/Keyed/RunAndGun_Keys.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
<RG_MovementPenaltyHeavy_Description>The movement penalty while shooting for heavy weapons</RG_MovementPenaltyHeavy_Description>
<RG_MovementPenaltyLight_Title>Movement penalty for light weapons (%)</RG_MovementPenaltyLight_Title>
<RG_MovementPenaltyLight_Description>The movement penalty while shooting for light weapons</RG_MovementPenaltyLight_Description>
<RG_EnableRGForColonists_Title>Enable Run And Gun for Colonists</RG_EnableRGForColonists_Title>
<RG_EnableRGForColonists_Description>Enables Run And Gun for all colonist pawns by default.</RG_EnableRGForColonists_Description>
<RG_EnableRGForAI_Title>Enable Run And Gun for AI</RG_EnableRGForAI_Title>
<RG_EnableRGForAI_Description>Enables Run And Gun for all AI pawns. Beware, this makes them incredibly deadly.</RG_EnableRGForAI_Description>
<RG_EnableRGForFleeChance_Title>Flee and gun chance(%)</RG_EnableRGForFleeChance_Title>
Expand Down
20 changes: 20 additions & 0 deletions Source/RunAndGun/CanRunAndGunDebug.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using HarmonyLib;
using Verse;

#if DEBUG
namespace RunAndGun
{
[HarmonyPatch(typeof(Pawn), nameof(Pawn.GetInspectString))]
public static class CanRunAndGunDebug
{
public static void Postfix(Pawn __instance, ref string __result)
{
var comp = __instance.GetComp<CompRunAndGun>();
if (comp == null)
return;
__result += $"\nRunAndGun: {comp._isEnabled}, Disabled: {comp._disabled}";
}

}
}
#endif
24 changes: 7 additions & 17 deletions Source/RunAndGun/CompRunAndGun.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ private Pawn pawn
}
}

internal bool _isEnabled = false;
internal bool _disabled = false;
internal bool? _isEnabled;
internal bool? _disabled;
public bool isEnabled
{
get => _isEnabled && IsValid();
get => (_isEnabled??= pawn.IsColonist ? RunAndGun.settings.enabledByDefault : RunAndGun.settings.enableForAI) && IsValid();
set => _isEnabled = value;
}

Expand All @@ -38,7 +38,10 @@ public void RefreshDisabledState()

private bool IsValid()
{
if (_disabled)
if(_disabled == null)
RefreshDisabledState();

if (_disabled ?? true)
return false;

if (pawn == null)
Expand Down Expand Up @@ -70,19 +73,6 @@ public override string GetDescriptionPart()
return isEnabled.ToString();
}

public override void Initialize(CompProperties props)
{
base.Initialize(props);
Pawn pawn = (Pawn)(parent as Pawn);
bool enableRGForAI = RunAndGun.settings.enableForAI;
if (!pawn.IsColonist && enableRGForAI)
{
isEnabled = true;
}

RefreshDisabledState();
}

public override void PostExposeData()
{
base.PostExposeData();
Expand Down
2 changes: 1 addition & 1 deletion Source/RunAndGun/Harmony/Pawn_GetGizmos.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public static IEnumerable<Gizmo> Postfix(IEnumerable<Gizmo> __result, Pawn __ins
icon = ContentFinder<Texture2D>.Get(("UI/Buttons/enable_RG"), true),
isActive = () => data.isEnabled,
toggleAction = () => { data.isEnabled = !data.isEnabled; } ,
Disabled = data._disabled,
Disabled = data._disabled ?? true,
};
}
}
Expand Down
4 changes: 2 additions & 2 deletions Source/RunAndGun/RunAndGun.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<Configurations>Debug;Release</Configurations>
<OutputPath>..\..\1.6\Assemblies\</OutputPath>
<LangVersion>8</LangVersion>
<LangVersion>12</LangVersion>

</PropertyGroup>

Expand Down Expand Up @@ -57,7 +57,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.6.4543" GeneratePathProperty="true" />
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.6.4850" GeneratePathProperty="true" />

</ItemGroup>
</Project>
199 changes: 153 additions & 46 deletions Source/RunAndGun/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using RunAndGun.Utilities;
using UnityEngine;
using Verse;
Expand All @@ -17,6 +18,7 @@ public class Settings : ModSettings
{
// === Stored settings ===
public bool dialogCEShown = false;
public bool enabledByDefault = true;
public bool enableForAI = true;
public int enableForFleeChance = 100;
public int accuracyPenalty = 10;
Expand All @@ -27,18 +29,19 @@ public class Settings : ModSettings
public Dictionary<string, WeaponRecord> selectedWeapons = new Dictionary<string, WeaponRecord>();
public Dictionary<string, WeaponRecord> forbiddenWeapons = new Dictionary<string, WeaponRecord>();

public readonly bool DualWieldInstalled;
public readonly bool DualWieldInstalled = ModLister.AnyModActiveNoSuffix(["MemeGoddess.DualWield"]);
public const string CEIDs = "CETeam.CombatExtended";
public readonly bool CEInstalled = ModLister.AnyModActiveNoSuffix(CEIDs.Split(','));


private SettingsTab tab;
private QuickSearchWidget search = new QuickSearchWidget();


// === Cached state ===
public List<ThingDef> allWeapons;
private string[] tabNames = new[] { "Weapons", "Forbidden" };
private float maxWeightMelee, maxWeightRanged, maxWeightTotal;

public Settings()
{
DualWieldInstalled = ModsConfig.IsActive("MemeGoddess.DualWield") ||
ModsConfig.IsActive("MemeGoddess.DualWield_steam");
}
private Color MenuSectionBGBorderColor = new ColorInt(135, 135, 135).ToColor;

public void Initialize()
{
Expand All @@ -48,15 +51,15 @@ public void Initialize()
maxWeightRanged += 1;
maxWeightTotal = Math.Max(maxWeightMelee, maxWeightRanged);

bool combatExtendedLoaded = AssemblyExists("CombatExtended");
if (combatExtendedLoaded && !dialogCEShown)
switch (CEInstalled)
{
Find.WindowStack.Add(new Dialog_CE("RG_Dialog_CE_Title".Translate(), "RG_Dialog_CE_Description".Translate()));
dialogCEShown = true;
}
else if (!combatExtendedLoaded)
{
dialogCEShown = false;
case true when !dialogCEShown:
Find.WindowStack.Add(new Dialog_CE("RG_Dialog_CE_Title".Translate(), "RG_Dialog_CE_Description".Translate()));
dialogCEShown = true;
break;
case false:
dialogCEShown = false;
break;
}

if (selectedWeapons == null)
Expand All @@ -68,59 +71,99 @@ public void Initialize()
public void DoWindowContents(Rect rect)
{
Initialize();

var color = GUI.color;
var listing = new Listing_Standard();
listing.Begin(rect);

// === General Settings ===
listing.CheckboxLabeled("RG_EnableRGForColonists_Title".Translate(), ref enabledByDefault, "RG_EnableRGForColonists_Description".Translate());
listing.CheckboxLabeled("RG_EnableRGForAI_Title".Translate(), ref enableForAI, "RG_EnableRGForAI_Description".Translate());
var box = listing.GetRect((22f + Text.LineHeight + listing.verticalSpacing + listing.verticalSpacing) * 2);
if (enableForAI)
{
listing.Label("RG_EnableRGForFleeChance_Title".Translate() + ": " + enableForFleeChance + "%");
enableForFleeChance = (int)Widgets.HorizontalSlider(listing.GetRect(22f), enableForFleeChance, 0, 100, false, "");
var AISettings = new Listing_Standard();
box.SplitVerticallyWithMargin(out var aiListingBox, out box, 6f);
AISettings.Begin(aiListingBox);
AISettings.Label("RG_EnableRGForFleeChance_Title".Translate() + ": " + enableForFleeChance + "%");
enableForFleeChance = (int)Widgets.HorizontalSlider(AISettings.GetRect(22f), enableForFleeChance, 0, 100, false, "");

listing.Label("RG_AccuracyPenalty_Title".Translate() + ": " + accuracyPenalty + "%");
accuracyPenalty = (int)Widgets.HorizontalSlider(listing.GetRect(22f), accuracyPenalty, 0, 100, false, "");
AISettings.Label("RG_AccuracyPenalty_Title".Translate() + ": " + accuracyPenalty + "%");
accuracyPenalty = (int)Widgets.HorizontalSlider(AISettings.GetRect(22f), accuracyPenalty, 0, 100, false, "");
AISettings.End();
}

// === Movement Penalties ===
listing.Label("RG_MovementPenaltyHeavy_Title".Translate() + ": " + movementPenaltyHeavy + "%");
movementPenaltyHeavy = (int)Widgets.HorizontalSlider(listing.GetRect(22f), movementPenaltyHeavy, 0, 100, false, "");
var movementSettings = new Listing_Standard();
movementSettings.Begin(box);

listing.Label("RG_MovementPenaltyLight_Title".Translate() + ": " + movementPenaltyLight + "%");
movementPenaltyLight = (int)Widgets.HorizontalSlider(listing.GetRect(22f), movementPenaltyLight, 0, 100, false, "");
movementSettings.Label("RG_MovementPenaltyHeavy_Title".Translate() + ": " + movementPenaltyHeavy + "%");
movementPenaltyHeavy = (int)Widgets.HorizontalSlider(movementSettings.GetRect(22f), movementPenaltyHeavy, 0, 100, false, "");

movementSettings.Label("RG_MovementPenaltyLight_Title".Translate() + ": " + movementPenaltyLight + "%");
movementPenaltyLight = (int)Widgets.HorizontalSlider(movementSettings.GetRect(22f), movementPenaltyLight, 0, 100, false, "");
movementSettings.End();

listing.GapLine();

// === Tabs ===
listing.Label("RG_Tabs_Title".Translate());
if (Widgets.ButtonText(listing.GetRect(24f), tabsHandler))

//var tabs = new Listing_Standard();
var tabRect = listing.GetRect(Text.LineHeight);

var tabsList = new List<TabRecord>
{
List<FloatMenuOption> menu = new List<FloatMenuOption>();
foreach (var name in tabNames)
{
string local = name;
menu.Add(new FloatMenuOption(local, () => tabsHandler = local));
}
Find.WindowStack.Add(new FloatMenu(menu));
}
new("RG_tab1".Translate(), () => tab = SettingsTab.Heavy, () => tab == SettingsTab.Heavy),
new("RG_tab2".Translate(), () => tab = SettingsTab.Forbidden, () => tab == SettingsTab.Forbidden)

listing.GapLine();
};

DrawTabs(tabRect, tabsList);
listing.Gap(4f);

// === Filters and Custom UI ===
if (tabsHandler == tabNames[0])
float remainingHeight = rect.height - listing.CurHeight;
var tabViewBox = listing.GetRect(remainingHeight);
GUI.color = MenuSectionBGBorderColor;
Widgets.DrawBox(tabViewBox);
GUI.color = color;
var tabView = new Listing_Standard();
var contraction = 8f;
tabView.Begin(tabViewBox.ContractedBy(contraction));
switch (tab)
{
listing.Label("RG_WeightLimitFilter_Title".Translate() + $" ({weightLimitFilter:F1})");
weightLimitFilter = Widgets.HorizontalSlider(listing.GetRect(22f), weightLimitFilter, 0f, maxWeightTotal, false, "", "0", maxWeightTotal.ToString("F1"));
case SettingsTab.Heavy:
tabView.Label("RG_WeightLimitFilter_Title".Translate() + $" ({weightLimitFilter:F1})");
tabView.Gap(4f);
weightLimitFilter = Widgets.HorizontalSlider(tabView.GetRect(22f), weightLimitFilter, 0f, maxWeightTotal, false, "", "0", maxWeightTotal.ToString("F1"));

//DrawUtility.CustomDrawer_Filter(listing.GetRect(120f), weightLimitFilter, false, 0, maxWeightTotal, Color.yellow);
DrawUtility.CustomDrawer_MatchingWeapons_active(listing.GetRect(253f), ref selectedWeapons, allWeapons, weightLimitFilter, "RG_ConsideredLight".Translate(), "RG_ConsideredHeavy".Translate());
}
else if (tabsHandler == tabNames[1])
{
DrawUtility.CustomDrawer_MatchingWeapons_active(listing.GetRect(253f), ref forbiddenWeapons, allWeapons, null, "RG_Allow".Translate(), "RG_Forbid".Translate());
search.OnGUI(tabView.GetRect(Text.LineHeight));
tabView.Gap(4f);
tabViewBox.SplitHorizontally(tabView.CurHeight + contraction, out tabViewBox, out var heavyRect);
tabView.End();

DrawUtility.CustomDrawer_MatchingWeapons_active(heavyRect.ContractedBy(1f), ref selectedWeapons,
allWeapons.Where(weapon =>
search.filter.Matches(weapon.label) ||
search.filter.Matches(weapon.defName)
).ToList(),
weightLimitFilter, "RG_ConsideredLight".Translate(), "RG_ConsideredHeavy".Translate());
break;
case SettingsTab.Forbidden:
tabView.Gap(4f);
search.OnGUI(tabView.GetRect(Text.LineHeight));
tabView.Gap(4f);
tabViewBox.SplitHorizontally(tabView.CurHeight + contraction, out tabViewBox, out var forbiddenRect);
tabView.End();

DrawUtility.CustomDrawer_MatchingWeapons_active(forbiddenRect.ContractedBy(1f), ref forbiddenWeapons,
allWeapons.Where(weapon =>
search.filter.Matches(weapon.label) ||
search.filter.Matches(weapon.defName)
).ToList(),
null, "RG_Allow".Translate(), "RG_Forbid".Translate());
break;
}

listing.End();
}

Expand All @@ -143,7 +186,7 @@ public override void ExposeData()

if (forbiddenWeapons == null)
forbiddenWeapons = new Dictionary<string, WeaponRecord>();

base.ExposeData();
}

Expand All @@ -154,6 +197,70 @@ private bool AssemblyExists(string assemblyName)
return true;
return false;
}

//private void DoTabClick(SettingsTab selectedTab)
//{
// search = new QuickSearchWidget();

// tab = selectedTab == tab
// ? SettingsTab.None
// : selectedTab;
//}

private static Color SelectedColor = new Color(0.5f, 1f, 0.5f, 1f);
private void DrawTabs(Rect rect, List<TabRecord> tabs)
{
var buttons = tabs.Count;
var rects = SplitRectangle(rect, buttons, 4f);

var color = GUI.color;
for (var index = 0; index < rects.Length; index++)
{
var button = rects[index];
var tab = tabs[index];

if (tab.Selected)
GUI.color = SelectedColor;
if (Widgets.ButtonText(button, tab.label))
tab.clickedAction();
GUI.color = color;
}
}

private Rect[] SplitRectangle(Rect rect, int count, float margin)
{
var rects = new Rect[count];
var totalMargin = margin * (count - 1);
var usableWidth = rect.width - totalMargin;
var rectWidth = usableWidth / count;

for (var i = 0; i < count; i++)
{
var xPosition = rect.x + (i * (rectWidth + margin));
rects[i] = new Rect(xPosition, rect.y, rectWidth, rect.height);
}

return rects;
}
}

public enum SettingsTab
{
Heavy,
Forbidden
}

public static class SettingsExtensions
{
private static Color SelectedColor = new Color(0.5f, 1f, 0.5f, 1f);
public static float Button(this Listing_Standard listing, string label, bool active, Action action)
{
var original = GUI.color;
GUI.color = active ? SelectedColor : original;
if (listing.ButtonText(label))
action.Invoke();
GUI.color = original;
return 30f + listing.verticalSpacing;
}
}
}
Loading