added "minified occluders map"; lights are faster now
[dd2d.git] / dengapi.d
blob972fe761e14c9deb7b6496fcd5e87f1a262a3927
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 __gshared ActorId cameraChick;
52 // ////////////////////////////////////////////////////////////////////////// //
53 public void addInternalActorFields () {
54 // &0xffff: position in drawlist
55 // (>>16)&0xff: drawlist index
56 //Actor.addField("0drawlistpos", Actor.Field.Type.Uint);
60 // ////////////////////////////////////////////////////////////////////////// //
61 public {
62 enum PLK_UP = (1<<0);
63 enum PLK_DOWN = (1<<1);
64 enum PLK_LEFT = (1<<2);
65 enum PLK_RIGHT = (1<<3);
66 enum PLK_FIRE = (1<<4);
67 enum PLK_JUMP = (1<<5);
68 enum PLK_USE = (1<<6);
71 __gshared uint plrKeysLast;
72 __gshared uint plrKeyState;
75 public void plrKeyDown (uint plidx, uint mask) {
76 if (mask == 0 || plidx > 0) return;
77 plrKeysLast |= mask;
78 plrKeyState |= mask;
82 public void plrKeyUp (uint plidx, uint mask) {
83 if (mask == 0 || plidx > 0) return;
84 plrKeyState &= ~mask;
88 public void plrKeyUpDown (uint plidx, uint mask, bool down) {
89 if (down) plrKeyDown(plidx, mask); else plrKeyUp(plidx, mask);
93 void plrKeysFix (uint plidx) {
94 if (plidx > 0) return;
95 //conwritefln!"plrKeyState=0x%02x; plrKeysLast=0x%02x"(plrKeyState, plrKeysLast);
96 foreach (uint n; 0..12) {
97 if ((plrKeyState&(1<<n)) == 0) plrKeysLast &= ~(1<<n);
102 // ////////////////////////////////////////////////////////////////////////// //
103 void updateActorClasses () {
104 foreach (string ctype; dacsClassTypes) {
105 foreach (auto mod; dacsClassModules(ctype)) {
106 if (mod.rmoduletype.classtype == "map") {
107 // map scripts, not here
108 } else {
109 auto adef = registerActorDef(mod.rmoduletype.classtype, mod.rmoduletype.classname);
110 conwriteln("found info for actor '", mod.rmoduletype.classtype, ":", mod.rmoduletype.classname, "'");
112 auto animInitFn = FuncPool.findByFQMG(mod.name~".initializeAnim", ":void");
113 if (animInitFn !is null) adef.setAnimInitFunc(animInitFn);
116 auto initFn = FuncPool.findByFQMG(mod.name~".initialize", ":void:Actor");
117 if (initFn !is null) adef.setInitFunc(initFn);
120 auto thinkFn = FuncPool.findByFQMG(mod.name~".think", ":void:Actor");
121 if (thinkFn !is null) adef.setThinkFunc(thinkFn);
129 // ////////////////////////////////////////////////////////////////////////// //
130 private string modLoader (string modname) {
131 static string getTextFile (string fname) {
132 try {
133 auto res = loadTextFile(fname);
134 conwriteln("loading DACS module file '", fname, "' (", res.length, " bytes)");
135 if (res is null) res = "";
136 return res;
137 } catch (Exception) {}
138 return null;
141 string res;
143 //res = getTextFile(modname~".dacs");
144 //if (res !is null) return res;
146 res = getTextFile("scripts/"~modname~".dacs");
147 if (res !is null) return res;
148 assert(res is null);
150 string[] parts = ["scripts"];
151 string mn = modname;
152 while (mn.length > 0) {
153 int pos = 0;
154 if (mn[0] >= 'A' && mn[0] <= 'Z') ++pos;
155 while (pos < mn.length && (mn[pos] < 'A' || mn[pos] > 'Z')) ++pos;
156 if (mn[0] >= 'A' && mn[0] <= 'Z') {
157 parts ~= cast(char)(mn[0]+32)~mn[1..pos];
158 } else {
159 parts ~= mn[0..pos];
161 mn = mn[pos..$];
164 import std.array : join;
165 string path = parts.join("/")~".dacs";
166 res = getTextFile(path);
167 if (res !is null) return res;
168 assert(res is null);
170 assert(res is null);
171 throw new Exception("module '"~modname~"' not found");
175 __gshared string[] dacsModules;
177 public void registerWadScripts () {
178 try {
179 import std.array : split;
180 auto t = loadTextFile("scripts/dacsmain.txt");
181 foreach (string line; t.split('\n')) {
182 while (line.length && line[0] <= ' ') line = line[1..$];
183 if (line.length == 0 || line[0] == ';' || line[0] == '#') continue;
184 while (line.length && line[$-1] <= ' ') line = line[0..$-1];
185 import std.algorithm : canFind;
186 if (!dacsModules.canFind(line)) dacsModules ~= line;
188 } catch (Exception e) { return; }
192 public void loadWadScripts () {
193 if (moduleLoader is null) moduleLoader = (string modname) => modLoader(modname);
194 try {
195 //conwriteln("loading main DACS module '", mainmod, "'");
196 foreach (string mod; dacsModules) parseModule(mod);
197 parseComplete();
198 updateActorClasses();
199 setupDAPI();
200 dacsFinalizeCompiler();
201 } catch (CompilerException e) {
202 _d_print_throwable(e);
203 conwriteln("PARSE ERROR: ", e.file, " at ", e.line);
204 conwriteln(e.toString);
205 assert(0);
210 // ////////////////////////////////////////////////////////////////////////// //
211 public __gshared LevelMap map;
212 //public __gshared DACSVM dvm;
213 public __gshared uint prngSyncSeed = 0x29a;
214 public __gshared uint prngSeed = 0x29a; // unsynced
215 public __gshared string curmapname;
216 public __gshared string nextmapname; // nonempty: go to next level
218 struct MapScripts {
219 FuncPool.FuncInfo fiInit; // called after level map is loaded
220 FuncPool.FuncInfo fiLoaded; // called after mosters set
221 FuncPool.FuncInfo fiUnloading; // called before level is unloaded (i.e. player finished the level)
222 FuncPool.FuncInfo fiPreThink; // called before monster think
223 FuncPool.FuncInfo fiPostThink; // called after monster think
225 void runInit () { if (fiInit !is null) fiInit(); }
226 void runLoaded () { if (fiLoaded !is null) fiLoaded(); }
227 void runUnloading () { if (fiUnloading !is null) fiUnloading(); }
228 void runPreThink () { if (fiPreThink !is null) fiPreThink(); }
229 void runPostThink () { if (fiPostThink !is null) fiPostThink(); }
232 public __gshared MapScripts mapscripts;
235 // generate next map name for exit
236 public string genNextMapName (int lnum) {
237 import std.algorithm : endsWith;
238 import std.path;
239 string ext;
240 string nn = curmapname;
241 if (nn.endsWith(".d2m")) {
242 ext = ".d2m";
243 nn = nn[0..$-4];
245 if (nn[$-1] < '0' || nn[$-1] > '9') return null;
246 uint mapnum = 0, mmul = 1;
247 while (nn.length > 0 && nn[$-1] >= '0' && nn[$-1] <= '9') {
248 mapnum += (nn[$-1]-'0')*mmul;
249 mmul *= 10;
250 nn = nn[0..$-1];
252 ++mapnum;
253 if (lnum > 0 && lnum < 100) mapnum = lnum;
254 if (mapnum > 99) return null; // alas
255 import std.string : format;
256 return "%s%02u%s".format(nn, mapnum, ext);
260 public void clearMapScripts () {
261 mapscripts = mapscripts.init; // remove old scripts
265 public void setupMapScripts () {
266 void setupFromModule(T) (T mod) {
267 mapscripts.fiInit = FuncPool.findByFQMG(mod.name~".initialize", ":void");
268 mapscripts.fiLoaded = FuncPool.findByFQMG(mod.name~".loaded", ":void");
269 mapscripts.fiUnloading = FuncPool.findByFQMG(mod.name~".unloading", ":void");
270 mapscripts.fiPreThink = FuncPool.findByFQMG(mod.name~".prethink", ":void");
271 mapscripts.fiPostThink = FuncPool.findByFQMG(mod.name~".postthink", ":void");
274 import std.path : baseName, setExtension;
275 clearMapScripts();
276 string mapname = curmapname.baseName.setExtension("");
277 foreach (string ctype; dacsClassTypes) {
278 foreach (auto mod; dacsClassModules(ctype)) {
279 if (mod.rmoduletype.classtype != "map") continue;
280 if (mod.rmoduletype.classname != mapname) continue;
281 conwriteln("found module for map '", mapname, "'");
282 setupFromModule(mod);
283 return;
286 // not found, try "unnamed map"
287 foreach (string ctype; dacsClassTypes) {
288 foreach (auto mod; dacsClassModules(ctype)) {
289 if (mod.rmoduletype.classtype != "map") continue;
290 if (mod.rmoduletype.classname != " ") continue;
291 conwriteln("using module for unknownmap '", mapname, "'");
292 setupFromModule(mod);
293 return;
299 // ////////////////////////////////////////////////////////////////////////// //
300 // Park-Miller-Carta Pseudo-Random Number Generator, based on David G. Carta paper
301 // 31 bit of randomness
302 // seed is previous result, as usual
303 public uint prngR31next (uint seed) {
304 if (seed == 0) seed = 0x29a;
305 uint lo = 16807*(seed&0xffff);
306 uint hi = 16807*(seed>>16);
307 lo += (hi&0x7fff)<<16;
308 lo += hi>>15;
309 //if (lo > 0x7fffffff) lo -= 0x7fffffff; // should be >=, actually
310 lo = (lo&0x7FFFFFFF)+(lo>>31); // same as previous code, but branch-less
311 return lo;
315 // "synced" random, using common seed for all players
316 public uint syncrandu31 () {
317 pragma(inline, true);
318 return (prngSyncSeed = prngR31next(prngSyncSeed));
322 // "unsynced" random, seed isn't saved
323 public uint unsyncrandu31 () {
324 pragma(inline, true);
325 return (prngSeed = prngR31next(prngSeed));
329 // ////////////////////////////////////////////////////////////////////////// //
330 version(dacs_use_vm) {
331 // this is VAT function, argument order is reversed
332 void doWrite(bool donl) (FuncPool.FuncInfo fi, DACSVM vm) {
333 import std.stdio;
334 auto count = vm.popInt();
335 while (count-- > 0) {
336 auto type = vm.popInt();
337 switch (cast(VATArgType)type) {
338 case VATArgType.Int: write(vm.popInt()); break;
339 case VATArgType.Uint: write(vm.popUint()); break;
340 case VATArgType.Float: write(vm.popFloat()); break;
341 case VATArgType.StrId: write(StrId(vm.popUint()).get); break;
342 case VATArgType.ActorId: write("<actor>"); vm.popUint(); break; //TODO
343 default: write("<invalid-type>"); vm.popUint(); break; //TODO
346 static if (donl) writeln();
347 // push dummy return value
348 vm.pushInt(0);
350 } else {
351 extern(C) void doWrite(bool donl) (uint argc, ...) {
352 import core.vararg;
353 import std.stdio;
354 mainloop: while (argc >= 2) {
355 argc -= 2;
356 int tp = va_arg!int(_argptr);
357 switch (tp) {
358 case VATArgType.Int:
359 auto v = va_arg!int(_argptr);
360 write(v);
361 break;
362 case VATArgType.Uint:
363 auto v = va_arg!uint(_argptr);
364 write(v);
365 break;
366 case VATArgType.Float:
367 auto v = va_arg!float(_argptr);
368 write(v);
369 break;
370 case VATArgType.StrId:
371 auto v = StrId(va_arg!uint(_argptr)).get;
372 write(v);
373 break;
374 case VATArgType.ActorId:
375 auto v = ActorId(va_arg!uint(_argptr));
376 write("<actor:", v.valid, ":", v.id, ">");
377 //write("<actor>");
378 break;
379 default: write("<invalid-type>"); break mainloop;
382 static if (donl) writeln();
387 // ////////////////////////////////////////////////////////////////////////// //
388 void animClearFrames (StrId classtype, StrId classname, StrId state) {
389 auto adef = findActorDef(classtype.get, classname.get);
390 if (adef is null) throw new Exception("animClearFrames: actor '"~classtype.get~":"~classname.get~"' not found!");
391 adef.clearFrames(state);
395 void animAddFrame (StrId classtype, StrId classname, StrId state, uint dir, StrId sprname) {
396 auto adef = findActorDef(classtype.get, classname.get);
397 if (adef is null) throw new Exception("animAddFrame: actor '"~classtype.get~":"~classname.get~"' not found!");
398 if (dir != 0) dir = 1; //TODO: process mirror flag here
399 adef.addFrame(state, dir, sprname);
403 // ////////////////////////////////////////////////////////////////////////// //
404 void setupDAPI () {
405 conwriteln("setting up D API");
407 FuncPool["write"] = &doWrite!false;
408 FuncPool["writeln"] = &doWrite!true;
410 FuncPool["syncrandu31"] = &syncrandu31;
412 FuncPool["animClearFrames"] = &animClearFrames;
413 FuncPool["animAddFrame"] = &animAddFrame;
415 FuncPool["actorSetAnimation"] = function void (ActorId me, StrId state) {
416 if (!me.valid) return;
417 if (auto adef = findActorDef(me.classtype!string, me.classname!string)) {
418 me.zAnimstate = state;
419 me.zAnimidx = 0;
423 //FuncPool["getPlayer"] = function ActorId () { assert(0); };
424 //FuncPool["isPlayer"] = function int (ActorId me) { return (me == players.ptr[0] ? 1 : 0); };
426 FuncPool["getPlayerCount"] = function int () => 1;
428 FuncPool["getPlayerActor"] = function ActorId (uint pnum) {
429 if (pnum == 1) return players.ptr[0];
430 return ActorId(0);
433 FuncPool["getPlayerButtons"] = function uint (uint pidx) { return (pidx == 1 ? plrKeysLast : 0); };
435 FuncPool["mapGetTypeTile"] = function int (int x, int y) {
436 int res = 0;
437 if (map !is null && x >= 0 && y >= 0 && x < map.width && y < map.height) {
438 res = map.tiles.ptr[LevelMap.Type].ptr[y*map.width+x];
439 if (res != LevelMap.TILE_ACTTRAP) res &= 0x7f;
441 return res;
444 FuncPool["mapGetTile"] = function int (uint layer, int x, int y) {
445 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);
448 FuncPool["mapSetTypeTile"] = function void (int x, int y, int tid) {
449 if (map is null || x < 0 || y < 0 || x >= map.width || y >= map.height || tid < 0 || tid > 255) return;
450 auto p = map.tiles.ptr[LevelMap.Type].ptr+y*map.width+x;
451 if (*p != tid) {
452 *p = cast(ubyte)tid;
453 import render : mapDirty;
454 mapDirty((1<<LevelMap.Type)|(1<<LevelMap.LightMask));
458 FuncPool["mapSetTile"] = function void (uint layer, int x, int y, int tid) {
459 if (map is null || layer < 0 || layer > 1 || x < 0 || y < 0 || x >= map.width || y >= map.height || tid < 0 || tid > 255) return;
460 auto p = map.tiles.ptr[LevelMap.Front+layer].ptr+y*map.width+x;
461 if (*p != tid) {
462 *p = cast(ubyte)tid;
463 import render : mapDirty;
464 if (layer == 0) {
465 // front
466 mapDirty((1<<LevelMap.Front)|(1<<LevelMap.AllLiquids)|(1<<LevelMap.LiquidMask));
467 } else {
468 // back
469 mapDirty((1<<LevelMap.Back)|(1<<LevelMap.AllLiquids)|(1<<LevelMap.LiquidMask));
474 FuncPool["mapGetWaterTexture"] = function int (int fg) {
475 if (fg < 0 || fg >= map.wallnames.length) return 0;
476 switch (map.wallnames.ptr[fg]) {
477 case "_water_0": return 1;
478 case "_water_1": return 2;
479 case "_water_2": return 3;
480 default:
482 return 0;
485 FuncPool["getMapViewHeight"] = function int () {
486 import render : vlWidth, vlHeight, getScale;
487 return vlHeight/getScale;
490 FuncPool["actorsOverlap"] = function int (ActorId a, ActorId b) { return (actorsOverlap(a, b) ? 1 : 0); };
492 FuncPool["actorRemove"] = function void (ActorId me) {
493 if (me.valid) {
494 if ((me.fget_flags&AF_NOCOLLISION) == 0) ugActorModify!false(me); // remove from grid
495 Actor.remove(me);
499 FuncPool["rewindTouchList"] = &rewindTouchList;
500 FuncPool["getNextTouchListItem"] = &getNextTouchListItem;
502 FuncPool["actorListRewind"] = &xactorListRewind;
503 FuncPool["actorListNext"] = &xactorListNext;
505 FuncPool["getCheatNoDoors"] = function int () { import render : cheatNoDoors; return (cheatNoDoors ? 1 : 0); };
506 FuncPool["getCheatNoWallClip"] = function int () { import render : cheatNoWallClip; return (cheatNoWallClip ? 1 : 0); };
507 FuncPool["getCheatNoCeilClip"] = function int () { return 0; };
508 FuncPool["getCheatNoLiftClip"] = function int () { return 0; };
510 FuncPool["addMessage"] = function void (string text, int pause, bool noreplace) {
511 import render : postAddMessage;
512 postAddMessage(text, pause, noreplace);
515 import d2dparts : dotAddBlood, dotAddSpark, dotAddWater;
517 FuncPool["dotAddBlood"] = &dotAddBlood;
518 FuncPool["dotAddSpark"] = &dotAddSpark;
519 FuncPool["dotAddWater"] = &dotAddWater;
521 function void (int x, int y, int xv, int yv, int n, int color) {
522 conwriteln("dotAddWater: x=", x, "; y=", y, "; xv=", xv, "; yv=", yv, "; n=", n, "; color=", color);
523 dotAddWater(x, y, xv, yv, n, color);
527 FuncPool["gactLevelExit"] = function void (int lnum) {
528 if (nextmapname.length == 0) {
529 string nmname = genNextMapName(lnum);
530 if (nmname.length == 0) assert(0, "no level!");
531 nextmapname = nmname;
535 FuncPool["actorSpawn"] = &actorSpawn;
537 // return previous one
538 FuncPool["setCameraChick"] = function ActorId (ActorId act) {
539 auto res = cameraChick;
540 cameraChick = act;
541 return res;
546 public void registerAPI () {
547 //Actor.actorSize += 256;
548 conwriteln("actor size is ", Actor.actorSize, " bytes");
550 version(dacs_use_vm) FuncPool.vm = new DACSVM();
551 //dvm = new DACSVM();
553 // initialize actor animation
554 ActorDef.forEach((adef) => adef.callAnimInit());
556 conwriteln("API registered");
560 // ////////////////////////////////////////////////////////////////////////// //
561 mixin(Actor.FieldGetMixin!("classtype", StrId)); // fget_classtype
562 mixin(Actor.FieldGetMixin!("classname", StrId)); // fget_classname
563 mixin(Actor.FieldGetMixin!("x", int));
564 mixin(Actor.FieldGetMixin!("y", int));
565 mixin(Actor.FieldGetMixin!("dir", uint));
566 mixin(Actor.FieldGetMixin!("height", int));
567 mixin(Actor.FieldGetMixin!("radius", int));
568 mixin(Actor.FieldGetMixin!("flags", uint));
569 mixin(Actor.FieldGetMixin!("zAnimstate", StrId));
570 mixin(Actor.FieldGetMixin!("zAnimidx", int));
572 mixin(Actor.FieldSetMixin!("zAnimidx", int));
575 public bool actorsOverlap (ActorId a, ActorId b) {
576 if (!a.valid || !b.valid) return false;
577 if (a.id == b.id) return false; // no self-overlap
579 int ax = a.fget_x;
580 int bx = b.fget_x;
581 int ar = a.fget_radius;
582 int br = b.fget_radius;
584 if (ax-ar > bx+br || ax+ar < bx-br) return false;
586 int ay = a.fget_y;
587 int by = b.fget_y;
588 int ah = a.fget_height;
589 int bh = b.fget_height;
591 //return (ay > by-bh && ay-ah < by);
592 return (by-bh <= ay && by >= ay-ah);
596 // ////////////////////////////////////////////////////////////////////////// //
597 public __gshared ActorId[2] players;
600 public void loadAllMonsterGraphics () {
601 ActorDef.forEach((adef) {
602 conwriteln("loading graphics for '", adef.classtype.get, ":", adef.classname.get, "'");
603 adef.loadGraphics();
605 //Actor.dumpActors();
606 realiseSpriteAtlases();
610 ActorId actorSpawn (StrId classtype, StrId classname, int x, int y, uint dir) {
611 auto adef = findActorDef(classtype, classname);
612 if (adef is null) return ActorId(0);
613 auto aid = Actor.alloc;
614 aid.classtype = classtype;
615 aid.classname = classname;
616 aid.state = StrPool.MNST_SLEEP;
617 aid.x = x;
618 aid.y = y;
619 aid.dir = (dir ? 1 : 0);
620 adef.callInit(aid);
621 if ((aid.fget_flags&AF_NOCOLLISION) == 0) ugActorModify!true(aid);
622 return aid;
626 public void loadMapMonsters () {
627 assert(map !is null);
628 ugClear();
629 players[] = ActorId(0);
630 //conwriteln(players[0].valid, "; ", players[0].id);
631 foreach (ref thing; map.things) {
632 if (thing.dmonly) continue;
633 auto did = (thing.type&0x7fff) in d2dactordefsById;
634 if (did !is null && did.classtype == "playerstart") {
635 if ((thing.type&0x7fff) == 1 || (thing.type&0x7fff) == 2) {
636 int pnum = thing.type-1;
637 if (!players[pnum].valid) {
638 auto aid = Actor.alloc;
639 aid.classtype = StrPool.intern("monster");
640 aid.classname = StrPool.intern("Player");
641 aid.plrnum = cast(uint)(pnum+1);
642 aid.state = StrPool.MNST_SLEEP;
643 aid.x = cast(int)thing.x;
644 aid.y = cast(int)thing.y;
645 aid.dir = cast(uint)(thing.right ? 1 : 0);
646 auto adef = findD2DActorDef(thing.type);
647 if (adef is null) assert(0);
648 adef.callInit(aid);
649 if ((aid.fget_flags&AF_NOCOLLISION) == 0) ugActorModify!true(aid);
650 players[pnum] = aid;
651 conwriteln("player #", pnum+1, " aid is ", aid.id);
654 continue;
656 auto adef = findD2DActorDef(thing.type&0x7fff);
657 if (adef is null) {
658 if (did !is null) {
659 conwriteln("ignoring D2D thing '", did.classtype.get, ":", did.classname.get, "'");
660 } else {
661 conwriteln("ignoring unknown D2D thing with mapid ", thing.type);
663 continue;
665 // create actor and initialize it
666 auto aid = Actor.alloc;
667 aid.classtype = StrPool.intern(adef.classtype);
668 aid.classname = StrPool.intern(adef.classname);
669 //conwriteln("found '", aid.classtype.get, ":", aid.classname.get, "'");
670 if (did !is null) {
671 assert(did.classtype == adef.classtype);
672 assert(did.classname == adef.classname);
673 conwriteln("mapid=", thing.type, "; ", adef.classtype, ":", adef.classname, "; id=", aid.id);
674 assert(aid.classtype!string == adef.classtype);
675 assert(aid.classname!string == adef.classname);
676 } else {
677 assert(0);
679 aid.state = StrPool.MNST_SLEEP;
680 aid.x = cast(int)thing.x;
681 aid.y = cast(int)thing.y;
682 aid.dir = cast(uint)(thing.right ? 1 : 0);
683 if (thing.type&0x8000) aid.flags = aid.fget_flags|AF_NOGRAVITY;
684 //if (aid.classtype!string == "item" && aid.x!int < 64) { aid.x = 92; aid.y = aid.y!int-16; conwriteln("!!!"); }
685 adef.callInit(aid);
686 if ((aid.fget_flags&AF_NOCOLLISION) == 0) ugActorModify!true(aid);
689 // create switches
690 foreach (ref sw; map.switches) {
691 if (sw.type == 0) continue; // just in case
692 auto swname = getD2DSwitchClassName(sw.type);
693 if (swname.length == 0) {
694 conwriteln("unknown switch type ", sw.type);
695 continue;
697 if (auto adef = findActorDef("switch", swname)) {
698 auto aid = Actor.alloc;
699 aid.classtype = StrPool.intern("switch");
700 aid.classname = StrPool.intern(swname);
701 // nocollision, 'cause collision checking will be processed in switch thinker, but other actors should not touch switches
702 aid.flags = /*AF_NOCOLLISION|*/AF_NOGRAVITY|/*AF_NOONTOUCH|*/AF_NODRAW|AF_NOLIGHT|AF_NOANIMATE;
703 aid.x = sw.x*8;
704 aid.y = sw.y*8;
705 aid.switchabf = (sw.a<<16)|(sw.b<<8)|sw.flags;
706 // should be enough
707 aid.radius = 16;
708 aid.height = 16;
709 adef.callInit(aid);
710 if ((aid.fget_flags&AF_NOCOLLISION) == 0) ugActorModify!true(aid); // just in case
711 } else {
712 conwriteln("switch definition 'switch:", swname, "' not found");
716 conwriteln("initial snapshot size: ", Actor.snapshotSize, " bytes");
720 // ////////////////////////////////////////////////////////////////////////// //
721 __gshared ActorId[65536] xactorList; // aids
722 __gshared uint xactorListCount, xactorListIndex = uint.max;
725 // dacs API
726 void xactorListRewind () {
727 if (!touchListAllowed) return; // it's ok to reuse it here
728 xactorListCount = xactorListIndex = 0;
729 Actor.forEach((ActorId me) {
730 if (me.fget_classtype != StrPool.X_X) xactorList.ptr[xactorListCount++] = me;
735 // dacs API
736 ActorId xactorListNext () {
737 if (!touchListAllowed) return ActorId(0); // it's ok to reuse it here
738 if (xactorListIndex == uint.max) xactorListRewind();
739 while (xactorListIndex < xactorListCount) {
740 auto aid = xactorList.ptr[xactorListIndex];
741 ++xactorListIndex;
742 if (aid.fget_classtype == StrPool.X_X) continue;
743 return aid;
745 return ActorId(0);
749 // ////////////////////////////////////////////////////////////////////////// //
750 __gshared uint[65536] touchList; // aids
751 __gshared uint[] realTouchList;
752 __gshared uint realTouchListIndex = uint.max;
753 __gshared ActorId curThinkingActor;
754 __gshared bool touchListAllowed = false;
757 // dacs API
758 void rewindTouchList () {
759 if (!touchListAllowed) return;
760 realTouchListIndex = 0;
761 realTouchList = ugActorHitList(curThinkingActor, touchList[]);
765 // dacs API
766 ActorId getNextTouchListItem () {
767 if (!touchListAllowed) return ActorId(0);
768 if (realTouchListIndex == uint.max) rewindTouchList();
769 while (realTouchListIndex < realTouchList.length) {
770 auto aid = ActorId(realTouchList.ptr[realTouchListIndex]);
771 ++realTouchListIndex;
772 if (aid.fget_classtype == StrPool.X_X) continue;
773 if (aid.valid && (aid.fget_flags&AF_NOONTOUCH) == 0) return aid;
775 return ActorId(0);
779 public void doActorsThink () {
780 // we have too much memory!
781 __gshared uint[65536] validActorsList;
782 __gshared ActorId[65536] postponedDeath;
783 __gshared uint pdcount;
785 touchListAllowed = true;
786 scope(exit) touchListAllowed = false;
787 pdcount = 0;
788 mapscripts.runPreThink();
789 foreach (uint xid; Actor.getValidList(validActorsList[])) {
790 auto me = ActorId(xid);
791 if (me.valid) {
792 if (me.fget_classtype == StrPool.X_X) { postponedDeath.ptr[pdcount++] = me; continue; }
793 auto flags = me.fget_flags;
794 if ((flags&AF_NOCOLLISION) == 0) ugActorModify!false(me); // remove from grid
795 if (auto adef = findActorDef(me)) {
796 if ((flags&AF_NOTHINK) == 0) {
797 realTouchListIndex = uint.max;
798 xactorListIndex = uint.max;
799 curThinkingActor = me;
800 adef.callThink(me);
801 //if (me.x!int < 32) conwriteln("actor: ", me.id, "; attLightRGBX=", me.attLightRGBX!uint);
802 if (!me.valid) continue; // we are dead
803 if (me.fget_classtype == StrPool.X_X) { postponedDeath.ptr[pdcount++] = me; continue; }
804 flags = me.fget_flags; // in case script updated flags
806 if ((flags&AF_NOANIMATE) == 0) {
807 int aidx = me.fget_zAnimidx;
808 int nidx = adef.nextAnimIdx(me.fget_zAnimstate, me.fget_dir, aidx);
809 //conwriteln("actor ", me.id, " (", me.classtype!string, me.classname!string, "): state=", me.zAnimstate!string, "; aidx=", aidx, "; nidx=", nidx);
810 me.fset_zAnimidx = nidx;
811 //assert(me.fget_zAnimidx == nidx);
813 // put back to grid
814 if ((flags&AF_NOCOLLISION) == 0) ugActorModify!true(me);
818 mapscripts.runPostThink();
819 // process scheduled death
820 foreach (ActorId aid; postponedDeath[0..pdcount]) {
821 /*if ((flags&AF_NOCOLLISION) == 0)*/ ugActorModify!false(aid); // remove from grid
822 Actor.remove(aid);
824 // reset player keys
825 plrKeysFix(0);
826 //{ import std.stdio : stdout; stdout.writeln("========================================="); }
827 //Actor.dumpActors();