Public interface

MenuAdventuresModule
MenuAdventures

A module for creating text adventures based on menus.

julia> using MenuAdventures

julia> using MenuAdventures.Testing

julia> import MenuAdventures: ever_possible, is_transparent, is_vehicle

julia> @universe not_a_struct
ERROR: LoadError: ArgumentError: Cannot parse user struct definition
[...]

julia> @universe struct Universe <: AbstractUniverse
        end;

julia> @noun struct Room <: AbstractRoom
            already_lit::Bool = true
        end;

julia> @noun struct Person <: Noun
        end;

julia> @noun struct Key <: Noun
        end;

julia> ever_possible(::Take, ::Reachable, ::Key) = true;

julia> ever_possible(::UnlockOrLock, ::Inventory, ::Key) = true;

julia> @noun mutable struct LockableDoor <: AbstractDoor
            key::Noun
            closed::Bool = true
            locked::Bool = true
        end;

julia> ever_possible(::OpenOrClose, ::Reachable, ::LockableDoor) = true;

julia> ever_possible(::UnlockOrLock, ::Reachable, ::LockableDoor) = true;

julia> @noun mutable struct Chest <: Noun
            key::Noun
            closed::Bool = true
            locked::Bool = true
        end;

julia> ever_possible(::OpenOrClose, ::Reachable, ::Chest) = true;

julia> ever_possible(::UnlockOrLock, ::Reachable, ::Chest) = true;

julia> ever_possible(::PutInto, ::Reachable, ::Chest) = true;

julia> @noun struct Car <: Noun
        end;

julia> ever_possible(::GoInto, ::Immediate, ::Car) = true;

julia> is_transparent(::Car) = true;

julia> is_vehicle(::Car) = true;

julia> cd(joinpath(pkgdir(MenuAdventures), "test")) do
            check_choices() do interface
                you = Person(
                    "Brandon",
                    description = (universe, self) -> "What a dork!",
                    grammatical_person = second_person,
                    indefinite_article = "",
                )
                entrance = Room(
                    "the entrance",
                    description = (universe, self) -> "The entrance to the castle",
                    indefinite_article = ""
                )
                small_key = Key("small key")
                large_key = Key("large key")
                chest = Chest("chest", small_key)
                universe = Universe(
                    you,
                    introduction = "Welcome!",
                    interface = interface
                )
                universe[entrance, Room("the castle", indefinite_article = "")] = LockableDoor("door", large_key), West()
                universe[entrance, you] = Containing()
                universe[entrance, small_key] = Containing()
                universe[entrance, chest] = Containing()
                universe[chest, large_key] = Containing()
                universe[entrance, Car("car")] = Containing()
                universe
            end
        end
true
source

Core interface

MenuAdventures.AbstractRoomType
abstract type AbstractRoom <: Location end

An abstract room.

In addition to the required fields for @noun, you must also include an already_lit::Bool field for an AbstractRoom.

source
MenuAdventures.AbstractUniverseType
abstract type AbstractUniverse end

Contains all the information about the game universe.

You will need to create your own AbstractUniverse subtype. See @universe for an easy way to do this.

The universe is organized as an interlinking web of Locations connected by Directions. Each location is the root of a tree of Nouns connected by Relationships.

You can add a new thing to the universe, or change the location of something, by specifying its relation to another thing:

universe[parent_thing, thing] = relationship

You can add a connection between locations too, optionally interspersed by a door:

universe[origin, destination, one_way = false] = direction
universe[origin, destination, one_way = false] = door, direction

By default, this will create a way back in the MenuAdventures.opposite direction. To suppress this, set one_way = true

source
MenuAdventures.AnswerType
Answer(text::String, object::Any)

An answer has two fields: text, which will be how the option is displayed in a menu, and object.

object might be a noun, direction, trigger, or even a question.

source
MenuAdventures.NounType
abstract type Noun end

You must make your own custom Noun subtypes for almost everything in your game.

See @noun for information about required fields. Nouns are additionally characterized by the following traits and methods:

The following IOContext components will be respected when showing nouns:

  • :capitalize::Bool => false
  • :known::Bool => true, set to false to include the indefinite article if it exists.
  • :is_subject => true, whether the noun is the subject of a clause. If this is set to false, you must also include
  • :subject::Noun, the subject of the clause.
source
MenuAdventures.@nounMacro
@noun user_definition

Automatically add MenuAdventures.NOUN_FIELDS to a Noun struct definition, including sane defaults.

Adds the following fields and defaults:

  • name::String
  • plural::Bool = false
  • grammatical_person::GrammaticalPerson = third_person, see third_person
  • indefinite_article::String = "a"
  • description = (universe, self) -> ""

Set indefinite_article to "" for proper nouns. description should be a function which takes two arguments, the universe and the thing itself, and returns a description.

source
MenuAdventures.SentenceType
Sentence(action::Action; argument_answers = Answer[])

A sentence has two fields: action, the Action to be taken, and argument_answers, the arguments to the action.

Arguments will be returned as Answers. The subject is implicitly universe.player.

source
MenuAdventures.@universeMacro
@universe user_definition

Automatically add the required fields to an AbstractUniverse struct definition, including sane defaults.

Adds the following fields and defaults:

  • player::Noun. The player will typically be in second_person.
  • interface::TTYTerminal = terminal
  • introduction::String = ""
  • relationships_graph = MetaGraph(DiGraph(), Label = Noun, EdgeMeta = Relationship)), the Relationships between Nouns.
  • directions_graph = MetaGraph(DiGraph(), Label = Location, EdgeMeta = Direction)), the Directions between Locations.
  • choices_log::Vector{Int} = Int[] saves all choices the user makes

See AbstractUniverse for more information.

source

Directions

Relationships

Domains

Actions

MenuAdventures.ActionType
abstract type Action end

An Action the player can take.

To create a new action, you will need to add methods for

Note that the order arguments are printed in need not match the order they are listed. However, the order of arguments for MenuAdventures.argument_domains must match the order of arguments for MenuAdventures.print_sentence.

Most importantly, define:

function (::MyNewAction)(universe, arguments...) -> Bool

which will conduct the action based on user choices. Return true to end the game, or false to continue onto the next turn. You can overload Action calls for a Noun subtype. Use Core.invoke to avoid replicating the Action machinery.

source
MenuAdventures.PutIntoType
PutInto()

Put something from your Inventory into something Reachable.

An Action. By default,

ever_possible(::PutInto, ::Inventory, anything) = true

that is, it is always possible to put something from your inventory into a container, and

ever_possible(::PutInto, ::Reachable, ::AbstractRoom) = true

that is, all rooms act like containers.

source

Grammatical persons

MenuAdventures.third_personConstant
third_person

Third person (e.g. he, she, it).

In games, everything is typically third person except for the player and narrator.

source

Miscellaneous

MenuAdventures.VerbType
Verb(base; third_person_singular_present = string(base, "s"))

Create an English verb.

Use subject_to_verb to get the form of a verb to agree with a subject. Unexported verbs include MenuAdventures.DO and MenuAdventures.BE.

source