Git Product home page Git Product logo

narrat-engine's Introduction

πŸš€ Narrat

example workflow Netlify Status.

Narrat is a game engine for making interactive narrative RPGs packed with features.. Create your game by editing with a Simple scripting syntax. It supports Skills with skill check rolls, an Items inventory, and has a Quests System. The script system is very powerful and allows branching choices, functions, variables and conditions.

Getting Started

Go to the Getting Started Guide.

Try the engine directly in your browser (experimental, only works on desktop):

Open in Codeflow

Or try editing the default narrat game easily:

Try editing the default game

With Codeflow you can test Narrat quickly without setting up an IDE

More info on the narrat website. There is an online demo available to try in the browser.

Development (for contributors)

Note: Narrat is a monorepo using pnpm as package manager. The packages folder contains the individual packages, including the main narrat package.

Narrat is written in TypeScript. It uses the Vue framework for front-end development. It also uses pinia for state management

Requirements

  • node.js (Downloading LTS is fine)
  • pnpm. This Narrat repo is a monorepo using pnpm workspaces to easily manage the different packages.
  • A text editor capable of editing a TypeScript and Vue.js project. Recommended: VSCode and the extension Volar for Vue.

Running the engine for development

  1. Install dependencies

pnpm install

  1. Run the demo

pnpm dev

This starts the engine on the demo page with the example.narrat demo script loaded for testing.

Developping narrat specifically

If developping on the narrat package, it's better to go inside the narrat package folder and run commands there.

Multiple demo games

There are multiple demo games and it's possible to add new ones. For example npm run rpg will run the rpg demo. Demos are in the examples folder.

Running a specific demo is done by changing an environment variables. See the scripts in package.json for how to run a specific demo.

Available pnpm commands in narrat

  • start: Start the demo for development
  • build: The main build command, builds the library in a state ready to use or publish.
  • test: Unit tests, used in CI
  • dev: Same as start
  • generate-types: Generates .d.ts type declaration files for the library (auto run on builds)
  • fix-type-aliases: Uses tsc-alias to auto-replace path mappings in the built files, as the TypeScript compiler doesn't do it. (auto run at the end of builds)
  • lint: Runs the linter on the code (used in CI)
  • check-types: Validates TypeScript types.

CI info

The following tasks run on CI, and CI is required to pass for pull requests to be merged:

  • Linting
  • Engine build
  • Unit tests (currently only the parser is unit tested)
  • TypeScript type checking.

Please make sure your code passes lint/tests/build/type checks before opening a PR.

Working on the code

The narrat codebase isn't currently documented, outside of the usage docs. The code is fully written in TypeScript though and should be clearly separated enough to be approachable.

Engine architecture

The narrat engine runs primarily as a Vue app. All the UI in the game is made of Vue components, and when a game uses narrat it effectively creates a Vue app.

App state

The engine's state is stored in pinia. It is split in separate pinia modules for different areas of the engine. They can all be found in the src/stores folder.

  • audio: handles playing and stopping audio and music
  • dialog: Receives and stores all lines of dialogue created by game scripts which appear at the right of the game in the main interactive narrative view.
  • hud: Stores stats which are displayed on the hud
  • inventory: Manages the player's item inventory
  • main: Manages app-level logic and other generic systems like startup/reset/load/save
  • notifications: Manages notifications which appear at the top of the screen
  • quests: Manages quests and objectives
  • rendering: Manages the layout's state for dynamically placing and resizing the game UI
  • skills: Manages player skills and xp. Also store skill check states
  • vm: Manages the virtual machine which runs the scripting language and controls the game flow.

All stores can be accessed anywhere in the code with the use hooks (example: useVM()).

Scripting language (VM and parser)

The scripting language for narrat runs in a virtual machine which has its state stored in the Vue app with Pinia.

The narrat scripting language relies on the parser and the vm. The code for the parser is mainly in src/vm/vm-parser.ts. The parser is in charge of taking the .narrat files as input, and returning a tree of parsed expressions that the engine can use.

The actual VM is what runs narrat games, and generally controls the flow of the application. The general logic for the vm is in the vm store, but there is also some logic sprinkled in the src/vm file.

