switched to GPLv3 ONLY, because i don't trust FSF anymore
[knightmare.git] / monsters.d
blob7d7d97353cd816c9a437e3740ebc77b93c00a0ea
1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * Based on the DOS Knightmare source code by Andrew Zabolotny
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 3 of the License ONLY.
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 monsters;
20 import arsd.color;
21 import arsd.simpledisplay;
23 import iv.cmdcon;
24 import iv.cmdcon.gl;
25 import iv.cmdcon.keybinds;
26 import iv.glbinds.util;
27 import iv.prng.pcg32;
28 import iv.strex;
29 import iv.vfs.io;
31 import tatlas;
32 import zmythspr;
33 import keybinds;
34 import levelmap;
35 import mesactor;
36 import mesengine;
37 import music;
40 // ////////////////////////////////////////////////////////////////////////// //
41 __gshared int[512] batganginfo; // key: gangid; value: # of bats not killed
42 __gshared int nextenemy = 0;
44 __gshared bool xoptContinuousFire = false;
47 // ////////////////////////////////////////////////////////////////////////// //
48 enum PrizeType {
49 Rook,
50 Knight,
51 Queen,
52 King,
53 Exit,
56 shared static this () {
57 conRegFunc!((ConString type) {
58 try {
59 if (type == "rook") VMIFace["conhookGivePrize"](PrizeType.Rook);
60 else if (type == "knight") VMIFace["conhookGivePrize"](PrizeType.Knight);
61 else if (type == "queen") VMIFace["conhookGivePrize"](PrizeType.Queen);
62 else if (type == "king") VMIFace["conhookGivePrize"](PrizeType.King);
63 else if (type == "exit") VMIFace["conhookGivePrize"](PrizeType.Exit);
64 } catch (Throwable e) {
65 conwriteln("FATAL: ", e.msg);
66 assert(0, e.msg);
68 })("prize", "gain prize effect");
70 conSetArgCompleter("prize", delegate (ConCommand self) {
71 import std.range;
72 static immutable string[5] names = ["rook", "knight", "queen", "king", "exit"];
73 concmdRangeCompleter(self, names[]);
74 });
76 static immutable string[17] spawnEntities = [
77 "sphere",
78 "cloud",
79 "vampir",
80 "vampir1",
81 "knight",
82 "gun",
83 "prot",
84 "carlson",
85 "daemon",
86 "hag",
87 "jball",
88 "skeleton",
89 "chaos",
90 "mine",
91 "walkingguy",
92 "monk",
93 "ghost",
96 conRegFunc!((ConString type, int power=60) {
97 foreach (immutable eidx, string ename; spawnEntities[]) {
98 if (type == ename) {
99 cheatSpawnType = cast(int)(eidx+1);
100 cheatSpawnPower = (power < 0 ? 0 : power);
101 return;
104 cheatSpawnType = 0;
105 cheatSpawnPower = 0;
106 conwriteln("unknown entity: ", type);
107 })("spawn", "spawn non-devil enemy (name [power])");
109 conSetArgCompleter("spawn", delegate (ConCommand self) {
110 import std.range;
111 concmdRangeCompleter(self, spawnEntities[]);
115 conRegFunc!(() {
116 try {
117 VMIFace["conhookRemoveAllEnemies"]();
118 } catch (Throwable e) {
119 conwriteln("FATAL: ", e.msg);
120 assert(0, e.msg);
122 })("remove_enemies", "remove all enemy entities");
126 // ////////////////////////////////////////////////////////////////////////// //
127 // actor queues; used to speedup rendering
128 private __gshared ActorId[][int] actorQueues;
130 private void aq_reset_all () {
131 foreach (ref v; actorQueues.byValue) {
132 v.length = 0;
133 v.assumeSafeAppend;
137 private void aq_append (ActorId act, int qidx) {
138 if (!act.alive) return; // anyway
139 if (auto ap = qidx in actorQueues) {
140 (*ap) ~= act;
141 } else {
142 ActorId[] qq;
143 qq ~= act;
144 actorQueues[qidx] = qq;
148 private void aq_reset (int qidx) {
149 if (auto ap = qidx in actorQueues) {
150 (*ap).length = 0;
151 (*ap).assumeSafeAppend;
155 private int aq_count (int qidx) {
156 if (auto ap = qidx in actorQueues) return cast(int)((*ap).length);
157 return 0;
160 private ActorId aq_get (int qidx, int idx) {
161 if (idx >= 0) {
162 if (auto ap = qidx in actorQueues) return (idx < (*ap).length ? (*ap)[idx] : ActorId.init);
164 return ActorId.init;
168 // ////////////////////////////////////////////////////////////////////////// //
169 private void doConWrite (const(int)[] vargs) {
170 foreach (immutable aidx; 0..vargs.mesVAGetArgCount) {
171 auto tp = vargs.mesVAGetArgType(aidx);
172 if (tp is null) assert(0);
173 // avoid needless allocations
174 if (tp.isInt) conwrite(vargs.mesVAGetArgValue!int(aidx));
175 else if (tp.isActor) conwrite("{", vargs.mesVAGetArgValue!ActorId(aidx).id, "}");
176 else if (tp.isEnum) {
177 auto et = cast(TypeEnum)tp;
178 string mname = et.getMember(vargs.mesVAGetArgValue!int(aidx));
179 if (mname !is null) conwrite(et.name, ".", mname); else conwrite(et.name, "(", vargs.mesVAGetArgValue!int(aidx), ")");
180 } else conwrite(vargs.mesVAGetArgValue!string(aidx));
185 // [0]: sin; [1]: cos
186 private immutable int[2][360] sincostbl = () {
187 import std.math : cos, sin, PI;
188 int[2][360] sincostbl;
189 foreach (immutable angle; 0..360) {
190 immutable rang = angle*PI/180;
191 sincostbl[angle][0] = cast(int)(1024*sin(rang));
192 sincostbl[angle][1] = cast(int)(1024*cos(rang));
194 return sincostbl;
195 }();
198 public void loadScripts () {
199 import std.functional : toDelegate;
201 //MESDisableDumps = true;
203 mesOnCompilerMessage = delegate (string s) { conwriteln(s); };
205 mesIntroduceConstant("StageCount", StageCount);
206 mesIntroduceConstant("ScrTilesH", ScrTilesH);
207 mesIntroduceConstant("MapWidth", MapWidth);
208 mesIntroduceConstant("MapHeight", MapHeight);
210 mesIntroduceEnum!ActionKey;
211 mesIntroduceEnum!PrizeType;
213 mesRegisterBuiltin("abort", delegate (string msg) { assert(0, msg); });
215 mesRegisterBuiltin("isStick", delegate (int kidx) {
216 return (kidx >= ActionKey.min && kidx <= ActionKey.max ? actionPressed(cast(ActionKey)kidx) : false);
219 mesRegisterBuiltin("isStickWasRelease", delegate (int kidx) {
220 return (kidx >= ActionKey.min && kidx <= ActionKey.max ? actionWasRelease(cast(ActionKey)kidx) : false);
223 mesRegisterBuiltin("conwrite", delegate (const(int)[] vargs) { doConWrite(vargs); });
224 mesRegisterBuiltin("conwriteln", delegate (const(int)[] vargs) { doConWrite(vargs); conwriteln(); });
226 mesRegisterBuiltin("spawn", (&spawnActor).toDelegate);
227 mesRegisterBuiltin("kill", (&killActor).toDelegate);
229 mesRegisterBuiltin("alive", delegate (ActorId act) { return act.alive; });
231 mesRegisterBuiltin("Sound", (&Sound).toDelegate);
233 mesRegisterBuiltin("NumObjects", (&NumObjects).toDelegate);
235 mesRegisterBuiltin("putSprite", delegate (string pfx, int idx, int x, int y, int ctint=-1) { putSprite(pfx, cast(uint)idx, x, y, ctint); });
237 mesRegisterBuiltin("SpawnRandom", (&SpawnRandom).toDelegate);
238 mesRegisterBuiltin("Random", (&Random).toDelegate);
240 mesRegisterBuiltin("stageHCAt", (&stageHCAt).toDelegate);
241 mesRegisterBuiltin("stageAt", (&stageAt).toDelegate);
243 mesRegisterBuiltin("stageHCPutAt", (&stageHCPutAt).toDelegate);
244 mesRegisterBuiltin("stagePutAt", (&stagePutAt).toDelegate);
246 mesRegisterBuiltin("iterFirst", (&actorIterStart).toDelegate);
247 mesRegisterBuiltin("iterNext", (&actorIterNext).toDelegate);
249 mesRegisterBuiltin("stageMonstersClear", delegate () { mons.length = 0; mons.assumeSafeAppend; });
250 mesRegisterBuiltin("stageMonstersCount", delegate () => cast(int)mons.length);
251 mesRegisterBuiltin("stageMonstersAppendEmpty", delegate () {
252 if (mons.length > int.max/1024) assert(0, "too many stage monsters added");
253 mons.length += 1;
254 mons[$-1].type = 0/*MonsType.Nothing*/;
255 return cast(int)mons.length-1;
258 mesRegisterBuiltin("getStageMonsterTypeAt", delegate (int idx) => (idx >= 0 && idx < mons.length ? mons[idx].type : 0));
259 mesRegisterBuiltin("getStageMonsterDelayAt", delegate (int idx) => (idx >= 0 && idx < mons.length ? mons[idx].delayNext : 0));
260 mesRegisterBuiltin("getStageMonsterPowerAt", delegate (int idx) => (idx >= 0 && idx < mons.length ? mons[idx].power : 0));
261 mesRegisterBuiltin("getStageMonsterGangIdAt", delegate (int idx) => (idx >= 0 && idx < mons.length ? mons[idx].groupid : 0));
262 mesRegisterBuiltin("getStageMonsterGangCountAt", delegate (int idx) => (idx >= 0 && idx < mons.length ? mons[idx].groupcount : 0));
264 mesRegisterBuiltin("setStageMonsterTypeAt", delegate (int idx, int v) { if (idx >= 0 && idx < mons.length) mons[idx].type = v; });
265 mesRegisterBuiltin("setStageMonsterDelayAt", delegate (int idx, int v) { if (idx >= 0 && idx < mons.length) mons[idx].delayNext = v; });
266 mesRegisterBuiltin("setStageMonsterPowerAt", delegate (int idx, int v) { if (idx >= 0 && idx < mons.length) mons[idx].power = v; });
267 mesRegisterBuiltin("setStageMonsterGangIdAt", delegate (int idx, int v) { if (idx >= 0 && idx < mons.length) mons[idx].groupid = v; });
268 mesRegisterBuiltin("setStageMonsterGangCountAt", delegate (int idx, int v) { if (idx >= 0 && idx < mons.length) mons[idx].groupcount = v; });
270 mesRegisterBuiltin("cheatGod", delegate () => cast(bool)cheatGod);
271 mesRegisterBuiltin("cheatGod", delegate (bool v) { cheatGod = v; });
273 mesRegisterBuiltin("cheatInvul", delegate () => cast(bool)cheatInvul);
274 mesRegisterBuiltin("cheatInvul", delegate (bool v) { cheatInvul = v; });
276 mesRegisterBuiltin("cheatGhost", delegate () => cast(bool)cheatGhost);
277 mesRegisterBuiltin("cheatGhost", delegate (bool v) { cheatGhost = v; });
279 mesRegisterBuiltin("cheatKonamiCode", delegate () => cast(bool)cheatKonamiCode);
280 mesRegisterBuiltin("cheatKonamiCode", delegate (bool v) { cheatKonamiCode = v; });
282 mesRegisterBuiltin("cheatSpawnGun", delegate () => cast(bool)cheatSpawnGun);
283 mesRegisterBuiltin("cheatSpawnGun", delegate (bool v) { cheatSpawnGun = v; });
285 mesRegisterBuiltin("cheatSpawnPup", delegate () => cast(bool)cheatSpawnPup);
286 mesRegisterBuiltin("cheatSpawnPup", delegate (bool v) { cheatSpawnPup = v; });
288 mesRegisterBuiltin("cheatSpawnType", delegate () => cast(int)cheatSpawnType);
289 mesRegisterBuiltin("cheatSpawnType", delegate (int v) { cheatSpawnType = v; });
291 mesRegisterBuiltin("cheatSpawnPower", delegate () => cast(int)cheatSpawnPower);
292 mesRegisterBuiltin("cheatSpawnPower", delegate (int v) { cheatSpawnPower = v; });
294 mesRegisterBuiltin("cheatToDevil", delegate () => cast(bool)cheatToDevil);
295 mesRegisterBuiltin("cheatToDevil", delegate (bool v) { cheatToDevil = v; });
297 mesRegisterBuiltin("cheatDevilDead", delegate () => cast(bool)cheatDevilDead);
298 mesRegisterBuiltin("cheatDevilDead", delegate (bool v) { cheatDevilDead = v; });
300 mesRegisterBuiltin("cheatSetStage", delegate () => cast(int)cheatSetStage);
301 mesRegisterBuiltin("cheatSetStage", delegate (int v) { cheatSetStage = v; });
303 mesRegisterBuiltin("loadStageData", (&loadStageData).toDelegate);
304 mesRegisterBuiltin("resetStagePRNGs", (&resetStagePRNGs).toDelegate);
306 // used for bat gangs
307 mesRegisterBuiltin("clearGangs", delegate () { batganginfo[] = -1; });
308 mesRegisterBuiltin("newGang", delegate (int gangid, int gangcount) {
309 if (gangcount > 0 && gangid > 0 && gangid < batganginfo.length && batganginfo[gangid] == -1) {
310 batganginfo[gangid] = gangcount;
311 //conwriteln("new bat gang ", gangid);
314 mesRegisterBuiltin("gangMemberDead", delegate (int gangid) {
315 if (gangid > 0 && gangid < batganginfo.length) {
316 --batganginfo[gangid];
317 //conwriteln("gang ", gangid, " has ", batganginfo[gangid], " members lefs");
320 mesRegisterBuiltin("isWholeGangDead", delegate (int gangid) {
321 if (gangid > 0 && gangid < batganginfo.length) {
322 //conwriteln("checking gang ", gangid, ": alldead=", (batganginfo[gangid] == 0));
323 return (batganginfo[gangid] == 0);
325 return false;
328 mesRegisterBuiltin("sin", delegate (int degrees) {
329 degrees = (degrees%360+360)%360;
330 return sincostbl[degrees][0];
333 mesRegisterBuiltin("cos", delegate (int degrees) {
334 degrees = (degrees%360+360)%360;
335 return sincostbl[degrees][1];
339 mesRegisterBuiltin("soundAbort", (&soundAbort).toDelegate);
340 mesRegisterBuiltin("musicLoopCount", (&musicLoopCount).toDelegate);
341 mesRegisterBuiltin("musicPaused", delegate () => musicPaused);
342 mesRegisterBuiltin("musicPaused", delegate (bool v) { musicPaused = v; });
343 mesRegisterBuiltin("musicNew", (&musicNew).toDelegate);
344 mesRegisterBuiltin("musicAbort", (&musicAbort).toDelegate);
346 mesRegisterBuiltin("optionSoundOn", delegate () => cast(bool)xoptSoundOn);
347 mesRegisterBuiltin("optionSoundOn", delegate (bool v) { xoptSoundOn = v; });
349 mesRegisterBuiltin("optionMusicOn", delegate () => cast(bool)xoptMusicOn);
350 mesRegisterBuiltin("optionMusicOn", delegate (bool v) { xoptMusicOn = v; });
352 mesRegisterBuiltin("optionContinuousFire", delegate () => cast(bool)xoptContinuousFire);
353 mesRegisterBuiltin("optionContinuousFire", delegate (bool v) { xoptContinuousFire = v; });
355 conRegVar!xoptContinuousFire("cheat_continuous_fire", "allow continuous fire by holding fire button");
357 mesRegisterBuiltin("optFixedSpawn", delegate () => cast(bool)optFixedSpawn);
359 mesRegisterBuiltin("concmd", delegate (string cmd) { if (cmd.length) concmd(cmd); });
361 mesRegisterBuiltin("actionBindClear", (&actionBindClear).toDelegate);
362 mesRegisterBuiltin("actionBindFrameEnd", (&actionBindFrameEnd).toDelegate);
365 mesRegisterBuiltin("GC_Collect", delegate () { import core.memory : GC; GC.collect(); GC.minimize(); });
367 mesRegisterBuiltin("actorsPack", (&actorsPack).toDelegate);
368 mesRegisterBuiltin("actorsGC", (&actorsGC).toDelegate);
370 mesRegisterBuiltin("fillRect", delegate (int x, int y, int w, int h, int r, int g, int b, int a) {
371 if (a <= 0 || w < 1 || h < 1) return;
372 if (a > 255) a = 255;
373 if (r < 0) r = 0; else if (r > 255) r = 255;
374 if (g < 0) g = 0; else if (g > 255) g = 255;
375 if (b < 0) b = 0; else if (b > 255) b = 255;
376 glDisable(GL_TEXTURE_2D);
377 if (a != 255) glEnable(GL_BLEND); else glDisable(GL_BLEND);
378 glColor4f(r/255.0f, g/255.0f, b/255.0f, a/255.0f);
379 glBegin(GL_QUADS);
380 glVertex2i(x, y);
381 glVertex2i(x+w, y);
382 glVertex2i(x+w, y+h);
383 glVertex2i(x, y+h);
384 glEnd();
385 glColor3f(1, 1, 1);
386 glDisable(GL_BLEND);
390 mesRegisterBuiltin("aq_reset_all", (&aq_reset_all).toDelegate);
391 mesRegisterBuiltin("aq_append", (&aq_append).toDelegate);
392 mesRegisterBuiltin("aq_reset", (&aq_reset).toDelegate);
393 mesRegisterBuiltin("aq_count", (&aq_count).toDelegate);
394 mesRegisterBuiltin("aq_get", (&aq_get).toDelegate);
397 mesRegisterBuiltin("loadSpriteGfx", (&loadSpriteGfx).toDelegate);
400 try {
401 mesCompileFile(VFile("mes/main.mes"), delegate (const(char)[] fname) {
402 conwriteln("...including script '", fname, "'...");
404 if (!mesCheckUndefined()) {
405 mesDumpUndefined(stderr);
406 assert(0);
408 } catch (CompilerError e) {
409 conwriteln("ERROR in ", e.loc.fname, " at ", e.loc, ": ", e.msg);
410 //throw e;
411 assert(0);
414 { import core.memory : GC; GC.collect(); GC.minimize(); }