Lootr

Lootr \lutʁ\ is a simple RPG-like looting system.

Lootr provides a way to organize data commonly named loot in a gameplay context. It helps you to manage which items can be found, in a generic and statisticaly controlled system.

It is heavily inspired from the work of Bob Nystrom http://journal.stuffwithstuff.com/2014/07/05/dropping-loot

A JS version is available here https://github.com/vincent/lootr
A C# version is available here https://github.com/Sparox/LootrCsharp

In Lootr, lootables are organized in a tree of categories and items.

ROOT
├─ Staff
├─ equipment
│  ├─ Glove
│  │  Boots
│  └─ leather
│     │  Jacket
│     └─ Pads
└─ weapons
   ├─ Bat
   └─ Knife

Then, a collection of Drops describe how items are yield from a loot action.

ignore equipment: .5 chances, stack of 1 equipment: .2 chances, stack of 2 equipment: .1 chances, stack of 2

This might yield items in the equipment tree, for example - 1 Boots, once every 2 rolls on average - 2 Glove, once every 5 rolls - 1 Knife, once every 10 rolls

Create a loot bag

Create items.

```rust use lootr::{Lootr, item::Item}; let mut loot = Lootr::new();

loot.add( Item::a("Berries") ); ```

Items can have properties.

```rust use lootr::{Lootr, item::{Item, Props}}; let mut loot = Lootr::new();

let item = Item::from("crown", Props::from([ ("strength", "10"), ("charisma", "+100") ]));

loot.add(item);

// Items can printed // crown{strength=10,charisma=+100}

```

Each level is composed by a list of .items and nested .branchs.

Organize the loot repository by adding branchs

```rust use lootr::Lootr; let mut loot = Lootr::new();

let weapons = loot.addbranch("weapons", Lootr::new()); let armor = loot.addbranch("armor", Lootr::new()); ```

Optionnaly with items

```rust use lootr::{Lootr, item::Item}; let mut loot = Lootr::new();

loot.add_branch("weapons", Lootr::from(vec![ Item::a("Staff"), Item::an("Uzi") ]));

loot.add_branch("armor", Lootr::from(vec![ Item::a("Boots"), Item::a("Socks") ]));

loot.branchmut("armor") .unwrap() .addbranch("leather", Lootr::from(vec![ Item::a("Belt"), Item::a("Hat") ])); ```

Looting

Loot against a loot table, described by a like the following.

```rust use lootr::{ROOT, drops::Drop};

let drops = [ Drop { path: ROOT, depth: 1, luck: 1.0, stack: 1..=1, modify: false }, ]; ```

A builder pattern is also available to ease drops creation.

```rust use lootr::{Lootr, item::Item, drops::DropBuilder}; let mut loot = Lootr::new();

loot.add_branch("weapons", Lootr::from(vec![ Item::a("Staff"), Item::an("Uzi") ]));

loot.add_branch("armor", Lootr::from(vec![ Item::a("Boots"), Item::a("Socks") ]));

let drops = [ DropBuilder::new() .path("armor") .luck(1.0) .build(),

DropBuilder::new()
    .path("weapons")
    .luck(1.0)
    .stack(1..=3)
    .build(),

];

// Loot your reward from a dead monster let rewards = loot.loot(&drops);

// rewards = [ "Berries", "Plates", "Uzi", "Uzi", "Staff" ] ```

Seeded RNG

Lootr.loot_seeded() takes a PRNG arguments to yield items in a consitent and reproductible way.

```rust use lootr::{Lootr, item::Item, drops::DropBuilder}; use rand_chacha::ChaCha20Rng; use rand::SeedableRng;

(0..10).foreach(|f| { let mut loot = Lootr::from(vec![ Item::named("Socks"), Item::named("Boots"), ]); let drops = [DropBuilder::new().build()];

let rng = &mut ChaCha20Rng::seed_from_u64(123);

loot.loot_seeded(&drops, rng);
loot.loot_seeded(&drops, rng);
// ...

// Will always loot Boots, then Socks, then Socks, then Boots ..

}) ```

Modifiers

Lootr.add_modifier() allows to give some Item transformers, call Modifiers.

Modifiers are simple functions that return a new Item from a given one.

```rust use lootr::{Lootr, item::{Item, Props}, drops::DropBuilder}; let mut loot = Lootr::new(); loot.add(Item::a("crown"));

fn with_strength(source: Item) -> Item { source.extend(source.name, Props::from([ ("strength", "10"), ])) }

loot.addmodifier(withstrength);

// // Then, at loot time:

let drops = [DropBuilder::new().modify().build()];

let rewards = loot.loot(&drops);

// rewards = [ crown{strength=10} ] ```

Macros

To make tha building easier, you can use the bag! macro.

