diff --git a/assets/index.md.14bdf584.js b/assets/index.md.14bdf584.js
deleted file mode 100644
index ca3603db..00000000
--- a/assets/index.md.14bdf584.js
+++ /dev/null
@@ -1 +0,0 @@
-import{_ as e,c as t,o}from"./app.829490d2.js";const u='{"title":"Home","description":"","frontmatter":{"home":true,"title":"Home","heroText":"Profectus","tagline":"A game engine that grows with you","actionText":"Get Started","actionLink":"/guide/setup","features":[{"title":"Easy to Use","details":"Everything is written to be as intuitive to use as possible"},{"title":"Well integrated","details":"Seamlessly deploy your project, get type hints in your IDE, etc."},{"title":"Incremental","details":"Designed to actively encourage you to become better at programming"}],"footer":"Copyright \xA9 2022 thepaperpilot"},"headers":[],"relativePath":"index.md"}',i={};function a(r,s,n,c,l,p){return o(),t("div")}var m=e(i,[["render",a]]);export{u as __pageData,m as default};
diff --git a/assets/index.md.14bdf584.lean.js b/assets/index.md.14bdf584.lean.js
deleted file mode 100644
index ca3603db..00000000
--- a/assets/index.md.14bdf584.lean.js
+++ /dev/null
@@ -1 +0,0 @@
-import{_ as e,c as t,o}from"./app.829490d2.js";const u='{"title":"Home","description":"","frontmatter":{"home":true,"title":"Home","heroText":"Profectus","tagline":"A game engine that grows with you","actionText":"Get Started","actionLink":"/guide/setup","features":[{"title":"Easy to Use","details":"Everything is written to be as intuitive to use as possible"},{"title":"Well integrated","details":"Seamlessly deploy your project, get type hints in your IDE, etc."},{"title":"Incremental","details":"Designed to actively encourage you to become better at programming"}],"footer":"Copyright \xA9 2022 thepaperpilot"},"headers":[],"relativePath":"index.md"}',i={};function a(r,s,n,c,l,p){return o(),t("div")}var m=e(i,[["render",a]]);export{u as __pageData,m as default};
diff --git a/assets/index.md.4f35ffdc.js b/assets/index.md.4f35ffdc.js
new file mode 100644
index 00000000..726a766d
--- /dev/null
+++ b/assets/index.md.4f35ffdc.js
@@ -0,0 +1 @@
+import{_ as e,c as t,o}from"./app.829490d2.js";const u='{"title":"Home","description":"","frontmatter":{"home":true,"title":"Home","heroText":"Profectus","tagline":"A game engine that grows with you","actionText":"Get Started","actionLink":"/guide/setup","features":[{"title":"Easy to Use","details":"Everything is written to be as intuitive to use as possible"},{"title":"Helpful","details":"Seamlessly deploy your project, get type hints in your IDE, and more"},{"title":"Incremental","details":"Designed to actively encourage you to become better at programming"}]},"headers":[],"relativePath":"index.md"}',a={};function i(r,s,n,c,l,d){return o(),t("div")}var m=e(a,[["render",i]]);export{u as __pageData,m as default};
diff --git a/assets/index.md.4f35ffdc.lean.js b/assets/index.md.4f35ffdc.lean.js
new file mode 100644
index 00000000..726a766d
--- /dev/null
+++ b/assets/index.md.4f35ffdc.lean.js
@@ -0,0 +1 @@
+import{_ as e,c as t,o}from"./app.829490d2.js";const u='{"title":"Home","description":"","frontmatter":{"home":true,"title":"Home","heroText":"Profectus","tagline":"A game engine that grows with you","actionText":"Get Started","actionLink":"/guide/setup","features":[{"title":"Easy to Use","details":"Everything is written to be as intuitive to use as possible"},{"title":"Helpful","details":"Seamlessly deploy your project, get type hints in your IDE, and more"},{"title":"Incremental","details":"Designed to actively encourage you to become better at programming"}]},"headers":[],"relativePath":"index.md"}',a={};function i(r,s,n,c,l,d){return o(),t("div")}var m=e(a,[["render",i]]);export{u as __pageData,m as default};
diff --git a/guide/changelog.html b/guide/changelog.html
index 810adb82..a7d6b91c 100644
--- a/guide/changelog.html
+++ b/guide/changelog.html
@@ -32,7 +32,7 @@
</ul>
</details>
The details
and summary
tags will create a section that can be collapsed and uncollapsed. While collapsed only the text in the summary
tag will appear. By default sections are collapsed, although adding the open
attribute to the details
element will change that.
Within the details of the version, you can add a description and a list of changes for that version. CSS classes have been defined to automatically mark a change as a feature, fix, breaking change, or balancing tweak. You can of course add additional tags as you have full control over the entire component.
-
+
Creating Features
# TODO
Because typescript does not emit JS, if a value is supposed to be a function it is impossible to determine if a given function is the intended value or a function that returns the actual value. For this reason it is not recommended for any feature types to include properties that are Computable<Function>
s, and all functions will be wrapped in computed
. The notable exception to this is JSX, which uses a utility function to mark that a function should not be wrapped.
-
+
Introduction
Profectus is a web-based game engine. You can write your content using many built in features, write your own features, and build up complex gameplay quickly and easily.
The purpose of creating profectus was to create an easy to use engine that does not create a ceiling for a programmer's personal growth. This engine will grow in complexity with you, empowering you to create increasingly complex designs and mechanics.
Design Philosophies
While absolute purity is impossible, design decisions have been and will continue to be made using these principles in mind.
- An engine should be intuitive, and code readable without context
- An engine should not constrain a creator
- An engine should be consistent
- An engine should be extensible, and those extensions sharable
- An engine should be efficient
- An engine should report issues early and thoroughly
- Passing by reference is one honking great idea -- let's do more of that!
-
+
Layers
Profectus content is organized into units called "Layers". When display content to the user, it will be done so by having some number of layers appearing as sections on the screen. They are stored in /src/data/layers
.
Each layer is ultimately a collection of different features, and a display function. While there are a couple reserved properties for layers, most of its structure is fully up to the creator.
Layers can be dynamically added or removed at any time, which also allows for effectively disabling or enabling content based on arbitrary conditions. Just make sure getInitialLayers can process the player save data object and determine which layers should be currently active.
Lazy Proxies
Layers (and features) are not actually created immediately. Instead, their options are gotten through a function which is then run the first time something inside the layer is accessed. This is a concept called lazy evaluation, which is also used for things like computed
, and allows for features to reference each other without worrying about cyclical dependencies.
-
+
Project Entry
This is a TypeScript file containing the non-static parts of your project, and acts as the entry point for it.
It is stored at /src/data/projEntry.jsx
.
This file has 3 things it must export, but beyond that can export anything the creator wants it to. Typically in addition to the required 3, the initial/"main" layer will be exported. Typically utilites belong in common.tsx
, which exists next to projEntry.tsx
.
Required Exports
getInitialLayers
- Type:
(player: Partial<PlayerData>) => GenericLayer[]
A function that is given a player save data object currently being loaded, and returns a list of layers that should be active for that player. If a project does not have dynamic layers, this should always return a list of all layers.
hasWon
- Type:
ComputedRef<boolean>
A computed ref whose value is true whenever the game is over.
For example, in a game where the goal is to have a resource reach 10:
export const hasWon = computed(() => Decimal.gte(resource.value, 10));
fixOldSave
- Type:
(oldVersion: string | undefined, player: Partial<PlayerData>) => void
This function will be run whenever a save is loaded that has a different version than the one in project info. It will be given the old version number, and the player save data object currently being loaded.
The purpose of this function is to perform any necessary migrations, such as capping a resource that accidentally inflated in a previous version of the project. By default it will do nothing.
-
+
Project Info
This is a JSON file containing information that describes your project and configures parts of how Profectus should represent it.
It is stored at /src/data/projInfo.json
.
Basic Config
title
- Type:
string
- Default:
Profectus
The name of the project, which will appear in the info tab and in the header, if enabled. The page title will also be set to this value.
id
This is a unique ID used when saving player data. Changing this will effectively erase all save data for all players.
WARNING
This ID MUST be unique to your project, and should not be left as the default value. Otherwise your project may use the save data from another project and cause issues for both projects.
author
The author of the project, which will appear in the info tab.
discordName
- Type:
string
- Default:
The Paper Pilot Community
The text to display for the discord server to point user's to. This will appear when hovering over the discord icon, inside the info tab, the game over screen, as well as the NaN detected screen.
By default, this is The Paper Pilot Community, which can act as a catch-all for any Profectus projects without their own servers. If you change the discord server with your own, The Paper Pilot Community will still display underneath the custom server when hovering over the discord icon and within the info tab. Those places will also contain a link to the Modding Tree discord server.
discordLink
- Type:
string
- Default:
https://discord.gg/WzejVAx
The link for the discord server to point user's to. See discordName for more details.
Version Config
versionNumber
The current version of the project loaded. If the player data was last saved in a different version of the project, fixOldSave will be run, so you can perform any save migrations necessary. This will also appear in the nav, the info tab, and the game over screen.
versionTitle
- Type:
string
- Default:
Initial Commit
The display name for the current version of the project loaded. This will also appear in the nav, the info tab, and the game over screen, unless set to an empty string.
Display Config
allowGoBack
- Type:
boolean
- Default:
true
Whether or not to allow tabs (besides the first) to display a "back" button to close them (and any other tabs to the right of them).
defaultShowSmall
- Type:
boolean
- Default:
false
Whether or not to allow resources to display small values (<.001). If false they'll just display as 0. Individual resources can also be configured to override this value.
defaultDecimalsShown
Default precision to display numbers at when passed into format
. Individual format
calls can override this value, and resources can be configured with a custom precision as well.
- Type:
boolean
- Default:
true
Whether or not to display the nav as a header at the top of the screen. If disabled, the nav will appear on the left side of the screen laid over the first tab.
banner
- Type:
string | null
- Default:
null
A path to an image file to display as the logo of the app. If null, the title will be shown instead. This will appear in the nav when useHeader
is true.
logo
A path to an image file to display as the logo of the app within the info tab. If left blank no logo will be shown.
initialTabs
- Type:
string[]
- Default:
["main"]
The list of initial tabs to display on new saves. This value must have at least one element. Each element should be the ID of the layer to display in that tab.
Advanced Config
maxTickLength
- Type:
number
- Default:
3600
The longest duration a single tick can be, in seconds. When calculating things like offline time, a single tick will be forced to be this amount or lower. This will make calculating offline time spread out across many ticks as necessary. The default value is 1 hour.
offlineLimit
The max amount of time that can be stored as offline time, in hours.
-
+
Reactivity
Profectus takes large advantage of Vue's reactivity system. It's recommended to read up on how refs and computed refs work. Ultimately this means that sometimes you'll need to type .value
to get the actual value of something, but also you are able to pass things around by reference instead of by value. Indeed, it is recommended to only unwrap the actual value when you actually need it. .value
is guaranteed to be correct and up to date only on the exact moment it is accessed.
With a proper IDE, such as Visual Studio Code, you should be able to see whether or not something is a ref or not from type hints. If in doubt, you can always wrap the property in an unref
call.
Vue's reactivity is probably the "quirkiest" part of Profectus, and not even the documentation makes all of those quirks clear. It is recommend to read this thread of common misconceptions around Vue reactivity.
Persistent
Some refs are "persistent" refs. Most notably, any time you use the persistent()
function it will be a persistent ref. You access these the same way you would any other ref, but keep in mind its value will be saved and loaded automatically.
All persistent refs must be included in a layer object in order for persistence to work. Since many features will create persistent refs internally its recommended to include all of them just to be safe.
Computable
Most properties on features will accept Computable
values. Computable values can either be a raw value, a ref to the value, or a function that returns the value. In the lattermost case it will be wrapped in computed
, turning it into a ref. The feature type will handle it being a ref or a raw value by using unref
when accessing those values. With type hints, your IDE should correctly identify these values as refs or raw values so you can treat them as the types they actually are.
Because functions are automatically wrapped in computed
for many properties, it might be expected to happen to custom properties you add to a feature that isn't defined by the feature type. These functions will not be wrapped, and if you want it cached you should wrap it in a computed
yourself. This does, however, allow you to include custom methods on a feature without worry.
-
+
Setting Up
Profectus requires a node development environment in order to work on a project. If you are comfortable with the command line, it is recommended to use a local development environment.
Local Development
You will require the following tools for local development:
Create a new project from the Profectus repository via the "Use this template" button. You can then copy the link for the repository to clone it locally.
INFO
Since the repository is a template repository, you can easily create multiple projects from one repository. However, it does make updating an existing project to a newer version of Profectus more difficult.
It's recommended to create a new git branch for development, so you can push your changes without it affecting the live build. This allows you to continue working with smaller commits, and only release new versions when you're actually ready to. The github workflow will automatically rebuild the page whenever you push to the main
branch.
The next step is to install Profectus' dependencies. This is as simple as running npm install
.
You can now run npm serve
to start a local server that will host your project so you can work on it. As you change files the site will automatically reload them.
Deploying
If you're using git, deploying is as easy as pushing your changes to the main
branch. In a couple minutes the site will be updated fully automatically. If you'd like to see progress on it, or look at any errors that happened, you can do so from the actions tab on your repository.
Before github knows to actually host the generated site, you'll have to enable github pages in the repo settings. This just means selecting the branch to use - gh-pages
. You will only need to perform this step once. This will automatically start another github action to deploy the website.
Once the action completes, your project should be available at https://<YOUR_GITHUB_USERNAME>.github.io/<YOUR_REPO_NAME>/
. For example, the TMT Demo project hosted at https://github.com/profectus-engine/TMT-Demo is available at https://profectus-engine.github.io/TMT-Demo/.
Visual Studio Code Setup
If you don't already have a preferred IDE, Profectus is currently developed in Visual Studio Code and is known to work well with that IDE in particular. It's recommend to use Take Over Mode for proper type analysis, and consider turning off .value
autocomplete by running the Preferences: Open Settings
command and setting volar.autoCompleteRefs
to false
.
Replit
As an alternative to local development, you may use replit, which will automatically set up your development environment for you and also host your project itself.
However, on the free plan you will be limited by the amount of resources you can use, and the program will need to start up occasionally.
To create a Profectus project on replit, all you have to do is click this button:
You can then start the developing by clicking the "Run" button at the top of the screen. This will also make the project run publicly, so you could consider it as automatically deployed. This does mean you cannot have a separate development environment from your production environment, however.
Glitch
Glitch is a site similar to replit, with much the same pros and cons. To create a projectus project on glitch, select "New Project", "Import from GitHub", and then type in profectus-engine/Profectus
. The new project will be created and should be automatically configured and ready to go.
-
+
Themes
Themes are objects that change how the project's interface should look. This is done mostly by changing the values of various CSS variables. You can look at the existing themes as a reference for the kind of values these CSS variables expect. They can also set various theme options that change how parts of the screen are laid out, which are described below.
They are stored in /src/data/themes.ts
.
Modifying Themes
You can add a theme by adding a property to the Themes
enum and then including the theme in the exported object. It's recommended to use the spread operator if you'd like to have a theme look like another, but override specific options / CSS variables.
Themes added in this way will be automatically included in the Themes dropdown in the Options tab. Removing themes from the enum and exported object will similarly hide them from the dropdown.
If you'd like to change which theme is the default, you may modify the initial player settings object in the /src/game/settings.ts
file. Keep in mind you'll also want to change it in the hardResetSettings
function in the same file.
Theme Options
floatingTabs
Toggles whether to display tab buttons in a tab list, similar to how a browser displays tabs; or to display them as floating buttons, similar to how TMT displays buttons.
mergeAdjacent
If true, elements in a row or column will have their margins removed and border radiuses set to 0 between elements. This will cause the elements to appear as segments in a single object.
Currently, this can only merge in a single dimension. Rows of columns or columns of rows will not merge into a single rectangular object.
-
+
Utilities
There are often concepts that aren't inherent to a single feature, but rather work with joining different features together. For example, a reset clickable that activates a conversion and resets a tree, which happens to be a common use case but isn't inherent to clickables, conversions, or trees.
These are perfect situations for utilities, and so to encourage creators to learn to identify and take advantage of these situations, a file called src/data/common.tsx
has been created to demo some of the more common utility functions a project might use. Adding new utilities to this file is encouraged, as is creating utils in general. It also works as a good stepping stone to creating your own features.
-
+
-
-
+
+