301 Days

A year of gamedev experiments.

Day 71 - Old Age and Poison

| Comments

In which we easily become very old, and have a lot of trouble with poison.


Test every day

Continuing to explore the various ways in which a character is sent to the Underworld…

You are older than “ancient” and lose a die roll

How old is too old?

DragonsSpine/World/World.cslink
68
69
public static int[] AgeCycles = { 14400, 28800, 43200, 57600, 72000, 80000 };
public static string[] age_humanoid = new string[] { "a very young", "a young", "a middle-aged", "an old", "a very old", "an ancient" };

Looks like 80k turns is the “beyond ancient” that this code is addressing:

DragonsSpine/GameSystems/Rules/Rules.cslink
650
651
652
653
654
655
656
657
658
if (ch.Age >= World.AgeCycles[5] && ch.ImpLevel == Globals.eImpLevel.USER)
{
    int grim;
    grim = Rules.RollD(1, 100);
    // if character is lucky add to grim roll...
    if (grim < 5)
    {
        Rules.EnterUnderworld(ch);
    }

Keep in mind that at five seconds per turn, 80k turns is over 100 hours. Further keep in mind that back in the day it was ten seconds per turn, at a minimum $6/hour. By the time your character was very old, you’d invested quite a bit of money in it.

Not too much work to add the test:

features/config_file.featurelink
486
487
488
489
490
491
492
493
494
495
@slow
Scenario: UnderworldEnabled True, ancient character eventually goes there
    Given I use the "minimal" database
    And I set "UnderworldEnabled" in the config file to "True"
    And I add player and character "TestAncientDead01"
    And I set the character's current age to "80001"
    And the server is started
    When I log on as "TestAncientDead01"
    And I enter the game
    Then within "50" turns I see a message "The world dissolves around you."

Main trouble here is that it’s possible to get a false negative: the character can get lucky for 50 consecutive turns and not get sent to the Underworld. Ditto false positives on the opposing test (where we disable the Underworld). Any suggestions on how to handle this are certainly welcome.

Rest when dead with positive karma, in a map with no karma res point, if not a thief

Decided to also tackle a more complex case, which turned out to be more interesting than I had wanted.

For this, we need a death other than burning (to avoid the Underworld cases we’ve already covered); poison seems like a good one.

I added all of the database code necessary to add a Poisoned effect to the PlayerEffect table. Nothing happens. Turns out that effect doesn’t actually get loaded from the database.

So then I try putting poison berries in the character’s hands, and causing the character to eat them. Still nothing happens. I find that the in-hand item actually needs to be set to poisonous there as well.

So I set the poison berries as actually poisonous in-hand, and make the character eat them. Still nothing happens.

Long story short (too late), poison wasn’t fully implemented in this version of the server code. Which is too bad, since there were some fairly interesting aspects of poison in the original game (e.g. drinking wine would make the poison worse, but delay the effect, so you could try to get to a healer).

Stubbornly insisting on the poison berry method, I applied a bit of a fix to the server code; adding an actual poison effect when a character is poisoned:

DragonsSpine/GameObjects/GameLiving/Character/Character.cslink
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
public int Poisoned
{
    get
    {
        if (this.effectList.ContainsKey(Effect.EffectType.Poison))
        {
            return this.effectList[Effect.EffectType.Poison].effectAmount;
        }
        return 0;

    }
    set
    {
        if (this.effectList.ContainsKey(Effect.EffectType.Poison))
        {
            if (value > 0)
            {
                this.effectList[Effect.EffectType.Poison].effectAmount = value;
            }
            else
            {
                this.effectList[Effect.EffectType.Poison].StopCharacterEffect();
            }
        } else {
            Effect.CreateCharacterEffect(Effect.EffectType.Poison, value, this, 20, this);
        }
    }
}

It’s not a full implementation, but at least it makes this work:

features/config_file.featurelink
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
Scenario: UnderworldEnabled True, positive karma non-thief corpse goes there if no karma res point
    Given I use the "minimal" database
    And I set "UnderworldEnabled" in the config file to "True"
    And I remove the karma res point for map "0"
    And I add player and character "TestBadCorpse03"
    And I set the character's current karma to "1"
    And I set the character's current HP to "1"
    And I put poison berries in the the character's right hand
    And I put poison berries in the the character's left hand
    And the server is started
    When I log on as "TestBadCorpse03"
    And I enter the game
    And I eat "berries"
    And within "10" turns I see the message "You are dead"
    And I rest
    Then I saw a message "The world dissolves around you."

A Testy Underworld

Well, things almost worked. I had never made a test Underworld map for the minimal database. In most cases, the character would attempt to transport to the Underworld, be declared “out of bounds”, and be sent back to the initial spawn point.

But in these latest cases, characters sent to the Underworld never even got the prompt back. Their session was simply dead, so much that they weren’t even in the connected PCs list.

Rather than try to chase that down, I decided to finally make a test Underworld map. The necessary location is hardcoded into the server:

DragonsSpine/GameSystems/Globals/Globals.cslink
7
8
9
10
static public class Globals
{
    public const int UNDERWORLD_LANDID = 2;
    public const int PRAETOSEBA_MAPID = 4;

DragonsSpine/GameSystems/Rules/Rules.cslink
4598
4599
// remove character from current location and place at Underworld entrance
chr.CurrentCell = Cell.GetCell(chr.FacetID, Globals.UNDERWORLD_LANDID, Globals.PRAETOSEBA_MAPID, 26, 4, 0);

so we add an entry to the Maps table:

EntireDB-minimal.sqllink
914
915
INSERT [dbo].[Map] ([mapID], [landID], [name], [shortDesc], [longDesc], [suggestedMaximumLevel], [suggestedMinimumLevel], [pvpEnabled], [expModifier], [difficulty], [climateType], [balmBushes], [poisonBushes], [manaBushes], [staminaBushes], [resX], [resY], [resZ], [thiefResX], [thiefResY], [thiefResZ], [karmaResX], [karmaResY], [karmaResZ], [randomMagicIntensity])
   VALUES (4, 2, N'testunder', N'testunder', NULL, 12, 3, 0, 1, 1, 2, 1, 1, 0, 0, 41, 26, 0, 42, 27, 0, 43, 28, 0, 0)

and a nice minimal map file:

maps/testunder.txtlink
1
2
3
4
5
6
7
8
9
10
11
<z>0</z> <x>22</x> <y>0</y> <f>light</f> <n>TestUnder Surface</n> outdoor=true
WWWWWWWWWWWWWWWWWWWWWWWWWW
WW. . . **. . . . . . . WW
WW. . . **. . . . . a0. WW
WW. . . **. . . . . . . WW
WW******. ******. . a1. WW
WW. . . **. . . . . . . WW
WW. . . **. . . . . gg. WW
WW. . . **. . . . . . . WW
WW. . . . . . . . . . . WW
WWWWWWWWWWWWWWWWWWWWWWWWWW

and then everyone’s happy.

1
2
3
48 scenarios (48 passed)
402 steps (402 passed)
9m46.022s

We’re really starting to have a useful set of test steps; we may be able to finally finish up the Underworld cases. Tomorrow.


Comments