doomer can walk, jump and swim now, via scripting! ;-)
[dd2d.git] / dengapi.d
blob7808544963a7d41fab1517fe9a024e1fe770785c
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) {
79 if (animInitFn.code.length == 0) throw new CompilerException("invalid 'initializeAnim' in module '"~mod.name~"' for '"~mod.rmoduletype.classtype~":"~mod.rmoduletype.classname~"'");
81 adef.setAnimInitFunc(animInitFn);
84 auto initFn = FuncPool.findByFQMG(mod.name~".initialize", ":void:Actor");
85 if (initFn !is null) {
86 if (initFn.code.length == 0) throw new CompilerException("invalid 'initialize' in module '"~mod.name~"' for '"~mod.rmoduletype.classtype~":"~mod.rmoduletype.classname~"'");
88 adef.setInitFunc(initFn);
91 auto thinkFn = FuncPool.findByFQMG(mod.name~".think", ":void:Actor");
92 if (thinkFn !is null) {
93 if (thinkFn.code.length == 0) throw new CompilerException("invalid 'think' in module '"~mod.name~"' for '"~mod.rmoduletype.classtype~":"~mod.rmoduletype.classname~"'");
95 adef.setThinkFunc(thinkFn);
102 // ////////////////////////////////////////////////////////////////////////// //
103 private string modLoader (string modname) {
104 static string getTextFile (string fname) {
105 try {
106 auto res = loadTextFile(fname);
107 conwriteln("loading DACS module file '", fname, "' (", res.length, " bytes)");
108 if (res is null) res = "";
109 return res;
110 } catch (Exception) {}
111 return null;
114 string res;
116 //res = getTextFile(modname~".dacs");
117 //if (res !is null) return res;
119 res = getTextFile("scripts/"~modname~".dacs");
120 if (res !is null) return res;
121 assert(res is null);
123 string[] parts = ["scripts"];
124 string mn = modname;
125 while (mn.length > 0) {
126 int pos = 0;
127 if (mn[0] >= 'A' && mn[0] <= 'Z') ++pos;
128 while (pos < mn.length && (mn[pos] < 'A' || mn[pos] > 'Z')) ++pos;
129 if (mn[0] >= 'A' && mn[0] <= 'Z') {
130 parts ~= cast(char)(mn[0]+32)~mn[1..pos];
131 } else {
132 parts ~= mn[0..pos];
134 mn = mn[pos..$];
137 import std.array : join;
138 string path = parts.join("/")~".dacs";
139 res = getTextFile(path);
140 if (res !is null) return res;
141 assert(res is null);
143 assert(res is null);
144 throw new Exception("module '"~modname~"' not found");
148 __gshared int modIteration = 0;
150 public void loadWadScripts () {
151 if (moduleLoader is null) moduleLoader = (string modname) => modLoader(modname);
152 string[] mainmods;
153 try {
154 import std.array : split;
155 auto t = loadTextFile("scripts/dacsmain.txt");
156 foreach (string line; t.split('\n')) {
157 while (line.length && line[0] <= ' ') line = line[1..$];
158 if (line.length == 0 || line[0] == ';') continue;
159 while (line.length && line[$-1] <= ' ') line = line[0..$-1];
160 mainmods ~= line;
162 } catch (Exception e) { return; }
163 try {
164 //conwriteln("loading main DACS module '", mainmod, "'");
165 auto iter = modIteration++;
166 foreach (string mod; mainmods) {
167 parseModule(mod, iter);
168 parseComplete();
169 updateActorClasses(iter);
171 } catch (CompilerException e) {
172 _d_print_throwable(e);
173 conwriteln("PARSE ERROR: ", e.file, " at ", e.line);
174 conwriteln(e.toString);
175 assert(0);
180 // ////////////////////////////////////////////////////////////////////////// //
181 public __gshared LevelMap map;
182 public __gshared DACSVM dvm;
183 public __gshared uint prngSyncSeed = 0x29a;
184 public __gshared uint prngSeed = 0x29a; // unsynced
187 // ////////////////////////////////////////////////////////////////////////// //
188 // Park-Miller-Carta Pseudo-Random Number Generator, based on David G. Carta paper
189 // 31 bit of randomness
190 // seed is previous result, as usual
191 public uint prngR31next (uint seed) {
192 if (seed == 0) seed = 0x29a;
193 uint lo = 16807*(seed&0xffff);
194 uint hi = 16807*(seed>>16);
195 lo += (hi&0x7fff)<<16;
196 lo += hi>>15;
197 //if (lo > 0x7fffffff) lo -= 0x7fffffff; // should be >=, actually
198 lo = (lo&0x7FFFFFFF)+(lo>>31); // same as previous code, but branch-less
199 return lo;
203 // "synced" random, using common seed for all players
204 public uint syncrandu31 () {
205 pragma(inline, true);
206 return (prngSyncSeed = prngR31next(prngSyncSeed));
210 // ////////////////////////////////////////////////////////////////////////// //
211 // this is VAT function, argument order is reversed
212 void doWrite (DACSVM vm, bool donl) {
213 auto count = vm.popInt();
214 while (count-- > 0) {
215 auto type = vm.popInt();
216 switch (cast(VATArgType)type) {
217 case VATArgType.Int: conwrite(vm.popInt()); break;
218 case VATArgType.Uint: conwrite(vm.popUint()); break;
219 case VATArgType.Float: conwrite(vm.popFloat()); break;
220 case VATArgType.StrId: conwrite(StrId(vm.popUint()).get); break;
221 case VATArgType.ActorId: conwrite("<actor>"); vm.popUint(); break; //TODO
222 default: conwrite("<invalid-type>"); vm.popUint(); break; //TODO
225 if (donl) conwriteln();
226 // push dummy return value
227 vm.pushInt(0);
231 // ////////////////////////////////////////////////////////////////////////// //
232 void animClearFrames (StrId classtype, StrId classname, StrId state) {
233 auto adef = findActorDef(classtype.get, classname.get);
234 if (adef is null) throw new Exception("animClearFrames: actor '"~classtype.get~":"~classname.get~"' not found!");
235 adef.clearFrames(state);
239 void animAddFrame (StrId classtype, StrId classname, StrId state, uint dir, StrId sprname) {
240 auto adef = findActorDef(classtype.get, classname.get);
241 if (adef is null) throw new Exception("animAddFrame: actor '"~classtype.get~":"~classname.get~"' not found!");
242 if (dir != 0) dir = 1; //TODO: process mirror flag here
243 adef.addFrame(state, dir, sprname);
247 // ////////////////////////////////////////////////////////////////////////// //
248 public void registerAPI () {
249 dvm = new DACSVM();
251 FuncPool["write"] = delegate void (FuncPool.FuncInfo fi, DACSVM vm) { doWrite(vm, false); };
252 FuncPool["writeln"] = delegate void (FuncPool.FuncInfo fi, DACSVM vm) { doWrite(vm, true); };
254 FuncPool["syncrandu31"] = &syncrandu31;
256 FuncPool["animClearFrames"] = &animClearFrames;
257 FuncPool["animAddFrame"] = &animAddFrame;
259 FuncPool["actorSetAnimation"] = function void (ActorId me, StrId state) {
260 if (!me.valid) return;
261 if (auto adef = findActorDef(me.classtype!string, me.classname!string)) {
262 me.zAnimstate = state;
263 me.zAnimidx = 0;
267 FuncPool["getPlayer"] = function ActorId () { assert(0); };
269 FuncPool["getPlayerButtons"] = function uint (uint pidx) { return (pidx == 0 ? plrKeysLast : 0); };
271 FuncPool["mapGetTypeTile"] = function int (int x, int y) {
272 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);
275 FuncPool["mapGetTile"] = function int (uint layer, int x, int y) {
276 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);
279 // initialize actor animation
280 foreach (ActorDef adef; actordefs.byValue) {
281 adef.callAnimInit(dvm);
286 // ////////////////////////////////////////////////////////////////////////// //
287 __gshared ActorId[2] players;
289 public void loadMapMonsters () {
290 assert(map !is null);
291 players[] = ActorId(0);
292 conwriteln(players[0].valid, "; ", players[0].id);
293 foreach (ref thing; map.things) {
294 if (thing.dmonly) continue;
295 if (auto did = thing.type in d2dactordefsById) {
296 if (did.classtype == "item") continue;
297 if (did.classtype == "playerstart") {
298 if (thing.type == 1 || thing.type == 2) {
299 int pnum = thing.type-1;
300 if (!players[pnum].valid) {
301 auto aid = Actor.alloc;
302 aid.classtype = StrPool.intern("monster");
303 aid.classname = StrPool.intern("Player");
304 aid.plrnum = cast(uint)pnum;
305 aid.state = StrPool.MNST_SLEEP;
306 aid.x = cast(int)thing.x;
307 aid.y = cast(int)thing.y;
308 aid.dir = cast(uint)(thing.right ? 1 : 0);
309 auto adef = findD2DActorDef(thing.type);
310 if (adef is null) assert(0);
311 adef.callInit(dvm, aid);
312 players[pnum] = aid;
313 conwriteln("player #", pnum+1, " aid is ", aid.id);
316 continue;
319 auto adef = findD2DActorDef(thing.type);
320 if (adef is null) {
321 if (auto did = thing.type in d2dactordefsById) {
322 conwriteln("ignoring D2D thing '", did.fullname, "'");
323 } else {
324 conwriteln("ignoring unknown D2D thing with mapid ", thing.type);
326 continue;
328 // load graphics (we'll load all graphics)
329 //adef.loadGraphics();
330 // create actor and initialize it
331 auto aid = Actor.alloc;
332 aid.classtype = StrPool.intern(adef.classtype);
333 aid.classname = StrPool.intern(adef.classname);
334 if (auto did = thing.type in d2dactordefsById) {
335 assert(did.classtype == adef.classtype);
336 assert(did.classname == adef.classname);
337 conwriteln("mapid=", thing.type, "; ", adef.classtype, ":", adef.classname, "; id=", aid.id);
338 assert(aid.classtype!string == adef.classtype);
339 assert(aid.classname!string == adef.classname);
340 } else {
341 assert(0);
343 aid.state = StrPool.MNST_SLEEP;
344 aid.x = cast(int)thing.x;
345 aid.y = cast(int)thing.y;
346 aid.dir = cast(uint)(thing.right ? 1 : 0);
347 adef.callInit(dvm, aid);
350 foreach (ActorDef adef; actordefs.byValue) {
351 conwriteln("loading graphics for '", adef.fullname, "'");
352 adef.loadGraphics();
354 //Actor.dumpActors();
358 // ////////////////////////////////////////////////////////////////////////// //
359 public void doActorsThink () {
360 Actor.forEach((ActorId me) {
361 // `act` is always valid here
362 if (auto adef = findActorDef(me.classtype!string, me.classname!string)) {
363 adef.callThink(dvm, me);
364 int aidx = me.zAnimidx!int;
365 int nidx = adef.nextAnimIdx(me.zAnimstate!StrId, me.dir!uint, aidx);
366 //conwriteln("actor ", me.id, " (", me.classtype!string, me.classname!string, "): state=", me.zAnimstate!string, "; aidx=", aidx, "; nidx=", nidx);
367 me.zAnimidx = nidx;
368 assert(me.zAnimidx!int == nidx);
371 plrKeysFix(0);
372 //{ import std.stdio : stdout; stdout.writeln("========================================="); }
373 //Actor.dumpActors();