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 module gaem
.runner
.objects
is aliced
;
21 import gaem
.runner
.strpool
;
22 import gaem
.runner
.value
;
23 import gaem
.runner
.sprites
;
26 // ////////////////////////////////////////////////////////////////////////// //
27 // each instance is registered in all it's parent objects instance lists
29 private __gshared
uint[string
] fields
;
32 package(gaem
.runner
) short allocateFieldId (string name
) {
33 assert(name
.length
> 0);
34 if (auto fpi
= name
in fields
) return cast(short)*fpi
;
35 auto fid
= cast(uint)fields
.length
;
36 if (fid
> short.max
) assert(0, "too many fields");
38 return cast(short)fid
;
42 private enum PredefinedFields
= [
65 // create `fi_xxx` variables
68 foreach (string name
; PredefinedFields
) res
~= "private __gshared uint fi_"~name
~";\n";
72 // create predefined fields
73 shared static this () {
76 foreach (string name
; PredefinedFields
) res
~= "fi_"~name
~" = allocateFieldId(`"~name
~"`);\n";
82 package(gaem
.runner
) uint fieldCount () { pragma(inline
, true); return cast(uint)fields
.length
; }
85 // ////////////////////////////////////////////////////////////////////////// //
86 // game object (instance template)
89 ObjectTpl parent
; // 0: no parent -- root object
90 uint idx
; // globally unique index (should never be zero)
98 uint[][GMEvent
.Type
.max
+1] events
; //TODO
100 InstList ilist
; // all instances with this object in parent chain
103 private __gshared ObjectTpl
[] objects
; // 0 is unused
104 shared static this () { objects
.length
= 1; }
106 enum ObjIdAll
= -666;
109 // ////////////////////////////////////////////////////////////////////////// //
110 // circular double-linked list
115 void append (InstProxy o
) {
116 if (o
is null) return;
117 assert(o
.prev
is null);
118 assert(o
.next
is null);
119 // append to circular list
124 } else if (head
.next
is head
) {
125 // list has only one item
126 o
.prev
= o
.next
= head
;
127 head
.prev
= head
.next
= o
;
129 // list has more than one item
130 auto tail
= head
.prev
;
131 o
.prev
= tail
; // previous is list tail
132 o
.next
= head
; // next is list head
139 void remove (InstProxy o
) {
140 if (o
is null || o
.prev
is null) return;
141 assert(head
!is null);
142 // remove from circular list
143 if (head
.prev
is head
) {
148 // list has more than one item
149 if (head
is o
) head
= head
.next
; // deleting head item, move head
150 o
.prev
.next
= o
.next
;
151 o
.next
.prev
= o
.prev
;
153 o
.prev
= o
.next
= null;
159 // proxy for instance lists
160 private final class InstProxy
{
162 InstProxy prev
, next
;
165 this (Instance aself
, ObjectTpl aparent
=null) {
168 if (aself
!is null) {
169 if (aparent
!is null) aparent
.ilist
.append(this); else iall
.append(this);
173 void removeFromLists () {
175 if (parent
!is null) parent
.ilist
.remove(this); else iall
.remove(this);
181 // ////////////////////////////////////////////////////////////////////////// //
182 private __gshared InstList iall
; // all created instances
183 // single-linked list of all dead instances
184 private __gshared Instance deadList
;
188 // ////////////////////////////////////////////////////////////////////////// //
189 final class Instance
{
191 enum IdStart
= 100000;
192 __gshared
uint nextid
= IdStart
;
193 __gshared Instance
[uint] instById
;
194 __gshared
uint curStep
= 0;
197 Instance deadNext
; // in `deadList`
205 uint stepMark
; // if this != curStep, the object is just created and should be skipped
207 this (ObjectTpl aparent
) {
209 proxies
~= new InstProxy(this); // add to list of all instances
211 fields
.length
= fieldCount
;
213 // copy initial fields from parent object
214 if (aparent
!is null) {
215 fields
.ptr
[fi_id
] = Value(mId
);
216 fields
.ptr
[fi_sprite_index
] = Value(aparent
.sprite_index
);
217 fields
.ptr
[fi_mask_index
] = Value(aparent
.mask_index
);
218 fields
.ptr
[fi_solid
] = Value(aparent
.solid
);
219 fields
.ptr
[fi_visible
] = Value(aparent
.visible
);
220 fields
.ptr
[fi_depth
] = Value(aparent
.depth
);
221 fields
.ptr
[fi_persistent
] = Value(aparent
.persistent
);
223 // add to parents' lists
224 while (aparent
!is null) {
225 proxies
~= new InstProxy(this, aparent
);
226 aparent
= aparent
.parent
;
228 stepMark
= curStep
+1;
232 bool isInstanceOf (int objid
) {
233 if (objid
== ObjIdAll
) return true;
234 if (objid
<= 0 || objid
>= objects
.length || mParent
is null) return false;
235 for (ObjectTpl p
= objects
.ptr
[objid
]; p
!is null; p
= p
.parent
) if (mParent
is p
) return true;
240 if (deadNext
is null) {
244 debug(objlist
) { import core
.stdc
.stdio
: printf
; printf("* instance %u of type '%.*s' marked as dead\n", mId
, cast(uint)mParent
.name
.length
, mParent
.name
.ptr
); }
250 void advanceFrame () {
251 pragma(inline
, true);
255 // ////////////////////////////////////////////////////////////////////// //
258 private static struct Iterator
{
259 InstProxy head
; // starting instance
260 InstProxy cur
; // current instance
261 Instance si
; // instance for single-instance iterator
263 private __gshared Iterator
[] iters
;
264 private __gshared
uint itersUsed
= 1;
266 shared static this () {
267 iters
.length
= 1024; // arbitrary number
270 private uint newIId () {
271 pragma(inline
, true);
272 auto iid
= itersUsed
++;
273 if (iid
== iters
.length
) iters
.length
+= 1024;
277 // create new iterator, return iid
278 uint newIterator (int objid
) {
279 if (itersUsed
>= short.max
) assert(0, "too many iterators");
281 if (objid
>= IdStart
) {
282 if (auto i
= cast(uint)objid
in instById
) {
284 iters
.ptr
[iid
].si
= *i
;
290 if (objid
== ObjIdAll
) {
291 if (iall
.head
is null) return 0; // no instances yet
293 with (iters
.ptr
[iid
]) head
= cur
= iall
.head
;
297 if (objid
<= 0) return 0;
298 if (objid
< objects
.length
) {
300 if (auto proxy
= objects
.ptr
[objid
].ilist
.head
) {
302 with (iters
.ptr
[iid
]) head
= cur
= proxy
;
310 // returns current object or 0 on completion, move iterator to next object
311 uint iteratorNext (uint iid
) {
312 if (iid
== 0 || iid
>= itersUsed
) return 0;
313 auto it
= &iters
.ptr
[iid
];
314 if (it
.head
is null) {
315 if (it
.si
is null) return 0; // dead iterator
316 auto res
= (!it
.si
.mDead ? it
.si
.mId
: 0);
322 auto ri
= it
.cur
.self
;
323 if ((it
.cur
= it
.cur
.next
) is it
.head
) it
.head
= it
.cur
= null;
324 if (!ri
.mDead
) return ri
.mId
; // good instance
325 // bad instance, move on
326 } while (it
.cur
!is null);
327 return 0; // no more instances
330 void freeAllIterators () {
332 foreach (ref it
; iters
[1..itersUsed
]) { it
.head
= it
.cur
= null; it
.si
= null; }
338 // return `true` from delegate to stop
339 // will skip dead instances
340 Instance
forEach (int objid
, scope bool delegate (Instance inst
) dg
) {
343 if (objid
>= IdStart
) {
344 if (auto i
= cast(uint)objid
in instById
) return (dg(*i
) ?
*i
: null);
349 if (objid
== ObjIdAll
) {
350 head
= cur
= iall
.head
;
351 } else if (objid
> 0 && objid
< objects
.length
) {
352 if ((head
= cur
= objects
.ptr
[objid
].ilist
.head
) is null) return null;
358 if (!cur
.self
.mDead
&& dg(cur
.self
)) return cur
.self
;
359 if ((cur
= cur
.next
) is head
) break;
365 void freeDeadObjects () {
366 while (deadList
!is null) {
367 assert(deadList
.mDead
);
368 foreach (InstProxy px
; deadList
.proxies
) px
.removeFromLists();
369 deadList
= deadList
.deadNext
;