```rust use lootr::{bag, Lootr, item::{Item, Props}}; let loot = bag! { @Weapons Knife attack="1" desc="A simple knife", @Wooden BarkShield attack="0" magicpower="10" desc="A wooden shield reinforced with bark, providing magic power", @Staffs WoodenStaff attack="5" magicpower="10" desc="A wooden staff imbued with magic power", CrystalStaff attack="8" magicpower="15" icedamage="10" desc="A crystal staff with ice elemental damage", ElementalStaff attack="12" magicpower="20" thunderdamage="15" desc="An elemental staff with thunder elemental damage", . @Bows ShortBow attack="10" accuracy="10" desc="A short bow with high accuracy", LongBow attack="20" accuracy="20" icedamage="10" desc="A long bow with ice elemental damage", . . @Swords ShortSword attack="10" critical="5" desc="A short sword with increased critical hit rate", LongSword attack="15" critical="10" desc="A long sword with a high critical hit rate", TwoHandedSword attack="20" critical="15" desc="A two-handed sword with a very high critical hit rate", . @Axes BattleAxe attack="12" critical="8" desc="A battle axe with increased critical hit rate", WarAxe attack="14" critical="9" desc="A war axe with a high critical hit rate", . @Mace MorningStar attack="13" critical="7" desc="A mace with increased critical hit rate", Flail attack="16" critical="11" desc="A flail with a very high critical hit rate", . . @Armors Shirt defense="0" desc="A simple shirt", @LightArmor LeatherArmor defense="5" agility="2" desc="Armor made of leather with increased agility", Chainmail defense="8" agility="1" desc="Armor made of interlocking rings with moderate agility", . @HeavyArmor PlateArmor defense="10" agility="-2" desc="Heavy armor made of plates with decreased agility", FullPlateArmor defense="15" agility="-5" desc="Very heavy armor made of plates with greatly decreased agility", . . @Consumables Water healing="2" desc="Just water", @Potion HealthPotion healing="20" desc="A potion that restores a small amount of health", GreaterHealthPotion healing="40" desc="A potion that restores a moderate amount of health", ManaPotion manarestoration="20" desc="A potion that restores a small amount of mana", GreaterManaPotion manarestoration="40" desc="A potion that restores a moderate amount of mana", . @Elixirs ElixirOfStrength strengthboost="5" desc="An elixir that boosts strength", GreaterElixirOfStrength strengthboost="10" desc="An elixir that greatly boosts strength", ElixirOfAgility agilityboost="5" desc="An elixir that boosts agility", GreaterElixirOfAgility agility_boost="10" desc="An elixir that greatly boosts agility", . . };

println!("{}", loot); ```

ignore ROOT ├─ test{} ├─ Armors │ ├─ Shirt{defense="0",desc="A simple shirt"} │ ├─ HeavyArmor │ │ └─ PlateArmor{agility="-2",defense="10",desc="Heavy armor made of plates with decreased agility"} │ │ FullPlateArmor{agility="-5",defense="15",desc="Very heavy armor made of plates with greatly decreased agility"} │ └─ LightArmor │ └─ LeatherArmor{defense="5",desc="Armor made of leather with increased agility",agility="2"} │ Chainmail{agility="1",defense="8",desc="Armor made of interlocking rings with moderate agility"} ├─ Consumables │ ├─ Water{desc="Just water",healing="2"} │ ├─ Elixirs │ │ └─ ElixirOfStrength{strength_boost="5",desc="An elixir that boosts strength"} │ │ GreaterElixirOfStrength{strength_boost="10",desc="An elixir that greatly boosts strength"} │ │ ElixirOfAgility{agility_boost="5",desc="An elixir that boosts agility"} │ │ GreaterElixirOfAgility{desc="An elixir that greatly boosts agility",agility_boost="10"} │ └─ Potion │ └─ HealthPotion{desc="A potion that restores a small amount of health",healing="20"} │ GreaterHealthPotion{desc="A potion that restores a moderate amount of health",healing="40"} │ ManaPotion{mana_restoration="20",desc="A potion that restores a small amount of mana"} │ GreaterManaPotion{desc="A potion that restores a moderate amount of mana",mana_restoration="40"} └─ Weapons ├─ Knife{desc="A simple knife",attack="1"} ├─ Axes │ └─ BattleAxe{attack="12",critical="8",desc="A battle axe with increased critical hit rate"} │ WarAxe{attack="14",desc="A war axe with a high critical hit rate",critical="9"} ├─ Mace │ └─ MorningStar{attack="13",critical="7",desc="A mace with increased critical hit rate"} │ Flail{desc="A flail with a very high critical hit rate",attack="16",critical="11"} ├─ Swords │ └─ ShortSword{critical="5",desc="A short sword with increased critical hit rate",attack="10"} │ LongSword{desc="A long sword with a high critical hit rate",attack="15",critical="10"} │ TwoHandedSword{attack="20",desc="A two-handed sword with a very high critical hit rate",critical="15"} └─ Wooden ├─ BarkShield{attack="0",magic_power="10",desc="A wooden shield reinforced with bark, providing magic power"} ├─ Bows │ └─ ShortBow{accuracy="10",attack="10",desc="A short bow with high accuracy"} │ LongBow{desc="A long bow with ice elemental damage",attack="20",ice_damage="10",accuracy="20"} └─ Staffs └─ WoodenStaff{desc="A wooden staff imbued with magic power",magic_power="10",attack="5"} CrystalStaff{ice_damage="10",desc="A crystal staff with ice elemental damage",magic_power="15",attack="8"} ElementalStaff{thunder_damage="15",desc="An elemental staff with thunder elemental damage",magic_power="20",attack="12"}

Tests

cargo test

Bump version

cargo bump minor