Skip to content

Instantly share code, notes, and snippets.

@nornagon
Last active December 23, 2015 11:29
Show Gist options
  • Save nornagon/6629179 to your computer and use it in GitHub Desktop.
Save nornagon/6629179 to your computer and use it in GitHub Desktop.
component = (c) ->
c::_added = (gobj) ->
throw 'added twice' if @me?
@me = gobj
@world = @me.world
@init? @_args...
new_c = (args...) ->
r = new c
r._args = args
r
eval "#{c.name} = new_c"
new_c
class GameObject
constructor: (@world) ->
@_messages = {}
@components = {} # can have > 1 component of each type?
addComponent: (c) ->
@components[c.constructor.component_name] = c
for k,_ of c.__proto__ when k isnt 'init' and k isnt 'constructor'
@on k, do (k) -> (args...) -> c[k] args...
c._added? this
on: (e, f) ->
# TODO: optimisation: when there's only one listener, hook it up directly.
(@[e] = (args...) -> @send e, args...) unless @[e]?
(@_messages[e] ?= []).push f
send: (e, args...) ->
val = f.apply(this, args) for f in (@_messages[e] ? [])
val
remove: ->
@removeMe = yes
@send '_removed'
class World
constructor: ->
@entities = []
make: (template) ->
if typeof template is 'function' then template = template()
e = new GameObject this
for c in template
e.addComponent c
@add e
add: (e) ->
@entities.push e
e
update: (dt) ->
e.update? dt for e in @entities
@entities = (e for e in @entities when not e.removeMe)
Template = (fn) -> throw 'need a function' unless typeof fn is 'function'; fn
# e.g:
component class Countdown
init: (@sec, @opts = {}) ->
update: (dt) ->
if @sec > 0
@sec -= dt * (@opts.speed ? 1)
@me.send 'counted' if @sec <= 0
component class ExplodeOnCounted
counted: ->
console.log 'boom!'
@me.remove()
# ...
Bomb = Template ({seconds}) -> [
Countdown seconds, speed: 2
ExplodeOnCounted()
]
w = new World
w.make Bomb seconds: 3
w.update(2)
w.update(2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment