Input Needed: API Design for Dungeons (which could inspire future lands, town, etc)

I’m getting close to finalizing the API for Dungeons and wanted to increase visibility so I could get more eyes/input on the design.

Here’s an overview of the project. Click here to jump to the part about the API. I’ve also published this as a README on github if you prefer that.

Dungeons (for Adventurers). A simple procedural dungeon map generator that developers can use to build out games for the Loot ecosystem.

0000000                                        0000000
0003300   Dungeons (for Adventurers)           0003300
0003300     simple procedural map generator    0003300
0004400                                        0004400

Example:
image

See live outputs, visit: https://lootdungeons.vercel.app

Follow along with the project: https://twitter.com/lootdungeons/

More info about Loot: Loot

Principles

  1. This is a primitive. It is designed to play nicely with many other primitives.
  2. Let the game developers/dungeon masters make as many important decisions as possible, for example…
    a. Monsters
    b. Visual style
    c. Spawn points
  3. Expose everything via Solidity API (vs proprietary formats or focusing on the art)

NFT Structure

A Dungeon NFT is made up of three parts:

  1. ID (Number) - Each dungeon has a unique identifier (integer) numbered 1-10000.
  2. Image (svg) - Each dungeon has a unique image representing its layout, rendered in pixel art.
  3. Metadata - Each dungeon has metadata representing it which can be accessed from getters on the smart contract:
    a. id (int) - The ID of the dungeon
    c. dungeon (string) - A string representing walls, walkable floor, interactable objects (e.g. doors), and points of interest.
    d. environment (int) - An integer depicting an environment for the dungeon
    e. name (string) - The name of the dungeon

Developers can choose to ignore any of these.

Dungeon Structure

Dungeons are always square (e.g. 10x10) and can range from 6x6 → 25x25.

Dungeons can be ‘rooms’ (rectangular areas connected by hallways) or ‘caverns’ (sprawling organic shapes). Most dungeon areas are connected but some dungeons have small areas that are not connected (for secret rooms, etc).

Dungeons contain a list of ‘tiles’ which have different charactertics:

  • Walls: Represented by ‘X’
  • Floors: Represented by ’ ’ (where players can walk)
  • Points of interest: Represented by ‘i’
  • Doors: Represented by ‘D’

Dungeons have a name (e.g. ‘Den of the Twins’) to give each one a sense of permanence. Some names will be repeated (e.g. ‘Stony Field’) but more rare names will be unique.

Dungeons have an environment that implies a theme. Our hope is that the environment combined with the name and shape can give each dungeon its own character. Environments are as follows:

  • 0 - Desert Plateau
  • 1 - Forest Ruins
  • 2 - Underwater Keep
  • 3 - Stone Temple
  • 4 - Mountain Deep
  • 5 - Ember’s Glow

image

Developers can choose to ignore any of the above attributes and use whatever is useful.

All other attributes (e.g. monsters, spawn point, etc) to be defined by the developer.

What could I build with this ?

We used a simple API (string with characters) so that developers who build on top of this can mirror that structure and add metadata on top of the dungeon. Your array could indicate where players can find water, where they can ride mounts, etc.

We kept dungeons incredibly simple so developers, dungeon masters, etc so it plays
nicely with other components. We hope that people add Encounters, Monsters, Rewards, Traps, Locks, Environmental Art, Flying, Running, Walking… even full 2D and 3D environments!

We hope that incentives are built over time to reward people that hold dungeons (e.g. a proceeds of the rewards could go to the holders).

This is just the beginning.

Usage

We expect most developers will want to create a 2D array containing to represent the dungeon:
For example:

[
  ['X', 'X', 'X', 'X', 'X'],
  ['X', ' ', ' ', 'X', ' '],
  ['X', ' ', 'X', 'D', ' '],
  ['X', 'D', ' ', ' ', 'X'],
  ['X', ' ', 'i', ' ', 'X'],
  ['X', 'X', ' ', 'X', 'X']
]

Would generate:

You can generate a 2D array from the getDungeon() string by looping through the string.

For example (javascript):

// Assume you start with the string in a variable called 'dungeon'
const size = Math.sqrt(dungeon.length)

