1 /*
2  * Holotz's Castle
3  * Copyright (C) 2004 Juan Carlos Seijo P�rez
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc., 59
17  * Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  * Juan Carlos Seijo P�rez
20  * jacob@mainreactor.net
21  */
22 
23 /** Game level. Includes a map and its enemies.
24  * @file    HCLevel.cpp
25  * @author  Juan Carlos Seijo P�rez
26  * @date    29/05/2004
27  * @version 0.0.1 - 29/05/2004 - First version
28  */
29 
30 #include <HCLevel.h>
31 
32 #ifndef HC_DATA_DIR
33 #define HC_DATA_DIR "res/"
34 #endif
35 
HCLevel()36 HCLevel::HCLevel()  : numEnemies(0), enemies(0), numRopes(0),
37 											ropes(0), remainingObjects(0), numObjects(0), objects(0),
38 											scripted(false), narrative(0), wideMap(false), highMap(false),
39 											maxTime(1), timerFont(0)
40 {
41 }
42 
Init()43 bool HCLevel::Init()
44 {
45 	// Initializes the character
46 	character.Init(theme.MainChar(character.Subtype()), &map, ropes, numRopes);
47 	character.State(HCCS_STOP);
48 
49 	// Creates an empty background
50 	bg.Destroy();
51 	if (!bg.Create(1, 1, 0))
52 	{
53 		fprintf(stderr, "Error creating empty level background.\n");
54 		return false;
55 	}
56 
57 	// Loads sounds
58 	soundObjectAcquired.Destroy();
59 	musicEndLevel.Destroy();
60 
61 	if (JApp::App()->SoundEnabled() &&
62 			(!soundObjectAcquired.LoadWave(HC_DATA_DIR "sound/HCObjectAcquired.wav") ||
63 			 !musicEndLevel.LoadWave(HC_DATA_DIR "sound/HCEndLevel.wav") ||
64 			 !soundExitUnlocked.LoadWave(HC_DATA_DIR "sound/HCExitUnlocked.wav")))
65 	{
66 		fprintf(stderr, "Error loading sounds, check installation.\n");
67 	}
68 
69 	// Initializes the exit
70 	levelExit.Lock();
71 	levelExit.Init(&map, 100);
72 
73 	// Initializes the timer
74 	levelTimer.Init(maxTime, timerFont);
75 	levelTimer.Pos(0, 0);
76 
77 	return true;
78 }
79 
Update()80 s32 HCLevel::Update()
81 {
82 	s32 ret = 0;
83 
84 	s32 i;
85 
86 	// Updates the map
87 	map.Update();
88 
89 	// Updates the ropes
90 	for (i = 0; i < numRopes; ++i)
91 	{
92 		ropes[i]->Update();
93 	}
94 
95 	// Updates the character unless it's exiting the level
96 	if (!scripted)
97 	{
98 		switch (LevelExit()->State())
99 		{
100 		default:
101 		case HCEXITSTATE_LOCKED:
102 			{
103 				if (IsExitUnlocked())
104 				{
105 					LevelExit()->Unlock();
106 
107 					if (JApp::App()->SoundEnabled())
108 					{
109 						soundExitUnlocked.Play();
110 					}
111 				}
112 				character.Update();
113 			}
114 			break;
115 
116 		case HCEXITSTATE_UNLOCKED:
117 			{
118 				character.Update();
119 
120 				// Checks if the character is in the exit sight and swallows it if so
121 				if (Character()->X() >= LevelExit()->X() &&
122 						Character()->X() <= LevelExit()->X1() &&
123 						Character()->Y() >= LevelExit()->Y() &&
124 						Character()->Y() <= LevelExit()->Y1())
125 				{
126 					// Time stops
127 					levelTimer.Pause();
128 
129 					// Inside, swallow it!
130 					LevelExit()->Swallow(Character());
131 
132 					if (JApp::App()->SoundEnabled())
133 					{
134 						musicEndLevel.Play();
135 					}
136 
137 					for (i = 0; i < NumEnemies(); ++i)
138 					{
139 						Enemies()[i]->State(HCCS_DIE);
140 					}
141 				}
142 			}
143 			break;
144 
145 		case HCEXITSTATE_SWALLOWED:
146 			{
147 				// Level achieved!!
148 				if (!JApp::App()->SoundEnabled() || !musicEndLevel.IsPlaying())
149 				{
150 					ret = 2;
151 				}
152 			}
153 			break;
154 
155 		case HCEXITSTATE_SWALLOWING:
156 			break;
157 		}
158 	}
159 	else
160 	{
161 		character.Update();
162 	}
163 
164 	// Updates the objects
165 	for (i = 0; i < numObjects; ++i)
166 	{
167 		objects[i]->Update();
168 	}
169 
170 	// Checks for objects to pick up
171 	CheckObjects();
172 
173 	// Positions the level according to the character position so it's always visible
174 	// but it can be overriden if any script exists
175 	spot = character.Pos();
176 
177 	// Updates the enemies
178 	if (scripted)
179 	{
180 		for (i = 0; i < numEnemies; ++i)
181 		{
182 			// Updates the enemies as if they were normal characters
183 			enemies[i]->HCCharacter::Update();
184 		}
185 	}
186 	else
187 	{
188 		bool collide = false;
189 
190 		for (i = 0; !collide && i < numEnemies; ++i)
191 		{
192 			enemies[i]->Update();
193 
194 			if (HCPreferences::Prefs()->Difficulty() != HCPREFERENCES_TOY)
195 			{
196 				// Checks collisions with enemies, except in toy mode
197 				if (!collide && Collide(&character, enemies[i]))
198 				{
199 					character.State(HCCS_DIE);
200 					collide = true;
201 					ret = 1;
202 				}
203 			}
204 		}
205 
206 		// Updates the time remaining, except in toy mode
207 		if (levelTimer.Update() <= 0)
208 		{
209 			if (HCPreferences::Prefs()->Difficulty() != HCPREFERENCES_TOY)
210 			{
211 				// Time up! (Except in toy mode)
212 				character.State(HCCS_DIE);
213 				ret = 1;
214 			}
215 		}
216 	}
217 
218 	if (narrative)
219 	{
220 		narrative->Update();
221 	}
222 
223 	if (!scripted)
224 	{
225 		levelExit.Update();
226 	}
227 
228 	// Updates the level position according to the spot position
229 	s32 x = (s32)pos.x, y = (s32)pos.y;
230 	if (wideMap)
231 	{
232 		s32 w2 = JApp::App()->Width()/2;
233 
234 		if (spot.x < (w2 - viewRadius))
235 		{
236 			// Spot outside viewing spot, moves towards the spot
237 			s32 vx = (s32)JMin((w2 - viewRadius - spot.x)/4, map.CellWidth());
238 
239 			if (vx <= 1) vx = 1;
240 
241 			x += vx;
242 		}
243 		else
244 		if (spot.x > (w2 + viewRadius))
245 		{
246 			// Spot outside viewing spot, moves towards the spot
247 			s32 vx = (s32)JMin((spot.x - (w2 + viewRadius))/4, map.CellWidth());
248 
249 			if (vx <= 1) vx = 1;
250 
251 			x -= vx;
252 		}
253 	}
254 
255 	if (highMap)
256 	{
257 		s32 h2 = JApp::App()->Height()/2;
258 
259 		if (spot.y < (h2 - viewRadius))
260 		{
261 			// Spot outside viewing spot, moves towards the spot
262 			s32 vy = (s32)JMin((h2 - viewRadius - spot.y)/5, map.CellHeight());
263 
264 			if (vy <= 1) vy = 1;
265 
266 			y += vy;
267 		}
268 		else
269 		if (spot.y > (h2 + viewRadius))
270 		{
271 			// Spot outside viewing spot, moves towards the spot
272 			s32 vy = (s32)JMin((spot.y - (h2 + viewRadius))/5, map.CellHeight());
273 
274 			if (vy <= 1) vy = 1;
275 
276 			y -= vy;
277 		}
278 	}
279 
280 	if (wideMap || highMap)
281 	{
282 		Pos(x, y);
283 	}
284 
285 	return ret;
286 }
287 
Draw()288 void HCLevel::Draw()
289 {
290 	s32 i;
291 
292 	// Draws the background
293 	bg.Draw();
294 
295 	// Draws the map
296 	map.Draw();
297 
298 	// Draws the objects
299 	for (i = 0; i < numObjects; ++i)
300 	{
301 		objects[i]->Draw();
302 	}
303 
304 	// Draws the enemies
305 	for (i = 0; i < numEnemies; ++i)
306 	{
307 		enemies[i]->Draw();
308 	}
309 
310 	// Draws the ropes
311 	for (i = 0; i < numRopes; ++i)
312 	{
313 		ropes[i]->Draw();
314 	}
315 
316 	// Draws the main character
317 	character.Draw();
318 
319 	if (narrative)
320 	{
321 		narrative->Draw();
322 	}
323 
324 	if (!scripted)
325 	{
326 		levelExit.Draw();
327 
328 		if (HCPreferences::Prefs()->Difficulty() != HCPREFERENCES_TOY)
329 		{
330 			// Draws the time remaining
331 			levelTimer.Draw();
332 		}
333 
334 		// Draws the remaining objects to complete the level
335 		float qw = float(JApp::App()->Width())/4.0f, dx = qw/float(numObjects + 1);
336 		for (i = 0; i < numObjects; ++i)
337 		{
338 			objects[i]->Draw();
339 
340 			if (objects[i]->State() == HCOBJECTSTATE_NORMAL)
341 			{
342 				((JImage *)objects[i]->Normal().Frame(0))->Draw(s32(qw * 3.0f + dx * float(i)), 0);
343 			}
344 		}
345 	}
346 }
347 
Load(JRW & file,const char * filename)348 u32 HCLevel::Load(JRW &file, const char *filename)
349 {
350 	Destroy();
351 
352 	// Maximum time to complete the level, 0 means no timed
353 	if (0 == file.ReadLE32(&maxTime))
354 	{
355 		fprintf(stderr, "Error reading the level completion time.\n");
356 		perror("");
357 
358 		return 1;
359 	}
360 
361 	// Adjusts max time according to the level of difficulty (x1, x2, x3)
362 	maxTime *= HCPreferences::Prefs()->Difficulty();
363 
364 	// Theme name
365 	JString str;
366 	if (0 != str.Load(file))
367 	{
368 		fprintf(stderr, "Error loading the theme name.\n");
369 
370 		return 2;
371 	}
372 
373 	// Loads the theme
374 	if (!theme.Load(str))
375 	{
376 		fprintf(stderr, "Error loading the theme %s.\n", theme.Name());
377 
378 		return 2;
379 	}
380 
381 	// Initializes and loads the map
382 	if (!map.Init(theme) ||
383 			0 != map.Load(file))
384 	{
385 		fprintf(stderr, "Error loading the level map.\n");
386 
387 		return 2;
388 	}
389 
390 	// Main character
391 	if (0 != character.Load(file))
392 	{
393 		fprintf(stderr, "Error loading main character.\n");
394 
395 		return 1;
396 	}
397 
398 	// Initializes enemie's maximum global veloccities
399 	HCEnemy::MaxXVeloccity(character.MaxVeloccity().x);
400 	HCEnemy::MaxYVeloccity(character.MaxVeloccity().y);
401 
402 	// Loads the enemies
403 	if (0 == file.ReadLE32(&numEnemies))
404 	{
405 		fprintf(stderr, "Error reading the number of enemies.\n");
406 
407 		return 1;
408 	}
409 
410 	if (numEnemies > 0)
411 	{
412 		enemies = new HCEnemy *[numEnemies];
413 
414 		s32 enemyType;
415 
416 		for (s32 i = 0; i < numEnemies; ++i)
417 		{
418 			if (0 != file.ReadLE32((s32 *)&enemyType))
419 			{
420         // Lets the file at ist original position
421         file.Seek(-4, SEEK_CUR);
422 
423 				// Peeps the type of enemy
424 				switch (enemyType)
425 				{
426 				default:
427 				case HCENEMYTYPE_BALL:
428 					enemies[i] = new HCEnemyBall;
429 					break;
430 
431 				case HCENEMYTYPE_RANDOM:
432 					enemies[i] = new HCEnemyRandom;
433 					break;
434 
435 				case HCENEMYTYPE_STATIC:
436 					enemies[i] = new HCEnemyStatic;
437 					break;
438 
439 				case HCENEMYTYPE_MAKER:
440 					enemies[i] = new HCEnemyMaker;
441 					break;
442 
443 				case HCENEMYTYPE_CHASER:
444 					enemies[i] = new HCEnemyChaser;
445 					break;
446 				}
447 
448 				if (0 != enemies[i]->Load(file, theme, &map))
449 				{
450 					Destroy();
451 
452 					fprintf(stderr, "Error loading the enemies.\n");
453 					return 2;
454 				}
455 			}
456 		}
457 	}
458 
459 	// Loads the objects
460 	if (0 == file.ReadLE32(&numObjects))
461 	{
462 		fprintf(stderr, "Error reading the number of objects.\n");
463 
464 		return 1;
465 	}
466 
467 	if (numObjects > 0)
468 	{
469 		remainingObjects = numObjects;
470 		objects = new HCObject *[numObjects];
471 
472 		for (s32 i = 0; i < numObjects; ++i)
473 		{
474 			objects[i] = new HCObject;
475 
476 			if (0 != objects[i]->Load(file))
477 			{
478 				Destroy();
479 
480 				fprintf(stderr, "Error loading the objects.\n");
481 				return 2;
482 			}
483 
484 			objects[i]->Init(&theme);
485 		}
486 	}
487 
488 	// Loads the ropes
489 	if (0 == file.ReadLE32(&numRopes))
490 	{
491 		fprintf(stderr, "Error reading the number of ropes.\n");
492 
493 		return 1;
494 	}
495 
496 	if (numRopes > 0)
497 	{
498 		ropes = new HCRope *[numRopes];
499 
500 		for (s32 i = 0; i < numRopes; ++i)
501 		{
502 			ropes[i] = new HCRope();
503 
504 			if (0 != ropes[i]->Load(file, theme))
505 			{
506 				Destroy();
507 
508 				fprintf(stderr, "Error loading the ropes.\n");
509 				return 2;
510 			}
511 		}
512 	}
513 
514 	if (!Init())
515 	{
516 		fprintf(stderr, "Error initializing the level.\n");
517 		return 2;
518 	}
519 
520 	// Checks if a background named as the file and ended in '.tga' exists
521 	str.Format("%s.tga", filename);
522 
523 	if (JFile::Exists(str))
524 	{
525 		bg.Destroy();
526 
527 		if (!bg.Load(str))
528 		{
529 			fprintf(stderr, "Error loading level background %s.\n", str.Str());
530 			return 1;
531 		}
532 
533 		bg.Pos(0, 0);
534 	}
535 	else
536 	{
537 		// Creates an empty background
538 		if (!bg.Create(1, 1, 0))
539 		{
540 			fprintf(stderr, "Error creating empty level background.\n");
541 			return 2;
542 		}
543 	}
544 
545 	// Checks for chaser enemies in the level
546 	for (s32 i = 0; i < numEnemies; ++i)
547 	{
548 		if (enemies[i]->Type() == HCENEMYTYPE_CHASER)
549 		{
550 			((HCEnemyChaser *)enemies[i])->Chase(&character);
551 		}
552 	}
553 
554 	// Initializes the viewing parameters
555 	s32 x = 0, y = 0;
556 	viewRadius = 0;
557 
558 	if (map.Width() > JApp::App()->Width())
559 	{
560 		wideMap = true;
561 
562 		// Defaults the viewing square size to a quarter of the application width
563 		viewRadius = JApp::App()->Width()/8;
564 	}
565 	else
566 	{
567 		x = (JApp::App()->Width() - map.Width())/2;
568 		wideMap = false;
569 	}
570 
571 	if (map.Height() > JApp::App()->Height())
572 	{
573 		highMap = true;
574 
575 		// Defaults the viewing square size to a quarter of the application height or
576 		// the viewRadius, the greatest
577 		if (viewRadius != 0)
578 		{
579 			viewRadius = (s32)JMin(JApp::App()->Height()/6, viewRadius);
580 		}
581 		else
582 		{
583 			viewRadius = JApp::App()->Height()/6;
584 		}
585 	}
586 	else
587 	{
588 		y = (JApp::App()->Height() - map.Height())/2;
589 		highMap = false;
590 	}
591 
592 	Pos(x, y);
593 
594 	return 0;
595 }
596 
Save(JRW & file)597 u32 HCLevel::Save(JRW &file)
598 {
599 	s32 x, y;
600 
601 	// Time to complete the level
602 	if (0 == file.WriteLE32(&maxTime))
603 	{
604 		fprintf(stderr, "Error writing the completion time.\n");
605 
606 		return 1;
607 	}
608 
609 	// Theme name
610 	JString str;
611 	str = theme.Name();
612 	if (0 != str.Save(file))
613 	{
614 		fprintf(stderr, "Error saving the theme name.\n");
615 
616 		return 2;
617 	}
618 
619 	if (0 != map.Save(file))
620 	{
621 		fprintf(stderr, "Error saving the level map.\n");
622 
623 		return 2;
624 	}
625 
626 	// Main character
627 	x = (s32)character.X();
628 	y = (s32)character.Y();
629 	character.Pos(x - (s32)map.X(), y - (s32)map.Y());
630 
631 	if (0 != character.Save(file))
632 	{
633 		fprintf(stderr, "Error saving main character.\n");
634 
635 		return 1;
636 	}
637 
638 	character.Pos(x, y);
639 
640 	// Stores the objects, enemies, ropes relative to the map top-left corner
641 	if (0 == file.WriteLE32(&numEnemies))
642 	{
643 		fprintf(stderr, "Error writing the number of enemies.\n");
644 
645 		return 1;
646 	}
647 
648 	for (s32 i = 0; i < numEnemies; ++i)
649 	{
650 		x = (s32)enemies[i]->X();
651 		y = (s32)enemies[i]->Y();
652 		enemies[i]->Pos(x - (s32)map.X(), y - (s32)map.Y());
653 
654 		if (0 != enemies[i]->Save(file))
655 		{
656 			fprintf(stderr, "Error saving the enemies.\n");
657 			return 2;
658 		}
659 
660 		enemies[i]->Pos(x, y);
661 	}
662 
663 	if (0 == file.WriteLE32(&numObjects))
664 	{
665 		fprintf(stderr, "Error writing the number of objects.\n");
666 
667 		return 1;
668 	}
669 
670 	for (s32 i = 0; i < numObjects; ++i)
671 	{
672 		x = (s32)objects[i]->X();
673 		y = (s32)objects[i]->Y();
674 		objects[i]->Pos(x - (s32)map.X(), y - (s32)map.Y());
675 
676 		if (0 != objects[i]->Save(file))
677 		{
678 			fprintf(stderr, "Error saving the objects.\n");
679 			return 2;
680 		}
681 
682 		objects[i]->Pos(x, y);
683 	}
684 
685 	if (0 == file.WriteLE32(&numRopes))
686 	{
687 		fprintf(stderr, "Error writing the number of ropes.\n");
688 
689 		return 1;
690 	}
691 
692 	for (s32 i = 0; i < numRopes; ++i)
693 	{
694 		x = (s32)ropes[i]->X();
695 		y = (s32)ropes[i]->Y();
696 		ropes[i]->Pos(x - (s32)map.X(), y - (s32)map.Y());
697 
698 		if (0 != ropes[i]->Save(file))
699 		{
700 			fprintf(stderr, "Error saving the ropes.\n");
701 			return 2;
702 		}
703 
704 		ropes[i]->Pos(x, y);
705 	}
706 
707 	return 0;
708 }
709 
Pos(float xPos,float yPos)710 void HCLevel::Pos(float xPos, float yPos)
711 {
712 	float xMap, yMap, xBg, yBg, dx, dy;
713 	float xMapOrg = map.X();
714 	float yMapOrg = map.Y();
715 
716 	pos.x = xPos;
717 	pos.y = yPos;
718 
719 	if (map.Width() > bg.Width())
720 	{
721 		xMap = xPos;
722 		xBg = xPos + ((map.Width() - bg.Width())/2);
723 	}
724 	else
725 	{
726 		xBg = xPos;
727 		xMap = xPos + ((bg.Width() - map.Width())/2);
728 	}
729 
730 	if (map.Height() > bg.Height())
731 	{
732 		yMap = yPos;
733 		yBg = yPos + ((map.Height() - bg.Height())/2);
734 	}
735 	else
736 	{
737 		yBg = yPos;
738 		yMap = yPos + ((bg.Height() - map.Height())/2);
739 	}
740 
741 	map.Pos(xMap, yMap);
742 	bg.Pos(xBg, yBg);
743 
744 	dx = xMap - xMapOrg;
745 	dy = yMap - yMapOrg;
746 
747 	// Now move the objects
748 	float t1, t2;
749 	for (s32 i = 0; i < numObjects; ++i)
750 	{
751 		t1 = objects[i]->X();
752 		t2 = objects[i]->Y();
753 
754 		objects[i]->Pos(objects[i]->X() + (s32)dx,
755 										objects[i]->Y() + (s32)dy);
756 	}
757 
758 	// Now move the ropes
759 	for (s32 i = 0; i < numRopes; ++i)
760 	{
761 		ropes[i]->Pos(ropes[i]->X() + (s32)dx,
762 									ropes[i]->Y() + (s32)dy);
763 	}
764 
765 	// Now move the enemies
766 	for (s32 i = 0; i < numEnemies; ++i)
767 	{
768 		enemies[i]->Pos(enemies[i]->X() + (s32)dx,
769 										enemies[i]->Y() + (s32)dy);
770 	}
771 
772 	// Move the level exit
773 	levelExit.Pos(levelExit.X() + (s32)dx, (s32)levelExit.Y() + (s32)dy);
774 
775 	// And finally the main character
776 	character.Pos(character.X() + (s32)dx,
777 								character.Y() + (s32)dy);
778 }
779 
CheckObjects()780 void HCLevel::CheckObjects()
781 {
782 	JVector vc, vo;
783 	vc = character.Pos();
784 
785 	// Y Offset of the center of the character
786 	JImage * img = (JImage *)character.CurSprite()->Frame(character.CurSprite()->CurFrame());
787 	vc.y += img->Pos().y + (img->Width()/2);
788 
789 	for (s32 i = 0; i < numObjects; ++i)
790 	{
791 		if (objects[i]->State() == HCOBJECTSTATE_NORMAL)
792 		{
793 			vo = objects[i]->Pos();
794 
795 			// Y Offset of the center of the character
796 			img = (JImage *)objects[i]->Normal().Frame(objects[i]->Normal().CurFrame());
797 			vo.y += img->Pos().y + (img->Width()/2);
798 
799 			if (vo == vc)
800 			{
801 				// 0 length case
802 				ObjectAcquired(objects[i]);
803 
804 				if (JApp::App()->SoundEnabled())
805 				{
806 					soundObjectAcquired.Play();
807 				}
808 			}
809 			else
810 			{
811 				if ((vo - vc).Length() < (0.9f * character.CurSprite()->MaxW()))
812 				{
813 					ObjectAcquired(objects[i]);
814 
815 					if (JApp::App()->SoundEnabled())
816 					{
817 						soundObjectAcquired.Play();
818 					}
819 				}
820 			}
821 		}
822 	}
823 }
824 
ObjectAcquired(HCObject * object)825 void HCLevel::ObjectAcquired(HCObject *object)
826 {
827 	if (object)
828 	{
829 		--remainingObjects;
830 
831 		object->Acquire();
832 	}
833 }
834 
Collide(HCCharacter * c1,HCCharacter * c2)835 bool HCLevel::Collide(HCCharacter *c1, HCCharacter *c2)
836 {
837 	float px1, py1, px2, py2, pw, ph;
838 	float ex1, ey1, ex2, ey2, ew, eh;
839 	JImage *cfp, *cfe;
840 	float difficulty = 0.75;
841 	cfp = (JImage *)c1->CurSprite()->Frame(c1->CurSprite()->CurFrame());
842 	cfe = (JImage *)c2->CurSprite()->Frame(c2->CurSprite()->CurFrame());
843 
844 	pw = cfp->Width() * difficulty;
845 	ph = cfp->Height() * difficulty;
846 	px1 = c1->CurSprite()->Pos().x + cfp->X() + (cfp->Width()/2) - (pw/2);
847 	px2 = px1 + pw;
848 	py1 = c1->CurSprite()->Pos().y + cfp->Y() + (cfp->Height()/2) - (ph/2);
849 	py2 = py1 + ph;
850 
851 	ew = cfe->Width() * difficulty;
852 	eh = cfe->Height() * difficulty;
853 	ex1 = c2->CurSprite()->Pos().x + cfe->X() + (cfe->Width()/2) - (ew/2);
854 	ex2 = ex1 + ew;
855 	ey1 = c2->CurSprite()->Pos().y + cfe->Y() + (cfe->Height()/2) - (eh/2);
856 	ey2 = ey1 + eh;
857 
858 // 	SDL_Rect rcp = {px1, py1, pw, ph};
859 // 	SDL_Rect rce = {ex1, ey1, ew, eh};
860 // 	SDL_FillRect(SDL_GetVideoSurface(), &rce, 0xffffffff);
861 // 	SDL_FillRect(SDL_GetVideoSurface(), &rcp, 0xffffffff);
862 // 	SDL_Flip(SDL_GetVideoSurface());
863 
864 	// Approximate collisions using half the width and height, centered of the characters
865 	if ((px1 > ex1 && px1 < ex2) ||
866 			(px2 > ex1 && px2 < ex2) ||
867 			(px1 < ex1 && px2 > ex2) ||
868 			(px1 > ex1 && px2 < ex2))
869 	{
870 		if ((py1 > ey1 && py1 < ey2) ||
871 				(py2 > ey1 && py2 < ey2) ||
872 				(py1 < ey1 && py2 > ey2) ||
873 				(py1 > ey1 && py2 < ey2))
874 		{
875 			// Collide!
876 			return true;
877 		}
878 	}
879 
880 	return false;
881 }
882 
Destroy()883 void HCLevel::Destroy()
884 {
885 	JDELETE_POINTER_ARRAY(enemies, numEnemies);
886 	numEnemies = 0;
887 	JDELETE_POINTER_ARRAY(ropes, numRopes);
888 	numRopes = 0;
889 	JDELETE_POINTER_ARRAY(objects, numObjects);
890 	numObjects = 0;
891 	map.Destroy();
892 	theme.Destroy();
893 	levelExit.Destroy();
894 	bg.Destroy();
895 	character.Destroy();
896 	narrative = 0;
897 	levelTimer.Destroy();
898 	soundObjectAcquired.Destroy();
899 	musicEndLevel.Destroy();
900 }
901