NXEngine v1.0.0.5
[NXEngine.git] / graphics / sprites.cpp
blob6ebede962f1066224ac596ec4bec70307f50fcc8
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 using namespace Graphics;
15 #include "sprites.h"
16 #include "sprites.fdh"
18 static NXSurface *spritesheet[MAX_SPRITESHEETS];
19 static int num_spritesheets;
20 static StringList sheetfiles;
22 SIFSprite sprites[MAX_SPRITES];
23 int num_sprites;
26 bool Sprites::Init()
28 memset(spritesheet, 0, sizeof(spritesheet));
30 // load sprites info--sheet positions, bounding boxes etc
31 if (load_sif("sprites.sif"))
32 return 1;
34 num_spritesheets = sheetfiles.CountItems();
35 return 0;
38 void Sprites::Close()
40 FlushSheets();
41 sheetfiles.MakeEmpty();
44 void Sprites::FlushSheets()
46 for(int i=0;i<MAX_SPRITESHEETS;i++)
48 if (spritesheet[i])
50 delete spritesheet[i];
51 spritesheet[i] = NULL;
57 void c------------------------------() {}
60 // ensure the given spritesheet is loaded
61 static void Sprites::LoadSheetIfNeeded(int sheetno)
63 if (!spritesheet[sheetno])
65 char pbm_name[MAXPATHLEN];
67 sprintf(pbm_name, "%s/%s", data_dir, sheetfiles.StringAt(sheetno));
68 spritesheet[sheetno] = new NXSurface;
69 spritesheet[sheetno]->LoadImage(pbm_name, true);
71 // fix the blue dash in the middle of the starpoof effect on that one frame,
72 // I'm pretty sure this is a glitch.
73 if (!settings->emulate_bugs)
75 if (sheetno == 3) // Caret.pbm
76 spritesheet[sheetno]->FillRect(40, 58, 41, 58, 0, 0, 0);
82 // master sprite drawing function
83 static void Sprites::BlitSprite(int x, int y, int s, int frame, uint8_t dir, \
84 int xoff, int yoff, int wd, int ht)
86 LoadSheetIfNeeded(sprites[s].spritesheet);
88 dir %= sprites[s].ndirs;
89 SIFDir *sprdir = &sprites[s].frame[frame].dir[dir];
91 DrawSurface(spritesheet[sprites[s].spritesheet], \
92 x, y, \
93 (sprdir->sheet_offset.x + xoff), \
94 (sprdir->sheet_offset.y + yoff), \
95 wd, ht);
99 void c------------------------------() {}
103 // draw sprite "s" at [x,y]. drawing frame "frame" and dir "dir".
104 void Sprites::draw_sprite(int x, int y, int s, int frame, uint8_t dir)
106 BlitSprite(x, y, s, frame, dir, 0, 0, sprites[s].w, sprites[s].h);
109 // draw sprite "s", place it's draw point at [x,y] instead of it's upper-left corner.
110 void Sprites::draw_sprite_at_dp(int x, int y, int s, int frame, uint8_t dir)
112 x -= sprites[s].frame[frame].dir[dir].drawpoint.x;
113 y -= sprites[s].frame[frame].dir[dir].drawpoint.y;
114 BlitSprite(x, y, s, frame, dir, 0, 0, sprites[s].w, sprites[s].h);
118 // draw a portion of a sprite, such as a sprite in the middle of "teleporting".
119 // only the area between clipy1 (inclusive) and clipy2 (exclusive) are visible.
120 void Sprites::draw_sprite_clipped(int x, int y, int s, int frame, uint8_t dir, \
121 int clipx1, int clipx2, int clipy1, int clipy2)
123 BlitSprite(x + clipx1, y + clipy1, s, frame, dir, clipx1, clipy1, \
124 (clipx2 - clipx1), (clipy2 - clipy1));
127 // draw a clipped sprite while clipping only the width.
128 // used for drawing percentage bars, etc.
129 void Sprites::draw_sprite_clip_width(int x, int y, int s, int frame, int wd)
131 BlitSprite(x, y, s, frame, 0, 0, 0, wd, sprites[s].h);
134 // draws a sprite at less than it's actual width by chopping it into two chunks.
135 // on the left, the first "repeat_at" pixels are drawn.
136 // then, the remaining "wd" is drawn from the right half of the sprite.
137 // used for things like drawing the textboxes.
138 void Sprites::draw_sprite_chopped(int x, int y, int s, int frame, int wd, int repeat_at)
140 int xoff;
142 if (wd >= sprites[s].w)
144 draw_sprite(x, y, s, frame);
145 return;
148 // draw the left part
149 BlitSprite(x, y, s, frame, 0, 0, 0, repeat_at, sprites[s].h);
150 x += repeat_at;
151 wd -= repeat_at;
153 // draw the rest of it
154 xoff = (sprites[s].w - wd);
156 BlitSprite(x, y, s, frame, 0, xoff, 0, wd, sprites[s].h);
159 // draws a sprite to any arbitrary width by repeating it over the given distance.
160 // if needed, the rightmost instance of the sprite is clipped.
161 void Sprites::draw_sprite_repeating_x(int x, int y, int s, int frame, int wd)
163 int wdleft = wd;
164 while(wdleft > 0)
166 int blitwd = wdleft;
167 if (blitwd > sprites[s].w) blitwd = sprites[s].w;
169 BlitSprite(x, y, s, frame, 0, 0, 0, blitwd, sprites[s].h);
170 x += blitwd;
171 wdleft -= blitwd;
176 void c------------------------------() {}
179 // return the NXSurface for a given spritesheet #
180 NXSurface *Sprites::get_spritesheet(int sheetno)
182 LoadSheetIfNeeded(sheetno);
183 return spritesheet[sheetno];
186 // create an empty spritesheet of the given size and return it's index.
187 int Sprites::create_spritesheet(int wd, int ht)
189 if (num_spritesheets >= MAX_SPRITESHEETS)
190 return -1;
192 spritesheet[num_spritesheets] = new NXSurface(wd, ht);
193 return num_spritesheets++;
196 // draw a sprite onto some surface other than the screen
197 void Sprites::draw_sprite_to_surface(NXSurface *dst, int x, int y, int s, int frame, uint8_t dir)
199 Graphics::SetDrawTarget(dst);
200 draw_sprite(x, y, s, frame, dir);
201 Graphics::SetDrawTarget(screen);
205 void c------------------------------() {}
208 static bool load_sif(const char *fname)
210 SIFLoader sif;
211 uint8_t *sheetdata, *spritesdata;
212 int sheetdatalength, spritesdatalength;
214 if (sif.LoadHeader(fname))
215 return 1;
217 if (!(sheetdata = sif.FindSection(SIF_SECTION_SHEETS, &sheetdatalength)))
219 staterr("load_sif: file '%s' missing SIF_SECTION_SHEETS", fname);
220 return 1;
223 if (!(spritesdata = sif.FindSection(SIF_SECTION_SPRITES, &spritesdatalength)))
225 staterr("load_sif: file '%s' missing SIF_SECTION_SPRITES", fname);
226 return 1;
229 // decode sheets
230 sheetfiles.MakeEmpty();
231 if (SIFStringArraySect::Decode(sheetdata, sheetdatalength, &sheetfiles))
232 return 1;
234 // decode sprites
235 if (SIFSpritesSect::Decode(spritesdata, spritesdatalength, \
236 &sprites[0], &num_sprites, MAX_SPRITES))
238 staterr("load_sif: SIFSpritesSect decoder failed");
239 return 1;
242 sif.CloseFile();
244 create_slope_boxes();
245 offset_by_draw_points();
246 expand_single_dir_sprites();
248 return 0;
252 // create slope boxes for all sprites, used by the slope-handling routines
253 // these are basically just a form of bounding box describing the bounds of the
254 // blockd points.
255 static void create_slope_boxes()
257 for(int s=0;s<num_sprites;s++)
259 if (sprites[s].block_d.count != 0)
261 int leftmost = 99999;
262 int rightmost = -99999;
263 for(int i=0;i<sprites[s].block_d.count;i++)
265 if (sprites[s].block_d[i].x < leftmost) leftmost = sprites[s].block_d[i].x;
266 if (sprites[s].block_d[i].x > rightmost) rightmost = sprites[s].block_d[i].x;
269 sprites[s].slopebox.x1 = leftmost;
270 sprites[s].slopebox.x2 = rightmost;
272 if (sprites[s].block_u.count)
273 sprites[s].slopebox.y1 = (sprites[s].block_u[0].y + 1);
274 else
275 sprites[s].slopebox.y1 = 0;
277 sprites[s].slopebox.y2 = (sprites[s].block_d[0].y - 1);
281 sprites[SPR_MYCHAR].slopebox.y1 += 3;
284 // offset things like blockl/r/u/d, bounding box etc by the draw point of all
285 // sprites so that these things are consistent with where the sprite appears to be
286 static void offset_by_draw_points()
288 for(int s=0;s<num_sprites;s++)
290 int dx = -sprites[s].frame[0].dir[0].drawpoint.x;
291 int dy = -sprites[s].frame[0].dir[0].drawpoint.y;
293 sprites[s].bbox.offset(dx, dy);
294 sprites[s].slopebox.offset(dx, dy);
295 sprites[s].solidbox.offset(dx, dy);
297 sprites[s].block_l.offset(dx, dy);
298 sprites[s].block_r.offset(dx, dy);
299 sprites[s].block_u.offset(dx, dy);
300 sprites[s].block_d.offset(dx, dy);
302 for(int f=0;f<sprites[s].nframes;f++)
304 for(int d=0;d<sprites[s].ndirs;d++)
306 int dx = -sprites[s].frame[f].dir[d].drawpoint.x;
307 int dy = -sprites[s].frame[f].dir[d].drawpoint.y;
308 sprites[s].frame[f].dir[d].pf_bbox.offset(dx, dy);
314 // for sprites which only have 1 dir (no separate frames for left & right),
315 // create a 2nd identical dir as the rest of the engine doesn't bother
316 // with this complication.
317 static void expand_single_dir_sprites()
319 for(int s=0;s<num_sprites;s++)
321 if (sprites[s].ndirs == 1)
323 sprites[s].ndirs = 2;
324 for(int f=0;f<sprites[s].nframes;f++)
325 sprites[s].frame[f].dir[1] = sprites[s].frame[f].dir[0];