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
;
38 // ////////////////////////////////////////////////////////////////////////// //
39 private extern (C
) void _d_print_throwable (Throwable t
);
42 // ////////////////////////////////////////////////////////////////////////// //
47 // ////////////////////////////////////////////////////////////////////////// //
50 enum PLK_DOWN
= (1<<1);
51 enum PLK_LEFT
= (1<<2);
52 enum PLK_RIGHT
= (1<<3);
53 enum PLK_FIRE
= (1<<4);
54 enum PLK_JUMP
= (1<<5);
55 enum PLK_USE
= (1<<6);
58 __gshared
uint plrKeysLast
;
59 __gshared
uint plrKeyState
;
62 public void plrKeyDown (uint plidx
, uint mask
) {
63 if (mask
== 0 || plidx
> 0) return;
69 public void plrKeyUp (uint plidx
, uint mask
) {
70 if (mask
== 0 || plidx
> 0) return;
75 public void plrKeyUpDown (uint plidx
, uint mask
, bool down
) {
76 if (down
) plrKeyDown(plidx
, mask
); else plrKeyUp(plidx
, mask
);
80 void plrKeysFix (uint plidx
) {
81 if (plidx
> 0) return;
82 //conwritefln!"plrKeyState=0x%02x; plrKeysLast=0x%02x"(plrKeyState, plrKeysLast);
83 foreach (uint n
; 0..12) {
84 if ((plrKeyState
&(1<<n
)) == 0) plrKeysLast
&= ~(1<<n
);
89 // ////////////////////////////////////////////////////////////////////////// //
90 void updateActorClasses (int iteration
) {
91 foreach (string ctype
; dacsClassTypes(iteration
)) {
92 foreach (auto mod
; dacsClassModules(ctype
, iteration
)) {
93 auto adef
= registerActorDef(mod
.rmoduletype
.classtype
, mod
.rmoduletype
.classname
);
94 conwriteln("found info for actor '", adef
.fullname
, "'");
96 auto animInitFn
= FuncPool
.findByFQMG(mod
.name
~".initializeAnim", ":void");
97 if (animInitFn
!is null) adef
.setAnimInitFunc(animInitFn
);
100 auto initFn
= FuncPool
.findByFQMG(mod
.name
~".initialize", ":void:Actor");
101 if (initFn
!is null) adef
.setInitFunc(initFn
);
104 auto thinkFn
= FuncPool
.findByFQMG(mod
.name
~".think", ":void:Actor");
105 if (thinkFn
!is null) adef
.setThinkFunc(thinkFn
);
112 // ////////////////////////////////////////////////////////////////////////// //
113 private string
modLoader (string modname
) {
114 static string
getTextFile (string fname
) {
116 auto res
= loadTextFile(fname
);
117 conwriteln("loading DACS module file '", fname
, "' (", res
.length
, " bytes)");
118 if (res
is null) res
= "";
120 } catch (Exception
) {}
126 //res = getTextFile(modname~".dacs");
127 //if (res !is null) return res;
129 res
= getTextFile("scripts/"~modname
~".dacs");
130 if (res
!is null) return res
;
133 string
[] parts
= ["scripts"];
135 while (mn
.length
> 0) {
137 if (mn
[0] >= 'A' && mn
[0] <= 'Z') ++pos
;
138 while (pos
< mn
.length
&& (mn
[pos
] < 'A' || mn
[pos
] > 'Z')) ++pos
;
139 if (mn
[0] >= 'A' && mn
[0] <= 'Z') {
140 parts
~= cast(char)(mn
[0]+32)~mn
[1..pos
];
147 import std
.array
: join
;
148 string path
= parts
.join("/")~".dacs";
149 res
= getTextFile(path
);
150 if (res
!is null) return res
;
154 throw new Exception("module '"~modname
~"' not found");
158 __gshared
int modIteration
= 0;
160 public void loadWadScripts () {
161 if (moduleLoader
is null) moduleLoader
= (string modname
) => modLoader(modname
);
164 import std
.array
: split
;
165 auto t
= loadTextFile("scripts/dacsmain.txt");
166 foreach (string line
; t
.split('\n')) {
167 while (line
.length
&& line
[0] <= ' ') line
= line
[1..$];
168 if (line
.length
== 0 || line
[0] == ';') continue;
169 while (line
.length
&& line
[$-1] <= ' ') line
= line
[0..$-1];
172 } catch (Exception e
) { return; }
174 //conwriteln("loading main DACS module '", mainmod, "'");
175 auto iter
= modIteration
++;
176 conwriteln("iteration=", iter
);
177 foreach (string mod
; mainmods
) {
178 parseModule(mod
, iter
);
180 conwriteln("calling parseComplete; iteration=", iter
);
182 updateActorClasses(iter
);
183 } catch (CompilerException e
) {
184 _d_print_throwable(e
);
185 conwriteln("PARSE ERROR: ", e
.file
, " at ", e
.line
);
186 conwriteln(e
.toString
);
192 public void scriptLoadingComplete () {
194 dacsFinalizeCompiler();
198 // ////////////////////////////////////////////////////////////////////////// //
199 public __gshared LevelMap map
;
200 //public __gshared DACSVM dvm;
201 public __gshared
uint prngSyncSeed
= 0x29a;
202 public __gshared
uint prngSeed
= 0x29a; // unsynced
205 // ////////////////////////////////////////////////////////////////////////// //
206 // Park-Miller-Carta Pseudo-Random Number Generator, based on David G. Carta paper
207 // 31 bit of randomness
208 // seed is previous result, as usual
209 public uint prngR31next (uint seed
) {
210 if (seed
== 0) seed
= 0x29a;
211 uint lo
= 16807*(seed
&0xffff);
212 uint hi
= 16807*(seed
>>16);
213 lo
+= (hi
&0x7fff)<<16;
215 //if (lo > 0x7fffffff) lo -= 0x7fffffff; // should be >=, actually
216 lo
= (lo
&0x7FFFFFFF)+(lo
>>31); // same as previous code, but branch-less
221 // "synced" random, using common seed for all players
222 public uint syncrandu31 () {
223 pragma(inline
, true);
224 return (prngSyncSeed
= prngR31next(prngSyncSeed
));
228 // ////////////////////////////////////////////////////////////////////////// //
229 version(dacs_use_vm
) {
230 // this is VAT function, argument order is reversed
231 void doWrite(bool donl
) (FuncPool
.FuncInfo fi
, DACSVM vm
) {
233 auto count
= vm
.popInt();
234 while (count
-- > 0) {
235 auto type
= vm
.popInt();
236 switch (cast(VATArgType
)type
) {
237 case VATArgType
.Int
: write(vm
.popInt()); break;
238 case VATArgType
.Uint
: write(vm
.popUint()); break;
239 case VATArgType
.Float
: write(vm
.popFloat()); break;
240 case VATArgType
.StrId
: write(StrId(vm
.popUint()).get
); break;
241 case VATArgType
.ActorId
: write("<actor>"); vm
.popUint(); break; //TODO
242 default: write("<invalid-type>"); vm
.popUint(); break; //TODO
245 static if (donl
) writeln();
246 // push dummy return value
250 extern(C
) void doWrite(bool donl
) (uint argc
, ...) {
253 mainloop
: while (argc
>= 2) {
255 int tp
= va_arg
!int(_argptr
);
258 auto v
= va_arg
!int(_argptr
);
261 case VATArgType
.Uint
:
262 auto v
= va_arg
!uint(_argptr
);
265 case VATArgType
.Float
:
266 auto v
= va_arg
!float(_argptr
);
269 case VATArgType
.StrId
:
270 auto v
= StrId(va_arg
!uint(_argptr
)).get
;
273 case VATArgType
.ActorId
:
274 auto v
= ActorId(va_arg
!uint(_argptr
));
275 write("<actor:", v
.valid
, ":", v
.id
, ">");
278 default: write("<invalid-type>"); break mainloop
;
281 static if (donl
) writeln();
286 // ////////////////////////////////////////////////////////////////////////// //
287 void animClearFrames (StrId classtype
, StrId classname
, StrId state
) {
288 auto adef
= findActorDef(classtype
.get
, classname
.get
);
289 if (adef
is null) throw new Exception("animClearFrames: actor '"~classtype
.get
~":"~classname
.get
~"' not found!");
290 adef
.clearFrames(state
);
294 void animAddFrame (StrId classtype
, StrId classname
, StrId state
, uint dir
, StrId sprname
) {
295 auto adef
= findActorDef(classtype
.get
, classname
.get
);
296 if (adef
is null) throw new Exception("animAddFrame: actor '"~classtype
.get
~":"~classname
.get
~"' not found!");
297 if (dir
!= 0) dir
= 1; //TODO: process mirror flag here
298 adef
.addFrame(state
, dir
, sprname
);
302 // ////////////////////////////////////////////////////////////////////////// //
304 conwriteln("setting up D API");
306 FuncPool
["write"] = &doWrite
!false;
307 FuncPool
["writeln"] = &doWrite
!true;
309 FuncPool
["syncrandu31"] = &syncrandu31
;
311 FuncPool
["animClearFrames"] = &animClearFrames
;
312 FuncPool
["animAddFrame"] = &animAddFrame
;
314 FuncPool
["actorSetAnimation"] = function void (ActorId me
, StrId state
) {
315 if (!me
.valid
) return;
316 if (auto adef
= findActorDef(me
.classtype
!string
, me
.classname
!string
)) {
317 me
.zAnimstate
= state
;
322 //FuncPool["getPlayer"] = function ActorId () { assert(0); };
323 //FuncPool["isPlayer"] = function int (ActorId me) { return (me == players.ptr[0] ? 1 : 0); };
325 FuncPool
["getPlayerButtons"] = function uint (uint pidx
) { return (pidx
== 1 ? plrKeysLast
: 0); };
327 FuncPool
["mapGetTypeTile"] = function int (int x
, int y
) {
328 return (map
!is null && x
>= 0 && y
>= 0 && x
< map
.width
&& y
< map
.height ? map
.tiles
.ptr
[LevelMap
.Type
].ptr
[y
*map
.width
+x
] : 0);
331 FuncPool
["mapGetTile"] = function int (uint layer
, int x
, int y
) {
332 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);
335 FuncPool
["mapSetTypeTile"] = function void (int x
, int y
, int tid
) {
336 if (map
is null || x
< 0 || y
< 0 || x
>= map
.width || y
>= map
.height || tid
< 0 || tid
> 255) return;
337 auto p
= map
.tiles
.ptr
[LevelMap
.Type
].ptr
+y
*map
.width
+x
;
340 import xmain_d2d
: mapDirty
;
345 FuncPool
["mapSetTile"] = function void (uint layer
, int x
, int y
, int tid
) {
346 if (map
is null || layer
< 0 || layer
> 1 || x
< 0 || y
< 0 || x
>= map
.width || y
>= map
.height || tid
< 0 || tid
> 255) return;
347 auto p
= map
.tiles
.ptr
[LevelMap
.Front
+layer
].ptr
+y
*map
.width
+x
;
350 import xmain_d2d
: mapDirty
;
355 FuncPool
["getMapViewHeight"] = function int () {
356 import xmain_d2d
: vlWidth
, vlHeight
, scale
;
357 return vlHeight
/scale
;
360 FuncPool
["setMapViewPos"] = function void (int x
, int y
) {
361 import xmain_d2d
: setMapViewPos
;
365 FuncPool
["actorsOverlap"] = function int (ActorId a
, ActorId b
) { return (actorsOverlap(a
, b
) ?
1 : 0); };
367 FuncPool
["actorRemove"] = function void (ActorId me
) { Actor
.remove(me
); };
369 FuncPool
["rewindTouchList"] = &rewindTouchList
;
370 FuncPool
["getNextTouchListItem"] = &getNextTouchListItem
;
372 FuncPool
["getCheatNoDoors"] = function int () { import xmain_d2d
: cheatNoDoors
; return (cheatNoDoors ?
1 : 0); };
377 public void registerAPI () {
378 //Actor.actorSize += 256;
379 conwriteln("actor size is ", Actor
.actorSize
, " bytes");
381 version(dacs_use_vm
) FuncPool
.vm
= new DACSVM();
382 //dvm = new DACSVM();
384 // initialize actor animation
385 foreach (ActorDef adef
; actordefs
.byValue
) {
389 conwriteln("API registered");
393 // ////////////////////////////////////////////////////////////////////////// //
394 mixin(Actor
.FieldGetMixin
!("classtype", StrId
)); // fget_classtype
395 mixin(Actor
.FieldGetMixin
!("classname", StrId
)); // fget_classname
396 mixin(Actor
.FieldGetMixin
!("x", int));
397 mixin(Actor
.FieldGetMixin
!("y", int));
398 mixin(Actor
.FieldGetMixin
!("dir", uint));
399 mixin(Actor
.FieldGetMixin
!("height", int));
400 mixin(Actor
.FieldGetMixin
!("radius", int));
401 mixin(Actor
.FieldGetMixin
!("flags", uint));
402 mixin(Actor
.FieldGetMixin
!("zAnimstate", StrId
));
403 mixin(Actor
.FieldGetMixin
!("zAnimidx", int));
405 mixin(Actor
.FieldSetMixin
!("zAnimidx", int));
408 public bool actorsOverlap (ActorId a
, ActorId b
) {
409 if (!a
.valid ||
!b
.valid
) return false;
410 if (a
.id
== b
.id
) return false; // no self-overlap
414 int ar
= a
.fget_radius
;
415 int br
= b
.fget_radius
;
417 if (ax
-ar
> bx
+br || ax
+ar
< bx
-br
) return false;
421 int ah
= a
.fget_height
;
422 int bh
= b
.fget_height
;
424 //return (ay > by-bh && ay-ah < by);
425 return (by
-bh
<= ay
&& by
>= ay
-ah
);
429 // ////////////////////////////////////////////////////////////////////////// //
430 __gshared ActorId
[2] players
;
432 public void loadMapMonsters () {
433 assert(map
!is null);
435 players
[] = ActorId(0);
436 //conwriteln(players[0].valid, "; ", players[0].id);
437 foreach (ref thing
; map
.things
) {
438 if (thing
.dmonly
) continue;
439 if (auto did
= thing
.type
in d2dactordefsById
) {
440 //if (did.classtype == "item") continue;
441 if (did
.classtype
== "playerstart") {
442 if (thing
.type
== 1 || thing
.type
== 2) {
443 int pnum
= thing
.type
-1;
444 if (!players
[pnum
].valid
) {
445 auto aid
= Actor
.alloc
;
446 aid
.classtype
= StrPool
.intern("monster");
447 aid
.classname
= StrPool
.intern("Player");
448 aid
.plrnum
= cast(uint)(pnum
+1);
449 aid
.state
= StrPool
.MNST_SLEEP
;
450 aid
.x
= cast(int)thing
.x
;
451 aid
.y
= cast(int)thing
.y
;
452 aid
.dir
= cast(uint)(thing
.right ?
1 : 0);
453 auto adef
= findD2DActorDef(thing
.type
);
454 if (adef
is null) assert(0);
456 if ((aid
.flags
!uint&AF_NOCOLLISION
) == 0) ugActorModify
!true(aid
);
458 conwriteln("player #", pnum
+1, " aid is ", aid
.id
);
462 import xmain_d2d : setMapViewPos;
463 setMapViewPos(aid.x!int, aid.y!int);
471 auto adef
= findD2DActorDef(thing
.type
);
473 if (auto did
= thing
.type
in d2dactordefsById
) {
474 conwriteln("ignoring D2D thing '", did
.fullname
, "'");
476 conwriteln("ignoring unknown D2D thing with mapid ", thing
.type
);
480 // load graphics (we'll load all graphics)
481 //adef.loadGraphics();
482 // create actor and initialize it
483 auto aid
= Actor
.alloc
;
484 aid
.classtype
= StrPool
.intern(adef
.classtype
);
485 aid
.classname
= StrPool
.intern(adef
.classname
);
486 //conwriteln("found '", aid.classtype.get, ":", aid.classname.get, "'");
487 if (auto did
= thing
.type
in d2dactordefsById
) {
488 assert(did
.classtype
== adef
.classtype
);
489 assert(did
.classname
== adef
.classname
);
490 conwriteln("mapid=", thing
.type
, "; ", adef
.classtype
, ":", adef
.classname
, "; id=", aid
.id
);
491 assert(aid
.classtype
!string
== adef
.classtype
);
492 assert(aid
.classname
!string
== adef
.classname
);
496 aid
.state
= StrPool
.MNST_SLEEP
;
497 aid
.x
= cast(int)thing
.x
;
498 aid
.y
= cast(int)thing
.y
;
499 aid
.dir
= cast(uint)(thing
.right ?
1 : 0);
500 //if (aid.classtype!string == "item" && aid.x!int < 64) { aid.x = 92; aid.y = aid.y!int-16; conwriteln("!!!"); }
502 if ((aid
.flags
!uint&AF_NOCOLLISION
) == 0) ugActorModify
!true(aid
);
505 foreach (ActorDef adef
; actordefs
.byValue
) {
506 conwriteln("loading graphics for '", adef
.fullname
, "'");
509 //Actor.dumpActors();
511 conwriteln("initial snapshot size: ", Actor
.snapshotSize
, " bytes");
515 // ////////////////////////////////////////////////////////////////////////// //
516 __gshared
uint[65536] touchList
; // aids
517 __gshared
uint[] realTouchList
;
518 __gshared
uint realTouchListIndex
= uint.max
;
519 __gshared ActorId curThinkingActor
;
520 __gshared
bool touchListAllowed
= false;
524 void rewindTouchList () {
525 if (!touchListAllowed
) return;
526 realTouchListIndex
= 0;
527 realTouchList
= ugActorHitList(curThinkingActor
, touchList
[]);
532 ActorId
getNextTouchListItem () {
533 if (!touchListAllowed
) return ActorId(0);
534 if (realTouchListIndex
== uint.max
) rewindTouchList();
535 while (realTouchListIndex
< realTouchList
.length
) {
536 auto aid
= ActorId(realTouchList
.ptr
[realTouchListIndex
]);
537 ++realTouchListIndex
;
538 if (aid
.valid
&& (aid
.flags
!uint&AF_NOONTOUCH
) == 0) return aid
;
544 public void doActorsThink () {
546 //static uint fflags = uint.max;
547 //if (fflags == uint.max) fflags = Actor.fields["flags"].ofs;
549 // we have too much memory!
550 __gshared
uint[65536] validActorsList
;
552 touchListAllowed
= true;
553 scope(exit
) touchListAllowed
= false;
554 foreach (uint xid
; Actor
.getValidList(validActorsList
[])) {
555 auto me
= ActorId(xid
);
557 auto flags
= me
.fget_flags
;
558 if ((flags
&AF_NOCOLLISION
) == 0) ugActorModify
!false(me
); // remove from grid
559 if (auto adef
= findActorDef(me
)) {
560 if ((flags
&AF_NOTHINK
) == 0) {
561 realTouchListIndex
= uint.max
;
562 curThinkingActor
= me
;
564 //if (me.x!int < 32) conwriteln("actor: ", me.id, "; attLightRGBX=", me.attLightRGBX!uint);
565 if (!me
.valid
) continue; // we are dead
566 flags
= me
.fget_flags
; // in case script updated flags
568 if ((flags
&AF_NOANIMATE
) == 0) {
569 int aidx
= me
.fget_zAnimidx
;
570 int nidx
= adef
.nextAnimIdx(me
.fget_zAnimstate
, me
.fget_dir
, aidx
);
571 //conwriteln("actor ", me.id, " (", me.classtype!string, me.classname!string, "): state=", me.zAnimstate!string, "; aidx=", aidx, "; nidx=", nidx);
572 me
.fset_zAnimidx
= nidx
;
573 //assert(me.fget_zAnimidx == nidx);
576 if ((flags
&AF_NOCOLLISION
) == 0) ugActorModify
!true(me
);
581 //{ import std.stdio : stdout; stdout.writeln("========================================="); }
582 //Actor.dumpActors();