osrlib.Core 0.0.1-alpha

This is a prerelease version of osrlib.Core.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package osrlib.Core --version 0.0.1-alpha
NuGet\Install-Package osrlib.Core -Version 0.0.1-alpha
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="osrlib.Core" Version="0.0.1-alpha" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add osrlib.Core --version 0.0.1-alpha
#r "nuget: osrlib.Core, 0.0.1-alpha"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install osrlib.Core as a Cake Addin
#addin nuget:?package=osrlib.Core&version=0.0.1-alpha&prerelease

// Install osrlib.Core as a Cake Tool
#tool nuget:?package=osrlib.Core&version=0.0.1-alpha&prerelease

OSRlib.NET License: MIT

OSRlib.NET logo

Build status (main) Docs Reference Package
Build Status Documentation API reference NuGet

OSRlib.NET is a .NET Core class library written in C# that you can use as the game mechanics engine for a turn-based computer role-playing game (CRPG).

The osrlib.Core object model and API were designed with the original Bard's Tale and similar CRPGs in mind. The library is appropriate for use in any game modeled after the Dungeons & Dragons Basic/Expert (or B/X) edition or other tabletop RPG in the Old School Renaissance (OSR) style.

Add your own UI (or even CLI) that talks to the OSRlib.NET API, and you've got yourself a turn-based RPG!

⚠️ OSRlib.NET is early in development and several critical systems have yet to be built. To get an idea of what's missing, check out the open issues.

Prerequisites

  • .NET SDK 7.0+

Install the package

dotnet add package osrlib.Core

Getting started

OSRlib.NET provides an object model representing well-known RPG entities like adventures, dungeons, beings (player characters and monsters), encounters, and weapons. It has an event-based interaction model for manipulating these entities, their relationships, and state.

Any game you build with OSRlib.NET will include at least these four core operations, presented here with example code and in typical order of operation:

ℹ️ TIP: You can see these code snippets in context in OSRlib.NET's test project: src/osrlib.Tests/ReadMeTests.cs

Create a character

// Get a ten-sided die ready
DiceRoll roll = new DiceRoll(new DiceHand(1, DieType.d10));

// Roll up a fighter-type character
Being fighter = new Being
{
    Name = "Blarg the Destructor",
    Defense = roll.RollDice(),
    MaxHitPoints = roll.RollDice() + 10
};
fighter.HitPoints = fighter.MaxHitPoints;
fighter.RollAbilities();

// Give Blarg a sweet sword
Weapon magicSword = new Weapon
{
    Name = "Long Sword + 1",
    Description = "A finely crafted sword, its blade dimly glows.",
    Type = WeaponType.Melee,
    DamageDie = new DiceHand(1, DieType.d8)
};
magicSword.AttackModifiers.Add(new Modifier { ModifierSource = magicSword, ModifierValue = 1 });
magicSword.DamageModifiers.Add(new Modifier { ModifierSource = magicSword, ModifierValue = 1 });
fighter.ActiveWeapon = magicSword;

// Now, add the fighter to the player's party
Party playerParty = new Party();
playerParty.AddPartyMember(fighter);

Stock the dungeon

Dungeon dungeon = new Dungeon();

// Create some monsters for an encounter
Being goblin1 = new Being
{
    Name = "Goblin Chieftain",
    Defense = 10,
    HitPoints = 10,
    MaxHitPoints = 10
};
goblin1.RollAbilities();

Being goblin2 = new Being
{
    Name = "Goblin",
    Defense = 5,
    HitPoints = 4,
    MaxHitPoints = 4
};
goblin2.RollAbilities();

// Add the goblins to the monster party
Party monsterParty = new Party();
monsterParty.AddPartyMember(goblin1);
monsterParty.AddPartyMember(goblin2);

// Add the monsters to an encounter
Encounter encounter = new Encounter
{
    EncounterParty = monsterParty,
    Position = new GamePosition(10, 10)
};

// Add the encounter to the dungeon
dungeon.Encounters.Add(encounter);

Subscribe to battle events

The Encounter, like most top-level entities in OSRlib, exposes events to notify subscribers of actions it performs and actions performed on it. Subscribe to events like these and use them as triggers to update your game's user interface or perform other runtime actions.

// OSRlib is heavily event-driven and most top-level classes expose public events.
// Determine when and how to change the state of your game at runtime (prompt for
// target selection, play a sound when a monster is killed, etc.) by subscribing
// to events exposed by objects you create.
encounter.EncounterStarted += (sender, eventArgs) =>
    {
        Console.WriteLine($"Encounter has started! Monsters:\r\n{((Encounter)sender).EncounterParty}");
    };

