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