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