let dungeonArray = []
let count = 0

for(let y = 0; y < size; y++) {
  let row = []
  for(let x = 0; x < size; x++) {
    row.push(dungeon[count])
    count++
  }
  dungeonArray.push(row)
}

For more details, see API - getDungeon() below.

API

The dungeon contract exposes the following endpoints to allow developers to use and query dungeons in their games and applications.

We’ll use this dungeon for all examples below:

[
  ['X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'],
  ['X', 'X', 'X', 'X', ' ', ' ', ' ', 'X'],
  ['X', ' ', ' ', 'X', ' ', 'i', ' ', 'X'],
  ['X', ' ', ' ', 'X', ' ', ' ', ' ', 'X'],
  ['X', ' ', ' ', 'X', ' ', ' ', ' ', 'X'],
  ['X', ' ', ' ', ' ', 'D', ' ', 'X', 'X'],
  ['X', ' ', ' ', 'X', 'X', 'X', 'X', 'X'],
  ['X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'],  
  ['X', 'X', 'X', 'X', 'X', 'X', 'X', 'X']
]

image

Dungeon Geometry

getDungeon() (string) - Returns a string representing all tiles in the dungeon as ascii characters. The array uses a zero-based index and the top left corner of the dungeon starts at (0, 0).

Using our example dungeon:
“XXXXXXXXXXXX i XX X XX X XX X XX D XXX XXXXXXXXXXXXXXXXXXXXX”

Each character represents a ‘tile’ which have different characteristics:

  • Walls: Represented by ‘X’
  • Floors: Represented by ’ ’ (where players can walk)
  • Points of interest: Represented by ‘i’
  • Doors: Represented by ‘D’

Developers who embrace this format for other purposes could use whatever ascii characters they want to represent new types of tiles or objects.

Dungeon Attributes

getName() (string) - Returns the name of the dungeon. In our example: Den of the Twins.

getEnvironment() (int) - Returns the environment of the dungeon. There are six total environments and each affects the color of the original SVG. Developers can choose to embrace or ignore these environments. In our example: 0

numDoors() (int) - Returns the number of doors present in the dungeon. In our example: 1.
Useful for: Querying for dungeons with a specific number of doors.

numPoints() (int) - Returns the number of points of interest present in the dungeon. In our example: 1.
Useful for: Querying for dungeons with a specific number of points.

numFloors() (int) - Returns the number of accessible floor tiles in the dungeon. This includes doors and points of interest. In our example: 26
Useful for: Determining probabilities of objects based on the amount of floor space. Making sure a given dungeon is suitable for a given party size.

size() (int) - Returns the overall size of the dungeon. In this case, we have an 8x8 dungeon, so in our example: 64
Useful for: Querying for dungeon based on size

Note: Dungeons are always square. Developers can also determine the size of the dungeon by taking the sqrt of the length of the string

5 Likes

Regardless of what you do you’re going to piss off someone somewhere. I like the effort the tokenomics apply toward inclusion and maintained value. More ideas:

Mint Allocation Details
There’s likely to be a gas rush. You could go the contributory meritocratic route and do a pseudo-timestamp model for who contributed before Dungeons were cool. Dungeons Hipsters, as it were.

You could also keep a few pre-emptively set aside for a mini-grant that you use to sell for liquidity to incent other modules to integrate the project.

Acknowledgment

  • Acknowledging OG Loot holders
    • For inclusion consider mLooters get some middle pricing – like 0.05 or something
  • That .1 Eth is brutal for normies… But also not insignificant for VC posers, so maybe I like it…

I know some don’t like or even dislike the mLooters but by giving them a middle tier you acknowledge them while still acknowledging they’re not OGs. It’s your choice ultimately, I’m not married to the idea.

Inflation / Deflation
You create a constant expectation of inflation. I really had to think about that.

That makes me think the asset price wont balloon because users know their item loses value
daily.

However, it also makes me think that you have a content factory constantly cranking out new stuff. The content production pipeline is the lifeblood of live operations; staying ahead of player behavior is what passes or fails live games. Players always find a way to stay ahead – but it’s a motherfucking boolean for Dungeons lol.