All the actual operations available to the scripting language (like talk) are defined as Command plugins. The built-in commands included in the language by default are in the src/vm/commands folder. More command can be added by games by registering plugins.

Adding new commands

Language commands are defined in the vm/commands folder. It should be easy to add new commands to the game engine by looking at the existing commands as an example. New command plugins are included in the vm/commands/index file.

Config file

The main config file has a type defined in src/config.ts. The vast majority of the config options for narrat are passed in this config files, which games must provide to the engine when starting. New options for the engine should be added to the type for the config file, and also have their default value included in the src/defaultConfig.ts file.

Engine code can access the config with the getConfig() function.

Plugin system

There is now a plugin system developers can use to add new functionality to narrat (more documentation soon). See the narrat-bitsy plugin for an example.

(Note: The narrat-bitsy plugin hasn't been updated for 2.0.0 and might be a bit out of date).

Docs

The documentation website uses Vitepress. Files are in markdown and can be edited. Making a pull request or pushing to master will deploy the change.

To run the docs, open a terminal at the root of the monorepo (not in the docs folder) and run:

pnpm run docs:dev

The docs also use:

  • highlight.js for custom syntax highlighting
  • code-input to turn syntax highlight editable for the live narrat preview in the docs

Assets used in demo games

The rest is handmade.

narrat-engine's People

Contributors

alisonselby avatar dependabot[bot] avatar digitalpoppy avatar jornvandebeek avatar liana-p avatar nophinou avatar pinkpenguin418 avatar slashscreen avatar solarisfire avatar william-f99 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

narrat-engine's Issues

[REQUEST] Ability to override built-in strings

Is your feature request related to a problem? Please describe.
The engine contains various built-in strings that can't be customised by games. For example title of skills popup, quest details etc.

Ideally all those strings should be configurable in some way

Describe the solution you'd like
Add new config keys in relevant config files to override various default strings contained in the engine, and make the engine use them if present

This will probably be an ongoing piece of work as there are many places with "hardcoded" strings, so anyone can do a small amount of them without needing to do all of them at once

[REQUEST] More example games

Is your feature request related to a problem? Please describe.
We need more example games or demo of script features

Describe the solution you'd like
The new narrat-examples repo is meant to collect standalone example projects. This is a good place to add new examples.

[REQUEST] Label that runs on game reload

Is your feature request related to a problem? Please describe.
If a game edits values in the config, those values won't persist as the game config would be reloaded on game launch.

Having a label that is ran on game load would allow games to run bits of script to setup whatever needs to be set up, like overriding bits of config.

Describe the solution you'd like
Nothing specific, it should definitely run and not jump to not break the existing flow

[REQUEST] Allow usage of "if" conditions in choices that have a skill roll

Is your feature request related to a problem? Please describe.
Sometimes we want a skill roll choice which also has an if condition

Describe the solution you'd like
This is currently not possible because the code in the choice command looks for a line starting either with if or roll.

As the line of code to parse would get pretty complex combining roll and if syntax, this issue should be tackled after #61 and we should only allow usage of if for "simple" roll commands that have the skill check config in the config file. This will avoid parsing a nightmarish potential of options.

Additional context
Requested by people

Manual Save Broken

Describe the bug
When doing a manual save the game stops working. We receive the following error in the debugger "Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'layers')". Identified the potential culprit to be the variable "save.screen.layers[0]" in the "save-slot-ui.vue" file on line 77 and 78. It should likely be "save.screens.layers[0]"

To Reproduce
Steps to reproduce the behavior:

  1. In code have a line that uses save "Manual Save 1"
  2. Run that line.

Expected behavior
I'd expect the manual save dialog to appear.

Screenshots
No screenshots available

Browser
Running this on latest version of Chrome.

[BUG] Items don't appear at all if they have no category, and the game has no default category

Describe the bug
Items don't appear when they don't have a category

To Reproduce
Steps to reproduce the behavior:

  1. If an item has no category, it doesn't appear
  2. There is not default category if it's not explicitly added to the items file
  3. The game template files don't have categories setup (because it used to automatically create the default if none provided), so they currently have items not appearing

Expected behavior

  • If no category exists, it should create a default one
  • If an item has no category, it should default to the default one
  • While we're there, there should be an option in the items file to specify a different id for the default category.

[REQUEST] System to make item shops easier

Is your feature request related to a problem? Please describe.
Buying items is a common need in games with items.

Doing so involves creating choice menus with conditions and checking currency/removing cost etc, which is a lot of manual work. Would be nice to have an engine feature for this.

Describe the solution you'd like
Shops could be defined in yaml files that would list available items, price, and which currency they use (from hud stats that can be used as currency).
Then creating a shop could be an option in choice instructions to use the shop and populate the choice menu with the items in the shop

More complex use cases might want to manually add/delete items from shops though, so maybe the shop should take an array of items, and it's up to the game to either load that from a yaml file, or generate it dynamically.

Then shops could show options to purchase item with name/icon of the item, cost, greyed choice if not enough money etc, and automatically handle taking the money and adding the item

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

[REQUEST] Skill checks config file

Is your feature request related to a problem? Please describe.
Skill checks have a lot of complicated options already, and we've been avoiding adding more because it would make scripts and the roll command too complex. For example right now the last option of the roll command is a string of either hideAfterRoll or repeatable, but this won't scale very well.

We should add a way to configure specific skill checks in a yaml config file, like we do for other things, to allow for more complex options.

Describe the solution you'd like
Skill checks should still work the same way as before where they can be done directly using a roll, but setting specific complex ones up in a config file should be an alternative. For example, in a yaml file:

skillChecks:
  mySkillCheckId:
    skill: agility
    difficulty: 10
    hideAfterRoll: false
    repeatable: true
  myOtherSkillCheckId:
    //... and so on

Using this config, the places in the code that run skill checks should find out the options for the roll if they exist. Then, usage in a script could simply be:

roll mySkillCheckId

It's important that we don't force the user to put every skill check in the config file though as it would be tedious. this config file should be optional for complex skill checks that people want to keep in a config file, or for future advanced roll options that couldn't fit in a one-line command.

[REQUEST] Add a speed slider for autoplay text advance speed

Is your feature request related to a problem? Please describe.
Players may want to customise autoplay speed in the game beyond the speed set by the game developer

Describe the solution you'd like
The systems menu should have a slider similar to the volume one, with the min, max and default value setup in the game's config

[REQUEST] Item descriptions render as HTML

Is your feature request related to a problem? Please describe.
When providing text descriptions for items, it's occasionally helpful to format that text with line endings, horizontal rules, list, blockquotes, etc. For example, an email item could contain the text of the email, or a flyer can have it's text in the description too. This allows for reading important information, even when a choice is presented: you wouldn't have to use the item to generate it's content in the dialog, and you can save that use action for something else.

Describe the solution you'd like
Have the text rendered in the item description as HTML instead of text. I suspect it's a matter of adding v-html to the element that renders it, currently .item-description-container > .item-right > p

Would also like the paragraph element swapped with a div, so that the p element can be used inside.

[REQUEST] Display/graphics settings for desktop builds

Is your feature request related to a problem? Please describe.
Most games have display settings to toggle fullscreen mode, change window size etc. Narrat games should have this by default for newly created projects

Describe the solution you'd like
Electron already has all the functionality to use fullscreen, windowed, borderless etc as far as I know.

To make this work, we'd need to update the template to have functions in the electron config that can handle changing display mode appropriately, and add a way for the engine to call those electron functions if present.

Then we can add those options in the game settings.

Games should also be able to configure in settings whether they want to allow players to change the settings.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

[REQUEST] Tooltip showing skill roll info when hovering after success/failure

Is your feature request related to a problem? Please describe.
People want a tooltip that would show a breakdown of the roll that happened and why it succeeded/failed

Describe the solution you'd like
We should refactor the existing tooltip system to support different types of tooltips, where it could embed a component for the actual contents of the tooltip window. then depending on what's passed to the tooltip store it can choose which component to display and pass whatever data is needed to it.

Doing this will allow us to create a second tooltip component for displaying skill roll breakdowns.

Additional context
It's probably worth considering adding a tooltip hover on the skill roll when picking them in choices too

[REQUEST] Vite module to load narrat scripts with hot code swapping

Is your feature request related to a problem? Please describe.
It would be really useful to be able to use Vite hot code reloading with narrat.

By doing this, we could allow people to develop their game and change code while playing without having to restart the whole game to see changes to labels (very useful when needing to test something in a specific test, or when finding a bug in the middle of a complex playthrough with a lot of state that takes time to reach).

Describe the solution you'd like
This would involve a few changes:

  • We'd need some sort of Vite plugin to load .nar files
  • Said .nar files would need to be imported in index.ts instead of being loaded by the game from a list of scripts in the config

Once this is working, reloading a .nar file could simply override the content of all the labels included in said file, which would mean that the next time the game jumps to one of those new labels, the new code is loaded.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

[BUG] When a script tries to use a hud stat that's not defined in the config, it silently fails

Describe the bug
If a script tries to modify or access a hud stat which doesn't exist in the config, silent side effects will cause issues in the game like breaking the hud display and making it not update (because the function errors while trying to read the stat's config)

