1 //----------------------------------------------------------------------------
2 // EDGE Sprite Management
3 //----------------------------------------------------------------------------
4 //
5 // Copyright (c) 1999-2009 The EDGE Team.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 // Based on the DOOM source code, released by Id Software under the
20 // following copyright:
21 //
22 // Copyright (C) 1993-1996 by id Software, Inc.
23 //
24 //----------------------------------------------------------------------------
25 //
26 // -KM- 1998/07/26 Replaced #ifdef RANGECHECK with #ifdef DEVELOPERS
27 // -KM- 1998/09/27 Dynamic colourmaps
28 // -AJA- 1999/07/12: Now uses colmap.ddf.
29 //
30
31 #include "i_defs.h"
32
33 #include "e_main.h"
34 #include "e_search.h"
35 #include "r_image.h"
36 #include "r_things.h"
37 #include "w_sprite.h"
38 #include "w_wad.h"
39
40 #include "p_local.h" // mobjlisthead
41
42
43 //
44 // A sprite definition: a number of animation frames.
45 //
46 class spritedef_c
47 {
48 public:
49 // four letter sprite name (e.g. "TROO").
50 char name[6];
51
52 // total number of frames. Zero for missing sprites.
53 int numframes;
54
55 // sprite frames.
56 spriteframe_c *frames;
57
58 public:
spritedef_c(const char * _name)59 spritedef_c(const char *_name) : numframes(0), frames(NULL)
60 {
61 strcpy(name, _name);
62 }
63
~spritedef_c()64 ~spritedef_c()
65 {
66 // TODO: free the frames
67 }
68
HasWeapon() const69 bool HasWeapon() const
70 {
71 for (int i = 0 ; i < numframes ; i++)
72 if (frames[i].is_weapon)
73 return true;
74
75 return false;
76 }
77 };
78
79
80 //----------------------------------------------------------------------------
81
82 //
83 // Sprite rotation 0 is facing the viewer,
84 // rotation 1 is one angle turn CLOCKWISE around the axis.
85 // This is not the same as the angle,
86 // which increases counter clockwise (protractor).
87 // There was a lot of stuff grabbed wrong, so I changed it...
88 //
89
90 // Sprite definitions
91 static spritedef_c **sprites;
92 static int numsprites = 0;
93
94 // Sorted map of sprite defs. Only used during initialisation.
95 static spritedef_c ** sprite_map = NULL;
96 static int sprite_map_len;
97
98
99 //
100 // SPRITE LOADING FUNCTIONS
101 //
102
WhatFrame(spritedef_c * def,const char * name,int pos)103 static spriteframe_c *WhatFrame(spritedef_c *def, const char *name, int pos)
104 {
105 char frame_ch = name[pos];
106
107 int index;
108
109 if ('A' <= frame_ch && frame_ch <= 'Z')
110 {
111 index = (frame_ch - 'A');
112 }
113 else switch (frame_ch)
114 {
115 case '[': index = 26; break;
116 case '\\': index = 27; break;
117 case ']': index = 28; break;
118 case '^': index = 29; break;
119 case '_': index = 30; break;
120
121 default:
122 I_Warning("Sprite lump %s has illegal frame.\n", name);
123 return NULL;
124 }
125
126 SYS_ASSERT(index >= 0);
127
128 // ignore frames larger than what is used in DDF
129 if (index >= def->numframes)
130 return NULL;
131
132 return & def->frames[index];
133 }
134
SetExtendedRots(spriteframe_c * frame)135 static void SetExtendedRots(spriteframe_c *frame)
136 {
137 frame->rots = 16;
138
139 for (int i = 7; i >= 1; i--)
140 {
141 frame->images[2*i] = frame->images[i];
142 frame->flip[2*i] = frame->flip[i];
143 }
144
145 for (int k = 1; k <= 15; k += 2)
146 {
147 frame->images[k] = NULL;
148 frame->flip[k] = 0;
149 }
150 }
151
WhatRot(spriteframe_c * frame,const char * name,int pos)152 static int WhatRot(spriteframe_c *frame, const char *name, int pos )
153 {
154 char rot_ch = name[pos];
155
156 int rot;
157
158 // NOTE: rotations 9 and A-G are EDGE specific.
159
160 if ('0' <= rot_ch && rot_ch <= '9')
161 rot = (rot_ch - '0');
162 else if ('A' <= rot_ch && rot_ch <= 'G')
163 rot = (rot_ch - 'A') + 10;
164 else
165 {
166 I_Warning("Sprite lump %s has illegal rotation.\n", name);
167 return -1;
168 }
169
170 if (frame->rots == 0)
171 frame->rots = 1;
172
173 if (rot >= 1 && frame->rots == 1)
174 frame->rots = 8;
175
176 if (rot >= 9 && frame->rots != 16)
177 SetExtendedRots(frame);
178
179 switch (frame->rots)
180 {
181 case 1:
182 return 0;
183
184 case 8:
185 return rot-1;
186
187 case 16:
188 if (rot >= 9)
189 return 1 + (rot-9) * 2;
190 else
191 return 0 + (rot-1) * 2;
192
193 default:
194 I_Error("INTERNAL ERROR: frame->rots = %d\n", frame->rots);
195 return -1; /* NOT REACHED */
196 }
197 }
198
InstallSpriteLump(spritedef_c * def,int lump,const char * lumpname,int pos,byte flip)199 static void InstallSpriteLump(spritedef_c *def, int lump,
200 const char *lumpname, int pos, byte flip)
201 {
202 spriteframe_c *frame = WhatFrame(def, lumpname, pos);
203 if (! frame)
204 return;
205
206 // don't disturb any frames already loaded
207 if (frame->finished)
208 return;
209
210 int rot = WhatRot(frame, lumpname, pos+1);
211 if (rot < 0)
212 return;
213
214 SYS_ASSERT(0 <= rot && rot < 16);
215
216 if (frame->images[rot])
217 {
218 I_Warning("Sprite %s has two lumps mapped to it (frame %c).\n",
219 lumpname, lumpname[pos]);
220 return;
221 }
222
223 frame->images[rot] = W_ImageCreateSprite(lumpname, lump,
224 frame->is_weapon);
225
226 frame->flip[rot] = flip;
227 }
228
InstallSpriteImage(spritedef_c * def,const image_c * img,const char * img_name,int pos,byte flip)229 static void InstallSpriteImage(spritedef_c *def, const image_c *img,
230 const char *img_name, int pos, byte flip)
231 {
232 spriteframe_c *frame = WhatFrame(def, img_name, pos);
233 if (! frame)
234 return;
235
236 // don't disturb any frames already loaded
237 if (frame->finished)
238 return;
239
240 int rot = WhatRot(frame, img_name, pos+1);
241 if (rot < 0)
242 return;
243
244 if (frame->images[rot])
245 {
246 I_Warning("Sprite %s has two images mapped to it (frame %c)\n",
247 img_name, img_name[pos]);
248 return;
249 }
250
251 frame->images[rot] = img;
252 frame->flip[rot] = flip;
253 }
254
255 //
256 // FillSpriteFrames
257 //
FillSpriteFrames(int file,int prog_base,int prog_total)258 static void FillSpriteFrames(int file, int prog_base, int prog_total)
259 {
260 epi::u32array_c& lumps = W_GetListLumps(file, LMPLST_Sprites);
261 int lumpnum = lumps.GetSize();
262
263 if (lumpnum == 0)
264 return;
265
266 // check all lumps for prefixes matching the ones in the sprite
267 // list. Both lists have already been sorted to make this as fast
268 // as possible.
269
270 int S = 0, L = 0;
271
272 while (S < sprite_map_len && L < lumpnum)
273 {
274 const char *sprname = sprite_map[S]->name;
275 const char *lumpname = W_GetLumpName(lumps[L]);
276
277 // ignore model skins
278 if (lumpname[4] == 'S' && lumpname[5] == 'K' && lumpname[6] == 'N')
279 {
280 L++; continue;
281 }
282
283 if (strlen(lumpname) != 6 && strlen(lumpname) != 8)
284 {
285 I_Warning("Sprite name %s has illegal length.\n", lumpname);
286 L++; continue;
287 }
288
289 int comp = strncmp(sprname, lumpname, 4);
290
291 if (comp < 0) // S < L
292 {
293 S++; continue;
294 }
295 else if (comp > 0) // S > L
296 {
297 L++; continue;
298 }
299
300 // we have a match
301 InstallSpriteLump(sprite_map[S], lumps[L], lumpname, 4, 0);
302
303 if (lumpname[6])
304 InstallSpriteLump(sprite_map[S], lumps[L], lumpname, 6, 1);
305
306 L++;
307
308 // update startup progress bar
309 E_LocalProgress(prog_base + L * 100 / lumpnum, prog_total);
310 }
311 }
312
313 //
314 // FillSpriteFramesUser
315 //
316 // Like the above, but made especially for IMAGES.DDF.
317 //
FillSpriteFramesUser(int prog_base,int prog_total)318 static void FillSpriteFramesUser(int prog_base, int prog_total)
319 {
320 int img_num;
321 const image_c ** images = W_ImageGetUserSprites(&img_num);
322
323 if (img_num == 0)
324 return;
325
326 SYS_ASSERT(images);
327
328 int S = 0, L = 0;
329
330 while (S < sprite_map_len && L < img_num)
331 {
332 const char *sprname = sprite_map[S]->name;
333 const char *img_name = W_ImageGetName(images[L]);
334
335 // ignore model skins
336 if (img_name[4] == 'S' && img_name[5] == 'K' && img_name[6] == 'N')
337 {
338 L++; continue;
339 }
340
341 if (strlen(img_name) != 6 && strlen(img_name) != 8)
342 {
343 I_Warning("Sprite name %s (IMAGES.DDF) has illegal length.\n", img_name);
344 L++; continue;
345 }
346
347 int comp = strncmp(sprname, img_name, 4);
348
349 if (comp < 0) // S < L
350 {
351 S++; continue;
352 }
353 else if (comp > 0) // S > L
354 {
355 L++; continue;
356 }
357
358 // we have a match
359 InstallSpriteImage(sprite_map[S], images[L], img_name, 4, 0);
360
361 if (img_name[6])
362 InstallSpriteImage(sprite_map[S], images[L], img_name, 6, 1);
363
364 L++;
365
366 // update startup progress bar
367 E_LocalProgress(prog_base + L * 100 / img_num, prog_total);
368 }
369
370 delete[] images;
371 }
372
MarkCompletedFrames(void)373 static void MarkCompletedFrames(void)
374 {
375 int src, dst;
376
377 for (src = dst = 0; src < sprite_map_len; src++)
378 {
379 spritedef_c *def = sprite_map[src];
380
381 int finish_num = 0;
382
383 for (int f = 0; f < def->numframes; f++)
384 {
385 char frame_ch = 'A' + f;
386 spriteframe_c *frame = def->frames + f;
387
388 if (frame->finished)
389 {
390 finish_num++;
391 continue;
392 }
393
394 int rot_count = 0;
395
396 // check if all image pointers are NULL
397 for (int i=0; i < frame->rots; i++)
398 rot_count += frame->images[i] ? 1 : 0;
399
400 if (rot_count == 0)
401 continue;
402
403 frame->finished = 1;
404 finish_num++;
405
406 if (rot_count < frame->rots)
407 I_Warning("Sprite %s:%c is missing rotations (%d of %d).\n",
408 def->name, frame_ch, frame->rots - rot_count, frame->rots);
409 }
410
411 // remove complete sprites from sprite_map
412 if (finish_num == def->numframes)
413 continue;
414
415 sprite_map[dst++] = def;
416 }
417
418 sprite_map_len = dst;
419 }
420
421 // show warnings for missing patches
CheckSpriteFrames(spritedef_c * def)422 static void CheckSpriteFrames(spritedef_c *def)
423 {
424 int missing = 0;
425
426 for (int i = 0; i < def->numframes; i++)
427 if (! def->frames[i].finished)
428 {
429 I_Debugf("Frame %d/%d is not finished\n", 1+i, def->numframes);
430 missing++;
431 }
432
433 if (missing > 0 && missing < def->numframes)
434 I_Warning("Missing %d frames in sprite: %s\n", missing, def->name);
435
436 // free some memory for completely missing sprites
437 if (missing == def->numframes)
438 {
439 delete[] def->frames;
440
441 def->numframes = 0;
442 def->frames = NULL;
443 }
444 }
445
446 //
447 // W_InitSprites
448 //
449 // Use the sprite lists in the WAD (S_START..S_END) to flesh out the
450 // known sprite definitions (global `sprites' array, created while
451 // parsing DDF) with images.
452 //
453 // Checking for missing frames still done at run time.
454 //
455 // -AJA- 2001/02/01: rewrote this stuff.
456 //
W_InitSprites(void)457 void W_InitSprites(void)
458 {
459 numsprites = (int)ddf_sprite_names.size();
460
461 if (numsprites <= 1)
462 I_Error("Missing sprite definitions !!\n");
463
464 I_Printf("W_InitSprites: Finding sprite patches\n");
465
466 // 1. Allocate sprite definitions (ignore NULL sprite, #0)
467
468 sprites = new spritedef_c* [numsprites];
469 sprites[SPR_NULL] = NULL;
470
471 for (int i=1; i < numsprites; i++)
472 {
473 const char *name = ddf_sprite_names[i].c_str();
474
475 SYS_ASSERT(strlen(name) == 4);
476
477 sprites[i] = new spritedef_c(name);
478 }
479
480 // 2. Scan the state table, count frames
481
482 for (int stnum = 1; stnum < num_states; stnum++)
483 {
484 state_t *st = &states[stnum];
485
486 if (st->flags & SFF_Model)
487 continue;
488
489 if (st->sprite == SPR_NULL)
490 continue;
491
492 spritedef_c *def = sprites[st->sprite];
493
494 if (def->numframes < st->frame+1)
495 def->numframes = st->frame+1;
496 }
497
498 // 3. Allocate frames
499
500 for (int k=1; k < numsprites; k++)
501 {
502 spritedef_c *def = sprites[k];
503
504 SYS_ASSERT(def->numframes > 0);
505
506 def->frames = new spriteframe_c[def->numframes];
507 }
508
509 // 4. Mark weapon frames
510
511 for (int st_kk = 1; st_kk < num_states; st_kk++)
512 {
513 state_t *st = &states[st_kk];
514
515 if (st->flags & SFF_Model)
516 continue;
517
518 if (st->sprite == SPR_NULL)
519 continue;
520
521 spritedef_c *def = sprites[st->sprite];
522
523 if (st->flags & SFF_Weapon)
524 def->frames[st->frame].is_weapon = true;
525 }
526
527 // 5. Fill in frames using wad lumps + images.ddf
528
529 // create a sorted list (ignore NULL entry, #0)
530 sprite_map_len = numsprites - 1;
531
532 sprite_map = new spritedef_c* [sprite_map_len];
533
534 for (int n=0; n < sprite_map_len; n++)
535 sprite_map[n] = sprites[n + 1];
536
537 #define CMP(a, b) (strcmp(a->name, b->name) < 0)
538 QSORT(spritedef_c *, sprite_map, sprite_map_len, CUTOFF);
539 #undef CMP
540
541 // iterate over each file. Order is important, we must go from
542 // newest wad to oldest, so that new sprites override the old ones.
543 // Completely finished sprites get removed from the list as we go.
544 //
545 // NOTE WELL: override granularity is single frames.
546
547 int numfiles = W_GetNumFiles();
548
549 FillSpriteFramesUser(0, (numfiles+1) * 100);
550
551 for (int file = numfiles - 1; file >= 0; file--)
552 {
553 FillSpriteFrames(file, (numfiles - file) * 100, (numfiles+1) * 100);
554 MarkCompletedFrames();
555 }
556
557 // 6. Perform checks and free stuff
558
559 for (int j=1; j < numsprites; j++)
560 CheckSpriteFrames(sprites[j]);
561
562 delete[] sprite_map;
563 sprite_map = NULL;
564 }
565
566
W_CheckSpritesExist(const state_group_t & group)567 bool W_CheckSpritesExist(const state_group_t& group)
568 {
569 for (int g = 0; g < (int)group.size(); g++)
570 {
571 const state_range_t& range = group[g];
572
573 for (int i = range.first; i <= range.last; i++)
574 {
575 if (states[i].sprite == SPR_NULL)
576 continue;
577
578 if (sprites[states[i].sprite]->numframes > 0)
579 return true;
580
581 // -AJA- only check one per group. It _should_ check them all,
582 // however this maintains compatibility.
583 break;
584 }
585 }
586
587 return false;
588 }
589
590
W_GetSpriteFrame(int spr_num,int framenum)591 spriteframe_c *W_GetSpriteFrame(int spr_num, int framenum)
592 {
593 // spr_num comes from the 'sprite' field of state_t, and
594 // is also an index into ddf_sprite_names vector.
595
596 SYS_ASSERT(spr_num > 0);
597 SYS_ASSERT(spr_num < numsprites);
598 SYS_ASSERT(framenum >= 0);
599
600 spritedef_c *def = sprites[spr_num];
601
602 if (framenum >= def->numframes)
603 return NULL;
604
605 spriteframe_c *frame = &def->frames[framenum];
606
607 if (!frame || !frame->finished)
608 return NULL;
609
610 return frame;
611 }
612
613
W_PrecacheSprites(void)614 void W_PrecacheSprites(void)
615 {
616 SYS_ASSERT(numsprites > 1);
617
618 byte *sprite_present = new byte[numsprites];
619 memset(sprite_present, 0, numsprites);
620
621 for (mobj_t * mo = mobjlisthead ; mo ; mo = mo->next)
622 {
623 SYS_ASSERT(mo->state);
624
625 if (mo->state->sprite < 1 || mo->state->sprite >= numsprites)
626 continue;
627
628 sprite_present[mo->state->sprite] = 1;
629 }
630
631 for (int i = 1 ; i < numsprites ; i++) // ignore SPR_NULL
632 {
633 spritedef_c *def = sprites[i];
634
635 const image_c *cur_image;
636 const image_c *last_image = NULL; // an optimisation
637
638 if (def->numframes == 0)
639 continue;
640
641 // Note: all weapon sprites are pre-cached
642
643 if (! (sprite_present[i] || def->HasWeapon()))
644 continue;
645
646 I_Debugf("Precaching sprite: %s\n", def->name);
647
648 SYS_ASSERT(def->frames);
649
650 for (int fr = 0 ; fr < def->numframes ; fr++)
651 {
652 if (! def->frames[fr].finished)
653 continue;
654
655 for (int rot = 0 ; rot < 16 ; rot++)
656 {
657 cur_image = def->frames[fr].images[rot];
658
659 if (cur_image == NULL || cur_image == last_image)
660 continue;
661
662 W_ImagePreCache(cur_image);
663
664 last_image = cur_image;
665 }
666 }
667 }
668
669 delete[] sprite_present;
670 }
671
672 //--- editor settings ---
673 // vi:ts=4:sw=4:noexpandtab
674