Add markdown documentation

This commit is contained in:
2026-02-05 12:40:27 -05:00
parent f0228083c8
commit 12f8062d9a
22 changed files with 2853 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
### Attention Lock
Note: at this time, none of the following is implemented.
Attention lock is a new mechanism that is used when you want to force the player to give his *undivided* attention to a single activity. Attention lock is particularly relevant for turn-based combat. Lets say you have a turn-based combat in progress. You dont want the player to wander away and grow flax when its his turn to attack.
The attention lock mechanism can affect the following aspects of the game:
- Stops the player from walking around (or walking away).
- Stops the player from clicking on unrelated sprites.
- Hides the gui interfaces of unrelated sprites.
- Forces the gui of a sprite to appear even if the user didnt click on that sprite.
- Directs the camera to point at a specific place, or a specific object.
## Enabling Attention Lock
The mechanism is as follows: the player sprite has a single field, *actor.attention*, which is a pointer to a sprite that is trying to hold the players attention. There is a second field, *actor.attentionID*, which must be equal to *place.attentionID* of the attention-holding sprite. If *attentionID* doesnt match, then *actor.attention* field is considered no longer valid, and the attention is considered to be not held. With that protocol, it is easy to implement these functions:
- function attentionClear(place)
- Release the attention of all sprites paying attention to place.
- Implementation: set attentionID to a new unique ID.
- function attentionHolder(actor)
- Return the ID of the sprite holding actors attention.
- Implementation: returns actor.attention after verifying attentionID match.
- function attentionGrab(place, actor):
- Cause place to grab the attention of actor.
- Implementation: sets actor.attention to place, copies attentionID to actor.
This is designed to be resilient to bugs: lets say a battleground is holding the attention of some actor. If the battleground gets bulldozed, attention is implicitly released. Or if the battleground starts a new battle, attention of previous combatants would be implicitly released. In short, it should be pretty easy to clean up messes caused by buggy sprites grabbing attention.

View File

@@ -0,0 +1,34 @@
## Mechanisms Discussed:
- Battlegrounds - using a sprite to implement combat.
- Battle friends - how the friend system (separate) might designate “battle friends.”
- Camera lock - a means where a sprite can force the camera to look in a specific place.
- Attention lock - a means where a sprite can prevent a player from doing anything else.
Battlegrounds:
The JRPG combat system expects the god-mode player to build “battleground” in various places throughout the world. A “battleground” marks a physical place where combat can take place, and it defines the parameters of that combat. The battleground sprite contains all the combat-related scripts.
Every NPC that can fight names a single combat arena. If a player picks a fight with that NPC, then the battle takes place in the specified arena. The players automatically run (or get teleported) to the left side of the arena, and the NPC runs to the right side of the arena, and the battle takes place.
Combatant Selection:
The initial step of the combat is that a *single* player picks a fight with a *single* NPC. But the battleground object then drags additional NPCs and players into the fight.
NPCs: the battleground scans the vicinity for other NPCs that are linked to the battleground. It brings in any other NPC linked to that battleground. There may be additional rules that govern this.
Players: the battleground scans the area for other players that are “battle friends” with the player. It picks the closest ones, and brings them into combat. If that isnt enough people on the player side, the battleground scans for non-friend players and brings them in instead.
Sometimes, the battleground might want to draw another player into the combat, but it might not be able to, because that other player is “too busy.” For example, the other player might already be involved in a different combat.
The friend system is also a plugin. The friend system plugin should be required to export a function “is_battle_friend” to make it possible for the combat plugin to interface with the friend system plugin.
Attention Lock:
When the player enters a combat, the battleground puts the player into “attention lock.” Attention lock is intended for activities where the player is supposed to give his full attention to the activity, and not do other things at the same time. There is a separate document that describes attention lock in greater detail. The attention lock mechanism is used to prevent the player from walking away from the combat, and prevents him from growing flax or doing other irrelevant activities while combat is in progress.
Turn-Based Combat:
In this combat system, all players move at the same time, then, all NPCs go at the same time. The players have 10 seconds to specify which attack they want to use. If a player fails to specify an attack in 10 seconds, then a simple combat AI takes over and makes a choice for them. The goal of the combat AI is to give players whose connection is lagging a fair chance.
When the 10 seconds are up, the battleground animates the combat round: it shows the players attacking one by one, then it shows the monsters attacking. The battleground sprite controls all the animation during this period.

