mirror of
https://github.com/FalloutCollaborationProject/FCP-Tools.git
synced 2026-07-27 16:59:37 -07:00
Refactor ghoul system and add ferality gene logic
Major overhaul of the ghoul system: removed legacy component-based ghoul logic and replaced it with a gene-based system. Added new gene classes for ferality, ghoul body, skin, glowing one, and toxic healing, along with associated hediffs and Harmony patches. Updated XML defs and project files to support the new system, and removed obsolete code and solution files. This refactor centralizes ghoul behavior, transformation, and ferality progression under the new gene architecture.
This commit is contained in:
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"files.exclude": {
|
||||
"1.5": true
|
||||
}
|
||||
}
|
||||
Vendored
-15
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "dotnet",
|
||||
"task": "build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"label": "build"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Defs>
|
||||
|
||||
<HediffDef>
|
||||
<defName>ToxicHealing</defName>
|
||||
<hediffClass>HediffWithComps</hediffClass>
|
||||
<label>toxic metabolism</label>
|
||||
<description>This creature's body converts toxic buildup into healing energy.</description>
|
||||
<defaultLabelColor>(0.4, 1.0, 0.4)</defaultLabelColor>
|
||||
<isBad>false</isBad>
|
||||
<initialSeverity>1</initialSeverity>
|
||||
<maxSeverity>1</maxSeverity>
|
||||
<comps>
|
||||
<li Class="FCP_Ghoul.HediffCompProperties_ToxicHealing" />
|
||||
</comps>
|
||||
</HediffDef>
|
||||
|
||||
</Defs>
|
||||
@@ -66,9 +66,6 @@
|
||||
</statFactors>
|
||||
</li>
|
||||
</stages>
|
||||
<modExtensions>
|
||||
<li Class="FCP_Ghoul.ToxHealHediff_ModExtension"></li>
|
||||
</modExtensions>
|
||||
</HediffDef>
|
||||
|
||||
</Defs>
|
||||
|
||||
+1
-10
@@ -1,12 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
|
||||
<!--_________ Mental States _________-->
|
||||
|
||||
<!--_____ Ghouls _____-->
|
||||
|
||||
<!-- Ghoul Feral Mental State -->
|
||||
|
||||
<MentalStateDef ParentName="BaseMentalState">
|
||||
<defName>FCP_MentalState_PermanentBerserk</defName>
|
||||
<stateClass>FCP_Ghoul.MentalState_Feral</stateClass>
|
||||
@@ -22,7 +16,7 @@
|
||||
<blockRandomInteraction>true</blockRandomInteraction>
|
||||
<blockNormalThoughts>true</blockNormalThoughts>
|
||||
<nameColor>(0.9,0.2,0.5)</nameColor>
|
||||
<beginLetter>{0} has gone berserk.\n\n[PAWN_pronoun] will attack anyone [PAWN_pronoun] sees.</beginLetter>
|
||||
<beginLetter>{0} has gone feral.\n\n[PAWN_pronoun] will defend [PAWN_possessive] territory and attack intruders.</beginLetter>
|
||||
<beginLetterDef>ThreatSmall</beginLetterDef>
|
||||
<minTicksBeforeRecovery>99999999</minTicksBeforeRecovery>
|
||||
<maxTicksBeforeRecovery>99999999</maxTicksBeforeRecovery>
|
||||
@@ -34,9 +28,6 @@
|
||||
<baseInspectLine>Mental state: Feral</baseInspectLine>
|
||||
<stateEffecter>Berserk</stateEffecter>
|
||||
<tale>MentalStateBerserk</tale>
|
||||
<modExtensions>
|
||||
<li Class="FCP_Ghoul.PermanentBerserk_ModExtension" />
|
||||
</modExtensions>
|
||||
</MentalStateDef>
|
||||
|
||||
</Defs>
|
||||
@@ -38,7 +38,7 @@
|
||||
<workerClass>FalloutCore.LaserBurn</workerClass>
|
||||
<label>laser burn</label>
|
||||
<hasForcefulImpact>false</hasForcefulImpact>
|
||||
<makesBlood>false</makesBlood>
|
||||
<makesBlood>true</makesBlood>
|
||||
<canInterruptJobs>false</canInterruptJobs>
|
||||
<externalViolence>true</externalViolence>
|
||||
<deathMessage>{0} has been laser burned to death.</deathMessage>
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using RimWorld.Planet;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using Verse.Noise;
|
||||
|
||||
namespace FalloutCore
|
||||
{
|
||||
public class CompProperties_HiddenFeralGhoul : CompProperties
|
||||
{
|
||||
public SimpleCurve chanceToTurnInYears;
|
||||
public CompProperties_HiddenFeralGhoul()
|
||||
{
|
||||
this.compClass = typeof(CompHiddenFeralGhoul);
|
||||
}
|
||||
}
|
||||
|
||||
public class CompHiddenFeralGhoul : ThingComp
|
||||
{
|
||||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||
{
|
||||
base.PostSpawnSetup(respawningAfterLoad);
|
||||
if (!respawningAfterLoad)
|
||||
{
|
||||
nextFeralChanceCheckTick = Find.TickManager.TicksGame + (GenDate.TicksPerDay * Rand.RangeInclusive(40, 80));
|
||||
}
|
||||
}
|
||||
public CompProperties_HiddenFeralGhoul Props => this.props as CompProperties_HiddenFeralGhoul;
|
||||
|
||||
public int nextFeralChanceCheckTick;
|
||||
|
||||
public Pawn Ghoul => this.parent as Pawn;
|
||||
public override void CompTick()
|
||||
{
|
||||
base.CompTick();
|
||||
DoCheck();
|
||||
}
|
||||
|
||||
public void DoCheck()
|
||||
{
|
||||
var ghoul = Ghoul;
|
||||
if (!ghoul.IsFeralGhoul() && this.parent.Map != null && Find.TickManager.TicksGame > nextFeralChanceCheckTick)
|
||||
{
|
||||
var chance = Props.chanceToTurnInYears.Evaluate(ghoul.ageTracker.AgeBiologicalYearsFloat);
|
||||
if (Rand.Chance(chance))
|
||||
{
|
||||
ConvertToFeral(ghoul);
|
||||
}
|
||||
nextFeralChanceCheckTick = Find.TickManager.TicksGame + (GenDate.TicksPerDay * Rand.RangeInclusive(40, 80));
|
||||
}
|
||||
}
|
||||
|
||||
public void ConvertToFeral(Pawn pawn)
|
||||
{
|
||||
var pawnKindDefName = "Feral" + pawn.kindDef.defName;
|
||||
var pawnKindDef = DefDatabase<PawnKindDef>.GetNamedSilentFail(pawnKindDefName) ?? DefDatabase<PawnKindDef>.GetNamed("FeralGhoul_Pawn");
|
||||
var newPawn = PawnUtils.GetPawnDuplicate(pawn, pawnKindDef);
|
||||
GenSpawn.Spawn(newPawn, pawn.Position, pawn.Map);
|
||||
pawn.Destroy(DestroyMode.Vanish);
|
||||
newPawn.SetFaction(null);
|
||||
newPawn.mindState.mentalStateHandler.TryStartMentalState(MentalStateDefOf.ManhunterPermanent);
|
||||
newPawn.equipment.DropAllEquipment(newPawn.Position);
|
||||
}
|
||||
|
||||
public override void PostExposeData()
|
||||
{
|
||||
base.PostExposeData();
|
||||
Scribe_Values.Look(ref nextFeralChanceCheckTick, "nextFeralChanceCheckTick");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace FalloutCore
|
||||
{
|
||||
public class CompProperties_GlowingGhoul : CompProperties_Glower
|
||||
{
|
||||
public float toxicBuildupSeverityAdjust;
|
||||
public int toxicBuildupEmitTickRate;
|
||||
public float toxicBuildupEmitRadius;
|
||||
public CompProperties_GlowingGhoul()
|
||||
{
|
||||
this.compClass = typeof(Comp_GlowingGhoul);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
using UnityEngine;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Verse.AI;
|
||||
|
||||
namespace FalloutCore
|
||||
{
|
||||
|
||||
public class Comp_GlowingGhoul : CompGlower
|
||||
{
|
||||
|
||||
public new CompProperties_GlowingGhoul Props => this.props as CompProperties_GlowingGhoul;
|
||||
|
||||
public override void Initialize(CompProperties props)
|
||||
{
|
||||
base.Initialize(props);
|
||||
}
|
||||
|
||||
public override void PostPostMake()
|
||||
{
|
||||
base.PostPostMake();
|
||||
}
|
||||
|
||||
public IntVec3 vec3 = IntVec3.Invalid;
|
||||
|
||||
public override void CompTick()
|
||||
{
|
||||
base.CompTick();
|
||||
Map map = this.parent.Map;
|
||||
if (map != null)
|
||||
{
|
||||
IntVec3 @int = this.parent.Position;
|
||||
if ((vec3 == IntVec3.Invalid || (vec3 != IntVec3.Invalid && vec3 != @int)) && Find.TickManager.TicksGame >= this.nextUpdateTick)
|
||||
{
|
||||
this.nextUpdateTick = Find.TickManager.TicksGame + 50;
|
||||
map.mapDrawer.MapMeshDirty(this.parent.Position, MapMeshFlagDefOf.Things);
|
||||
map.glowGrid.DeRegisterGlower(this);
|
||||
map.mapDrawer.MapMeshDirty(this.parent.Position, MapMeshFlagDefOf.Things);
|
||||
map.glowGrid.RegisterGlower(this);
|
||||
}
|
||||
if (Find.TickManager.TicksGame % Props.toxicBuildupEmitTickRate == 0)
|
||||
{
|
||||
var pawns = GenRadial.RadialDistinctThingsAround(this.parent.Position, map, Props.toxicBuildupEmitRadius, true).OfType<Pawn>().Where(x => !x.IsGhoul()).ToList();
|
||||
foreach (var pawn in pawns)
|
||||
{
|
||||
float num = Props.toxicBuildupSeverityAdjust;
|
||||
num *= 1f - pawn.GetStatValue(StatDefOf.ToxicResistance);
|
||||
if (num != 0f)
|
||||
{
|
||||
HealthUtility.AdjustSeverity(pawn, HediffDefOf.ToxicBuildup, num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||
{
|
||||
this.nextUpdateTick = Find.TickManager.TicksGame;
|
||||
//if (this.parent.Map != null)
|
||||
//{
|
||||
// base.PostSpawnSetup(respawningAfterLoad);
|
||||
// if (!respawningAfterLoad)
|
||||
// {
|
||||
// this.nextUpdateTick = Find.TickManager.TicksGame + Rand.Range(0, 100);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
public const int updatePeriodInTicks = 50;
|
||||
|
||||
// Token: 0x04000002 RID: 2
|
||||
public int nextUpdateTick;
|
||||
public override void PostExposeData()
|
||||
{
|
||||
base.PostExposeData();
|
||||
Scribe_Values.Look<int>(ref this.nextUpdateTick, "nextUpdateTick", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<!-- Ensure this matches the original framework version -->
|
||||
<RootNamespace>FalloutGhouls</RootNamespace>
|
||||
<AssemblyName>FalloutGhouls</AssemblyName>
|
||||
<RootNamespace>FCP_Ghoul</RootNamespace>
|
||||
<AssemblyName>FCP_Ghoul</AssemblyName>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Deterministic>true</Deterministic>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
@@ -41,11 +41,6 @@
|
||||
<PackageReference Include="Krafs.Publicizer" Version="2.*" PrivateAssets="all">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.6.4518" />
|
||||
<PackageReference Include="Lib.Harmony" Version="2.3.6">
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Disable automatic generation of assembly attributes -->
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31410.357
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FalloutGhouls", "FalloutGhouls.csproj", "{23EA1664-1986-4D57-ACF0-AC8BBDA964C5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{23EA1664-1986-4D57-ACF0-AC8BBDA964C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{23EA1664-1986-4D57-ACF0-AC8BBDA964C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{23EA1664-1986-4D57-ACF0-AC8BBDA964C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23EA1664-1986-4D57-ACF0-AC8BBDA964C5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {13E3D5D9-F7FA-4CCE-93A0-BA00218054F3}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,56 @@
|
||||
using RimWorld;
|
||||
using RimWorld.BaseGen;
|
||||
using Verse;
|
||||
using System.Linq;
|
||||
|
||||
namespace FCP_Ghoul
|
||||
{
|
||||
public class GenStep_FeralGhoulSpawn : GenStep
|
||||
{
|
||||
public PawnKindDef pawnKindDef;
|
||||
public float spawnChance = 0.35f;
|
||||
public int minPackSize = 2;
|
||||
public int maxPackSize = 5;
|
||||
|
||||
public override int SeedPart => 674893246;
|
||||
|
||||
public override void Generate(Map map, GenStepParams parms)
|
||||
{
|
||||
if (!ModsConfig.BiotechActive || pawnKindDef == null)
|
||||
return;
|
||||
|
||||
if (Rand.Value > spawnChance)
|
||||
return;
|
||||
|
||||
int packSize = Rand.RangeInclusive(minPackSize, maxPackSize);
|
||||
|
||||
for (int i = 0; i < packSize; i++)
|
||||
{
|
||||
IntVec3 loc;
|
||||
if (!CellFinder.TryFindRandomEdgeCellWith(c =>
|
||||
c.Standable(map) &&
|
||||
!c.Fogged(map),
|
||||
map, CellFinder.EdgeRoadChance_Ignore, out loc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Pawn ghoul = PawnGenerator.GeneratePawn(pawnKindDef, null);
|
||||
|
||||
var feralityGene = ghoul.genes?.GetFirstGeneOfType<Gene_Ferality>();
|
||||
if (feralityGene != null)
|
||||
{
|
||||
feralityGene.SetFerality(100f);
|
||||
}
|
||||
|
||||
GenSpawn.Spawn(ghoul, loc, map);
|
||||
|
||||
var mentalState = DefDatabase<MentalStateDef>.GetNamed("FCP_PermanentBerserk", false);
|
||||
if (mentalState != null)
|
||||
{
|
||||
ghoul.mindState.mentalStateHandler.TryStartMentalState(mentalState, forceWake: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using RimWorld;
|
||||
using RimWorld.Planet;
|
||||
using Verse;
|
||||
|
||||
namespace FCP_Ghoul
|
||||
{
|
||||
public class GenStep_FeralGhoulNest : GenStep
|
||||
{
|
||||
public PawnKindDef pawnKindDef;
|
||||
public int minGhouls = 8;
|
||||
public int maxGhouls = 16;
|
||||
|
||||
private static GeneDef feralityGeneDef;
|
||||
private static MentalStateDef berserkDef;
|
||||
|
||||
public override int SeedPart => 982347123;
|
||||
|
||||
public override void Generate(Map map, GenStepParams parms)
|
||||
{
|
||||
if (!ModsConfig.BiotechActive || pawnKindDef == null) return;
|
||||
|
||||
if (feralityGeneDef == null) feralityGeneDef = DefDatabase<GeneDef>.GetNamed("FCP_Gene_Ferality", false);
|
||||
if (berserkDef == null) berserkDef = DefDatabase<MentalStateDef>.GetNamed("FCP_PermanentBerserk", false);
|
||||
|
||||
int count = Rand.RangeInclusive(minGhouls, maxGhouls);
|
||||
IntVec3 center = map.Center;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (!CellFinder.TryFindRandomCellNear(center, map, 30, c => c.Standable(map) && !c.Fogged(map), out IntVec3 loc))
|
||||
continue;
|
||||
|
||||
Pawn ghoul = PawnGenerator.GeneratePawn(pawnKindDef, null);
|
||||
EnsureFeralityGene(ghoul);
|
||||
GenSpawn.Spawn(ghoul, loc, map);
|
||||
|
||||
if (berserkDef != null)
|
||||
ghoul.mindState.mentalStateHandler.TryStartMentalState(berserkDef, forceWake: true);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureFeralityGene(Pawn pawn)
|
||||
{
|
||||
var gene = pawn.genes?.GetFirstGeneOfType<Gene_Ferality>();
|
||||
if (gene == null && feralityGeneDef != null && pawn.genes != null)
|
||||
{
|
||||
pawn.genes.AddGene(feralityGeneDef, false);
|
||||
gene = pawn.genes.GetFirstGeneOfType<Gene_Ferality>();
|
||||
}
|
||||
gene?.SetFerality(100f);
|
||||
}
|
||||
}
|
||||
|
||||
public class GenStep_GlowingOne : GenStep
|
||||
{
|
||||
public PawnKindDef pawnKindDef;
|
||||
public bool glowingOneIsBerserk;
|
||||
|
||||
private static GeneDef feralityGeneDef;
|
||||
private static MentalStateDef berserkDef;
|
||||
private static PawnKindDef feralKindDef;
|
||||
|
||||
public override int SeedPart => 723491823;
|
||||
|
||||
public override void Generate(Map map, GenStepParams parms)
|
||||
{
|
||||
if (!ModsConfig.BiotechActive || pawnKindDef == null) return;
|
||||
|
||||
if (feralityGeneDef == null) feralityGeneDef = DefDatabase<GeneDef>.GetNamed("FCP_Gene_Ferality", false);
|
||||
if (berserkDef == null) berserkDef = DefDatabase<MentalStateDef>.GetNamed("FCP_PermanentBerserk", false);
|
||||
if (feralKindDef == null) feralKindDef = DefDatabase<PawnKindDef>.GetNamed("FCP_Pawnkind_Ghoul_Feral", false);
|
||||
|
||||
if (!CellFinder.TryFindRandomCellNear(map.Center, map, 15, c => c.Standable(map) && !c.Fogged(map), out IntVec3 loc))
|
||||
return;
|
||||
|
||||
Pawn glowingOne = PawnGenerator.GeneratePawn(pawnKindDef, null);
|
||||
EnsureFeralityGene(glowingOne);
|
||||
GenSpawn.Spawn(glowingOne, loc, map);
|
||||
|
||||
if (glowingOneIsBerserk && berserkDef != null)
|
||||
glowingOne.mindState.mentalStateHandler.TryStartMentalState(berserkDef, forceWake: true);
|
||||
|
||||
SpawnGuards(loc, map);
|
||||
}
|
||||
|
||||
private void SpawnGuards(IntVec3 center, Map map)
|
||||
{
|
||||
if (feralKindDef == null) return;
|
||||
|
||||
for (int i = 0; i < Rand.RangeInclusive(2, 4); i++)
|
||||
{
|
||||
if (!CellFinder.TryFindRandomCellNear(center, map, 8, c => c.Standable(map) && !c.Fogged(map), out IntVec3 guardLoc))
|
||||
continue;
|
||||
|
||||
Pawn guard = PawnGenerator.GeneratePawn(feralKindDef, null);
|
||||
EnsureFeralityGene(guard);
|
||||
GenSpawn.Spawn(guard, guardLoc, map);
|
||||
|
||||
if (berserkDef != null)
|
||||
guard.mindState.mentalStateHandler.TryStartMentalState(berserkDef, forceWake: true);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureFeralityGene(Pawn pawn)
|
||||
{
|
||||
var gene = pawn.genes?.GetFirstGeneOfType<Gene_Ferality>();
|
||||
if (gene == null && feralityGeneDef != null && pawn.genes != null)
|
||||
{
|
||||
pawn.genes.AddGene(feralityGeneDef, false);
|
||||
gene = pawn.genes.GetFirstGeneOfType<Gene_Ferality>();
|
||||
}
|
||||
gene?.SetFerality(100f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace FCP_Ghoul
|
||||
{
|
||||
[StaticConstructorOnStartup]
|
||||
public class GeneGizmo_Ferality : Gizmo
|
||||
{
|
||||
private Gene_Ferality gene;
|
||||
private static readonly Texture2D FullBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.8f, 0.2f, 0.2f));
|
||||
private static readonly Texture2D EmptyBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.1f, 0.1f, 0.1f));
|
||||
|
||||
public GeneGizmo_Ferality(Gene_Ferality gene)
|
||||
{
|
||||
this.gene = gene;
|
||||
Order = -100f;
|
||||
}
|
||||
|
||||
public override float GetWidth(float maxWidth)
|
||||
{
|
||||
return 140f;
|
||||
}
|
||||
|
||||
public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms)
|
||||
{
|
||||
Rect rect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), 75f);
|
||||
Widgets.DrawWindowBackground(rect);
|
||||
|
||||
// Title
|
||||
Text.Font = GameFont.Tiny;
|
||||
Text.Anchor = TextAnchor.UpperCenter;
|
||||
Rect labelRect = new Rect(rect.x, rect.y + 2f, rect.width, 20f);
|
||||
Widgets.Label(labelRect, "Ferality");
|
||||
|
||||
// Bar background
|
||||
Rect barRect = new Rect(rect.x + 10f, rect.y + 25f, rect.width - 20f, 20f);
|
||||
Widgets.DrawBoxSolid(barRect, EmptyBarTex.GetPixel(0, 0));
|
||||
|
||||
float fillWidth = barRect.width * gene.FeralityPercent;
|
||||
Rect fillRect = new Rect(barRect.x, barRect.y, fillWidth, barRect.height);
|
||||
|
||||
Color barColor;
|
||||
if (gene.FeralityPercent < 0.25f)
|
||||
barColor = Color.Lerp(Color.green, Color.yellow, gene.FeralityPercent * 4f);
|
||||
else if (gene.FeralityPercent < 0.5f)
|
||||
barColor = Color.Lerp(Color.yellow, new Color(1f, 0.5f, 0f), (gene.FeralityPercent - 0.25f) * 4f);
|
||||
else if (gene.FeralityPercent < 0.75f)
|
||||
barColor = Color.Lerp(new Color(1f, 0.5f, 0f), Color.red, (gene.FeralityPercent - 0.5f) * 4f);
|
||||
else
|
||||
barColor = Color.Lerp(Color.red, new Color(0.6f, 0f, 0f), (gene.FeralityPercent - 0.75f) * 4f);
|
||||
|
||||
Widgets.DrawBoxSolid(fillRect, barColor);
|
||||
Widgets.DrawBox(barRect);
|
||||
|
||||
Text.Anchor = TextAnchor.MiddleCenter;
|
||||
Rect textRect = new Rect(rect.x, rect.y + 47f, rect.width, 20f);
|
||||
Widgets.Label(textRect, gene.Ferality.ToString("F0") + "%");
|
||||
|
||||
Text.Anchor = TextAnchor.UpperLeft;
|
||||
Text.Font = GameFont.Small;
|
||||
|
||||
if (Mouse.IsOver(rect))
|
||||
{
|
||||
string tooltip = $"Ferality: {gene.Ferality:F1}%\n\nThis ghoul is slowly losing their mind. Use drugs to reduce ferality and prevent them from going feral.";
|
||||
if (gene.Ferality >= 100f)
|
||||
tooltip += "\n\n<color=red>About to go feral!</color>";
|
||||
TooltipHandler.TipRegion(rect, tooltip);
|
||||
}
|
||||
|
||||
return new GizmoResult(GizmoState.Clear);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace FCP_Ghoul
|
||||
{
|
||||
public class Gene_Ferality : Gene
|
||||
{
|
||||
private float feralityValue;
|
||||
private int tickCounter;
|
||||
private bool needsTransform;
|
||||
|
||||
public float Ferality => feralityValue;
|
||||
public float FeralityPercent => feralityValue / 100f;
|
||||
|
||||
public override void PostAdd()
|
||||
{
|
||||
base.PostAdd();
|
||||
var ext = def.GetModExtension<FeralityGene_ModExtension>();
|
||||
feralityValue = ext?.initialFerality ?? 0f;
|
||||
needsTransform = feralityValue >= 100f;
|
||||
}
|
||||
|
||||
public override void Tick()
|
||||
{
|
||||
base.Tick();
|
||||
|
||||
if (needsTransform)
|
||||
{
|
||||
needsTransform = false;
|
||||
TryGoFeral();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pawn.IsHashIntervalTick(250)) return;
|
||||
|
||||
var ext = def.GetModExtension<FeralityGene_ModExtension>();
|
||||
if (ext == null) return;
|
||||
|
||||
tickCounter += 250;
|
||||
if (tickCounter >= ext.increaseIntervalTicks)
|
||||
{
|
||||
tickCounter = 0;
|
||||
feralityValue += ext.feralityIncreaseAmount;
|
||||
|
||||
if (feralityValue >= 100f)
|
||||
{
|
||||
feralityValue = 100f;
|
||||
TryGoFeral();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReduceFerality(float amount)
|
||||
{
|
||||
feralityValue -= amount;
|
||||
if (feralityValue < 0f)
|
||||
feralityValue = 0f;
|
||||
}
|
||||
|
||||
public void SetFerality(float amount)
|
||||
{
|
||||
feralityValue = Mathf.Clamp(amount, 0f, 100f);
|
||||
needsTransform = feralityValue >= 100f;
|
||||
}
|
||||
|
||||
private void TryGoFeral()
|
||||
{
|
||||
if (pawn.Dead || pawn.Downed) return;
|
||||
|
||||
if (pawn.equipment?.Primary != null)
|
||||
{
|
||||
var eq = pawn.equipment.Primary;
|
||||
if (pawn.equipment.TryDropEquipment(eq, out ThingWithComps result, pawn.Position))
|
||||
result?.SetForbidden(false, false);
|
||||
}
|
||||
|
||||
if (pawn.genes != null)
|
||||
{
|
||||
Gene skinGene = null;
|
||||
bool isGlowing = false;
|
||||
|
||||
foreach (var gene in pawn.genes.GenesListForReading)
|
||||
{
|
||||
if (gene.def.defName == "FCP_Gene_Ghoul_Skin_GlowingOne")
|
||||
{
|
||||
isGlowing = true;
|
||||
break;
|
||||
}
|
||||
if (gene.def.defName.StartsWith("FCP_Gene_Ghoul_Skin"))
|
||||
skinGene = gene;
|
||||
}
|
||||
|
||||
if (skinGene != null && !isGlowing)
|
||||
{
|
||||
pawn.genes.RemoveGene(skinGene);
|
||||
var feralSkin = DefDatabase<GeneDef>.GetNamed("FCP_Gene_Ghoul_Feral_Skin", false);
|
||||
if (feralSkin != null)
|
||||
{
|
||||
pawn.genes.AddGene(feralSkin, false);
|
||||
pawn.Drawer.renderer.SetAllGraphicsDirty();
|
||||
PortraitsCache.SetDirty(pawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pawn.Faction?.IsPlayer == true)
|
||||
pawn.SetFaction(null);
|
||||
|
||||
var berserk = DefDatabase<MentalStateDef>.GetNamed("FCP_MentalState_PermanentBerserk", false);
|
||||
if (berserk != null)
|
||||
pawn.mindState.mentalStateHandler.TryStartMentalState(berserk, forceWake: true);
|
||||
|
||||
Messages.Message("MessageFeralGhoul".Translate(pawn.Named("PAWN")), pawn, MessageTypeDefOf.NegativeEvent);
|
||||
}
|
||||
|
||||
public override IEnumerable<Gizmo> GetGizmos()
|
||||
{
|
||||
foreach (var gizmo in base.GetGizmos())
|
||||
if (gizmo != null) yield return gizmo;
|
||||
|
||||
if (pawn?.Faction == Faction.OfPlayer)
|
||||
yield return new GeneGizmo_Ferality(this);
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Values.Look(ref feralityValue, "feralityValue", 0f);
|
||||
Scribe_Values.Look(ref tickCounter, "tickCounter", 0);
|
||||
Scribe_Values.Look(ref needsTransform, "needsTransform", false);
|
||||
}
|
||||
}
|
||||
|
||||
public class FeralityGene_ModExtension : DefModExtension
|
||||
{
|
||||
public int increaseIntervalTicks = 60000; // 1 in-game day
|
||||
public float feralityIncreaseAmount = 1f;
|
||||
public float amountReducedPerDrug = 20f;
|
||||
public float initialFerality = 0f; // Starting ferality value (0-100)
|
||||
public List<ThingDef> drugs = new List<ThingDef>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace FCP_Ghoul
|
||||
{
|
||||
public class Gene_GhoulBody : Gene
|
||||
{
|
||||
private Graphic cachedGraphic;
|
||||
private string cachedBodyType;
|
||||
private Color cachedSkinColor;
|
||||
|
||||
public override void PostAdd()
|
||||
{
|
||||
base.PostAdd();
|
||||
pawn.Drawer.renderer.SetAllGraphicsDirty();
|
||||
}
|
||||
|
||||
public override void PostRemove()
|
||||
{
|
||||
base.PostRemove();
|
||||
pawn.Drawer.renderer.SetAllGraphicsDirty();
|
||||
}
|
||||
|
||||
public Graphic GetBodyOverlay(Pawn pawn)
|
||||
{
|
||||
string bodyType = pawn.story.bodyType.defName;
|
||||
Color skinColor = pawn.story.SkinColor;
|
||||
|
||||
if (cachedGraphic != null && cachedBodyType == bodyType && cachedSkinColor == skinColor)
|
||||
return cachedGraphic;
|
||||
|
||||
bool isFeral = def.defName == "FCP_Gene_Ghoul_Feral_Skin";
|
||||
string path = isFeral ? $"FCP_Ghoul/Feral/Bodies/Naked_{bodyType}" : $"FCP_Ghoul/Bodies/Naked_{bodyType}";
|
||||
|
||||
cachedGraphic = GraphicDatabase.Get<Graphic_Multi>(path, ShaderDatabase.CutoutSkin, Vector2.one * 1.5f, skinColor);
|
||||
cachedBodyType = bodyType;
|
||||
cachedSkinColor = skinColor;
|
||||
|
||||
return cachedGraphic;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace FCP_Ghoul
|
||||
{
|
||||
public class Gene_GhoulSkin : Gene
|
||||
{
|
||||
public override void PostAdd()
|
||||
{
|
||||
base.PostAdd();
|
||||
RefreshGraphics();
|
||||
}
|
||||
|
||||
public override void PostRemove()
|
||||
{
|
||||
base.PostRemove();
|
||||
RefreshGraphics();
|
||||
}
|
||||
|
||||
private void RefreshGraphics()
|
||||
{
|
||||
if (pawn?.Drawer?.renderer == null)
|
||||
return;
|
||||
|
||||
// Force complete graphics refresh
|
||||
pawn.Drawer.renderer.SetAllGraphicsDirty();
|
||||
|
||||
// Regenerate render tree
|
||||
if (pawn.Drawer.renderer.renderTree != null)
|
||||
{
|
||||
pawn.Drawer.renderer.renderTree.SetDirty();
|
||||
}
|
||||
|
||||
// Force graphics to be reinitialized
|
||||
pawn.Drawer.renderer.EnsureGraphicsInitialized();
|
||||
|
||||
// Notify the graphics are dirty
|
||||
if (pawn.Map != null)
|
||||
{
|
||||
pawn.Map.mapDrawer.MapMeshDirty(pawn.Position, MapMeshFlagDefOf.Things);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user