Provided that we get people onboard integrating the Dungeons into some form of consumable content I have 0 concerns. Hell, maybe if someone “beats” a dungeon (however that manifests) it gets retired from the list somehow. Narratively: “the dungeon is cleared and no longer a threat”. Functionally: we get persistent consistent inflation plus a straightforward potential deflationary mechanism.

It’s edgy, and requires work, but I like it.

Inflation / Deflation: Part Deux

After this, two dungeons will release per week:

One to a random wallet that holds OG Loot. The loot holder will need to pay 0.1ETH to mint this new dungeon. If the dungeon goes unclaimed for 24 hours, it becomes available for mint by anyone for 1ETH.

One open to anyone for 1ETH. This will allow new users to have a chance at owning a dungeon.

Like the required activity / lotto; and it functionally encourages holding. This synergizes with the inflation well, since it removes liquidity without encouraging sells.

Is 2/10000 daily odds good for a lottery?

I want to say so but interested in hearing others’ thoughts. My only gambling is crypto and honestly if you read a lot the odds are pretty good.

BIG ALSO: this is new code outside of the template.
I don’t know solidity, but in my experience this is the kind of thing someone would find an exploit for to give themselves all the things and fuck the project.
For anything non-standard I would request for an audit early, possibly pre-mint.

How are you surfacing that someone has the golden ticket and wins the weekly prize?
A readContract function call would be the simplest way I can think of.

Again the pricing! 1 Eth for the newbies HOLY. I get it though. Bloody moneygrubbing VC moochers (whom are admittedly some of my irl friends but still). An intermediary element here would be a mid-tier for alt-loot projects at .66 ish.

Your Alt-Loot Thoughts
…How do you actually feel about xLoot/mLoot/SynthLoot/etc. ? I view them as a second-class citizens in a first world nation; they deserve the worst of the best. Non-holders on the other hand are foreigners and should pay hefty taxes to live in OUR GREAT NATION. This becomes more pronounced as time passes and the alt-loots progress to a price floor similar to the current Loot price floor.

Will contribute some more fleshed out thoughts later but why the grid size limitations? Is this a memory issue because we’re in solidity? It leaves us with pretty low resolution maps, hard to do things like wider corridors, more complex rounded shapes etc. Will some of these limits rise when ETH goes to proof of stake? Let’s make sure the base format can scale somewhat.

I like having names on them, I think it adds flavor and makes it more than a pure data format.

I am probably an outlier in that I would distribute heavily to non loot holders and make everything much cheaper. There’s a big rich get richer dynamic and all the loot is getting into hands of speculators not builders or players. If everything just gets air dropped to the rich few this community dries up imo. I think we need new things to bring new people into the community, not reward the very small group who either got in early or could drop 30k worth of eth on secondary.

I was thinking something similar about the inflationary/deflationary dynamic–if the use of Dungeons is expanding, adding new dungeons consistently seems great (kind of like Nouns project–the number that will be added is consistent and known by the community), but also I was curious about adding a “deflationary” element, where old dungeons are retired/burned so the supply doesn’t get too big. (but also having new elements to play with is just great.)

Also, I am a total noobie without Loot, and I agree that even the initial .1 entry will actually still be high for a lot of people. I wonder if there are ways to make Dungeons accessible to individuals who just missed the Loot boat but are interested in the community while avoiding gas wars/VC/flippers…though that seems like an issue all projects are facing right now

@bobolo333

Also, I am a total noobie without Loot, and I agree that even the initial .1 entry will actually still be high for a lot of people. I wonder if there are ways to make Dungeons accessible to individuals who just missed the Loot boat but are interested in the community while avoiding gas wars/VC/flippers…though that seems like an issue all projects are facing right now

I am viewing dungeons a bit like ‘Land’ in that hopefully collecting them will generate value over time (e.g. perhaps you earn AGLD when people visit your dungeon?) If this is the case, even 0.1ETH should be trivial compared to the monthly earnings possible from a dungeon.
I’m trying to learn from what I’ve seen with Art Blocks pricing over time - scarce resources quickly become impossible to enter for anyone and even new mints result in insane gas wars because the upside to flip/sell is so high.

1 Like