// Example of subscribing to an event that you might use to update the UI state to notify the player
// or make some other changes within your application.
encounter.EncounterEnded += (sender, eventArgs) =>
    {
        Encounter enc = sender as Encounter;

        if (enc.AdventuringParty.IsAlive)
        {
            Console.WriteLine("Your party has won the battle!");
        }
        else if (enc.EncounterParty.IsAlive)
        {
            Console.WriteLine("Sorry, your party has been vanquished.");
        }
    };

Start the battle

Finally we're ready to let the adventuring party (currently comprised of only one character, Blarg the Destructor) and the encounter party (the evil orcs) battle to the death.

// Encounters can be set to auto-resolve the battle. This is OPTIONAL! In your game, you'd not
// typically set this to true, and instead allow your players to select a target(s) for a character.
encounter.AutoBattleEnabled = true;

// Add the adventuring party to the encounter
encounter.SetAdventuringParty(playerParty);

// Subscribe to some events on the combatants so we can respond to things that happen to them.
List<Being> combatants = encounter.AdventuringParty.Members.Concat(encounter.EncounterParty.Members).ToList();
foreach (Being combatant in combatants)
{
    combatant.SelectedAsTarget += (s, e) =>
        {
            Being attackedBeing = s as Being;
            BeingTargetingEventArgs args = e as BeingTargetingEventArgs;

            Console.WriteLine($"{e.TargetingBeing} attacks {attackedBeing} with their {e.TargetingBeing.ActiveWeapon}...");
        };
    combatant.ActionPerformed += (s, e) =>
        {
            GameActionEventArgs actionArgs = e as GameActionEventArgs;
            GameAction action = actionArgs.Action;

            if (action.Victor.Equals(combatant))
            {
                Console.WriteLine($"{combatant} rolled a {action.AttackRoll} and hit for {action.DamageRoll} points of damage.");
            }
            else
            {
                Console.WriteLine($"{combatant} rolled a {action.AttackRoll} and missed.");
            }
        };
    combatant.Killed += (s, e) =>
        {
            Console.WriteLine($"{((Being)s).Name} was killed!");
        };
}

// Start the battle. This will fire the EncounterStarted event we subscribed to
// above, and since we set this encounter to resolve all combat automatically with
// AutoBattleEnabled, each member of both parties takes turns attacking each other
// until one side has been defeated.
encounter.StartEncounter();

Display event data

The battle resolves fully (because we set Encounter.AutoBattleEnabled = true) and, because we subscribed Being and Encounter events, we can see what transpires during the battle:

Encounter has started! Monsters:
[0] Goblin Chieftain    Hit points: 10
[1] Goblin      Hit points: 4

Blarg the Destructor (18/18) attacks Goblin (4/4) with their Long Sword + 1...
Goblin was killed!
Blarg the Destructor (18/18) rolled a 9 (1d20+1) and hit for 6 (1d8+1) points of damage.
Goblin Chieftain (10/10) attacks Blarg the Destructor (18/18) with their Fists...
Goblin Chieftain (10/10) rolled a 16 (1d20-1) and hit for 0 (1d2-1) points of damage.
Blarg the Destructor (18/18) attacks Goblin Chieftain (10/10) with their Long Sword + 1...
Blarg the Destructor (18/18) rolled a 15 (1d20+1) and hit for 7 (1d8+1) points of damage.
Goblin Chieftain (3/10) attacks Blarg the Destructor (18/18) with their Fists...
Goblin Chieftain (3/10) rolled a 15 (1d20-1) and hit for 1 (1d2-1) points of damage.
Blarg the Destructor (17/18) attacks Goblin Chieftain (3/10) with their Long Sword + 1...
Your party has won the battle!
Goblin Chieftain was killed!
Blarg the Destructor (17/18) rolled a 13 (1d20+1) and hit for 3 (1d8+1) points of damage.

Next steps

This README was a quick intro to a few of the types and operations available in OSRlib. Here are some other resources to help you use OSRlib.NET in your turn-based RPG:

Have fun!

Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.0.4-alpha 96 2/14/2023
0.0.3-alpha 76 2/14/2023
0.0.2-alpha 78 2/14/2023
0.0.1-alpha 91 12/24/2022

Initial NuGet release of OSRlib.NET.