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
;
26 import iv
.cmdcon
.keybinds
;
27 import iv
.glbinds
.util
;
29 import iv
.prng
.seeder
;
43 // ////////////////////////////////////////////////////////////////////////// //
44 version(single_binary
) {
45 immutable pakdata
= import("data/base.pak");
49 // ////////////////////////////////////////////////////////////////////////// //
50 private __gshared VFSDriver dataPathDriver
;
51 private __gshared string dataPath
;
54 // ////////////////////////////////////////////////////////////////////////// //
55 void setDataPath (const(char)[] path
) {
56 if (dataPathDriver
!is null) assert(0);
57 if (path
.length
== 0) {
59 } else if (path
[$-1] != '/') {
60 dataPath
= path
.idup
~"/";
64 dataPathDriver
= vfsNewDiskDriver(dataPath
);
65 vfsRegister
!"first"(dataPathDriver
);
69 string
getDataPath () { pragma(inline
, true); return dataPath
; }
72 void addPak (const(char)[] fname
) {
73 conwriteln("; adding '", fname
, "'...");
78 void addPak (VFile fl
) {
83 // ////////////////////////////////////////////////////////////////////////// //
84 int c2i() (in auto ref Color c
) nothrow @safe @nogc {
86 return c
.r|
(c
.g
<<8)|
(c
.b
<<16);
90 Color
i2c() (in int c
) nothrow @safe @nogc {
92 return Color(c
&0xff, (c
>>8)&0xff, (c
>>16)&0xff);
96 // ////////////////////////////////////////////////////////////////////////// //
99 scope(exit
) delete title
;
101 auto fl
= VFile("title.png");
102 if (fl
.size
> 1024*1024*32) assert(0, "title image too big");
103 auto data
= new ubyte[](cast(uint)fl
.size
);
104 scope(exit
) delete data
;
105 fl
.rawReadExact(data
);
106 title
= loadImageFromMemory(data
);
109 scope(exit
) delete img
;
110 img
.length
= title
.width
*title
.height
;
112 auto csky
= title
.getPixel(0, 0);
113 VMIFace
["gtitle_csky"].set
!int(c2i(csky
));
114 // find lightning colors
116 foreach (immutable dx
; 1..title
.width
) if (title
.getPixel(dx
, 0) != csky
) { clgt
[0] = title
.getPixel(dx
, 0); break; }
117 foreach_reverse (immutable dx
; 1..title
.width
) if (title
.getPixel(dx
, 0) != csky
) { clgt
[1] = title
.getPixel(dx
, 0); break; }
118 // create first overlay image with lightning
119 foreach (immutable dy
; 0..title
.height
) {
120 foreach (immutable dx
; 0..title
.width
) {
121 Color c
= title
.getPixel(dx
, dy
);
122 if (c
!= clgt
[0]) c
= Color
.transparent
; else c
= Color
.white
;
123 img
[dy
*title
.width
+dx
] = c
.asUint
;
126 atlasAdd("titleovl_", 0, img
[], title
.width
, title
.height
);
127 // create second overlay image with lightning
128 foreach (immutable dy
; 0..title
.height
) {
129 foreach (immutable dx
; 0..title
.width
) {
130 Color c
= title
.getPixel(dx
, dy
);
131 if (c
!= clgt
[1]) c
= Color
.transparent
; else c
= Color
.white
;
132 img
[dy
*title
.width
+dx
] = c
.asUint
;
135 atlasAdd("titleovl_", 1, img
[], title
.width
, title
.height
);
136 // create title image without lightnings
137 foreach (immutable dy
; 0..title
.height
) {
138 foreach (immutable dx
; 0..title
.width
) {
139 Color c
= title
.getPixel(dx
, dy
);
140 if (c
== clgt
[0] || c
== clgt
[1]) c
= csky
;
141 img
[dy
*title
.width
+dx
] = c
.asUint
;
144 atlasAdd("title_", 0, img
[], title
.width
, title
.height
);
148 // ////////////////////////////////////////////////////////////////////////// //
149 void createGamePhases () {
150 conRegVar
!paused("pause", "pause game");
154 VMIFace
["conhookAbortGame"]();
155 } catch (Throwable e
) {
156 conwriteln("FATAL: ", e
.msg
);
159 })("abort_game", "abort current game and return to title");
163 VMIFace
["conhookGameOver"]();
164 } catch (Throwable e
) {
165 conwriteln("FATAL: ", e
.msg
);
168 })("game_over", "run \"game over\" sequence");
172 VMIFace
["conhookStageNext"]();
173 } catch (Throwable e
) {
174 conwriteln("FATAL: ", e
.msg
);
177 })("stage_next", "go to next stage");
181 VMIFace
["conhookStagePrev"]();
182 } catch (Throwable e
) {
183 conwriteln("FATAL: ", e
.msg
);
186 })("stage_prev", "go to previous stage");
190 // ////////////////////////////////////////////////////////////////////////// //
191 __gshared
bool optPauseOnDefocus
= true;
192 __gshared
bool optVSync
= true;
195 // ////////////////////////////////////////////////////////////////////////// //
196 void main (string
[] args
) {
197 import std
.functional
: toDelegate
;
199 glconShowKey
= "M-Grave";
201 conRegVar
!optFixedSpawn("predictable_spawn", "should spawn PRNG be initialized with fixed seeds? (default: no)");
202 conRegVar
!optPauseOnDefocus("defocus_pause", "autopause the game when window loses focus");
203 conRegVar
!optVSync("v_vsync", "OpenGL vsync");
204 conRegVar
!Scale(1, 16, "v_scale", "game scale (can be set only on startup)");
206 MESDisableDumps
= true;
207 conRegVar
!MESDisableDumps("dbg_vm_nodump", "don't dump compiler VM code");
209 //glconSetAndSealFPS(18);
210 glconSetAndSealFPS(20);
211 //glconSetAndSealFPS(35);
213 static void setDP () {
214 version(single_binary
) {
215 //immutable pakdata = import("data/base.pak");
216 addPak(wrapMemoryRO(pakdata
, "data/base.pak"));
220 } else version(Windows
) {
223 import std
.file
: thisExePath
;
224 import std
.path
: dirName
;
225 setDataPath(thisExePath
.dirName
~"/data");
228 addPak(getDataPath
~"base.pak");
229 } catch (Exception e
) {
236 setupDefaultKeyBindings();
238 conProcessQueue(256*1024); // load config
239 conProcessArgs
!true(args
);
240 conProcessQueue(256*1024);
242 conSealVar("predictable_spawn");
243 conSealVar("v_vsync");
244 conSealVar("v_scale");
246 mesIntroduceEnum
!Key
; // arsd.Key
250 mesRegisterBuiltin("paused", delegate () => cast(bool)paused
);
251 mesRegisterBuiltin("paused", delegate (bool v
) { paused
= v
; });
253 mesRegisterBuiltin("isAudioFucked", delegate () => cast(bool)isAudioFucked
);
255 mesRegisterBuiltin("optPauseOnDefocus", delegate () => cast(bool)optPauseOnDefocus
);
257 mesRegisterBuiltin("atlasUpdateTexture", delegate (string pfx
, int idx
) {
258 if (auto ai
= atlasGet(pfx
, idx
)) ai
.updateTexture();
261 mesRegisterBuiltin("atlasSetPixel", delegate (string pfx
, int idx
, int x
, int y
, int c
) {
262 if (auto ai
= atlasGet(pfx
, idx
)) {
263 ai
.setPixel(x
, y
, i2c(c
));
267 mesRegisterBuiltin("atlasGetPixel", delegate (string pfx
, int idx
, int x
, int y
) {
268 if (auto ai
= atlasGet(pfx
, idx
)) {
269 auto clr
= ai
.getPixel(x
, y
);
270 if (clr
.a
== 0) return -666;
288 oglSetupDG
= delegate () {
289 glconCtlWindow
.vsync
= optVSync
;
296 VMIFace
["onGameLoadGraphics"]();
297 } catch (Throwable e
) {
298 conwriteln("FATAL: ", e
.msg
);
302 // this will create texture
303 //gxResize(glconCtlWindow.width, glconCtlWindow.height);
308 VMIFace
["onGameInit"]();
309 } catch (Throwable e
) {
310 conwriteln("FATAL: ", e
.msg
);
316 redrawFrameDG
= delegate () {
317 oglSetup2D(glconCtlWindow
.width
, glconCtlWindow
.height
);
320 glClearColor(0, 0, 0, 0);
321 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_ACCUM_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
325 glMatrixMode(GL_MODELVIEW
);
326 glScalef(Scale
, Scale
, 1);
329 VMIFace
["onGameRender"]();
330 } catch (Throwable e
) {
331 conwriteln("FATAL: ", e
.msg
);
336 // rebuild main screen (do any calculations we might need)
337 nextFrameDG
= delegate () {
339 VMIFace
["onGameTick"]();
340 } catch (Throwable e
) {
341 conwriteln("FATAL: ", e
.msg
);
344 actionBindFrameEnd();
347 keyEventDG
= delegate (KeyEvent event
) {
349 VMIFace
["eventKeyKey"].set
!Key(event
.key
);
350 VMIFace
["eventKeyPressed"].set
!bool(event
.pressed
);
351 VMIFace
["eventKeyCtrl"].set
!bool((event
.modifierState
&ModifierState
.ctrl
) != 0);
352 VMIFace
["eventKeyAlt"].set
!bool((event
.modifierState
&ModifierState
.alt
) != 0);
353 VMIFace
["eventKeyShift"].set
!bool((event
.modifierState
&ModifierState
.shift
) != 0);
354 VMIFace
["eventKeyHyper"].set
!bool((event
.modifierState
&ModifierState
.windows
) != 0);
358 bool changed
= doActionBind(event
, &kk
);
359 VMIFace
["onActionBindChanged"].set
!bool(changed
);
360 VMIFace
["onActionBindWasPress"].set
!bool(event
.pressed
);
361 if (changed
) VMIFace
["actionBindKK"].set
!ActionKey(kk
);
363 VMIFace
["onGameKeyEvent"]();
364 } catch (Throwable e
) {
365 conwriteln("FATAL: ", e
.msg
);
371 mouseEventDG = delegate (MouseEvent event) {
374 charEventDG = delegate (dchar ch) {
377 resizeEventDG = delegate (int wdt, int hgt) {
381 focusEventDG
= delegate (bool focused
) {
383 VMIFace
["onGameFocusEvent"](focused
);
384 } catch (Throwable e
) {
385 conwriteln("FATAL: ", e
.msg
);
390 glconRunGLWindow
/*Resizeable*/(320*Scale
, 200*Scale
, "Konami's Knightmare", "KNIGHTMARE");