1 /*
2  * OpenTyrian: A modern cross-platform port of Tyrian
3  * Copyright (C) 2007-2009  The OpenTyrian Development Team
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 #include "opentyr.h"
20 #include "backgrnd.h"
21 
22 #include "config.h"
23 #include "mtrand.h"
24 #include "varz.h"
25 #include "video.h"
26 
27 #include <assert.h>
28 
29 /*Special Background 2 and Background 3*/
30 
31 /*Back Pos 3*/
32 JE_word backPos, backPos2, backPos3;
33 JE_word backMove, backMove2, backMove3;
34 
35 /*Main Maps*/
36 JE_word mapX, mapY, mapX2, mapX3, mapY2, mapY3;
37 JE_byte **mapYPos, **mapY2Pos, **mapY3Pos;
38 JE_word mapXPos, oldMapXOfs, mapXOfs, mapX2Ofs, mapX2Pos, mapX3Pos, oldMapX3Ofs, mapX3Ofs, tempMapXOfs;
39 intptr_t mapXbpPos, mapX2bpPos, mapX3bpPos;
40 JE_byte map1YDelay, map1YDelayMax, map2YDelay, map2YDelayMax;
41 
42 
43 JE_boolean  anySmoothies;
44 JE_byte     smoothie_data[9]; /* [1..9] */
45 
JE_darkenBackground(JE_word neat)46 void JE_darkenBackground( JE_word neat )  /* wild detail level */
47 {
48 	Uint8 *s = VGAScreen->pixels; /* screen pointer, 8-bit specific */
49 	int x, y;
50 
51 	s += 24;
52 
53 	for (y = 184; y; y--)
54 	{
55 		for (x = 264; x; x--)
56 		{
57 			*s = ((((*s & 0x0f) << 4) - (*s & 0x0f) + ((((x - neat - y) >> 2) + *(s-2) + (y == 184 ? 0 : *(s-(VGAScreen->pitch-1)))) & 0x0f)) >> 4) | (*s & 0xf0);
58 			s++;
59 		}
60 		s += VGAScreen->pitch - 264;
61 	}
62 }
63 
blit_background_row(SDL_Surface * surface,int x,int y,Uint8 ** map)64 void blit_background_row( SDL_Surface *surface, int x, int y, Uint8 **map )
65 {
66 	assert(surface->format->BitsPerPixel == 8);
67 
68 	Uint8 *pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x,
69 	      *pixels_ll = (Uint8 *)surface->pixels,  // lower limit
70 	      *pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch);  // upper limit
71 
72 	for (int y = 0; y < 28; y++)
73 	{
74 		// not drawing on screen yet; skip y
75 		if ((pixels + (12 * 24)) < pixels_ll)
76 		{
77 			pixels += surface->pitch;
78 			continue;
79 		}
80 
81 		for (int tile = 0; tile < 12; tile++)
82 		{
83 			Uint8 *data = *(map + tile);
84 
85 			// no tile; skip tile
86 			if (data == NULL)
87 			{
88 				pixels += 24;
89 				continue;
90 			}
91 
92 			data += y * 24;
93 
94 			for (int x = 24; x; x--)
95 			{
96 				if (pixels >= pixels_ul)
97 					return;
98 				if (pixels >= pixels_ll && *data != 0)
99 					*pixels = *data;
100 
101 				pixels++;
102 				data++;
103 			}
104 		}
105 
106 		pixels += surface->pitch - 12 * 24;
107 	}
108 }
109 
blit_background_row_blend(SDL_Surface * surface,int x,int y,Uint8 ** map)110 void blit_background_row_blend( SDL_Surface *surface, int x, int y, Uint8 **map )
111 {
112 	assert(surface->format->BitsPerPixel == 8);
113 
114 	Uint8 *pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x,
115 	      *pixels_ll = (Uint8 *)surface->pixels,  // lower limit
116 	      *pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch);  // upper limit
117 
118 	for (int y = 0; y < 28; y++)
119 	{
120 		// not drawing on screen yet; skip y
121 		if ((pixels + (12 * 24)) < pixels_ll)
122 		{
123 			pixels += surface->pitch;
124 			continue;
125 		}
126 
127 		for (int tile = 0; tile < 12; tile++)
128 		{
129 			Uint8 *data = *(map + tile);
130 
131 			// no tile; skip tile
132 			if (data == NULL)
133 			{
134 				pixels += 24;
135 				continue;
136 			}
137 
138 			data += y * 24;
139 
140 			for (int x = 24; x; x--)
141 			{
142 				if (pixels >= pixels_ul)
143 					return;
144 				if (pixels >= pixels_ll && *data != 0)
145 					*pixels = (*data & 0xf0) | (((*pixels & 0x0f) + (*data & 0x0f)) / 2);
146 
147 				pixels++;
148 				data++;
149 			}
150 		}
151 
152 		pixels += surface->pitch - 12 * 24;
153 	}
154 }
155 
draw_background_1(SDL_Surface * surface)156 void draw_background_1( SDL_Surface *surface )
157 {
158 	SDL_FillRect(surface, NULL, 0);
159 
160 	Uint8 **map = (Uint8 **)mapYPos + mapXbpPos - 12;
161 
162 	for (int i = -1; i < 7; i++)
163 	{
164 		blit_background_row(surface, mapXPos, (i * 28) + backPos, map);
165 
166 		map += 14;
167 	}
168 }
169 
draw_background_2(SDL_Surface * surface)170 void draw_background_2( SDL_Surface *surface )
171 {
172 	if (map2YDelayMax > 1 && backMove2 < 2)
173 		backMove2 = (map2YDelay == 1) ? 1 : 0;
174 
175 	if (background2 != 0)
176 	{
177 		// water effect combines background 1 and 2 by syncronizing the x coordinate
178 		int x = smoothies[1] ? mapXPos : mapX2Pos;
179 
180 		Uint8 **map = (Uint8 **)mapY2Pos + (smoothies[1] ? mapXbpPos : mapX2bpPos) - 12;
181 
182 		for (int i = -1; i < 7; i++)
183 		{
184 			blit_background_row(surface, x, (i * 28) + backPos2, map);
185 
186 			map += 14;
187 		}
188 	}
189 
190 	/*Set Movement of background*/
191 	if (--map2YDelay == 0)
192 	{
193 		map2YDelay = map2YDelayMax;
194 
195 		backPos2 += backMove2;
196 
197 		if (backPos2 >  27)
198 		{
199 			backPos2 -= 28;
200 			mapY2--;
201 			mapY2Pos -= 14;  /*Map Width*/
202 		}
203 	}
204 }
205 
draw_background_2_blend(SDL_Surface * surface)206 void draw_background_2_blend( SDL_Surface *surface )
207 {
208 	if (map2YDelayMax > 1 && backMove2 < 2)
209 		backMove2 = (map2YDelay == 1) ? 1 : 0;
210 
211 	Uint8 **map = (Uint8 **)mapY2Pos + mapX2bpPos - 12;
212 
213 	for (int i = -1; i < 7; i++)
214 	{
215 		blit_background_row_blend(surface, mapX2Pos, (i * 28) + backPos2, map);
216 
217 		map += 14;
218 	}
219 
220 	/*Set Movement of background*/
221 	if (--map2YDelay == 0)
222 	{
223 		map2YDelay = map2YDelayMax;
224 
225 		backPos2 += backMove2;
226 
227 		if (backPos2 >  27)
228 		{
229 			backPos2 -= 28;
230 			mapY2--;
231 			mapY2Pos -= 14;  /*Map Width*/
232 		}
233 	}
234 }
235 
draw_background_3(SDL_Surface * surface)236 void draw_background_3( SDL_Surface *surface )
237 {
238 	/* Movement of background */
239 	backPos3 += backMove3;
240 
241 	if (backPos3 > 27)
242 	{
243 		backPos3 -= 28;
244 		mapY3--;
245 		mapY3Pos -= 15;   /*Map Width*/
246 	}
247 
248 	Uint8 **map = (Uint8 **)mapY3Pos + mapX3bpPos - 12;
249 
250 	for (int i = -1; i < 7; i++)
251 	{
252 		blit_background_row(surface, mapX3Pos, (i * 28) + backPos3, map);
253 
254 		map += 15;
255 	}
256 }
257 
JE_filterScreen(JE_shortint col,JE_shortint int_)258 void JE_filterScreen( JE_shortint col, JE_shortint int_)
259 {
260 	Uint8 *s = NULL; /* screen pointer, 8-bit specific */
261 	int x, y;
262 	unsigned int temp;
263 
264 	if (filterFade)
265 	{
266 		levelBrightness += levelBrightnessChg;
267 		if ((filterFadeStart && levelBrightness < -14) || levelBrightness > 14)
268 		{
269 			levelBrightnessChg = -levelBrightnessChg;
270 			filterFadeStart = false;
271 			levelFilter = levelFilterNew;
272 		}
273 		if (!filterFadeStart && levelBrightness == 0)
274 		{
275 			filterFade = false;
276 			levelBrightness = -99;
277 		}
278 	}
279 
280 	if (col != -99 && filtrationAvail)
281 	{
282 		s = VGAScreen->pixels;
283 		s += 24;
284 
285 		col <<= 4;
286 
287 		for (y = 184; y; y--)
288 		{
289 			for (x = 264; x; x--)
290 			{
291 				*s = col | (*s & 0x0f);
292 				s++;
293 			}
294 			s += VGAScreen->pitch - 264;
295 		}
296 	}
297 
298 	if (int_ != -99 && explosionTransparent)
299 	{
300 		s = VGAScreen->pixels;
301 		s += 24;
302 
303 		for (y = 184; y; y--)
304 		{
305 			for (x = 264; x; x--)
306 			{
307 				temp = (*s & 0x0f) + int_;
308 				*s = (*s & 0xf0) | (temp >= 0x1f ? 0 : (temp >= 0x0f ? 0x0f : temp));
309 				s++;
310 			}
311 			s += VGAScreen->pitch - 264;
312 		}
313 	}
314 }
315 
JE_checkSmoothies(void)316 void JE_checkSmoothies( void )
317 {
318 	anySmoothies = (processorType > 2 && (smoothies[1-1] || smoothies[2-1])) || (processorType > 1 && (smoothies[3-1] || smoothies[4-1] || smoothies[5-1]));
319 }
320 
lava_filter(SDL_Surface * dst,SDL_Surface * src)321 void lava_filter( SDL_Surface *dst, SDL_Surface *src )
322 {
323 	assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8);
324 
325 	/* we don't need to check for over-reading the pixel surfaces since we only
326 	 * read from the top 185+1 scanlines, and there should be 320 */
327 
328 	const int dst_pitch = dst->pitch;
329 	Uint8 *dst_pixel = (Uint8 *)dst->pixels + (185 * dst_pitch);
330 	const Uint8 * const dst_pixel_ll = (Uint8 *)dst->pixels;  // lower limit
331 
332 	const int src_pitch = src->pitch;
333 	const Uint8 *src_pixel = (Uint8 *)src->pixels + (185 * src->pitch);
334 	const Uint8 * const src_pixel_ll = (Uint8 *)src->pixels;  // lower limit
335 
336 	int w = 320 * 185 - 1;
337 
338 	for (int y = 185 - 1; y >= 0; --y)
339 	{
340 		dst_pixel -= (dst_pitch - 320);  // in case pitch is not 320
341 		src_pixel -= (src_pitch - 320);  // in case pitch is not 320
342 
343 		for (int x = 320 - 1; x >= 0; x -= 8)
344 		{
345 			int waver = abs(((w >> 9) & 0x0f) - 8) - 1;
346 			w -= 8;
347 
348 			for (int xi = 8 - 1; xi >= 0; --xi)
349 			{
350 				--dst_pixel;
351 				--src_pixel;
352 
353 				// value is average value of source pixel (2x), destination pixel above, and destination pixel below (all with waver)
354 				// hue is red
355 				Uint8 value = 0;
356 
357 				if (src_pixel + waver >= src_pixel_ll)
358 					value += (*(src_pixel + waver) & 0x0f) * 2;
359 				value += *(dst_pixel + waver + dst_pitch) & 0x0f;
360 				if (dst_pixel + waver - dst_pitch >= dst_pixel_ll)
361 					value += *(dst_pixel + waver - dst_pitch) & 0x0f;
362 
363 				*dst_pixel = (value / 4) | 0x70;
364 			}
365 		}
366 	}
367 }
368 
water_filter(SDL_Surface * dst,SDL_Surface * src)369 void water_filter( SDL_Surface *dst, SDL_Surface *src )
370 {
371 	assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8);
372 
373 	Uint8 hue = smoothie_data[1] << 4;
374 
375 	/* we don't need to check for over-reading the pixel surfaces since we only
376 	 * read from the top 185+1 scanlines, and there should be 320 */
377 
378 	const int dst_pitch = dst->pitch;
379 	Uint8 *dst_pixel = (Uint8 *)dst->pixels + (185 * dst_pitch);
380 
381 	const Uint8 *src_pixel = (Uint8 *)src->pixels + (185 * src->pitch);
382 
383 	int w = 320 * 185 - 1;
384 
385 	for (int y = 185 - 1; y >= 0; --y)
386 	{
387 		dst_pixel -= (dst_pitch - 320);  // in case pitch is not 320
388 		src_pixel -= (src->pitch - 320);  // in case pitch is not 320
389 
390 		for (int x = 320 - 1; x >= 0; x -= 8)
391 		{
392 			int waver = abs(((w >> 10) & 0x07) - 4) - 1;
393 			w -= 8;
394 
395 			for (int xi = 8 - 1; xi >= 0; --xi)
396 			{
397 				--dst_pixel;
398 				--src_pixel;
399 
400 				// pixel is copied from source if not blue
401 				// otherwise, value is average of value of source pixel and destination pixel below (with waver)
402 				if ((*src_pixel & 0x30) == 0)
403 				{
404 					*dst_pixel = *src_pixel;
405 				}
406 				else
407 				{
408 					Uint8 value = *src_pixel & 0x0f;
409 					value += *(dst_pixel + waver + dst_pitch) & 0x0f;
410 					*dst_pixel = (value / 2) | hue;
411 				}
412 			}
413 		}
414 	}
415 }
416 
iced_blur_filter(SDL_Surface * dst,SDL_Surface * src)417 void iced_blur_filter( SDL_Surface *dst, SDL_Surface *src )
418 {
419 	assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8);
420 
421 	Uint8 *dst_pixel = dst->pixels;
422 	const Uint8 *src_pixel = src->pixels;
423 
424 	for (int y = 0; y < 184; ++y)
425 	{
426 		for (int x = 0; x < 320; ++x)
427 		{
428 			// value is average value of source pixel and destination pixel
429 			// hue is icy blue
430 
431 			const Uint8 value = (*src_pixel & 0x0f) + (*dst_pixel & 0x0f);
432 			*dst_pixel = (value / 2) | 0x80;
433 
434 			++dst_pixel;
435 			++src_pixel;
436 		}
437 
438 		dst_pixel += (dst->pitch - 320);  // in case pitch is not 320
439 		src_pixel += (src->pitch - 320);  // in case pitch is not 320
440 	}
441 }
442 
blur_filter(SDL_Surface * dst,SDL_Surface * src)443 void blur_filter( SDL_Surface *dst, SDL_Surface *src )
444 {
445 	assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8);
446 
447 	Uint8 *dst_pixel = dst->pixels;
448 	const Uint8 *src_pixel = src->pixels;
449 
450 	for (int y = 0; y < 184; ++y)
451 	{
452 		for (int x = 0; x < 320; ++x)
453 		{
454 			// value is average value of source pixel and destination pixel
455 			// hue is source pixel hue
456 
457 			const Uint8 value = (*src_pixel & 0x0f) + (*dst_pixel & 0x0f);
458 			*dst_pixel = (value / 2) | (*src_pixel & 0xf0);
459 
460 			++dst_pixel;
461 			++src_pixel;
462 		}
463 
464 		dst_pixel += (dst->pitch - 320);  // in case pitch is not 320
465 		src_pixel += (src->pitch - 320);  // in case pitch is not 320
466 	}
467 }
468 
469 
470 /* Background Starfield */
471 typedef struct
472 {
473 	Uint8 color;
474 	JE_word position; // relies on overflow wrap-around
475 	int speed;
476 } StarfieldStar;
477 
478 #define MAX_STARS 100
479 #define STARFIELD_HUE 0x90
480 static StarfieldStar starfield_stars[MAX_STARS];
481 int starfield_speed;
482 
initialize_starfield(void)483 void initialize_starfield( void )
484 {
485 	for (int i = MAX_STARS-1; i >= 0; --i)
486 	{
487 		starfield_stars[i].position = mt_rand() % 320 + mt_rand() % 200 * VGAScreen->pitch;
488 		starfield_stars[i].speed = mt_rand() % 3 + 2;
489 		starfield_stars[i].color = mt_rand() % 16 + STARFIELD_HUE;
490 	}
491 }
492 
update_and_draw_starfield(SDL_Surface * surface,int move_speed)493 void update_and_draw_starfield( SDL_Surface* surface, int move_speed )
494 {
495 	Uint8* p = (Uint8*)surface->pixels;
496 
497 	for (int i = MAX_STARS-1; i >= 0; --i)
498 	{
499 		StarfieldStar* star = &starfield_stars[i];
500 
501 		star->position += (star->speed + move_speed) * surface->pitch;
502 
503 		if (star->position < 177 * surface->pitch)
504 		{
505 			if (p[star->position] == 0)
506 			{
507 				p[star->position] = star->color;
508 			}
509 
510 			// If star is bright enough, draw surrounding pixels
511 			if (star->color - 4 >= STARFIELD_HUE)
512 			{
513 				if (p[star->position + 1] == 0)
514 					p[star->position + 1] = star->color - 4;
515 
516 				if (star->position > 0 && p[star->position - 1] == 0)
517 					p[star->position - 1] = star->color - 4;
518 
519 				if (p[star->position + surface->pitch] == 0)
520 					p[star->position + surface->pitch] = star->color - 4;
521 
522 				if (star->position >= surface->pitch && p[star->position - surface->pitch] == 0)
523 					p[star->position - surface->pitch] = star->color - 4;
524 			}
525 		}
526 	}
527 }
528