Faction Territory Display Update

- lots of minor code cleanup
- faction territory test patch added (commented out for now since using Ceasars Legion to test instead now)
- added the TextureCache class to handle faction territory asset loading
- added the ModExtension_FactionTerritory class for linking logic to xml for a given FactionDef
- added the WorldFeatureTextMesh_FactionLabel class to display the factions' name for it's covered territory
- added the WorldLayer_FactionTerritory class for handling new faction territory bounds and drawing on the world (contains public hooks for updating the territory bounds)
- more tiny syntax changes throughout
This commit is contained in:
Steve
2025-03-07 15:24:39 -05:00
parent 1aaff8c8c7
commit 3d895dbd37
32 changed files with 920 additions and 589 deletions
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -113,7 +113,7 @@
<defName>MinePocket_Mine</defName>
<label>mine</label>
<thingClass>Building_TrapExplosive</thingClass>
<designationCategory Inherit="False"></designationCategory> <!-- important to not be able to be built normally-->
<designationCategory Inherit="False"/> <!-- important to not be able to be built normally-->
<description>placeholder.</description>
<graphicData>
<texPath>Weapons/Mines/FCP_Frag_Mine</texPath>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Patch>
<!-- THIS IS FOR TESTING SOME NEW CODE -->
<!-- <Operation Class="PatchOperationAddModExtension">
<xpath>Defs/FactionDef[defName="Empire"]</xpath>
<value>
<li Class="FCP.Core.ModExtension_FactionTerritory">
<territoryColor>(0.95, 0.95, 0.85, 0.5)</territoryColor>
<territoryBorderColor>(1, 1, 0.8, 0.75)</territoryBorderColor>
<initialTerritoryRadius>10</initialTerritoryRadius>
<renderTerritoryOverWater>false</renderTerritoryOverWater>
</li>
</value>
</Operation> -->
</Patch>
+5
View File
@@ -1,5 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<!--
PUT THIS IN A LOADFOLDER OR WRAP IN A PATCHOPERTIONFINDMOD,
OTHERWISE YOU WILL GET ERRORS IF MUSICEXPANDED IS NOT LOADED!
-->
<!-- Battle Cues -->
<Operation Class="PatchOperationAddModExtension">
<xpath>/Defs/IncidentDef[
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

+1
View File
@@ -2,5 +2,6 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
<mapping directory="$PROJECT_DIR$/../FCP-UnityAssets" vcs="Git" />
</component>
</project>
@@ -0,0 +1,14 @@
using UnityEngine;
namespace FCP.Core
{
[StaticConstructorOnStartup]
public static class TextureCache
{
public static readonly Texture2D FactionTerritoryTex =
ContentFinder<Texture2D>.Get("WorldOverlays/FactionTerritoryTex");
public static readonly Texture2D FactionTerritoryBorderTex =
ContentFinder<Texture2D>.Get("WorldOverlays/FactionTerritoryBorderTex");
}
}
@@ -5,9 +5,12 @@ namespace FCP.Core;
public class LegendaryEffectGameTracker : GameComponent
{
public LegendaryEffectGameTracker(Game game) { }
public static Dictionary<Thing, List<LegendaryEffectDef>> EffectsDict = new Dictionary<Thing, List<LegendaryEffectDef>>();
public static Dictionary<Thing, List<LegendaryEffectDef>> EffectsDict = new();
public LegendaryEffectGameTracker(Game game)
{
}
public override void ExposeData()
{
@@ -41,17 +44,17 @@ public class LegendaryEffectGameTracker : GameComponent
}
if (!EffectsDict.TryGetValue(thing, out var effects))
effects = new List<LegendaryEffectDef>();
effects = [];
effects.Add(effect);
EffectsDict.SetOrAdd(thing, effects);
}
public static void ClearEffectsFor(Thing thing)
private static void ClearEffectsFor(Thing thing)
{
if (!EffectsDict.TryGetValue(thing, out var effects))
effects = new List<LegendaryEffectDef>();
effects = [];
effects.Clear();
@@ -80,11 +83,9 @@ public class LegendaryEffectGameTracker : GameComponent
public static string GetEffectDescription(Thing thing)
{
if (!HasEffect(thing))
{
return null;
}
StringBuilder body = new StringBuilder();
StringBuilder body = new();
foreach (LegendaryEffectDef effect in EffectsDict[thing])
{
body.AppendLine($" - {effect.LabelCap} - {effect.description}");
@@ -106,6 +107,7 @@ public class LegendaryEffectGameTracker : GameComponent
List<LegendaryEffectDef> AllDefs = DefDatabase<LegendaryEffectDef>.AllDefsListForReading;
List<FloatMenuOption> options = [];
IEnumerable<LegendaryEffectDef> validEffects;
if (thing.def.IsApparel)
{
validEffects = AllDefs.Where(def => def.IsForApparel);
@@ -155,13 +157,17 @@ public class LegendaryEffectGameTracker : GameComponent
{
foreach (Apparel apparel in pawn.apparel.UnlockedApparel)
{
output.AddRange(GetEffectsFor(apparel).Select(eff => new ThingAndEffect { thing = apparel, effect = eff }));
output.AddRange(GetEffectsFor(apparel)
.Select(eff =>
new ThingAndEffect { thing = apparel, effect = eff }));
}
}
if (pawn.equipment != null && pawn.equipment.Primary != null)
if (pawn.equipment is { Primary: not null })
{
output.AddRange(GetEffectsFor(pawn.equipment.Primary).Select(eff => new ThingAndEffect { thing = pawn.equipment.Primary, effect = eff }));
output.AddRange(GetEffectsFor(pawn.equipment.Primary)
.Select(eff =>
new ThingAndEffect { thing = pawn.equipment.Primary, effect = eff }));
}
return output;
@@ -178,4 +184,4 @@ public class LegendaryEffectGameTracker : GameComponent
return modifier;
}
}
}
@@ -5,18 +5,17 @@ namespace FCP.Core;
[UsedImplicitly]
public class UniqueCharactersTracker : WorldComponent
{
public static UniqueCharactersTracker Instance { get; private set; }
private List<UniqueCharacter> characters = new List<UniqueCharacter>();
private List<UniqueCharacter> characters = [];
public UniqueCharactersTracker(World world) : base(world)
{
Instance = this;
}
/// <summary>
/// Check for a UniqueCharacter entry in the tracker and if the entry has a non destroyed/discarded pawn.
/// Check for a UniqueCharacter entry in the tracker and if the entry has a non-destroyed/discarded pawn.
/// </summary>
public bool CharacterPawnExists(CharacterDef charDef)
{
@@ -34,7 +33,7 @@ public class UniqueCharactersTracker : WorldComponent
}
/// <summary>
/// Try find a matching UniqueCharacter for a given pawn
/// Try to find a matching UniqueCharacter for a given pawn
/// </summary>
public bool TryGetPawnCharacter(Pawn pawn, out UniqueCharacter character)
{
@@ -88,10 +87,9 @@ public class UniqueCharactersTracker : WorldComponent
Instance = this;
}
public override void ExposeData()
{
Scribe_Collections.Look(ref characters, "character", lookMode: LookMode.Deep, saveDestroyedThings: true);
}
}
@@ -1,18 +1,21 @@
using Verse.AI.Group;
namespace FCP.Core;
public class DeathActionWorker_MechDetonatorExplosion : DeathActionWorker
namespace FCP.Core
{
public override RulePackDef DeathRules => RulePackDefOf.Transition_DiedExplosive;
public override bool DangerousInMelee => true;
public override void PawnDied(Corpse corpse, Lord prevLord)
public class DeathActionWorker_MechDetonatorExplosion : DeathActionWorker
{
if (corpse.InnerPawn.TryGetComp<CompMechDetonator>(out CompMechDetonator comp))
public override RulePackDef DeathRules => RulePackDefOf.Transition_DiedExplosive;
public override bool DangerousInMelee => true;
public override void PawnDied(Corpse corpse, Lord prevLord)
{
GenExplosion.DoExplosion(radius: comp.Props.radius, damAmount: comp.Props.damage, center: corpse.Position, map: corpse.Map, damType: DamageDefOf.Flame, instigator: corpse.InnerPawn);
if (corpse.InnerPawn.TryGetComp(out CompMechDetonator comp))
{
GenExplosion.DoExplosion(radius: comp.Props.radius, damAmount:
comp.Props.damage, center: corpse.Position, map: corpse.Map,
damType: DamageDefOf.Flame, instigator: corpse.InnerPawn);
}
}
}
}
@@ -1,46 +1,47 @@
using System.Diagnostics;
using System.Threading;
namespace FCP.Core;
/// <summary>
/// Utility class for running somewhat accurate-ish... maybe.. benchmarks
/// </summary>
public static class FCPBenchmark
namespace FCP.Core
{
/// <summary>
/// Profile a given action.
/// Utility class for running somewhat accurate-ish... maybe.. benchmarks
/// </summary>
/// <param name="iterations">Number of loops</param>
/// <param name="func">Function to be ran</param>
/// <param name="average">The averate time taken over all functions in ms (result / iterations)</param>
/// <returns>Total Milliseconds over all loops</returns>
public static double Profile(int iterations, Action func, out double average) {
//Run at highest priority to minimize fluctuations caused by other processes/threads
ProcessPriorityClass originalPrioClass = Process.GetCurrentProcess().PriorityClass;
ThreadPriority originalPrio = Thread.CurrentThread.Priority;
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
public static class FCPBenchmark
{
/// <summary>
/// Profile a given action.
/// </summary>
/// <param name="iterations">Number of loops</param>
/// <param name="func">Function to be ran</param>
/// <param name="average">The averate time taken over all functions in ms (result / iterations)</param>
/// <returns>Total Milliseconds over all loops</returns>
public static double Profile(int iterations, Action func, out double average) {
//Run at highest priority to minimize fluctuations caused by other processes/threads
ProcessPriorityClass originalPrioClass = Process.GetCurrentProcess().PriorityClass;
ThreadPriority originalPrio = Thread.CurrentThread.Priority;
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
// warm up
func();
Stopwatch watch = new ();
// clean up
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
watch.Start();
for (int i = 0; i < iterations; i++) {
// warm up
func();
Stopwatch watch = new ();
// clean up
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
watch.Start();
for (int i = 0; i < iterations; i++) {
func();
}
watch.Stop();
Process.GetCurrentProcess().PriorityClass = originalPrioClass;
Thread.CurrentThread.Priority = originalPrio;
average = watch.ElapsedMilliseconds / (float)iterations;
return watch.ElapsedMilliseconds;
}
watch.Stop();
Process.GetCurrentProcess().PriorityClass = originalPrioClass;
Thread.CurrentThread.Priority = originalPrio;
average = watch.ElapsedMilliseconds / (float)iterations;
return watch.ElapsedMilliseconds;
}
}
@@ -2,9 +2,9 @@
public static class FCPLog
{
private const string ErrorPrefix = "<color=#7F66FFFF>[FCP Core] </color>";
private const string WarnPrefix = "<color=#B266FFFF>[FCP Core] </color>";
private const string MsgPrefix = "<color=#66ff7fFF>[FCP Core] </color>";
private const string ErrorPrefix = "<color=#7F66FFFF>[FCP Core/Tools] </color>";
private const string WarnPrefix = "<color=#B266FFFF>[FCP Core/Tools] </color>";
private const string MsgPrefix = "<color=#66ff7fFF>[FCP Core/Tools] </color>";
public static void Error(string msg)
{
@@ -6,6 +6,4 @@ public class ModExtension_BiomeFeatureRequirements : DefModExtension
public bool requireCoast = false;
public bool requireCaves = false;
public bool requireHills = false;
}
@@ -0,0 +1,13 @@
using UnityEngine;
namespace FCP.Core
{
public class ModExtension_FactionTerritory : DefModExtension
{
public Color territoryColor = new();
public Color territoryBorderColor = new();
public Color factionLabelColor = new();
public int initialTerritoryRadius = 10;
public bool renderTerritoryOverWater = false;
}
}
@@ -6,7 +6,6 @@ namespace FCP.Core;
public class FCPCoreMod : Mod
{
public static FCPCoreMod mod;
public static FCP_Settings Settings;
public FCPCoreMod(ModContentPack content) : base(content)
+28 -28
View File
@@ -17,57 +17,57 @@ public static class Patches
static Patches()
{
Harmony harmony = new("FCP.Core.Patches"); // PatchesUwU ~ Steve
// Biome Feature Requirements
harmony.Patch(original: AccessTools.Method(typeof(WildAnimalSpawner), "CommonalityOfAnimalNow"),
postfix: new HarmonyMethod(typeof(Patches), nameof(WildAnimalSpawnerCommonalityOfAnimalNow_Postfix)));
// Non Slaves in Traders
harmony.Patch(original: AccessTools.Method(typeof(TraderCaravanUtility), "GetTraderCaravanRole"),
postfix: new HarmonyMethod(typeof(Patches), nameof(TraderCaravanUtilityGetTraderCaravanRole_Postfix)));
harmony.Patch(original: AccessTools.Method(typeof(Pawn_GuestTracker), "RandomizeJoinStatus"),
postfix: new HarmonyMethod(typeof(Patches), nameof(Pawn_GuestTrackerRandomizeJoinStatus_Postfix)));
harmony.Patch(original: AccessTools.Method(typeof(Pawn), "PreTraded"),
postfix: new HarmonyMethod(typeof(Patches), nameof(PawnPreTraded_Postfix)));
// Flying Pawns
harmony.Patch(original: AccessTools.Method(typeof(DamageWorker_AddInjury), "ApplyDamageToPart"),
prefix: new HarmonyMethod(typeof(Patches), nameof(DamageWorker_AddInjuryApplyDamageToPart_Prefix)));
harmony.Patch(original: AccessTools.Method(typeof(JobGiver_AIDefendPawn), "FindAttackTarget"),
postfix: new HarmonyMethod(typeof(Patches), nameof(JobGiver_AIDefendPawnFindAttackTarget_Postfix)));
harmony.Patch(original: AccessTools.Method(typeof(Pawn_JobTracker), "CleanupCurrentJob"),
prefix: new HarmonyMethod(typeof(Patches), nameof(Pawn_JobTrackerCleanupCurrentJob_Prefix)));
harmony.Patch(original: AccessTools.Method(typeof(Verb_MeleeAttack), "GetDodgeChance"),
postfix: new HarmonyMethod(typeof(Patches), nameof(Verb_MeleeAttackGetDodgeChance_Postfix)));
harmony.Patch(original: AccessTools.Method(typeof(Pawn_JobTracker), nameof(Pawn_JobTracker.StartJob)),
postfix: new HarmonyMethod(typeof(Patches), nameof(Pawn_JobTrackerStartJob_Postfix)));
harmony.Patch(original: AccessTools.PropertyGetter(typeof(ShotReport), nameof(ShotReport.AimOnTargetChance_StandardTarget)),
postfix: new HarmonyMethod(typeof(Patches), nameof(ShotReportAimOnTargetChance_StandardTarget_Postfix)));
harmony.Patch(original: AccessTools.Method(typeof(StatExtension), nameof(StatExtension.GetStatValue)),
postfix: new HarmonyMethod(typeof(Patches), nameof(StatExtensionGetStatValue_Postfix)));
// Faction Fixed Ideology
harmony.Patch(original: AccessTools.Method(typeof(IdeoGenerator), "MakeFixedIdeo"),
postfix: new HarmonyMethod(typeof(Patches), nameof(IdeoGeneratorMakeFixedIdeo_Postfix)));
harmony.Patch(original: AccessTools.Method(typeof(IdeoFoundation), "RandomizeIcon"),
prefix: new HarmonyMethod(typeof(Patches), nameof(IdeoFoundationRandomizeIcon_Prefix)));
harmony.Patch(original: AccessTools.Method(typeof(IdeoFoundation), "InitPrecepts"),
postfix: new HarmonyMethod(typeof(Patches), nameof(IdeoFoundationInitPrecepts_Postfix)));
// Banned Arrival Modes
harmony.Patch(original: AccessTools.Method(typeof(PawnsArrivalModeWorker), "CanUseWith"),
postfix: new HarmonyMethod(typeof(Patches), nameof(PawnsArrivalModeWorkerCanUseWith_Postfix)));
// Hidden Faction Traders
harmony.Patch(
original: typeof(IncidentWorker_CaravanMeeting).GetNestedTypes(AccessTools.all)
@@ -75,52 +75,52 @@ public static class Patches
.First(mi => mi.ReturnType == typeof(bool) &&
mi.GetParameters().ContainsAny(pi => pi.ParameterType == typeof(Faction))),
transpiler: new HarmonyMethod(typeof(Patches), nameof(IncidentWorker_CaravanMeetingTryFindFaction_Linq_Transpiler)));
harmony.Patch(original: AccessTools.Method(typeof(IncidentWorker_NeutralGroup), "FactionCanBeGroupSource"),
transpiler: new HarmonyMethod(typeof(Patches), nameof(IncidentWorker_NeutralGroup_FactionCanBeGroupSource_Transpiler)));
// Max Title for Permits
harmony.Patch(original: AccessTools.Method(typeof(PermitsCardUtility), "DoLeftRect"),
transpiler: new HarmonyMethod(typeof(Patches), nameof(PermitsCardUtility_LeftRect_Transpiler)));
harmony.Patch(original: AccessTools.Method(typeof(RoyalTitlePermitDef), "AvailableForPawn"),
postfix: new HarmonyMethod(typeof(Patches), nameof(RoyalTitlePermitDef_AvailableForPawn_Postfix)));
harmony.Patch(original: AccessTools.Method(typeof(RoyalTitleAwardWorker), "DoAward"),
postfix: new HarmonyMethod(typeof(Patches), nameof(RoyalTitleAwardWorker_DoAward_Postfix)));
harmony.Patch(original: AccessTools.Method(typeof(RoyalTitleAwardWorker_Instant), "DoAward"),
postfix: new HarmonyMethod(typeof(Patches), nameof(RoyalTitleAwardWorker_DoAward_Postfix)));
// Forced TraderKindDef for PawnGroupMaker
harmony.Patch(original: AccessTools.Method(typeof(PawnGroupKindWorker_Trader), "GeneratePawns", parameters: [typeof(PawnGroupMakerParms), typeof(PawnGroupMaker), typeof(List<Pawn>), typeof(bool)]),
prefix: new HarmonyMethod(typeof(Patches), nameof(PawnGroupKindWorker_Trader_GeneratePawns_Prefix)));
// Faction Permanent Hostility
harmony.Patch(original: AccessTools.Method(typeof(GoodwillSituationWorker_PermanentEnemy), "ArePermanentEnemies"),
postfix: new HarmonyMethod(typeof(Patches), nameof(GoodwillSituationWorker_PermanentEnemy_ArePermanentEnemies_Postfix)));
harmony.Patch(original: AccessTools.Method(typeof(Faction), "CanChangeGoodwillFor"),
postfix: new HarmonyMethod(typeof(Patches), nameof(Faction_CanChangeGoodwillFor_Postfix)));
harmony.Patch(original: AccessTools.Method(typeof(FactionDef), "PermanentlyHostileTo"),
postfix: new HarmonyMethod(typeof(Patches), nameof(FactionDef_PermanentlyHostileTo_Postfix)));
harmony.Patch(original: typeof(Faction).GetDeclaredMethods().First(mi => mi.Name.Contains("GetInitialGoodwill")),
prefix: new HarmonyMethod(typeof(Patches), nameof(Faction_TryMakeInitialRelationsWith_GetInitialGoodwill_Prefix)));
// Unique Characters
harmony.Patch(original: AccessTools.Method(typeof(Faction), nameof(Faction.TryGenerateNewLeader)),
prefix: new HarmonyMethod(typeof(Patches), nameof(Faction_TryGenerateNewLeader_Prefix)));
// Weapon Sprite Adjustment
harmony.Patch(original: AccessTools.Method(typeof(PawnRenderUtility), nameof(PawnRenderUtility.DrawEquipmentAndApparelExtras)),
prefix: new HarmonyMethod(typeof(Patches), nameof(WeaponDrawPosPatch)));
//Rarity Label
harmony.Patch(original: AccessTools.Method(typeof(InspectPaneUtility), nameof(InspectPaneUtility.AdjustedLabelFor)),
postfix: new HarmonyMethod(typeof(Patches), nameof(RarityLabelPatch)));
//Pick Up
harmony.Patch(original: AccessTools.Method(typeof(FloatMenuMakerMap), "AddHumanlikeOrders"),
postfix: new HarmonyMethod(typeof(Patches), nameof(AddHumanLikeOrders_PickUp)));
@@ -54,9 +54,10 @@ public static class DamageWorker_Patch
{
foreach (
Apparel apparel in pawn
.apparel.WornApparel.Where(app => app.TryGetQuality(out QualityCategory qual) && qual == QualityCategory.Legendary)
.Where(LegendaryEffectGameTracker.HasEffect)
)
.apparel.WornApparel
.Where(app => app
.TryGetQuality(out QualityCategory qual) && qual == QualityCategory.Legendary)
.Where(LegendaryEffectGameTracker.HasEffect))
{
foreach (LegendaryEffectDef effectDef in LegendaryEffectGameTracker.EffectsDict[apparel])
{
@@ -65,4 +66,4 @@ public static class DamageWorker_Patch
}
}
}
}
}
@@ -1,8 +1,10 @@
namespace FCP.Core.Hediffs;
public class HediffCompProperties_CripplePart : HediffCompProperties
namespace FCP.Core.Hediffs
{
public DamageDef damageDef;
public HediffCompProperties_CripplePart() => compClass = typeof(HediffComp_CripplePart);
}
public class HediffCompProperties_CripplePart : HediffCompProperties
{
public DamageDef damageDef;
public HediffCompProperties_CripplePart() =>
compClass = typeof(HediffComp_CripplePart);
}
}

Some files were not shown because too many files have changed in this diff Show More