conveyor graphics fix
[jetset.git] / jetset.d
blob0e7fce05023c7ac7a1b740ae32f9aa1271e978ba
1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
3 * Based on Jet-Set Willy, v1.0.1 by <Florent.Guillaume@ens.fr>
4 * Linux port and preliminary sound by jmd@dcs.ed.ac.uk
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, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 module jetset;
21 import arsd.simpledisplay;
22 import arsd.simpleaudio;
24 import wadarc;
25 import x11gfx;
26 import engine;
27 import willy;
28 import audio;
31 // ////////////////////////////////////////////////////////////////////////// //
32 __gshared bool debugHarmlessMonsters = false;
33 __gshared WillySavePos[10] gameSaves;
34 __gshared bool gamePaused = false;
35 __gshared int gvGameFlash = 0;
36 __gshared bool gameJustStarted = true;
39 // ////////////////////////////////////////////////////////////////////////// //
40 void jumpToRoom (ref Willy willy, int dir) {
41 auto room = gameRooms[willy.roomnum];
42 if (room.exits[dir] < 0) return;
43 willy.gotoRoom(room.exits[dir]);
47 // ////////////////////////////////////////////////////////////////////////// //
48 __gshared int gvTitleFadingOut;
50 void gsTitleEnter (ref Willy willy) {
51 blitShine = 0;
52 blitType = BlitType.Normal;
53 gvTitleFadingOut = 0;
54 jssPlaySfx("jss_titl");
57 void gsTitleExit (ref Willy willy) {
58 gamePaused = false;
59 gameJustStarted = true;
62 void gsTitleKeys (ref Willy willy, KeyEvent evt) {
63 if (!evt.pressed) return;
64 switch (evt.key) {
65 case Key.Space: case Key.Enter:
66 if (gvTitleFadingOut) {
67 willy.restartGame();
68 willy.switchState(GameState.Playing);
69 } else {
70 gvTitleFadingOut = 1;
72 break;
73 default:
77 void gsTitleStep (ref Willy willy) {
80 void gsTitleDraw (ref Willy willy) {
81 //drawStr(4, vbufH-12, "items: %s".format(countAllItems()), rgbcol(255, 127, 0));
82 drawStr!"d10"(10, 10, "Jet Set Willy II", rgbcol(255, 127, 0));
83 drawStr!"dos"(10, 22, "Jet Set Willy II", rgbcol(255, 127, 0));
84 drawStrProp!"dos"(10, 32, "Jet Set Willy II", rgbcol(255, 127, 0));
85 drawStrProp(10, 42, "Jet Set Willy II", rgbcol(255, 127, 0));
86 if (gvTitleFadingOut) {
87 blitShine = -gvTitleFadingOut*10;
88 if (++gvTitleFadingOut >= 20) {
89 willy.restartGame();
90 willy.switchState(GameState.Playing);
96 // ////////////////////////////////////////////////////////////////////////// //
97 void gsGameKeysRestoreSaves (ref Willy willy, KeyEvent evt) {
98 switch (evt.key) {
99 case Key.R:
100 if (evt.pressed) {
101 if (evt.modifierState&ModifierState.shift) { willy.restartGame(); willy.switchState(GameState.Playing); break; }
102 if (evt.modifierState&ModifierState.alt) { if (willy.restoreSavePos()) willy.switchState(GameState.Playing); break; }
104 break;
105 case Key.P:
106 if (evt.pressed) {
107 if (evt.modifierState&ModifierState.shift) { willy.restartGame(); willy.switchState(GameState.Playing); break; }
108 if (evt.modifierState&ModifierState.alt) { if (willy.restoreSavePosPrev()) willy.switchState(GameState.Playing); break; }
109 gamePaused = !gamePaused;
111 break;
112 case Key.N0: .. case Key.N9:
113 if (evt.pressed) {
114 // load save
115 if (evt.modifierState&ModifierState.shift) {
116 if (willy.restoreSave(gameSaves[evt.key-Key.N0])) willy.switchState(GameState.Playing);
117 break;
119 // save save
120 if (evt.modifierState&ModifierState.alt) {
121 if (!willy.dead && willy.gstate != GameState.Title && willy.gstate != GameState.Dead) {
122 gameSaves[evt.key-Key.N0] = willy.saveSave();
124 break;
127 break;
128 default:
133 // shared by all states
134 void gsGameKeys (ref Willy willy, KeyEvent evt) {
135 willy.gsGameKeysRestoreSaves(evt);
136 switch (evt.key) {
137 case Key.Left:
138 if (evt.pressed) {
139 if (evt.modifierState&ModifierState.shift) { willy.jumpToRoom(JSWRoom.Exit.Left); break; }
141 willy.goleft = (evt.pressed ? 1 : 0);
142 break;
143 case Key.Right:
144 if (evt.pressed) {
145 if (evt.modifierState&ModifierState.shift) { willy.jumpToRoom(JSWRoom.Exit.Right); break; }
147 willy.goright = (evt.pressed ? 1 : 0);
148 break;
149 case Key.Up:
150 if (evt.pressed) {
151 if (evt.modifierState&ModifierState.shift) { willy.jumpToRoom(JSWRoom.Exit.Up); break; }
153 willy.dojump = (evt.pressed ? 1 : 0);
154 break;
155 case Key.Down:
156 if (evt.pressed) {
157 if (evt.modifierState&ModifierState.shift) { willy.jumpToRoom(JSWRoom.Exit.Down); break; }
159 break;
160 case Key.Ctrl:
161 willy.dojump = (evt.pressed ? 1 : 0);
162 break;
163 case Key.D:
164 if (evt.pressed) {
165 if (evt.modifierState&ModifierState.alt) { debugHarmlessMonsters = !debugHarmlessMonsters; break; }
167 break;
168 case Key.Space: case Key.Enter:
169 if (evt.pressed) {
170 if (willy.dead) {
171 if (!willy.restoreSavePos()) willy.restartGame();
172 willy.switchState(GameState.Playing);
175 break;
176 case Key.W: // alt+shift+w: collect all items
177 if (evt.pressed) {
178 if (evt.modifierState&ModifierState.shift && evt.modifierState&ModifierState.alt) { JSWRoom.cheatCollectAllItems(); break; }
180 break;
181 default: break;
186 // shared by all states
187 void gsGameStep (ref Willy willy) {
188 if (gamePaused) return;
189 auto room = gameRooms[willy.roomnum];
190 if (room is null) assert(0, "wtf?!");
191 if (willy.dead) return;
192 scope(exit) {
193 if (willy.dead) willy.switchState(GameState.Dead); // willy just died, switch state
195 ++frameno; // let it move! (animate lifts, items, etc.)
196 willy.room.stepSpecial(willy);
197 willy.move();
198 if (willy.dead) return; // willy just died
199 room.step();
200 // check collisions
201 room.buildColMap(willy);
202 // check monster collision here
203 if (!debugHarmlessMonsters && !willy.dead && willy.invulnTimer == 0 && willy.checkMonsterCollision()) {
204 willy.dead = true;
205 return;
207 // if willy should exit to another room, do it here
208 willy.processExit();
212 // shared by all states
213 void gsGameDraw (ref Willy willy) {
214 auto room = gameRooms[willy.roomnum];
215 if (room !is null) {
216 room.drawTiles();
217 room.drawMonsters(willy);
218 willy.draw(debugHarmlessMonsters);
223 // ////////////////////////////////////////////////////////////////////////// //
224 void gsPlayingEnter (ref Willy willy) {
225 jssStopSfx();
226 if (willy.dead) willy.newLife();
227 gvGameFlash = 0;
228 blitShine = 0;
229 blitType = BlitType.Normal;
230 if (gameJustStarted) {
231 gameJustStarted = false;
232 // special rooms
233 //willy.gotoRoom(0x21); // bedroom
234 //{ willy.gotoRoom(0x77); willy.setXY(120, 90); } // rocket
235 //willy.gotoRoom(0x4b); // rigor mortis
236 //willy.gotoRoom(0x4c); // crypt
237 //willy.gotoRoom(0x3b); // hell
238 //{ willy.gotoRoom(0x71); willy.setXY(10, 38); } // tribbles
239 //willy.gotoRoom(0x7e); // eggoids
240 //willy.gotoRoom(0x67); // foot
241 //willy.gotoRoom(0x60); // beam down
242 //willy.gotoRoom(0x7f); // beam up
243 //{ willy.gotoRoom(0x50); willy.setXY(50, 88); } // isle
244 //{ willy.gotoRoom(0x44); willy.setXY(50, 88); } // bell
245 //willy.gotoRoom(0x79); // invasion
246 //willy.gotoRoom(0x47); // trip switch
247 //{ willy.gotoRoom(0x38); willy.setXY(10, 96); } // yacht
251 void gsPlayingExit (ref Willy willy) {
254 void gsPlayingKeys (ref Willy willy, KeyEvent evt) {
255 willy.gsGameKeys(evt);
258 void gsPlayingStep (ref Willy willy) {
259 willy.gsGameStep();
260 auto room = willy.room;
261 switch (room.sptype) {
262 case JSWRoom.SpType.Rocket:
263 if (room.countItems == 0 && willy.y == 0x30) {
264 willy.rocket_cnt = 0x80;
265 willy.switchState(GameState.Rocket);
267 break;
268 default:
272 void gsPlayingDraw (ref Willy willy) {
273 if (gvGameFlash > 0) {
274 --gvGameFlash;
275 blitShine = 100;
276 } else {
277 gvGameFlash = 0;
278 blitShine = 0;
280 if (gamePaused) {
281 blitType = BlitType.Green;
282 } else {
283 blitType = BlitType.Normal;
285 willy.gsGameDraw();
289 // ////////////////////////////////////////////////////////////////////////// //
290 __gshared int gvDeathFadingOut;
292 void gsDeadEnter (ref Willy willy) {
293 jssPlaySfx("jss_dead");
294 blitShine = 0;
295 blitType = BlitType.Red;
296 gvDeathFadingOut = 0;
299 void gsDeadExit (ref Willy willy) {
302 void gsDeadKeys (ref Willy willy, KeyEvent evt) {
303 switch (evt.key) {
304 case Key.Space: case Key.Enter:
305 if (evt.pressed) {
306 if (!willy.restoreSavePos()) willy.restartGame();
307 willy.switchState(GameState.Playing);
308 return;
310 break;
311 default:
313 willy.gsGameKeysRestoreSaves(evt);
316 void gsDeadStep (ref Willy willy) {
317 //willy.gsGameStep();
318 if (blitShine > -180) --blitShine;
321 void gsDeadDraw (ref Willy willy) {
322 willy.gsGameDraw();
326 // ////////////////////////////////////////////////////////////////////////// //
327 void gsRocketEnter (ref Willy willy) {
330 void gsRocketExit (ref Willy willy) {
333 void gsRocketKeys (ref Willy willy, KeyEvent evt) {
334 willy.gsGameKeys(evt);
337 void gsRocketStep (ref Willy willy) {
338 willy.gsGameStep();
341 void gsRocketDraw (ref Willy willy) {
342 willy.gsGameDraw();
346 // ////////////////////////////////////////////////////////////////////////// //
347 void gsTeleportEnter (ref Willy willy) {
350 void gsTeleportExit (ref Willy willy) {
353 void gsTeleportKey (ref Willy willy, KeyEvent evt) {
354 willy.gsGameKeys(evt);
357 void gsTeleportStep (ref Willy willy) {
358 willy.gsGameStep();
361 void gsTeleportDraw (ref Willy willy) {
362 willy.gsGameDraw();
366 // ////////////////////////////////////////////////////////////////////////// //
367 void gsBoatEnter (ref Willy willy) {
370 void gsBoatExit (ref Willy willy) {
373 void gsBoatKey (ref Willy willy, KeyEvent evt) {
374 willy.gsGameKeys(evt);
377 void gsBoatStep (ref Willy willy) {
378 willy.gsGameStep();
381 void gsBoatDraw (ref Willy willy) {
382 willy.gsGameDraw();
386 // ////////////////////////////////////////////////////////////////////////// //
387 // if forced, do leave/enter even if state wasn't changed
388 void switchState (ref Willy willy, GameState newstate, bool force=false) {
389 if (!force && willy.gstate == newstate) return;
390 int newdead = 0; // 0: don't change; <0: dead; >0: alive
391 final switch (willy.gstate) {
392 case GameState.Title: gsTitleExit(willy); break;
393 case GameState.Playing: gsPlayingExit(willy); newdead = 1; break;
394 case GameState.Dead: gsDeadExit(willy); newdead = -1; break;
395 case GameState.Rocket: gsRocketExit(willy); newdead = 1; break;
396 case GameState.Teleport: gsTeleportExit(willy); newdead = 1; break;
397 case GameState.Boat: gsBoatExit(willy); newdead = 1; break;
399 willy.gstate = newstate;
400 if (newdead) {
401 if (willy.dead && newdead > 0) willy.newLife();
402 if (!willy.dead && newdead < 0) willy.dead = true; //FIXME
404 final switch (willy.gstate) {
405 case GameState.Title: gsTitleEnter(willy); break;
406 case GameState.Playing: gsPlayingEnter(willy); break;
407 case GameState.Dead: gsDeadEnter(willy); break;
408 case GameState.Rocket: gsRocketEnter(willy); break;
409 case GameState.Teleport: gsTeleportEnter(willy); break;
410 case GameState.Boat: gsBoatEnter(willy); break;
415 void stepState (ref Willy willy) {
416 final switch (willy.gstate) {
417 case GameState.Title: gsTitleStep(willy); break;
418 case GameState.Playing: gsPlayingStep(willy); break;
419 case GameState.Dead: gsDeadStep(willy); break;
420 case GameState.Rocket: gsRocketStep(willy); break;
421 case GameState.Teleport: gsTeleportStep(willy); break;
422 case GameState.Boat: gsBoatStep(willy); break;
427 void drawState (ref Willy willy) {
428 final switch (willy.gstate) {
429 case GameState.Title: gsTitleDraw(willy); break;
430 case GameState.Playing: gsPlayingDraw(willy); break;
431 case GameState.Dead: gsDeadDraw(willy); break;
432 case GameState.Rocket: gsRocketDraw(willy); break;
433 case GameState.Teleport: gsTeleportDraw(willy); break;
434 case GameState.Boat: gsBoatDraw(willy); break;
439 void keysState (ref Willy willy, KeyEvent evt) {
440 final switch (willy.gstate) {
441 case GameState.Title: gsTitleKeys(willy, evt); break;
442 case GameState.Playing: gsPlayingKeys(willy, evt); break;
443 case GameState.Dead: gsDeadKeys(willy, evt); break;
444 case GameState.Rocket: gsRocketKeys(willy, evt); break;
445 case GameState.Teleport: gsTeleportKey(willy, evt); break;
446 case GameState.Boat: gsBoatKey(willy, evt); break;
451 // ////////////////////////////////////////////////////////////////////////// //
452 void main (string[] args) {
453 registerWad("data/jetset.wad");
454 registerWad("data/music.wad", true);
456 for (int idx = 1; idx < args.length; ++idx) {
457 if (args[idx] == "-file") {
458 if (args.length-idx < 2) assert(0, "arg?!");
459 registerWad(args[idx+1]);
460 ++idx;
461 } else {
462 assert(0, "invalid arg");
466 loadGameData();
468 jssSetupSound();
469 scope(exit) jssAbortSound();
471 auto sdwin = x11gfxInit("Jet Set Willy");
473 clear(0);
474 realizeVBuf();
475 x11gfxBlit();
478 bool doQuit;
479 Willy willy;
481 willy.onTakeItem = delegate () {
482 gvGameFlash = 2;
483 jssPlaySfx("jss_pick");
486 // hacked init
487 willy.restartGame();
488 willy.dead = true;
490 willy.switchState(GameState.Title, true); // forced switch
492 sdwin.eventLoop(1000/20,
493 // timer
494 delegate () {
495 if (sdwin.closed) return;
496 clear(0);
497 willy.stepState();
498 willy.drawState();
500 import std.format : format;
501 drawStr(4, vbufH-12, "items: %s".format(countAllItems()), rgbcol(255, 127, 0));
503 realizeVBuf();
504 x11gfxBlit();
506 // keyboard
507 delegate (KeyEvent evt) {
508 if (sdwin.closed) return;
509 switch (evt.key) {
510 case Key.Q:
511 if (evt.pressed) {
512 if (evt.modifierState&ModifierState.ctrl) { doQuit = true; break; }
513 if (evt.modifierState&ModifierState.alt) { willy.switchState(GameState.Title); break; }
515 break;
516 case Key.C:
517 if (evt.pressed) {
518 if (evt.modifierState&ModifierState.ctrl) { willy.switchState(GameState.Title); break; }
520 break;
521 case Key.X:
522 if (evt.pressed) {
523 if (evt.modifierState&ModifierState.alt) { doQuit = true; break; }
525 break;
526 case Key.Escape:
527 //doQuit = true;
528 break;
529 default: break;
531 if (doQuit) { sdwin.close(); return; }
532 willy.keysState(evt);
534 // mouse
535 delegate (MouseEvent evt) {
536 if (sdwin.closed) return;
537 if (willy.gstate == GameState.Title) return;
538 if (evt.type == MouseEventType.buttonPressed) {
539 if (evt.button == MouseButton.left) {
540 willy.setXY(evt.x/2, evt.y/2);
544 // char
545 delegate (dchar ch) {
546 if (sdwin.closed) return;
549 x11gfxDeinit();