summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/deco_zoomspr.cpp
blob: 3413231f01df1388a6e4bf63636c5d4abd7b7796 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
// license:BSD-3-Clause
// copyright-holders:Bryan McPhail,David Haywood

/* the sprites used by DragonGun + Lock 'n' Loaded */

/* probably 186 + 187 custom chips, although there is a 145 too? */

// helicopter in lockload is only half drawn? (3rd attract demo)
// how are we meant to mix these with the tilemaps, parts must go above / behind parts of tilemap '4/7' in lockload, could be more priority masking sprites we're missing / not drawing tho?
// dragongun also does a masking trick on the dragon during the attract intro, it should not be visible but rather cause the fire to be invisible in the shape of the dragon (see note / hack in code to enable this effect)
// dragongun 'waterfall' prior to one of the bosses also needs correct priority

#include "emu.h"
#include "deco_zoomspr.h"

DEFINE_DEVICE_TYPE(DECO_ZOOMSPR, deco_zoomspr_device, "deco_zoomspr", "DECO Zooming Sprites")

deco_zoomspr_device::deco_zoomspr_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, DECO_ZOOMSPR, tag, owner, clock)
	, m_gfxdecode(*this, finder_base::DUMMY_TAG)
{
}

void deco_zoomspr_device::device_start()
{
}

void deco_zoomspr_device::device_reset()
{
}

/******************************************************************************/


inline void deco_zoomspr_device::dragngun_drawgfxzoom(
		bitmap_rgb32 &dest_bmp,const rectangle &clip,gfx_element *gfx,
		uint32_t code,uint32_t color,int flipx,int flipy,int sx,int sy,
		int transparent_color,
		int scalex, int scaley,bitmap_ind8 *pri_buffer,uint32_t pri_mask, int sprite_screen_width, int  sprite_screen_height, uint8_t alpha,
		bitmap_ind8 &pri_bitmap, bitmap_rgb32 &temp_bitmap,
		int priority)
{
	rectangle myclip;

	if (!scalex || !scaley) return;

	/*
	scalex and scaley are 16.16 fixed point numbers
	1<<15 : shrink to 50%
	1<<16 : uniform scale
	1<<17 : double to 200%
	*/

	/* KW 991012 -- Added code to force clip to bitmap boundary */
	myclip = clip;
	myclip &= temp_bitmap.cliprect();

	{
		if( gfx )
		{
			const pen_t *pal = &m_gfxdecode->palette().pen(gfx->colorbase() + gfx->granularity() * (color % gfx->colors()));
			const uint8_t *code_base = gfx->get_data(code % gfx->elements());

			if (sprite_screen_width && sprite_screen_height)
			{
				/* compute sprite increment per screen pixel */
				int dx = (gfx->width()<<16)/sprite_screen_width;
				int dy = (gfx->height()<<16)/sprite_screen_height;

				int ex = sx+sprite_screen_width;
				int ey = sy+sprite_screen_height;

				int x_index_base;
				int y_index;

				if( flipx )
				{
					x_index_base = (sprite_screen_width-1)*dx;
					dx = -dx;
				}
				else
				{
					x_index_base = 0;
				}

				if( flipy )
				{
					y_index = (sprite_screen_height-1)*dy;
					dy = -dy;
				}
				else
				{
					y_index = 0;
				}

				if( sx < clip.left())
				{ /* clip left */
					int pixels = clip.left()-sx;
					sx += pixels;
					x_index_base += pixels*dx;
				}
				if( sy < clip.top() )
				{ /* clip top */
					int pixels = clip.top()-sy;
					sy += pixels;
					y_index += pixels*dy;
				}
				/* NS 980211 - fixed incorrect clipping */
				if( ex > clip.right()+1 )
				{ /* clip right */
					int pixels = ex-clip.right()-1;
					ex -= pixels;
				}
				if( ey > clip.bottom()+1 )
				{ /* clip bottom */
					int pixels = ey-clip.bottom()-1;
					ey -= pixels;
				}

				if( ex>sx )
				{ /* skip if inner loop doesn't draw anything */
					int y;

					/* case 1: no alpha */
					if (alpha == 0xff)
					{
						{
							for( y=sy; y<ey; y++ )
							{
								const uint8_t *source = code_base + (y_index>>16) * gfx->rowbytes();
								uint32_t *dest = &temp_bitmap.pix32(y);
								uint8_t *pri = &pri_bitmap.pix8(y);


								int x, x_index = x_index_base;
								for( x=sx; x<ex; x++ )
								{
									int c = source[x_index>>16];
									if (c != transparent_color)
									{
										if (priority >= pri[x])
										{
											dest[x] = pal[c];
											dest[x] |= 0xff000000;
										}
										else // sprites can have a 'masking' effect on other sprites
										{
											dest[x] = 0x00000000;
										}
									}

									x_index += dx;
								}

								y_index += dy;
							}
						}
					}

					/* alpha-blended */
					else
					{
						{
							for( y=sy; y<ey; y++ )
							{
								const uint8_t *source = code_base + (y_index>>16) * gfx->rowbytes();
								uint32_t *dest = &temp_bitmap.pix32(y);
								uint8_t *pri = &pri_bitmap.pix8(y);
								uint32_t *tmapcolor = &dest_bmp.pix32(y);


								int x, x_index = x_index_base;
								for( x=sx; x<ex; x++ )
								{
									int c = source[x_index>>16];
									if (c != transparent_color)
									{
										if (priority >= pri[x])
										{
											// this logic doesn't seem correct.  Sprites CAN blend other sprites (needed in many places) but based on videos of the character select screen it appears that sprites can't blend already blended sprites
											// I'm not sure which colour gets used but the video shows a single shade of yellow rather than the yellow blending the yellow)

											if ((dest[x] & 0xff000000) == 0x00000000)
												dest[x] = alpha_blend_r32(tmapcolor[x] & 0x00ffffff, pal[c] & 0x00ffffff, alpha); // if nothing has been drawn pull the pixel from the tilemap to blend with
											else
												dest[x] = alpha_blend_r32(dest[x] & 0x00ffffff, pal[c] & 0x00ffffff, alpha); // otherwise blend with what was previously drawn

											dest[x] |= 0xff000000;
										}
										else // sprites can have a 'masking' effect on other sprites
										{
											dest[x] = 0x00000000;
										}
									}

									x_index += dx;
								}

								y_index += dy;
							}
						}
					}
				}
			}
		}
	}
}

