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 #include "screen.h"
20 
21 #include "archi.h"
22 #include "sprites.h"
23 #include "robots.h"
24 #include "stars.h"
25 #include "points.h"
26 #include "toppler.h"
27 #include "snowball.h"
28 #include "level.h"
29 #include "decl.h"
30 #include "keyb.h"
31 #include "configuration.h"
32 
33 #include <string.h>
34 #include <stdlib.h>
35 #include <math.h>
36 #include <wchar.h>
37 
38 static SDL_Surface *display;
39 
40 static int color_ramp_radj = 3;
41 static int color_ramp_gadj = 5;
42 static int color_ramp_badj = 7;
43 
44 static Uint8 *slicedata, *battlementdata, *crossdata;
45 
46 static int slicestart;
47 static int battlementstart;
48 
49 static Uint8 robotcount;
50 
51 typedef struct {
52   Uint8 count;            // how many pictures are in the animation of this robot
53   unsigned short start;   // number of first robot
54 } robot_data;
55 
56 robot_data * robots;      // array with robot data, robotcount contains size
57 
58 static unsigned short ballst, boxst, snowballst, starst, crossst,
59          fishst, subst, torb;
60 static int topplerstart;
61 
62 static unsigned short  step, elevatorsprite, stick;
63 
64 /* table used to calculate the distance of an object from the center of the
65  tower that is at x degrees on the tower */
66 static int sintab[TOWER_ANGLES];
67 
68 /* this table is used for the waves of the water */
69 static Sint8 waves[0x80];
70 
71 /* this value added to the start of the animal sprites leads to
72  the mirrored ones */
73 #define mirror          37
74 
75 /* the state of the flashing boxes */
76 static int boxstate;
77 
78 static struct {
79   int xstart;          // x start position
80   int width;           // width of door
81   unsigned short s[3]; // the sprite index for the 3 layers of the door
82   Uint8 *data[3];      // the data for the 3 layers of the door (pixel info for recoloring)
83 } doors[73];
84 
85 #define MAXCHARNUM 256*256
86 
87 static struct {
88   unsigned short s;
89   unsigned char width;
90 } fontchars[MAXCHARNUM];
91 
92 /* bonus game scrolling layer */
93 typedef struct  {
94   long xpos, ypos;    // position of the layer
95   long xrepeat;       // how often the image repeats
96   long width, height; // size of the layer
97   int  num, den;      // speed
98   Uint16 image;
99 } _scroll_layer;
100 
101 /* # of scrolling layers in the bonus game */
102 static int num_scrolllayers;
103 /* tower layering depth and scrolling speed in the bonus game */
104 static int sl_tower_depth,
105            sl_tower_num,
106            sl_tower_den;
107 static _scroll_layer *scroll_layers;
108 
109 Uint8 towerpal[2*256];
110 Uint8 crosspal[2*256];
111 
112 Uint8 last_towercol_r, last_towercol_g, last_towercol_b;
113 
color_ramp1(int * c,int * adj,int min,int max)114 void color_ramp1(int *c, int *adj, int min, int max) {
115   *c = *c + *adj;
116   if (*c > max - abs(*adj)) {
117     *c = max;
118     *adj = -(*adj);
119   } else if (*c < min + abs(*adj)) {
120     *c = min;
121     *adj = -(*adj);
122   }
123 }
124 
scr_color_ramp(int * r,int * g,int * b)125 void scr_color_ramp(int *r, int *g, int *b) {
126   color_ramp1(r, &color_ramp_radj, 1, 255);
127   color_ramp1(g, &color_ramp_gadj, 1, 255);
128   color_ramp1(b, &color_ramp_badj, 1, 255);
129 }
130 
131 void
scr_savedisplaybmp(char * fname)132 scr_savedisplaybmp(char *fname)
133 {
134   SDL_SaveBMP(display, fname);
135 }
136 
137 /*
138  * Set the pixel at (x, y) to the given value
139  * NOTE: The surface must be locked before calling this!
140  */
putpixel(SDL_Surface * surface,int x,int y,Uint32 pixel)141 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
142 {
143   int bpp = surface->format->BytesPerPixel;
144   /* Here p is the address to the pixel we want to set */
145   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
146 
147   switch(bpp) {
148     case 1:
149       *p = pixel;
150       break;
151 
152     case 2:
153       *(Uint16 *)p = pixel;
154       break;
155 
156     case 3:
157       if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
158         p[0] = (pixel >> 16) & 0xff;
159         p[1] = (pixel >> 8) & 0xff;
160         p[2] = pixel & 0xff;
161         } else {
162         p[0] = pixel & 0xff;
163         p[1] = (pixel >> 8) & 0xff;
164         p[2] = (pixel >> 16) & 0xff;
165         }
166         break;
167 
168     case 4:
169         *(Uint32 *)p = pixel;
170         break;
171   }
172 }
173 
174 
175 
scr_loadsprites(spritecontainer * spr,file * fi,int num,int w,int h,bool sprite,const Uint8 * pal,bool use_alpha)176 Uint16 scr_loadsprites(spritecontainer *spr, file * fi, int num, int w, int h, bool sprite, const Uint8 *pal, bool use_alpha) {
177   Uint16 erg = 0;
178   Uint8 b, a;
179   SDL_Surface *z;
180   Uint32 pixel;
181 
182   for (int t = 0; t < num; t++) {
183     z = SDL_CreateRGBSurface(SDL_SWSURFACE | (sprite) ? SDL_SRCALPHA : 0,
184                              w, h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, (sprite & use_alpha) ? 0xFF000000 : 0);
185 
186     if (sprite & !use_alpha)
187       SDL_SetColorKey(z, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(z->format, 1, 1, 1));
188 
189     for (int y = 0; y < h; y++)
190       for (int x = 0; x < w; x++) {
191         b = fi->getbyte();
192         pixel=SDL_MapRGB(z->format,pal[b*3 + 0],pal[b*3 + 1],pal[b*3 + 2]);
193         if (sprite) {
194           a = fi->getbyte();
195           if (use_alpha) {
196             pixel=SDL_MapRGBA(z->format,pal[b*3 + 0],pal[b*3 + 1],pal[b*3 + 2],a);
197           } else {
198             if (a<128)
199               pixel=SDL_MapRGB(z->format,1,1,1);
200             else
201             {
202               if (((pal[b*3+2] == 1) && (pal[b*3+1] == 1)) || (pal[b*3] == 1))
203                 pixel=SDL_MapRGB(z->format,pal[b*3 + 0],pal[b*3 + 1],pal[b*3 + 2]+1);
204               else
205                 /* ok, this is the case where we have a sprite and don't want
206                  to use alpha blending, so we use normal sprites with key color
207                  instead, this is much faster. So if the pixel is more than 50% transparent
208                  make the whole pixel transparent by setting this pixel to the
209                  key color. if the pixel is not supoosed to be transparent
210                  we need to check if the pixel color is by accident the key color,
211                  if so we alter is slightly */
212                 pixel=SDL_MapRGB(z->format,pal[b*3 + 0],pal[b*3 + 1],pal[b*3 + 2]);
213             }
214           }
215         }
216         putpixel(z,x,y,pixel);
217       }
218 
219     SDL_Surface * z2 = SDL_DisplayFormatAlpha(z);
220     SDL_FreeSurface(z);
221     z = z2;
222 
223     if (t == 0)
224       erg = spr->save(z);
225     else
226       spr->save(z);
227   }
228 
229   return erg;
230 }
231 
232 
scr_gensprites(spritecontainer * spr,int num,int w,int h,bool sprite,bool use_alpha,bool screenformat)233 static Uint16 scr_gensprites(spritecontainer *spr, int num, int w, int h, bool sprite, bool use_alpha, bool screenformat) {
234   Uint16 erg = 0;
235   SDL_Surface *z;
236 
237   for (int t = 0; t < num; t++) {
238     z = SDL_CreateRGBSurface(SDL_SWSURFACE | (sprite && use_alpha) ? SDL_SRCALPHA : 0,
239                              w, h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, (sprite && use_alpha) ? 0xFF000000 : 0);
240 
241     if (sprite & !use_alpha)
242       /* SDL_RLEACCEL is not allowed here, because we need to edit the data later
243        on for the new colors */
244       SDL_SetColorKey(z, SDL_SRCCOLORKEY, SDL_MapRGB(z->format, 1, 1, 1));
245 
246     if (screenformat) {
247       SDL_Surface * z2 = SDL_DisplayFormat(z);
248       SDL_FreeSurface(z);
249       z = z2;
250     }
251 
252     if (t == 0)
253       erg = spr->save(z);
254     else
255       spr->save(z);
256   }
257 
258   return erg;
259 }
260 
scr_regensprites(Uint8 * data,SDL_Surface * const target,int num,int w,int h,bool sprite,const Uint8 * pal,bool use_alpha,bool screenformat)261 static void scr_regensprites(Uint8 *data, SDL_Surface * const target, int num, int w, int h, bool sprite, const Uint8 *pal, bool use_alpha, bool screenformat) {
262   Uint8 a, b;
263   Uint32 datapos = 0;
264   SDL_Surface * z;
265   Uint32 pixel;
266 
267   if (screenformat) {
268     z = SDL_CreateRGBSurface(SDL_SWSURFACE | (sprite && use_alpha) ? SDL_SRCALPHA : 0,
269                              w, h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, (sprite && use_alpha) ? 0xFF000000 : 0);
270 
271     if (sprite & !use_alpha)
272       /* SDL_RLEACCEL is not allowed here, because we need to edit the data later
273        on for the new colors */
274       SDL_SetColorKey(z, SDL_SRCCOLORKEY, SDL_MapRGB(z->format, 1, 1, 1));
275   } else
276     z = target;
277 
278   for (int t = 0; t < num; t++)
279     for (int y = 0; y < h; y++)
280       for (int x = 0; x < w; x++) {
281         b = data[datapos++];
282         pixel=SDL_MapRGB(z->format,pal[b*3 + 0],pal[b*3 + 1],pal[b*3 + 2]);
283         if (sprite) {
284           a = data[datapos++];
285           if (use_alpha) {
286             pixel=SDL_MapRGBA(z->format,pal[b*3 + 0],pal[b*3 + 1],pal[b*3 + 2],a);
287           } else {
288             if (a<128)
289               pixel=SDL_MapRGB(z->format,1,1,1);
290             else
291             {
292               if (((pal[b*3+2] == 1) && (pal[b*3+1] == 1)) || (pal[b*3] == 1))
293                 pixel=SDL_MapRGB(z->format,pal[b*3 + 0],pal[b*3 + 1],pal[b*3 + 2]+1);
294               else
295                 /* ok, this is the case where we have a sprite and don't want
296                  to use alpha blending, so we use normal sprites with key color
297                  instead, this is much faster. So if the pixel is more than 50% transparent
298                  make the whole pixel transparent by setting this pixel to the
299                  key color. if the pixel is not supoosed to be transparent
300                  we need to check if the pixel color is by accident the key color,
301                  if so we alter is slightly */
302                 pixel=SDL_MapRGB(z->format,pal[b*3 + 0],pal[b*3 + 1],pal[b*3 + 2]);
303             }
304           }
305         }
306         putpixel(z,x,y,pixel);
307       }
308   if (screenformat) {
309     SDL_Rect r;
310     r.h = h;
311     r.w = w;
312     r.x = 0;
313     r.y = 0;
314     SDL_BlitSurface(z, &r, target, &r);
315 
316     SDL_FreeSurface(z);
317   }
318 }
319 
scr_read_palette(file * fi,Uint8 * pal)320 void scr_read_palette(file * fi, Uint8 *pal) {
321   Uint8 b;
322   b = fi->getbyte();
323 
324   fi->read(pal, (Uint32)b*3+3);
325 }
326 
327 
328 /* loads all the graphics */
loadgraphics(Uint8 what)329 static void loadgraphics(Uint8 what) {
330 
331   unsigned char pal[3*256];
332   int t;
333 
334   if (what == 0xff) {
335 
336     file fi(dataarchive, grafdat);
337 
338     fi.read(towerpal, 2*256);
339 
340     slicedata = (Uint8*)malloc(SPR_SLICESPRITES * SPR_SLICEWID * SPR_SLICEHEI);
341     fi.read(slicedata, SPR_SLICESPRITES * SPR_SLICEWID * SPR_SLICEHEI);
342 
343     battlementdata = (Uint8*)malloc(SPR_BATTLFRAMES * SPR_BATTLWID * SPR_BATTLHEI);
344     fi.read(battlementdata, SPR_BATTLFRAMES * SPR_BATTLWID * SPR_BATTLHEI);
345 
346     slicestart = scr_gensprites(&restsprites, SPR_SLICESPRITES, SPR_SLICEWID, SPR_SLICEHEI, false, false, true);
347     battlementstart = scr_gensprites(&restsprites, SPR_BATTLFRAMES, SPR_BATTLWID, SPR_BATTLHEI, false, false, true);
348 
349     for (t = -36; t < 37; t++) {
350 
351       doors[t+36].xstart = fi.getword();
352       doors[t+36].width = fi.getword();
353 
354       for (int et = 0; et < 3; et++)
355         if (doors[t+36].width != 0) {
356           doors[t+36].s[et] = scr_gensprites(&restsprites, 1, doors[t+36].width, 16, false, false, true);
357           doors[t+36].data[et] = (Uint8*)malloc(doors[t+36].width*16);
358           fi.read(doors[t+36].data[et], doors[t+36].width*16);
359         } else {
360           doors[t+36].s[et] = 0;
361           doors[t+36].data[et] = NULL;
362         }
363     }
364 
365     for (t = 0; t < 256; t++) {
366       unsigned char c1, c2;
367 
368       c1 = fi.getbyte();
369       c2 = fi.getbyte();
370 
371       pal[3*t] = c1;
372       pal[3*t+1] = c2;
373       pal[3*t+2] = c2;
374     }
375 
376     step = scr_loadsprites(&restsprites, &fi, SPR_STEPFRAMES, SPR_STEPWID, SPR_STEPHEI, false, pal, false);
377     elevatorsprite = scr_loadsprites(&restsprites, &fi, SPR_ELEVAFRAMES, SPR_ELEVAWID, SPR_ELEVAHEI, false, pal, false);
378     stick = scr_loadsprites(&restsprites, &fi, 1, SPR_STICKWID, SPR_STICKHEI, false, pal, false);
379   }
380 
381   {
382     file fi(dataarchive, topplerdat);
383 
384     scr_read_palette(&fi, pal);
385 
386     topplerstart = scr_loadsprites(&objectsprites, &fi, 74, SPR_HEROWID, SPR_HEROHEI, true, pal, config.use_alpha_sprites());
387   }
388 
389   {
390     file fi(dataarchive, spritedat);
391 
392     scr_read_palette(&fi, pal);
393 
394     robotcount = fi.getbyte();
395 
396     robots = new robot_data[robotcount];
397 
398     for (t = 0; t < 8; t++) {
399       robots[t].count = fi.getbyte();
400       robots[t].start = scr_loadsprites(&objectsprites, &fi, robots[t].count, SPR_ROBOTWID, SPR_ROBOTHEI, true, pal, config.use_alpha_sprites());
401     }
402 
403     scr_read_palette(&fi, pal);
404     ballst = scr_loadsprites(&objectsprites, &fi, 2, SPR_ROBOTWID, SPR_ROBOTHEI, true, pal, config.use_alpha_sprites());
405 
406     scr_read_palette(&fi, pal);
407     boxst = scr_loadsprites(&objectsprites, &fi, 16, SPR_BOXWID, SPR_BOXHEI, true, pal, config.use_alpha_sprites());
408 
409     scr_read_palette(&fi, pal);
410     snowballst = scr_loadsprites(&objectsprites, &fi, 1, SPR_AMMOWID, SPR_AMMOHEI, true, pal, config.use_alpha_sprites());
411 
412     scr_read_palette(&fi, pal);
413     starst = scr_loadsprites(&objectsprites, &fi, 16, SPR_STARWID, SPR_STARHEI, true, pal, config.use_alpha_sprites());
414     sts_init(starst + 9, NUM_STARS);
415 
416     scr_read_palette(&fi, pal);
417     fishst = scr_loadsprites(&objectsprites, &fi, 32*2, SPR_FISHWID, SPR_FISHHEI, true, pal, config.use_alpha_sprites());
418 
419     scr_read_palette(&fi, pal);
420     subst = scr_loadsprites(&objectsprites, &fi, 31, SPR_SUBMWID, SPR_SUBMHEI, true, pal, config.use_alpha_sprites());
421 
422     scr_read_palette(&fi, pal);
423     torb = scr_loadsprites(&objectsprites, &fi, 1, SPR_TORPWID, SPR_TORPHEI, true, pal, config.use_alpha_sprites());
424   }
425 
426   {
427     file fi(dataarchive, crossdat);
428 
429     Uint8 numcol = fi.getbyte();
430 
431     for (t = 0; t < numcol + 1; t++) {
432       crosspal[2*t] = fi.getbyte();
433       fi.getbyte();
434       crosspal[2*t+1] = fi.getbyte();
435     }
436 
437     crossdata = (Uint8*)malloc(120*SPR_CROSSWID*SPR_CROSSHEI*2);
438     fi.read(crossdata, 120*SPR_CROSSWID*SPR_CROSSHEI*2);
439 
440     crossst = scr_gensprites(&objectsprites, 120, SPR_CROSSWID, SPR_CROSSHEI, true, config.use_alpha_sprites(), false);
441   }
442 }
443 
scr_numrobots(void)444 Uint8 scr_numrobots(void) { return robotcount; }
445 
446 
scr_settowercolor(Uint8 r,Uint8 g,Uint8 b)447 void scr_settowercolor(Uint8 r, Uint8 g, Uint8 b) {
448 
449   Uint8 pal[3*256];
450 
451   int t;
452   int bw, gw, rw;
453 
454   for (t = 0; t < 256; t++) {
455     rw = (int)r*towerpal[2*t+1] + (255-(int)r)*towerpal[2*t];
456     gw = (int)g*towerpal[2*t+1] + (255-(int)g)*towerpal[2*t];
457     bw = (int)b*towerpal[2*t+1] + (255-(int)b)*towerpal[2*t];
458 
459     rw /= 256;
460     gw /= 256;
461     bw /= 256;
462 
463     pal[3*t] = rw;
464     pal[3*t+1] = gw;
465     pal[3*t+2] = bw;
466   }
467 
468   for (t = 0; t < SPR_SLICESPRITES; t++)
469     scr_regensprites(slicedata + t*SPR_SLICEWID*SPR_SLICEHEI, restsprites.data(slicestart + t), 1, SPR_SLICEWID, SPR_SLICEHEI, false, pal, false, true);
470 
471   for (t = 0; t < SPR_BATTLFRAMES; t++)
472     scr_regensprites(battlementdata + t*SPR_BATTLWID*SPR_BATTLHEI, restsprites.data(battlementstart + t), 1, SPR_BATTLWID, SPR_BATTLHEI, false, pal, false, true);
473 
474   for (t = -36; t < 37; t++)
475     for (int et = 0; et < 3; et++)
476       if (doors[t+36].width != 0)
477         scr_regensprites(doors[t+36].data[et], restsprites.data(doors[t+36].s[et]), 1, doors[t+36].width, SPR_SLICEHEI, false, pal, false, true);
478 
479   last_towercol_r = r;
480   last_towercol_g = g;
481   last_towercol_b = b;
482 }
483 
resettowercolor(void)484 void resettowercolor(void) {
485   scr_settowercolor(last_towercol_r, last_towercol_g, last_towercol_b);
486 }
487 
scr_setcrosscolor(Uint8 rk,Uint8 gk,Uint8 bk)488 void scr_setcrosscolor(Uint8 rk, Uint8 gk, Uint8 bk) {
489 
490   Uint8 pal[256*3];
491 
492   int t, r, g, b;
493 
494   for (t = 0; t < 256; t++) {
495     r = g = b = crosspal[2*t];
496 
497     r += ((int)crosspal[2*t+1] * rk) / 256;
498     g += ((int)crosspal[2*t+1] * gk) / 256;
499     b += ((int)crosspal[2*t+1] * bk) / 256;
500 
501     if (r > 255)
502       r = 255;
503     if (g > 255)
504       g = 255;
505     if (b > 255)
506       b = 255;
507 
508     pal[3*t+0] = r;
509     pal[3*t+1] = g;
510     pal[3*t+2] = b;
511   }
512 
513   for (t = 0; t < 120; t++) {
514     scr_regensprites(crossdata + t*SPR_CROSSWID*SPR_CROSSHEI*2,
515                      objectsprites.data(crossst+t),
516                      1, SPR_CROSSWID, SPR_CROSSHEI, true, pal, config.use_alpha_sprites(), false);
517   }
518 }
519 
loadfont(void)520 static void loadfont(void) {
521 
522   unsigned char pal[256*3];
523   Uint16 c;
524   int fontheight;
525 
526   file fi(dataarchive, fontdat);
527 
528   scr_read_palette(&fi, pal);
529 
530   fontheight = fi.getbyte();
531 
532   while (!fi.eof()) {
533     c = fi.getword();
534 
535     if (!c) break;
536 
537     fontchars[c].width = fi.getbyte();
538     fontchars[c].s = scr_loadsprites(&fontsprites, &fi, 1, fontchars[c].width, fontheight, true, pal, config.use_alpha_font());
539   }
540 }
541 
loadscroller(void)542 static void loadscroller(void) {
543 
544   file fi(dataarchive, scrollerdat);
545 
546   Uint8 layers;
547   Uint8 towerpos;
548   Uint8 pal[3*256];
549 
550   layers = fi.getbyte();
551 
552   num_scrolllayers = layers;
553 
554   assert_msg(num_scrolllayers > 1, "Must have at least 2 scroll layers!");
555 
556   scroll_layers = new _scroll_layer[layers];
557   assert_msg(scroll_layers, "Failed to alloc memory for bonus scroller!");
558 
559   towerpos = fi.getbyte();
560 
561   sl_tower_depth = towerpos;
562 
563   sl_tower_num = fi.getword();
564   sl_tower_den = fi.getword();
565 
566   for (int l = 0; l < layers; l++) {
567 
568     scroll_layers[l].xpos = fi.getword();
569     scroll_layers[l].ypos = fi.getword();
570     scroll_layers[l].width = fi.getword();
571     scroll_layers[l].height = fi.getword();
572     scroll_layers[l].num = fi.getword();
573     scroll_layers[l].den = fi.getword();
574     scroll_layers[l].xrepeat = fi.getword();
575 
576     scr_read_palette(&fi, pal);
577 
578     scroll_layers[l].image = scr_loadsprites(&layersprites, &fi, 1,
579 	scroll_layers[l].width, scroll_layers[l].height,
580 	l != 0, pal, config.use_alpha_layers());
581   }
582 }
583 
load_sprites(Uint8 what)584 static void load_sprites(Uint8 what) {
585   if ((what == 0xff) || (what & RL_OBJECTS))
586     loadgraphics(what);
587 
588   if (what & RL_FONT)
589     loadfont();
590 
591   if (what & RL_SCROLLER)
592     loadscroller();
593 }
594 
free_memory(Uint8 what)595 static void free_memory(Uint8 what) {
596   int t;
597 
598   if (what == 0xff) {
599     free(slicedata);
600     free(battlementdata);
601   }
602 
603   if (what & RL_OBJECTS) {
604     free(crossdata);
605     delete [] robots;
606   }
607 
608   if (what & RL_SCROLLER)
609     delete [] scroll_layers;
610 
611   if (what == 0xff)
612     for (t = -36; t < 37; t++)
613       for (int et = 0; et < 3; et++)
614         if (doors[t+36].data[et])
615           free(doors[t+36].data[et]);
616 }
617 
scr_reload_sprites(Uint8 what)618 void scr_reload_sprites(Uint8 what) {
619   free_memory(what);
620   load_sprites(what);
621   resettowercolor();
622 }
623 
scr_init(void)624 void scr_init(void) {
625 
626   scr_reinit();
627 
628   load_sprites(0xff);
629 
630   /* initialize sinus table */
631   for (int i = 0; i < TOWER_ANGLES; i++)
632     sintab[i] = int(sin(i * 2 * M_PI / TOWER_ANGLES) * (TOWER_RADIUS + SPR_STEPWID / 2) + 0.5);
633 
634 
635   /* initialize wave table */
636   for (int t = 0; t < 0x80; t++)
637     waves[t] = (Sint8)(8 * (sin(t * 2.0 * M_PI / 0x7f)) +
638       4 * (sin(t * 3.0 * M_PI / 0x7f+2)) +
639       3 * (sin(t * 5.0 * M_PI / 0x7f+3)) + 0.5);
640 
641 }
642 
scr_reinit()643 void scr_reinit() {
644   display = SDL_SetVideoMode(SCREENWID, SCREENHEI, 16, (config.fullscreen()) ? (SDL_FULLSCREEN) : (0));
645   assert_msg(display, "could not open display");
646 }
647 
scr_done(void)648 void scr_done(void) {
649   free_memory(0xff);
650   sts_done();
651 }
652 
cleardesk(long height)653 static void cleardesk(long height) {
654   SDL_Rect r;
655   height *= 4;
656 
657 /* clear left side from top to water */
658   r.w = (SCREENWID - SPR_SLICEWID) / 2;
659   if (height < (SCREENHEI / 2))
660     r.h = (SCREENHEI / 2) + height;
661   else
662     r.h = SCREENHEI;
663   r.x = r.y = 0;
664   SDL_FillRect(display, &r, 0);
665 
666 /* clear right side from top to water */
667   r.x = (SCREENWID - SPR_SLICEWID) / 2 + SPR_SLICEWID;
668   SDL_FillRect(display, &r, 0);
669 
670 /* clear middle row from top to battlement */
671   int upend = (SCREENHEI / 2) - (lev_towerrows() * SPR_SLICEHEI - height + SPR_BATTLHEI);
672   if (upend > 0) {
673     r.x = (SCREENWID - SPR_SLICEWID) / 2;
674     r.w = SPR_SLICEWID;
675     r.h = upend;
676     SDL_FillRect(display, &r, 0);
677   }
678 }
679 
scr_darkenscreen(void)680 void scr_darkenscreen(void) {
681 
682   if (!config.use_alpha_darkening())
683     return;
684 
685   scr_putbar(0, 0, SCREENWID, SCREENHEI, 0, 0, 0, 128);
686 }
687 
688 
689 /*
690  angle: 0 means column 0 is in front
691         8 means column 1 ...
692 
693  height: 0 means row 0 is in the middle
694          4 means row 1 ...
695 */
puttower(long angle,long height,long towerheight,int shift=0)696 static void puttower(long angle, long height, long towerheight, int shift = 0) {
697 
698   /* calculate the blit position of the lowest slice considering the current
699    * vertical position
700    */
701   int slice = 0;
702   int ypos = SCREENHEI / 2 - SPR_SLICEHEI + height;
703 
704   /* now go up until we go over the top of the screen or reach the
705    * top of the tower
706    */
707   while ((ypos > -SPR_SLICEHEI) && (slice < towerheight)) {
708 
709     /* if we are over the bottom of the screen, draw the slice */
710     if (ypos < SCREENHEI)
711       scr_blit(restsprites.data(slicestart + (angle % SPR_SLICEANGLES)), (SCREENWID / 2) - (SPR_SLICEWID / 2) + shift, ypos);
712 
713     slice++;
714     angle = (angle + (SPR_SLICEANGLES / 2)) % TOWER_ANGLES;
715     ypos -= SPR_SLICEHEI;
716   }
717 }
718 
putbattlement(long angle,long height)719 static void putbattlement(long angle, long height) {
720 
721   /* calculate the lower border position of the battlement */
722   int upend = (SCREENHEI / 2) - (lev_towerrows() * SPR_SLICEHEI - height);
723 
724   /* if it's below the top of the screen, then blit the battlement */
725   if (upend > 0)
726     scr_blit(restsprites.data((angle % SPR_BATTLFRAMES) + battlementstart),
727              (SCREENWID / 2) - (SPR_BATTLWID / 2), upend - SPR_BATTLHEI);
728 }
729 
putwater(long height)730 static void putwater(long height) {
731 
732   static const char simple_waves[] = {
733     4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
734     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
735     7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4,
736     4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
737     1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
738     0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
739     3, 3 };
740 
741   static int wavetime = 0;
742 
743   height *= 4;
744 
745   if (height < (SCREENHEI / 2)) {
746 
747     switch(config.waves_type()) {
748 
749     case configuration::waves_expensive:
750       {
751         int source_line = (SCREENHEI / 2) + height - 1;
752 
753         Uint8 buffer[4] = {0,0,0,0};
754 
755         for (int y = 0; y < (SCREENHEI / 2) - height; y++) {
756 
757           Uint8 * target = (Uint8*)display->pixels + ((SCREENHEI/2) + height + y) * display->pitch;
758 
759           for (int x = 0; x < SCREENWID; x++) {
760             Sint16 dx = waves[(x+y+12*wavetime) & 0x7f] + waves[(2*x-y+11*wavetime) & 0x7f];
761             Sint16 dy = waves[(x-y+13*wavetime) & 0x7f] + waves[(2*x-3*y-14*wavetime) & 0x7f];
762 
763             dx = dx * y / (SCREENHEI/2);
764             dy = dy * y / (SCREENHEI/2);
765 
766             if ((x+dx < 0) || (x+dx > SCREENWID) || (source_line+dy < 0))
767               memcpy(target, &buffer, display->format->BytesPerPixel);
768             else
769               memcpy(target, (Uint8*)display->pixels +
770                      (x+dx) * display->format->BytesPerPixel +
771                      (source_line+dy) * display->pitch, display->format->BytesPerPixel);
772 
773             target += display->format->BytesPerPixel;
774           }
775           scr_putbar(0, (SCREENHEI/2) + height + y, SCREENWID, 1, 0, 0, y, 128);
776           source_line --;
777         }
778       }
779       break;
780     case configuration::waves_simple:
781       {
782         int horizontal_shift;
783 
784         scr_putbar(0, (SCREENHEI / 2) + height, 10,  (SCREENHEI / 2) - height, 0, 0, 0, 255);
785         scr_putbar(SCREENWID-10, (SCREENHEI / 2) + height, 10,  (SCREENHEI / 2) - height, 0, 0, 0, 255);
786 
787         for (int y = 0; y < (SCREENHEI / 2) - height; y++) {
788 
789           int target_line = (SCREENHEI / 2) + height + y;
790           int source_line = (SCREENHEI / 2) + height - y - 1 - simple_waves[(wavetime * 4 + y * 2) & 0x7f];
791           if (source_line < 0)
792             source_line = 0;
793 
794           int z = simple_waves[(wavetime*5 + y) & 0x7f];
795           if (abs(z - 4) > y) {
796             if (z < 4)
797               horizontal_shift = 4 - y;
798             else
799               horizontal_shift = 4 + y;
800           } else {
801             horizontal_shift = z;
802           }
803 
804           SDL_Rect r1;
805           SDL_Rect r2;
806 
807           r1.w = r2.w = SCREENWID;
808           r1.h = r2.h = 1;
809 
810           r2.y = target_line;
811           r1.y = source_line;
812 
813           if (horizontal_shift > 0) {
814             r1.x = horizontal_shift;
815             r2.x = 0;
816           } else {
817             r1.x = 0;
818             r2.x = -horizontal_shift;
819           }
820 
821           SDL_BlitSurface(display, &r1, display, &r2);
822           scr_putbar(0, target_line, SCREENWID, 1, 0, 0, y, 128);
823 
824         }
825       }
826       break;
827     case configuration::waves_nonreflecting:
828       for (int y = 0; y < (SCREENHEI / 2) - height; y++)
829         scr_putbar(0, SCREENHEI/2 + height + y, SCREENWID, 1, 0, 0, 30 + y/2, 255);
830       break;
831     }
832   }
833 
834   wavetime++;
835 }
836 
scr_textlength(const char * s,int chars)837 int scr_textlength(const char *s, int chars) {
838   int len = 0;
839   int pos = 0;
840   mbstate_t state;
841   memset (&state, '\0', sizeof (state));
842   wchar_t tmp;
843 
844   while (s[pos] && (chars > 0)) {
845 
846     size_t nbytes = mbrtowc (&tmp, &s[pos], chars, &state);
847     if (nbytes <= 0)
848       return 0;
849 
850     if (tmp == ' ') {
851       len += FONTMINWID;
852     } else {
853       if (fontchars[tmp & 0xffff].width != 0)
854         len += fontchars[tmp & 0xffff].width + 3;
855     }
856 
857     pos += nbytes;
858     chars -= nbytes;
859   }
860 
861   return len;
862 }
863 
scr_writetext_center(long y,const char * s)864 void scr_writetext_center(long y, const char *s) {
865   scr_writetext ((SCREENWID - scr_textlength(s)) / 2, y, s);
866 }
867 
scr_writetext_broken_center(long y,const char * s)868 void scr_writetext_broken_center(long y, const char *s) {
869 
870   // ok, we try to break the text into several lines, if the lines are longer then the
871   // screenwidth
872 
873   int len = strlen(s);
874   int start = 0;
875   int end = len;
876 
877   while (start < len) {
878 
879     while (scr_textlength(s+start, end-start+1) > SCREENWID) {
880       end--;
881       while ((end > start) && (s[end] != ' ')) end--;
882 
883       if (end == start) {
884         while ((end < len) && (s[end] != ' ')) end++;
885         break;
886       }
887     }
888 
889     if (s[end] == ' ') end--;
890 
891     scr_writetext((SCREENWID - scr_textlength(s+start, end-start+1)) / 2, y, s+start, end-start+1);
892 
893     start = end+1;
894     end = len;
895     while ((start < len) && (s[start] == ' ')) start++;
896 
897     y += 40;
898 
899   }
900 }
901 
scr_putbar(int x,int y,int br,int h,Uint8 colr,Uint8 colg,Uint8 colb,Uint8 alpha)902 void scr_putbar(int x, int y, int br, int h, Uint8 colr, Uint8 colg, Uint8 colb, Uint8 alpha) {
903 
904   if (alpha != 255) {
905 
906     SDL_Surface *s = SDL_CreateRGBSurface(SDL_HWSURFACE | SDL_SRCALPHA, br, h, 16,
907                                           display->format->Rmask, display->format->Gmask,
908                                           display->format->Bmask, 0);
909     SDL_SetAlpha(s, SDL_SRCALPHA, alpha);
910 
911     SDL_Rect r;
912     r.w = br;
913     r.h = h;
914     r.x = 0;
915     r.y = 0;
916 
917     SDL_FillRect(s, &r, SDL_MapRGB(display->format, colr, colg, colb));
918 
919     scr_blit(s, x, y);
920 
921     SDL_FreeSurface(s);
922 
923   } else {
924 
925     SDL_Rect r;
926     r.w = br;
927     r.h = h;
928     r.x = x;
929     r.y = y;
930     SDL_FillRect(display, &r, SDL_MapRGBA(display->format, colr, colg, colb, alpha));
931   }
932 }
933 
934 void
scr_putrect(int x,int y,int br,int h,Uint8 colr,Uint8 colg,Uint8 colb,Uint8 alpha)935 scr_putrect(int x, int y, int br, int h, Uint8 colr, Uint8 colg, Uint8 colb, Uint8 alpha)
936 {
937   scr_putbar(x, y,      1     , h, colr, colg, colb, alpha);
938   scr_putbar(x, y,      br    , 1, colr, colg, colb, alpha);
939   scr_putbar(x + br, y, 1     , h, colr, colg, colb, alpha);
940   scr_putbar(x, y + h , br + 1, 1, colr, colg, colb, alpha);
941 }
942 
943 /* exchange active and inactive page */
scr_swap(void)944 void scr_swap(void) {
945   if (!tt_has_focus) {
946       scr_darkenscreen();
947       SDL_UpdateRect(display, 0, 0, 0, 0);
948       wait_for_focus();
949   }
950   SDL_UpdateRect(display, 0, 0, 0, 0);
951 }
952 
scr_setclipping(int x,int y,int w,int h)953 void scr_setclipping(int x, int y, int w, int h) {
954   if (x < 0) SDL_SetClipRect(display, NULL);
955   else {
956     SDL_Rect r;
957     r.x = x;
958     r.y = y;
959     r.w = w;
960     r.h = h;
961     SDL_SetClipRect(display, &r);
962   }
963 }
964 
scr_blit(SDL_Surface * s,int x,int y)965 void scr_blit(SDL_Surface * s, int x, int y) {
966   SDL_Rect r;
967   r.w = s->w;
968   r.h = s->h;
969   r.x = x;
970   r.y = y;
971   SDL_BlitSurface(s, NULL, display, &r);
972 }
973 
974 
975 /* draws the tower and the doors */
draw_tower(long vert,long angle)976 static void draw_tower(long vert, long angle) {
977 
978   puttower(angle, vert, lev_towerrows());
979 
980   int slice = 0;
981   int ypos = SCREENHEI / 2 - SPR_SLICEHEI + vert;
982 
983   while (ypos > SCREENHEI) {
984     slice++;
985     ypos -= SPR_SLICEHEI;
986   }
987 
988   while ((ypos > -SPR_SLICEHEI) && (slice < lev_towerrows())) {
989 
990     for (int col = 0; col < 16; col++) {
991 
992       int a = (col * 8 + angle + 36) % TOWER_ANGLES;
993 
994       if ((a > 72) || !doors[a].width)
995         continue;
996 
997       if (lev_is_door(slice, col)) {
998         if (lev_is_door_upperend(slice, col))
999           scr_blit(restsprites.data(doors[a].s[2]), (SCREENWID / 2) + doors[a].xstart, ypos);
1000         else if (lev_is_door_upperend(slice - 1, col))
1001           scr_blit(restsprites.data(doors[a].s[1]), (SCREENWID / 2) + doors[a].xstart, ypos);
1002         else
1003           scr_blit(restsprites.data(doors[a].s[0]), (SCREENWID / 2) + doors[a].xstart, ypos);
1004       }
1005     }
1006 
1007     slice++;
1008     ypos -= SPR_SLICEHEI;
1009   }
1010 }
1011 
draw_tower_editor(long vert,long angle,int state)1012 static void draw_tower_editor(long vert, long angle, int state) {
1013 
1014   puttower(angle, vert, lev_towerrows());
1015 
1016 
1017   int slice = 0;
1018   int ypos = SCREENHEI / 2 - SPR_SLICEHEI + vert;
1019 
1020   while (ypos > SCREENHEI) {
1021     slice++;
1022     ypos -= SPR_SLICEHEI;
1023   }
1024 
1025   while ((ypos > -SPR_SLICEHEI) && (slice < lev_towerrows())) {
1026 
1027     for (int col = 0; col < 16; col++) {
1028 
1029       int a = (col * 8 + angle + 36) % TOWER_ANGLES;
1030 
1031       if ((a > 72) || !doors[a].width)
1032         continue;
1033 
1034       if (lev_is_door(slice, col)) {
1035 
1036         if (lev_is_targetdoor(slice, col) && (state & 1)) continue;
1037 
1038         if (lev_is_door_upperend(slice, col))
1039           scr_blit(restsprites.data(doors[a].s[2]), (SCREENWID / 2) + doors[a].xstart, ypos);
1040         else if (lev_is_door_upperend(slice - 1, col))
1041           scr_blit(restsprites.data(doors[a].s[1]), (SCREENWID / 2) + doors[a].xstart, ypos);
1042         else
1043           scr_blit(restsprites.data(doors[a].s[0]), (SCREENWID / 2) + doors[a].xstart, ypos);
1044       }
1045     }
1046 
1047     slice++;
1048     ypos -= SPR_SLICEHEI;
1049   }
1050 }
1051 
1052 
1053 /* draws something of the environment */
putcase(unsigned char w,long x,long h)1054 static void putcase(unsigned char w, long x, long h) {
1055   long angle = 0;
1056   switch (w) {
1057 
1058   case TB_EMPTY:
1059     /* blank case */
1060     break;
1061 
1062   case TB_ELEV_BOTTOM:
1063   case TB_ELEV_TOP:
1064   case TB_ELEV_MIDDLE:
1065     scr_blit(restsprites.data((angle % SPR_ELEVAFRAMES) + elevatorsprite), x - (SPR_ELEVAWID / 2), h);
1066 
1067     break;
1068 
1069   case TB_STEP:
1070   case TB_STEP_VANISHER:
1071   case TB_STEP_LSLIDER:
1072   case TB_STEP_RSLIDER:
1073     scr_blit(restsprites.data((angle % SPR_STEPFRAMES) + step), x - (SPR_STEPWID / 2), h);
1074 
1075     break;
1076 
1077   case TB_STICK:
1078   case TB_STICK_BOTTOM:
1079   case TB_STICK_MIDDLE:
1080   case TB_STICK_DOOR:
1081   case TB_STICK_DOOR_TARGET:
1082     scr_blit(restsprites.data(stick), x - (SPR_STICKWID / 2), h);
1083 
1084     break;
1085 
1086   case TB_BOX:
1087     scr_blit(objectsprites.data(boxst + boxstate), x - (SPR_BOXWID / 2), h);
1088 
1089     break;
1090   }
1091 }
1092 
putcase_editor(unsigned char w,long x,long h,int state)1093 static void putcase_editor(unsigned char w, long x, long h, int state) {
1094   long angle = 0;
1095   switch (w) {
1096 
1097   case TB_EMPTY:
1098     /* blank case */
1099     break;
1100 
1101   case TB_ELEV_BOTTOM:
1102     scr_blit(restsprites.data((angle % SPR_ELEVAFRAMES) + elevatorsprite), x - (SPR_ELEVAWID / 2), h - (state % 4));
1103     break;
1104   case TB_STATION_MIDDLE:
1105     scr_blit(restsprites.data((angle % SPR_ELEVAFRAMES) + elevatorsprite), x - (SPR_ELEVAWID / 2), h - SPR_SLICEHEI/2 + abs(state - 8));
1106     break;
1107   case TB_STATION_TOP:
1108     scr_blit(restsprites.data((angle % SPR_ELEVAFRAMES) + elevatorsprite), x - (SPR_ELEVAWID / 2), h + (state % 4));
1109     break;
1110   case TB_STEP:
1111     scr_blit(restsprites.data(((angle % SPR_STEPFRAMES) + step)), x - (SPR_STEPWID / 2), h);
1112     break;
1113   case TB_STEP_VANISHER:
1114     if (config.use_alpha_sprites()) {
1115       SDL_Surface *s = SDL_CreateRGBSurface(SDL_HWSURFACE | SDL_SRCALPHA, SPR_STEPWID, SPR_STEPHEI, 24, 0xff, 0xff00, 0xff0000, 0);
1116       SDL_Rect r;
1117       r.w = SPR_STEPWID;
1118       r.h = SPR_STEPHEI;
1119       r.x = 0;
1120       r.y = 0;
1121       SDL_BlitSurface(restsprites.data(((angle % SPR_STEPFRAMES) + step)), NULL, s, &r);
1122       SDL_SetAlpha(s, SDL_SRCALPHA, 96);
1123       scr_blit(s, x - (SPR_STEPWID / 2), h);
1124       SDL_FreeSurface(s);
1125     } else {
1126       if (state & 1)
1127         scr_blit(restsprites.data(((angle % SPR_STEPFRAMES) + step)), x - (SPR_STEPWID / 2), h);
1128     }
1129     break;
1130   case TB_STEP_LSLIDER:
1131     scr_blit(restsprites.data(((angle % SPR_STEPFRAMES) + step)), x - (SPR_STEPWID / 2) + state % 4, h);
1132     break;
1133 
1134   case TB_STEP_RSLIDER:
1135     scr_blit(restsprites.data(((angle % SPR_STEPFRAMES) + step)), x - (SPR_STEPWID / 2) - state % 4, h);
1136     break;
1137 
1138   case TB_STICK:
1139     scr_blit(restsprites.data(stick), x - (SPR_STICKWID / 2), h);
1140     break;
1141 
1142   case TB_BOX:
1143     scr_blit(objectsprites.data(boxst + boxstate), x - (SPR_BOXWID / 2), h);
1144     break;
1145 
1146   case TB_ROBOT1:
1147     scr_blit(objectsprites.data(ballst + 1), x - (SPR_ROBOTWID / 2), h - SPR_ROBOTHEI/2);
1148     break;
1149   case TB_ROBOT2:
1150     scr_blit(objectsprites.data(ballst), x - (SPR_ROBOTWID / 2) + state / 2, h - SPR_ROBOTHEI/2);
1151     break;
1152   case TB_ROBOT3:
1153     scr_blit(objectsprites.data(ballst), x - (SPR_ROBOTWID / 2), h - SPR_ROBOTHEI/2);
1154     break;
1155   case TB_ROBOT4:
1156     scr_blit(objectsprites.data(robots[lev_robotnr()].start + state % robots[lev_robotnr()].count),
1157              x - (SPR_ROBOTWID / 2), h - SPR_ROBOTHEI/2 + abs(state - 8));
1158     break;
1159   case TB_ROBOT5:
1160     scr_blit(objectsprites.data(robots[lev_robotnr()].start + state % robots[lev_robotnr()].count),
1161              x - (SPR_ROBOTWID / 2), h - SPR_ROBOTHEI + abs(state - 8) * 2);
1162     break;
1163   case TB_ROBOT6:
1164     scr_blit(objectsprites.data(robots[lev_robotnr()].start + state % robots[lev_robotnr()].count),
1165              x - (SPR_ROBOTWID / 2) + abs(state - 8), h - SPR_SLICEHEI/2);
1166     break;
1167   case TB_ROBOT7:
1168     scr_blit(objectsprites.data(robots[lev_robotnr()].start + state % robots[lev_robotnr()].count),
1169              x - (SPR_ROBOTWID / 2) + 2 * abs(state - 8), h - SPR_SLICEHEI/2);
1170     break;
1171   }
1172 }
1173 
1174 
1175 /* draws a robot */
putrobot(int t,int m,long x,long h)1176 static void putrobot(int t, int m, long x, long h)
1177 {
1178   int nr;
1179 
1180   if (h > (SCREENHEI + SPR_ROBOTHEI)) return;
1181 
1182   switch (t) {
1183 
1184     case OBJ_KIND_JUMPBALL:
1185       nr = ballst;
1186       break;
1187 
1188     case OBJ_KIND_FREEZEBALL:
1189     case OBJ_KIND_FREEZEBALL_FROZEN:
1190     case OBJ_KIND_FREEZEBALL_FALLING:
1191       nr = ballst + 1;
1192       break;
1193 
1194     case OBJ_KIND_DISAPPEAR:
1195       nr = starst + m * 2;
1196       break;
1197 
1198     case OBJ_KIND_APPEAR:
1199       nr = starst - m * 2 + 16;
1200       break;
1201 
1202     case OBJ_KIND_ROBOT_VERT:
1203     case OBJ_KIND_ROBOT_HORIZ:
1204       nr = robots[lev_robotnr()].start + ((m / 2) % robots[lev_robotnr()].count);
1205       break;
1206 
1207     default:
1208       nr = 40;
1209       break;
1210   }
1211 
1212   scr_blit(objectsprites.data(nr), x + (SCREENWID / 2) - (SPR_ROBOTWID / 2), h - SPR_ROBOTHEI);
1213 }
1214 
scr_writetext(long x,long y,const char * s,int maxchars)1215 void scr_writetext(long x, long y, const char *s, int maxchars) {
1216 
1217   mbstate_t state;
1218   memset (&state, '\0', sizeof (state));
1219   wchar_t tmp;
1220 
1221   int t = 0;
1222 
1223   if (maxchars == -1)
1224     maxchars = strlen(s);
1225 
1226   while (s[t] && (maxchars > 0)) {
1227 
1228     size_t nbytes = mbrtowc (&tmp, &s[t], maxchars, &state);
1229 
1230     if (nbytes >= (size_t) -2) {
1231       return;
1232     }
1233 
1234     if (tmp == ' ') {
1235       x += FONTMINWID;
1236       t += nbytes;
1237       maxchars -= nbytes;
1238       continue;
1239     }
1240 
1241     if (fontchars[tmp & 0xffff].width != 0) {
1242       scr_blit(fontsprites.data(fontchars[tmp & 0xffff].s), x, y-20);
1243       x += fontchars[tmp & 0xffff].width + 3;
1244     }
1245 
1246     t += nbytes;
1247     maxchars -= nbytes;
1248   }
1249 }
1250 
scr_writeformattext(long x,long y,const char * s)1251 void scr_writeformattext(long x, long y, const char *s) {
1252 
1253   mbstate_t state;
1254   memset (&state, '\0', sizeof (state));
1255   wchar_t tmp;
1256 
1257   int len = strlen(s);
1258 
1259   int origx = x;
1260   int t = 0;
1261   Uint8 towerblock = 0;
1262   while (t < len) {
1263 
1264     size_t nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1265 
1266     if (nbytes >= (size_t) -2) {
1267       return;
1268     }
1269 
1270     t += nbytes;
1271 
1272     switch(tmp) {
1273     case ' ':
1274       x += FONTMINWID;
1275       break;
1276     case '~':
1277 
1278       nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1279       if (nbytes >= (size_t) -2) {
1280         return;
1281       }
1282       t += nbytes;
1283 
1284       switch(tmp) {
1285       case 't':
1286 
1287         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1288         if (nbytes >= (size_t) -2) {
1289           return;
1290         }
1291         t += nbytes;
1292 
1293         x = (tmp - '0') * 100;
1294 
1295         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1296         if (nbytes >= (size_t) -2) {
1297           return;
1298         }
1299         t += nbytes;
1300 
1301         x += (tmp - '0') * 10;
1302 
1303         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1304         if (nbytes >= (size_t) -2) {
1305           return;
1306         }
1307         t += nbytes;
1308 
1309         x += (tmp - '0');
1310 
1311         break;
1312       case 'T':
1313         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1314         if (nbytes >= (size_t) -2) {
1315           return;
1316         }
1317         t += nbytes;
1318 
1319         x = origx + (tmp - '0') * 100;
1320 
1321         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1322         if (nbytes >= (size_t) -2) {
1323           return;
1324         }
1325         t += nbytes;
1326 
1327         x += (tmp - '0') * 10;
1328 
1329         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1330         if (nbytes >= (size_t) -2) {
1331           return;
1332         }
1333         t += nbytes;
1334 
1335         x += (tmp - '0');
1336 
1337         break;
1338       case 'b':
1339 
1340         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1341         if (nbytes >= (size_t) -2) {
1342           return;
1343         }
1344         t += nbytes;
1345 
1346         towerblock = conv_char2towercode(tmp);
1347         putcase(towerblock, x+16, y);
1348         x += 32;
1349         break;
1350       case 'e':
1351         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1352         if (nbytes >= (size_t) -2) {
1353           return;
1354         }
1355         t += nbytes;
1356 
1357         towerblock = conv_char2towercode(tmp);
1358         putcase_editor(towerblock, x+16, y, boxstate);
1359         x += 32;
1360         break;
1361       default:
1362         assert_msg(0, "Wrong command in formatted text.");
1363       }
1364       break;
1365     default:
1366       if (fontchars[tmp & 0xffff].width != 0) {
1367         scr_blit(fontsprites.data(fontchars[tmp & 0xffff].s), x, y-20);
1368         x += fontchars[tmp & 0xffff].width + 3;
1369       }
1370     }
1371   }
1372 }
1373 
scr_formattextlength(long x,long y,const char * s)1374 long scr_formattextlength(long x, long y, const char *s) {
1375   mbstate_t state;
1376   memset (&state, '\0', sizeof (state));
1377   wchar_t tmp;
1378 
1379   int len = strlen(s);
1380 
1381   int origx = x;
1382   int t = 0;
1383   while (t < len) {
1384 
1385     size_t nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1386     if (nbytes >= (size_t) -2) return 0;
1387     t += nbytes;
1388 
1389     switch(tmp) {
1390     case ' ':
1391       x += FONTMINWID;
1392       break;
1393     case '~':
1394 
1395       nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1396       if (nbytes >= (size_t) -2) return 0;
1397       t += nbytes;
1398 
1399       switch(tmp) {
1400       case 't':
1401 
1402         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1403         if (nbytes >= (size_t) -2) return 0;
1404         t += nbytes;
1405         x = (tmp - '0') * 100;
1406 
1407         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1408         if (nbytes >= (size_t) -2) return 0;
1409         t += nbytes;
1410         x += (tmp - '0') * 10;
1411 
1412         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1413         if (nbytes >= (size_t) -2) return 0;
1414         t += nbytes;
1415         x += (tmp - '0');
1416 
1417         break;
1418       case 'T':
1419         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1420         if (nbytes >= (size_t) -2) return 0;
1421         t += nbytes;
1422         x = origx + (tmp - '0') * 100;
1423 
1424         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1425         if (nbytes >= (size_t) -2) return 0;
1426         t += nbytes;
1427         x += (tmp - '0') * 10;
1428 
1429         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1430         if (nbytes >= (size_t) -2) return 0;
1431         t += nbytes;
1432         x += (tmp - '0');
1433 
1434         break;
1435       case 'b':
1436         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1437         if (nbytes >= (size_t) -2) return 0;
1438         t += nbytes;
1439         x += 32;
1440         break;
1441       case 'e':
1442         nbytes = mbrtowc (&tmp, &s[t], len-t, &state);
1443         if (nbytes >= (size_t) -2) return 0;
1444         t += nbytes;
1445         x += 32;
1446         break;
1447       default:
1448         assert_msg(0, "Wrong command in formatted text.");
1449       }
1450       break;
1451     default:
1452       if (fontchars[tmp & 0xffff].width != 0) {
1453         x += fontchars[tmp & 0xffff].width + 3;
1454       }
1455     }
1456   }
1457 
1458   return (x-origx);
1459 }
1460 
1461 /* draws something of the tower */
1462 
1463 /* vert is the vertical position of the tower
1464  * a is the angle on the tower to be drawn: 0 front, 32 right, ...
1465  * angle is the angle of the tower: 0 column 0 in front, 8, column 1, ...
1466  * hs, he are start and ending rows to be drawn
1467  */
putthings(long vert,long a,long angle)1468 static void putthings(long vert, long a, long angle) {
1469 
1470   /* ok, at first lets check if there is a column right at the
1471    angle to be drawn */
1472   if (((a - angle) & 0x7) == 0) {
1473 
1474     /* yes there is one, find out wich one */
1475     int col = ((a - angle) / TOWER_STEPS_PER_COLUMN) & (TOWER_COLUMNS - 1);
1476 
1477     /* calc the x pos where the thing has to be drawn */
1478     int x = sintab[a % TOWER_ANGLES] + (SCREENWID/2);
1479 
1480     int slice = 0;
1481     int ypos = SCREENHEI / 2 - SPR_SLICEHEI + vert;
1482 
1483     while ((ypos > -SPR_SLICEHEI) && (slice < lev_towerrows())) {
1484 
1485       /* if we are over the bottom of the screen, draw the slice */
1486       if (ypos < SCREENHEI)
1487         putcase(lev_tower(slice, col), x, ypos);
1488 
1489       slice++;
1490       ypos -= SPR_SLICEHEI;
1491     }
1492   }
1493 
1494   /* and now check for robots to be drawn */
1495   for (int rob = 0; rob < 4; rob++) {
1496 
1497     /* if the the current robot is active and not the cross */
1498     if (rob_kind(rob) != OBJ_KIND_NOTHING && rob_kind(rob) != OBJ_KIND_CROSS) {
1499 
1500       /* ok calc the angle the robots needs to be drawn at */
1501       int rob_a = (rob_angle(rob) - 4 + angle) & (TOWER_ANGLES - 1);
1502 
1503       /* check if the robot is "inside" the current column */
1504       if (rob_a > a - 2 && rob_a <= a + 2)
1505         putrobot(rob_kind(rob), rob_time(rob),
1506                  sintab[rob_a], SCREENHEI / 2 + vert - rob_vertical(rob) * 4);
1507     }
1508   }
1509 }
1510 
putthings_editor(long vert,long a,long angle,int state)1511 static void putthings_editor(long vert, long a, long angle, int state) {
1512 
1513   /* ok, at first lets check if there is a column right at the
1514    angle to be drawn */
1515   if (((a - angle) & 0x7) == 0) {
1516 
1517     /* yes there is one, find out wich one */
1518     int col = ((a - angle) / TOWER_STEPS_PER_COLUMN) & (TOWER_COLUMNS - 1);
1519 
1520     /* calc the x pos where the thing has to be drawn */
1521     int x = sintab[a % TOWER_ANGLES] + (SCREENWID/2);
1522 
1523     int slice = 0;
1524     int ypos = SCREENHEI / 2 - SPR_SLICEHEI + vert;
1525 
1526     while ((ypos > -SPR_SLICEHEI) && (slice < lev_towerrows())) {
1527 
1528       /* if we are over the bottom of the screen, draw the slice */
1529       if (ypos < SCREENHEI)
1530         putcase_editor(lev_tower(slice, col), x, ypos, state);
1531 
1532       slice++;
1533       ypos -= SPR_SLICEHEI;
1534     }
1535   }
1536 }
1537 
1538 /* draws everything behind the tower */
draw_behind(long vert,long angle)1539 static void draw_behind(long vert, long angle)
1540 {
1541   for (int a = 0; a < 16; a ++) {
1542 /* angle 48 to 31 */
1543     putthings(vert, 48 - a, angle);
1544 /* amgle 80 to 95 */
1545     putthings(vert, 80 + a, angle);
1546   }
1547 }
1548 
1549 
draw_behind_editor(long vert,long angle,int state)1550 static void draw_behind_editor(long vert, long angle, int state)
1551 {
1552   for (int a = 0; a < 16; a ++) {
1553     putthings_editor(vert, 48 - a, angle, state);
1554     putthings_editor(vert, 80 + a, angle, state);
1555   }
1556 }
1557 
1558 /* draws everything in front of the tower */
draw_before(long vert,long angle)1559 static void draw_before(long  vert, long angle)
1560 {
1561   for (int a = 0; a < 32; a ++) {
1562     putthings(vert, 32 - a, angle);
1563     putthings(vert, 96 + a, angle);
1564   }
1565   putthings(vert, 0, angle);
1566 }
1567 
draw_before_editor(long vert,long angle,int state)1568 static void draw_before_editor(long  vert, long angle, int state)
1569 {
1570   for (int a = 0; a < 32; a ++) {
1571     putthings_editor(vert, 32 - a, angle, state);
1572     putthings_editor(vert, 96 + a, angle, state);
1573   }
1574   putthings_editor(vert, 0, angle, state);
1575 }
1576 
1577 /* draws the cross that flies over the screen */
putcross(long vert)1578 static void putcross(long vert)
1579 {
1580   long i, y;
1581 
1582   for (int t = 0; t < 4; t++) {
1583     if (rob_kind(t) == OBJ_KIND_CROSS) {
1584       i = (rob_angle(t) - 60) * 5;
1585       y = (vert - rob_vertical(t)) * 4 + (SCREENHEI / 2) - SPR_CROSSHEI;
1586       if (y > -SPR_CROSSHEI && y < SCREENHEI)
1587         scr_blit(objectsprites.data(crossst + labs(rob_time(t)) % 120), i + (SCREENWID - SPR_CROSSWID) / 2, y);
1588       return;
1589     }
1590   }
1591 }
1592 
1593 /* draws the points, time and lifes left */
draw_data(int time,screenflag flags)1594 static void draw_data(int time, screenflag flags)
1595 {
1596   char s[256];
1597   int t;
1598   int y = config.status_top() ? 5 : SCREENHEI - FONTHEI;
1599 
1600   if (time > 0) {
1601     snprintf(s, 256, "%u", time);
1602     scr_writetext_center(y, s);
1603   }
1604 
1605   snprintf(s, 256, "%u", pts_points());
1606   scr_writetext(5L, y, s);
1607 
1608   *s = '\0';
1609   if (pts_lifes() < 4)
1610     for (t = 0; t < pts_lifes(); t++)
1611       snprintf(s + strlen(s), 256-strlen(s), "%c", fonttoppler);
1612   else snprintf(s, 256, "%ix%c", pts_lifes(), fonttoppler);
1613   scr_writetext(SCREENWID - scr_textlength(s) - 5, y, s);
1614 
1615   y = config.status_top() ? SCREENHEI - FONTHEI : 5;
1616   switch (flags) {
1617     case SF_REC:  if (!(boxstate & 8)) scr_writetext_center(y, _("REC")); break;
1618     case SF_DEMO: scr_writetext_center(y, _("DEMO")); break;
1619     case SF_NONE:
1620     default:      break;
1621   }
1622 }
1623 
scr_drawall(long vert,long angle,long time,bool svisible,int subshape,int substart,screenflag flags)1624 void scr_drawall(long vert,
1625                  long angle,
1626                  long time,
1627                  bool svisible,
1628                  int subshape,
1629                  int substart,
1630                  screenflag flags
1631                 ) {
1632 
1633   cleardesk(vert);
1634 
1635   sts_blink();
1636   sts_draw();
1637   draw_behind(vert * 4, angle);
1638   draw_tower(vert * 4, angle);
1639   draw_before(vert * 4, angle);
1640 
1641   if (snb_exists())
1642     scr_blit(objectsprites.data(snowballst),
1643              sintab[(snb_anglepos() + angle) % TOWER_ANGLES] + (SCREENWID / 2) - (SPR_HEROWID - SPR_AMMOWID),
1644              ((vert - snb_verticalpos()) * 4) + (SCREENHEI / 2) - SPR_AMMOHEI);
1645 
1646   if (top_visible()) {
1647       scr_blit(objectsprites.data(topplerstart + top_shape() +
1648                               ((top_look_left()) ?  mirror : 0)),
1649                (SCREENWID / 2) - (SPR_HEROWID / 2),
1650                (vert - top_verticalpos()) * 4 + (SCREENHEI / 2) - SPR_HEROHEI);
1651 
1652     if (top_onelevator())
1653       scr_blit(restsprites.data((angle % SPR_ELEVAFRAMES) + elevatorsprite), (SCREENWID / 2) - (SPR_ELEVAWID / 2),
1654                vert - top_verticalpos() + (SCREENHEI / 2));
1655   }
1656 
1657   if (svisible) {
1658     scr_blit(objectsprites.data(subst + subshape),
1659              (SCREENWID / 2) - 70,
1660              (SCREENHEI / 2) + 12 - substart + 16);
1661 
1662   }
1663 
1664   putcross(vert);
1665 
1666   putbattlement(angle, vert * 4);
1667 
1668   putwater(vert);
1669 
1670   draw_data(time, flags);
1671 
1672   if (dcl_wait_overflow())
1673     scr_putbar(0, 0, 5, 5, 255, 0, 0, 255);
1674 
1675   boxstate = (boxstate + 1) & 0xf;
1676 }
1677 
scr_drawedit(long vpos,long apos,bool showtime)1678 void scr_drawedit(long vpos, long apos, bool showtime) {
1679 
1680   long t;
1681   static long vert = 0, angle = 0;
1682 
1683   if (vpos != vert) {
1684     if (vpos > vert) {
1685       if (vpos > vert + 0x8)
1686         vert += 4;
1687       else
1688         vert += 1;
1689     } else {
1690       if (vpos < vert - 0x8)
1691         vert -= 4;
1692       else
1693         vert -= 1;
1694     }
1695   }
1696 
1697   apos &= 0x7f;
1698 
1699   t = (apos - angle) & (TOWER_ANGLES - 1);
1700 
1701   if (t != 0) {
1702     if (t < 0x3f) {
1703       if (t > 0x8)
1704         angle += 4;
1705       else
1706         angle += 1;
1707     } else {
1708       if (t < 0x7f-0x8)
1709         angle -= 4;
1710       else
1711         angle -= 1;
1712     }
1713     angle &= 0x7f;
1714   }
1715 
1716   cleardesk(vert);
1717 
1718   draw_behind_editor(vert * 4, angle, boxstate);
1719   draw_tower_editor(vert * 4, angle, boxstate);
1720   draw_before_editor(vert * 4, angle, boxstate);
1721 
1722   putbattlement(angle, vert * 4);
1723 
1724   putwater(vert);
1725 
1726   if (boxstate & 1) {
1727     scr_putrect((SCREENWID / 2) - (32 / 2), (SCREENHEI / 2) - 16, 32, 16,
1728                 boxstate * 0xf, boxstate *0xf, boxstate *0xf, 128);
1729   }
1730 
1731   if (showtime) {
1732       char s[20];
1733       snprintf(s, 20, "%u", lev_towertime());
1734       scr_writetext_center(5, s);
1735   }
1736 
1737   boxstate = (boxstate + 1) & 0xf;
1738 }
1739 
put_scrollerlayer(long horiz,int layer)1740 static void put_scrollerlayer(long horiz, int layer) {
1741   horiz += scroll_layers[layer].xpos;
1742   horiz %= scroll_layers[layer].xrepeat;
1743   scr_blit(layersprites.data(scroll_layers[layer].image), -horiz, scroll_layers[layer].ypos);
1744   if (horiz + SCREENWID > scroll_layers[layer].xrepeat)
1745     scr_blit(layersprites.data(scroll_layers[layer].image),
1746              scroll_layers[layer].width - horiz, scroll_layers[layer].ypos);
1747 }
1748 
scr_draw_bonus1(long horiz,long towerpos)1749 void scr_draw_bonus1(long horiz, long towerpos) {
1750   int l;
1751 
1752   if (config.use_full_scroller())
1753     for (l = 0; (l < num_scrolllayers) && (l < sl_tower_depth); l++)
1754       put_scrollerlayer(scroll_layers[l].num*horiz/scroll_layers[l].den, l);
1755   else
1756     put_scrollerlayer(scroll_layers[0].num*horiz/scroll_layers[0].den, 0);
1757 
1758   puttower(0, SCREENHEI/2, SCREENHEI, sl_tower_num*towerpos/sl_tower_den);
1759 }
1760 
scr_draw_bonus2(long horiz,long towerpos)1761 void scr_draw_bonus2(long horiz, long towerpos) {
1762   int l;
1763 
1764   if (config.use_full_scroller())
1765     for (l = sl_tower_depth; l < num_scrolllayers; l++)
1766       put_scrollerlayer(scroll_layers[l].num*horiz/scroll_layers[l].den, l);
1767   else
1768     put_scrollerlayer(scroll_layers[num_scrolllayers-1].num*horiz/scroll_layers[num_scrolllayers-1].den, num_scrolllayers-1);
1769 
1770   draw_data(-1, SF_NONE);
1771 }
1772 
scr_draw_submarine(long vert,long x,long number)1773 void scr_draw_submarine(long vert, long x, long number) {
1774   scr_blit(objectsprites.data(subst+number), x, vert);
1775 }
1776 
scr_draw_fish(long vert,long x,long number)1777 void scr_draw_fish(long vert, long x, long number) {
1778   scr_blit(objectsprites.data(fishst+number), x, vert);
1779 }
1780 
scr_draw_torpedo(long vert,long x)1781 void scr_draw_torpedo(long vert, long x) {
1782   scr_blit(objectsprites.data(torb), x, vert);
1783 }
1784