cosmetix
[dd2d.git] / dengapi.d
blob9546c05c33ac607369e3bf7265d048d6ffbd1969
1 module dengapi is aliced;
3 private:
4 import core.atomic;
5 import core.thread;
6 import core.time;
8 import iv.glbinds;
9 import glutils;
10 import console;
11 import wadarc;
13 import d2dmap;
14 import d2dadefs;
15 import d2dgfx;
16 import dacs;
19 // ////////////////////////////////////////////////////////////////////////// //
20 private extern (C) void _d_print_throwable (Throwable t);
23 // ////////////////////////////////////////////////////////////////////////// //
24 import arsd.color;
25 import arsd.png;
28 // ////////////////////////////////////////////////////////////////////////// //
29 public {
30 enum PLK_UP = (1<<0);
31 enum PLK_DOWN = (1<<1);
32 enum PLK_LEFT = (1<<2);
33 enum PLK_RIGHT = (1<<3);
34 enum PLK_FIRE = (1<<4);
35 enum PLK_JUMP = (1<<5);
36 enum PLK_USE = (1<<6);
39 __gshared uint plrKeysLast;
40 __gshared uint plrKeyState;
43 public void plrKeyDown (uint plidx, uint mask) {
44 if (mask == 0 || plidx > 0) return;
45 plrKeysLast |= mask;
46 plrKeyState |= mask;
50 public void plrKeyUp (uint plidx, uint mask) {
51 if (mask == 0 || plidx > 0) return;
52 plrKeyState &= ~mask;
56 public void plrKeyUpDown (uint plidx, uint mask, bool down) {
57 if (down) plrKeyDown(plidx, mask); else plrKeyUp(plidx, mask);
61 void plrKeysFix (uint plidx) {
62 if (plidx > 0) return;
63 //conwritefln!"plrKeyState=0x%02x; plrKeysLast=0x%02x"(plrKeyState, plrKeysLast);
64 foreach (uint n; 0..12) {
65 if ((plrKeyState&(1<<n)) == 0) plrKeysLast &= ~(1<<n);
70 // ////////////////////////////////////////////////////////////////////////// //
71 void updateActorClasses (int iteration) {
72 foreach (string ctype; dacsClassTypes(iteration)) {
73 foreach (auto mod; dacsClassModules(ctype, iteration)) {
74 auto adef = registerActorDef(mod.rmoduletype.classtype, mod.rmoduletype.classname);
75 conwriteln("found info for actor '", adef.fullname, "'");
77 auto animInitFn = FuncPool.findByFQMG(mod.name~".initializeAnim", ":void");
78 if (animInitFn !is null) adef.setAnimInitFunc(animInitFn);
81 auto initFn = FuncPool.findByFQMG(mod.name~".initialize", ":void:Actor");
82 if (initFn !is null) adef.setInitFunc(initFn);
85 auto thinkFn = FuncPool.findByFQMG(mod.name~".think", ":void:Actor");
86 if (thinkFn !is null) adef.setThinkFunc(thinkFn);
89 auto onTouchFn = FuncPool.findByFQMG(mod.name~".onTouch", ":void:Actor:Actor");
90 if (onTouchFn !is null) adef.setOnTouchFunc(onTouchFn);
97 // ////////////////////////////////////////////////////////////////////////// //
98 private string modLoader (string modname) {
99 static string getTextFile (string fname) {
100 try {
101 auto res = loadTextFile(fname);
102 conwriteln("loading DACS module file '", fname, "' (", res.length, " bytes)");
103 if (res is null) res = "";
104 return res;
105 } catch (Exception) {}
106 return null;
109 string res;
111 //res = getTextFile(modname~".dacs");
112 //if (res !is null) return res;
114 res = getTextFile("scripts/"~modname~".dacs");
115 if (res !is null) return res;
116 assert(res is null);
118 string[] parts = ["scripts"];
119 string mn = modname;
120 while (mn.length > 0) {
121 int pos = 0;
122 if (mn[0] >= 'A' && mn[0] <= 'Z') ++pos;
123 while (pos < mn.length && (mn[pos] < 'A' || mn[pos] > 'Z')) ++pos;
124 if (mn[0] >= 'A' && mn[0] <= 'Z') {
125 parts ~= cast(char)(mn[0]+32)~mn[1..pos];
126 } else {
127 parts ~= mn[0..pos];
129 mn = mn[pos..$];
132 import std.array : join;
133 string path = parts.join("/")~".dacs";
134 res = getTextFile(path);
135 if (res !is null) return res;
136 assert(res is null);
138 assert(res is null);
139 throw new Exception("module '"~modname~"' not found");
143 __gshared int modIteration = 0;
145 public void loadWadScripts () {
146 if (moduleLoader is null) moduleLoader = (string modname) => modLoader(modname);
147 string[] mainmods;
148 try {
149 import std.array : split;
150 auto t = loadTextFile("scripts/dacsmain.txt");
151 foreach (string line; t.split('\n')) {
152 while (line.length && line[0] <= ' ') line = line[1..$];
153 if (line.length == 0 || line[0] == ';') continue;
154 while (line.length && line[$-1] <= ' ') line = line[0..$-1];
155 mainmods ~= line;
157 } catch (Exception e) { return; }
158 try {
159 //conwriteln("loading main DACS module '", mainmod, "'");
160 auto iter = modIteration++;
161 conwriteln("iteration=", iter);
162 foreach (string mod; mainmods) {
163 parseModule(mod, iter);
165 conwriteln("calling parseComplete; iteration=", iter);
166 parseComplete();
167 updateActorClasses(iter);
168 } catch (CompilerException e) {
169 _d_print_throwable(e);
170 conwriteln("PARSE ERROR: ", e.file, " at ", e.line);
171 conwriteln(e.toString);
172 assert(0);
177 public void scriptLoadingComplete () {
178 setupDAPI();
179 dacsFinalizeCompiler();
183 // ////////////////////////////////////////////////////////////////////////// //
184 public __gshared LevelMap map;
185 //public __gshared DACSVM dvm;
186 public __gshared uint prngSyncSeed = 0x29a;
187 public __gshared uint prngSeed = 0x29a; // unsynced
190 // ////////////////////////////////////////////////////////////////////////// //
191 // Park-Miller-Carta Pseudo-Random Number Generator, based on David G. Carta paper
192 // 31 bit of randomness
193 // seed is previous result, as usual
194 public uint prngR31next (uint seed) {
195 if (seed == 0) seed = 0x29a;
196 uint lo = 16807*(seed&0xffff);
197 uint hi = 16807*(seed>>16);
198 lo += (hi&0x7fff)<<16;
199 lo += hi>>15;
200 //if (lo > 0x7fffffff) lo -= 0x7fffffff; // should be >=, actually
201 lo = (lo&0x7FFFFFFF)+(lo>>31); // same as previous code, but branch-less
202 return lo;
206 // "synced" random, using common seed for all players
207 public uint syncrandu31 () {
208 pragma(inline, true);
209 return (prngSyncSeed = prngR31next(prngSyncSeed));
213 // ////////////////////////////////////////////////////////////////////////// //
214 version(dacs_use_vm) {
215 // this is VAT function, argument order is reversed
216 void doWrite(bool donl) (FuncPool.FuncInfo fi, DACSVM vm) {
217 import std.stdio;
218 auto count = vm.popInt();
219 while (count-- > 0) {
220 auto type = vm.popInt();
221 switch (cast(VATArgType)type) {
222 case VATArgType.Int: write(vm.popInt()); break;
223 case VATArgType.Uint: write(vm.popUint()); break;
224 case VATArgType.Float: write(vm.popFloat()); break;
225 case VATArgType.StrId: write(StrId(vm.popUint()).get); break;
226 case VATArgType.ActorId: write("<actor>"); vm.popUint(); break; //TODO
227 default: write("<invalid-type>"); vm.popUint(); break; //TODO
230 static if (donl) writeln();
231 // push dummy return value
232 vm.pushInt(0);
234 } else {
235 extern(C) void doWrite(bool donl) (uint argc, ...) {
236 import core.vararg;
237 import std.stdio;
238 mainloop: while (argc >= 2) {
239 argc -= 2;
240 int tp = va_arg!int(_argptr);
241 switch (tp) {
242 case VATArgType.Int:
243 auto v = va_arg!int(_argptr);
244 write(v);
245 break;
246 case VATArgType.Uint:
247 auto v = va_arg!uint(_argptr);
248 write(v);
249 break;
250 case VATArgType.Float:
251 auto v = va_arg!float(_argptr);
252 write(v);
253 break;
254 case VATArgType.StrId:
255 auto v = StrId(va_arg!uint(_argptr)).get;
256 write(v);
257 break;
258 case VATArgType.ActorId:
259 auto v = ActorId(va_arg!uint(_argptr));
260 //write("<actor:", v.valid, ":", v.id, ">");
261 write("<actor>");
262 break;
263 default: write("<invalid-type>"); break mainloop;
266 static if (donl) writeln();
271 // ////////////////////////////////////////////////////////////////////////// //
272 void animClearFrames (StrId classtype, StrId classname, StrId state) {
273 auto adef = findActorDef(classtype.get, classname.get);
274 if (adef is null) throw new Exception("animClearFrames: actor '"~classtype.get~":"~classname.get~"' not found!");
275 adef.clearFrames(state);
279 void animAddFrame (StrId classtype, StrId classname, StrId state, uint dir, StrId sprname) {
280 auto adef = findActorDef(classtype.get, classname.get);
281 if (adef is null) throw new Exception("animAddFrame: actor '"~classtype.get~":"~classname.get~"' not found!");
282 if (dir != 0) dir = 1; //TODO: process mirror flag here
283 adef.addFrame(state, dir, sprname);
287 // ////////////////////////////////////////////////////////////////////////// //
288 void setupDAPI () {
289 conwriteln("setting up D API");
291 FuncPool["write"] = &doWrite!false;
292 FuncPool["writeln"] = &doWrite!true;
294 FuncPool["syncrandu31"] = &syncrandu31;
296 FuncPool["animClearFrames"] = &animClearFrames;
297 FuncPool["animAddFrame"] = &animAddFrame;
299 FuncPool["actorSetAnimation"] = function void (ActorId me, StrId state) {
300 if (!me.valid) return;
301 if (auto adef = findActorDef(me.classtype!string, me.classname!string)) {
302 me.zAnimstate = state;
303 me.zAnimidx = 0;
307 FuncPool["getPlayer"] = function ActorId () { assert(0); };
309 FuncPool["getPlayerButtons"] = function uint (uint pidx) { return (pidx == 0 ? plrKeysLast : 0); };
311 FuncPool["mapGetTypeTile"] = function int (int x, int y) {
312 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);
315 FuncPool["mapGetTile"] = function int (uint layer, int x, int y) {
316 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);
319 FuncPool["getMapViewHeight"] = function int () {
320 import xmain_d2d : vlWidth, vlHeight, scale;
321 return vlHeight/scale;
324 FuncPool["setMapViewPos"] = function void (int x, int y) {
325 import xmain_d2d : setMapViewPos;
326 setMapViewPos(x, y);
330 public void registerAPI () {
331 conwriteln("actor size is ", Actor.actorSize, " bytes");
333 version(dacs_use_vm) FuncPool.vm = new DACSVM();
334 //dvm = new DACSVM();
336 // initialize actor animation
337 foreach (ActorDef adef; actordefs.byValue) {
338 adef.callAnimInit();
341 conwriteln("API registered");
345 // ////////////////////////////////////////////////////////////////////////// //
346 __gshared ActorId[2] players;
348 public void loadMapMonsters () {
349 assert(map !is null);
350 players[] = ActorId(0);
351 //conwriteln(players[0].valid, "; ", players[0].id);
352 foreach (ref thing; map.things) {
353 if (thing.dmonly) continue;
354 if (auto did = thing.type in d2dactordefsById) {
355 //if (did.classtype == "item") continue;
356 if (did.classtype == "playerstart") {
357 if (thing.type == 1 || thing.type == 2) {
358 int pnum = thing.type-1;
359 if (!players[pnum].valid) {
360 auto aid = Actor.alloc;
361 aid.classtype = StrPool.intern("monster");
362 aid.classname = StrPool.intern("Player");
363 aid.plrnum = cast(uint)pnum;
364 aid.state = StrPool.MNST_SLEEP;
365 aid.x = cast(int)thing.x;
366 aid.y = cast(int)thing.y;
367 aid.dir = cast(uint)(thing.right ? 1 : 0);
368 auto adef = findD2DActorDef(thing.type);
369 if (adef is null) assert(0);
370 adef.callInit(aid);
371 players[pnum] = aid;
372 conwriteln("player #", pnum+1, " aid is ", aid.id);
373 //FIXME: HACK!
374 if (pnum == 0) {
375 import xmain_d2d : setMapViewPos;
376 setMapViewPos(aid.x!int, aid.y!int);
380 continue;
383 auto adef = findD2DActorDef(thing.type);
384 if (adef is null) {
385 if (auto did = thing.type in d2dactordefsById) {
386 conwriteln("ignoring D2D thing '", did.fullname, "'");
387 } else {
388 conwriteln("ignoring unknown D2D thing with mapid ", thing.type);
390 continue;
392 // load graphics (we'll load all graphics)
393 //adef.loadGraphics();
394 // create actor and initialize it
395 auto aid = Actor.alloc;
396 aid.classtype = StrPool.intern(adef.classtype);
397 aid.classname = StrPool.intern(adef.classname);
398 //conwriteln("found '", aid.classtype.get, ":", aid.classname.get, "'");
399 if (auto did = thing.type in d2dactordefsById) {
400 assert(did.classtype == adef.classtype);
401 assert(did.classname == adef.classname);
402 conwriteln("mapid=", thing.type, "; ", adef.classtype, ":", adef.classname, "; id=", aid.id);
403 assert(aid.classtype!string == adef.classtype);
404 assert(aid.classname!string == adef.classname);
405 } else {
406 assert(0);
408 aid.state = StrPool.MNST_SLEEP;
409 aid.x = cast(int)thing.x;
410 aid.y = cast(int)thing.y;
411 aid.dir = cast(uint)(thing.right ? 1 : 0);
412 adef.callInit(aid);
415 foreach (ActorDef adef; actordefs.byValue) {
416 conwriteln("loading graphics for '", adef.fullname, "'");
417 adef.loadGraphics();
419 //Actor.dumpActors();
421 conwriteln("initial snapshot size: ", Actor.snapshotSize, " bytes");
425 // ////////////////////////////////////////////////////////////////////////// //
426 public void doActorsThink () {
427 Actor.forEach((ActorId me) {
428 // `act` is always valid here
429 if (auto adef = findActorDef(me.classtype!string, me.classname!string)) {
430 adef.callThink(me);
431 int aidx = me.zAnimidx!int;
432 int nidx = adef.nextAnimIdx(me.zAnimstate!StrId, me.dir!uint, aidx);
433 //conwriteln("actor ", me.id, " (", me.classtype!string, me.classname!string, "): state=", me.zAnimstate!string, "; aidx=", aidx, "; nidx=", nidx);
434 me.zAnimidx = nidx;
435 assert(me.zAnimidx!int == nidx);
438 plrKeysFix(0);
439 //{ import std.stdio : stdout; stdout.writeln("========================================="); }
440 //Actor.dumpActors();