-
Notifications
You must be signed in to change notification settings - Fork 17
Implementation Notes
There is a compromise between speed and convenience. Convenience here is how easy it is for authors to create games, and includes how simple it is to create a basic item, how easy it is to customise to do the bizarre and how easy it is to understand the author's code.
It would be faster to load all the JavaScript files at the same time, however, they would then run more-or-less in size order, with the smallest file going first. That will make it impossible for one file to reliably access something in another file - the most obvious example of that is using createItem
in your data files needs the file where createItem
is defined already loaded. Now there are ways around this - just delay when createItem
is called - but this adds extra complications to the data files and, rightly or wrongly, I chose not to do so.
The second issue is with layout reflow. The browser does a lot of work trying to decide a page layout, and if you then change the layout part way through, it has to start again. I am not sure how big an issue this is with QuestJS, but again I took the decision that convenience to authors was more important. Testing on my PC indicates this typically takes 50 ms which to my mind is perfectly reasonable.
With regards to commands, the slow bits are searching for the best fit command, which takes 15 to 20 ms on my PC, and running world.endTurn which takes a similar time. This means commands are being completed within 50 ms, which is fine.
Initialisation involves loading the right files in the right order. This is complicated by the fact that the files can be in different places. The game (i.e., those usually fond in the "game" folder, the ones specific to a particular game) files can be in a different directory to allow multiple games to be written at the same time. However, the editor also has its own ideas about where files are, the game files being created on-the-fly, while others are static files in a very different location. The index.html file therefore has a script
section that sets up some global variables that define where these things are, while the editor can create its own version with its own values.
The initialisation process is kicked off in index.html, which loads lib/_util.js, lib/_settings.js and then your game settings.js file. Once the last has loaded, settings.writeScript
is called.
The settings.writeScript
file loads all the CSS files, then sets the favicon, and then queues each script file, using settings.loadScript
. When a script file has been loaded, it is run, so items and locations are created at this point. As each is done, settings.scriptOnLoad
is called, which either starts the next script loading, or, if the queue is now empty, kick-starts the game, first calling world.init
then io.init
.
The world.init
function does some basic housekeeping, including initialising all the world objects, finding the player object and initialising all the commands.
The io.init
function sets up the user interface. It creates the side panes, adds event listeners to capture certain keypresses, loads sound files. Then it removes the game loading message, and displays the title. If the game has a starting dialog, that is now displayed, otherwise world.begin
is called (if there is a dialog, world.begin
will be called when that is closed).
The world.begin
function shows the introductory text, settings.intro
and runs setting.setup
, then calls world.enterRoom
which is the function that is called every time the player enters a room, so calls "afterEnter" and "afterFrstEnter".
A turn is initiated by the player typing something and clicking enter.
The text is passed to the parser, which will attempt to match the text to a command. If it is successful, the command's "script" attribute is run. Details here. The parser calls world.endTurn
to kick off the end-of-turn housekeeping.
- Change listeners are handled
- Game turn and time are incremented*
- "endTurn" functions in objects are run*
- "endTurn" functions in modules are run*
- Change listeners are handled again*
- The "pause" state for any items is reset*
- Scope for items is updated by
world.update
* - Game state is saved (for UNDO)*
- The UI is updated
Items marked * occur only if the command returned world.SUCCESS
.
This could also be called namespaces; it is kind of the same thing in JavaScript.
Note that only w
will be saved (and so game
indirectly); nothing else gets saved.
The world is held in a dictionary called w
. This means you can access any item by prefixing its name with "w." and can access an item by a variable with square brackets.
// You can access the object called "hatstand" two ways:
name = "hatstand";
w.hatstand;
name = hatstand
w[name];
The advantage of using a dictionary is there is no chance of a name collision. The w
stands for "world", and is deliberately short.
Some transitory game values are stored in a dictionary called game
for convenience. It should be reset whenever the player changes rooms with game.update()
, and when the lighting changes and possibly other times too.
game.turnCount
game.elapsedTime
game.elapsedRealTime
game.dark
The game
object is in w
so string, integer and Boolean attributes will get saved.
An array of all the commands in the game. Do not expect this to get saved.
Built-in rules for commands.
Functions for the parser.
Hidden functions and variables for I/O.
The text processor.
For the world model.
Language specific settings.
For unit testing
A lot of IF systems, including Quest 5, have software files and data files as two distinct things. The data files contain all the information for a specific game, all the details for each item and location and probably more besides. The software loads it in and runs the game. Why not do that in Quest 6?
The problem arises with scripts. People will inevitably want to do something new, so absolutely need the ability to code or to write their own scripts, and that has to be in the data files. To then run code from a data file, you either need to run JavaScript, using eval
(or some variant), which has significant security issues, or you need some kind of interpreter in the software, which is the approach Quest 5 takes (and I guess other systems).
It seemed better all around to have the data in JavaScript too. Scripts can be added very easily because it is already code. You have the full power of JavaScript immediately available, with no effort from me. And the data is easier to write too, than say XML (which Quest 5 uses) or JSON.
Quest 5 uses named capture groups. JavaScript supports named capture groups. So why does Quest 6 not use them?
Simply because they are not universally implemented across all browser. This table indicates the version and date that various browsers introduced them:
Browser | version | date |
---|---|---|
Edge | 79 | 15/Jan/20 |
Firefox | 78 | 30/Jun/20 |
Chrome | 64 | 24/Jan/18 |
Safari | 11.1 | 29Mar/18 |
Opera | 51 | 7/Feb/18 |
Android browser | 81 | 21/Apr/20 |
Chrome for Android* | 87 | 17/Nov/20 |
Data from here
The first upload of Quest 6 to Github was 2/Dec/18. Edge and Chrome had named capture groups for eleven months by then, but it would be some time before Firefix would. And who knows how many users had updated their browser?
Even as I write this (08/Dec/20), only 91% of users have browsers that can use this feature, so even now I would be reluctant to use it. In two years time I expect the situation will be different, but I have to go with what it is right now.
Tutorial
- First steps
- Rooms and Exits
- Items
- Templates
- Items and rooms again
- More items
- Locks
- Commands
- Complex mechanisms
- Uploading
QuestJS Basics
- General
- Settings
- Attributes for items
- Attributes for rooms
- Attributes for exits
- Naming Items and Rooms
- Restrictions, Messages and Reactions
- Creating objects on the fly
- String Functions
- Random Functions
- Array/List Functions
- The
respond
function - Other Functions
The Text Processor
Commands
- Introduction
- Basic commands (from the tutorial)
- Complex commands
- Example of creating a command (implementing SHOOT GUN AT HENRY)
- More on commands
- Shortcut for commands
- Modifying existing commands
- Custom parser types
- Note on command results
- Meta-Commands
- Neutral language (including alternatives to "you")
- The parser
- Command matching
- Vari-verbs (for verbs that are almost synonyms)
Templates for Items
- Introduction
- Takeable
- Openable
- Container and surface
- Locks and keys
- Wearable
- Furniture
- Button and Switch
- Readable
- Edible
- Vessel (handling liquids)
- Components
- Countable
- Consultable
- Rope
- Construction
- Backscene (walls, etc.)
- Merchandise (including how to create a shop)
- Shiftable (can be pushed from one room to another)
See also:
- Custom templates (and alternatives)
Handing NPCs
- Introduction
- Attributes
- Allowing the player to give commands
- Conversations
- Simple TALK TO
- SAY
- ASK and TELL
- Dynamic conversations with TALK TO
- TALK and DISCUSS
- Following an agenda
- Reactions
- Giving
- Followers
- Visibility
- Changing the player point-of-view
The User Experience (UI)
The main screen
- Basics
- Printing Text Functions
- Special Text Effects
- Output effects (including pausing)
- Hyperlinks
- User Input
The Side Panes
Multi-media (sounds, images, maps, etc.)
- Images
- Sounds
- Youtube Video (Contribution by KV)
- Adding a map
- Node-based maps
- Image-based maps
- Hex maps
- Adding a playing board
- Roulette!... in a grid
Dialogue boxes
- Character Creation
- Other example dialogs [See also "User Input"]
Other Elements
- Toolbar (status bar across the top)
- Custom UI Elements
Role-playing Games
- Introduction
- Getting started
- Items
- Characters (and Monsters!)
- Spawning Monsters and Items)
- Systema Naturae
- Who, When and How NPCs Attack
- Attributes for characters
- Attacking and guarding
- Communicating monsters
- Skills and Spells
- Limiting Magic
- Effects
- The Attack Object
- [Extra utility functions](https://github.com/ThePix/QuestJS/wiki/RPG-Library-%E2%80%90-Extra Functions)
- Randomly Generated Dungeon
- Quests for Quest
- User Interface
Web Basics
- HTML (the basic elements of a web page)
- CSS (how to style web pages)
- SVG (scalable vector graphics)
- Colours
- JavaScript
- Regular Expressions
How-to
Time
- Events (and Turnscripts)
- Date and Time (including custom calendars)
- Timed Events (i.e., real time, not game time)
Items
- Phone a Friend
- Using the USE verb
- Display Verbs
- Change Listeners
- Ensembles (grouping items)
- How to spit
Locations
- Large, open areas
- Region,s with sky, walls, etc.
- Dynamic Room Descriptions
- Transit system (lifts/elevators, buses, trains, simple vehicles)
- Rooms split into multiple locations
- Create rooms on the fly
- Handling weather
Exits
- Alternative Directions (eg, port and starboard)
- Destinations, Not Directions
Meta
- Customise Help
- Provide hints
- Include Achievements
- Add comments to your code
-
End The Game (
io.finish
)
Meta: About The Whole Game
- Translate from Quest 5
- Authoring Several Games at Once
- Chaining Several Games Together
- Competition Entry
- Walk-throughs
- Unit testing
- Debugging (trouble-shooting)
Releasing Your Game
Reference
- The Language File
- List of settings
- Scope
- The Output Queue
- Security
- Implementation notes (initialisation order, data structures)
- Files
- Code guidelines
- Save/load
- UNDO
- The editor
- The Cloak of Darkness
- Versions
- Quest 6 or QuestJS
- The other Folders
- Choose your own adventure