1 /* DooM2D: Midnight on the Firing Line
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 // uniform grid to speed up collision detection
19 module d2dunigrid
is aliced
;
21 private import dacs
.actor
;
22 private import d2dadefs
: AF_NOCOLLISION
;
25 // ////////////////////////////////////////////////////////////////////////// //
29 void ugInit (int width
, int height
, int cellSizeX
=32, int cellSizeY
=32) {
30 assert(width
> 0 && height
> 0);
31 assert(cellSizeX
> 0 && cellSizeY
> 0);
32 gwidth
= (width
+cellSizeX
-1)/cellSizeX
;
33 gheight
= (height
+cellSizeY
-1)/cellSizeY
;
36 grid
.length
= gwidth
*gheight
;
37 grid
[] = GridBucket
.init
;
42 foreach (ref bt; grid
) bt.actCount
= bt.mactCount
= 0;
47 void ugModifyRadius(bool doput
) (uint aid
, int x
, int y
, int radius
) {
49 doModifyAABB
!doput(aid
, x
-radius
, y
-radius
, x
+radius
, y
+radius
);
53 // `x1` and `y1` are included
54 void ugModifyAABB(bool doput
) (uint aid
, int x0
, int y0
, int x1
, int y1
) {
56 doModifyAABB
!doput(aid
, x0
, y0
, x1
, y1
);
60 uint[] ugHitListRadius (uint[] dest
, int x
, int y
, int radius
) {
62 return getHitListAABB(dest
, x
-radius
, y
-radius
, x
+radius
, y
+radius
);
66 uint[] ugHitListAABB (uint[] dest
, int x0
, int y0
, int x1
, int y1
) {
68 return getHitListAABB(dest
, x0
, y0
, x1
, y1
);
73 void ugActorModify(bool doput
) (ActorId a
) {
76 if (a
.fget_flags
&AF_NOCOLLISION
) return;
80 int ar
= a
.fget_radius
;
81 int ah
= a
.fget_height
;
82 if (ar
< 1 || ah
< 1) return;
84 ugModifyAABB
!doput(a
.id
, ax
-ar
, ay
-ah
+1, ax
+ar
, ay
);
89 uint[] ugActorPossibleHitList (ActorId a
, uint[] dest
) {
90 if (!a
.valid
) return null;
92 if (a
.fget_flags
&AF_NOCOLLISION
) return null;
96 int ar
= a
.fget_radius
;
97 int ah
= a
.fget_height
;
98 if (ar
< 1 || ah
< 1) return null;
100 return ugHitListAABB(dest
, ax
-ar
, ay
-ah
+1, ax
+ar
, ay
);
105 uint[] ugActorHitList (ActorId a
, uint[] dest
) {
106 auto res
= ugActorPossibleHitList(a
, dest
);
109 for (usize pos
= 0; pos
< res
.length
; ++pos
) {
110 bool killIt
= (res
.ptr
[pos
] == a
.id
);
112 // check for real collision
113 import dengapi
: actorsOverlap
;
114 killIt
= !actorsOverlap(a
, ActorId(res
.ptr
[pos
]));
117 // no collision, remove this actor from list
118 foreach (immutable c
; pos
+1..res
.length
) res
.ptr
[c
-1] = res
.ptr
[c
];
120 --pos
; // compensate loop increment
128 // ////////////////////////////////////////////////////////////////////////// //
131 mixin(Actor
.FieldGetMixin
!("x", int));
132 mixin(Actor
.FieldGetMixin
!("y", int));
133 mixin(Actor
.FieldGetMixin
!("height", int));
134 mixin(Actor
.FieldGetMixin
!("radius", int));
135 mixin(Actor
.FieldGetMixin
!("flags", uint));
139 uint[32] acts
; // actorids
141 uint[] macts
; // dynamically grows
142 uint mactCount
; // actorids
144 @disable this (this); // no copies
147 __gshared
int gwidth
, gheight
; // in cells
148 __gshared
int gcw
, gch
; // cell size
149 __gshared GridBucket
[] grid
;
152 GridBucket
* calcNormAABB (ref int x0
, ref int y0
, ref int x1
, ref int y1
) {
159 if (x1
>= gwidth
) x1
= gwidth
-1;
160 if (y1
>= gheight
) y1
= gheight
-1;
161 if (x0
> x1 || y0
> y1 || x1
< 0 || y1
< 0 || x0
>= gwidth || y0
>= gheight
) return null;
162 return grid
.ptr
+y0
*gwidth
+x0
;
166 // `x1` and `y1` are included
167 void doModifyAABB(bool doadd
) (uint aid
, int x0
, int y0
, int x1
, int y1
) @trusted {
168 GridBucket
* gdata
= calcNormAABB(ref x0
, ref y0
, ref x1
, ref y1
);
169 if (gdata
is null) return;
170 for (; y0
<= y1
; ++y0
, gdata
+= gwidth
) {
172 for (int x
= x0
; x
<= x1
; ++x
, ++p
) {
176 foreach (uint id
; p
.acts
[0..p
.actCount
]) if (id
== aid
) { found
= true; break; }
177 if (!found
) foreach (uint id
; p
.macts
[0..p
.mactCount
]) if (id
== aid
) { found
= true; break; }
179 if (p
.actCount
< p
.acts
.length
) {
181 p
.acts
.ptr
[p
.actCount
++] = aid
;
182 } else if (p
.mactCount
< p
.macts
.length
) {
184 p
.macts
.ptr
[p
.mactCount
++] = aid
;
186 assert(p
.mactCount
== p
.macts
.length
);
187 // alas, we have to allocate
188 p
.macts
.assumeSafeAppend
; // no, really
190 p
.macts
.ptr
[p
.mactCount
++] = aid
;
195 foreach (immutable idx
, uint id
; p
.acts
[0..p
.actCount
]) {
199 foreach (immutable c
; idx
+1..p
.actCount
) p
.acts
.ptr
[c
-1] = p
.acts
.ptr
[c
];
204 foreach (immutable idx
, uint id
; p
.macts
[0..p
.mactCount
]) {
207 foreach (immutable c
; idx
+1..p
.mactCount
) p
.macts
.ptr
[c
-1] = p
.macts
.ptr
[c
];
219 // `x1` and `y1` are included
220 uint[] getHitListAABB (uint[] dest
, int x0
, int y0
, int x1
, int y1
) @trusted {
221 // we have alot of memory!
222 __gshared
uint[65536] wasSeen
= 0;
223 __gshared
uint hitCount
= 0;
225 GridBucket
* gdata
= calcNormAABB(ref x0
, ref y0
, ref x1
, ref y1
);
226 if (gdata
is null) return null;
228 if (++hitCount
== 0) {
229 // rare case of overflow
236 bool addToRes (uint id
) {
237 if (wasSeen
.ptr
[id
&0xffff] != hitCount
) {
238 if (dpos
>= dest
.length
) return false;
239 wasSeen
.ptr
[id
&0xffff] = hitCount
;
240 dest
.ptr
[dpos
++] = id
;
245 mainloop
: for (; y0
<= y1
; ++y0
, gdata
+= gwidth
) {
247 for (int x
= x0
; x
<= x1
; ++x
, ++p
) {
248 foreach (uint id
; p
.acts
[0..p
.actCount
]) if (!addToRes(id
)) break mainloop
;
249 foreach (uint id
; p
.macts
[0..p
.mactCount
]) if (!addToRes(id
)) break mainloop
;
252 return dest
[0..dpos
];