View File

@@ -0,0 +1,17 @@
This document lists all the configurable elements in the game.
The engine has a list of plugins that *must* be provided in order for the game to be operational. For example. you *must* choose a combat system plugin. The plugin is a lua file. When you select a lua file to fulfill the combat system requirement, the lua file is checked to verify that it defines all the functions that a combat system must define. For most plugin slots, a null plugin is available - for example, a “no combat” combat system plugin.
Combat System: a Plugin.
Friend System: a Plugin.
- Must export a function “battle_partner(A,B)” that returns true if player A has volunteered to help player B win fights. This exported function may be used by the combat system plugin.
Camera Control: Manually controlled, but can be overridden by sprites
- The camera system is an interactive camera control system like in the original Tale engine. (Not necessarily the same interactive control system). Its mostly unscripted, but sprites can take control when they want to. This will involve some set of flags that I havent figured out yet.
Skill Tree: a Database of Skills
- Skill trees are a hardwired part of the game. Any particular game can choose not to use the skill tree system, or to have an empty skill tree, but the skill tree systems code is always loaded.

View File

@@ -0,0 +1,43 @@
## Login System
When you first connect to the server, the server calls *World::create_login_actor()* on the master world model. This creates a tangible of class *login*. This login tangible becomes the actor until further notice. On the server side, the ID of this actor is associated with the connection. This ID is also forwarded to the client so that the client may know its actor ID.
The *login* is an actor like any other actor. In theory, its perfectly possible for the *login* to walk around and interact with objects. However, its more commonly the case that the login script prompts the player for a username and password. The login script then uses the lua *redirect(old_actor_id, new_actor_id)* command [not implemented yet].
In standalone mode, the standalone client mimics this sequence.
## Current Actor, Current Place, Current ID Player Pool flag.
Before a thread is awakened (by calling lua_resume or lua_pcall from C), the actorid and placeid are is stored in World::lthread_actor_id and World::lthread_place_id. As soon as lua_resume or lua_pcall returns, these two variables are cleared. That way, whenever any thread is running, those two variables contain the current actor and place. These two variables can be fetched using the lua functions tangible.actor and tangible.place.
Of course, the lua thread scheduling code (which is responsible for calling lua_resume) needs to know the actorid and placeid in order to be able to put them into World::lthread_actor_id and World::lthread_place_id. That is possible because the thread info table stores the actor and implicitly the place.
In addition to “current actor” and “current place,” we also store “current ID player pool flag.” This flag determines whether a thread uses the players ID pool, or the global ID pool to allocate IDs. In general, a thread uses the player pool if its just been awakened by “invoke_plan”. It uses the global pool if its been awakened by the system clock (ie, after a “wait” statement). This flag is in World::lthread_use_ppool, and in the thread info table.
## Deleted Tangibles and Tangible Garbage Collection
Tangibles can be in three states: “created”, “deleted,” and “nothing”. When a tangible is in the created state,, the following things are true:
- Theres a lua portion of the tangible.
- Theres a C++ portion of the tangible.
- The lua portion has a class, a metatable, and several important fields.
When a tangible is in the deleted state, the following things are true:
- Theres still a lua portion, but its minimal.
- Theres no C++ portion of the tangible.
- The lua portion still has a class and a metatable, but its mostly blank and writes are forbidden.
When a tangible is in the nothing state, nothing is stored. No lua portion, no C++ portion.
The function LuaStack::maketan retrieves a tangible. If that tangible doesnt exist, it creates a tangible in the deleted state and returns that.
The function World::tangible_make creates a tangible. If a deleted tangible with the same ID already exists, that deleted tangible is converted into a created tangible. The function World::tangible_delete transforms a created tangible into a deleted tangible.
Were going to have to write our own garbage collector to convert deleted tangibles into nothing tangibles only when there are no references to the deleted tangible.
## Makeclass
Were going to store a table in the registry, “classes”, that maps names to class tables. When you call makeclass, it creates a class in this table and copies it into the global environment. If for some reason the global environment gets trashed, the “classes” table in the registry will still be good. In this way, we can guarantee that the class of a given name is really a unique table.
If we want class tables to be garbage collected (as opposed to just cleared), were going to have to write our own garbage collector to handle that.

View File

