diff --git a/1.4/Patches/RacePatches/Nyaron.xml b/1.4/Patches/RacePatches/Nyaron.xml
index be62f20..c176521 100644
--- a/1.4/Patches/RacePatches/Nyaron.xml
+++ b/1.4/Patches/RacePatches/Nyaron.xml
@@ -15,6 +15,15 @@
+
+ Defs/AlienRace.ThingDef_AlienRace[defName = "Alien_Nyaron"]/alienRace/generalSettings/alienPartGenerator/bodyAddons/li[bodyPartLabel="tail"]
+
+ /Defs/AlienRace.ThingDef_AlienRace[defName = "Alien_Nyaron"]/alienRace/generalSettings/alienPartGenerator/bodyAddons/li[bodyPartLabel="tail"]
+
+ false
+
+
+
diff --git a/1.4/Source/Animations/AnimationStage.cs b/1.4/Source/Animations/AnimationStage.cs
index 7a1304e..6f0f709 100644
--- a/1.4/Source/Animations/AnimationStage.cs
+++ b/1.4/Source/Animations/AnimationStage.cs
@@ -15,14 +15,17 @@ namespace Rimworld_Animations {
public List animationClips;
public List tags = new List();
- public void initialize() {
- foreach (BaseAnimationClip clip in animationClips) {
- clip.buildSimpleCurves();
+ public int Initialize(List isGenitalAngleMandatory) {
+ var brokenClips = 0;
+ for (var i = 0; i < animationClips.Count; ++i) {
+ var clip = animationClips[i];
+ if (clip.BuildSimpleCurves(isGenitalAngleMandatory[i]))
+ ++brokenClips;
//select playTimeTicks as longest playtime of all the animations
- if(clip.duration > playTimeTicks) {
- playTimeTicks = clip.duration;
- }
+ if (clip.duration > playTimeTicks)
+ playTimeTicks = clip.duration;
}
+ return brokenClips;
}
}
}
diff --git a/1.4/Source/Animations/Clips/BaseAnimationClip.cs b/1.4/Source/Animations/Clips/BaseAnimationClip.cs
index aa35b31..119fbf3 100644
--- a/1.4/Source/Animations/Clips/BaseAnimationClip.cs
+++ b/1.4/Source/Animations/Clips/BaseAnimationClip.cs
@@ -12,9 +12,18 @@ namespace Rimworld_Animations {
public Dictionary SoundEffects = new Dictionary();
public List types; //types of participants
public int duration;
- public abstract void buildSimpleCurves();
+ /** failure - if 'true', this clip should not be used */
+ public abstract bool BuildSimpleCurves(bool isGenitalAngleMandatory = false);
public string soundDef = null; //for playing sounds
public int actor;
public List tags = new List();
+
+ protected bool EmitMandatoryCurveEmptyError(SimpleCurve curve, string name) {
+ if (curve.PointsCount <= 0) {
+ Log.Error($"Mandatory curve '{name}' is empty in an animation clip!");
+ return true;
+ }
+ return false;
+ }
}
}
diff --git a/1.4/Source/Animations/Clips/PawnAnimationClip.cs b/1.4/Source/Animations/Clips/PawnAnimationClip.cs
index e9d2489..e4e2f67 100644
--- a/1.4/Source/Animations/Clips/PawnAnimationClip.cs
+++ b/1.4/Source/Animations/Clips/PawnAnimationClip.cs
@@ -21,9 +21,9 @@ namespace Rimworld_Animations {
public SimpleCurve BodyOffsetZ = new SimpleCurve();
public SimpleCurve HeadFacing = new SimpleCurve();
public SimpleCurve BodyFacing = new SimpleCurve();
-
- public override void buildSimpleCurves() {
+
+ public override bool BuildSimpleCurves(bool isGenitalAngleMandatory) {
int duration = 0;
@@ -109,7 +109,18 @@ namespace Rimworld_Animations {
}
+ // check whether all mandatory curves have been filled
+ bool invalid = false;
+ invalid |= this.EmitMandatoryCurveEmptyError(this.BodyOffsetX, "BodyOffsetX");
+ invalid |= this.EmitMandatoryCurveEmptyError(this.BodyOffsetZ, "BodyOffsetZ");
+ invalid |= this.EmitMandatoryCurveEmptyError(this.BodyAngle, "BodyAngle");
+ invalid |= this.EmitMandatoryCurveEmptyError(this.HeadAngle, "HeadAngle");
+ if (isGenitalAngleMandatory)
+ invalid |= this.EmitMandatoryCurveEmptyError(this.GenitalAngle, "GenitalAngle");
+ invalid |= this.EmitMandatoryCurveEmptyError(this.BodyFacing, "BodyFacing");
+ invalid |= this.EmitMandatoryCurveEmptyError(this.HeadFacing, "HeadFacing");
+ invalid |= this.EmitMandatoryCurveEmptyError(this.HeadBob, "HeadBob");
+ return invalid;
}
-
}
}
diff --git a/1.4/Source/Animations/Clips/ThingAnimationClip.cs b/1.4/Source/Animations/Clips/ThingAnimationClip.cs
index 26f4d4c..c3661de 100644
--- a/1.4/Source/Animations/Clips/ThingAnimationClip.cs
+++ b/1.4/Source/Animations/Clips/ThingAnimationClip.cs
@@ -16,7 +16,7 @@ namespace Rimworld_Animations {
public SimpleCurve Rotation = new SimpleCurve();
- public override void buildSimpleCurves() {
+ public override bool BuildSimpleCurves(bool unused = false) {
int duration = 0;
//getting the length of the whole clip
foreach (ThingKeyframe frame in keyframes)
@@ -69,6 +69,13 @@ namespace Rimworld_Animations {
}
}
+
+ // check whether all mandatory curves have been filled
+ bool invalid = false;
+ invalid |= this.EmitMandatoryCurveEmptyError(this.PositionX, "PositionX");
+ invalid |= this.EmitMandatoryCurveEmptyError(this.PositionZ, "PositionZ");
+ invalid |= this.EmitMandatoryCurveEmptyError(this.Rotation, "Rotation");
+ return invalid;
}
}
}
diff --git a/1.4/Source/Defs/AnimationDef.cs b/1.4/Source/Defs/AnimationDef.cs
index 395ff83..2b13680 100644
--- a/1.4/Source/Defs/AnimationDef.cs
+++ b/1.4/Source/Defs/AnimationDef.cs
@@ -19,8 +19,12 @@ namespace Rimworld_Animations {
public override void PostLoad() {
base.PostLoad();
+ // FIXME: I could not figure out how to map between controlGenitalAngle and PawnAnimationClip. This is brittle AF! -Zsar 2023-04-16
+ var controlGenitalAngle = this.actors.ConvertAll(actor => actor.controlGenitalAngle);
foreach(AnimationStage stage in animationStages) {
- stage.initialize();
+ var brokenClips = stage.Initialize(controlGenitalAngle);
+ if (brokenClips > 0)
+ throw new Exception($"AnimationDef '{this.defName}', stage '{stage.stageName}' contained {brokenClips} broken Clips. The animation has been dropped.");
animationTimeTicks += stage.playTimeTicks;
}
}