/
object.coffee
92 lines (71 loc) · 3.04 KB
/
object.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
{EventEmitter} = require 'events'
## WorldObject
# The base class for all objects living in the game world.
class WorldObject extends EventEmitter
# The back-reference to the `World` instance this object belongs to.
world: null
# An index in the world object list for this object.
idx: null
# The `updatePriority` is an arbitrary value that is used to determine the order in which objects
# are updated. Objects are sorted in descending order of priority, so high priority objects get
# updated before others.
updatePriority: 0
# Instantiating a `WorldObject` is done using `world.spawn(MyObject, params...);`. This wraps the
# call to the actual constructor, and the world can thus keep track of the object.
#
# Any `spawn` parameters are passed to the `spawn` method of this object. The constructor itself
# is usually bare-bones, only receiving and setting the `world` attribute, and adding listeners.
constructor: (@world) ->
#### Abstract methods
# These methods are called at key moments during the object's life span. They are called before
# the related events are emitted. All of these are optional to implement.
spawn: ->
update: ->
destroy: ->
#### Events
# You can install listeners for any of the following events in the constructor, or through
# references from within other objects:
#
# * `spawn`
# * `update`
# * `destroy`
#
# These are called after the related methods defined above.
# There is also a special event:
#
# * `finalize`
#
# The finalize event is called when you can be completely sure the object is gone. This is more
# definitive than `destroy` for example, because networking may still revive an object after
# such an event.
#### Helpers
# This helper is used to track references to other objects. The idea is to keep track of
# listeners installed on the other object, which directly or indirectly (through a closure) hold
# a back-reference. If we go away, or the reference is cleared, these listeners will be cleaned
# up as well.
#
# We can't really create proxies in JavaScript (yet), so this tries to make things as painless
# as possible. The `attribute` of this object is set to a thin wrapper. You may dereference
# simply by doing: `@other.$.something`. However, to add an event listener on the other object
# you do *not* dereference, but instead do: `@other.on 'someEvent', someHandler`.
ref: (attribute, other) ->
return this[attribute] if this[attribute]?.$ == other
this[attribute]?.clear()
return unless other
this[attribute] = r = { $: other, owner: this, attribute }
r.events = {}
r.on = (event, listener) ->
other.on event, listener
(r.events[event] ||= []).push listener
r
r.clear = ->
for event, listeners of r.events
for listener in listeners
other.removeListener event, listener
r.owner.removeListener 'finalize', r.clear
r.owner[r.attribute] = null
r.on 'finalize', r.clear
r.owner.on 'finalize', r.clear
r
## Exports
module.exports = WorldObject