1 /*
2 * dstar.c
3 *
4 * dstarz88 is a conversion of a TI86 game I found with
5 * source on www.ticalc.org.
6 *
7 * The original program was written by Andrew Von Dollen who
8 * in turn based it on a HP game by Joe W.
9 *
10 * The aim of the game is to collect all the clear bubbles by
11 * running over them. You control either the dark bubble or
12 * the solid box. The dark bubble is used to collect the clear
13 * bubbles, and the solid box is used as a sort of movable wall.
14 *
15 * Both objects carry on moving until they hit something else
16 * (except for the dark bubble in the case of clear bubbles).
17 *
18 * The keys are defined in #define statements, and default thus:
19 *
20 * Up: Q
21 * Down: A
22 * Left: O
23 * Right: P
24 * Quit: G
25 * Retry: H
26 * Switch: [SPACE]
27 *
28 * Switch changes between the dark bubble and the solid box.
29 *
30 * This is the first game ever produced with the Small C compiler -
31 * it was written as a statement saying that it is possible to
32 * write something easily, quickly and efficiently using the
33 * compiler. Hopefully it will be an encouragement for others to
34 * do likewise!
35 *
36 * For your interest I've also included the original Z88 converted
37 * assembler from which I worked - it's quite crude but it just
38 * about functions, the C is much more refined!
39 *
40 * Enough twaddle, enjoy the game and study the source!
41 *
42 * d. 1/12/98
43 *
44 * This is the application version of the program, compile with:
45 *
46 * This example demonstrates the dstar_redrawscreen function which allows
47 * you to supply a routine to redraw the screen and take the load
48 * off of OZ. This is a bad app out of necessity really, you can
49 * write good ones! - Have a look at the file doc/apps.html for
50 * more details.
51 */
52
53 /*
54 * Compiler options, need expanded z88 (cos we use the map), need 5
55 * bad pages and we should write the .asm file in app format
56 */
57 #ifdef __Z88_APPLICATION
58 #pragma -expandz88
59 #pragma redirect redrawscreen = _dstar_redrawscreen
60 #pragma redirect handlecmds = _dstar_handlecmds
61 #endif
62
63
64 /* Call up the required header files */
65
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <graphics.h>
69
70 /* dstar.h contains the levels and "sprite" data */
71
72 #include "dstar.h"
73
74
75 #define NO 0
76 #define MAXLEVEL 25
77 #define STARTLEV 0 /* Start level -1 */
78
79 /* Block numbers.. */
80
81 #define WALL 1
82 #define BUBB 2
83 #define BALL 3
84 #define BOX 4
85
86 /* Key definitions, change these to define your keys! */
87
88 #define K_UP 'Q'
89 #define K_DOWN 'A'
90 #define K_LEFT 'O'
91 #define K_RIGHT 'P'
92 #define K_SWITCH 32
93 #define K_EXIT 'G'
94 #define K_CLEAR 'H'
95
96
97 /* Declare some variables to start off with! */
98
99
100 static unsigned char balloffset; /* Ball position */
101 static unsigned char boxoffset; /* Box position */
102 static unsigned char ballorbox; /* 1 if box, 0 if ball */
103 static unsigned char level; /* Take a guess! */
104 static unsigned char gameon; /* Flag for dstar_redrawscreen */
105 static unsigned char board[144]; /* Level internal map thing */
106 static unsigned char tiscr[1024]; /* Our very own TI86 screen! */
107
108
109 /* Some character arrays to handle window functions */
110 static unsigned char windtitle[]= {
111 1,'7','#','3',32+7,32+1,32+34,32+7,131,
112 1,'2','C','3',1,'4','+','T','U','R',1,'2','J','C',
113 1,'3','@',32,32, /*reset to (0,0)*/
114 'D','S','t','a','r',' ','z','8','8',
115 1,'3','@',32,32 ,1,'2','A',32+34,0
116 };
117
118 static unsigned char baswindres[]= {
119 1,'7','#','3',32,32,32+94,32+8,128,1,'2','C','3',0
120 };
121
122 static unsigned char windclr[]= {
123 1,'2','C','3',
124 1,'3','@',32,32,1,'2','+','B',0
125 };
126
127 /* Used for the map, keep it global so we can close it easily! */
128 static struct window graphics;
129
130 static void myexit();
131 static void playgame();
132 static void setupgame();
133 static void gamekeys();
134 static void left(unsigned char *ptr);
135 static void right(unsigned char *ptr);
136 static void down(unsigned char *ptr);
137 static void up(unsigned char *ptr);
138 static int standardmiddle(unsigned char nextpos);
139 static void setuplevel();
140 void dstar_redrawscreen(void);
141 static void puttiblock(unsigned char spr,int x, int y);
142 static void drawboard();
143 static void doozcopyasm();
144 static int checkfinish();
145
146
147
main()148 int main()
149 {
150 gameon=0;
151 dstar_redrawscreen(); /* Define the windows */
152 playgame(); /* Play the game */
153 myexit(); /* Clean up after ourselves */
154 return 0;
155 }
156
myexit()157 void myexit()
158 {
159 closegfx(&graphics); /* Close the Map window */
160 exit(0); /* Get outta here! */
161 }
162
163
playgame()164 void playgame()
165 {
166 setupgame(); /* Set level to 1, get level etc */
167
168 /* Loop while checkfinish() says we haven't finished! */
169 while ( checkfinish() ) {
170 gamekeys(); /* Scan keys */
171 }
172 }
173
174
175 /* Set some variables up at the start.. */
176
setupgame()177 void setupgame()
178 {
179 ballorbox=NO;
180 level=STARTLEV;
181 setuplevel();
182 }
183
184
gamekeys()185 void gamekeys()
186 {
187 unsigned char *charptr;
188
189 /* Set up a pointer to the variable we want to change (either for
190 * box or for ball
191 */
192 if (ballorbox)
193 charptr=&boxoffset;
194 else
195 charptr=&balloffset;
196
197 switch( getk() ) { /* Use OZ to get the key */
198 case K_DOWN:
199 down(charptr);
200 break;
201 case K_UP:
202 up(charptr);
203 break;
204 case K_RIGHT:
205 right(charptr);
206 break;
207 case K_LEFT:
208 left(charptr);
209 break;
210 case K_SWITCH:
211 ballorbox^=1; /* Toggle ball/box */
212 break;
213 case K_EXIT:
214 myexit();
215 case K_CLEAR:
216 setuplevel();
217 }
218 }
219
220
221 /* Movement functions - all of these are pretty well similar so I
222 * will only comment the first one - it's fairly obvious what is
223 * happening though
224 */
225
left(unsigned char * ptr)226 void left(unsigned char *ptr)
227 {
228 unsigned char *locn;
229
230 while(1) {
231 locn=*(ptr)+board;
232 if (standardmiddle(*(locn-1)) ) return;
233 *(locn-1)=*locn;
234 *locn=0;
235 (*ptr)--; /* ptr is the location of blob */
236 drawboard(); /* Draw screen */
237 }
238 }
239
240
right(unsigned char * ptr)241 void right(unsigned char *ptr)
242 {
243 unsigned char *locn;
244
245 while(1) {
246 locn=*(ptr)+board;
247 if (standardmiddle(*(locn+1)) )
248 return;
249 *(locn+1)=*locn;
250 *locn=0;
251 (*ptr)++;
252 drawboard();
253 }
254 }
255
down(unsigned char * ptr)256 void down(unsigned char *ptr)
257 {
258 unsigned char *locn;
259
260 while(1) {
261 locn=*(ptr)+board;
262 if (standardmiddle(*(locn+16)) )
263 return;
264 *(locn+16)=*locn;
265 *locn=0;
266 (*ptr)+=16;
267 drawboard();
268 }
269 }
270
up(unsigned char * ptr)271 void up(unsigned char *ptr)
272 {
273 unsigned char *locn;
274
275 while(1) {
276 locn=*(ptr)+board;
277 if ( standardmiddle(*(locn-16)) ) return;
278 *(locn-16)=*locn;
279 *locn=0;
280 (*ptr)-=16;
281 drawboard();
282 }
283 }
284
285
286 /* Check to see if we're running into anything, if box is set then
287 * if we hit anything we want to stop, if we're ball then if we
288 * hit anything except for bubble we wanna stop
289 */
290
standardmiddle(unsigned char nextpos)291 int standardmiddle(unsigned char nextpos)
292 {
293 if (ballorbox)
294 return (nextpos); /* For box */
295 else
296 if (nextpos==BUBB || nextpos==NO) return(0);
297 return(1);
298 }
299
300
301
302 /* Check to see if a level is finished
303 * There are 144 squares in each level, note the use of != instead of
304 * </<= - this is quicker to execute on the Z80!
305 */
checkfinish()306 int checkfinish()
307 {
308 unsigned char *ptr;
309 int i;
310 ptr=board;
311 for (i=1; i!=144; i++) {
312 if (*ptr++ == BUBB)
313 return(1);
314 }
315 if ( ++level==MAXLEVEL )
316 return(0); /* Done all the levels!! */
317 setuplevel();
318 return(1);
319 }
320
321 /* Setup a level..the level is stored compressed, taking up 38 bytes a
322 * time.
323 * byte 0 - position of ball
324 * byte 1 - position of box
325 * 2-37 - level data
326 *
327 * Level data is stored as two bits per square, so we have to shift our
328 * picked up byte round to get it
329 */
330
setuplevel()331 void setuplevel()
332 {
333 int y,x;
334 unsigned char *ptr,*ptr2;
335 ptr2=board;
336 ptr=levels+(level*38);
337 /* ptr points to start of level now */
338 /* First two bytes are the ball and the boxes position */
339 balloffset=*ptr++;
340 boxoffset=*ptr++;
341
342 /* Now, decompress level into board */
343 for (y=0; y!=9; y++) {
344 for (x=0; x !=4; x++) {
345 *ptr2++=((*ptr)>>6)&3;
346 *ptr2++=((*ptr)>>4)&3;
347 *ptr2++=((*ptr)>>2)&3;
348 *ptr2++=(*ptr)&3;
349 ptr++;
350 }
351 }
352 /* Now, plot the ball and box into the internal map */
353 ptr2=board;
354 *(ptr2+balloffset)=BALL;
355 *(ptr2+boxoffset)=BOX;
356 gameon=1;
357 drawboard();
358 }
359
360
361
dstar_redrawscreen(void)362 void dstar_redrawscreen(void)
363 {
364 struct window text;
365
366 puts(baswindres);
367 /* Define and open a text window with title and other embellishments
368 * strings are at top for clearing, position etc
369 */
370 text.number='3';
371 text.x=32+7;
372 text.y=32+1;
373 text.width=32+34;
374 text.depth=32+7;
375 text.type=131;
376 text.graph=0;
377 window(&text);
378 fputs(windtitle,stdout);
379 text.x=32+8;
380 text.y=32+3;
381 text.width=32+32;
382 text.depth=32+5;
383 text.type=128;
384 window(&text);
385 fputs(windclr,stdout);
386 /* Now, try to open a map window.. */
387 graphics.graph=1;
388 graphics.width=128;
389 graphics.number='4';
390 if (window(&graphics)) {
391 puts("Sorry, Can't Open Map");
392 sleep(5);
393 exit(0);
394 }
395 /* Sucessfully opened, now dump some info in the text window! */
396 puts(" DStar Z88 - C Demo");
397 fputs("Original TI game By A Von Dollen",stdout);
398 puts(" Converted to Z88 By D Morris");
399 puts(" Keys: Q,A,O,P,SPACE,H,G");
400 if (gameon) drawboard();
401 }
402
403 /* Draw the board, mostly written in C, even though we did take a bit
404 * of a performance hit when it was converted over from asm
405 */
406
drawboard()407 void drawboard()
408 {
409 int x,y;
410 unsigned char *ptr;
411
412 /* We let OZ in here, because we only go back to the loop once our
413 * thing has hit something
414 */
415 getk();
416
417 ptr=board;
418
419 for (y=0;y!=9;y++) {
420 for (x=0;x!=16;x++) {
421 puttiblock((*ptr++),x,y);
422 }
423 }
424 doozcopyasm();
425 }
426
427
428 /* Dump a sprite onto the TI screen we've built
429 * The TI screen is 16 characters wide by 8 deep i.e. half the size
430 * of the Z88's map screen. It's stored line by line (sensible!)
431 *
432 * We enter with y being y/7 and x being x/8 (if we think in pixels)
433 * So for each y we have to step down by 112.
434 * The increment between rows is 16.
435 */
puttiblock(unsigned char spr,int x,int y)436 void puttiblock(unsigned char spr,int x, int y)
437 {
438 unsigned char *ptr2,*ptr;
439 int i;
440
441 y=(y*14)*8;
442 /* So, get where we want to dump our sprite into ptr */
443 ptr=tiscr+y+x;
444 /* Calculate where the sprite is */
445 spr*=8;
446 ptr2=sprites+spr;
447 /* And dump it in there */
448 for (i=0; i!=7;i++) {
449 *ptr=*(ptr2++);
450 ptr+=16;
451 }
452 }
453
454 /* Ooops, forgive me this one bit of assembler in the entire program!
455 * This just copies the TI screen onto the OZ map.
456 *
457 * It really is easier to keep this routine in here, change it into
458 * C if you like..
459 */
460
461
doozcopyasm()462 void doozcopyasm()
463 {
464 #asm
465
466 EXTERN swapgfxbk
467
468 call swapgfxbk
469 call ozscrcpy
470 jp swapgfxbk
471
472 EXTERN base_graphics
473
474 .ozscrcpy
475 ld de,(base_graphics)
476 ld hl,_tiscr
477 ld c,8
478 .ozscrcpy1
479 push hl
480 ld b,16
481 .ozscrcpy3
482 push bc
483 push hl
484 ld bc,16
485 ld a,8
486 .ozscrcpy2
487 ex af,af
488 ld a,(hl)
489 ld (de),a
490 inc de
491 add hl,bc
492 ex af,af
493 dec a
494 jr nz,ozscrcpy2
495 pop hl
496 inc hl
497 pop bc
498 djnz ozscrcpy3
499 pop hl
500 push bc
501 ld bc,16*8
502 add hl,bc
503 pop bc
504 dec c
505 jr nz,ozscrcpy1
506 #endasm
507 }
508
509 /*
510 * Now, some application info, have a look at doc/apps.html to see
511 * what is going on!
512 */
513
514 #ifdef __Z88_APPLICATION
dstar_handlecmds(int cmd)515 void dstar_handlecmds(int cmd)
516 {
517 switch(cmd) {
518 case 0x82:
519 myexit();
520 case 0x81:
521 setuplevel();
522 break;
523 }
524 }
525
526 #include <dor.h>
527
528 #define HELP1 "A demo application made with z88dk - Small C+ Compiler"
529 #define HELP2 "Converted from a TI86 game found with source on"
530 #define HELP3 "www.ticalc.org. Converted to C and Z88 by"
531 #define HELP4 "Dominic Morris <djm@jb.man.ac.uk>"
532 #define HELP5 ""
533 #define HELP6 "v3.0 - 5.4.99 (djm)"
534
535 #define APP_INFO "Made by z88dk"
536 #define APP_KEY 'D'
537 #define APP_NAME "Dstarz88"
538 #define APP_TYPE AT_Bad
539
540 #define TOPIC1 "Commands"
541 #define TOPIC1ATTR TP_Help
542 #define TOPIC1HELP1 "The only menu we have!"
543 #define TOPIC1HELP2 "This contains actions that affect the game"
544
545 #define TOPIC1_1 "Restart"
546 #define TOPIC1_1KEY "CR"
547 #define TOPIC1_1CODE $81
548 #define TOPIC1_1ATTR MN_Help
549 #define TOPIC1_1HELP1 "Use this to restart the level"
550 #define TOPIC1_1HELP2 "For example when you get terrible stuck!"
551
552 #define TOPIC1_2 "Quit"
553 #define TOPIC1_2KEY "CQ"
554 #define TOPIC1_2CODE $82
555 #define TOPIC1_2ATTR MN_Help
556
557 #define TOPIC1_2HELP1 "Use this to quit the game"
558 #define TOPIC1_2HELP2 "Not that you'll ever get bored of the game!"
559
560 #include <application.h>
561 #endif
562 /* THE END! */
563