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