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/>.
21 import arsd
.simpledisplay
;
25 import iv
.cmdcon
.keybinds
;
26 import iv
.glbinds
.util
;
40 // ////////////////////////////////////////////////////////////////////////// //
41 __gshared
int[512] batganginfo
; // key: gangid; value: # of bats not killed
42 __gshared
int nextenemy
= 0;
44 __gshared
bool xoptContinuousFire
= false;
47 // ////////////////////////////////////////////////////////////////////////// //
56 shared static this () {
57 conRegFunc
!((ConString type
) {
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
);
68 })("prize", "gain prize effect");
70 conSetArgCompleter("prize", delegate (ConCommand self
) {
72 static immutable string
[5] names
= ["rook", "knight", "queen", "king", "exit"];
73 concmdRangeCompleter(self
, names
[]);
76 static immutable string
[17] spawnEntities
= [
96 conRegFunc
!((ConString type
, int power
=60) {
97 foreach (immutable eidx
, string ename
; spawnEntities
[]) {
99 cheatSpawnType
= cast(int)(eidx
+1);
100 cheatSpawnPower
= (power
< 0 ?
0 : power
);
106 conwriteln("unknown entity: ", type
);
107 })("spawn", "spawn non-devil enemy (name [power])");
109 conSetArgCompleter("spawn", delegate (ConCommand self
) {
111 concmdRangeCompleter(self
, spawnEntities
[]);
117 VMIFace
["conhookRemoveAllEnemies"]();
118 } catch (Throwable e
) {
119 conwriteln("FATAL: ", 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
) {
137 private void aq_append (ActorId act
, int qidx
) {
138 if (!act
.alive
) return; // anyway
139 if (auto ap
= qidx
in actorQueues
) {
144 actorQueues
[qidx
] = qq
;
148 private void aq_reset (int qidx
) {
149 if (auto ap
= qidx
in actorQueues
) {
151 (*ap
).assumeSafeAppend
;
155 private int aq_count (int qidx
) {
156 if (auto ap
= qidx
in actorQueues
) return cast(int)((*ap
).length
);
160 private ActorId
aq_get (int qidx
, int idx
) {
162 if (auto ap
= qidx
in actorQueues
) return (idx
< (*ap
).length ?
(*ap
)[idx
] : 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
));
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");
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);
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);
382 glVertex2i(x
+w
, y
+h
);
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
);
401 mesCompileFile(VFile("mes/main.mes"), delegate (const(char)[] fname
) {
402 conwriteln("...including script '", fname
, "'...");
404 if (!mesCheckUndefined()) {
405 mesDumpUndefined(stderr
);
408 } catch (CompilerError e
) {
409 conwriteln("ERROR in ", e
.loc
.fname
, " at ", e
.loc
, ": ", e
.msg
);
414 { import core
.memory
: GC
; GC
.collect(); GC
.minimize(); }