Hopefully the Final Day of Soothing
The last thing I wanted to add to the Soothe spell was a change to the reticle when you are pointing at a target withing range. I was having a hard time determining if my Soothe spell would actually be applied to the expected target before I cast the spell.
To do this, I decided to expand the SpellBase class to include a method to check if it has valid targets. The intent was that the caller would essentially "dry run" the cast and see if anything would have been hit. By default, it won't do anything but can be overridden in the sub-class if they need that functionality.
// In base class
public virtual bool HasValidTargets(SpellCaster caster)
{
// Do nothing by default
return false;
}
// In sub-class
public override bool HasValidTargets(SpellCaster caster)
{
var (didFindTarget, _) = GetTargetFromRayCast<ISoothable>();
return didFindTarget;
}
I also added a property that determines if the targeting reticle should change for this spell. This was also where I learned that you can abstract a property. I've used abstract and virtual quite a bit in my career but I've never seen it used on a property. Surprisingly, this was exactly what I needed to avoid a bunch of weird parameter passing or something.
// In base class
public abstract bool ShouldChangeTargetReticle { get; }
// In sub-class
public override bool ShouldChangeTargetReticle => true;
Combining both of those in the SpellCaster's Update method, along with updating the ReticleUI class, looked like this
private void HandleHasValidSpellTargets()
{
var activeSpell = _spells[_activeSpellIndex];
if (!activeSpell.ShouldChangeTargetReticle) return;
if (activeSpell.HasValidTargets(this))
{
_reticleUI.ShowSpellTargetValidReticle();
}
}
And here is what it looks like in game. Now that I watch the gif, the color needs to be more contrast-y. It looked better in the actual game, as the gif gets compressed a little. However, the ideal change would be to change the actual shape of the reticle so we are not relying solely on color. It'll work for now though.

With that done, I was ready to merge in the latest changes from the team. They had been busy making awesome changes, like adding in the glimmerstalker and the gaquk!
Merging in Other Changes and Regrouping
I merged in their changes and had to resolve some merge conflicts (naturally). I'd love to figure out how to avoid those but when multiple team members are working in the same prefab, things get weird. Code conflicts I can handle. Another thing on the list to research further after the jam.
Anyway, I got through the conflicts pretty quickly. There wasn't anything serious and I know more what to look for now. When I ran the program everything initially seemed on the up and up.

Opening up the debug panel I noticed a couple of problems:
- The Soothe spell was displaying as a Minor Fear modifier instead of Soothed (though the actual value was being applied correctly)
- Major and Minor Fears are not configured as Timed modifiers
- Once Crew have Broke they can still be affected by modifiers
- The debug detail lines didn't expand to include all contents
- This shouldn't be much of an issue, since they will most likely not have that many modifiers applied at once. These stacked up over time because the Fear ones were not timing out (See #2 above)
- If we do end up having more modifiers, then that can be reworked but for now I'm going to leave it
Fixing the Soothed and Fear Morale Modifiers
The problem with the Soothed modifier turned out to be a "flaw" with using ScriptableObjects. When you set an enum value through the Inspector, it will always use the underlying value. I'm not really sure how it would work any other way, hence the quotes around "flaw". When I merged in the changes, both Andrew and I added new Morale Modifiers to the enum, changing what value my Soothed enum item was pointing to.
I knew there was a high probability of this happening, so I ended up adding mine at the end, since there was only one, and leaving the two that Andrew added at their original places. It was easily fixed by updating the value in the ScriptableObject instance. I could see that being a massive problem on larger projects though.
Likewise for the two Fear modifiers, it was as simple as setting the Clear Type to Timed instead of Manual and setting a duration. After fixing those, now everything is looking how I would expect.

Fixing the Broke Morale State
The main problem with this is that once a Crew member has broken, they should remain in that state and not be affected by any Morale modifiers. It was being treated as a regular state, where if the Morale value passed a threshold it would change to the new state instead of ignoring everything.
This was a simple fix. When we go to change the state, we just don't if their current state is Broke.
if (_currentState != MoraleStatus.Broke)
{
var newState = GetMoraleState(_currentMorale);
SetState(newState);
}
Some More Cleanup
I ended up cleaning up the modifiers on the Debug window to only show the time remaining if it is actually a timed modifier. I also made it so they are explicitly on new lines. That helped clean up that column quite a bit.
