Goal: Test everything in PlayerEffects that actually does something
#
As we found out previously, not all of the effects can be imposed on a player by adding to the
PlayerEffects table. So let’s dig into the server and see what actually has an impact:
publicstaticvoidCreateCharacterEffect(EffectTypeeffectType,inteffectAmount,Charactertarget,intduration,Charactercaster){try{if(target.effectList.ContainsKey(effectType))// the target already has this effect type{if(target.effectList[effectType].effectAmount<=effectAmount){target.effectList[effectType].effectAmount=effectAmount;target.effectList[effectType].duration=duration;target.effectList[effectType].caster=caster;}return;}Effecteffect=newEffect(effectType,effectAmount,duration,target,caster);switch(effectType){caseEffectType.Dog_Follow:target.FollowName=caster.Name;effect.caster=null;break;#regionStrictlyBottleEffectscaseEffectType.Ale:target.SendToAllInSight(target.Name+" burps loudly.");break;caseEffectType.Balm:// healing over timeEffect.DoBalmEffect(target,target.effectList[effectType]);break;
// character effectpublicEffect(EffectTypeeffectType,intamount,intduration,Charactertarget,Charactercaster){this.effectType=effectType;this.effectAmount=amount;this.duration=duration;this.target=target;this.caster=caster;this.target.effectList.Add(this.effectType,this);this.effectTimer=newSystem.Timers.Timer(DragonsSpineMain.MasterRoundInterval);this.effectTimer.Elapsed+=newSystem.Timers.ElapsedEventHandler(CharacterEffectEvent);this.effectTimer.Start();}
Which is a lot of lines but not much action (full disclosure: I strongly prefer
K&R style braces over the Allman style
used in this code). Currently concerned with DB-driven effects (where the caster doesn’t exist), we can boil
this down to:
The duration is decremented each round.
If duration is zero, the effect is stopped unless it is Hide_In_Shadows.
If the duration is not zero, the Balm effect occurs.
caseEffectType.Balm:// healing over timeif(this.effectAmount>0){this.target.Hits+=this.effectAmount;if(this.target.Hits>this.target.HitsFull)this.target.Hits=this.target.HitsFull;}break;default:break;}this.target.effectList.Remove(this.effectType);if(this.target.PCState==Globals.ePlayerState.PLAYING||!this.target.IsPC){if(Effect.GetEffectName(this.effectType)!="Unknown"){this.target.WriteToDisplay("The "+Effect.GetEffectName(this.effectType)+" spell has worn off.");if(this.caster!=null&&this.caster.IsPC){PCpc=PC.getOnline(this.caster.PlayerID);if(pc!=null&&pc.PCState==Globals.ePlayerState.PLAYING&&pc.PlayerID!=this.target.PlayerID){pc.WriteToDisplay("Your "+GetEffectName(this.effectType)+" spell has worn off of "+this.target.Name+".");}}}}}catch(Exceptione){Utils.Log("Failure at StopCharacterEffect Effect: "+this.effectType.ToString(),Utils.LogType.SystemFailure);Utils.LogException(e);}}
Cleans up the actual effect, and always tells the player it was a spell (as we’ve seen). And what
about that DoBalmEffect?
At first glance, it looks like this: heals and consumes half of the remaining effectAmount each
turn, until either a) the character is fully healed, b) the duration has ended, or c) the
effectAmount has diminished to zero; and then fully heals the character anyway?
But we know from CharacterEffectEvent that this is only called when duration is non-zero, and
duration is only decremented when above zero, so we have no chance of getting here with a duration
of zero or less. (Assuming an effect is not created with a zero or negative duration.)
And the effect amount is diminished by half its value truncated to an int. As long as it started
out positive, it’ll never reach zero. At one, it will stay at one, healing zero hits per turn.
So the tripartite conditional in DoBalmEffect is probably historical in origin, and doesn’t
actually cause the issues we were worried about.
Reality: Trying to Figure Out One Test Takes Forever
#
After a little trial and error, here’s a test that passes:
Scenario:BalmeffectfadescorrectlywhenlimitedbydurationGivenIusethe"minimal"databaseAndIaddplayerandcharacter"TestBalm01"AndIsetthecharacter's max HP to "30"
And I set the character'scurrentHPto"1"AndIgivethecharacterabalmeffectof"20"for"3"turnsAndtheserverisstartedWhenIlogonas"TestBalm01"AndIenterthegameAndafter"0"turnsIhaveacurrentHPof"16"Andafter"1"turnsIhaveacurrentHPof"18"Andafter"1"turnsIhaveacurrentHPof"20"Andafter"1"turnsIhaveacurrentHPof"22"Andafter"1"turnsIhaveacurrentHPof"22"Andafter"1"turnsIhaveacurrentHPof"23"Andafter"1"turnsIhaveacurrentHPof"23"Andafter"1"turnsIhaveacurrentHPof"23"Andafter"1"turnsIhaveacurrentHPof"24"
That seems a little counter-intuitive. To make some sense of it, we need to recall that the effect
triggers immediately on being created, and to know about the ThirdRoundEvent:
#regionTimerRelatedprotectedvoidThirdRoundEvent(objectobj,ElapsedEventArgse){if(this.IsPC&&this.PCState!=Globals.ePlayerState.PLAYING)return;if(!this.IsDead){//TODO: modify stats gain to reflect additional stats due to temporary stat effectsif(this.Hits<this.HitsFull)// gain a point of health{this.Hits++;this.updateHits=true;
A whole set of things happen every third round the character is in the game, including one point
of healing.
So let’s attempt to annotate that test:
And I enter the game
# 1 + 10 (initial effect) = 11 (amount now 10, duration now 3)
And after "0" turns I have a current HP of "16"
# 11 + 5 (effect on initial turn) = 16 (amount now 5, duration now 2)
And after "1" turns I have a current HP of "18"
# 16 + 2 (effect) = 18 (amount now 3, duration now 1)
And after "1" turns I have a current HP of "20"
# 18 + 1 (effect) = 19 (amount now 2, duration now 0)
# 19 + 1 (third round healing) = 20
And after "1" turns I have a current HP of "22"
# ???
It makes sense up to the round where I suddenly have 22 HP. I’ll have to actually add some debug to
figure this one out.
9/30/2016 0:00:06 AM: {Unknown} Round 0: Character TestBalm01_name receives balm effect. effectAmount 20, duration 3.
9/30/2016 0:00:06 AM: {Unknown} Round 0: Character TestBalm01_name received balm effect. Healed 10 HP to make current HP 11, effectAmount now 10.
9/30/2016 0:00:06 AM: {Unknown} Round 0: Character TestBalm01_name receives balm effect. effectAmount 10, duration 3.
9/30/2016 0:00:06 AM: {Unknown} Round 0: Character TestBalm01_name received balm effect. Healed 5 HP to make current HP 16, effectAmount now 5.
9/30/2016 0:00:11 AM: {Unknown} Round 1: Character TestBalm01_name receives balm effect. effectAmount 5, duration 2.
9/30/2016 0:00:11 AM: {Unknown} Round 1: Character TestBalm01_name received balm effect. Healed 2 HP to make current HP 18, effectAmount now 3.
9/30/2016 0:00:16 AM: {Unknown} Round 2: Character TestBalm01_name receives balm effect. effectAmount 3, duration 1.
9/30/2016 0:00:16 AM: {Unknown} Round 2: Character TestBalm01_name received balm effect. Healed 1 HP to make current HP 19, effectAmount now 2.
9/30/2016 0:00:19 AM: {Unknown} Round 3: Character TestBalm01_name received ThirdRoundEvent healing, current HP now 20.
9/30/2016 0:00:34 AM: {Unknown} Round 6: Character TestBalm01_name received ThirdRoundEvent healing, current HP now 23.
9/30/2016 0:00:49 AM: {Unknown} Round 9: Character TestBalm01_name received ThirdRoundEvent healing, current HP now 24.
My expectations are confirmed, but there’s some extra healing going on that I don’t know about. I
looked through the code but couldn’t see what was going on. In a final desperate effort I added
code to print out System.Environment.StackTrace every time the character’s HP changed.
9/30/2016 0:12:23 AM: {Unknown} Round 3: Character TestBalm01_name had their Hits set to 22 (was 20).
9/30/2016 0:12:23 AM: {Unknown} Stack trace: at System.Environment.get_StackTrace()
at DragonsSpine.Character.set_Hits(Int32 value) in
k:\301days\Code\drag-spin-exp\DragonsSpine\GameObjects\GameLiving\Character\Character.cs:line 1000
at DragonsSpine.Effect.StopCharacterEffect() in
k:\301days\Code\drag-spin-exp\DragonsSpine\GameSystems\Effects\Effect.cs:line 555
and there it was. I had somehow missed what was going on in StopCharacterEffect; once the
duration has elapsed, there’s a kind of “catch-up” clause that applies the remaining amount:
caseEffectType.Balm:// healing over timeif(this.effectAmount>0){this.target.Hits+=this.effectAmount;if(this.target.Hits>this.target.HitsFull)this.target.Hits=this.target.HitsFull;}break;default:break;}
I’ll add some debug output there just to make sure:
9/30/2016 0:28:03 AM: {Unknown} Round 0: Character TestBalm01_name receives balm effect. effectAmount 20, duration 3.
9/30/2016 0:28:03 AM: {Unknown} Round 0: Character TestBalm01_name received balm effect. Healed 10 HP to make current HP 11, effectAmount now 10.
9/30/2016 0:28:03 AM: {Unknown} Round 0: Character TestBalm01_name receives balm effect. effectAmount 10, duration 3.
9/30/2016 0:28:03 AM: {Unknown} Round 0: Character TestBalm01_name received balm effect. Healed 5 HP to make current HP 16, effectAmount now 5.
9/30/2016 0:28:08 AM: {Unknown} Round 1: Character TestBalm01_name receives balm effect. effectAmount 5, duration 2.
9/30/2016 0:28:08 AM: {Unknown} Round 1: Character TestBalm01_name received balm effect. Healed 2 HP to make current HP 18, effectAmount now 3.
9/30/2016 0:28:13 AM: {Unknown} Round 2: Character TestBalm01_name receives balm effect. effectAmount 3, duration 1.
9/30/2016 0:28:13 AM: {Unknown} Round 2: Character TestBalm01_name received balm effect. Healed 1 HP to make current HP 19, effectAmount now 2.
9/30/2016 0:28:16 AM: {Unknown} Round 3: Character TestBalm01_name received ThirdRoundEvent healing, current HP now 20.
9/30/2016 0:28:18 AM: {Unknown} Round 3: Character TestBalm01_name received final balm effect. Healed 2 HP to make current HP 22.
9/30/2016 0:28:31 AM: {Unknown} Round 6: Character TestBalm01_name received ThirdRoundEvent healing, current HP now 23.
9/30/2016 0:28:46 AM: {Unknown} Round 9: Character TestBalm01_name received ThirdRoundEvent healing, current HP now 24.
Scenario: Balm effect processes correctly when limited by duration
Given I use the "minimal" database
And I add player and character "TestBalm01"
And I set the character's max HP to "30"
And I set the character's current HP to "1"
And I give the character a balm effect of "20" for "3" turns
And the server is started
When I log on as "TestBalm01"
And I enter the game
# 1 + 10 (initial effect) = 11 (amount now 10, duration now 3)And after "0" turns I have a current HP of "16"
# 11 + 5 (effect on initial turn) = 16 (amount now 5, duration now 2)And after "1" turns I have a current HP of "18"
# 16 + 2 (effect) = 18 (amount now 3, duration now 1)And after "1" turns I have a current HP of "20"
# 18 + 1 (effect) = 19 (amount now 2, duration now 0) # 19 + 1 (third round healing) = 20And after "1" turns I have a current HP of "22"
# 20 + 2 (effect) = 22 (effect completed)And after "1" turns I have a current HP of "22"
And after "1" turns I have a current HP of "23"
# 22 + 1 (third round healing) = 23
And it even passes. One more to make sure we understand how this logic works:
Scenario: Balm effect processes correctly when limited by effect
Given I use the "minimal" database
And I add player and character "TestBalm02"
And I set the character's max HP to "30"
And I set the character's current HP to "1"
And I give the character a balm effect of "10" for "4" turns
And the server is started
When I log on as "TestBalm02"
And I enter the game
# 1 + 5 (initial effect) = 6 (amount now 5, duration now 4)And after "0" turns I have a current HP of "8"
# 6 + 2 (effect on initial turn) = 8 (amount now 3, duration now 3)And after "1" turns I have a current HP of "9"
# 8 + 1 (effect) = 9 (amount now 2, duration now 2)And after "1" turns I have a current HP of "11"
# 9 + 1 (effect) = 10 (amount now 1, duration now 1) # 10 + 1 (third round healing) = 11And after "1" turns I have a current HP of "11"
# 11 + 0 (effect) = 11 (amount still 1, duration now 0)And after "1" turns I have a current HP of "12"
# 11 + 1 (effect) = 12 (effect completed)And after "1" turns I have a current HP of "13"
# 12 + 1 (third round healing) = 13
Ok, that’s one effect down, many to go. Unfortunately database connection issues keep getting in the way of running through
the complete test suite. I’ll need to tackle that once and for all. Tomorrow.