Compare commits

...

3 Commits

Author SHA1 Message Date
Zsar 72692c880f Merge branch 'incompleteAnimationDefFix' into 'master'
fix issue #4 "Incompatible animation may cause infinite loop [...]"

Closes #4

See merge request c0ffeeeeeeee/rimworld-animations!11
2024-03-29 22:33:14 +00:00
Tory 3fb106e8f3 Updated Nyaron patch 2024-03-29 17:23:08 +00:00
Zsar 2cdffc37bc fix issue #4 "Incompatible animation may cause infinite loop [...]"
? function AnimationDef::postLoad now throws if any clip contained lacks mandatory curves
? function AnimationStage::initialize
	+ parameter isGenitalAngleMandatory passes whether generally optional curve GenitalAngle is mandatory in this particular case
	+ return value shows number of broken clips
	? renamed to Initialize as per usual C# convention
? class BaseAnimationClip
	+ function EmitMandatoryCurveEmptyError as fluent interface for checking mandatory curves
	? function buildSimpleCurves
		+ parameter isGenitalAngleMandatory as above
		+ return value shows whether all mandatory curves for the clip have been filled
		? renamed to Initialize as per usual C# convention
2023-04-16 05:05:00 +02:00
6 changed files with 55 additions and 12 deletions

View File

@ -15,6 +15,15 @@
</value>
</match>
</li>
<li Class="PatchOperationConditional">
<xpath>Defs/AlienRace.ThingDef_AlienRace[defName = "Alien_Nyaron"]/alienRace/generalSettings/alienPartGenerator/bodyAddons/li[bodyPartLabel="tail"]</xpath>
<match Class="PatchOperationAdd">
<xpath>/Defs/AlienRace.ThingDef_AlienRace[defName = "Alien_Nyaron"]/alienRace/generalSettings/alienPartGenerator/bodyAddons/li[bodyPartLabel="tail"]</xpath>
<value>
<drawnInBed>false</drawnInBed>
</value>
</match>
</li>
</operations>
</match>
</Operation>

View File

@ -15,14 +15,17 @@ namespace Rimworld_Animations {
public List<BaseAnimationClip> animationClips;
public List<string> tags = new List<string>();
public void initialize() {
foreach (BaseAnimationClip clip in animationClips) {
clip.buildSimpleCurves();
public int Initialize(List<bool> 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;
}
}
}

View File

@ -12,9 +12,18 @@ namespace Rimworld_Animations {
public Dictionary<int, string> SoundEffects = new Dictionary<int, string>();
public List<ThingDef> types; //types of participants
public int duration;
public abstract void buildSimpleCurves();
/** <returns>failure - if 'true', this clip should not be used</returns> */
public abstract bool BuildSimpleCurves(bool isGenitalAngleMandatory = false);
public string soundDef = null; //for playing sounds
public int actor;
public List<string> tags = new List<string>();
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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}