The engine should handle that better and clearly show an error indicating that an unconfigured stat is being used

[REQUEST] Parse narrat labels (or script files?) just in time

Is your feature request related to a problem? Please describe.
(This is pretty low priority as few games even have that problem for the moment).

Large narrat games can take a bit of time to load due to the huge amount of scripts to parse on game load.

We could make this faster by only parsing files or labels when they're reached. It could also save memory by not keeping the data for every single script/label saved constantly. I haven't checked if memory is an issue, but I suspect it could be on large games if played on a lower end device.

Describe the solution you'd like
The game could load all scripts, find all labels (very quick to do), but not parse the actual code in them.

Then only when using a certain label would it parse either the file containing that label, or maybe even just specifically that label, which should pretty much be instant.

Only issue is in dev mode it's useful to parse the whole game on launch to find script issues, so maybe we want an option to still be able to do that during dev, or maybe a "check scripts" button somewhere?

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

[REQUEST] Dice based skill check options

Is your feature request related to a problem? Please describe.
Skill check rolls based on dice are a common popular system that many people want to use

Describe the solution you'd like
It should be a different "mode" for the skill check, which can be configured either globally to be the default for all skill checks, or per skill check.

Would need to define the config format to define how many dice are rolled and how the thresholds are defined.

This also depends on #61 as we should really implement skill check configs before implementing more advanced options like this

