301 Days

A year of gamedev experiments.

Day 62 - Timesavers

| Comments

In which we save time in various ways.

rake isolate[‘day-62’]

I feel a fool for not knowing about this. To optimize auto-regenerating the blog, it makes sense to temporarily move the other posts to a safe place and iterate as if this were the only one. Then bring back the older posts when it’s time to do the final site generation.

Luckily, Octopress’s Rakefile automates this as rake isolate[postname] and rake integrate. That should keep me happy for just a bit longer.

Don’t wait for not

One of my little rules for automated testing is “Don’t wait a fixed time for something to not happen”. If you wait for five seconds, the event you didn’t want will occur at the sixth second. Tests will pass, but the system will fail. Then you wait for ten seconds; then it occurs after eleven seconds. Then you get mad and wait for five minutes. Then your tests are way too slow and don’t get run as often.

This can be a hard rule to follow, especially if the test is “x should never happen”; we can’t just wait forever. But in the case where the test is really “I don’t want x to happen before y”, you should always wait until y rather than a fixed amount of time. So this:

features/steps/vendor_steps.rblink
32
33
34
Then(/^the log does not show any items restocked in stores$/) do
  expect { log_contains('Restocked % store records.') }.to raise_error(Timeout::Error)
end

should become this:

features/steps/vendor_steps.rblink
30
31
32
33
Then(/^the log does not show any items restocked in stores$/) do
  expect(log_not_this_before_that('Restocked % store records.', 'SystemGo',
           'Starting main game loop.', 'SystemGo')).to be_truthy
end
features/lib/server_helper.rblink
14
15
16
def log_not_this_before_that(message1, logtype1, message2, logtype2)
  log_contains(message2, logtype2) && !log_contains_immediate(message1, logtype1)
end

So that this:

1
2
3
1 scenario (1 passed)
9 steps (9 passed)
0m28.315s

can become this:

1
2
3
1 scenario (1 passed)
9 steps (9 passed)
0m6.702s

ColorLog?

The next key in the config file is ColorLog, but a quick code search shows that only having any effect in the enigmatic Windows Forms code that I have no idea about or interest in. Moving on…

RequireMakeRecallReagent

This looks interesting. Recall rings were one of the more useful items in the original game: put it on, do some adventuring, take it off and teleport back to where you put it on. The only catch was that it was one-use. After the teleport, you were holding a simple gold ring. For some it was an emergency-use-only thing; for others, it was the preferred way back to town, loaded down with all of the spoils of combat; for still others, keeping two fingers free meant never having to walk to or from the dungeon.

Because Thieves had a harder time getting around (plenty of Lawful-only areas, and they were Neutral), they could learn a spell to make their own recall rings.

But what does this config option do?

DragonsSpine/GameSystems/Spells/Spell.cslink
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
private void castMakeRecall(Character caster, string args)
{
    if (caster.RightHand != null && caster.RightHand.itemID == Item.ID_GOLDRING)
    {
        caster.RightHand = null;
        caster.RightHand = Item.CopyItemFromDictionary(Item.ID_RECALLRING);
        caster.WriteToDisplay("You cast the spell " + this.Name + ".");
    }
    else if (caster.LeftHand != null && caster.LeftHand.itemID == Item.ID_GOLDRING)
    {
        caster.LeftHand = null;
        caster.LeftHand = Item.CopyItemFromDictionary(Item.ID_RECALLRING);
        caster.WriteToDisplay("You cast the spell " + this.Name + ".");
    }
    else
    {
        if (System.Configuration.ConfigurationManager.AppSettings["RequireMakeRecallReagent"].ToLower() == "false")
        {
            if (caster.RightHand == null)
                caster.RightHand = Item.CopyItemFromDictionary(Item.ID_RECALLRING);
            else if (caster.LeftHand == null)
                caster.LeftHand = Item.CopyItemFromDictionary(Item.ID_RECALLRING);
            else caster.CurrentCell.Add(Item.CopyItemFromDictionary(Item.ID_RECALLRING));
        }
        else
        {

            if (caster.RightHand != null)
            {
                caster.WriteToDisplay("Your " + caster.RightHand.name + " explodes!");
                caster.RightHand = null;
                Rules.DoSpellDamage(null, caster, null, Rules.dice.Next(1, 20), "concussion");
            }
            else if (caster.LeftHand != null)
            {
                caster.WriteToDisplay("Your " + caster.LeftHand.name + " explodes!");
                caster.LeftHand = null;
                Rules.DoSpellDamage(null, caster, null, Rules.dice.Next(1, 20), "concussion");
            }
            else
            {
                GenericFailMessage(caster, "");
            }
        }
    }
}

Ah, the “reagent” is the gold ring. So if this option is set, you need a gold ring in hand to imbue with recall powers. Otherwise the spell fails, exploding something on you unless your hands were empty.

To test this we’ll need the ability to:

  • make a thief character
  • give him the makerecall spell
  • give him a gold ring
  • make him attempt to cast a spell
  • check the contents of his hands

Sounds like a lot of fun test code to write. Tomorrow.


Useful Stuff


Day 62 code - tests

Comments