void deco_zoomspr_device::dragngun_draw_sprites( bitmap_rgb32 &bitmap, const rectangle &cliprect, const uint32_t *spritedata, uint32_t* dragngun_sprite_layout_0_ram, uint32_t* dragngun_sprite_layout_1_ram, uint32_t* dragngun_sprite_lookup_0_ram, uint32_t* dragngun_sprite_lookup_1_ram, uint32_t dragngun_sprite_ctrl,  bitmap_ind8 &pri_bitmap, bitmap_rgb32 &temp_bitmap)
{
	const uint32_t *layout_ram;
	const uint32_t *lookup_ram;
	int offs;
	temp_bitmap.fill(0x00000000, cliprect);

	/*
	    Sprites are built from main control ram, which references tile
	    layout ram, which finally references tile lookup ram which holds
	    the actual tile indices to draw and index into the banking
	    control.  Tile lookup and tile layout ram are double buffered.


	    Main sprite control ram, 8 * 32 bit words per sprite, so

	    Word 0:
	        0x0400 - Banking control for tile layout RAM + tile lookup ram
	        0x0200 - ?
	        0x01ff - Index into tile layout RAM
	    Word 1 :
	    Word 2 : X base position
	    Word 3 : Y base position
	    Word 4 :
	        0x8000: X flip
	        0x03ff: X size of block in pixels (for scaling)
	    Word 5 :
	        0x8000: Y flip
	        0x03ff: Y size of block in pixels (for scaling)
	    Word 6 :
	        0x0000001f - colour.
	        0x00000020 - ?  Used for background at 'frog' boss and title screen dragon.
	        0x00000040 - ?  priority?
	        0x00000080 - Alpha blending enable
	        0x40000000 - Additive/Subtractable blend? (dragngun)
	    Word 7 :


	    Tile layout ram, 4 * 32 bit words per sprite, so

	    Word 0:
	        0x2000 - Selector for tile lookup bank!?!?!?!?!?!?
	        0x1fff - Index into tile lookup ram (16 bit word based, NOT 32)
	    Word 1:
	        0xff00 - ?
	        0x00f0 - Width
	        0x000f - Height
	    Word 2:
	        0x01ff - X block offset
	    Word 3:
	        0x01ff - Y block offset
	*/

	/* Sprite global disable bit - can't be, it's set in lockload calibration menu where the targets are sprites */
//  if (dragngun_sprite_ctrl&0x40000000)
//      return;

	for (offs = 0;offs < 0x800;offs += 8)
	{
		int sx,sy,colour,fx,fy,w,h,x,y,bx,by,alpha,scalex,scaley;
		int zoomx,zoomy;
		int xpos,ypos;

		scalex=spritedata[offs+4]&0x3ff;
		scaley=spritedata[offs+5]&0x3ff;
		if (!scalex || !scaley) /* Zero pixel size in X or Y - skip block */
			continue;

		int layoutram_offset = (spritedata[offs + 0] & 0x1ff) * 4;

		if (spritedata[offs + 0] & 0x400)
			layout_ram = dragngun_sprite_layout_1_ram;
		else
			layout_ram = dragngun_sprite_layout_0_ram;
		h = (layout_ram[layoutram_offset + 1]>>0)&0xf;
		w = (layout_ram[layoutram_offset + 1]>>4)&0xf;
		if (!h || !w)
			continue;

		sx = spritedata[offs+2] & 0x3ff;
		sy = spritedata[offs+3] & 0x3ff;
		bx = layout_ram[layoutram_offset + 2] & 0x1ff;
		by = layout_ram[layoutram_offset + 3] & 0x1ff;
		if (bx&0x100) bx=1-(bx&0xff);
		if (by&0x100) by=1-(by&0xff); /* '1 - ' is strange, but correct for Dragongun 'Winners' screen. */
		if (sx >= 512) sx -= 1024;
		if (sy >= 512) sy -= 1024;

		colour = spritedata[offs+6]&0x1f;

		int priority = (spritedata[offs + 6] & 0x60) >> 5;




//      printf("%02x\n", priority);

		if (priority == 0) priority = 7;
		else if (priority == 1) priority = 7; // set to 1 to have the 'masking effect' with the dragon on the dragngun attract mode, but that breaks the player select where it needs to be 3, probably missing some bits..
		else if (priority == 2) priority = 7;
		else if (priority == 3) priority = 7;

		if (spritedata[offs+6]&0x80)
			alpha=0x80;
		else
			alpha=0xff;

		fx = spritedata[offs+4]&0x8000;
		fy = spritedata[offs+5]&0x8000;

		int lookupram_offset = layout_ram[layoutram_offset + 0] & 0x1fff;

//      if (spritedata[offs+0]&0x400)
		if (layout_ram[layoutram_offset + 0] & 0x2000)
			lookup_ram = dragngun_sprite_lookup_1_ram;
		else
			lookup_ram = dragngun_sprite_lookup_0_ram;

		zoomx=scalex * 0x10000 / (w*16);
		zoomy=scaley * 0x10000 / (h*16);

		if (!fy)
			ypos=(sy<<16) - (by*zoomy); /* The block offset scales with zoom, the base position does not */
		else
			ypos=(sy<<16) + (by*zoomy) - (16*zoomy);

		for (y=0; y<h; y++) {
			if (!fx)
				xpos=(sx<<16) - (bx*zoomx); /* The block offset scales with zoom, the base position does not */
			else
				xpos=(sx<<16) + (bx*zoomx) - (16*zoomx);

			for (x=0; x<w; x++) {
				int bank,sprite;

				sprite = lookup_ram[lookupram_offset];
				sprite &= 0x3fff;

				lookupram_offset++;

				/* High bits of the sprite reference into the sprite control bits for banking */
				switch (sprite&0x3000) {
				default:
				case 0x0000: sprite=(sprite&0xfff) | ((dragngun_sprite_ctrl&0x000f)<<12); break;
				case 0x1000: sprite=(sprite&0xfff) | ((dragngun_sprite_ctrl&0x00f0)<< 8); break;
				case 0x2000: sprite=(sprite&0xfff) | ((dragngun_sprite_ctrl&0x0f00)<< 4); break;
				case 0x3000: sprite=(sprite&0xfff) | ((dragngun_sprite_ctrl&0xf000)<< 0); break;
				}

				/* Because of the unusual interleaved rom layout, we have to mangle the bank bits
				even further to suit our gfx decode */
				switch (sprite&0xf000) {
				case 0x0000: sprite=0xc000 | (sprite&0xfff); break;
				case 0x1000: sprite=0xd000 | (sprite&0xfff); break;
				case 0x2000: sprite=0xe000 | (sprite&0xfff); break;
				case 0x3000: sprite=0xf000 | (sprite&0xfff); break;

				case 0xc000: sprite=0x0000 | (sprite&0xfff); break;
				case 0xd000: sprite=0x1000 | (sprite&0xfff); break;
				case 0xe000: sprite=0x2000 | (sprite&0xfff); break;
				case 0xf000: sprite=0x3000 | (sprite&0xfff); break;
				}

				if (sprite&0x8000) bank=4; else bank=3;
				sprite&=0x7fff;

					dragngun_drawgfxzoom(
						bitmap,cliprect,m_gfxdecode->gfx(bank),
						sprite,
						colour,
						fx,fy,
						xpos>>16,ypos>>16,
						15,zoomx,zoomy,nullptr,0,
						((xpos+(zoomx<<4))>>16) - (xpos>>16), ((ypos+(zoomy<<4))>>16) - (ypos>>16), alpha,
						pri_bitmap, temp_bitmap,
						priority
						);


				if (fx)
					xpos-=zoomx<<4;
				else
					xpos+=zoomx<<4;
			}
			if (fy)
				ypos-=zoomy<<4;
			else
				ypos+=zoomy<<4;
		}
	}

	for (int y = cliprect.top(); y <= cliprect.bottom(); y++)
	{
		uint32_t *src = &temp_bitmap.pix32(y);
		uint32_t *dst = &bitmap.pix32(y);

		for (int x = cliprect.left(); x <= cliprect.right(); x++)
		{
			uint32_t srcpix = src[x];

			if ((srcpix & 0xff000000) == 0xff000000)
			{
				dst[x] = srcpix & 0x00ffffff;
			}
		}

	}

}