Hey folks - I’ve updated the post to remove the drop mechanics and anything other than the spec for dungeons (core data structure and API). I’m going to copy/paste the responses about drop structure into the original Dungeons thread so we can keep talking about how we release my specific implementation of the API.

@matto-matto

why the grid size limitations? Is this a memory issue because we’re in solidity? It leaves us with pretty low resolution maps, hard to do things like wider corridors, more complex rounded shapes etc. Will some of these limits rise when ETH goes to proof of stake?

This is primarily due to storage limitations. My thinking so far is that developers can choose their granularity of how to interpret each point on the grid. E.g. Each grid tile could be zoomed in and reference another dungeon or sub-map that is smaller. I don’t actually think the grid size needs to be capped in the standard (e.g. you could in theory have multiple strings concat’d together between maps). Perhaps this is specific to my implementation of the standard.

I do think there’s a question or whether we should support non-square grids (e.g. allow devs to specify a width/height). The upside is lots more variation in the base grid design. The downside is you’ll always need at minimum 2 API calls e.g. getdungeon() and getWidthHeight() to assemble the 2D array representing the dungeon.

I think that having “layered” maps where you consider them as different levels of zoom is a good answer, but I also think that something like 8x8 map size you’re working on now feels too small for me for all but the simplest gameplay.

Take a look at Blitmaps for example, this is all on-chain and does 32x32 images in 4 colors: Info - Blitmap

Basically my desire is that we try to start with the biggest grid size we can reasonably support for now. Laying down a foundation that doesn’t look toward future improvement in terms of memory, speed etc would be unfortunate.

2 Likes

I think we are in agreement. The map size limit was related to my personal project (not the format/standard).

Here’s an updated proposal after taking into account feedback in this thread and from discord:

Maps (v0.2)

Proposed API for building maps compatible with the loot ecosystem.

Objective: Define a common format for maps so game designers and developers can build maps that are compatible with Loot.

Principles

We aim to follow the principles set down by Loot:

  1. This is a primitive. It is designed to play nicely with many other primitives.
  2. Let game developers/dungeon masters make as many important decisions as possible, for example…
    a. Monsters
    b. Visual style
    c. Spawn points
  3. Expose everything via Solidity API (vs proprietary formats or focusing on the art)

NFT Structure

A Map NFT is made up of three parts:

  • ID (Number) - Each map has a unique identifier (integer)
  • Image (svg) - Each map has a unique image representing its layout.
  • Metadata - Each map has metadata representing it which can be accessed from getters on the smart contract:
  • a. id (int) - The ID of the map
  • b. layout (string) - A string representing walls, walkable floor, interactable objects (e.g. doors), and points of interest.
  • c. environment (int) - An integer depicting an environment for the map
  • d. name (string) - The name of the map

Developers can choose to ignore any of these.

To see a live example, view the project @threepwave/dungeons (coming soon)

Map Structure

Maps are always square (e.g. 10x10) but the geometry inside can be any shape. Maps can be any size >= 4x4.

Maps must contain at least one ‘walkable’ area that is at least 2 tiles.

Maps contain a list of ‘tiles’ which have different charactertics:

  • Walls: Represented by ‘0’
  • Floors: Represented by ‘1’ (where players can walk and move around)
  • Points: Represented by ‘2’
  • Doors/Encounters: Represented by ‘3’

Maps have a name (e.g. ‘Den of the Twins’) to give each one a sense of permanence. Some names will be repeated across the ecosystem, which is ok.

Maps have an environment (e.g. ‘2’) to give a sense of ambiance. This is akin to a ‘tileset’ in 2D RPG’s.

Developers can choose to ignore any of the above attributes and use whatever is useful.

All other attributes (e.g. monsters, spawn point, etc) to be defined by the developer or game designer via separate entities.

What could I build with this ?

We focused on a simple API (uint256) so that developers/designers can easily copy it and start building their own maps.

This format supports both generative maps (see Dungeons) and manually/offline created maps. It is purposefully simple to allow for tons of interpretation on the format.

Other entities could lay metadata on top of this map to indicate where players can find water, where they can ride mounts, etc.

