Skip to content

Instantly share code, notes, and snippets.

@SupremeTechnopriest
Last active July 24, 2019 02:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SupremeTechnopriest/b5bd84d04f12d80b07e21d118d725556 to your computer and use it in GitHub Desktop.
Save SupremeTechnopriest/b5bd84d04f12d80b07e21d118d725556 to your computer and use it in GitHub Desktop.
Finite State Game Mode Script
properties:
scripts:
- /game-modes/sandbox/fsm.js
- /game-modes/sandbox/start.js
import StateMachine from 'javascript-state-machine'
export default function (props) {
this.on('start', start.bind(this, props))
}
function start (props) {
this.fsmManager = new FSMManager(this.engine)
}
class FSMManager {
constructor (engine) {
this.engine = engine
this.entities = {}
}
addFSM (entity, data) {
// Create an entry for this client if it doesnt exist
if (!this.entities[entity.nid]) this.entities[entity.nid] = {}
// Create the method
const methods = {}
Object.keys(data.methods).forEach(method => {
methods[method] = (...args) => {
let { op, component, defaults, values } = data.methods[method]
if (values) defaults = values
if (!defaults) defaults = {}
// Process automated operations
switch (op) {
case 'add':
this.engine.addComponent(entity, component, defaults)
break
case 'remove':
this.engine.removeComponent(entity, component)
break
case 'update':
if (this.engine.getComponent(entity, component)) {
this.engine.updateComponent(entity, component, defaults)
}
break
default:
// NoOp
}
// Fire an event for scripts to consume
this.engine.emit(method, entity, ...args)
}
})
// Create the fsm
const fsm = new StateMachine({
init: data.init,
transitions: data.transitions,
methods
})
// Add a record for this entity
if (data.name) {
this.entities[entity.nid][data.name] = fsm
} else {
this.entities[entity.nid] = fsm
}
}
setState (entity, name, state, props = []) {
const fsm = name ? this.fsms[entity.nid][name] : this.fsms[entity.nid]
if (fsm.can(state)) fsm[state](...props)
}
removeFSM (entity, name) {
if (name) {
delete this.entities[entity.nid][name]
} else {
delete this.entities[entity.nid]
}
}
}
name: Player
components:
- POSITION
- ROTATION
fsm:
init: idle
transitions:
-
name: emote
from: idle
to: emoting
methods:
onEmote:
-
op: add
component: EMOTE
defaults:
emote: wave
// The this scope of .scripts and .systems is the modngn instance
export default function (props) {
this.on('update', update.bind(this))
this.on('client-connected', onConnect.bind(this, props))
this.on('client-disconnected', onDisconnect.bind(this))
this.on('onEmote', (entity, emote) => { // do stuff with emote })
}
function onConnect (props, { client, clientData, callback }) {
// Create a player body for this client.
const entity = this.entities.Player()
// Add fsm to player
const fsm = this.getAddress('player.body.entity.fsm')
this.fsmManager.addFSM(entity, fsm)
this.fsmManager.setState(entity, 'emote', 'wave')
// Establish a relation between entity and client.
entity.client = client
client.entity = entity
// Define the view. This area of the game visible to
// this client. All else is culled.
client.view = {
x: entity.x,
y: entity.y,
halfWidth: 1000,
halfHeight: 1000
}
// Send the call back
callback({ accepted: true, text: 'Welcome!' }) // eslint-disable-line
}
function onDisconnect ({ client }) {
this.fsmManager.removeSpine(client.entity)
this.engine.removeEntity(client.entity)
}
function update ({ delta, tick, now }) {
// Set each clients view
this.engine.instance.clients.forEach(client => {
client.view.x = client.entity.x
client.view.y = client.entity.y
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment