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