1 /* Tower Toppler - Nebulus
2  * Copyright (C) 2000-2012  Andreas R�ver
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  */
18 
19 #ifndef CREATOR
20 
21 #include "level.h"
22 #include "points.h"
23 #include "archi.h"
24 #include "configuration.h"
25 #include "screen.h"
26 
27 #endif
28 
29 #include "decl.h"
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #define TOWERWID 16
35 
36 /* tower block flags */
37 #define TBF_NONE     0x0000
38 #define TBF_EMPTY    0x0001 /* block is not solid */
39 #define TBF_PLATFORM 0x0002 /* block is a platform */
40 #define TBF_STATION  0x0004 /* block is a lift station */
41 #define TBF_DEADLY   0x0008 /* block is deadly */
42 #define TBF_ROBOT    0x0010 /* block is a robot */
43 
44 struct _tblockdata {
45     const char *nam; /* name */
46     char       ch;   /* representation in saved tower file */
47     Uint16     tf;   /* flags; TBF_foo */
48 } static towerblockdata[NUM_TBLOCKS] = {
49     { "space",            ' ', TBF_EMPTY },
50     { "lift top stop",    'v', TBF_EMPTY|TBF_STATION },
51     { "lift middle stop", '+', TBF_EMPTY|TBF_STATION },
52     { "lift bottom stop",  0,  TBF_EMPTY|TBF_STATION },
53     { "robot 1",          '1', TBF_EMPTY|TBF_DEADLY|TBF_ROBOT },
54     { "robot 2",          '2', TBF_EMPTY|TBF_DEADLY|TBF_ROBOT },
55     { "robot 3",          '3', TBF_EMPTY|TBF_DEADLY|TBF_ROBOT },
56     { "robot 4",          '4', TBF_EMPTY|TBF_DEADLY|TBF_ROBOT },
57     { "robot 5",          '5', TBF_EMPTY|TBF_DEADLY|TBF_ROBOT },
58     { "robot 6",          '6', TBF_EMPTY|TBF_DEADLY|TBF_ROBOT },
59     { "robot 7",          '7', TBF_EMPTY|TBF_DEADLY|TBF_ROBOT },
60     { "stick",            '!', /*TBF_PLATFORM*/ },
61     { "step",             '-', TBF_PLATFORM },
62     { "vanisher step",    '.', TBF_PLATFORM },
63     { "slider > step",    '>', TBF_PLATFORM },
64     { "slider < step",    '<', TBF_PLATFORM },
65     { "box",              'b', TBF_NONE },
66     { "door",             '#', TBF_EMPTY },
67     { "target door",      'T', TBF_EMPTY },
68     { "stick top",         0,  TBF_STATION/*|TBF_PLATFORM*/ },
69     { "stick middle",      0,  TBF_STATION/*|TBF_PLATFORM*/ },
70     { "stick bottom",      0,  TBF_STATION/*|TBF_PLATFORM*/ },
71     { "lift top",          0,  TBF_STATION|TBF_PLATFORM },
72     { "lift middle",       0,  TBF_STATION|TBF_PLATFORM },
73     { "lift bottom",      '^', TBF_STATION|TBF_PLATFORM },
74     { "stick at door",     0,  },
75     { "stick at target",   0,  },
76     { "lift at door",      0 , TBF_STATION|TBF_PLATFORM },
77     { "lift at target",    0 , TBF_STATION|TBF_PLATFORM },
78 };
79 
80 /* Sections in the data files; do not change the order,
81  * and always add new ones to the end, so that we keep
82  * compatibility with old towers/missions.
83  * (the loader silently ignores unrecognized sections)
84  */
85 enum towersection {
86     TSS_END,
87     TSS_TOWERNAME,
88     TSS_TOWERTIME,
89     TSS_TOWERCOLOR,
90     TSS_TOWERDATA,
91     TSS_DEMO,
92     TSS_ROBOT
93 };
94 
95 char tss_string_name[] = "name";
96 char tss_string_time[] = "time";
97 char tss_string_color[] = "color";
98 char tss_string_data[] = "data";
99 char tss_string_demo[] = "demo";
100 char tss_string_robot[] = "robot";
101 
102 static Uint8 * mission = NULL;
103 static Uint8 towerheight;
104 static Uint8 towerrobot;
105 static Uint8 tower[256][TOWERWID];
106 static char towername[TOWERNAMELEN+1];
107 static Uint8 towernumber;
108 static Uint8 towercolor_red, towercolor_green, towercolor_blue;
109 static Uint16 towertime;
110 static Uint16 *towerdemo = NULL;
111 static int towerdemo_len = 0;
112 
113 typedef struct mission_node {
114   char name[30];
115   char fname[100];
116   bool archive;       // is the mission inside the archive, or not
117   Uint8 prio;         // the lower prio, the further in front the mission will be in the list
118   mission_node *next;
119 } mission_node;
120 
121 mission_node * missions;
122 
123 #ifndef CREATOR
124 
missionfiles(const struct dirent * file)125 static int missionfiles (const struct dirent *file)
126 {
127   int len = strlen(file->d_name);
128 
129   return ((len > 4) &&
130           (file->d_name[len - 1] == 'm') &&
131           (file->d_name[len - 2] == 't') &&
132           (file->d_name[len - 3] == 't') &&
133           (file->d_name[len - 4] == '.'));
134 }
135 
136 #endif
137 
conv_char2towercode(wchar_t ch)138 Uint8 conv_char2towercode(wchar_t ch) {
139   if (ch)
140     for (int x = 0; x < NUM_TBLOCKS; x++)
141       // we can do that because we use only chars below 128
142       if (ch == towerblockdata[x].ch) return x;
143   return TB_EMPTY;
144 }
145 
conv_towercode2char(Uint8 code)146 char conv_towercode2char(Uint8 code) {
147   if ((code < NUM_TBLOCKS) && (towerblockdata[code].ch))
148       return towerblockdata[code].ch;
149   return towerblockdata[TB_EMPTY].ch;
150 }
151 
152 
add_mission(const char * fname,bool archive=false)153 static void add_mission(const char *fname, bool archive = false) {
154 
155   char mname[30];
156   Uint8 prio;
157 
158   if (archive) {
159 
160     file f(dataarchive, fname);
161 
162     unsigned char mnamelength;
163     f.read(&mnamelength, 1);
164 
165     if (mnamelength > 29) mnamelength = 29;
166 
167     f.read(mname, mnamelength);
168     mname[mnamelength] = 0;
169     f.read(&prio, 1);
170 
171   } else {
172 
173     FILE * f = fopen(fname, "rb");
174 
175     if (!f) return;
176 
177     unsigned char mnamelength;
178     fread(&mnamelength, 1, 1, f);
179 
180     if (mnamelength > 29) mnamelength = 29;
181 
182     fread(mname, mnamelength, 1, f);
183     mname[mnamelength] = 0;
184     fread(&prio, 1, 1, f);
185     fclose(f);
186   }
187 
188   mission_node * m = missions;
189   mission_node * l = NULL;
190 
191   /* first check if the mission is already there */
192   while (m) {
193 
194     int erg = strcmp(m->name, mname);
195     /* no two missions with the same name */
196     if (erg == 0) {
197       return;
198     }
199     l = m;
200     m = m->next;
201   }
202 
203   m = missions;
204   l = NULL;
205 
206   while (m) {
207 
208     /* we have passed our target, the current mission must
209      * be inserted before this mission
210      */
211     if (m->prio > prio) {
212       mission_node * n = new mission_node;
213       strcpy(n->name, mname);
214       strcpy(n->fname, fname);
215       n->prio = prio;
216       n->next = m;
217       n->archive = archive;
218 
219       if (l)
220         l->next = n;
221       else
222         missions = n;
223 
224       return;
225     }
226     l = m;
227     m = m->next;
228   }
229 
230   /* insert at the end */
231   m = new mission_node;
232   strcpy(m->name, mname);
233   strcpy(m->fname, fname);
234   m->prio = prio;
235   m->next = NULL;
236   m->archive = archive;
237 
238   if (l)
239     l->next = m;
240   else
241     missions = m;
242 
243 }
244 
245 #ifndef CREATOR
246 
lev_findmissions()247 void lev_findmissions() {
248 
249   char pathname[100];
250 
251   struct dirent **eps = NULL;
252 
253   missions = NULL;
254 
255   /* check if already called, if so free the old list */
256   while (missions) {
257     mission_node *n = missions;
258     missions = missions->next;
259     delete n;
260   }
261 
262   /* first check inside the archive */
263 
264   for (int fn = 0; fn < dataarchive->fileNumber(); fn++) {
265     const char * n = dataarchive->fname(fn);
266 
267     int len = strlen(n);
268 
269     if ((len > 4) && (n[len - 1] == 'm') && (n[len - 2] == 't') &&
270         (n[len - 3] == 't') && (n[len - 4] == '.'))
271       add_mission(n, true);
272   }
273 
274 #ifdef WIN32
275   {
276     char n[100];
277     getcwd(n, 100);
278     sprintf(pathname, "%s\\", n);
279   }
280 #else
281   sprintf(pathname, "%s", "./");
282 #endif
283 
284   int n = alpha_scandir(pathname, &eps, missionfiles);
285 
286   if (n >= 0) {
287 
288     for (int i = 0; i < n; i++) {
289 
290       char fname[200];
291       sprintf(fname, "%s%s", pathname, eps[i]->d_name);
292 
293       add_mission(fname);
294 
295       free(eps[i]);
296     }
297   }
298   free(eps);
299   eps = NULL;
300 
301 #ifndef WIN32
302 
303   snprintf(pathname, 100, "%s/.toppler/", homedir());
304   n = alpha_scandir(pathname, &eps, missionfiles);
305 
306   if (n >= 0) {
307 
308     for (int i = 0; i < n; i++) {
309 
310       char fname[200];
311       snprintf(fname, 200, "%s%s", pathname, eps[i]->d_name);
312 
313       add_mission(fname);
314     }
315   }
316   free(eps);
317   eps = NULL;
318 
319   snprintf(pathname, 100, "%s/", TOP_DATADIR);
320   n = alpha_scandir(pathname, &eps, missionfiles);
321 
322   if (n >= 0) {
323 
324     for (int i = 0; i < n; i++) {
325 
326       char fname[200];
327       snprintf(fname, 200, "%s%s", pathname, eps[i]->d_name);
328 
329       add_mission(fname);
330     }
331   }
332   free(eps);
333   eps = NULL;
334 
335 #endif
336 
337 }
338 
339 #endif
340 
lev_done()341 void lev_done() {
342   if (mission) {
343     delete [] mission;
344     mission = NULL;
345   }
346 
347   mission_node * m = missions;
348 
349   while (m) {
350     m = m->next;
351     delete missions;
352     missions = m;
353   }
354 
355   if (towerdemo) delete [] towerdemo;
356 }
357 
358 
lev_missionnumber()359 Uint16 lev_missionnumber() {
360   int num = 0;
361   mission_node * m = missions;
362 
363   while (m) {
364     num++;
365     m = m->next;
366   }
367 
368   return num;
369 }
370 
lev_missionname(Uint16 num)371 const char * lev_missionname(Uint16 num) {
372   mission_node * m = missions;
373 
374   while (num) {
375     m = m->next;
376     num--;
377   }
378 
379   return m->name;
380 }
381 
lev_loadmission(Uint16 num)382 bool lev_loadmission(Uint16 num) {
383 
384   mission_node *m = missions;
385   while (num) {
386     num--;
387     m = m->next;
388   }
389 
390   if (mission) delete [] mission;
391 
392   if (m->archive) {
393 
394     file f(dataarchive, m->fname);
395     int fsize = f.size();
396 
397     mission = new unsigned char[fsize];
398     f.read(mission, fsize);
399 
400   } else {
401 
402     FILE * in = fopen(m->fname, "rb");
403 
404     /* find out file size */
405     fseek(in, 0, SEEK_END);
406     int fsize = ftell(in);
407 
408     /* get enough memory and load the whole file into memory */
409     mission = new unsigned char[fsize];
410     fseek(in, 0, SEEK_SET);
411     fread(mission, fsize, 1, in);
412 
413     fclose(in);
414   }
415 
416   for (int t = 0; t < lev_towercount(); t++) {
417     lev_selecttower(t);
418     for (int r = 0; r < towerheight; r++)
419       for (int c = 0; c < TOWERWID; c++)
420         if (tower[r][c] >= NUM_TBLOCKS)
421           return false;
422   }
423 
424   return true;
425 }
426 
lev_towercount(void)427 Uint8 lev_towercount(void) {
428   return mission[mission[0] + 2];
429 }
430 
lev_selecttower(Uint8 number)431 void lev_selecttower(Uint8 number) {
432 
433   Uint32 towerstart;
434 
435   towernumber = number;
436   towerrobot = number;
437   Uint8 section;
438   Uint32 section_len;
439 
440   lev_set_towerdemo(0, NULL);
441 
442   // find start of towerdata in mission
443   {
444     Uint32 idxpos = 0;
445 
446     idxpos += mission[mission[0] + 3];
447     idxpos += long(mission[mission[0] + 4]) << 8;
448     idxpos += long(mission[mission[0] + 5]) << 16;
449     idxpos += long(mission[mission[0] + 6]) << 24;
450 
451     towerstart = mission[idxpos + 4 * number];
452     towerstart += long(mission[idxpos + 4 * number + 1]) << 8;
453     towerstart += long(mission[idxpos + 4 * number + 2]) << 16;
454     towerstart += long(mission[idxpos + 4 * number + 3]) << 24;
455   }
456 
457   do {
458     section = mission[towerstart++];
459     section_len = mission[towerstart++];
460     section_len += Uint32(mission[towerstart++]) << 8;
461     section_len += Uint32(mission[towerstart++]) << 16;
462     section_len += Uint32(mission[towerstart++]) << 24;
463     switch ((towersection)section) {
464     case TSS_TOWERNAME:
465       memmove(towername, &mission[towerstart], section_len);
466       towername[section_len] = 0;
467       break;
468     case TSS_TOWERTIME:
469       towertime = mission[towerstart] + (int(mission[towerstart + 1]) << 8);
470       break;
471     case TSS_TOWERCOLOR:
472       towercolor_red = mission[towerstart];
473       towercolor_green = mission[towerstart + 1];
474       towercolor_blue = mission[towerstart + 2];
475       break;
476     case TSS_TOWERDATA:
477       {
478         towerheight = mission[towerstart];
479 
480         Uint32 bitstart = towerstart + 1;
481         Uint32 bytestart = bitstart + 2 * towerheight;
482         Uint16 wpos = 0;
483         Uint16 bpos = 0;
484 
485         lev_clear_tower();
486 
487         for (Uint8 row = 0; row < towerheight; row++) {
488           for (Uint8 col = 0; col < TOWERWID; col++) {
489             if ((mission[bitstart + (bpos >> 3)] << (bpos & 7)) & 0x80)
490               tower[row][col] = mission[bytestart + wpos++];
491             else
492               tower[row][col] = 0;
493             bpos++;
494           }
495         }
496         break;
497       }
498     case TSS_DEMO:
499       {
500         // get tower demo
501         Uint16 *tmpbuf = NULL;
502         Uint16 tmpbuf_len = mission[towerstart];
503         tmpbuf_len += Uint16(mission[towerstart+1]) << 8;
504         Uint16 ofs = 2;
505 
506         if (tmpbuf_len) {
507           tmpbuf = new Uint16[tmpbuf_len];
508           Uint16 idx = 0;
509           while (idx < tmpbuf_len) {
510             Uint8 run = mission[towerstart + ofs++];
511             Uint16 data = mission[towerstart + ofs++];
512             data += Uint16(mission[towerstart + ofs++]) << 8;
513 
514             while (run) {
515               tmpbuf[idx++] = data;
516               run--;
517             }
518           }
519         }
520 
521         lev_set_towerdemo(tmpbuf_len, tmpbuf);
522         break;
523       }
524     case TSS_ROBOT:
525       towerrobot = mission[towerstart];
526 #ifndef CREATOR
527       towerrobot %= scr_numrobots();
528 #endif
529       break;
530     case TSS_END:
531     default:      break;
532     }
533     towerstart += section_len;
534   } while ((towersection)section != TSS_END);
535 }
536 
537 char *
gen_passwd(int pwlen,const char * allowed,int buflen,char * buf)538 gen_passwd(int pwlen, const char *allowed, int buflen, char *buf)
539 {
540   static char passwd[PASSWORD_LEN + 1];
541   int len = buflen;
542   int alen;
543   int i;
544 
545   if (!allowed) return NULL;
546 
547   alen = strlen(allowed);
548 
549   if (pwlen > PASSWORD_LEN) pwlen = PASSWORD_LEN;
550 
551   if (buflen < (pwlen*5)) len = pwlen*5;
552 
553   (void)memset(passwd, 0, PASSWORD_LEN);
554 
555   for (i = 0; i < len; i++) {
556     passwd[i % pwlen] += buf[i % buflen];
557     if (passwd[i % pwlen] > alen) passwd[(i+1) % pwlen]++;
558   }
559 
560   for (i = 0; i < pwlen; i++)
561     passwd[i] = allowed[abs(passwd[i]) % alen];
562 
563   passwd[pwlen] = '\0';
564 
565   return passwd;
566 }
567 
lev_get_passwd(void)568 char *lev_get_passwd(void) {
569     return gen_passwd(PASSWORD_LEN, PASSWORD_CHARS, 256*TOWERWID, (char *)tower);
570 }
571 
lev_show_passwd(int levnum)572 bool lev_show_passwd(int levnum) {
573   return ((levnum > 0) &&
574           (levnum < lev_towercount()) &&
575           ((levnum % 3) == 0));
576 }
577 
lev_tower_passwd_entry(const char * passwd)578 int lev_tower_passwd_entry(const char *passwd) {
579   int i;
580   if (!passwd) return 0;
581   for (i = 0; i < lev_towercount(); i++) {
582     lev_selecttower(i);
583     if (!strcmp(passwd,lev_get_passwd())) return i;
584   }
585   return 0;
586 }
587 
lev_clear_tower(void)588 void lev_clear_tower(void) {
589     memset(&tower, TB_EMPTY, 256*TOWERWID);
590 }
591 
lev_set_towercol(Uint8 r,Uint8 g,Uint8 b)592 void lev_set_towercol(Uint8 r, Uint8 g, Uint8 b) {
593   towercolor_red = r;
594   towercolor_green = g;
595   towercolor_blue = b;
596 }
597 
lev_towercol_red()598 Uint8 lev_towercol_red() {
599   return towercolor_red;
600 }
601 
lev_towercol_green()602 Uint8 lev_towercol_green() {
603   return towercolor_green;
604 }
605 
lev_towercol_blue()606 Uint8 lev_towercol_blue() {
607   return towercolor_blue;
608 }
609 
lev_tower(Uint16 row,Uint8 column)610 Uint8 lev_tower(Uint16 row, Uint8 column) {
611   return tower[row][column];
612 }
613 
lev_set_tower(Uint16 row,Uint8 column,Uint8 block)614 Uint8 lev_set_tower(Uint16 row, Uint8 column, Uint8 block) {
615     Uint8 tmp = tower[row][column];
616     tower[row][column] = block;
617     return tmp;
618 }
619 
lev_towerrows(void)620 Uint8 lev_towerrows(void) {
621   return towerheight;
622 }
623 
lev_towername(void)624 char * lev_towername(void) {
625   return towername;
626 }
627 
lev_set_towerdemo(int demolen,Uint16 * demobuf)628 void lev_set_towerdemo(int demolen, Uint16 *demobuf) {
629     if (towerdemo) delete [] towerdemo;
630     towerdemo = demobuf;
631     towerdemo_len = demolen;
632 }
633 
lev_get_towerdemo(int & demolen,Uint16 * & demobuf)634 void lev_get_towerdemo(int &demolen, Uint16 *&demobuf) {
635     demobuf = towerdemo;
636     demolen = towerdemo_len;
637 }
638 
lev_set_towername(const char * str)639 void lev_set_towername(const char *str) {
640     (void) strncpy(towername, str, TOWERNAMELEN);
641     towername[TOWERNAMELEN] = '\0';
642 }
643 
lev_towernr(void)644 Uint8 lev_towernr(void) {
645   return towernumber;
646 }
647 
lev_lasttower(void)648 bool lev_lasttower(void) {
649   return (towernumber+1) == lev_towercount();
650 }
651 
lev_robotnr(void)652 Uint8 lev_robotnr(void) {
653   return towerrobot;
654 }
655 
lev_set_robotnr(Uint8 robot)656 void lev_set_robotnr(Uint8 robot) {
657   towerrobot = robot;
658 }
659 
lev_towertime(void)660 Uint16 lev_towertime(void) {
661   return towertime;
662 }
663 
lev_set_towertime(Uint16 time)664 void lev_set_towertime(Uint16 time) {
665   towertime = time;
666 }
667 
lev_removelayer(Uint8 layer)668 void lev_removelayer(Uint8 layer) {
669   while (layer < towerheight) {
670     for (Uint8 c = 0; c < TOWERWID; c++)
671       tower[layer][c] = tower[layer + 1][c];
672     layer++;
673   }
674 
675   towerheight--;
676 }
677 
678 /* empties a cell in the tower */
lev_clear(int row,int col)679 void lev_clear(int row, int col) {
680   tower[row][col] = TB_EMPTY;
681 }
682 
683 /* if the given position contains a vanishing step, remove it */
lev_removevanishstep(int row,int col)684 void lev_removevanishstep(int row, int col) {
685   if (tower[row][col] == TB_STEP_VANISHER)
686     tower[row][col] = TB_EMPTY;
687 }
688 
689 /********** everything for doors ********/
690 
691 /* returns true, if the given position is the upper end of a door
692  (a door is always 3 layers) */
lev_is_door_upperend(int row,int col)693 bool lev_is_door_upperend(int row, int col) {
694   return lev_is_door(row, col) &&
695     lev_is_door(row + 1, col) &&
696     lev_is_door(row + 2, col);
697 }
698 
699 /* returns true if the given position contains a door */
lev_is_door(int row,int col)700 bool lev_is_door(int row, int col) {
701   return (tower[row][col] == TB_DOOR ||
702           tower[row][col] == TB_DOOR_TARGET ||
703           tower[row][col] == TB_STICK_DOOR);
704 }
705 
706 /* returns true, if the given fiels contains a target door */
lev_is_targetdoor(int row,int col)707 bool lev_is_targetdoor(int row, int col) {
708   return tower[row][col] == TB_DOOR_TARGET;
709 }
710 
711 /**************** everything for elevators ******************/
712 
lev_is_station(int row,int col)713 bool lev_is_station(int row, int col) {
714   return ((towerblockdata[tower[row][col]].tf & TBF_STATION) != 0);
715 }
lev_is_up_station(int row,int col)716 bool lev_is_up_station(int row, int col) {
717   return ((tower[row][col] == TB_ELEV_BOTTOM) ||
718           (tower[row][col] == TB_ELEV_MIDDLE));
719 }
lev_is_down_station(int row,int col)720 bool lev_is_down_station(int row, int col) {
721   return ((tower[row][col] == TB_ELEV_TOP) ||
722           (tower[row][col] == TB_ELEV_MIDDLE));
723 }
lev_is_bottom_station(int row,int col)724 bool lev_is_bottom_station(int row, int col) {
725    return (tower[row][col] == TB_ELEV_BOTTOM);
726 }
727 
lev_is_platform(int row,int col)728 bool lev_is_platform(int row, int col) {
729   return ((towerblockdata[tower[row][col]].tf & TBF_PLATFORM) != 0);
730 }
lev_is_stick(int row,int col)731 bool lev_is_stick(int row, int col) {
732   return ((tower[row][col] == TB_STICK) ||
733           (tower[row][col] == TB_STICK_TOP) ||
734           (tower[row][col] == TB_STICK_MIDDLE) ||
735           (tower[row][col] == TB_STICK_BOTTOM) ||
736           (tower[row][col] == TB_STICK_DOOR) ||
737           (tower[row][col] == TB_STICK_DOOR_TARGET));
738 }
739 
lev_is_elevator(int row,int col)740 bool lev_is_elevator(int row, int col) {
741   return ((tower[row][col] == TB_STICK_BOTTOM) ||
742           (tower[row][col] == TB_STICK_MIDDLE) ||
743           (tower[row][col] == TB_STICK_TOP) ||
744           (tower[row][col] == TB_ELEV_BOTTOM) ||
745           (tower[row][col] == TB_ELEV_MIDDLE) ||
746           (tower[row][col] == TB_ELEV_TOP));
747 }
748 
lev_platform2stick(int row,int col)749 void lev_platform2stick(int row, int col) {
750   if (tower[row][col] == TB_ELEV_TOP) tower[row][col] = TB_STICK_TOP;
751   else if (tower[row][col] == TB_ELEV_MIDDLE) tower[row][col] = TB_STICK_MIDDLE;
752   else if (tower[row][col] == TB_ELEV_BOTTOM) tower[row][col] = TB_STICK_BOTTOM;
753   else if (tower[row][col] == TB_STEP) tower[row][col] = TB_STICK;
754 }
lev_stick2platform(int row,int col)755 void lev_stick2platform(int row, int col) {
756   if (tower[row][col] == TB_STICK_TOP) tower[row][col] = TB_ELEV_TOP;
757   else if (tower[row][col] == TB_STICK_MIDDLE) tower[row][col] = TB_ELEV_MIDDLE;
758   else if (tower[row][col] == TB_STICK_BOTTOM) tower[row][col] = TB_ELEV_BOTTOM;
759   else if (tower[row][col] == TB_STICK_DOOR) tower[row][col] = TB_ELEV_DOOR;
760   else if (tower[row][col] == TB_STICK_DOOR_TARGET) tower[row][col] = TB_ELEV_DOOR_TARGET;
761   else if (tower[row][col] == TB_STICK) tower[row][col] = TB_STEP;
762 }
lev_stick2empty(int row,int col)763 void lev_stick2empty(int row, int col) {
764   if (tower[row][col] == TB_STICK_TOP) tower[row][col] = TB_STATION_TOP;
765   else if (tower[row][col] == TB_STICK_MIDDLE) tower[row][col] = TB_STATION_MIDDLE;
766   else if (tower[row][col] == TB_STICK_BOTTOM) tower[row][col] = TB_STATION_BOTTOM;
767   else if (tower[row][col] == TB_STICK_DOOR_TARGET) tower[row][col] = TB_DOOR_TARGET;
768   else if (tower[row][col] == TB_STICK_DOOR) tower[row][col] = TB_DOOR;
769   else if (tower[row][col] == TB_STICK) tower[row][col] = TB_EMPTY;
770 }
lev_empty2stick(int row,int col)771 void lev_empty2stick(int row, int col) {
772   if (tower[row][col] == TB_STATION_TOP) tower[row][col] = TB_STICK_TOP;
773   else if (tower[row][col] == TB_STATION_MIDDLE) tower[row][col] = TB_STICK_MIDDLE;
774   else if (tower[row][col] == TB_STATION_BOTTOM) tower[row][col] = TB_STICK_BOTTOM;
775   else if (tower[row][col] == TB_DOOR) tower[row][col] = TB_STICK_DOOR;
776   else if (tower[row][col] == TB_DOOR_TARGET) tower[row][col] = TB_STICK_DOOR_TARGET;
777   else if (tower[row][col] == TB_EMPTY) tower[row][col] = TB_STICK;
778 }
lev_platform2empty(int row,int col)779 void lev_platform2empty(int row, int col) {
780   if (tower[row][col] == TB_ELEV_TOP) tower[row][col] = TB_STATION_TOP;
781   else if (tower[row][col] == TB_ELEV_MIDDLE) tower[row][col] = TB_STATION_MIDDLE;
782   else if (tower[row][col] == TB_ELEV_BOTTOM) tower[row][col] = TB_STATION_BOTTOM;
783   else if (tower[row][col] == TB_ELEV_DOOR_TARGET) tower[row][col] = TB_DOOR_TARGET;
784   else if (tower[row][col] == TB_ELEV_DOOR) tower[row][col] = TB_DOOR;
785   else if (tower[row][col] == TB_STEP) tower[row][col] = TB_EMPTY;
786 }
787 
788 /* misc questions */
lev_is_empty(int row,int col)789 bool lev_is_empty(int row, int col) {
790   return ((towerblockdata[tower[row][col]].tf & TBF_EMPTY));
791 }
792 
lev_is_box(int row,int col)793 bool lev_is_box(int row, int col) {
794   return tower[row][col] == TB_BOX;
795 }
796 
lev_is_sliding(int row,int col)797 int lev_is_sliding(int row, int col) {
798     return ((tower[row][col] == TB_STEP_LSLIDER) ? 1 :
799             (tower[row][col] == TB_STEP_RSLIDER) ? -1 :
800             0);
801 }
802 
lev_is_robot(int row,int col)803 bool lev_is_robot(int row, int col) {
804   return ((towerblockdata[tower[row][col]].tf & TBF_ROBOT) != 0);
805 }
806 
807 
inside_cyclic_intervall(int x,int start,int end,int cycle)808 static bool inside_cyclic_intervall(int x, int start, int end, int cycle) {
809 
810   while (x < start) x += cycle;
811   while (x >= end) x -= cycle;
812 
813   return (x >= start) && (x < end);
814 }
815 
816 #ifndef CREATOR
817 
818 /* returns true, if the given figure can be at the given position
819  without colliding with fixed objects of the tower */
lev_testfigure(long angle,long vert,long back,long fore,long typ,long height,long width)820 bool lev_testfigure(long angle, long vert, long back,
821                     long fore, long typ, long height, long width) {
822   long hinten, vorn, y, x = 0, k, t;
823 
824   hinten = ((angle + back) >> 3) & 0xf;
825   vorn = (((angle + fore) >> 3) + 1) & 0xf;
826 
827   y = vert / 4;
828   vert &= 3;
829 
830   switch (typ) {
831 
832     case 0:  /* toppler */
833       x = (vert == 3) ? 3 : 2;
834       break;
835 
836     case 1:  /* robot */
837       x = (vert == 0) ? 1 : 2;
838       break;
839 
840     case 2:  /* snowball */
841       x = (vert == 0) ? 0 : 1;
842       break;
843   }
844 
845   do {
846     k = x;
847     do {
848       if (lev_is_platform(k + y, hinten)) {
849         return false;
850       } else if (lev_is_stick(k + y, hinten)) {
851         t = hinten * 8 + height;
852         if (inside_cyclic_intervall(angle, t, t+width, 0x80))
853           return false;
854       } else if (lev_is_box(k + y, hinten)) {
855         t = hinten * 8 + height;
856         if (inside_cyclic_intervall(angle, t, t+width, 0x80)) {
857           if (typ == 2) {
858             // the snowball removes the box
859             lev_clear(k + y, hinten);
860             pts_add(50);
861           }
862           return false;
863         }
864       }
865       k--;
866     } while (k != -1);
867     hinten = (hinten + 1) & 0xf;
868   } while (hinten != vorn);
869 
870   return true;
871 }
872 
873 #endif
874 
lev_putplatform(int row,int col)875 unsigned char lev_putplatform(int row, int col) {
876   unsigned char erg = tower[row][col];
877 
878   tower[row][col] = TB_ELEV_BOTTOM;
879 
880   return erg;
881 }
882 
lev_restore(int row,int col,unsigned char bg)883 void lev_restore(int row, int col, unsigned char bg) {
884   tower[row][col] = bg;
885 }
886 
887 
888 /* load and save a tower */
lev_loadtower(const char * fname)889 bool lev_loadtower(const char *fname) {
890   FILE *in = open_local_data_file(fname);
891   char line[200];
892 
893   if (in == NULL) return false;
894 
895   lev_clear_tower();
896   lev_set_towerdemo(0, NULL);
897   towertime = 0;
898   towerheight = 0;
899   towerrobot = 0;
900 
901   while (true) {
902 
903     if (feof(in)) break;
904 
905     /* look for next section start */
906     fgets(line, 200, in);
907     if (line[0] != '[')
908       continue;
909 
910     if (strncmp(&line[1], tss_string_name, strlen(tss_string_name)) == 0) {
911       fgets(towername, TOWERNAMELEN+1, in);
912       /* remove not allowed characters */
913       {
914         int inp = 0;
915         int outp = 0;
916         while(towername[inp]) {
917           if ((towername[inp] >= 32) && (towername[inp] < 127))
918             towername[outp++] = towername[inp];
919           inp++;
920         }
921         towername[outp] = 0;
922       }
923     } else if (strncmp(&line[1], tss_string_color, strlen(tss_string_color)) == 0) {
924       fgets(line, 200, in);
925       sscanf(line, "%hhu, %hhu, %hhu\n", &towercolor_red, &towercolor_green, &towercolor_blue);
926     } else if (strncmp(&line[1], tss_string_time, strlen(tss_string_time)) == 0) {
927       fgets(line, 200, in);
928       sscanf(line, "%hu\n", &towertime);
929     } else if (strncmp(&line[1], tss_string_data, strlen(tss_string_data)) == 0) {
930 
931       fgets(line, 200, in);
932       sscanf(line, "%hhu\n", &towerheight);
933 
934       for (int row = towerheight - 1; row >= 0; row--) {
935 
936         fgets(line, 200, in);
937 
938         for (int col = 0; col < TOWERWID; col++)
939           tower[row][col] = conv_char2towercode(line[col]);
940       }
941     } else if (strncmp(&line[1], tss_string_demo, strlen(tss_string_demo)) == 0) {
942       if (fgets(line, 200, in)) {
943           sscanf(line, "%i\n", &towerdemo_len);
944 
945           if (towerdemo_len > 0) {
946               towerdemo = new Uint16[towerdemo_len];
947 
948               for (int idx = 0; idx < towerdemo_len; idx++) {
949                   fgets(line, 200, in);
950                   sscanf(line, "%hu\n", &towerdemo[idx]);
951               }
952           } else towerdemo = NULL;
953       }
954     } else if (strncmp(&line[1], tss_string_robot, strlen(tss_string_robot)) == 0) {
955       fgets(line, 200, in);
956       {
957         int i;
958         sscanf(line, "%u\n", &i);
959 #ifdef CREATOR
960         towerrobot = i & 0xFF;
961 #else
962         towerrobot = (i & 0xFF) % scr_numrobots();
963 #endif
964       }
965     }
966   }
967 
968   fclose(in);
969   return true;
970 }
971 
972 #ifndef CREATOR
973 
lev_savetower(const char * fname)974 bool lev_savetower(const char *fname) {
975   FILE *out = create_local_data_file(fname);
976 
977   if (out == NULL) return false;
978 
979   fprintf(out, "[%s]\n", tss_string_name);
980   fprintf(out, "%s\n", towername);
981 
982   fprintf(out, "[%s]\n", tss_string_color);
983   fprintf(out, "%hhu, %hhu, %hhu\n", towercolor_red, towercolor_green, towercolor_blue);
984 
985   fprintf(out, "[%s]\n", tss_string_time);
986   fprintf(out, "%hu\n", towertime);
987 
988   fprintf(out, "[%s]\n", tss_string_robot);
989   fprintf(out, "%hhu\n", towerrobot);
990 
991   fprintf(out, "[%s]\n", tss_string_data);
992   fprintf(out, "%hhu\n", towerheight);
993   for (int row = towerheight - 1; row >= 0; row--) {
994     char line[TOWERWID+2];
995 
996     for (int col = 0; col < TOWERWID; col++)
997       line[col] = conv_towercode2char(tower[row][col]);
998 
999     line[TOWERWID] = '|';
1000     line[TOWERWID+1] = 0;
1001     fprintf(out, "%s\n", line);
1002   }
1003 
1004   fprintf(out, "[%s]\n", tss_string_demo);
1005   fprintf(out, "%i\n", towerdemo_len);
1006   if (towerdemo && (towerdemo_len > 0)) {
1007     for (int idx = 0; idx < towerdemo_len; idx++) {
1008       fprintf(out, "%hu\n", towerdemo[idx]);
1009     }
1010   }
1011 
1012   fclose(out);
1013 
1014   return true;
1015 }
1016 
1017 #endif
1018 
1019 /* rotate row clock and counter clockwise */
lev_rotaterow(int row,bool clockwise)1020 void lev_rotaterow(int row, bool clockwise) {
1021   if (clockwise) {
1022     int k = tower[row][0];
1023     for (int i = 1; i < TOWERWID; i++)
1024       tower[row][i - 1] = tower[row][i];
1025     tower[row][TOWERWID-1] = k;
1026   } else {
1027     int k = tower[row][TOWERWID-1];
1028     for (int i = TOWERWID-1; i >= 0; i++)
1029       tower[row][i] = tower[row][i - 1];
1030     tower[row][0] = k;
1031   }
1032 }
1033 
1034 /* insert and delete one row */
lev_insertrow(int position)1035 void lev_insertrow(int position) {
1036   if ((towerheight < 255) && (position < towerheight)) {
1037     int k = towerheight - 1;
1038     while (k >= position) {
1039       for (int i = 0; i < TOWERWID; i++)
1040         tower[k + 1][i] = tower[k][i];
1041       k--;
1042     }
1043     for (int i = 0; i < TOWERWID; i++)
1044       tower[position][i] = 0;
1045     towerheight++;
1046     return;
1047   }
1048   if (towerheight == 0) {
1049     for (int i = 0; i < TOWERWID; i++)
1050       tower[0][i] = 0;
1051     towerheight = 1;
1052   }
1053 }
1054 
lev_deleterow(int position)1055 void lev_deleterow(int position) {
1056   if ((position < towerheight) && (position >= 0)) {
1057     int k = position + 1;
1058     while (k < towerheight) {
1059       for (int i = 0; i < TOWERWID; i++)
1060         tower[k - 1][i] = tower[k][i];
1061       k++;
1062     }
1063     towerheight--;
1064   }
1065 }
1066 
lev_new(Uint8 hei)1067 void lev_new(Uint8 hei) {
1068   towerheight = hei;
1069   lev_clear_tower();
1070 }
1071 
lev_putspace(int row,int col)1072 void lev_putspace(int row, int col) {
1073 
1074   // always delete the whole door
1075   if (lev_is_door(row, col)) {
1076     int r = row - 1;
1077     while (lev_is_door(r, col)) {
1078       tower[r][col] = TB_EMPTY;
1079       r--;
1080     }
1081     r = row + 1;
1082     while (lev_is_door(r, col)) {
1083       tower[r][col] = TB_EMPTY;
1084       r++;
1085     }
1086   }
1087   tower[row][col] = TB_EMPTY;
1088 }
lev_putrobot1(int row,int col)1089 void lev_putrobot1(int row, int col) { tower[row][col] = TB_ROBOT1; }
lev_putrobot2(int row,int col)1090 void lev_putrobot2(int row, int col) { tower[row][col] = TB_ROBOT2; }
lev_putrobot3(int row,int col)1091 void lev_putrobot3(int row, int col) { tower[row][col] = TB_ROBOT3; }
lev_putrobot4(int row,int col)1092 void lev_putrobot4(int row, int col) { tower[row][col] = TB_ROBOT4; }
lev_putrobot5(int row,int col)1093 void lev_putrobot5(int row, int col) { tower[row][col] = TB_ROBOT5; }
lev_putrobot6(int row,int col)1094 void lev_putrobot6(int row, int col) { tower[row][col] = TB_ROBOT6; }
lev_putrobot7(int row,int col)1095 void lev_putrobot7(int row, int col) { tower[row][col] = TB_ROBOT7; }
lev_putstep(int row,int col)1096 void lev_putstep(int row, int col) { tower[row][col] = TB_STEP; }
lev_putvanishingstep(int row,int col)1097 void lev_putvanishingstep(int row, int col) { tower[row][col] = TB_STEP_VANISHER; }
lev_putslidingstep_left(int row,int col)1098 void lev_putslidingstep_left(int row, int col) { tower[row][col] = TB_STEP_LSLIDER; }
lev_putslidingstep_right(int row,int col)1099 void lev_putslidingstep_right(int row, int col) { tower[row][col] = TB_STEP_RSLIDER; }
1100 
lev_putdoor(int row,int col)1101 void lev_putdoor(int row, int col) {
1102 
1103   if (row + 2 < towerheight) {
1104 
1105     tower[row][col] = TB_DOOR;
1106     tower[row + 1][col] = TB_DOOR;
1107     tower[row + 2][col] = TB_DOOR;
1108 
1109     if ((tower[row][(col + (TOWERWID/2)) % TOWERWID] == 0) &&
1110         (tower[row + 1][(col + (TOWERWID/2)) % TOWERWID] == 0) &&
1111         (tower[row + 2][(col + (TOWERWID/2)) % TOWERWID] == 0)) {
1112       tower[row][(col + (TOWERWID/2)) % TOWERWID] = TB_DOOR;
1113       tower[row + 1][(col + (TOWERWID/2)) % TOWERWID] = TB_DOOR;
1114       tower[row + 2][(col + (TOWERWID/2)) % TOWERWID] = TB_DOOR;
1115     }
1116   }
1117 }
1118 
lev_puttarget(int row,int col)1119 void lev_puttarget(int row, int col) {
1120   if (row + 2 < towerheight) {
1121     tower[row][col] = TB_DOOR_TARGET;
1122     tower[row + 1][col] = TB_DOOR_TARGET;
1123     tower[row + 2][col] = TB_DOOR_TARGET;
1124   }
1125 }
1126 
lev_putstick(int row,int col)1127 void lev_putstick(int row, int col) { tower[row][col] = TB_STICK; }
lev_putbox(int row,int col)1128 void lev_putbox(int row, int col) { tower[row][col] = TB_BOX; }
lev_putelevator(int row,int col)1129 void lev_putelevator(int row, int col) { tower[row][col] = TB_ELEV_BOTTOM; }
lev_putmiddlestation(int row,int col)1130 void lev_putmiddlestation(int row, int col) { tower[row][col] = TB_STATION_MIDDLE; }
lev_puttopstation(int row,int col)1131 void lev_puttopstation(int row, int col) { tower[row][col] = TB_STATION_TOP; }
1132 
1133 
lev_save(unsigned char * & data)1134 void lev_save(unsigned char *&data) {
1135   data = new unsigned char[256*TOWERWID+1];
1136 
1137   data[0] = towerheight;
1138   memmove(&data[1], tower, 256 * TOWERWID);
1139 }
1140 
lev_restore(unsigned char * & data)1141 void lev_restore(unsigned char *&data) {
1142   memmove(tower, &data[1], 256 * TOWERWID);
1143   towerheight = data[0];
1144 
1145   delete [] data;
1146 }
1147 
lev_is_consistent(int & row,int & col)1148 lev_problem lev_is_consistent(int &row, int &col) {
1149 
1150   int y;
1151   bool has_exit = false;
1152   // check first, if the starting point is correctly organized
1153   // so that there is no obstacle and we can survive there
1154   if ((tower[1][0] != TB_STICK) && (tower[1][0] != TB_STEP) &&
1155        (tower[1][0] != TB_STEP_LSLIDER) && (tower[1][0] != TB_STEP_RSLIDER) &&
1156        (tower[1][0] != TB_BOX) &&
1157        (tower[1][0] != TB_ELEV_BOTTOM) &&
1158        (tower[0][0] != TB_STICK) && (tower[0][0] != TB_STEP) &&
1159        (tower[0][0] != TB_STEP_LSLIDER) && (tower[0][0] != TB_STEP_RSLIDER) &&
1160        (tower[0][0] != TB_BOX) &&
1161        (tower[0][0] != TB_ELEV_BOTTOM)) {
1162     row = 1;
1163     col = 0;
1164     return TPROB_NOSTARTSTEP;
1165   }
1166   for (y = 2; y < 5; y++)
1167     if ((towerblockdata[tower[y][0]].tf & TBF_DEADLY) ||
1168         !(towerblockdata[tower[y][0]].tf & TBF_EMPTY)) {
1169       row = y;
1170       col = 0;
1171       return TPROB_STARTBLOCKED;
1172     }
1173 
1174   if (towerheight < 4) return TPROB_SHORTTOWER;
1175 
1176   for (int r = 0; r < towerheight; r++)
1177     for (int c = 0; c < TOWERWID; c++) {
1178       // check for undefined symbols
1179       if (tower[r][c] >= NUM_TBLOCKS) {
1180           row = r;
1181           col = c;
1182           return TPROB_UNDEFBLOCK;
1183       }
1184 
1185       // check if elevators always have an opposing end without unremovable
1186       // obstacles
1187       if (tower[r][c] == TB_ELEV_BOTTOM) {
1188         int d = r + 1;
1189         while ((tower[d][c] != TB_STATION_TOP) && (d < towerheight)) {
1190           if ((tower[d][c] != TB_EMPTY) &&
1191               (tower[d][c] != TB_ROBOT1) &&
1192               (tower[d][c] != TB_ROBOT2) &&
1193               (tower[d][c] != TB_ROBOT3) &&
1194               (tower[d][c] != TB_ROBOT4) &&
1195               (tower[d][c] != TB_ROBOT5) &&
1196               (tower[d][c] != TB_ROBOT6) &&
1197               (tower[d][c] != TB_ROBOT7) &&
1198               (tower[d][c] != TB_BOX) &&
1199               (tower[d][c] != TB_STATION_MIDDLE) &&
1200               (tower[d][c] != TB_STEP_VANISHER) &&
1201               (tower[d][c] != TB_DOOR) &&
1202               (tower[d][c] != TB_DOOR_TARGET)) {
1203             row = r;
1204             col = c;
1205             return TPROB_ELEVATORBLOCKED;
1206           }
1207           d++;
1208         }
1209         if (d >= towerheight) {
1210           row = r;
1211           col = c;
1212           return TPROB_NOELEVATORSTOP;
1213         }
1214       }
1215 
1216       if (tower[r][c] == TB_STATION_MIDDLE) {
1217         int d = r + 1;
1218         while ((tower[d][c] != TB_STATION_TOP) && (d < towerheight)) {
1219           if ((tower[d][c] != TB_EMPTY) &&
1220               (tower[d][c] != TB_ROBOT1) &&
1221               (tower[d][c] != TB_ROBOT2) &&
1222               (tower[d][c] != TB_ROBOT3) &&
1223               (tower[d][c] != TB_ROBOT4) &&
1224               (tower[d][c] != TB_ROBOT5) &&
1225               (tower[d][c] != TB_ROBOT6) &&
1226               (tower[d][c] != TB_ROBOT7) &&
1227               (tower[d][c] != TB_BOX) &&
1228               (tower[d][c] != TB_STATION_MIDDLE) &&
1229               (tower[d][c] != TB_DOOR) &&
1230               (tower[d][c] != TB_DOOR_TARGET) &&
1231               (tower[d][c] != TB_STEP_VANISHER)) {
1232             row = r;
1233             col = c;
1234             return TPROB_ELEVATORBLOCKED;
1235           }
1236           d++;
1237         }
1238         if (d >= towerheight) {
1239           row = r;
1240           col = c;
1241           return TPROB_NOELEVATORSTOP;
1242         }
1243         d = r - 1;
1244         while ((tower[d][c] != TB_ELEV_BOTTOM) && (d >= 0)) {
1245           if ((tower[d][c] != TB_EMPTY) &&
1246               (tower[d][c] != TB_ROBOT1) &&
1247               (tower[d][c] != TB_ROBOT2) &&
1248               (tower[d][c] != TB_ROBOT3) &&
1249               (tower[d][c] != TB_ROBOT4) &&
1250               (tower[d][c] != TB_ROBOT5) &&
1251               (tower[d][c] != TB_ROBOT6) &&
1252               (tower[d][c] != TB_ROBOT7) &&
1253               (tower[d][c] != TB_BOX) &&
1254               (tower[d][c] != TB_STATION_MIDDLE) &&
1255               (tower[d][c] != TB_DOOR) &&
1256               (tower[d][c] != TB_DOOR_TARGET) &&
1257               (tower[d][c] != TB_STEP_VANISHER)) {
1258             row = r;
1259             col = c;
1260             return TPROB_ELEVATORBLOCKED;
1261           }
1262           d--;
1263         }
1264         if (d < 0) {
1265           row = r;
1266           col = c;
1267           return TPROB_NOELEVATORSTOP;
1268         }
1269       }
1270 
1271       if (tower[r][c] == TB_STATION_TOP) {
1272         int d = r - 1;
1273         while ((tower[d][c] != TB_ELEV_BOTTOM) && (d >= 0)) {
1274           if ((tower[d][c] != TB_EMPTY) &&
1275               (tower[d][c] != TB_ROBOT1) &&
1276               (tower[d][c] != TB_ROBOT2) &&
1277               (tower[d][c] != TB_ROBOT3) &&
1278               (tower[d][c] != TB_ROBOT4) &&
1279               (tower[d][c] != TB_ROBOT5) &&
1280               (tower[d][c] != TB_ROBOT6) &&
1281               (tower[d][c] != TB_ROBOT7) &&
1282               (tower[d][c] != TB_BOX) &&
1283               (tower[d][c] != TB_STATION_MIDDLE) &&
1284               (tower[d][c] != TB_DOOR) &&
1285               (tower[d][c] != TB_DOOR_TARGET) &&
1286               (tower[d][c] != TB_STEP_VANISHER)) {
1287             row = r;
1288             col = c;
1289             return TPROB_ELEVATORBLOCKED;
1290           }
1291           d--;
1292         }
1293         if (d < 0) {
1294           row = r;
1295           col = c;
1296           return TPROB_NOELEVATORSTOP;
1297         }
1298       }
1299 
1300       /* check for exit, and that it's reachable */
1301       if (tower[r][c] == TB_DOOR_TARGET) {
1302         int d = r - 1;
1303 
1304         if (d < 0) {
1305           row = 0;
1306           col = c;
1307           return TPROB_UNREACHABLEEXIT;
1308         }
1309 
1310         while ((d >= 0) && (tower[d][c] == TB_DOOR_TARGET))  d--;
1311         if (d >= 0) {
1312           if ((tower[d][c] != TB_STICK) && (tower[d][c] != TB_STEP) &&
1313               (tower[d][c] != TB_BOX) && (tower[d][c] != TB_ELEV_BOTTOM) &&
1314               (tower[d][c] != TB_STICK)) {
1315             row = r;
1316             col = c;
1317             return TPROB_UNREACHABLEEXIT;
1318           }
1319         }
1320         has_exit = true;
1321       }
1322 
1323       // check doors
1324       if ((tower[r][c] == TB_DOOR) &&
1325           !lev_is_door(r, (c + (TOWERWID/2)) % TOWERWID)) {
1326         row = r;
1327         col = c;
1328         return TPROB_NOOTHERDOOR;
1329       }
1330       if (lev_is_door(r,c)) {
1331         bool A = (r > 0) && (tower[r-1][c] == tower[r][c]);
1332         bool B = (r > 1) && (tower[r-2][c] == tower[r][c]);
1333         bool D = (r + 1 < towerheight) && (tower[r+1][c] == tower[r][c]);
1334         bool E = (r + 2 < towerheight) && (tower[r+2][c] == tower[r][c]);
1335 
1336         if (!((A&&B)||(A&&D)||(D&&E))) {
1337           row = r;
1338           col = c;
1339           return TPROB_BROKENDOOR;
1340         }
1341       }
1342     }
1343 
1344   if (!has_exit) return TPROB_NOEXIT;
1345 
1346   /* other, non-tower related problems */
1347   if (lev_towertime() < 5) return TPROB_SHORTTIME;
1348   if (!strlen(lev_towername())) return TPROB_NONAME;
1349 
1350   return TPROB_NONE;
1351 }
1352 
1353 /* the functions for mission creation */
1354 
1355 static FILE * fmission = NULL;
1356 static Uint8 nmission = 0;
1357 static Uint32 missionidx[256];
1358 
lev_mission_new(char * name,Uint8 prio)1359 bool lev_mission_new(char * name, Uint8 prio) {
1360   assert_msg(!fmission, "called mission_finish twice");
1361 
1362   char fname[200];
1363   snprintf(fname, 200, "%s.ttm", name);
1364 
1365   fmission = create_local_data_file(fname);
1366 
1367   if (!fmission) return false;
1368 
1369   unsigned char tmp = strlen(name);
1370 
1371   /* write out name */
1372   fwrite(&tmp, 1 ,1, fmission);
1373   fwrite(name, 1, tmp, fmission);
1374 
1375   fwrite(&prio, 1, 1, fmission);
1376 
1377   /* placeholders for towernumber and indexstart */
1378   fwrite(&tmp, 1, 1, fmission);
1379   fwrite(&tmp, 1, 4, fmission);
1380 
1381   nmission = 0;
1382 
1383   return true;
1384 }
1385 
write_fmission_section(Uint8 section,Uint32 section_len)1386 void write_fmission_section(Uint8 section, Uint32 section_len) {
1387     Uint8 tmp;
1388     fwrite(&section, 1, 1, fmission);
1389 
1390     tmp = section_len & 0xff;
1391     fwrite(&tmp, 1, 1, fmission);
1392     tmp = (section_len >> 8) & 0xff;
1393     fwrite(&tmp, 1, 1, fmission);
1394     tmp = (section_len >> 16) & 0xff;
1395     fwrite(&tmp, 1, 1, fmission);
1396     tmp = (section_len >> 24) & 0xff;
1397     fwrite(&tmp, 1, 1, fmission);
1398 }
1399 
lev_mission_addtower(char * name)1400 void lev_mission_addtower(char * name) {
1401   assert_msg(fmission, "called mission_addtower without mission_new");
1402 
1403   Uint8 rows, col;
1404   Sint16 row;
1405   Uint8 namelen, tmp;
1406   Uint32 section_len;
1407   int idx;
1408 
1409   missionidx[nmission] = ftell(fmission);
1410   nmission++;
1411 
1412   if (!lev_loadtower(name)) return;
1413 
1414   namelen = strlen(towername);
1415   write_fmission_section(TSS_TOWERNAME, namelen);
1416   fwrite(towername, 1, namelen, fmission);
1417 
1418   write_fmission_section(TSS_ROBOT, 1);
1419   fwrite(&towerrobot, 1, 1, fmission);
1420 
1421   write_fmission_section(TSS_TOWERTIME, 2);
1422   tmp = towertime & 0xff;
1423   fwrite(&tmp, 1, 1, fmission);
1424   tmp = (towertime >> 8) & 0xff;
1425   fwrite(&tmp, 1, 1, fmission);
1426 
1427   write_fmission_section(TSS_TOWERCOLOR, 3);
1428   fwrite(&towercolor_red, 1, 1, fmission);
1429   fwrite(&towercolor_green, 1, 1, fmission);
1430   fwrite(&towercolor_blue, 1, 1, fmission);
1431 
1432   rows = towerheight;
1433 
1434   /* calculate tower data section length */
1435   section_len = 2*towerheight;
1436   for (row = 0; row < rows; row++)
1437     for (col = 0; col < TOWERWID; col++)
1438       if (tower[row][col]) section_len++;
1439   write_fmission_section(TSS_TOWERDATA, section_len + 1);
1440 
1441 
1442   fwrite(&towerheight, 1, 1, fmission);
1443 
1444   /* output bitmap */
1445   for (row = 0; row < rows; row++) {
1446 
1447     Uint8 c = 0;
1448     for (col = 0; col < 8; col ++)
1449       if (tower[row][col])
1450         c |= (0x80 >> col);
1451 
1452     fwrite(&c, 1, 1, fmission);
1453 
1454     c = 0;
1455     for (col = 0; col < 8; col ++)
1456       if (tower[row][col + 8])
1457         c |= (0x80 >> col);
1458 
1459     fwrite(&c, 1, 1, fmission);
1460   }
1461 
1462   /* output bytemap */
1463   for (row = 0; row < rows; row++)
1464     for (col = 0; col < TOWERWID; col++)
1465       if (tower[row][col])
1466         fwrite(&tower[row][col], 1, 1, fmission);
1467 
1468   /* output towerdemo */
1469 
1470   if (towerdemo && (towerdemo_len > 0)) {
1471     Uint8 run;
1472     Uint16 data;
1473 
1474     /* calc data length */
1475     run = 1;
1476     data = towerdemo[0];
1477     section_len = 2;
1478 
1479     for (idx = 1; idx < towerdemo_len; idx++) {
1480       if ((data != towerdemo[idx]) || (run == 0xff)) {
1481         section_len += 3;
1482 
1483         data = towerdemo[idx];
1484         run = 1;
1485       } else
1486         run ++;
1487     }
1488     if (run) section_len += 3;
1489 
1490     write_fmission_section(TSS_DEMO, section_len);
1491 
1492     /* output length */
1493     tmp = towerdemo_len & 0xff;
1494     fwrite(&tmp, 1, 1, fmission);
1495     tmp = (towerdemo_len >> 8) & 0xff;
1496     fwrite(&tmp, 1, 1, fmission);
1497 
1498     /* output data using a simple runlength encoder */
1499     run = 1;
1500     data = towerdemo[0];
1501 
1502     for (idx = 1; idx < towerdemo_len; idx++) {
1503       if ((data != towerdemo[idx]) || (run == 0xff)) {
1504         fwrite(&run, 1, 1, fmission);
1505         tmp = data & 0xff;
1506         fwrite(&tmp, 1, 1, fmission);
1507         tmp = (data >> 8) & 0xff;
1508         fwrite(&tmp, 1, 1, fmission);
1509 
1510         data = towerdemo[idx];
1511         run = 1;
1512       } else
1513         run ++;
1514     }
1515 
1516     if (run) {
1517       fwrite(&run, 1, 1, fmission);
1518       tmp = data & 0xff;
1519       fwrite(&tmp, 1, 1, fmission);
1520       tmp = (data >> 8) & 0xff;
1521       fwrite(&tmp, 1, 1, fmission);
1522     }
1523   }
1524 
1525   write_fmission_section(TSS_END, 0);
1526 }
1527 
lev_mission_finish()1528 void lev_mission_finish() {
1529   assert_msg(fmission, "called mission_finish without mission_new");
1530 
1531   Uint8 c;
1532 
1533   /* save indexstart and write out index */
1534   Uint32 idxpos = ftell(fmission);
1535   for (Uint8 i = 0; i < nmission; i++) {
1536 
1537     c = missionidx[i] & 0xff;
1538     fwrite(&c, 1, 1, fmission);
1539     c = (missionidx[i] >> 8) & 0xff;
1540     fwrite(&c, 1, 1, fmission);
1541     c = (missionidx[i] >> 16) & 0xff;
1542     fwrite(&c, 1, 1, fmission);
1543     c = (missionidx[i] >> 24) & 0xff;
1544     fwrite(&c, 1, 1, fmission);
1545   }
1546 
1547   /* write out the number of towers in this mission */
1548   fseek(fmission, 0, SEEK_SET);
1549   fread(&c, 1, 1, fmission);
1550 
1551   fseek(fmission, c + 2, SEEK_SET);
1552   fwrite(&nmission, 1, 1, fmission);
1553 
1554   /* write out index position */
1555   c = idxpos & 0xff;
1556   fwrite(&c, 1, 1, fmission);
1557   c = (idxpos >> 8) & 0xff;
1558   fwrite(&c, 1, 1, fmission);
1559   c = (idxpos >> 16) & 0xff;
1560   fwrite(&c, 1, 1, fmission);
1561   c = (idxpos >> 24) & 0xff;
1562   fwrite(&c, 1, 1, fmission);
1563 
1564   fclose(fmission);
1565 
1566   fmission = NULL;
1567 }
1568 
1569