trap switches
[dd2d.git] / dengapi.d
blobaa914750635b900efc1e2073b1c9b7e562b5755c
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 module dengapi is aliced;
20 private:
21 import core.atomic;
22 import core.thread;
23 import core.time;
25 import iv.glbinds;
26 import glutils;
27 import console;
28 import wadarc;
30 import d2dmap;
31 import d2dadefs;
32 import d2dgfx;
33 import dacs;
35 import d2dunigrid;
36 import render;
39 // ////////////////////////////////////////////////////////////////////////// //
40 private extern (C) void _d_print_throwable (Throwable t);
43 // ////////////////////////////////////////////////////////////////////////// //
44 import arsd.color;
45 import arsd.png;
48 // ////////////////////////////////////////////////////////////////////////// //
49 public void addInternalActorFields () {
50 // &0xffff: position in drawlist
51 // (>>16)&0xff: drawlist index
52 //Actor.addField("0drawlistpos", Actor.Field.Type.Uint);
56 // ////////////////////////////////////////////////////////////////////////// //
57 public {
58 enum PLK_UP = (1<<0);
59 enum PLK_DOWN = (1<<1);
60 enum PLK_LEFT = (1<<2);
61 enum PLK_RIGHT = (1<<3);
62 enum PLK_FIRE = (1<<4);
63 enum PLK_JUMP = (1<<5);
64 enum PLK_USE = (1<<6);
67 __gshared uint plrKeysLast;
68 __gshared uint plrKeyState;
71 public void plrKeyDown (uint plidx, uint mask) {
72 if (mask == 0 || plidx > 0) return;
73 plrKeysLast |= mask;
74 plrKeyState |= mask;
78 public void plrKeyUp (uint plidx, uint mask) {
79 if (mask == 0 || plidx > 0) return;
80 plrKeyState &= ~mask;
84 public void plrKeyUpDown (uint plidx, uint mask, bool down) {
85 if (down) plrKeyDown(plidx, mask); else plrKeyUp(plidx, mask);
89 void plrKeysFix (uint plidx) {
90 if (plidx > 0) return;
91 //conwritefln!"plrKeyState=0x%02x; plrKeysLast=0x%02x"(plrKeyState, plrKeysLast);
92 foreach (uint n; 0..12) {
93 if ((plrKeyState&(1<<n)) == 0) plrKeysLast &= ~(1<<n);
98 // ////////////////////////////////////////////////////////////////////////// //
99 void updateActorClasses (int iteration) {
100 foreach (string ctype; dacsClassTypes(iteration)) {
101 foreach (auto mod; dacsClassModules(ctype, iteration)) {
102 auto adef = registerActorDef(mod.rmoduletype.classtype, mod.rmoduletype.classname);
103 conwriteln("found info for actor '", mod.rmoduletype.classtype, ":", mod.rmoduletype.classname, "'");
105 auto animInitFn = FuncPool.findByFQMG(mod.name~".initializeAnim", ":void");
106 if (animInitFn !is null) adef.setAnimInitFunc(animInitFn);
109 auto initFn = FuncPool.findByFQMG(mod.name~".initialize", ":void:Actor");
110 if (initFn !is null) adef.setInitFunc(initFn);
113 auto thinkFn = FuncPool.findByFQMG(mod.name~".think", ":void:Actor");
114 if (thinkFn !is null) adef.setThinkFunc(thinkFn);
121 // ////////////////////////////////////////////////////////////////////////// //
122 private string modLoader (string modname) {
123 static string getTextFile (string fname) {
124 try {
125 auto res = loadTextFile(fname);
126 conwriteln("loading DACS module file '", fname, "' (", res.length, " bytes)");
127 if (res is null) res = "";
128 return res;
129 } catch (Exception) {}
130 return null;
133 string res;
135 //res = getTextFile(modname~".dacs");
136 //if (res !is null) return res;
138 res = getTextFile("scripts/"~modname~".dacs");
139 if (res !is null) return res;
140 assert(res is null);
142 string[] parts = ["scripts"];
143 string mn = modname;
144 while (mn.length > 0) {
145 int pos = 0;
146 if (mn[0] >= 'A' && mn[0] <= 'Z') ++pos;
147 while (pos < mn.length && (mn[pos] < 'A' || mn[pos] > 'Z')) ++pos;
148 if (mn[0] >= 'A' && mn[0] <= 'Z') {
149 parts ~= cast(char)(mn[0]+32)~mn[1..pos];
150 } else {
151 parts ~= mn[0..pos];
153 mn = mn[pos..$];
156 import std.array : join;
157 string path = parts.join("/")~".dacs";
158 res = getTextFile(path);
159 if (res !is null) return res;
160 assert(res is null);
162 assert(res is null);
163 throw new Exception("module '"~modname~"' not found");
167 __gshared int modIteration = 0;
169 public void loadWadScripts () {
170 if (moduleLoader is null) moduleLoader = (string modname) => modLoader(modname);
171 string[] mainmods;
172 try {
173 import std.array : split;
174 auto t = loadTextFile("scripts/dacsmain.txt");
175 foreach (string line; t.split('\n')) {
176 while (line.length && line[0] <= ' ') line = line[1..$];
177 if (line.length == 0 || line[0] == ';') continue;
178 while (line.length && line[$-1] <= ' ') line = line[0..$-1];
179 mainmods ~= line;
181 } catch (Exception e) { return; }
182 try {
183 //conwriteln("loading main DACS module '", mainmod, "'");
184 auto iter = modIteration++;
185 conwriteln("iteration=", iter);
186 foreach (string mod; mainmods) {
187 parseModule(mod, iter);
189 conwriteln("calling parseComplete; iteration=", iter);
190 parseComplete();
191 updateActorClasses(iter);
192 } catch (CompilerException e) {
193 _d_print_throwable(e);
194 conwriteln("PARSE ERROR: ", e.file, " at ", e.line);
195 conwriteln(e.toString);
196 assert(0);
201 public void scriptLoadingComplete () {
202 setupDAPI();
203 dacsFinalizeCompiler();
207 // ////////////////////////////////////////////////////////////////////////// //
208 public __gshared LevelMap map;
209 //public __gshared DACSVM dvm;
210 public __gshared uint prngSyncSeed = 0x29a;
211 public __gshared uint prngSeed = 0x29a; // unsynced
214 // ////////////////////////////////////////////////////////////////////////// //
215 // Park-Miller-Carta Pseudo-Random Number Generator, based on David G. Carta paper
216 // 31 bit of randomness
217 // seed is previous result, as usual
218 public uint prngR31next (uint seed) {
219 if (seed == 0) seed = 0x29a;
220 uint lo = 16807*(seed&0xffff);
221 uint hi = 16807*(seed>>16);
222 lo += (hi&0x7fff)<<16;
223 lo += hi>>15;
224 //if (lo > 0x7fffffff) lo -= 0x7fffffff; // should be >=, actually
225 lo = (lo&0x7FFFFFFF)+(lo>>31); // same as previous code, but branch-less
226 return lo;
230 // "synced" random, using common seed for all players
231 public uint syncrandu31 () {
232 pragma(inline, true);
233 return (prngSyncSeed = prngR31next(prngSyncSeed));
237 // "unsynced" random, seed isn't saved
238 public uint unsyncrandu31 () {
239 pragma(inline, true);
240 return (prngSeed = prngR31next(prngSeed));
244 // ////////////////////////////////////////////////////////////////////////// //
245 version(dacs_use_vm) {
246 // this is VAT function, argument order is reversed
247 void doWrite(bool donl) (FuncPool.FuncInfo fi, DACSVM vm) {
248 import std.stdio;
249 auto count = vm.popInt();
250 while (count-- > 0) {
251 auto type = vm.popInt();
252 switch (cast(VATArgType)type) {
253 case VATArgType.Int: write(vm.popInt()); break;
254 case VATArgType.Uint: write(vm.popUint()); break;
255 case VATArgType.Float: write(vm.popFloat()); break;
256 case VATArgType.StrId: write(StrId(vm.popUint()).get); break;
257 case VATArgType.ActorId: write("<actor>"); vm.popUint(); break; //TODO
258 default: write("<invalid-type>"); vm.popUint(); break; //TODO
261 static if (donl) writeln();
262 // push dummy return value
263 vm.pushInt(0);
265 } else {
266 extern(C) void doWrite(bool donl) (uint argc, ...) {
267 import core.vararg;
268 import std.stdio;
269 mainloop: while (argc >= 2) {
270 argc -= 2;
271 int tp = va_arg!int(_argptr);
272 switch (tp) {
273 case VATArgType.Int:
274 auto v = va_arg!int(_argptr);
275 write(v);
276 break;
277 case VATArgType.Uint:
278 auto v = va_arg!uint(_argptr);
279 write(v);
280 break;
281 case VATArgType.Float:
282 auto v = va_arg!float(_argptr);
283 write(v);
284 break;
285 case VATArgType.StrId:
286 auto v = StrId(va_arg!uint(_argptr)).get;
287 write(v);
288 break;
289 case VATArgType.ActorId:
290 auto v = ActorId(va_arg!uint(_argptr));
291 write("<actor:", v.valid, ":", v.id, ">");
292 //write("<actor>");
293 break;
294 default: write("<invalid-type>"); break mainloop;
297 static if (donl) writeln();
302 // ////////////////////////////////////////////////////////////////////////// //
303 void animClearFrames (StrId classtype, StrId classname, StrId state) {
304 auto adef = findActorDef(classtype.get, classname.get);
305 if (adef is null) throw new Exception("animClearFrames: actor '"~classtype.get~":"~classname.get~"' not found!");
306 adef.clearFrames(state);
310 void animAddFrame (StrId classtype, StrId classname, StrId state, uint dir, StrId sprname) {
311 auto adef = findActorDef(classtype.get, classname.get);
312 if (adef is null) throw new Exception("animAddFrame: actor '"~classtype.get~":"~classname.get~"' not found!");
313 if (dir != 0) dir = 1; //TODO: process mirror flag here
314 adef.addFrame(state, dir, sprname);
318 // ////////////////////////////////////////////////////////////////////////// //
319 void setupDAPI () {
320 conwriteln("setting up D API");
322 FuncPool["write"] = &doWrite!false;
323 FuncPool["writeln"] = &doWrite!true;
325 FuncPool["syncrandu31"] = &syncrandu31;
327 FuncPool["animClearFrames"] = &animClearFrames;
328 FuncPool["animAddFrame"] = &animAddFrame;
330 FuncPool["actorSetAnimation"] = function void (ActorId me, StrId state) {
331 if (!me.valid) return;
332 if (auto adef = findActorDef(me.classtype!string, me.classname!string)) {
333 me.zAnimstate = state;
334 me.zAnimidx = 0;
338 //FuncPool["getPlayer"] = function ActorId () { assert(0); };
339 //FuncPool["isPlayer"] = function int (ActorId me) { return (me == players.ptr[0] ? 1 : 0); };
341 FuncPool["getPlayerCount"] = function int () => 1;
343 FuncPool["getPlayerActor"] = function ActorId (uint pnum) {
344 if (pnum == 1) return players.ptr[0];
345 return ActorId(0);
348 FuncPool["getPlayerButtons"] = function uint (uint pidx) { return (pidx == 1 ? plrKeysLast : 0); };
350 FuncPool["mapGetTypeTile"] = function int (int x, int y) {
351 int res = 0;
352 if (map !is null && x >= 0 && y >= 0 && x < map.width && y < map.height) {
353 res = map.tiles.ptr[LevelMap.Type].ptr[y*map.width+x];
354 if (res != LevelMap.TILE_ACTTRAP) res &= 0x7f;
356 return res;
359 FuncPool["mapGetTile"] = function int (uint layer, int x, int y) {
360 return (map !is null && x >= 0 && y >= 0 && x < map.width && y < map.height && layer >= 0 && layer <= 1 ? map.tiles.ptr[LevelMap.Front+layer].ptr[y*map.width+x] : 0);
363 FuncPool["mapSetTypeTile"] = function void (int x, int y, int tid) {
364 if (map is null || x < 0 || y < 0 || x >= map.width || y >= map.height || tid < 0 || tid > 255) return;
365 auto p = map.tiles.ptr[LevelMap.Type].ptr+y*map.width+x;
366 if (*p != tid) {
367 *p = cast(ubyte)tid;
368 import render : mapDirty;
369 mapDirty((1<<LevelMap.Type)|(1<<LevelMap.LightMask));
373 FuncPool["mapSetTile"] = function void (uint layer, int x, int y, int tid) {
374 if (map is null || layer < 0 || layer > 1 || x < 0 || y < 0 || x >= map.width || y >= map.height || tid < 0 || tid > 255) return;
375 auto p = map.tiles.ptr[LevelMap.Front+layer].ptr+y*map.width+x;
376 if (*p != tid) {
377 *p = cast(ubyte)tid;
378 import render : mapDirty;
379 if (layer == 0) {
380 // front
381 mapDirty((1<<LevelMap.Front)|(1<<LevelMap.AllLiquids)|(1<<LevelMap.LiquidMask));
382 } else {
383 // back
384 mapDirty((1<<LevelMap.Back)|(1<<LevelMap.AllLiquids)|(1<<LevelMap.LiquidMask));
389 FuncPool["mapGetWaterTexture"] = function int (int fg) {
390 if (fg < 0 || fg >= map.wallnames.length) return 0;
391 switch (map.wallnames.ptr[fg]) {
392 case "_water_0": return 1;
393 case "_water_1": return 2;
394 case "_water_2": return 3;
395 default:
397 return 0;
400 FuncPool["getMapViewHeight"] = function int () {
401 import render : vlWidth, vlHeight, getScale;
402 return vlHeight/getScale;
405 FuncPool["setMapViewPos"] = function void (int x, int y) {
406 import render : setMapViewPos;
407 setMapViewPos(x, y);
410 FuncPool["actorsOverlap"] = function int (ActorId a, ActorId b) { return (actorsOverlap(a, b) ? 1 : 0); };
412 FuncPool["actorRemove"] = function void (ActorId me) {
413 if (me.valid) {
414 if ((me.flags!uint&AF_NOCOLLISION) == 0) ugActorModify!false(me); // remove from grid
415 Actor.remove(me);
419 FuncPool["rewindTouchList"] = &rewindTouchList;
420 FuncPool["getNextTouchListItem"] = &getNextTouchListItem;
422 FuncPool["actorListRewind"] = &xactorListRewind;
423 FuncPool["actorListNext"] = &xactorListNext;
425 FuncPool["getCheatNoDoors"] = function int () { import render : cheatNoDoors; return (cheatNoDoors ? 1 : 0); };
426 FuncPool["getCheatNoWallClip"] = function int () { import render : cheatNoWallClip; return (cheatNoWallClip ? 1 : 0); };
427 FuncPool["getCheatNoCeilClip"] = function int () { return 0; };
428 FuncPool["getCheatNoLiftClip"] = function int () { return 0; };
430 FuncPool["addMessage"] = function void (string text, int pause, bool noreplace) {
431 import render : postAddMessage;
432 postAddMessage(text, pause, noreplace);
435 import d2dparts : dotAddBlood, dotAddSpark, dotAddWater;
437 FuncPool["dotAddBlood"] = &dotAddBlood;
438 FuncPool["dotAddSpark"] = &dotAddSpark;
439 FuncPool["dotAddWater"] = &dotAddWater;
441 function void (int x, int y, int xv, int yv, int n, int color) {
442 conwriteln("dotAddWater: x=", x, "; y=", y, "; xv=", xv, "; yv=", yv, "; n=", n, "; color=", color);
443 dotAddWater(x, y, xv, yv, n, color);
449 public void registerAPI () {
450 //Actor.actorSize += 256;
451 conwriteln("actor size is ", Actor.actorSize, " bytes");
453 version(dacs_use_vm) FuncPool.vm = new DACSVM();
454 //dvm = new DACSVM();
456 // initialize actor animation
457 ActorDef.forEach((adef) => adef.callAnimInit());
459 conwriteln("API registered");
463 // ////////////////////////////////////////////////////////////////////////// //
464 mixin(Actor.FieldGetMixin!("classtype", StrId)); // fget_classtype
465 mixin(Actor.FieldGetMixin!("classname", StrId)); // fget_classname
466 mixin(Actor.FieldGetMixin!("x", int));
467 mixin(Actor.FieldGetMixin!("y", int));
468 mixin(Actor.FieldGetMixin!("dir", uint));
469 mixin(Actor.FieldGetMixin!("height", int));
470 mixin(Actor.FieldGetMixin!("radius", int));
471 mixin(Actor.FieldGetMixin!("flags", uint));
472 mixin(Actor.FieldGetMixin!("zAnimstate", StrId));
473 mixin(Actor.FieldGetMixin!("zAnimidx", int));
475 mixin(Actor.FieldSetMixin!("zAnimidx", int));
478 public bool actorsOverlap (ActorId a, ActorId b) {
479 if (!a.valid || !b.valid) return false;
480 if (a.id == b.id) return false; // no self-overlap
482 int ax = a.fget_x;
483 int bx = b.fget_x;
484 int ar = a.fget_radius;
485 int br = b.fget_radius;
487 if (ax-ar > bx+br || ax+ar < bx-br) return false;
489 int ay = a.fget_y;
490 int by = b.fget_y;
491 int ah = a.fget_height;
492 int bh = b.fget_height;
494 //return (ay > by-bh && ay-ah < by);
495 return (by-bh <= ay && by >= ay-ah);
499 // ////////////////////////////////////////////////////////////////////////// //
500 public __gshared ActorId[2] players;
503 public void loadAllMonsterGraphics () {
504 ActorDef.forEach((adef) {
505 conwriteln("loading graphics for '", adef.classtype.get, ":", adef.classname.get, "'");
506 adef.loadGraphics();
508 //Actor.dumpActors();
509 realiseSpriteAtlases();
513 public void loadMapMonsters () {
514 assert(map !is null);
515 ugClear();
516 players[] = ActorId(0);
517 //conwriteln(players[0].valid, "; ", players[0].id);
518 foreach (ref thing; map.things) {
519 if (thing.dmonly) continue;
520 auto did = (thing.type&0x7fff) in d2dactordefsById;
521 if (did !is null && did.classtype == "playerstart") {
522 if ((thing.type&0x7fff) == 1 || (thing.type&0x7fff) == 2) {
523 int pnum = thing.type-1;
524 if (!players[pnum].valid) {
525 auto aid = Actor.alloc;
526 aid.classtype = StrPool.intern("monster");
527 aid.classname = StrPool.intern("Player");
528 aid.plrnum = cast(uint)(pnum+1);
529 aid.state = StrPool.MNST_SLEEP;
530 aid.x = cast(int)thing.x;
531 aid.y = cast(int)thing.y;
532 aid.dir = cast(uint)(thing.right ? 1 : 0);
533 if (pnum == 0) aid.flags = aid.flags!uint|AF_CAMERACHICK;
534 auto adef = findD2DActorDef(thing.type);
535 if (adef is null) assert(0);
536 adef.callInit(aid);
537 if ((aid.flags!uint&AF_NOCOLLISION) == 0) ugActorModify!true(aid);
538 players[pnum] = aid;
539 conwriteln("player #", pnum+1, " aid is ", aid.id);
542 continue;
544 auto adef = findD2DActorDef(thing.type&0x7fff);
545 if (adef is null) {
546 if (did !is null) {
547 conwriteln("ignoring D2D thing '", did.classtype.get, ":", did.classname.get, "'");
548 } else {
549 conwriteln("ignoring unknown D2D thing with mapid ", thing.type);
551 continue;
553 // create actor and initialize it
554 auto aid = Actor.alloc;
555 aid.classtype = StrPool.intern(adef.classtype);
556 aid.classname = StrPool.intern(adef.classname);
557 //conwriteln("found '", aid.classtype.get, ":", aid.classname.get, "'");
558 if (did !is null) {
559 assert(did.classtype == adef.classtype);
560 assert(did.classname == adef.classname);
561 conwriteln("mapid=", thing.type, "; ", adef.classtype, ":", adef.classname, "; id=", aid.id);
562 assert(aid.classtype!string == adef.classtype);
563 assert(aid.classname!string == adef.classname);
564 } else {
565 assert(0);
567 aid.state = StrPool.MNST_SLEEP;
568 aid.x = cast(int)thing.x;
569 aid.y = cast(int)thing.y;
570 aid.dir = cast(uint)(thing.right ? 1 : 0);
571 if (thing.type&0x8000) aid.flags = aid.flags!uint|AF_NOGRAVITY;
572 //if (aid.classtype!string == "item" && aid.x!int < 64) { aid.x = 92; aid.y = aid.y!int-16; conwriteln("!!!"); }
573 adef.callInit(aid);
574 if ((aid.flags!uint&AF_NOCOLLISION) == 0) ugActorModify!true(aid);
577 // create switches
578 foreach (ref sw; map.switches) {
579 if (sw.type == 0) continue; // just in case
580 auto swname = getD2DSwitchClassName(sw.type);
581 if (swname.length == 0) {
582 conwriteln("unknown switch type ", sw.type);
583 continue;
585 if (auto adef = findActorDef("switch", swname)) {
586 auto aid = Actor.alloc;
587 aid.classtype = StrPool.intern("switch");
588 aid.classname = StrPool.intern(swname);
589 // nocollision, 'cause collision checking will be processed in switch thinker, but other actors should not touch switches
590 aid.flags = /*AF_NOCOLLISION|*/AF_NOGRAVITY|/*AF_NOONTOUCH|*/AF_NODRAW|AF_NOLIGHT|AF_NOANIMATE;
591 aid.x = sw.x*8;
592 aid.y = sw.y*8;
593 aid.switchabf = (sw.a<<16)|(sw.b<<8)|sw.flags;
594 // should be enough
595 aid.radius = 16;
596 aid.height = 16;
597 adef.callInit(aid);
598 if ((aid.flags!uint&AF_NOCOLLISION) == 0) ugActorModify!true(aid); // just in case
599 } else {
600 conwriteln("switch definition 'switch:", swname, "' not found");
604 conwriteln("initial snapshot size: ", Actor.snapshotSize, " bytes");
608 // ////////////////////////////////////////////////////////////////////////// //
609 __gshared ActorId[65536] xactorList; // aids
610 __gshared uint xactorListCount, xactorListIndex = uint.max;
613 // dacs API
614 void xactorListRewind () {
615 if (!touchListAllowed) return; // it's ok to reuse it here
616 xactorListCount = xactorListIndex = 0;
617 Actor.forEach((ActorId me) {
618 if (me.fget_classtype != StrPool.X_X) xactorList.ptr[xactorListCount++] = me;
623 // dacs API
624 ActorId xactorListNext () {
625 if (!touchListAllowed) return ActorId(0); // it's ok to reuse it here
626 if (xactorListIndex == uint.max) xactorListRewind();
627 while (xactorListIndex < xactorListCount) {
628 auto aid = xactorList.ptr[xactorListIndex];
629 ++xactorListIndex;
630 if (aid.fget_classtype == StrPool.X_X) continue;
631 return aid;
633 return ActorId(0);
637 // ////////////////////////////////////////////////////////////////////////// //
638 __gshared uint[65536] touchList; // aids
639 __gshared uint[] realTouchList;
640 __gshared uint realTouchListIndex = uint.max;
641 __gshared ActorId curThinkingActor;
642 __gshared bool touchListAllowed = false;
645 // dacs API
646 void rewindTouchList () {
647 if (!touchListAllowed) return;
648 realTouchListIndex = 0;
649 realTouchList = ugActorHitList(curThinkingActor, touchList[]);
653 // dacs API
654 ActorId getNextTouchListItem () {
655 if (!touchListAllowed) return ActorId(0);
656 if (realTouchListIndex == uint.max) rewindTouchList();
657 while (realTouchListIndex < realTouchList.length) {
658 auto aid = ActorId(realTouchList.ptr[realTouchListIndex]);
659 ++realTouchListIndex;
660 if (aid.fget_classtype == StrPool.X_X) continue;
661 if (aid.valid && (aid.fget_flags&AF_NOONTOUCH) == 0) return aid;
663 return ActorId(0);
667 public void doActorsThink () {
668 // we have too much memory!
669 __gshared uint[65536] validActorsList;
670 __gshared ActorId[65536] postponedDeath;
671 __gshared uint pdcount;
673 touchListAllowed = true;
674 scope(exit) touchListAllowed = false;
675 pdcount = 0;
676 foreach (uint xid; Actor.getValidList(validActorsList[])) {
677 auto me = ActorId(xid);
678 if (me.valid) {
679 if (me.fget_classtype == StrPool.X_X) { postponedDeath.ptr[pdcount++] = me; continue; }
680 auto flags = me.fget_flags;
681 if ((flags&AF_NOCOLLISION) == 0) ugActorModify!false(me); // remove from grid
682 if (auto adef = findActorDef(me)) {
683 if ((flags&AF_NOTHINK) == 0) {
684 realTouchListIndex = uint.max;
685 xactorListIndex = uint.max;
686 curThinkingActor = me;
687 adef.callThink(me);
688 //if (me.x!int < 32) conwriteln("actor: ", me.id, "; attLightRGBX=", me.attLightRGBX!uint);
689 if (!me.valid) continue; // we are dead
690 if (me.fget_classtype == StrPool.X_X) { postponedDeath.ptr[pdcount++] = me; continue; }
691 flags = me.fget_flags; // in case script updated flags
693 if ((flags&AF_NOANIMATE) == 0) {
694 int aidx = me.fget_zAnimidx;
695 int nidx = adef.nextAnimIdx(me.fget_zAnimstate, me.fget_dir, aidx);
696 //conwriteln("actor ", me.id, " (", me.classtype!string, me.classname!string, "): state=", me.zAnimstate!string, "; aidx=", aidx, "; nidx=", nidx);
697 me.fset_zAnimidx = nidx;
698 //assert(me.fget_zAnimidx == nidx);
700 // put back to grid
701 if ((flags&AF_NOCOLLISION) == 0) ugActorModify!true(me);
705 // process scheduled death
706 foreach (ActorId aid; postponedDeath[0..pdcount]) {
707 Actor.remove(aid);
709 // reset player keys
710 plrKeysFix(0);
711 //{ import std.stdio : stdout; stdout.writeln("========================================="); }
712 //Actor.dumpActors();