slightly better message system
[dd2d.git] / dengapi.d
bloba66c2865ae94363fc65db3d5af406b49c9f580af
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);
93 // ////////////////////////////////////////////////////////////////////////// //
94 private string modLoader (string modname) {
95 static string getTextFile (string fname) {
96 try {
97 auto res = loadTextFile(fname);
98 conwriteln("loading DACS module file '", fname, "' (", res.length, " bytes)");
99 if (res is null) res = "";
100 return res;
101 } catch (Exception) {}
102 return null;
105 string res;
107 //res = getTextFile(modname~".dacs");
108 //if (res !is null) return res;
110 res = getTextFile("scripts/"~modname~".dacs");
111 if (res !is null) return res;
112 assert(res is null);
114 string[] parts = ["scripts"];
115 string mn = modname;
116 while (mn.length > 0) {
117 int pos = 0;
118 if (mn[0] >= 'A' && mn[0] <= 'Z') ++pos;
119 while (pos < mn.length && (mn[pos] < 'A' || mn[pos] > 'Z')) ++pos;
120 if (mn[0] >= 'A' && mn[0] <= 'Z') {
121 parts ~= cast(char)(mn[0]+32)~mn[1..pos];
122 } else {
123 parts ~= mn[0..pos];
125 mn = mn[pos..$];
128 import std.array : join;
129 string path = parts.join("/")~".dacs";
130 res = getTextFile(path);
131 if (res !is null) return res;
132 assert(res is null);
134 assert(res is null);
135 throw new Exception("module '"~modname~"' not found");
139 __gshared int modIteration = 0;
141 public void loadWadScripts () {
142 if (moduleLoader is null) moduleLoader = (string modname) => modLoader(modname);
143 string[] mainmods;
144 try {
145 import std.array : split;
146 auto t = loadTextFile("scripts/dacsmain.txt");
147 foreach (string line; t.split('\n')) {
148 while (line.length && line[0] <= ' ') line = line[1..$];
149 if (line.length == 0 || line[0] == ';') continue;
150 while (line.length && line[$-1] <= ' ') line = line[0..$-1];
151 mainmods ~= line;
153 } catch (Exception e) { return; }
154 try {
155 //conwriteln("loading main DACS module '", mainmod, "'");
156 auto iter = modIteration++;
157 conwriteln("iteration=", iter);
158 foreach (string mod; mainmods) {
159 parseModule(mod, iter);
161 conwriteln("calling parseComplete; iteration=", iter);
162 parseComplete();
163 updateActorClasses(iter);
164 } catch (CompilerException e) {
165 _d_print_throwable(e);
166 conwriteln("PARSE ERROR: ", e.file, " at ", e.line);
167 conwriteln(e.toString);
168 assert(0);
173 public void scriptLoadingComplete () {
174 setupDAPI();
175 dacsFinalizeCompiler();
179 // ////////////////////////////////////////////////////////////////////////// //
180 public __gshared LevelMap map;
181 //public __gshared DACSVM dvm;
182 public __gshared uint prngSyncSeed = 0x29a;
183 public __gshared uint prngSeed = 0x29a; // unsynced
186 // ////////////////////////////////////////////////////////////////////////// //
187 // Park-Miller-Carta Pseudo-Random Number Generator, based on David G. Carta paper
188 // 31 bit of randomness
189 // seed is previous result, as usual
190 public uint prngR31next (uint seed) {
191 if (seed == 0) seed = 0x29a;
192 uint lo = 16807*(seed&0xffff);
193 uint hi = 16807*(seed>>16);
194 lo += (hi&0x7fff)<<16;
195 lo += hi>>15;
196 //if (lo > 0x7fffffff) lo -= 0x7fffffff; // should be >=, actually
197 lo = (lo&0x7FFFFFFF)+(lo>>31); // same as previous code, but branch-less
198 return lo;
202 // "synced" random, using common seed for all players
203 public uint syncrandu31 () {
204 pragma(inline, true);
205 return (prngSyncSeed = prngR31next(prngSyncSeed));
209 // ////////////////////////////////////////////////////////////////////////// //
210 version(dacs_use_vm) {
211 // this is VAT function, argument order is reversed
212 void doWrite(bool donl) (FuncPool.FuncInfo fi, DACSVM vm) {
213 import std.stdio;
214 auto count = vm.popInt();
215 while (count-- > 0) {
216 auto type = vm.popInt();
217 switch (cast(VATArgType)type) {
218 case VATArgType.Int: write(vm.popInt()); break;
219 case VATArgType.Uint: write(vm.popUint()); break;
220 case VATArgType.Float: write(vm.popFloat()); break;
221 case VATArgType.StrId: write(StrId(vm.popUint()).get); break;
222 case VATArgType.ActorId: write("<actor>"); vm.popUint(); break; //TODO
223 default: write("<invalid-type>"); vm.popUint(); break; //TODO
226 static if (donl) writeln();
227 // push dummy return value
228 vm.pushInt(0);
230 } else {
231 extern(C) void doWrite(bool donl) (uint argc, ...) {
232 import core.vararg;
233 import std.stdio;
234 mainloop: while (argc >= 2) {
235 argc -= 2;
236 int tp = va_arg!int(_argptr);
237 switch (tp) {
238 case VATArgType.Int:
239 auto v = va_arg!int(_argptr);
240 write(v);
241 break;
242 case VATArgType.Uint:
243 auto v = va_arg!uint(_argptr);
244 write(v);
245 break;
246 case VATArgType.Float:
247 auto v = va_arg!float(_argptr);
248 write(v);
249 break;
250 case VATArgType.StrId:
251 auto v = StrId(va_arg!uint(_argptr)).get;
252 write(v);
253 break;
254 case VATArgType.ActorId:
255 auto v = ActorId(va_arg!uint(_argptr));
256 //write("<actor:", v.valid, ":", v.id, ">");
257 write("<actor>");
258 break;
259 default: write("<invalid-type>"); break mainloop;
262 static if (donl) writeln();
267 // ////////////////////////////////////////////////////////////////////////// //
268 void animClearFrames (StrId classtype, StrId classname, StrId state) {
269 auto adef = findActorDef(classtype.get, classname.get);
270 if (adef is null) throw new Exception("animClearFrames: actor '"~classtype.get~":"~classname.get~"' not found!");
271 adef.clearFrames(state);
275 void animAddFrame (StrId classtype, StrId classname, StrId state, uint dir, StrId sprname) {
276 auto adef = findActorDef(classtype.get, classname.get);
277 if (adef is null) throw new Exception("animAddFrame: actor '"~classtype.get~":"~classname.get~"' not found!");
278 if (dir != 0) dir = 1; //TODO: process mirror flag here
279 adef.addFrame(state, dir, sprname);
283 // ////////////////////////////////////////////////////////////////////////// //
284 void setupDAPI () {
285 conwriteln("setting up D API");
287 FuncPool["write"] = &doWrite!false;
288 FuncPool["writeln"] = &doWrite!true;
290 FuncPool["syncrandu31"] = &syncrandu31;
292 FuncPool["animClearFrames"] = &animClearFrames;
293 FuncPool["animAddFrame"] = &animAddFrame;
295 FuncPool["actorSetAnimation"] = function void (ActorId me, StrId state) {
296 if (!me.valid) return;
297 if (auto adef = findActorDef(me.classtype!string, me.classname!string)) {
298 me.zAnimstate = state;
299 me.zAnimidx = 0;
303 FuncPool["getPlayer"] = function ActorId () { assert(0); };
305 FuncPool["getPlayerButtons"] = function uint (uint pidx) { return (pidx == 0 ? plrKeysLast : 0); };
307 FuncPool["mapGetTypeTile"] = function int (int x, int y) {
308 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);
311 FuncPool["mapGetTile"] = function int (uint layer, int x, int y) {
312 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);
316 public void registerAPI () {
317 conwriteln("actor size is ", Actor.actorSize, " bytes");
319 version(dacs_use_vm) FuncPool.vm = new DACSVM();
320 //dvm = new DACSVM();
322 // initialize actor animation
323 foreach (ActorDef adef; actordefs.byValue) {
324 adef.callAnimInit();
327 conwriteln("API registered");
331 // ////////////////////////////////////////////////////////////////////////// //
332 __gshared ActorId[2] players;
334 public void loadMapMonsters () {
335 assert(map !is null);
336 players[] = ActorId(0);
337 //conwriteln(players[0].valid, "; ", players[0].id);
338 foreach (ref thing; map.things) {
339 if (thing.dmonly) continue;
340 if (auto did = thing.type in d2dactordefsById) {
341 if (did.classtype == "item") continue;
342 if (did.classtype == "playerstart") {
343 if (thing.type == 1 || thing.type == 2) {
344 int pnum = thing.type-1;
345 if (!players[pnum].valid) {
346 auto aid = Actor.alloc;
347 aid.classtype = StrPool.intern("monster");
348 aid.classname = StrPool.intern("Player");
349 aid.plrnum = cast(uint)pnum;
350 aid.state = StrPool.MNST_SLEEP;
351 aid.x = cast(int)thing.x;
352 aid.y = cast(int)thing.y;
353 aid.dir = cast(uint)(thing.right ? 1 : 0);
354 auto adef = findD2DActorDef(thing.type);
355 if (adef is null) assert(0);
356 adef.callInit(aid);
357 players[pnum] = aid;
358 conwriteln("player #", pnum+1, " aid is ", aid.id);
361 continue;
364 auto adef = findD2DActorDef(thing.type);
365 if (adef is null) {
366 if (auto did = thing.type in d2dactordefsById) {
367 conwriteln("ignoring D2D thing '", did.fullname, "'");
368 } else {
369 conwriteln("ignoring unknown D2D thing with mapid ", thing.type);
371 continue;
373 // load graphics (we'll load all graphics)
374 //adef.loadGraphics();
375 // create actor and initialize it
376 auto aid = Actor.alloc;
377 aid.classtype = StrPool.intern(adef.classtype);
378 aid.classname = StrPool.intern(adef.classname);
379 //conwriteln("found '", aid.classtype.get, ":", aid.classname.get, "'");
380 if (auto did = thing.type in d2dactordefsById) {
381 assert(did.classtype == adef.classtype);
382 assert(did.classname == adef.classname);
383 conwriteln("mapid=", thing.type, "; ", adef.classtype, ":", adef.classname, "; id=", aid.id);
384 assert(aid.classtype!string == adef.classtype);
385 assert(aid.classname!string == adef.classname);
386 } else {
387 assert(0);
389 aid.state = StrPool.MNST_SLEEP;
390 aid.x = cast(int)thing.x;
391 aid.y = cast(int)thing.y;
392 aid.dir = cast(uint)(thing.right ? 1 : 0);
393 adef.callInit(aid);
396 foreach (ActorDef adef; actordefs.byValue) {
397 conwriteln("loading graphics for '", adef.fullname, "'");
398 adef.loadGraphics();
400 //Actor.dumpActors();
402 conwriteln("initial snapshot size: ", Actor.snapshotSize, " bytes");
406 // ////////////////////////////////////////////////////////////////////////// //
407 public void doActorsThink () {
408 Actor.forEach((ActorId me) {
409 // `act` is always valid here
410 if (auto adef = findActorDef(me.classtype!string, me.classname!string)) {
411 adef.callThink(me);
412 int aidx = me.zAnimidx!int;
413 int nidx = adef.nextAnimIdx(me.zAnimstate!StrId, me.dir!uint, aidx);
414 //conwriteln("actor ", me.id, " (", me.classtype!string, me.classname!string, "): state=", me.zAnimstate!string, "; aidx=", aidx, "; nidx=", nidx);
415 me.zAnimidx = nidx;
416 assert(me.zAnimidx!int == nidx);
419 plrKeysFix(0);
420 //{ import std.stdio : stdout; stdout.writeln("========================================="); }
421 //Actor.dumpActors();