-
Notifications
You must be signed in to change notification settings - Fork 144
Game Mechanic State pattern
Most of the game mechanics in AD follow consistent logic and structure. Based on this assumption, we created a framework for working with them in the form of GameMechanicState
class which simplified and made cleaner game state access all over the code. This generalization also well with the Effects system explained here: Link
But, the game mechanic declaration doesn't consist only of state class - additionally, we need to bind GameDatabase configs, add declaration function, and add some additional functions/accessors that are tightly coupled with the game mechanic. Currently, there's no standard for such declarations, and the following pattern is expected to give the guidelines for a consistent game mechanic code.
- (Optional) GameDatabase declaration. For game mechanics that have configurable elements.
- State class that extends
GameMechanicState
. This is a staple element between player object (PO) and game logic that allows the separation of PO structure from the logic code. - Accessor object created with the
createAccessor
function. It hasindex
property which contains all the GameDB entries wrapped into State class instances. Apart from that, it is itself a callable function that accepts an index as an argument. Example:Achievement(101)
. This is the very method that should be used when accessing state objects in-game logic code. - (Very Optional) Additional method or getter declaration for Accessor object. Very situational. Most likely, you want to place this functionality in the collection object (explained below). Example:
NormalChallenge.current
- Collection object. Contains all sorts of functions for non-state-specific operations. Usually contains the
all
property that contains all state instances. SincecreateAccessor
creates a sparse array, GameDB-basedall
should be created with acompact
array extension, for instance:all: Achievement.index.compact()
GameDB file
// 1. GameDB declaration
GameDatabase.challenges.normal = [
{
id: 1,
legacyId: 1,
isQuickResettable: false,
description: "Reach Infinity for the first time.",
reward: "First Dimension Autobuyer"
},
...
};
File with game logic
// 2. Class that extends `GameMechanicState`
class NormalChallengeState extends GameMechanicState {
constructor(config) {
super(config);
...
}
...
}
// 3. Accessor object
/**
* @param {number} id
* @return {NormalChallengeState}
*/
const NormalChallenge = NormalChallengeState.createAccessor(GameDatabase.challenges.normal);
// 4. Additional method or getter declaration for Accessor object
Object.defineProperty(NormalChallenge, "current", {
get: () => (player.challenge.normal.current > 0
? NormalChallenge(player.challenge.normal.current)
: undefined),
});
// 5. Collection object
const NormalChallenges = {
/** @type {NormalChallengeState[]} */
all: NormalChallenge.index.compact(),
completeAll() {
for (const challenge of NormalChallenges.all) challenge.complete();
},
...
};