Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data rearchitect #16

Merged
merged 18 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ Skelerealms is designed in such a way where you can ignore or replace most of th

## How do I get started?

Visit the wiki for more details.

Visit the [documentation](docs/user%20guide/quick_start.md) for a quick start guide.


## What's the project status?

Expand All @@ -59,3 +61,4 @@ Please note that the project is in an Alpha state, which means breaking changes
- Integrating NetworkGD.
- 0.7
- Redesigning the save game system.
- Polish cross-scene navigation.
9 changes: 9 additions & 0 deletions docs/concepts/entities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Entities and Components

## Entities

Anything that needs to have cross-scene persistence must be an *Entity*. These are roughly equivalent to CE's *Actors*. An *Entity* is defined as a tree of nodes descending from an `SKEntity` node. During runtime, these entities will live underneath the `SKEntityManager`, which keeps track of what entity goes in what scene, among other things.

## Components

*Entites* are largely made up of *Components*. These are nodes that derive from `SKEntityComponent`, special nodes that have built-in management functions.
9 changes: 9 additions & 0 deletions docs/concepts/worlds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Worlds

*Worlds* are simply a way of expressing what scene an entity belongs to. This is roughly equivalent to CE's *Cells*.

A scene becoming a World has two requirements:
- The root node (It should be a Node3D but it doesn't have to) has the name of the world
- The scene file is saved in the directory defined by `skelerealms/worlds_path` - by default, `res://worlds` (It can be in a subforlder for organization). The name of the file should exactly match the name of the root node.

That's it!
53 changes: 53 additions & 0 deletions docs/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# The Skelerealms Grimoire
#### For Skelerealms Beta 0.6

## Introduction

Welcome! There are the high-level docs for Skelerealms, showing you how to use this addon. If you want methods and variables documentation, documentation comments are provided in-engine, like any other class.

### What is Skelerealms?

Skelerealms is an addon for Godot 4.2+ that aims to provide the foundation for creating a Bethesda-style Open World RPG (The Elder Scrolls, Fallout, Starfield). No gameplay is provided, but a lot of the challenging systems are in place to allow you to focus on telling your story.

Skelerealms is inpsired by Creation Engine, but aims to tackle many of its shortcomings with the benefit of 20+ years of hindsight. It's also been designed not to lock you in to any specific way of designing your game - most components are purely optional. You don't even need to have a player to run the game (but I'm not sure what the point of that would be...)

### What problems does it solve?

- Cross-scene persistence: If the player drops a book in one room, goes outside, and then comes back inside, that book should still be lying on the floor. This problem is much trickier to solve than it seems; but you don't need to worry about it. Skelerealms has got you covered!
- Cross-scene navigation: If you're running away from an enemy and duck into a building, it only makes sense that the enemy should follow you through the door. This, again, is a tricky problem to solve. Again, though, Skelerealms has solved this problem already, by introducing a supplementary navigation system that exists, no matter what scene is loaded.
- NPC AI: Skelerealms comes with a robust AI system that hopes to smooth out some of the awkwardness of Bethesda's infamous NPCs by using a GOAP (Goal-Orientated Action Planning) system that's also integrated (but not coupled to) a built-in faction and schedule system.
- And much more!

### Main Features

- Cross-scene persistence
- Inter-scene navigation
- GOAP
- Inventory
- Status effects and spells
- Loot tables
- Equipment
- Composable item behaviors
- Bartering system
- Factions
- Schedules
- Sight/stealth mechanics

## Table of Contents

### Concepts

### User guide


## Project Status

### Development

Despite what it may look like on the main repo's commit history, development is ongoing (in the submodule repository.) Skelerelams was originally developed for a game I am making myself, and so I am dog-fooding Skelerealms during the development of this game. As I encounter problems during the development process, I make changes and fixes, and push them upstream.

### Places to improve

- The Savegame system needs to be rethought.
- The way the navigation system is represented in code could be much more memory-efficient. I may end up rewriting it in Zig or something.
- Lots of more processing-heavy parts of code should probably be written in a compiled language.
8 changes: 8 additions & 0 deletions docs/user guide/ai_modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# AI Modules

NPC AI is made of two parts: AI Modules and GOAP Actions. This article covers the AI Modules. AI Modules determine *what* the NPC wants to do, and the GOAP Actions determine *how* to do it.

AI Modules are rough equivalents to Creation Engine's AI Packages, minus the schedules. They are nodes that are direct children (also see [tools](/docs/user%20guide/tools.md) for `NodeBundler`) of NPCs that inform the NPCs behavior. Without any modules, the NPC will do absolutely nothing.
AI Modules are expected to function largely autonomously from the NPC. The intended design is to hook into one of the NPC's many, many signals and change behavior in response. To get a better idea of how this looks, take a look at some of the exaples that come with Skelerealms, such as [movement](../../scripts/ai/Modules/default_movement.gd) for a simple example, or [threat response](../../scripts/ai/Modules/default_threat_response.gd) for a more complex one.

As with anything else, code documentation can be found in the in-engine documentation.
47 changes: 47 additions & 0 deletions docs/user guide/components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Components

Entities are designed to be used with Components, which are reusable bits oc foce. Such is the Godot way.
Components aren't particularly special; they simply have a few functions for workign with the entities.

## Functionality

- Allows you to specify dependencies, which if it doesn't have, shows up as a warning in the editor
- Virtual functions for generation, spawning, despawning
- Saving, loading
- Gathering debug information
- Printing to console with entity's name, etc.

## Making your own

Simply inherit the SKEntityComponent class, and have at it! Just make sure it renames itself to its class name:
the entity's `get_component()` method looks for names, since GDScript has no proper generics.
Also note that you have an easy way to grab the parent entity: `parent_entity`.
If you would like to provide a custom preview scene for manipulating your entity in the editor with SKWorldEntites,
implement a `get_world_entity_preview() -> Node` method that returns a scene you'd like to appear in the editor.

## Built-in components

A number of built-in components are offered for your convenience. More in-depth usage of these can be found in their in-engine documentation.

- Attributes: Covers attributes like Strength, Perception, Endurance, etc.
- Chest: Creates a refilling chest system. Expects a loot table.
- Covens: Integrates with Skelerealms Factions system.
- Damageable: Allows this entity to be damaged.
- Effects: Allows this entity to be subject to the status effects system.
- Equipment: This entity can equip items from an inventory into special equipment slots.
- GOAP: Puts the entity under control of the built-in GOAP system. GOAP Actions come as child nodes.
- Interactive: Allows the player (and others) to interact with this entity.
- Inventory: Gives the entity an inventory, along with management tools. Can use a Loot Table to generate an inventory. Also keeps track of currency.
- Item: This entity is now an item, and can be moved from inventory to inventory. Item Components come as child nodes.
- Marker: This entity can be used as waypoints, or whatever else you need. (I use them as destinations to teleport to in the console.)
- Navigator: Can calculate paths in the granular navigation system.
- NPC: This entity is now an NPC, and has many things it can do as a consequence. AI Modules come as child nodes.
- Player: This entity is a Player.
- Puppet Spawner: Spawns scenes into the game world as "puppets" that are linked to the entity. Used by Items and NPCs to give form to their being.
- Script: Deprecated.
- Shop: This entity can now barter in the barter system. Needs an overhaul.
- Skills: Keeps track of an entity's Skills - One-handed, block, archery, etc.
- Spell Target: Deprecated. Use EffectsComponent instead.
- Teleport: This entity can be teleported.
- View Direction: Keeps track of an entites viewing direction, for stealth mechanics and other such things.
- Vitals: Keeps track of health, stamina, magica, etc.
3 changes: 3 additions & 0 deletions docs/user guide/covens.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Covens

Covens are Skelerealms' "Factions" system. The odd name is because the game I was making Skelerealms for before deciding to open-source it calls factions "covens", and I never bothered to change it. Covens are an optional system that determine some things about NPC behavior, particularly the crime tracking system and how NPCs determine the opinion they have of something. Many of the covens' attributes are self-explanatory and have further documentation in-editor, but I should add that covens are loaded at game start, and so should be placed in the covens folder in the plugin settings - by default `res://covens`. An entity is added to a coven by adding a `CovensComponent` and adding a `CovenRankData` resource. This may get reworked in the future.
61 changes: 61 additions & 0 deletions docs/user guide/entities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Entities

Any scene with SKEntity as the root is considered an Entity. Entities are able to persist between scenes, so use these for everything you want
to stay put when unloaded: NPCs, Items, etc. Entities by themselves do very little - the first layer of children should be made up of SKEntityComponents,
reusable bits of code that inform the behavior of an Entity; for example, keeping track of an inventory.

## Functionality

Entities do have some functionality, though; they keep track of the following:

- Position and rotation of the entity (Note that SKEntity is *not* a Node3D; Making it one does some funky stuff with puppets.)
- The world the entity is in
- Whether the entity should be in the scene or not
- Form ID (used for determining what sort of thing non-unique entities are)
- Whether this entity is unique.

It provides some hooks for:

- Spawning, despawning
- Actions called from dialogue
- Generation
- Getting a preview mesh (Components can provide a preview mesh for you to see when placing them in the editor)

As well as utility functions for:

- Saving
- Loading
- Gathering debug information (used for debug consoles and the like)
- Managing components

## Concepts

The life cycle of an entity goes as follows:
```
Generation -> Spawning <-> Despawning -> Destruction
```

**Spawning** happens every time an entity appears in a scene. This is used for spawning anything
associated with the entity that the player interacts with (referred to as puppets).

**Generation** is when the entity appears for the first time in a game. Not a game *session*, but a game. This is distinct from spawning
in that spawning occurs every time an entity appears in a game *session*. Further appearances of the entity will be aided by the save game system.
Generation should be used for filling inventories for the first time.

**Destruction** means the entity will no longer exist in the game, ever. This stage is a work-in-progress, and will probably come in 0.7.

**Uniqueness** simply means that there will only be one instance of this entity that exists in the game; usually used for named NPC and unique items.
Internally, non-unique entities will be given a randomly-generated RefID upon generation. Non-unique entities should probably be given a FormID.

## Creating an entity

Simply create a new scene with an SKEntity as the root, and name it with a RefID if it's unique. The first layer of the tree
under the SKEntity should all be made of SKEntityComponent-derived nodes. Beyond that, that's up to whatever the components expect to be beneath them.
Then, save the entity as a scene file (I prefer `.res`) in the entities path under the `skelerealms/entities_path` project setting; `res://entities` by default.
They can be in a subdirectory for organization.
In the very likely chance that you have many entities with the same general shape, like NPCs, you can have an "archetype" scene that you can inherit entities from, using
the editor's "ingerit scene" functionality. This is handy for saving legwork, as well as changing large swaths of entities at once. Some archetypes are provided in the
Skelerealms folder.
To place individual entities in the world, you can create an SKWorldEntity, and place in an Entity in the inspector. Depending on the entity, you will get a preview.
Manipulate the World Entity to your linkng, then hit "Sync position and world" in the inspector. This will automatically edit the entity to set its position and world
so it spawns where you placed it in the world.
27 changes: 27 additions & 0 deletions docs/user guide/goap_actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# GOAP Actions

NPC AI is made of two parts: AI Modules and GOAP Actions. This article covers the AI Modules. AI Modules determine *what* the NPC wants to do, and the GOAP Actions determine *how* to do it.

GOAP Actions are a fair bit more complicated than the AI Modules. If you don't understand how GOAP works in theory, that's outside the scope of this article: search online for more information.

Again, they are nodes directly beneath a `GOAPComponent`. An actions prerequisites and effects are determined by overriding the `get_prerequisites()` and `get_effects()` functions, returning a dictionary of shape `Effect -> Value`. These are used in the planning process, so shouldn't change during runtime, unless you know what you're doing. The GOAP system works on a timeline divided into two parts, form the perspective of an action:

## Plan time

Plan time is simply the time when a plan is created. Every action will have `is_achievable()` called on them. Returning `false`, for any reason you may desire, will exclude them from the planning process.

## Action time

Action time is when the action is being executed. The steps of each sequence are given to you as a series of overrideable functions. Returning `false` from any of them will trigger a plan recalculation. The sequence is as follows:

1. `pre_perform()` - Any actions that should be taken when the action is first begun. Finding targets, triggering animations, etc.
2. `target_reached()` - Something that should happen when a target is reached, determined by the `is_target_reached` function. By default, returns whether a navigation agent has finished navigating to a point.
3. `post_perform()` - Any actions that should be taken once the action is complete. **This happens once `duration` passes after `target_reached()` is called.**

- `interrupt()` can happen at any time. This detemines what, if anything, should happen if a plan is recalculated mid-action (between `pre_perform()` and `post_perform()`, when `running` is true). This returns nothing.

---

The intended way for GOAP Actions to have an effect on the world is through the given `parent_gop` and `entity` properties, allowing them to, for instance, move an NPC around, or play an animation.

The planner itself is contained within the `GOAPComponent`, and will attempt to make a plan every frame where it does not have any plan.
22 changes: 22 additions & 0 deletions docs/user guide/loot_tables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Loot Tables

Skelerealms comes with an in-house loot table system inspired by that seen in Minecraft. It's created using a tree of `SKLootTableItem`s underneath an associated `SKLootTable`. A "resolved" (rolled) loot table creates a result consisting of:

- Generated items
- Unique items
- Currencies

Each of these can be manipulated by creating a nested of various `SKLootTableItem`s. Some loot table items will affect the chances or number of child items, while others will instead contribute items to the output. These can be combined in ways to create complex results. [Consider the following](https://www.youtube.com/watch?v=uI_N2tLw-vI) loot table:

- SKLootTable
- SKLTItem (Contains sword)
- SKLTXOfItem (Set to between 1 and 3)
- SKLTCurrency (Set to between 5 and 20 gold coins)

This loot table will always return a sword, and then will generate 5 to 20 coins 1 to 3 times. Not the most practical example, but hopefully you get the idea.

You can put entities you've created (Unique items) into a loot table with `SKLTItemEntity`. Do note, however, that no matter how many times a unique item is rolled, it will only appear in the result once. Non-item entities will appear in the result, but `InventoryComponent` will not add non-item entities into its inventory. Also, the unique items should still be saved and stored within Skelerealms' entities path, as configured in the project settings.

Built-in, Loot tables are applicable to the following components:
- `InventoryComponent`, which will roll when the entity is `generate`d
- `ChestComponent`, which will roll and fill an inventory whenever the chest refreshes
15 changes: 15 additions & 0 deletions docs/user guide/migrating.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Migrating from 0.5 to 0.6

This will not be easy. Sorry in advance.

## Instance data

This will be spotty at best, but a button will appear above `InstanceData`-derived classes that, when pressed, will create a roughly-equivalent Entity, and save it under the same directory. AI-related things and Loot boxes will not be conserved.

## GOAP

Luckily, this is easy. Simply change your existing GOAPBehaviors to GOAPActions. Instead of being a property, however, prerequisites and effects have moved into functions to override.

## Loot tables, schedules

You will have to remake these from scratch. Sorry.
5 changes: 5 additions & 0 deletions docs/user guide/navigation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Navigation

TODO

This is for cross-scene navigation. I might rework the way you do this soon but basically you use the network tool. You can find the repo [here](https://github.com/SlashScreen/godot-network-graph) and look at the wiki for how to use it, but I'm about to merge it into Skelerealms, I think, since having external dependencies creates complications. Anyway, save the network for a world into the networks folder in the project settings, and the system will pick it up into the pathfinding later. Cross-scene navigation is done using the `NavigatorComponent`, although I'm not sure it works right now.
Loading