We hope that people add Encounters, Monsters, Rewards, Traps, Locks, Environmental Art, Flying, Running, Walking… even full 2D and 3D environments!

We hope that incentives are built over time to reward people that hold maps (e.g. a proceeds of the rewards could go to the holders).

This is just the beginning.

Usage

We expect most developers will want to create a 2D array containing to represent the dungeon: For example:

[
  ['0', '0', '0', '0', '0'],
  ['0', '1', '1', '0', '1'],
  ['0', '1', '0', '2', '1'],
  ['0', '2', '1', '1', '0'],
  ['0', '1', '3', '1', '0'],
  ['0', '0', '0', '0', '0']
]

Would generate:

You can generate a 2D array from the getLayout() int by looping through the integer, character-by-character.

For example (javascript):

// Assume you start with the integer API call
let map = String(getLayout())
const size = Math.sqrt(map.length)

let mapArray = []
let count = 0

for(let y = 0; y < size; y++) {
  let row = []
  for(let x = 0; x < size; x++) {
    row.push(map[count])
    count++
  }
  mapArray.push(row)
}

For more details, see API - getLayout() below.

API

The dungeon contract exposes the following endpoints to allow developers to use and query dungeons in their games and applications.

We’ll use this dungeon for all examples below:

[
  ['0', '0', '0', '0', '0', '0', '0', '0'],
  ['0', '0', '0', '0', '1', '1', '1', '0'],
  ['0', '1', '1', '0', '1', '3', '1', '0'],
  ['0', '1', '1', '0', '1', '1', '1', '0'],
  ['0', '1', '1', '0', '1', '1', '1', '0'],
  ['0', '1', '1', '1', '2', '1', '0', '0'],
  ['0', '1', '1', '0', '0', '0', '0', '0'],
  ['0', '0', '0', '0', '0', '0', '0', '0'],  
  ['0', '0', '0', '0', '0', '0', '0', '0']
]

dev-example

Map Geometry

getLayout() (int) - Returns a uint256 representing all tiles in the map as integers. The top left corner of the map starts at (0, 0), which corresponds to the first number in the integer.

Using our example map:
“00000000000013100110111001101110011011001112100011000000000000000000000”

Each character represents a ‘tile’ which have different characteristics:

  • Walls: Represented by ‘0’
  • Floors: Represented by ‘1’ (where players can walk)
  • Interactables: Represented by ‘2’
  • Points: Represented by ‘3’

Dungeon Attributes

getName() (string) - Returns the name of the map. In our example: Den of the Twins.

getEnvironment() (int) - Returns the environment of the map. There are six total environments and each affects the color of the original SVG. Developers can choose to embrace or ignore these environments. In our example: 0

numInteractables() (int) - Returns the number of ‘interactables’’ present in the dungeon. The definition of interactable is purposefully left vague to be interpreted by the developer. In our example: 1.
Useful for: Querying for maps with a specific number of doors.

numPoints() (int) - Returns the number of points present in the map. In our example: 1.
Useful for: Querying for maps with a specific number of points, determining how many spawn points are listed by default.

numFloors() (int) - Returns the number of accessible floor tiles in the map. This includes interactables and points of interest. In our example: 26
Useful for: Determining probabilities of objects based on the amount of floor space. Making sure a given map is suitable for a given party size.

size() (int) - Returns the overall size of the map. In this case, we have an 8x8 dungeon, so in our example: 64
Useful for: Querying for maps based on size

Note: Maps are always square. Developers can also determine the size of the maps by taking the sqrt of the length of the int

License

The Maps API is licensed under a CC-0 (Free use) license (see: LICENSE.md).

Anyone can take this API and produce as many interesting maps as they see fit.

Everyone has read access to every map. Therefore, maps can be used by anyone, in any app/game/context. However they want.

I hope that over time, people will build incentives for maps holders but that is not baked into the contract or the design.


Going forward, I’ll keep this up to date in this repo, which will eventually include sample contracts so people can start making maps asap: https://github.com/threepwave/loot-maps

1 Like

This latest version looks good to me! Thanks for sharing.

I think the key concept here is that the map generator and map data format are two independent things.

1 Like

Awesome! I’d be interested in building graphically on top of this.

2 Likes