-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
filter.go
117 lines (100 loc) · 2.93 KB
/
filter.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
package ecs
import (
"slices"
)
// Optional - Lets you view even if component is missing (func will return nil)
// With - Lets you add additional components that must be present
// Without - Lets you add additional components that must not be present
type Filter interface {
Filter([]CompId) []CompId
}
type without struct {
mask archetypeMask
}
// Creates a filter to ensure that entities will not have the specified components
func Without(comps ...any) without {
return without{
mask: buildArchMaskFromAny(comps...),
}
}
func (w without) Filter(list []CompId) []CompId {
return list // Dont filter anything. We need to exclude later on
// return append(list, w.comps...)
}
type with struct {
comps []CompId
}
// Creates a filter to ensure that entities have the specified components
func With(comps ...any) with {
ids := make([]CompId, len(comps))
for i := range comps {
ids[i] = name(comps[i])
}
return with{
comps: ids,
}
}
func (w with) Filter(list []CompId) []CompId {
return append(list, w.comps...)
}
type optional struct {
comps []CompId
}
// Creates a filter to make the query still iterate even if a specific component is missing, in which case you'll get nil if the component isn't there when accessed
func Optional(comps ...any) optional {
ids := make([]CompId, len(comps))
for i := range comps {
ids[i] = name(comps[i])
}
return optional{
comps: ids,
}
}
func (f optional) Filter(list []CompId) []CompId {
for i := 0; i < len(list); i++ {
for j := range f.comps {
if list[i] == f.comps[j] {
// If we have a match, we want to remove it from the list.
list[i] = list[len(list)-1]
list = list[:len(list)-1]
// Because we just moved the last element to index i, we need to go back to process that element
i--
break
}
}
}
return list
}
type filterList struct {
comps []CompId
withoutArchMask archetypeMask
cachedArchetypeGeneration int // Denotes the world's archetype generation that was used to create the list of archIds. If the world has a new generation, we should probably regenerate
archIds []archetypeId
}
func newFilterList(comps []CompId, filters ...Filter) filterList {
var withoutArchMask archetypeMask
for _, f := range filters {
withoutFilter, isWithout := f.(without)
if isWithout {
withoutArchMask = withoutFilter.mask
} else {
comps = f.Filter(comps)
}
}
return filterList{
comps: comps,
withoutArchMask: withoutArchMask,
archIds: make([]archetypeId, 0),
}
}
func (f *filterList) regenerate(world *World) {
if world.engine.getGeneration() != f.cachedArchetypeGeneration {
f.archIds = world.engine.FilterList(f.archIds, f.comps)
if f.withoutArchMask != blankArchMask {
f.archIds = slices.DeleteFunc(f.archIds, func(archId archetypeId) bool {
return world.engine.dcr.archIdOverlapsMask(archId, f.withoutArchMask)
})
}
f.cachedArchetypeGeneration = world.engine.getGeneration()
}
}