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
;
40 // ////////////////////////////////////////////////////////////////////////// //
41 private extern (C
) void _d_print_throwable (Throwable t
);
44 // ////////////////////////////////////////////////////////////////////////// //
49 // ////////////////////////////////////////////////////////////////////////// //
50 public __gshared ActorId cameraChick
;
53 // ////////////////////////////////////////////////////////////////////////// //
54 public void addInternalActorFields () {
55 // &0xffff: position in drawlist
56 // (>>16)&0xff: drawlist index
57 //Actor.addField("0drawlistpos", Actor.Field.Type.Uint);
61 // ////////////////////////////////////////////////////////////////////////// //
64 enum PLK_DOWN
= (1<<1);
65 enum PLK_LEFT
= (1<<2);
66 enum PLK_RIGHT
= (1<<3);
67 enum PLK_FIRE
= (1<<4);
68 enum PLK_JUMP
= (1<<5);
69 enum PLK_USE
= (1<<6);
72 __gshared
uint plrKeysLast
;
73 __gshared
uint plrKeyState
;
76 public void plrKeyDown (uint plidx
, uint mask
) {
77 if (mask
== 0 || plidx
> 0) return;
83 public void plrKeyUp (uint plidx
, uint mask
) {
84 if (mask
== 0 || plidx
> 0) return;
89 public void plrKeyUpDown (uint plidx
, uint mask
, bool down
) {
90 if (down
) plrKeyDown(plidx
, mask
); else plrKeyUp(plidx
, mask
);
94 void plrKeysFix (uint plidx
) {
95 if (plidx
> 0) return;
96 //conwritefln!"plrKeyState=0x%02x; plrKeysLast=0x%02x"(plrKeyState, plrKeysLast);
97 foreach (uint n
; 0..12) {
98 if ((plrKeyState
&(1<<n
)) == 0) plrKeysLast
&= ~(1<<n
);
103 // ////////////////////////////////////////////////////////////////////////// //
104 void updateActorClasses () {
105 foreach (string ctype
; dacsClassTypes
) {
106 foreach (auto mod
; dacsClassModules(ctype
)) {
107 if (mod
.rmoduletype
.classtype
== "map") {
108 // map scripts, not here
110 auto adef
= registerActorDef(mod
.rmoduletype
.classtype
, mod
.rmoduletype
.classname
);
111 conwriteln("found info for actor '", mod
.rmoduletype
.classtype
, ":", mod
.rmoduletype
.classname
, "'");
113 auto animInitFn
= FuncPool
.findByFQMG(mod
.name
~".initializeAnim", ":void");
114 if (animInitFn
!is null) adef
.setAnimInitFunc(animInitFn
);
117 auto initFn
= FuncPool
.findByFQMG(mod
.name
~".initialize", ":void:Actor");
118 if (initFn
!is null) adef
.setInitFunc(initFn
);
121 auto thinkFn
= FuncPool
.findByFQMG(mod
.name
~".think", ":void:Actor");
122 if (thinkFn
!is null) adef
.setThinkFunc(thinkFn
);
129 if (auto mod
= dacsModuleFor("engine", "hud")) {
130 hudScripts
.fiDraw
= FuncPool
.findByFQMG(mod
.name
~".drawHud", ":void");
135 // ////////////////////////////////////////////////////////////////////////// //
136 private string
modLoader (string modname
) {
137 static string
getTextFile (string fname
) {
139 auto res
= loadTextFile(fname
);
140 conwriteln("loading DACS module file '", fname
, "' (", res
.length
, " bytes)");
141 if (res
is null) res
= "";
143 } catch (Exception
) {}
149 //res = getTextFile(modname~".dacs");
150 //if (res !is null) return res;
152 res
= getTextFile("scripts/"~modname
~".dacs");
153 if (res
!is null) return res
;
156 string
[] parts
= ["scripts"];
158 while (mn
.length
> 0) {
160 if (mn
[0] >= 'A' && mn
[0] <= 'Z') ++pos
;
161 while (pos
< mn
.length
&& (mn
[pos
] < 'A' || mn
[pos
] > 'Z')) ++pos
;
162 if (mn
[0] >= 'A' && mn
[0] <= 'Z') {
163 parts
~= cast(char)(mn
[0]+32)~mn
[1..pos
];
170 import std
.array
: join
;
171 string path
= parts
.join("/")~".dacs";
172 res
= getTextFile(path
);
173 if (res
!is null) return res
;
177 throw new Exception("module '"~modname
~"' not found");
181 __gshared string
[] dacsModules
;
183 public void registerWadScripts () {
185 import std
.array
: split
;
186 auto t
= loadTextFile("scripts/dacsmain.txt");
187 foreach (string line
; t
.split('\n')) {
188 while (line
.length
&& line
[0] <= ' ') line
= line
[1..$];
189 if (line
.length
== 0 || line
[0] == ';' || line
[0] == '#') continue;
190 while (line
.length
&& line
[$-1] <= ' ') line
= line
[0..$-1];
191 import std
.algorithm
: canFind
;
192 if (!dacsModules
.canFind(line
)) dacsModules
~= line
;
194 } catch (Exception e
) { return; }
198 public void loadWadScripts () {
199 if (moduleLoader
is null) moduleLoader
= (string modname
) => modLoader(modname
);
201 //conwriteln("loading main DACS module '", mainmod, "'");
202 foreach (string mod
; dacsModules
) parseModule(mod
);
204 updateActorClasses();
206 dacsFinalizeCompiler();
207 } catch (CompilerException e
) {
208 _d_print_throwable(e
);
209 conwriteln("PARSE ERROR: ", e
.file
, " at ", e
.line
);
210 conwriteln(e
.toString
);
216 // ////////////////////////////////////////////////////////////////////////// //
218 FuncPool
.FuncInfo fiDraw
;
221 if (fiDraw
!is null) {
222 //conwriteln("HUD SCRIPT!");
229 public __gshared HudScripts hudScripts
;
232 // ////////////////////////////////////////////////////////////////////////// //
233 public __gshared LevelMap map
;
234 //public __gshared DACSVM dvm;
235 public __gshared
uint prngSyncSeed
= 0x29a;
236 public __gshared
uint prngSeed
= 0x29a; // unsynced
237 public __gshared string curmapname
;
238 public __gshared string nextmapname
; // nonempty: go to next level
241 FuncPool
.FuncInfo fiInit
; // called after level map is loaded
242 FuncPool
.FuncInfo fiLoaded
; // called after mosters set
243 FuncPool
.FuncInfo fiUnloading
; // called before level is unloaded (i.e. player finished the level)
244 FuncPool
.FuncInfo fiPreThink
; // called before monster think
245 FuncPool
.FuncInfo fiPostThink
; // called after monster think
247 void runInit () { if (fiInit
!is null) fiInit(); }
248 void runLoaded () { if (fiLoaded
!is null) fiLoaded(); }
249 void runUnloading () { if (fiUnloading
!is null) fiUnloading(); }
250 void runPreThink () { if (fiPreThink
!is null) fiPreThink(); }
251 void runPostThink () { if (fiPostThink
!is null) fiPostThink(); }
254 public __gshared MapScripts mapscripts
;
257 // generate next map name for exit
258 public string
genNextMapName (int lnum
) {
259 import std
.algorithm
: endsWith
;
262 string nn
= curmapname
;
263 if (nn
.endsWith(".d2m")) {
267 if (nn
[$-1] < '0' || nn
[$-1] > '9') return null;
268 uint mapnum
= 0, mmul
= 1;
269 while (nn
.length
> 0 && nn
[$-1] >= '0' && nn
[$-1] <= '9') {
270 mapnum
+= (nn
[$-1]-'0')*mmul
;
275 if (lnum
> 0 && lnum
< 100) mapnum
= lnum
;
276 if (mapnum
> 99) return null; // alas
277 import std
.string
: format
;
278 return "%s%02u%s".format(nn
, mapnum
, ext
);
282 public void clearMapScripts () {
283 mapscripts
= mapscripts
.init
; // remove old scripts
287 public void setupMapScripts () {
288 void setupFromModule(T
) (T mod
) {
289 mapscripts
.fiInit
= FuncPool
.findByFQMG(mod
.name
~".initialize", ":void");
290 mapscripts
.fiLoaded
= FuncPool
.findByFQMG(mod
.name
~".loaded", ":void");
291 mapscripts
.fiUnloading
= FuncPool
.findByFQMG(mod
.name
~".unloading", ":void");
292 mapscripts
.fiPreThink
= FuncPool
.findByFQMG(mod
.name
~".prethink", ":void");
293 mapscripts
.fiPostThink
= FuncPool
.findByFQMG(mod
.name
~".postthink", ":void");
296 import std
.path
: baseName
, setExtension
;
298 string mapname
= curmapname
.baseName
.setExtension("");
299 // normal map scripts
300 if (auto mod
= dacsModuleFor("map", mapname
)) {
301 conwriteln("found module for map '", mapname
, "'");
302 setupFromModule(mod
);
305 // not found, try "unnamed map"
306 if (auto mod
= dacsModuleFor("map", " ")) {
307 conwriteln("using module for unknownmap '", mapname
, "'");
308 setupFromModule(mod
);
314 // ////////////////////////////////////////////////////////////////////////// //
315 // Park-Miller-Carta Pseudo-Random Number Generator, based on David G. Carta paper
316 // 31 bit of randomness
317 // seed is previous result, as usual
318 public uint prngR31next (uint seed
) {
319 if (seed
== 0) seed
= 0x29a;
320 uint lo
= 16807*(seed
&0xffff);
321 uint hi
= 16807*(seed
>>16);
322 lo
+= (hi
&0x7fff)<<16;
324 //if (lo > 0x7fffffff) lo -= 0x7fffffff; // should be >=, actually
325 lo
= (lo
&0x7FFFFFFF)+(lo
>>31); // same as previous code, but branch-less
330 // "synced" random, using common seed for all players
331 public uint syncrandu31 () {
332 pragma(inline
, true);
333 return (prngSyncSeed
= prngR31next(prngSyncSeed
));
337 // "unsynced" random, seed isn't saved
338 public uint unsyncrandu31 () {
339 pragma(inline
, true);
340 return (prngSeed
= prngR31next(prngSeed
));
344 // ////////////////////////////////////////////////////////////////////////// //
345 version(dacs_use_vm
) {
346 // this is VAT function, argument order is reversed
347 void doWrite(bool donl
) (FuncPool
.FuncInfo fi
, DACSVM vm
) {
349 auto count
= vm
.popInt();
350 while (count
-- > 0) {
351 auto type
= vm
.popInt();
352 switch (cast(VATArgType
)type
) {
353 case VATArgType
.Int
: write(vm
.popInt()); break;
354 case VATArgType
.Uint
: write(vm
.popUint()); break;
355 case VATArgType
.Float
: write(vm
.popFloat()); break;
356 case VATArgType
.StrId
: write(StrId(vm
.popUint()).get
); break;
357 case VATArgType
.ActorId
: write("<actor>"); vm
.popUint(); break; //TODO
358 default: write("<invalid-type>"); vm
.popUint(); break; //TODO
361 static if (donl
) writeln();
362 // push dummy return value
366 extern(C
) void doWrite(bool donl
) (uint argc
, ...) {
369 mainloop
: while (argc
>= 2) {
371 int tp
= va_arg
!int(_argptr
);
374 auto v
= va_arg
!int(_argptr
);
377 case VATArgType
.Uint
:
378 auto v
= va_arg
!uint(_argptr
);
381 case VATArgType
.Float
:
382 auto v
= va_arg
!float(_argptr
);
385 case VATArgType
.StrId
:
386 auto v
= StrId(va_arg
!uint(_argptr
)).get
;
389 case VATArgType
.ActorId
:
390 auto v
= ActorId(va_arg
!uint(_argptr
));
391 write("<actor:", v
.valid
, ":", v
.id
, ">");
394 default: write("<invalid-type>"); break mainloop
;
397 static if (donl
) writeln();
402 // ////////////////////////////////////////////////////////////////////////// //
403 void animClearFrames (StrId classtype
, StrId classname
, StrId state
) {
404 auto adef
= findActorDef(classtype
.get
, classname
.get
);
405 if (adef
is null) throw new Exception("animClearFrames: actor '"~classtype
.get
~":"~classname
.get
~"' not found!");
406 adef
.clearFrames(state
);
410 void animAddFrame (StrId classtype
, StrId classname
, StrId state
, uint dir
, StrId sprname
) {
411 auto adef
= findActorDef(classtype
.get
, classname
.get
);
412 if (adef
is null) throw new Exception("animAddFrame: actor '"~classtype
.get
~":"~classname
.get
~"' not found!");
413 if (dir
!= 0) dir
= 1; //TODO: process mirror flag here
414 adef
.addFrame(state
, dir
, sprname
);
418 // ////////////////////////////////////////////////////////////////////////// //
420 conwriteln("setting up D API");
422 FuncPool
["write"] = &doWrite
!false;
423 FuncPool
["writeln"] = &doWrite
!true;
425 FuncPool
["syncrandu31"] = &syncrandu31
;
427 FuncPool
["animClearFrames"] = &animClearFrames
;
428 FuncPool
["animAddFrame"] = &animAddFrame
;
430 FuncPool
["actorSetAnimation"] = function void (ActorId me
, StrId state
) {
431 if (!me
.valid
) return;
432 if (auto adef
= findActorDef(me
.classtype
!string
, me
.classname
!string
)) {
433 me
.zAnimstate
= state
;
438 //FuncPool["getPlayer"] = function ActorId () { assert(0); };
439 //FuncPool["isPlayer"] = function int (ActorId me) { return (me == players.ptr[0] ? 1 : 0); };
441 FuncPool
["getPlayerCount"] = function int () => 1;
443 FuncPool
["getPlayerActor"] = function ActorId (uint pnum
) {
444 if (pnum
== 1) return players
.ptr
[0];
448 FuncPool
["getPlayerButtons"] = function uint (uint pidx
) { return (pidx
== 1 ? plrKeysLast
: 0); };
450 FuncPool
["mapGetTypeTile"] = function int (int x
, int y
) {
452 if (map
!is null && x
>= 0 && y
>= 0 && x
< map
.width
&& y
< map
.height
) {
453 res
= map
.tiles
.ptr
[LevelMap
.Type
].ptr
[y
*map
.width
+x
];
454 if (res
!= LevelMap
.TILE_ACTTRAP
) res
&= 0x7f;
459 FuncPool
["mapGetTile"] = function int (uint layer
, int x
, int y
) {
460 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);
463 FuncPool
["mapSetTypeTile"] = function void (int x
, int y
, int tid
) {
464 if (map
is null || x
< 0 || y
< 0 || x
>= map
.width || y
>= map
.height || tid
< 0 || tid
> 255) return;
465 auto p
= map
.tiles
.ptr
[LevelMap
.Type
].ptr
+y
*map
.width
+x
;
468 import render
: mapDirty
;
469 mapDirty((1<<LevelMap
.Type
)|
(1<<LevelMap
.LightMask
));
473 FuncPool
["mapSetTile"] = function void (uint layer
, int x
, int y
, int tid
) {
474 if (map
is null || layer
< 0 || layer
> 1 || x
< 0 || y
< 0 || x
>= map
.width || y
>= map
.height || tid
< 0 || tid
> 255) return;
475 auto p
= map
.tiles
.ptr
[LevelMap
.Front
+layer
].ptr
+y
*map
.width
+x
;
478 import render
: mapDirty
;
481 mapDirty((1<<LevelMap
.Front
)|
(1<<LevelMap
.AllLiquids
)|
(1<<LevelMap
.LiquidMask
));
484 mapDirty((1<<LevelMap
.Back
)|
(1<<LevelMap
.AllLiquids
)|
(1<<LevelMap
.LiquidMask
));
489 FuncPool
["mapGetWaterTexture"] = function int (int fg
) {
490 if (fg
< 0 || fg
>= map
.wallnames
.length
) return 0;
491 switch (map
.wallnames
.ptr
[fg
]) {
492 case "_water_0": return 1;
493 case "_water_1": return 2;
494 case "_water_2": return 3;
500 FuncPool
["getMapViewHeight"] = function int () {
501 import render
: vlWidth
, vlHeight
, getScale
;
502 return vlHeight
/getScale
;
505 FuncPool
["actorsOverlap"] = function int (ActorId a
, ActorId b
) { return (actorsOverlap(a
, b
) ?
1 : 0); };
507 FuncPool
["actorRemove"] = function void (ActorId me
) {
509 if ((me
.fget_flags
&AF_NOCOLLISION
) == 0) ugActorModify
!false(me
); // remove from grid
514 FuncPool
["rewindTouchList"] = &rewindTouchList
;
515 FuncPool
["getNextTouchListItem"] = &getNextTouchListItem
;
517 FuncPool
["actorListRewind"] = &xactorListRewind
;
518 FuncPool
["actorListNext"] = &xactorListNext
;
520 FuncPool
["getCheatNoDoors"] = function int () { import render
: cheatNoDoors
; return (cheatNoDoors ?
1 : 0); };
521 FuncPool
["getCheatNoWallClip"] = function int () { import render
: cheatNoWallClip
; return (cheatNoWallClip ?
1 : 0); };
522 FuncPool
["getCheatNoCeilClip"] = function int () { return 0; };
523 FuncPool
["getCheatNoLiftClip"] = function int () { return 0; };
525 FuncPool
["addMessage"] = function void (string text
, int pause
, bool noreplace
) {
526 import render
: postAddMessage
;
527 postAddMessage(text
, pause
, noreplace
);
530 import d2dparts
: dotAddBlood
, dotAddSpark
, dotAddWater
;
532 FuncPool
["dotAddBlood"] = &dotAddBlood
;
533 FuncPool
["dotAddSpark"] = &dotAddSpark
;
534 FuncPool
["dotAddWater"] = &dotAddWater
;
536 function void (int x, int y, int xv, int yv, int n, int color) {
537 conwriteln("dotAddWater: x=", x, "; y=", y, "; xv=", xv, "; yv=", yv, "; n=", n, "; color=", color);
538 dotAddWater(x, y, xv, yv, n, color);
542 FuncPool
["gactLevelExit"] = function void (int lnum
) {
543 if (nextmapname
.length
== 0) {
544 string nmname
= genNextMapName(lnum
);
545 if (nmname
.length
== 0) assert(0, "no level!");
546 nextmapname
= nmname
;
550 FuncPool
["actorSpawn"] = &actorSpawn
;
552 // return previous one
553 FuncPool
["setCameraChick"] = function ActorId (ActorId act
) {
554 auto res
= cameraChick
;
559 FuncPool
["MapWidth"] = function int () { return (map
!is null ? map
.width
: 1); };
560 FuncPool
["MapHeight"] = function int () { return (map
!is null ? map
.height
: 1); };
562 FuncPool
["viewportWidth"] = function int () { return vlWidth
; };
563 FuncPool
["viewportHeight"] = function int () { return vlHeight
; };
565 FuncPool
["spriteWidth"] = function int (string spfile
) {
566 auto im
= loadSprite(spfile
, true);
567 return (im
!is null ? im
.vga
.width
: 0);
570 FuncPool
["spriteHeight"] = function int (string spfile
) {
571 auto im
= loadSprite(spfile
, true);
572 return (im
!is null ? im
.vga
.height
: 0);
575 FuncPool
["drawSpriteAt"] = function void (string spfile
, int x
, int y
) {
576 auto im
= loadSprite(spfile
, true);
581 FuncPool
["drawSpriteAtNoOfs"] = function void (string spfile
, int x
, int y
) {
582 auto im
= loadSprite(spfile
, true);
584 im
.drawAtXY(x
+im
.vga
.sx
, y
+im
.vga
.sy
-im
.vga
.height
);
588 FuncPool
["drawTextRGBA"] = function void (string text
, int x
, int y
, int r
, int g
, int b
, int a
) {
589 import d2dfont
: smbwDrawText
;
590 if (r
< 0) r
= 0; else if (r
> 255) r
= 255;
591 if (g
< 0) g
= 0; else if (g
> 255) g
= 255;
592 if (b
< 0) b
= 0; else if (b
> 255) b
= 255;
593 if (a
< 0) a
= 0; else if (a
> 255) a
= 255;
594 glColor4f(r
/255.0f, g
/255.0f, b
/255.0f, a
/255.0f);
595 smbwDrawText(x
, y
, text
);
596 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
599 FuncPool
["drawTextBigRGBA"] = function void (string text
, int x
, int y
, int r
, int g
, int b
, int a
) {
600 import d2dfont
: bfDrawText
;
601 if (r
< 0) r
= 0; else if (r
> 255) r
= 255;
602 if (g
< 0) g
= 0; else if (g
> 255) g
= 255;
603 if (b
< 0) b
= 0; else if (b
> 255) b
= 255;
604 if (a
< 0) a
= 0; else if (a
> 255) a
= 255;
605 glColor4f(r
/255.0f, g
/255.0f, b
/255.0f, a
/255.0f);
606 bfDrawText(x
, y
, text
);
607 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
610 FuncPool
["textWidth"] = function int (string text
) { import d2dfont
: smTextWidth
; return smTextWidth(text
); };
611 FuncPool
["textHeight"] = function int (string text
) { import d2dfont
: smTextHeight
; return smTextHeight(text
); };
613 FuncPool
["textWidthBig"] = function int (string text
) { import d2dfont
: bfTextWidth
; return bfTextWidth(text
); };
614 FuncPool
["textHeightBig"] = function int (string text
) { import d2dfont
: bfTextHeight
; return bfTextHeight(text
); };
618 public void registerAPI () {
619 //Actor.actorSize += 256;
620 conwriteln("actor size is ", Actor
.actorSize
, " bytes");
622 version(dacs_use_vm
) FuncPool
.vm
= new DACSVM();
623 //dvm = new DACSVM();
625 // initialize actor animation
626 ActorDef
.forEach((adef
) => adef
.callAnimInit());
628 conwriteln("API registered");
632 // ////////////////////////////////////////////////////////////////////////// //
633 mixin(Actor
.FieldGetMixin
!("classtype", StrId
)); // fget_classtype
634 mixin(Actor
.FieldGetMixin
!("classname", StrId
)); // fget_classname
635 mixin(Actor
.FieldGetMixin
!("x", int));
636 mixin(Actor
.FieldGetMixin
!("y", int));
637 mixin(Actor
.FieldGetMixin
!("dir", uint));
638 mixin(Actor
.FieldGetMixin
!("height", int));
639 mixin(Actor
.FieldGetMixin
!("radius", int));
640 mixin(Actor
.FieldGetMixin
!("flags", uint));
641 mixin(Actor
.FieldGetMixin
!("zAnimstate", StrId
));
642 mixin(Actor
.FieldGetMixin
!("zAnimidx", int));
644 mixin(Actor
.FieldSetMixin
!("zAnimidx", int));
647 public bool actorsOverlap (ActorId a
, ActorId b
) {
648 if (!a
.valid ||
!b
.valid
) return false;
649 if (a
.id
== b
.id
) return false; // no self-overlap
653 int ar
= a
.fget_radius
;
654 int br
= b
.fget_radius
;
656 if (ax
-ar
> bx
+br || ax
+ar
< bx
-br
) return false;
660 int ah
= a
.fget_height
;
661 int bh
= b
.fget_height
;
663 //return (ay > by-bh && ay-ah < by);
664 return (by
-bh
<= ay
&& by
>= ay
-ah
);
668 // ////////////////////////////////////////////////////////////////////////// //
669 public __gshared ActorId
[2] players
;
672 public void loadAllMonsterGraphics () {
673 ActorDef
.forEach((adef
) {
674 conwriteln("loading graphics for '", adef
.classtype
.get
, ":", adef
.classname
.get
, "'");
677 //Actor.dumpActors();
678 realiseSpriteAtlases();
682 ActorId
actorSpawn (StrId classtype
, StrId classname
, int x
, int y
, uint dir
) {
683 auto adef
= findActorDef(classtype
, classname
);
684 if (adef
is null) return ActorId(0);
685 auto aid
= Actor
.alloc
;
686 aid
.classtype
= classtype
;
687 aid
.classname
= classname
;
688 aid
.state
= StrPool
.MNST_SLEEP
;
691 aid
.dir
= (dir ?
1 : 0);
693 if ((aid
.fget_flags
&AF_NOCOLLISION
) == 0) ugActorModify
!true(aid
);
698 public void loadMapMonsters () {
699 assert(map
!is null);
701 players
[] = ActorId(0);
702 //conwriteln(players[0].valid, "; ", players[0].id);
703 foreach (ref thing
; map
.things
) {
704 if (thing
.dmonly
) continue;
705 auto did
= (thing
.type
&0x7fff) in d2dactordefsById
;
706 if (did
!is null && did
.classtype
== "playerstart") {
707 if ((thing
.type
&0x7fff) == 1 ||
(thing
.type
&0x7fff) == 2) {
708 int pnum
= thing
.type
-1;
709 if (!players
[pnum
].valid
) {
710 auto aid
= Actor
.alloc
;
711 aid
.classtype
= StrPool
.intern("monster");
712 aid
.classname
= StrPool
.intern("Player");
713 aid
.plrnum
= cast(uint)(pnum
+1);
714 aid
.state
= StrPool
.MNST_SLEEP
;
715 aid
.x
= cast(int)thing
.x
;
716 aid
.y
= cast(int)thing
.y
;
717 aid
.dir
= cast(uint)(thing
.right ?
1 : 0);
718 auto adef
= findD2DActorDef(thing
.type
);
719 if (adef
is null) assert(0);
721 if ((aid
.fget_flags
&AF_NOCOLLISION
) == 0) ugActorModify
!true(aid
);
723 conwriteln("player #", pnum
+1, " aid is ", aid
.id
);
728 auto adef
= findD2DActorDef(thing
.type
&0x7fff);
731 conwriteln("ignoring D2D thing '", did
.classtype
.get
, ":", did
.classname
.get
, "'");
733 conwriteln("ignoring unknown D2D thing with mapid ", thing
.type
);
737 // create actor and initialize it
738 auto aid
= Actor
.alloc
;
739 aid
.classtype
= StrPool
.intern(adef
.classtype
);
740 aid
.classname
= StrPool
.intern(adef
.classname
);
741 //conwriteln("found '", aid.classtype.get, ":", aid.classname.get, "'");
743 assert(did
.classtype
== adef
.classtype
);
744 assert(did
.classname
== adef
.classname
);
745 conwriteln("mapid=", thing
.type
, "; ", adef
.classtype
, ":", adef
.classname
, "; id=", aid
.id
);
746 assert(aid
.classtype
!string
== adef
.classtype
);
747 assert(aid
.classname
!string
== adef
.classname
);
751 aid
.state
= StrPool
.MNST_SLEEP
;
752 aid
.x
= cast(int)thing
.x
;
753 aid
.y
= cast(int)thing
.y
;
754 aid
.dir
= cast(uint)(thing
.right ?
1 : 0);
755 if (thing
.type
&0x8000) aid
.flags
= aid
.fget_flags|AF_NOGRAVITY
;
756 //if (aid.classtype!string == "item" && aid.x!int < 64) { aid.x = 92; aid.y = aid.y!int-16; conwriteln("!!!"); }
758 if ((aid
.fget_flags
&AF_NOCOLLISION
) == 0) ugActorModify
!true(aid
);
762 foreach (ref sw
; map
.switches
) {
763 if (sw
.type
== 0) continue; // just in case
764 auto swname
= getD2DSwitchClassName(sw
.type
);
765 if (swname
.length
== 0) {
766 conwriteln("unknown switch type ", sw
.type
);
769 if (auto adef
= findActorDef("switch", swname
)) {
770 auto aid
= Actor
.alloc
;
771 aid
.classtype
= StrPool
.intern("switch");
772 aid
.classname
= StrPool
.intern(swname
);
773 // nocollision, 'cause collision checking will be processed in switch thinker, but other actors should not touch switches
774 aid
.flags
= /*AF_NOCOLLISION|*/AF_NOGRAVITY|
/*AF_NOONTOUCH|*/AF_NODRAW|AF_NOLIGHT|AF_NOANIMATE
;
775 aid
.x
= sw
.x
*TileSize
;
776 aid
.y
= sw
.y
*TileSize
;
777 aid
.switchabf
= (sw
.a
<<16)|
(sw
.b
<<8)|sw
.flags
;
782 if ((aid
.fget_flags
&AF_NOCOLLISION
) == 0) ugActorModify
!true(aid
); // just in case
784 conwriteln("switch definition 'switch:", swname
, "' not found");
788 conwriteln("initial snapshot size: ", Actor
.snapshotSize
, " bytes");
792 // ////////////////////////////////////////////////////////////////////////// //
793 __gshared ActorId
[65536] xactorList
; // aids
794 __gshared
uint xactorListCount
, xactorListIndex
= uint.max
;
798 void xactorListRewind () {
799 if (!touchListAllowed
) return; // it's ok to reuse it here
800 xactorListCount
= xactorListIndex
= 0;
801 Actor
.forEach((ActorId me
) {
802 if (me
.fget_classtype
!= StrPool
.X_X
) xactorList
.ptr
[xactorListCount
++] = me
;
808 ActorId
xactorListNext () {
809 if (!touchListAllowed
) return ActorId(0); // it's ok to reuse it here
810 if (xactorListIndex
== uint.max
) xactorListRewind();
811 while (xactorListIndex
< xactorListCount
) {
812 auto aid
= xactorList
.ptr
[xactorListIndex
];
814 if (aid
.fget_classtype
== StrPool
.X_X
) continue;
821 // ////////////////////////////////////////////////////////////////////////// //
822 __gshared
uint[65536] touchList
; // aids
823 __gshared
uint[] realTouchList
;
824 __gshared
uint realTouchListIndex
= uint.max
;
825 __gshared ActorId curThinkingActor
;
826 __gshared
bool touchListAllowed
= false;
830 void rewindTouchList () {
831 if (!touchListAllowed
) return;
832 realTouchListIndex
= 0;
833 realTouchList
= ugActorHitList(curThinkingActor
, touchList
[]);
838 ActorId
getNextTouchListItem () {
839 if (!touchListAllowed
) return ActorId(0);
840 if (realTouchListIndex
== uint.max
) rewindTouchList();
841 while (realTouchListIndex
< realTouchList
.length
) {
842 auto aid
= ActorId(realTouchList
.ptr
[realTouchListIndex
]);
843 ++realTouchListIndex
;
844 if (aid
.fget_classtype
== StrPool
.X_X
) continue;
845 if (aid
.valid
&& (aid
.fget_flags
&AF_NOONTOUCH
) == 0) return aid
;
851 public void doActorsThink () {
852 // we have too much memory!
853 __gshared
uint[65536] validActorsList
;
854 __gshared ActorId
[65536] postponedDeath
;
855 __gshared
uint pdcount
;
857 touchListAllowed
= true;
858 scope(exit
) touchListAllowed
= false;
860 mapscripts
.runPreThink();
861 foreach (uint xid
; Actor
.getValidList(validActorsList
[])) {
862 auto me
= ActorId(xid
);
864 if (me
.fget_classtype
== StrPool
.X_X
) { postponedDeath
.ptr
[pdcount
++] = me
; continue; }
865 auto flags
= me
.fget_flags
;
866 if ((flags
&AF_NOCOLLISION
) == 0) ugActorModify
!false(me
); // remove from grid
867 if (auto adef
= findActorDef(me
)) {
868 if ((flags
&AF_NOTHINK
) == 0) {
869 realTouchListIndex
= uint.max
;
870 xactorListIndex
= uint.max
;
871 curThinkingActor
= me
;
873 //if (me.x!int < 32) conwriteln("actor: ", me.id, "; attLightRGBX=", me.attLightRGBX!uint);
874 if (!me
.valid
) continue; // we are dead
875 if (me
.fget_classtype
== StrPool
.X_X
) { postponedDeath
.ptr
[pdcount
++] = me
; continue; }
876 flags
= me
.fget_flags
; // in case script updated flags
878 if ((flags
&AF_NOANIMATE
) == 0) {
879 int aidx
= me
.fget_zAnimidx
;
880 int nidx
= adef
.nextAnimIdx(me
.fget_zAnimstate
, me
.fget_dir
, aidx
);
881 //conwriteln("actor ", me.id, " (", me.classtype!string, me.classname!string, "): state=", me.zAnimstate!string, "; aidx=", aidx, "; nidx=", nidx);
882 me
.fset_zAnimidx
= nidx
;
883 //assert(me.fget_zAnimidx == nidx);
886 if ((flags
&AF_NOCOLLISION
) == 0) ugActorModify
!true(me
);
890 mapscripts
.runPostThink();
891 // process scheduled death
892 foreach (ActorId aid
; postponedDeath
[0..pdcount
]) {
893 /*if ((flags&AF_NOCOLLISION) == 0)*/ ugActorModify
!false(aid
); // remove from grid
898 //{ import std.stdio : stdout; stdout.writeln("========================================="); }
899 //Actor.dumpActors();