they are marching!
[dd2d.git] / dengapi.d
blobdca77025ac82947c3d52c38497184ee6cbac8a4e
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 void updateActorClasses (int iteration) {
30 foreach (string ctype; dacsClassTypes(iteration)) {
31 foreach (auto mod; dacsClassModules(ctype, iteration)) {
32 auto adef = registerActorDef(mod.rmoduletype.classtype, mod.rmoduletype.classname);
33 conwriteln("found info for actor '", adef.fullname, "'");
35 auto animInitFn = FuncPool.findByFQMG(mod.name~".initializeAnim", ":void");
36 if (animInitFn !is null) {
37 if (animInitFn.code.length == 0) throw new CompilerException("invalid 'initializeAnim' in module '"~mod.name~"' for '"~mod.rmoduletype.classtype~":"~mod.rmoduletype.classname~"'");
39 adef.setAnimInitFunc(animInitFn);
42 auto initFn = FuncPool.findByFQMG(mod.name~".initialize", ":void:Actor");
43 if (initFn !is null) {
44 if (initFn.code.length == 0) throw new CompilerException("invalid 'initialize' in module '"~mod.name~"' for '"~mod.rmoduletype.classtype~":"~mod.rmoduletype.classname~"'");
46 adef.setInitFunc(initFn);
49 auto thinkFn = FuncPool.findByFQMG(mod.name~".think", ":void:Actor");
50 if (thinkFn !is null) {
51 if (thinkFn.code.length == 0) throw new CompilerException("invalid 'think' in module '"~mod.name~"' for '"~mod.rmoduletype.classtype~":"~mod.rmoduletype.classname~"'");
53 adef.setThinkFunc(thinkFn);
60 // ////////////////////////////////////////////////////////////////////////// //
61 private string modLoader (string modname) {
62 static string getTextFile (string fname) {
63 try {
64 auto res = loadTextFile(fname);
65 conwriteln("loading DACS module file '", fname, "' (", res.length, " bytes)");
66 if (res is null) res = "";
67 return res;
68 } catch (Exception) {}
69 return null;
72 string res;
74 //res = getTextFile(modname~".dacs");
75 //if (res !is null) return res;
77 res = getTextFile("scripts/"~modname~".dacs");
78 if (res !is null) return res;
79 assert(res is null);
81 string[] parts = ["scripts"];
82 string mn = modname;
83 while (mn.length > 0) {
84 int pos = 0;
85 if (mn[0] >= 'A' && mn[0] <= 'Z') ++pos;
86 while (pos < mn.length && (mn[pos] < 'A' || mn[pos] > 'Z')) ++pos;
87 if (mn[0] >= 'A' && mn[0] <= 'Z') {
88 parts ~= cast(char)(mn[0]+32)~mn[1..pos];
89 } else {
90 parts ~= mn[0..pos];
92 mn = mn[pos..$];
95 import std.array : join;
96 string path = parts.join("/")~".dacs";
97 res = getTextFile(path);
98 if (res !is null) return res;
99 assert(res is null);
101 assert(res is null);
102 throw new Exception("module '"~modname~"' not found");
106 __gshared int modIteration = 0;
108 public void loadWadScripts () {
109 if (moduleLoader is null) moduleLoader = (string modname) => modLoader(modname);
110 string mainmod;
111 try {
112 import std.array : split;
113 auto t = loadTextFile("dacsmain.txt");
114 foreach (string line; t.split('\n')) {
115 while (line.length && line[0] <= ' ') line = line[1..$];
116 if (line.length == 0 || line[0] == ';') continue;
117 while (line.length && line[$-1] <= ' ') line = line[0..$-1];
118 mainmod = line;
119 break;
121 } catch (Exception e) { return; }
122 try {
123 //conwriteln("loading main DACS module '", mainmod, "'");
124 auto iter = modIteration++;
125 parseModule(mainmod, iter);
126 parseComplete();
127 updateActorClasses(iter);
128 } catch (CompilerException e) {
129 _d_print_throwable(e);
130 conwriteln("PARSE ERROR: ", e.file, " at ", e.line);
131 conwriteln(e.toString);
132 assert(0);
137 // ////////////////////////////////////////////////////////////////////////// //
138 public __gshared LevelMap map;
139 public __gshared DACSVM dvm;
140 public __gshared uint prngSyncSeed = 0x29a;
141 public __gshared uint prngSeed = 0x29a; // unsynced
144 // ////////////////////////////////////////////////////////////////////////// //
145 // Park-Miller-Carta Pseudo-Random Number Generator, based on David G. Carta paper
146 // 31 bit of randomness
147 // seed is previous result, as usual
148 public uint prngR31next (uint seed) {
149 if (seed == 0) seed = 0x29a;
150 uint lo = 16807*(seed&0xffff);
151 uint hi = 16807*(seed>>16);
152 lo += (hi&0x7fff)<<16;
153 lo += hi>>15;
154 //if (lo > 0x7fffffff) lo -= 0x7fffffff; // should be >=, actually
155 lo = (lo&0x7FFFFFFF)+(lo>>31); // same as previous code, but branch-less
156 return lo;
160 // "synced" random, using common seed for all players
161 public uint syncrandu31 () {
162 pragma(inline, true);
163 return (prngSyncSeed = prngR31next(prngSyncSeed));
167 // ////////////////////////////////////////////////////////////////////////// //
168 // this is VAT function, argument order is reversed
169 void doWrite (DACSVM vm, bool donl) {
170 auto count = vm.popInt();
171 while (count-- > 0) {
172 auto type = vm.popInt();
173 switch (cast(VATArgType)type) {
174 case VATArgType.Int: conwrite(vm.popInt()); break;
175 case VATArgType.Uint: conwrite(vm.popUint()); break;
176 case VATArgType.Float: conwrite(vm.popFloat()); break;
177 case VATArgType.StrId: conwrite(StrId(vm.popUint()).get); break;
178 case VATArgType.ActorId: conwrite("<actor>"); vm.popUint(); break; //TODO
179 default: conwrite("<invalid-type>"); vm.popUint(); break; //TODO
182 if (donl) conwriteln();
183 // push dummy return value
184 vm.pushInt(0);
188 // ////////////////////////////////////////////////////////////////////////// //
189 void animClearFrames (StrId classtype, StrId classname, StrId state) {
190 auto adef = findActorDef(classtype.get, classname.get);
191 if (adef is null) throw new Exception("animClearFrames: actor '"~classtype.get~":"~classname.get~"' not found!");
192 adef.clearFrames(state);
196 void animAddFrame (StrId classtype, StrId classname, StrId state, uint dir, StrId sprname) {
197 auto adef = findActorDef(classtype.get, classname.get);
198 if (adef is null) throw new Exception("animAddFrame: actor '"~classtype.get~":"~classname.get~"' not found!");
199 if (dir != 0) dir = 1; //TODO: process mirror flag here
200 adef.addFrame(state, dir, sprname);
204 // ////////////////////////////////////////////////////////////////////////// //
205 public void registerAPI () {
206 dvm = new DACSVM();
208 FuncPool["write"] = delegate void (FuncPool.FuncInfo fi, DACSVM vm) { doWrite(vm, false); };
209 FuncPool["writeln"] = delegate void (FuncPool.FuncInfo fi, DACSVM vm) { doWrite(vm, true); };
211 FuncPool["syncrandu31"] = &syncrandu31;
213 FuncPool["animClearFrames"] = &animClearFrames;
214 FuncPool["animAddFrame"] = &animAddFrame;
216 FuncPool["actorSetAnimation"] = function void (ActorId me, StrId state) {
217 if (!me.valid) return;
218 if (auto adef = findActorDef(me.classtype!string, me.classname!string)) {
219 me.zAnimstate = state;
220 me.zAnimidx = 0;
224 FuncPool["getPlayer"] = function ActorId () { assert(0); };
226 // initialize actor animation
227 foreach (ActorDef adef; actordefs.byValue) {
228 adef.callAnimInit(dvm);
233 // ////////////////////////////////////////////////////////////////////////// //
234 public void loadMapMonsters () {
235 assert(map !is null);
236 foreach (ref thing; map.things) {
237 if (thing.dmonly) continue;
238 if (auto did = thing.type in d2dactordefsById) {
239 if (did.classtype == "item") continue;
240 if (did.classtype == "playerstart") continue;
242 auto adef = findD2DActorDef(thing.type);
243 if (adef is null) {
244 if (auto did = thing.type in d2dactordefsById) {
245 conwriteln("ignoring D2D thing '", did.fullname, "'");
246 } else {
247 conwriteln("ignoring unknown D2D thing with mapid ", thing.type);
249 continue;
251 // load graphics (we'll load all graphics)
252 //adef.loadGraphics();
253 // create actor and initialize it
254 auto aid = Actor.alloc;
255 aid.classtype = StrPool.intern(adef.classtype);
256 aid.classname = StrPool.intern(adef.classname);
257 if (auto did = thing.type in d2dactordefsById) {
258 assert(did.classtype == adef.classtype);
259 assert(did.classname == adef.classname);
260 conwriteln("mapid=", thing.type, "; ", adef.classtype, ":", adef.classname, "; id=", aid.id);
261 assert(aid.classtype!string == adef.classtype);
262 assert(aid.classname!string == adef.classname);
263 } else {
264 assert(0);
266 aid.state = StrPool.MNST_SLEEP;
267 aid.x = cast(int)thing.x;
268 aid.y = cast(int)thing.y;
269 aid.dir = cast(uint)(thing.right ? 1 : 0);
270 adef.callInit(dvm, aid);
273 foreach (ActorDef adef; actordefs.byValue) adef.loadGraphics();
274 //Actor.dumpActors();
278 // ////////////////////////////////////////////////////////////////////////// //
279 public void doActorsThink () {
280 Actor.forEach((ActorId me) {
281 // `act` is always valid here
282 if (auto adef = findActorDef(me.classtype!string, me.classname!string)) {
283 adef.callThink(dvm, me);
284 int aidx = me.zAnimidx!int;
285 int nidx = adef.nextAnimIdx(me.zAnimstate!StrId, me.dir!uint, aidx);
286 //conwriteln("actor ", me.id, " (", me.classtype!string, me.classname!string, "): state=", me.zAnimstate!string, "; aidx=", aidx, "; nidx=", nidx);
287 me.zAnimidx = nidx;
288 assert(me.zAnimidx!int == nidx);
291 //{ import std.stdio : stdout; stdout.writeln("========================================="); }
292 //Actor.dumpActors();