Additional context
Requested on discord

[BUG] Secret Achievement Issues

Describe the bug

To Reproduce
Steps to reproduce the behavior:
Reproduced on Demo Game

  1. Set secretAchievements information in achievements.yaml to the following:
secretAchievements:
  censorDescription: true
  censorName: true
  hideUntilObtained: true
  1. Navigate to Achievements Tab
    Secret achievement will still appear in the list of achievements even if locked.
  2. Unlock achievement
  3. Navigate to Achievement Tab
    The unlocked secret achievement has both the description and name will still censored. Image is no longer censored

Expected behavior
For step 2, expected behaviour would be for the secret achievement to not appear in the list
For step 4, expected behaviour would be for the unlocked secret achievement to show name and description

Screenshots
Screenshot 2024-03-31 223832

Browser
If relevant, which browser/OS is this happening on
Chrome

[REQUEST] Add a way for items to give passive stat bonuses

Is your feature request related to a problem? Please describe.
We want items to be able to give passive stat bonuses

Describe the solution you'd like
Should just be an extra option in the item config

Additional context
requested on discord

[REQUEST] Music queue

Is your feature request related to a problem? Please describe.
We want the ability to queue a "playlist" of music instead of playing only one, as there is no way for narrat scripts to know when a music ends.

Describe the solution you'd like
Would need some refactoring in the audio store to make the AudioChannel type also keep a queue of the next audio files to play. Then when an audio stops playing, if something is in the queue, it should be popped and played.

For the purposes of queuing, looping should probably be ignored. We will need a way to loop the whole playlist though, which would involve keeping track of what was originally in the queue.

Additional context
Requested on Discord

[REQUEST] Twitch integration

Is your feature request related to a problem? Please describe.
We want a twitch integration so games can have streamers play with their viewers voting on some choices

