1 
2 // sprites routines
3 #include "graphics.h"
4 #include <string.h>
5 #include "../siflib/sif.h"
6 #include "../siflib/sifloader.h"
7 #include "../siflib/sectSprites.h"
8 #include "../siflib/sectStringArray.h"
9 #include "../autogen/sprites.h"
10 #include "../common/StringList.h"
11 #include "../dirnames.h"
12 #include "../settings.h"
13 #include "../nx.h"
14 using namespace Graphics;
15 
16 #include "sprites.h"
17 #include "sprites.fdh"
18 
19 #include "../libretro/libretro_shared.h"
20 
21 static NXSurface *spritesheet[MAX_SPRITESHEETS];
22 static int num_spritesheets;
23 static StringList sheetfiles;
24 
25 SIFSprite sprites[MAX_SPRITES];
26 int num_sprites;
27 
28 
Init()29 bool Sprites::Init()
30 {
31         char f_sprites_sif[1024];
32 	memset(spritesheet, 0, sizeof(spritesheet));
33 
34 	retro_create_path_string(f_sprites_sif, sizeof(f_sprites_sif), "data", "sprites.sif");
35 
36 	// load sprites info--sheet positions, bounding boxes etc
37 	if (load_sif(f_sprites_sif))
38 		return 1;
39 
40 	num_spritesheets = sheetfiles.CountItems();
41 	return 0;
42 }
43 
Close()44 void Sprites::Close()
45 {
46 	FlushSheets();
47 	sheetfiles.MakeEmpty();
48 }
49 
FlushSheets()50 void Sprites::FlushSheets()
51 {
52 	for(int i=0;i<MAX_SPRITESHEETS;i++)
53 	{
54 		if (spritesheet[i])
55 		{
56 			delete spritesheet[i];
57 			spritesheet[i] = NULL;
58 		}
59 	}
60 }
61 
62 /*
63 void c------------------------------() {}
64 */
65 
66 // ensure the given spritesheet is loaded
LoadSheetIfNeeded(int sheetno)67 static void Sprites::LoadSheetIfNeeded(int sheetno)
68 {
69 	if (!spritesheet[sheetno])
70 	{
71                 char pbm_name[1024];
72 		retro_create_path_string(pbm_name, sizeof(pbm_name), data_dir, sheetfiles.StringAt(sheetno));
73 		NX_LOG("LoadSheetIfNeeded: %s\n", pbm_name);
74 
75 #ifdef _WIN32
76       for (unsigned i = 0; i < sizeof(pbm_name); i++)
77       {
78          if (pbm_name[i] == '/')
79             pbm_name[i] = '\\';
80       }
81 #endif
82 
83 		spritesheet[sheetno] = new NXSurface;
84 		spritesheet[sheetno]->LoadImage(pbm_name, true);
85 
86 		// fix the blue dash in the middle of the starpoof effect on that one frame,
87 		// I'm pretty sure this is a glitch.
88 		if (!settings->emulate_bugs)
89 		{
90 			if (sheetno == 3)	// Caret.pbm
91 				spritesheet[sheetno]->FillRect(40, 58, 41, 58, 0, 0, 0);
92 		}
93 	}
94 }
95 
96 
97 // master sprite drawing function
BlitSprite(int x,int y,int s,int frame,uint8_t dir,int xoff,int yoff,int wd,int ht)98 static void Sprites::BlitSprite(int x, int y, int s, int frame, uint8_t dir, \
99 								int xoff, int yoff, int wd, int ht)
100 {
101 	LoadSheetIfNeeded(sprites[s].spritesheet);
102 
103 	dir %= sprites[s].ndirs;
104 	SIFDir *sprdir = &sprites[s].frame[frame].dir[dir];
105 
106 	DrawSurface(spritesheet[sprites[s].spritesheet], \
107 				x, y, \
108 				(sprdir->sheet_offset.x + xoff), \
109 				(sprdir->sheet_offset.y + yoff), \
110 				wd, ht);
111 }
112 
113 /*
114 void c------------------------------() {}
115 */
116 
117 
118 // draw sprite "s" at [x,y]. drawing frame "frame" and dir "dir".
draw_sprite(int x,int y,int s,int frame,uint8_t dir)119 void Sprites::draw_sprite(int x, int y, int s, int frame, uint8_t dir)
120 {
121 	BlitSprite(x, y, s, frame, dir, 0, 0, sprites[s].w, sprites[s].h);
122 }
123 
124 // draw sprite "s", place it's draw point at [x,y] instead of it's upper-left corner.
draw_sprite_at_dp(int x,int y,int s,int frame,uint8_t dir)125 void Sprites::draw_sprite_at_dp(int x, int y, int s, int frame, uint8_t dir)
126 {
127 	x -= sprites[s].frame[frame].dir[dir].drawpoint.x;
128 	y -= sprites[s].frame[frame].dir[dir].drawpoint.y;
129 	BlitSprite(x, y, s, frame, dir, 0, 0, sprites[s].w, sprites[s].h);
130 }
131 
132 
133 // draw a portion of a sprite, such as a sprite in the middle of "teleporting".
134 // only the area between clipy1 (inclusive) and clipy2 (exclusive) are visible.
draw_sprite_clipped(int x,int y,int s,int frame,uint8_t dir,int clipx1,int clipx2,int clipy1,int clipy2)135 void Sprites::draw_sprite_clipped(int x, int y, int s, int frame, uint8_t dir, \
136 						int clipx1, int clipx2, int clipy1, int clipy2)
137 {
138 	BlitSprite(x + clipx1, y + clipy1, s, frame, dir, clipx1, clipy1, \
139 				(clipx2 - clipx1), (clipy2 - clipy1));
140 }
141 
142 // draw a clipped sprite while clipping only the width.
143 // used for drawing percentage bars, etc.
draw_sprite_clip_width(int x,int y,int s,int frame,int wd)144 void Sprites::draw_sprite_clip_width(int x, int y, int s, int frame, int wd)
145 {
146 	BlitSprite(x, y, s, frame, 0, 0, 0, wd, sprites[s].h);
147 }
148 
149 // draws a sprite at less than it's actual width by chopping it into two chunks.
150 // on the left, the first "repeat_at" pixels are drawn.
151 // then, the remaining "wd" is drawn from the right half of the sprite.
152 // used for things like drawing the textboxes.
draw_sprite_chopped(int x,int y,int s,int frame,int wd,int repeat_at)153 void Sprites::draw_sprite_chopped(int x, int y, int s, int frame, int wd, int repeat_at)
154 {
155 int xoff;
156 
157 	if (wd >= sprites[s].w)
158 	{
159 		draw_sprite(x, y, s, frame);
160 		return;
161 	}
162 
163 	// draw the left part
164 	BlitSprite(x, y, s, frame, 0, 0, 0, repeat_at, sprites[s].h);
165 	x += repeat_at;
166 	wd -= repeat_at;
167 
168 	// draw the rest of it
169 	xoff = (sprites[s].w - wd);
170 
171 	BlitSprite(x, y, s, frame, 0, xoff, 0, wd, sprites[s].h);
172 }
173 
174 // draws a sprite to any arbitrary width by repeating it over the given distance.
175 // if needed, the rightmost instance of the sprite is clipped.
draw_sprite_repeating_x(int x,int y,int s,int frame,int wd)176 void Sprites::draw_sprite_repeating_x(int x, int y, int s, int frame, int wd)
177 {
178 	int wdleft = wd;
179 	while(wdleft > 0)
180 	{
181 		int blitwd = wdleft;
182 		if (blitwd > sprites[s].w) blitwd = sprites[s].w;
183 
184 		BlitSprite(x, y, s, frame, 0, 0, 0, blitwd, sprites[s].h);
185 		x += blitwd;
186 		wdleft -= blitwd;
187 	}
188 }
189 
190 /*
191 void c------------------------------() {}
192 */
193 
194 // return the NXSurface for a given spritesheet #
get_spritesheet(int sheetno)195 NXSurface *Sprites::get_spritesheet(int sheetno)
196 {
197 	LoadSheetIfNeeded(sheetno);
198 	return spritesheet[sheetno];
199 }
200 
201 // create an empty spritesheet of the given size and return it's index.
create_spritesheet(int wd,int ht)202 int Sprites::create_spritesheet(int wd, int ht)
203 {
204 	if (num_spritesheets >= MAX_SPRITESHEETS)
205 		return -1;
206 
207 	spritesheet[num_spritesheets] = new NXSurface(wd, ht);
208 	return num_spritesheets++;
209 }
210 
211 // draw a sprite onto some surface other than the screen
draw_sprite_to_surface(NXSurface * dst,int x,int y,int s,int frame,uint8_t dir)212 void Sprites::draw_sprite_to_surface(NXSurface *dst, int x, int y, int s, int frame, uint8_t dir)
213 {
214 	Graphics::SetDrawTarget(dst);
215 	draw_sprite(x, y, s, frame, dir);
216 	Graphics::SetDrawTarget(screen);
217 }
218 
219 /*
220 void c------------------------------() {}
221 */
222 
load_sif(const char * fname)223 static bool load_sif(const char *fname)
224 {
225 SIFLoader sif;
226 uint8_t *sheetdata, *spritesdata;
227 int sheetdatalength, spritesdatalength;
228 
229 	if (sif.LoadHeader(fname))
230 		return 1;
231 
232 	if (!(sheetdata = sif.FindSection(SIF_SECTION_SHEETS, &sheetdatalength)))
233 	{
234 		NX_ERR("load_sif: file '%s' missing SIF_SECTION_SHEETS\n", fname);
235 		return 1;
236 	}
237 
238 	if (!(spritesdata = sif.FindSection(SIF_SECTION_SPRITES, &spritesdatalength)))
239 	{
240 		NX_ERR("load_sif: file '%s' missing SIF_SECTION_SPRITES\n", fname);
241 		return 1;
242 	}
243 
244 	// decode sheets
245 	sheetfiles.MakeEmpty();
246 	if (SIFStringArraySect::Decode(sheetdata, sheetdatalength, &sheetfiles))
247 		return 1;
248 
249 	// decode sprites
250 	if (SIFSpritesSect::Decode(spritesdata, spritesdatalength, \
251 						&sprites[0], &num_sprites, MAX_SPRITES))
252 	{
253 		NX_ERR("load_sif: SIFSpritesSect decoder failed\n");
254 		return 1;
255 	}
256 
257 	sif.CloseFile();
258 
259 	create_slope_boxes();
260 	offset_by_draw_points();
261 
262    // for sprites which only have 1 dir (no separate frames for left & right),
263    // create a 2nd identical dir as the rest of the engine doesn't bother
264    // with this complication.
265 
266 	for(int s=0;s<num_sprites;s++)
267 	{
268 		if (sprites[s].ndirs == 1)
269 		{
270 			sprites[s].ndirs = 2;
271 			for(int f=0;f<sprites[s].nframes;f++)
272 				sprites[s].frame[f].dir[1] = sprites[s].frame[f].dir[0];
273 		}
274 	}
275 
276 	return 0;
277 }
278 
279 
280 // create slope boxes for all sprites, used by the slope-handling routines
281 // these are basically just a form of bounding box describing the bounds of the
282 // blockd points.
create_slope_boxes()283 static void create_slope_boxes()
284 {
285 	for(int s=0;s<num_sprites;s++)
286 	{
287 		if (sprites[s].block_d.count != 0)
288 		{
289 			int leftmost = 99999;
290 			int rightmost = -99999;
291 			for(int i=0;i<sprites[s].block_d.count;i++)
292 			{
293 				if (sprites[s].block_d[i].x < leftmost)  leftmost = sprites[s].block_d[i].x;
294 				if (sprites[s].block_d[i].x > rightmost) rightmost = sprites[s].block_d[i].x;
295 			}
296 
297 			sprites[s].slopebox.x1 = leftmost;
298 			sprites[s].slopebox.x2 = rightmost;
299 
300 			if (sprites[s].block_u.count)
301 				sprites[s].slopebox.y1 = (sprites[s].block_u[0].y + 1);
302 			else
303 				sprites[s].slopebox.y1 = 0;
304 
305 			sprites[s].slopebox.y2 = (sprites[s].block_d[0].y - 1);
306 		}
307 	}
308 
309 	sprites[SPR_MYCHAR].slopebox.y1 += 3;
310 }
311 
312 // offset things like blockl/r/u/d, bounding box etc by the draw point of all
313 // sprites so that these things are consistent with where the sprite appears to be
offset_by_draw_points()314 static void offset_by_draw_points()
315 {
316 	for(int s=0;s<num_sprites;s++)
317 	{
318 		int dx = -sprites[s].frame[0].dir[0].drawpoint.x;
319 		int dy = -sprites[s].frame[0].dir[0].drawpoint.y;
320 
321 		sprites[s].bbox.offset(dx, dy);
322 		sprites[s].slopebox.offset(dx, dy);
323 		sprites[s].solidbox.offset(dx, dy);
324 
325 		sprites[s].block_l.offset(dx, dy);
326 		sprites[s].block_r.offset(dx, dy);
327 		sprites[s].block_u.offset(dx, dy);
328 		sprites[s].block_d.offset(dx, dy);
329 
330 		for(int f=0;f<sprites[s].nframes;f++)
331 		{
332 			for(int d=0;d<sprites[s].ndirs;d++)
333 			{
334 				int dx = -sprites[s].frame[f].dir[d].drawpoint.x;
335 				int dy = -sprites[s].frame[f].dir[d].drawpoint.y;
336 				sprites[s].frame[f].dir[d].pf_bbox.offset(dx, dy);
337 			}
338 		}
339 	}
340 }
341