-
Notifications
You must be signed in to change notification settings - Fork 83
/
trigger.go
176 lines (158 loc) · 5.1 KB
/
trigger.go
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package event
import (
"sync"
"time"
"github.com/oakmound/oak/timing"
)
// Oak uses the following built in events:
//
// - EnterFrame: the beginning of every logical frame.
// Payload: (int) frames passed since this scene started
//
// - CollisionStart/Stop: when a PhaseCollision entity starts/stops touching some label.
// Payload: (collision.Label) the label the entity has started/stopped touching
//
// - MouseCollisionStart/Stop: as above, for mouse collision
// Payload: (mouse.Event)
//
// - KeyDown/Up: when a key is pressed down / lifted up.
// Payload: (string) the key pressed
//
// - Mouse events: MousePress, MouseRelease, MouseScrollDown, MouseScrollUp, MouseDrag
// Payload: (mouse.Event) details on the mouse event
//
// - AnimationEnd: Triggered on animations CIDs when they loop from the last to the first frame
// Payload: nil
//
// - ViewportUpdate: Triggered when the viewport changes.
// Payload: []float64{viewportX, viewportY}
// Trigger an event, but only
// for one ID. Use case example:
// on onHit event
func (id CID) Trigger(eventName string, data interface{}) {
go func(eventName string, data interface{}) {
eb := GetBus()
mutex.RLock()
iid := int(id)
if idMap, ok := eb.bindingMap[eventName]; ok {
if bs, ok := idMap[iid]; ok {
for i := bs.highIndex - 1; i >= 0; i-- {
triggerDefault((*bs.highPriority[i]).sl, iid, eventName, data)
}
triggerDefault((bs.defaultPriority).sl, iid, eventName, data)
for i := 0; i < bs.lowIndex; i++ {
triggerDefault((*bs.lowPriority[i]).sl, iid, eventName, data)
}
}
}
mutex.RUnlock()
}(eventName, data)
}
// TriggerAfter will trigger the given event after d time.
func (id CID) TriggerAfter(d time.Duration, eventName string, data interface{}) {
go func() {
timing.DoAfter(d, func() {
id.Trigger(eventName, data)
})
}()
}
// Trigger is equivalent to bus.Trigger(...)
// Todo: move this to legacy.go, see mouse or collision
func Trigger(eventName string, data interface{}) {
thisBus.Trigger(eventName, data)
}
// TriggerBack is equivalent to bus.TriggerBack(...)
func TriggerBack(eventName string, data interface{}) chan bool {
return thisBus.TriggerBack(eventName, data)
}
// TriggerBack is a version of Trigger which returns a channel that
// informs on when all bindables have been called and returned from
// the input event. It is dangerous to use this unless you have a
// very good idea how things will synchronize, as if a triggered
// bindable itself makes a TriggerBack call, this will cause the engine to freeze,
// as the function will never end because the first TriggerBack has control of
// the lock for the event bus, and the first TriggerBack won't give up that lock
// until the function ends.
//
// This inherently means that when you call Trigger, the event will almost
// almost never be immediately triggered but rather will be triggered sometime
// soon in the future.
//
// TriggerBack is right now used by the primary logic loop to dictate logical
// framerate, so EnterFrame events are called through TriggerBack.
func (eb *Bus) TriggerBack(eventName string, data interface{}) chan bool {
ch := make(chan bool)
go func(ch chan bool, eb *Bus, eventName string, data interface{}) {
ebtrigger(eb, eventName, data)
ch <- true
}(ch, eb, eventName, data)
return ch
}
// Trigger will scan through the event bus and call all bindables found attached
// to the given event, with the passed in data.
func (eb *Bus) Trigger(eventName string, data interface{}) {
go func(eb *Bus, eventName string, data interface{}) {
ebtrigger(eb, eventName, data)
}(eb, eventName, data)
}
func ebtrigger(eb *Bus, eventName string, data interface{}) {
mutex.RLock()
// Loop through all bindableStores for this eventName
for id, bs := range (*eb).bindingMap[eventName] {
// Top to bottom, high priority
for i := bs.highIndex - 1; i >= 0; i-- {
triggerDefault((*bs.highPriority[i]).sl, id, eventName, data)
}
}
for id, bs := range (*eb).bindingMap[eventName] {
if bs != nil && bs.defaultPriority != nil {
triggerDefault((bs.defaultPriority).sl, id, eventName, data)
}
}
for id, bs := range (*eb).bindingMap[eventName] {
// Bottom to top, low priority
for i := 0; i < bs.lowIndex; i++ {
triggerDefault((*bs.lowPriority[i]).sl, id, eventName, data)
}
}
mutex.RUnlock()
}
func triggerDefault(sl []Bindable, id int, eventName string, data interface{}) {
prog := &sync.WaitGroup{}
prog.Add(len(sl))
for i, bnd := range sl {
go func(bnd Bindable, id int, eventName string, data interface{}, prog *sync.WaitGroup, index int) {
handleBindable(bnd, id, data, index, eventName)
prog.Done()
}(bnd, id, eventName, data, prog, i)
}
prog.Wait()
}
func handleBindable(bnd Bindable, id int, data interface{}, index int, eventName string) {
if bnd != nil {
if id == 0 || GetEntity(id) != nil {
response := bnd(id, data)
switch response {
case UnbindEvent:
UnbindAll(BindingOption{
Event{
eventName,
id,
},
0,
})
case UnbindSingle:
binding{
BindingOption{
Event{
eventName,
id,
},
0,
},
index,
}.unbind()
}
}
}
}