@@ -0,0 +1,25 @@
### Solutions to Hard Problems
**Saving the Game:** This is accomplished by serializing the lua state using eris, and serializing the rest of the C++ data structures the usual way.
**Snapshotting for Client-Side Prediction:** we serialize the synchronous world model (using the save-game serialization code) to save it. Then we apply predictive commands, and it becomes the asynchronous model. When necessary, we roll it back to its synchronous state.
### []() Solutions we Didnt Use
**Snapshotting Lua:** the function lua_newstate accepts a memory allocation function as a parameter. Using this hook, we can keep track of every block of memory that lua allocates. To snapshot luas state, its sufficient to memcpy all its memory blocks into a temporary buffer. To restore luas state, memcpy everything back, and also restore the heap to the state it was in. We built a proof-of-concept of this and it works. We could use this on the client side for asynchronous/synchronous work, instead of using eris serialization.
**Collision Detection:** We could have a function that is a bit different on the client vs. server. On the server:
```cpp
bool TryEnter(Location OldLoc, Location NewLoc)
```
And on the client:
```cpp
void TryEnter(Location OldLoc, Location NewLoc, Location &NewLocMod)
// NewLocMod will always be set to a value where the server version of this call will
// succeed, or to OldLoc.
```
We could have a data structure that is a pancake stack of heightfields. The area between odd and even heightfields is enterable. Given an x,y,z coordinate, it is easy to find the x,y,z coordinate that represents the floor. The resolution on the client will be higher of course.

View File

@@ -0,0 +1,34 @@
## Definitions
- Synchronous Models and Connections have a one-to-one mapping. The terms are used interchangeably.
- Sprites are objects in the 3D world. Each has a primary table associated with it.
- Tables are Lua Tables. The runtime assigns each a two-part ID consisting of a Sprite ID and a Table Index that starts at 1 for a Sprites Primary Table.
**Synchronous Models** - Each has a few values within it, which are not directly accessible from Lua:
- Position (Plane, X, Y, Z)
- SpriteID - If present, the connections Position gets updated periodically based on the Position of the Sprite
**Sprites** - These also have a few values which are not directly accessible from Lua:
- Position (Plane, X, Y, Z)
- Sprite ID
- NextTableID
**Tables** - Each table has a mode with hints to the difference transmitter when its OK to suppress it from a synchronous model. Each Table contains a two part ID consisting of a Sprite ID and a Table Index. A Table has an inferred position based on its Sprite ID. Some modes are:
- A: Never Suppress (A Global Table)
- B: Always Suppress (A Secret Table)
- C: Suppress from SMs that are far away (A Sprites Primary Table, a Players publicly viewable data)
- D: Suppress when a connections SpriteID differs from the Tables Sprite ID (A players sensitive data)
- E: Call the Table.Transmit(OtherSpriteID) to determine whether to transmit. (Does Lua have a way to make a const call that prohibits storing data in any table fields?)
**Table Creation** - I am assuming that whichever version of GUI->MethodCall we use there will be the notion of “Actor” and “Place” as in our old engine. The default mode of a table is:
- When Place is the same as Actor: D
- When Place is different from Actor: C
**Difference Transmitter Manipulation** - Designers can explicitly override the default mode of a Table with an intrinsic function:
- SetDiffMode(Table,Mode)
- SetOwner(Table,Sprite)

View File

@@ -0,0 +1,15 @@
Every game has a database of possible skills. The database can be edited on the fly.
Skills have:
- Name: eg, “handgun accuracy”
- Point cost: how many skill points do you need to increment this skill. Note that the game can bypass the point system by procedurally incrementing the players skill.
- Maximum skill level: how high can this skill be incremented. Again, the game can bypass this by procedurally incrementing the players skill.
- A graphical icon representing the skill.
- Prerequisites list: other skills you have to have before incrementing this skill.
The skill tree isnt hardwired into code. Instead, theres an in-game skill tree editor which you can use when youre in god mode. We should also provide facilities so that two different games can easily share a skill tree.
The engine should provide a built-in character progression screen, so that the player can spend skill points to advance along the skill tree.
When a player comes into a game, he brings his skills with him. There are two preliminary checks: first, that he hasnt spent too many skill points. Second, every skill he has is checked against the skill tree. If he has acquired a skill in violation of the prerequisites, then that skill is “greyed out” until he buys the prerequisites.