1 /*
2  * OpenBOR - http://www.LavaLit.com
3  * -----------------------------------------------------------------------
4  * All rights reserved, see LICENSE in OpenBOR root for details.
5  *
6  * Copyright (c) 2004 - 2011 OpenBOR Team
7  */
8 
9 // Sprite queueing and sorting
10 
11 #include <stdio.h>
12 #include "types.h"
13 #include "screen.h"
14 #include "sprite.h"
15 #include "draw.h"
16 #include "globals.h"
17 // This should be enough for most games...
18 // But bear in mind that text is also composed of sprites!
19 #define			MAXQSPRITES		500
20 
21 #define         SQT_SPRITE      0
22 #define         SQT_DOT         1
23 #define         SQT_LINE        2
24 #define         SQT_BOX         3
25 #define         SQT_CIRCLE      4
26 #define         SQT_SHADOW      5 //current unused, use common sprite method instead
27 #define         SQT_SCREEN      6
28 
29 #define         SQ_MAX_PARAMS   3
30 
31 
32 typedef struct{
33 	int		x;
34 	int		y;
35 	int	    z;
36 	int    sortid;
37 	void*  frame;
38 	s_drawmethod drawmethod;
39 	int     params[SQ_MAX_PARAMS];
40 	int     type;
41 }qstruct;
42 
43 static qstruct queue[MAXQSPRITES];
44 static qstruct * order[MAXQSPRITES];
45 
46 static int spritequeue_len = 0;
47 static int spriteq_old_len = 0;
48 static int spriteq_locked = 0;
49 
spriteq_add_frame(int x,int y,int z,s_sprite * frame,s_drawmethod * pdrawmethod,int sortid)50 void spriteq_add_frame(int x, int y, int z, s_sprite* frame, s_drawmethod* pdrawmethod, int sortid){
51 	if(frame == NULL) return;
52 	if(spritequeue_len>=MAXQSPRITES) return;
53 	queue[spritequeue_len].type = SQT_SPRITE;
54 	queue[spritequeue_len].x = x;
55 	queue[spritequeue_len].y = y;
56 	queue[spritequeue_len].z = z;
57 	queue[spritequeue_len].sortid = sortid;
58 	queue[spritequeue_len].frame = frame;
59 	if(pdrawmethod)
60 	{
61 	    queue[spritequeue_len].drawmethod = *pdrawmethod;
62 	}
63 	else queue[spritequeue_len].drawmethod.flag = 0;
64 	queue[spritequeue_len].params[0] = 0; // determin if the sprite's center should be readjusted;
65 	order[spritequeue_len] = &queue[spritequeue_len];
66 	++spritequeue_len;
67 }
68 
spriteq_add_sprite(int x,int y,int z,int id,s_drawmethod * pdrawmethod,int sortid)69 void spriteq_add_sprite(int x, int y, int z, int id, s_drawmethod* pdrawmethod, int sortid){
70 	s_sprite *frame = sprite_map[id].sprite;
71 	if(frame == NULL) return;
72 	if(spritequeue_len>=MAXQSPRITES) return;
73 	queue[spritequeue_len].type = SQT_SPRITE;
74 	queue[spritequeue_len].x = x;
75 	queue[spritequeue_len].y = y;
76 	queue[spritequeue_len].z = z;
77 	queue[spritequeue_len].sortid = sortid;
78 	queue[spritequeue_len].frame = frame;
79 	if(pdrawmethod)
80 	{
81 	    queue[spritequeue_len].drawmethod = *pdrawmethod;
82 	}
83 	else queue[spritequeue_len].drawmethod.flag = 0;
84 	queue[spritequeue_len].params[0] = 1; // determin if the sprite's center should be readjusted;
85 	queue[spritequeue_len].params[1] = sprite_map[id].centerx; // centerx
86 	queue[spritequeue_len].params[2] = sprite_map[id].centery; // centery
87 	order[spritequeue_len] = &queue[spritequeue_len];
88 	++spritequeue_len;
89 }
90 
spriteq_add_screen(int x,int y,int z,s_screen * ps,s_drawmethod * pdrawmethod,int sortid)91 void spriteq_add_screen(int x, int y, int z, s_screen* ps, s_drawmethod* pdrawmethod, int sortid){
92 	if(spritequeue_len>=MAXQSPRITES) return;
93 	if(ps==NULL) return;
94 	queue[spritequeue_len].type = SQT_SCREEN;
95 	queue[spritequeue_len].x = x;
96 	queue[spritequeue_len].y = y;
97 	queue[spritequeue_len].z = z;
98 	queue[spritequeue_len].sortid = sortid;
99 	queue[spritequeue_len].frame = ps;
100 	if(pdrawmethod)
101 	{
102 	    queue[spritequeue_len].drawmethod = *pdrawmethod;
103 	}
104 	else queue[spritequeue_len].drawmethod.flag = 0;
105 	order[spritequeue_len] = &queue[spritequeue_len];
106 	++spritequeue_len;
107 }
108 
spriteq_add_dot(int sx,int sy,int z,int colour,int alpha)109 void spriteq_add_dot(int sx, int sy, int z, int colour, int alpha)
110 {
111 	if(spritequeue_len>=MAXQSPRITES) return;
112 	queue[spritequeue_len].type = SQT_DOT;
113 	queue[spritequeue_len].x = sx;
114 	queue[spritequeue_len].y = sy;
115 	queue[spritequeue_len].z = z;
116 	queue[spritequeue_len].sortid = 0;
117 	queue[spritequeue_len].params[0] = colour;
118 	queue[spritequeue_len].frame = NULL;
119 	queue[spritequeue_len].drawmethod.alpha = alpha;
120 	queue[spritequeue_len].drawmethod.flag = (alpha>0);
121 	order[spritequeue_len] = &queue[spritequeue_len];
122 	++spritequeue_len;
123 }
124 
spriteq_add_line(int sx,int sy,int ex,int ey,int z,int colour,int alpha)125 void spriteq_add_line(int sx, int sy, int ex, int ey, int z, int colour, int alpha)
126 {
127 	if(spritequeue_len>=MAXQSPRITES) return;
128 	queue[spritequeue_len].type = SQT_LINE;
129 	queue[spritequeue_len].x = sx;
130 	queue[spritequeue_len].y = sy;
131 	queue[spritequeue_len].params[0] = colour;
132 	queue[spritequeue_len].params[1] = ex;
133 	queue[spritequeue_len].params[2] = ey;
134 	queue[spritequeue_len].z = z;
135 	queue[spritequeue_len].sortid = 0;
136 	queue[spritequeue_len].frame = NULL;
137 	queue[spritequeue_len].drawmethod.alpha = alpha;
138 	queue[spritequeue_len].drawmethod.flag = (alpha>0);
139 	order[spritequeue_len] = &queue[spritequeue_len];
140 	++spritequeue_len;
141 }
142 
spriteq_add_box(int x,int y,int width,int height,int z,int colour,int alpha)143 void spriteq_add_box(int x, int y, int width, int height, int z, int colour, int alpha)
144 {
145 	if(spritequeue_len>=MAXQSPRITES) return;
146 	queue[spritequeue_len].type = SQT_BOX;
147 	queue[spritequeue_len].x = x;
148 	queue[spritequeue_len].y = y;
149 	queue[spritequeue_len].params[0] = colour;
150 	queue[spritequeue_len].params[1] = width;
151 	queue[spritequeue_len].params[2] = height;
152 	queue[spritequeue_len].z = z;
153 	queue[spritequeue_len].sortid = 0;
154 	queue[spritequeue_len].frame = NULL;
155 	queue[spritequeue_len].drawmethod.alpha = alpha;
156 	queue[spritequeue_len].drawmethod.flag = (alpha>0);
157 	order[spritequeue_len] = &queue[spritequeue_len];
158 	++spritequeue_len;
159 }
160 
161 
162 // Double sort code (sorts high and low simultaneously from 2 directions)
163 // Can't get much faster than this - I think
spriteq_sort()164 static void spriteq_sort()
165 {
166 	int i, lidx, hidx, lz, hz, start, end, lsid, hsid;
167 	void * tempp;
168 
169 	start = 0;
170 	end = spritequeue_len - 1;
171 
172 	while(start < end)
173 	{
174 		lidx = end;
175 		hidx = start;
176 
177 		lz = order[lidx]->z;
178 		hz = order[hidx]->z;
179 		lsid = order[lidx]->sortid;
180 		hsid = order[hidx]->sortid;
181 
182 		// Search for lowest and highest Z coord
183 		for(i=start; i<=end; i++)
184 		{
185 			if(order[i]->z < lz || (order[i]->z == lz && order[i]->sortid < lsid))
186 			{
187 				lidx = i;
188 				lz = order[i]->z;
189 				lsid = order[i]->sortid;
190 			}
191 			if(order[i]->z > hz || (order[i]->z == hz && order[i]->sortid > hsid))
192 			{
193 				hidx = i;
194 				hz = order[i]->z;
195 				hsid = order[i]->sortid;
196 			}
197 		}
198 
199 		// No need to sort equal values!
200 		if(hz==lz && hsid==lsid) return;
201 
202 		// Exchange values (low)
203 		tempp = order[start];
204 		order[start] = order[lidx];
205 		order[lidx] = tempp;
206 
207 		// Prevent confusion:
208 		// This value may already have been exchanged!
209 		if(hidx==start) hidx = lidx;
210 
211 		// Exchange values (high)
212 		tempp = order[end];
213 		order[end] = order[hidx];
214 		order[hidx] = tempp;
215 
216 		++start;
217 		--end;
218 	}
219 }
220 
221 // newonly is 1 means don't draw locked sprites
spriteq_draw(s_screen * screen,int newonly,int minz,int maxz)222 void spriteq_draw(s_screen *screen, int newonly, int minz, int maxz)
223 {
224 	int i;
225 
226 	spriteq_sort();
227 
228 	for(i=0;i<spritequeue_len;i++)
229 	{
230 		if((newonly && spriteq_locked && order[i]<queue+spriteq_old_len) || order[i]->z<minz || order[i]->z>maxz)
231 			continue;
232 
233 		switch(order[i]->type)
234 		{
235 		case SQT_SPRITE: // sprite
236 
237 			if(order[i]->params[0])// determin if the sprite's center should be readjusted;
238 			{
239 				((s_sprite*)(order[i]->frame))->centerx = order[i]->params[1];
240 				((s_sprite*)(order[i]->frame))->centery = order[i]->params[2];
241 			}
242 			putsprite(order[i]->x, order[i]->y, order[i]->frame, screen, &(order[i]->drawmethod));
243 		    break;
244 		case SQT_SCREEN: // draw a screen instead of sprite
245 			putscreen(screen, (s_screen*)(order[i]->frame), order[i]->x, order[i]->y, &(order[i]->drawmethod));
246 		    break;
247 		case SQT_DOT:
248 			switch(screen->pixelformat)
249 			{
250 			case PIXEL_8:
251 				putpixel(order[i]->x, order[i]->y, order[i]->params[0], screen, order[i]->drawmethod.flag?order[i]->drawmethod.alpha:0);
252 				break;
253 			case PIXEL_16:
254 				putpixel16(order[i]->x, order[i]->y, order[i]->params[0], screen, order[i]->drawmethod.flag?order[i]->drawmethod.alpha:0);
255 				break;
256 			case PIXEL_32:
257 				putpixel32(order[i]->x, order[i]->y, order[i]->params[0], screen, order[i]->drawmethod.flag?order[i]->drawmethod.alpha:0);
258 				break;
259 			}
260 			break;
261 		case SQT_LINE:
262 			switch(screen->pixelformat)
263 			{
264 			case PIXEL_8:
265 				line(order[i]->x, order[i]->y, order[i]->params[1], order[i]->params[2], order[i]->params[0], screen, order[i]->drawmethod.flag?order[i]->drawmethod.alpha:0);
266 				break;
267 			case PIXEL_16:
268 				line16(order[i]->x, order[i]->y, order[i]->params[1], order[i]->params[2], order[i]->params[0], screen, order[i]->drawmethod.flag?order[i]->drawmethod.alpha:0);
269 				break;
270 			case PIXEL_32:
271 				line32(order[i]->x, order[i]->y, order[i]->params[1], order[i]->params[2], order[i]->params[0], screen, order[i]->drawmethod.flag?order[i]->drawmethod.alpha:0);
272 				break;
273 			}
274 			break;
275 		case SQT_BOX:
276 			switch(screen->pixelformat)
277 			{
278 			case PIXEL_8:
279 				drawbox(order[i]->x, order[i]->y, order[i]->params[1], order[i]->params[2], order[i]->params[0], screen, order[i]->drawmethod.flag?order[i]->drawmethod.alpha:0);
280 				break;
281 			case PIXEL_16:
282 				drawbox16(order[i]->x, order[i]->y, order[i]->params[1], order[i]->params[2], order[i]->params[0], screen, order[i]->drawmethod.flag?order[i]->drawmethod.alpha:0);
283 				break;
284 			case PIXEL_32:
285 				drawbox32(order[i]->x, order[i]->y, order[i]->params[1], order[i]->params[2], order[i]->params[0], screen, order[i]->drawmethod.flag?order[i]->drawmethod.alpha:0);
286 				break;
287 			}
288 			break;
289 		default:
290 			continue;
291 		}
292 	}
293 }
294 
295 // UT: lock the spriteq, don't clearn old sprites
spriteq_lock()296 void spriteq_lock()
297 {
298 	spriteq_old_len = spritequeue_len;
299 	spriteq_locked = 1;
300 }
301 
302 // don't forget to unlock the queue, or we'll have troubles
303 // if the sprite is unloaded for some reason
spriteq_unlock()304 void spriteq_unlock()
305 {
306 	spriteq_locked = 0;
307 }
308 
spriteq_islocked()309 int  spriteq_islocked()
310 {
311 	return (spriteq_locked!=0);
312 }
313 
spriteq_clear()314 void spriteq_clear()
315 {
316 	int i;
317 	if(spriteq_locked)
318 	{
319 		// when locked, always draw previous sprites,
320 		// and only clear new sprites
321 		spritequeue_len = spriteq_old_len;
322 		for(i=0; i<spriteq_old_len; i++)
323 			order[i] = queue+i;
324 	}
325 	else
326 	{
327 	    spriteq_old_len = spritequeue_len;
328 	    spritequeue_len = 0;
329 	}
330 }
331 
332 
333 
334 
335