Skip to content

Commit

Permalink
Merge pull request #30 from jecrell/dev
Browse files Browse the repository at this point in the history
Added PawnKindGeneExtensions; Organized JecsTools folders
  • Loading branch information
jecrell committed Oct 28, 2022
2 parents 57dbd4f + 1b9a9ca commit 85f7828
Show file tree
Hide file tree
Showing 55 changed files with 959 additions and 753 deletions.
Binary file modified 1.4/Assemblies/0JecsTools.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/AbilityUser.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/AbilityUserAI.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompActivatableEffect.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompAnimated.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompBalloon.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompBigBox.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompDeflector.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompDelayedSpawner.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompExtraSounds.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompInstalledPart.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompLumbering.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompOverlays.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompOversizedWeapon.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompSlotLoadable.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/CompToggleDef.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/PawnShields.dll
Binary file not shown.
Binary file modified 1.4/Assemblies/ThinkNodes.dll
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using RimWorld;
using UnityEngine;
using Verse;

namespace JecsTools;

public static partial class HarmonyPatches
{
public static void HarmonyPatches_ApparelExtension(Harmony harmony, Type type)
{
//Checks apparel that uses the ApparelExtension
harmony.Patch(AccessTools.Method(typeof(ApparelUtility), nameof(ApparelUtility.CanWearTogether)),
postfix: new HarmonyMethod(type, nameof(Post_CanWearTogether)));

//Handles cases where gendered apparel swaps out for individual genders.
harmony.Patch(AccessTools.Method(typeof(PawnApparelGenerator), nameof(PawnApparelGenerator.GenerateStartingApparelFor)),
postfix: new HarmonyMethod(type, nameof(GenerateStartingApparelFor_PostFix)));
}


//PawnApparelGenerator
public static void GenerateStartingApparelFor_PostFix(Pawn pawn)
{
var allWornApparel = pawn.apparel?.WornApparel;
if (allWornApparel.NullOrEmpty())
return;
List<(Apparel, Apparel)> swapEntries = null;
foreach (var wornApparel in allWornApparel)
{
if (wornApparel.def?.GetApparelExtension()?.swapCondition is SwapCondition sc &&
sc.swapWhenGender is Gender gen &&
gen != Gender.None && gen == pawn.gender)
{
var swapApparel = (Apparel)ThingMaker.MakeThing(sc.swapTo, wornApparel.Stuff);
// Avoid modifying WornApparel during its enumeration by doing the swaps afterwards.
swapEntries ??= new List<(Apparel worn, Apparel swap)>();
swapEntries.Add((wornApparel, swapApparel));
}
}
if (swapEntries != null)
{
foreach (var (wornApparel, swapApparel) in swapEntries)
{
PawnGenerator.PostProcessGeneratedGear(swapApparel, pawn);
if (ApparelUtility.HasPartsToWear(pawn, swapApparel.def))
{
pawn.apparel.Wear(swapApparel, false);
DebugMessage($"apparel generation for {pawn}: swapped from {wornApparel} to {swapApparel}");
}
wornApparel.Destroy();
DebugMessage($"apparel generation for {pawn}: destroyed old {wornApparel}");
}
}
}

/// <summary>
/// Using the new ApparelExtension, we can have a string based apparel check.
/// </summary>
public static void Post_CanWearTogether(ThingDef A, ThingDef B, BodyDef body, ref bool __result)
{
static HashSet<string> GetCoverage(ThingDef thingDef)
{
var coverage = thingDef.GetApparelExtension()?.Coverage;
return coverage == null || coverage.Count == 0 ? null : coverage;
}

if (A == null || B == null || body == null || __result == true)
return;
var coverageA = GetCoverage(A);
var coverageB = GetCoverage(B);
if (coverageA != null && coverageB != null)
{
foreach (var coverageItem in coverageB)
{
if (coverageA.Contains(coverageItem))
{
__result = false;
break;
}
}
}
else if ((coverageA != null && coverageB == null) || (coverageA == null && coverageB != null))
{
__result = true;
}
}
}
27 changes: 19 additions & 8 deletions Source/AllModdingComponents/JecsTools/Backstories/BackstoryDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ namespace JecsTools
//Link -> https://github.com/RimWorld-CCL-Reborn/AlienRaces/blob/94bf6b6d7a91e9587bdc40e8a231b18515cb6bb7/Source/AlienRace/AlienRace/BackstoryDef.cs
public class BackstoryDef : RimWorld.BackstoryDef
{

public static HashSet<BackstoryDef> checkBodyType = new HashSet<BackstoryDef>();

public List<ChancedTraitEntry> forcedTraitsChance = new List<ChancedTraitEntry>();
Expand All @@ -27,21 +26,33 @@ public class BackstoryDef : RimWorld.BackstoryDef
public IntRange chronoAgeRange;
public List<ThingDefCountRangeClass> forcedItems = new List<ThingDefCountRangeClass>();

public bool CommonalityApproved(Gender g) => Rand.Range(min: 0, max: 100) < (g == Gender.Female ? this.femaleCommonality : this.maleCommonality);
public bool CommonalityApproved(Gender g) => Rand.Range(min: 0, max: 100) <
(g == Gender.Female
? this.femaleCommonality
: this.maleCommonality);

public bool Approved(Pawn p) => this.CommonalityApproved(p.gender) &&
(this.bioAgeRange == default || (this.bioAgeRange.min < p.ageTracker.AgeBiologicalYears && p.ageTracker.AgeBiologicalYears < this.bioAgeRange.max)) &&
(this.chronoAgeRange == default || (this.chronoAgeRange.min < p.ageTracker.AgeChronologicalYears && p.ageTracker.AgeChronologicalYears < this.chronoAgeRange.max));
(this.bioAgeRange == default ||
(this.bioAgeRange.min < p.ageTracker.AgeBiologicalYears &&
p.ageTracker.AgeBiologicalYears < this.bioAgeRange.max)) &&
(this.chronoAgeRange == default ||
(this.chronoAgeRange.min < p.ageTracker.AgeChronologicalYears &&
p.ageTracker.AgeChronologicalYears < this.chronoAgeRange.max));

public override void ResolveReferences()
{
this.identifier = this.defName;
base.ResolveReferences();

this.forcedTraits = (this.forcedTraits ??= new List<BackstoryTrait>()).
Concat(this.forcedTraitsChance.Where(predicate: trait => Rand.Range(min: 0, max: 100) < trait.chance).ToList().ConvertAll(converter: trait => new BackstoryTrait { def = trait.defName, degree = trait.degree })).ToList();
this.disallowedTraits = (this.disallowedTraits ??= new List<BackstoryTrait>()).
Concat(this.disallowedTraitsChance.Where(predicate: trait => Rand.Range(min: 0, max: 100) < trait.chance).ToList().ConvertAll(converter: trait => new BackstoryTrait { def = trait.defName, degree = trait.degree })).ToList();
this.forcedTraits = (this.forcedTraits ??= new List<BackstoryTrait>()).Concat(this.forcedTraitsChance
.Where(predicate: trait => Rand.Range(min: 0, max: 100) < trait.chance).ToList()
.ConvertAll(converter: trait => new BackstoryTrait { def = trait.defName, degree = trait.degree }))
.ToList();
this.disallowedTraits = (this.disallowedTraits ??= new List<BackstoryTrait>()).Concat(this
.disallowedTraitsChance.Where(predicate: trait => Rand.Range(min: 0, max: 100) < trait.chance)
.ToList()
.ConvertAll(converter: trait => new BackstoryTrait { def = trait.defName, degree = trait.degree }))
.ToList();
this.workDisables = (this.workAllows & WorkTags.AllWork) != 0 ? this.workDisables : ~this.workAllows;

if (this.bodyTypeGlobal == null && this.bodyTypeFemale == null && this.bodyTypeMale == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using RimWorld;
using UnityEngine;
using Verse;

namespace JecsTools;

public static partial class HarmonyPatches
{
public static void HarmonyPatches_BuildingExtension(Harmony harmony, Type type)
{
//BuildingExtension prevents some things from wiping other things when spawned/constructing/blueprinted.
harmony.Patch(AccessTools.Method(typeof(GenSpawn), nameof(GenSpawn.SpawningWipes)),
postfix: new HarmonyMethod(type, nameof(SpawningWipes_PostFix)));
harmony.Patch(AccessTools.Method(typeof(GenConstruct), nameof(GenConstruct.CanPlaceBlueprintOver)),
postfix: new HarmonyMethod(type, nameof(CanPlaceBlueprintOver_PostFix)));

//Ignores all structures as part of objects that disallow being fired through.
harmony.Patch(AccessTools.Method(typeof(Projectile), "CanHit"),
postfix: new HarmonyMethod(type, nameof(CanHit_PostFix)));

//Allows a bullet to pass through walls when fired.
harmony.Patch(AccessTools.Method(typeof(Verb), "CanHitCellFromCellIgnoringRange"),
prefix: new HarmonyMethod(type, nameof(CanHitCellFromCellIgnoringRange_Prefix)));
}


public static void SpawningWipes_PostFix(BuildableDef newEntDef, BuildableDef oldEntDef, ref bool __result)
{
// If SpawningWipes is already returning true, don't need to do anything.
if (__result == false && newEntDef is ThingDef newDef && oldEntDef is ThingDef oldDef)
{
if (HasSharedWipeCategory(newDef, oldDef))
__result = true;
}
}


public static void CanPlaceBlueprintOver_PostFix(BuildableDef newDef, ThingDef oldDef, ref bool __result)
{
// If CanPlaceBlueprintOver is already returning false, don't need to do anything.
if (__result == true && newDef is ThingDef thingDef)
{
if (HasSharedWipeCategory(thingDef, oldDef))
__result = false;
}
}

//Check wipe categories on BuildingExtension between two defs
private static bool HasSharedWipeCategory(ThingDef newDef, ThingDef oldDef)
{
static HashSet<string> GetWipeCategories(ThingDef thingDef)
{
var buildingExtension = GenConstruct.BuiltDefOf(thingDef)?.GetBuildingExtension();
if (buildingExtension == null)
return null;
var wipeCategorySet = buildingExtension.WipeCategories;
return wipeCategorySet == null || wipeCategorySet.Count == 0 ? null : wipeCategorySet;
}

var wipeCategoriesA = GetWipeCategories(newDef);
DebugMessage($"{newDef} wipeCategoriesA: {wipeCategoriesA.ToStringSafeEnumerable()}");
var wipeCategoriesB = GetWipeCategories(oldDef);
DebugMessage($"{oldDef} wipeCategoriesB: {wipeCategoriesB.ToStringSafeEnumerable()}");
if (wipeCategoriesB == null && wipeCategoriesA == null)
{
DebugMessage("both wipeCategories null => false");
return false;
}
else if (wipeCategoriesA != null && wipeCategoriesB == null)
{
DebugMessage("wipeCategoriesB null => false");
return false;
}
else if (wipeCategoriesB != null && wipeCategoriesA == null)
{
DebugMessage("wipeCategoriesA null => false");
return false;
}
else
{
foreach (var strB in wipeCategoriesB)
{
if (wipeCategoriesA.Contains(strB))
{
DebugMessage($"found shared wipeCategories ({strB}) => true");
return true;
}
}
DebugMessage("no shared wipeCategories => false");
return false;
}
}



//Added B19, Oct 2019
//ProjectileExtension check
//Ignores all structures as part of objects that disallow being fired through.
public static void CanHit_PostFix(Projectile __instance, Thing thing, ref bool __result)
{
// TODO: This patch looks pointless since it can only change __result from false to ... false.
if (__result == false && __instance.def?.GetProjectileExtension() is ProjectileExtension ext)
{
if (ext.passesWalls)
{
//Mods will often have their own walls, so we cannot do a def check for ThingDefOf.Wall
//Most "walls" should either be in the structure category or be able to hold walls.
// TODO: In RW 1.3+, it seems like BuildingProperties.isPlaceOverableWall indicates whether something is a "wall",
// but it may be better to just look at ThingDef.Fillage/fillPercent instead,
// or maybe use PlaceWorker_OnTopOfWalls's heuristic of checking whether the defName contains "Wall"?
if (thing?.def is ThingDef def && (def.designationCategory == DesignationCategoryDefOf.Structure || def.holdsRoof))
{
__result = false;
return;
}
}
}
}


//Added B19, Oct 2019
//ProjectileExtension check
//Allows a bullet to pass through walls when fired.
public static bool CanHitCellFromCellIgnoringRange_Prefix(Verb __instance, ref bool __result)
{
if (__instance.EquipmentCompSource?.PrimaryVerb?.verbProps?.defaultProjectile?.GetProjectileExtension() is ProjectileExtension ext)
{
if (ext.passesWalls)
{
// TODO: While this does bypass the line-of-sight checks (and should it really bypass all LOS checks?),
// this also bypasses non-LOS checks, which doesn't look right.
__result = true;
}
return false;
}
return true;
}

}
Loading

0 comments on commit 85f7828

Please sign in to comment.