Describe the solution you'd like
Unknown for now, need to look at the API.

In terms of usage, it should probably be an option in choices, like:

choice myChoiceId twitchVoted:
  // ...

So that games can choose which choices are voted on. Once a voting choice is reached, the game would wait until the twitch API resolves whatever it needs to do and returns us the final choice, at which point the game can continue as usual.

Ideally it should show an overlay on screen or something to make it obvious that a viewer choice has started.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

[BUG] Video tag does not use playsinline attribute

Describe the bug
Video used as the screen is does not use the playsinline attribute. When the video is a looping screen, traveling to that screen means the video plays fullscreen, outside of (and covering) the game.

Adding playsinline should fix the issue.

Browser
iOS Safari, etc.

[BUG] array_join array argument not recognised as array

Describe the bug
When using the array_join command as documented, the operation fails with a runtime error that the command "requires an array argument. The args printed in the console show that the array argument is wrapped in a Proxy object.

To Reproduce
Steps to reproduce the behavior:

  1. Assign an array var testingarray (new Array "Test" "ing")
  2. Use the array_join command and log the result log (array_join $testingarray "")

Expected behavior
In this example, the expected result is the string "Testing"

Screenshots
image

Browser
If relevant, which browser/OS is this happening on
Tested on both Firefox 124 and Edge 123

[BUG] Loading Save Not Resetting $Data

Describe the bug
When you load a previous save, it does not reset the data object to previous state, it keeps current state.

To Reproduce
Steps to reproduce the behavior:

  1. set data.someNumber 2
  2. Manually Save
  3. set data.someNumber 3
  4. jump to some label
  5. Load Save
  6. display $data.someNumber
  7. Will show data.someNumber as 3

Expected behavior
I'd expect the data.someNumber to be a 2

Screenshots

Browser
Chrome, latest version

[BUG] Custom Store Save Error

Describe the bug
Saving a custom store causes the game to crash.

To Reproduce
Steps to reproduce the behavior:

  1. Register a custom store with a save.
  2. Make a jump in narrat
  3. Get the following error: β€œ(in promise) TypeError: Cannot set properties of undefined "

Expected behavior
Expect the store to be saved.

Browser
Latest version of Chrome

[REQUEST] Localisation support

Is your feature request related to a problem? Please describe.
We want localisation support for games

Describe the solution you'd like
There should be a script to extract all strings from a game. This is a bit tricky because the script will need to use the narrat parser in some way to extract only text strings, but not the ones that are technical (like an object key string).

Maybe a good solution would be to specifically only extract strings that match certain commands (like the base command, or the talk command, etc.

We also need to support having config keys refer to localisation strings in some way since a lot of strings exist in the config

[BUG] Error when attempting to create a project with Yarn on WSL

Describe the bug
Hi. On Linux (or at least WSL 2), attempting to run yarn create narrat results in this error:

/usr/bin/env: β€˜node\r’: No such file or directory

This is apparently because the create-narrat script has CRLF line endings. Changing them to just LF allows project creation to proceed as normal.

To Reproduce
Steps to reproduce the behavior:

  1. Simply run yarn create narrat on WSL (and possibly other Linux environments)

Screenshots
error message

Thanks!

[BUG] Keyboard control: choice is not highlighted, yet player can navigate with keyboard arrows with an invisible selection

Describe the bug
A clear and concise description of what the bug is. Please include any error messages or broswer console errors (open the js console with right-click -> inspect on the page, and look for errors in red in the console).

Testing the web demo, the current choice doesn't seem highlighted, yet pressing Enter will select the first choice and pressing down arrow then Enter will select the second choice, so there must be some invisible selection.

Entering a number 1-2-3 works immediately, but it's not relevant here.

To Reproduce
Steps to reproduce the behavior:

  1. Go to demo: https://demo.narrat.dev/
  2. Wait for first choice
  3. Tip: enter fullscreen to avoid arrow keys scrolling the page
  4. Press down arrow key
  5. Press enter

Expected behavior
The 1st then 2nd choice should be highlighted as you navigate with down arrow.

Screenshots

image

Browser

Chrome Version 126.0.6478.126 (Official Build) (64-bit)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.