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 /** Level editor for Holotz's Castle.
24 * @file HCed.cpp
25 * @author Juan Carlos Seijo P�rez
26 * @date 30/05/2004
27 * @version 0.0.1 - 30/05/2004 - First version
28 * @version 0.0.2 - 30/05/2004 - Install support, Miriam Ruiz (Debian package). Load/Create story support.
29 */
30
31 #include <HCed.h>
32
33 #ifndef _WIN32
34 #include <unistd.h>
35 #endif
36
37 #ifndef HC_DATA_DIR
38 #define HC_DATA_DIR "res/"
39 #endif
40
41 #ifndef HCED_DATA_DIR
42 #define HCED_DATA_DIR "HCedHome/res/"
43 #endif
44
45 /** This application.
46 */
47 HCed *theApp;
48 HCPreferences prefs;
49
PrintUsage(char * program)50 void HCed::PrintUsage(char *program)
51 {
52 fprintf(stderr, "HCed v1.3. (C) Juan Carlos Seijo P�rez - 2004.\n\n");
53 fprintf(stderr, "Usage: %s [-t themeName] [story name] [levelToLoad]", program);
54 fprintf(stderr, " [-h] [-r numRows] [-c numColumns]");
55 fprintf(stderr, " [-f]ullscreen [-w]indowed [--fps nnn] [-mWxHxBPP]\n");
56 fprintf(stderr, "\n");
57 exit(0);
58 }
59
OnKeyUp(SDL_keysym key)60 void HCed::OnKeyUp(SDL_keysym key)
61 {
62 if (theApp->state == HCEDSTATE_OPENSTORY)
63 {
64 if (key.sym == SDLK_ESCAPE)
65 {
66 // Ends opening/creating the a story
67 theApp->OnFloor(0);
68 theApp->inputNewStory = 0;
69 }
70
71 if (HCED_INPUT_STORY == theApp->inputNewStory)
72 {
73 bool upd = false;
74
75 if ((key.sym >= SDLK_0 && key.sym <= SDLK_9) ||
76 (key.sym >= SDLK_a && key.sym <= SDLK_z) ||
77 key.sym == SDLK_MINUS)
78 {
79 if (key.sym == SDLK_MINUS && (theApp->KeyMods() & KMOD_SHIFT))
80 {
81 theApp->strNewStoryName += '_';
82 }
83 else
84 {
85 if ((theApp->KeyMods() & KMOD_SHIFT))
86 {
87 theApp->strNewStoryName += toupper(key.sym);
88 }
89 else
90 {
91 theApp->strNewStoryName += key.sym;
92 }
93 }
94
95 upd = true;
96 }
97 else
98 if (key.sym == SDLK_DELETE || key.sym == SDLK_BACKSPACE)
99 {
100 // Deletes last character
101 if (theApp->strNewStoryName.Length() > 0)
102 {
103 JString str(theApp->strNewStoryName, 0, theApp->strNewStoryName.Length() - 1);
104 theApp->strNewStoryName = str;
105 upd = true;
106 }
107 }
108 else
109 if (key.sym == SDLK_RETURN || key.sym == SDLK_KP_ENTER)
110 {
111 theApp->inputNewStory = HCED_INPUT_THEME;
112
113 // Updates the input string
114 JDELETE(theApp->imgNewStory);
115 SDL_Color fg = {0xff, 0xcc, 0x00, 0x00};
116 SDL_Color bg = {0x00, 0x00, 0x00, 0x00};
117 theApp->imgNewStory = theApp->fontLarge.RenderTextShaded("Select a theme", fg, bg);
118 }
119
120 if (upd)
121 {
122 // Updates the input string
123 JDELETE(theApp->imgNewStory);
124 SDL_Color fg = {0xff, 0xcc, 0x00, 0x00};
125 SDL_Color bg = {0x00, 0x00, 0x00, 0x00};
126 theApp->imgNewStory = theApp->fontLarge.RenderTextShaded((const char *)(JString("Name: ") + theApp->strNewStoryName), fg, bg);
127 }
128 }
129 else
130 if (HCED_INPUT_THEME == theApp->inputNewStory)
131 {
132 theApp->menuTheme->TrackKeyboard(key);
133 }
134 else
135 {
136 theApp->menuOpenStory->TrackKeyboard(key);
137 }
138
139 return;
140 }
141
142 switch (key.sym)
143 {
144 case SDLK_1:
145 OnFloor(0);
146 break;
147
148 case SDLK_2:
149 OnContFloor(0);
150 break;
151
152 case SDLK_3:
153 OnLadder(0);
154 break;
155
156 case SDLK_4:
157 OnBar(0);
158 break;
159
160 case SDLK_5:
161 OnBreak(0);
162 break;
163
164 case SDLK_6:
165 OnObject(0);
166 break;
167
168 case SDLK_7:
169 OnRope(0);
170 break;
171
172 case SDLK_8:
173 OnStart(0);
174 break;
175
176 case SDLK_9:
177 OnExit(0);
178 break;
179
180 case SDLK_0:
181 OnEnemy(0);
182 break;
183
184 case SDLK_s:
185 OnSave(0);
186 break;
187
188 case SDLK_PAGEUP:
189 switch (theApp->PrevLevel())
190 {
191 case -1:
192 fprintf(stderr, "HCed: Error going to previous level.\n");
193 break;
194
195 case 1:
196 fprintf(stderr, "HCed: This is the first level.\n");
197 break;
198
199 case 0:
200 default:
201 break;
202 }
203 break;
204
205 case SDLK_PAGEDOWN:
206 if (theApp->NextLevel() < 0)
207 {
208 fprintf(stderr, "HCed: Error going to next level.\n");
209 }
210 break;
211
212 case SDLK_q:
213 if (theApp->KeyMods() & KMOD_CTRL)
214 {
215 theApp->Exit();
216 }
217 break;
218
219 default:
220 break;
221 }
222 }
223
OnKeyDown(SDL_keysym key)224 void HCed::OnKeyDown(SDL_keysym key)
225 {
226 switch (key.sym)
227 {
228 // Scrolls down the map
229 case SDLK_UP:
230 {
231 HCMap *map = &theApp->level.Map();
232
233 if (map->Y() < HCED_MARGIN)
234 {
235 theApp->level.Pos((s32)map->X(),
236 (s32)map->Y() + map->CellHeight());
237 }
238 else
239 if (map->Y() > HCED_MARGIN && map->Height() > float(theApp->Height() - HCED_MARGIN))
240 {
241 theApp->level.Pos((s32)map->X(), HCED_MARGIN);
242 }
243 }
244 break;
245
246 // Scrolls up the map
247 case SDLK_DOWN:
248 {
249 HCMap *map = &theApp->level.Map();
250
251 if (map->Y() + map->Height() > theApp->Height())
252 {
253 theApp->level.Pos((s32)map->X(),
254 (s32)map->Y() - map->CellHeight());
255 }
256 else
257 if (map->Y() + map->Height() > theApp->Height() &&
258 map->Height() > theApp->Height() - HCED_MARGIN)
259 {
260 theApp->level.Pos((s32)map->X(), HCED_MARGIN);
261 }
262 }
263 break;
264
265 // Scrolls right the map
266 case SDLK_LEFT:
267 {
268 HCMap *map = &theApp->level.Map();
269
270 if (map->X() < HCED_MARGIN)
271 {
272 theApp->level.Pos((s32)map->X() + map->CellWidth(),
273 (s32)map->Y());
274 }
275 else
276 if (map->X() > HCED_MARGIN && map->Width() > float(theApp->Width() - HCED_MARGIN))
277 {
278 theApp->level.Pos(HCED_MARGIN, (s32)map->Y());
279 }
280 }
281 break;
282
283 // Scrolls left the map
284 case SDLK_RIGHT:
285 {
286 HCMap *map = &theApp->level.Map();
287
288 if (map->X() + map->Width() > theApp->Width())
289 {
290 theApp->level.Pos((s32)map->X() - map->CellWidth(),
291 (s32)map->Y());
292 }
293 else
294 if (map->X() + map->Width() > theApp->Width() &&
295 map->Width() > theApp->Width() - HCED_MARGIN)
296 {
297 theApp->level.Pos(HCED_MARGIN, (s32)map->Y());
298 }
299 }
300 break;
301
302 case SDLK_KP_PLUS:
303 {
304 // Time to complete level up
305 theApp->level.maxTime += 1;
306 if (theApp->level.maxTime > 0xffff)
307 {
308 theApp->level.maxTime = 0xffff;
309 }
310 theApp->level.levelTimer.Init(theApp->level.maxTime, &theApp->fontNormal);
311 }
312 break;
313
314 case SDLK_KP_MINUS:
315 {
316 // Time to complete level down
317 theApp->level.maxTime -= 1;
318 if (theApp->level.maxTime == 0)
319 {
320 theApp->level.maxTime = 1;
321 }
322
323 theApp->level.levelTimer.Init(theApp->level.maxTime, &theApp->fontNormal);
324 }
325 break;
326
327 case SDLK_g:
328 {
329 if ((SDL_GetModState() & KMOD_SHIFT))
330 {
331 // Map gravity down
332 theApp->level.map.Gravity(theApp->level.map.Gravity() - 0.1f);
333 }
334 else
335 {
336 // Map gravity up
337 theApp->level.map.Gravity(theApp->level.map.Gravity() + 0.1f);
338 }
339
340 OnGravityChange();
341 }
342 break;
343
344 case SDLK_x:
345 {
346 if ((SDL_GetModState() & KMOD_SHIFT))
347 {
348 // Main character's Vx down
349 theApp->level.character.MaxVeloccity().x -= 0.1f;
350 }
351 else
352 {
353 // Main character's Vx up
354 theApp->level.character.MaxVeloccity().x += 0.1f;
355 }
356
357 OnCharVxChange();
358 }
359 break;
360
361 case SDLK_y:
362 {
363 if ((SDL_GetModState() & KMOD_SHIFT))
364 {
365 // Main character's Vy down
366 theApp->level.character.MaxVeloccity().y -= 0.1f;
367 }
368 else
369 {
370 // Main character's Vy up
371 theApp->level.character.MaxVeloccity().y += 0.1f;
372 }
373
374 OnCharVyChange();
375 }
376 break;
377
378 case SDLK_j:
379 {
380 if ((SDL_GetModState() & KMOD_SHIFT))
381 {
382 // Main character's max jump rows down
383 theApp->level.character.MaxJumpRows(theApp->level.character.MaxJumpRows() - 1);
384 }
385 else
386 {
387 // Main character's max jump rows up
388 theApp->level.character.MaxJumpRows(theApp->level.character.MaxJumpRows() + 1);
389 }
390
391 OnCharJumpRowsChange();
392 }
393 break;
394
395 case SDLK_r:
396 {
397 if ((SDL_GetModState() & KMOD_SHIFT))
398 {
399 if ((SDL_GetModState() & KMOD_CTRL))
400 {
401 // Decrease number of rows from the top
402 theApp->level.map.Resize(theApp->level.map.Rows() - 1, theApp->level.map.Cols(), true, false);
403 }
404 else
405 {
406 // Decrease number of rows from the bottom
407 theApp->level.map.Resize(theApp->level.map.Rows() - 1, theApp->level.map.Cols());
408 }
409 }
410 else
411 {
412 if ((SDL_GetModState() & KMOD_CTRL))
413 {
414 // Increase number of rows from the top
415 theApp->level.map.Resize(theApp->level.map.Rows() + 1, theApp->level.map.Cols(), true, false);
416 }
417 else
418 {
419 // Increase number of rows from the bottom
420 theApp->level.map.Resize(theApp->level.map.Rows() + 1, theApp->level.map.Cols());
421 }
422 }
423
424 OnMapSizeChange();
425 }
426 break;
427
428 case SDLK_c:
429 {
430 if ((SDL_GetModState() & KMOD_SHIFT))
431 {
432 if ((SDL_GetModState() & KMOD_CTRL))
433 {
434 // Decrease number of columns from the left
435 theApp->level.map.Resize(theApp->level.map.Rows(), theApp->level.map.Cols() - 1, false);
436 }
437 else
438 {
439 // Decrease number of columns from the right
440 theApp->level.map.Resize(theApp->level.map.Rows(), theApp->level.map.Cols() - 1);
441 }
442 }
443 else
444 {
445 if ((SDL_GetModState() & KMOD_CTRL))
446 {
447 // Increase number of columns from the left
448 theApp->level.map.Resize(theApp->level.map.Rows(), theApp->level.map.Cols() + 1, false);
449 }
450 else
451 {
452 // Increase number of columns from the right
453 theApp->level.map.Resize(theApp->level.map.Rows(), theApp->level.map.Cols() + 1);
454 }
455 }
456
457 OnMapSizeChange();
458 }
459 break;
460
461 default:
462 break;
463 }
464 }
465
OnMouseUp(s32 bt,s32 x,s32 y)466 void HCed::OnMouseUp(s32 bt, s32 x, s32 y)
467 {
468 switch (theApp->state)
469 {
470 case HCEDSTATE_FLOOR:
471 theApp->menuFloorSubtype->TrackMouse(bt, x, y);
472 break;
473 case HCEDSTATE_CONTFLOOR:
474 theApp->menuContFloorSubtype->TrackMouse(bt, x, y);
475 break;
476 case HCEDSTATE_LADDER:
477 theApp->menuLadderSubtype->TrackMouse(bt, x, y);
478 break;
479 case HCEDSTATE_BAR:
480 theApp->menuBarSubtype->TrackMouse(bt, x, y);
481 break;
482 case HCEDSTATE_BREAK:
483 theApp->menuBreakSubtype->TrackMouse(bt, x, y);
484 break;
485 case HCEDSTATE_OBJECT:
486 theApp->curObject = 0;
487 theApp->menuObjectSubtype->TrackMouse(bt, x, y);
488 break;
489 case HCEDSTATE_ROPE:
490 theApp->curRope = 0;
491 theApp->menuRopePeriod->TrackMouse(bt, x, y);
492 theApp->menuRopeSubtype->TrackMouse(bt, x, y);
493 break;
494 case HCEDSTATE_START:
495 theApp->menuMainSubtype->TrackMouse(bt, x, y);
496 break;
497 case HCEDSTATE_EXIT:
498 break;
499 case HCEDSTATE_ENEMY:
500 theApp->curEnemy = 0;
501 theApp->menuEnemyType->TrackMouse(bt, x, y);
502 theApp->menuEnemySubtype[theApp->enemyType]->TrackMouse(bt, x, y);
503 theApp->menuEnemyParam1->TrackMouse(bt, x, y);
504 theApp->menuEnemyParam2->TrackMouse(bt, x, y);
505 break;
506 case HCEDSTATE_SAVE:
507 break;
508
509 case HCEDSTATE_OPENSTORY:
510 switch (theApp->inputNewStory)
511 {
512 case 0:
513 theApp->menuOpenStory->TrackMouse(bt, x, y);
514 break;
515
516 case HCED_INPUT_THEME:
517 theApp->menuTheme->TrackMouse(bt, x, y);
518 break;
519
520 default:
521 break;
522 }
523 break;
524
525 case HCEDSTATE_APPEXIT:
526 break;
527 default:
528 break;
529 }
530
531 theApp->menuMain->TrackMouse(bt, x, y);
532 }
533
OnMouseDown(s32 bt,s32 x,s32 y)534 void HCed::OnMouseDown(s32 bt, s32 x, s32 y)
535 {
536 switch (theApp->state)
537 {
538 case HCEDSTATE_OBJECT:
539 theApp->curObject = 0;
540 HCObject *obj;
541
542 // Checks for objects bellow the mouse cursor
543 for (s32 i = 0; i < theApp->level.numObjects; ++i)
544 {
545 obj = theApp->level.objects[i];
546
547 if (theApp->MouseX() > obj->X() - obj->Normal().MaxW()/2 &&
548 theApp->MouseX() < obj->X() + obj->Normal().MaxW()/2 &&
549 theApp->MouseY() > obj->Y() - obj->Normal().MaxH() &&
550 theApp->MouseY() < obj->Y())
551 {
552 theApp->curObject = theApp->level.objects[i];
553 }
554 }
555 break;
556
557 case HCEDSTATE_ROPE:
558 theApp->curRope = 0;
559
560 // Checks for ropes bellow the mouse cursor
561 for (s32 i = 0; i < theApp->level.numRopes; ++i)
562 {
563 if (theApp->MouseX() > (theApp->level.ropes[i])->X() - theApp->level.Map().CellWidth()/2 &&
564 theApp->MouseX() < (theApp->level.ropes[i])->X() + theApp->level.Map().CellWidth()/2 &&
565 theApp->MouseY() > (theApp->level.ropes[i])->Y() &&
566 theApp->MouseY() < (theApp->level.ropes[i])->Y() + theApp->level.Map().CellHeight())
567 {
568 theApp->curRope = theApp->level.ropes[i];
569 }
570 }
571
572 if (theApp->curRope)
573 {
574 // Sets the period
575 if (theApp->ropePeriod != theApp->curRope->Period())
576 {
577 theApp->curRope->Init(theApp->ropePeriod,
578 theApp->curRope->Amplitude(),
579 theApp->curRope->Length(),
580 theApp->level.Theme());
581 }
582 }
583 break;
584
585 case HCEDSTATE_ENEMY:
586 theApp->curEnemy = 0;
587
588 // Checks for enemies bellow the mouse cursor
589 for (s32 i = 0; i < theApp->level.numEnemies; ++i)
590 {
591 if (theApp->MouseX() > (theApp->level.enemies[i])->X() - (theApp->level.enemies[i]->states[HCCS_STOP].MaxW()/2) &&
592 theApp->MouseX() < (theApp->level.enemies[i])->X() + (theApp->level.enemies[i]->states[HCCS_STOP].MaxW()/2) &&
593 theApp->MouseY() > (theApp->level.enemies[i])->Y() - (theApp->level.enemies[i]->states[HCCS_STOP].MaxH()/2) &&
594 theApp->MouseY() < (theApp->level.enemies[i])->Y())
595 {
596 theApp->curEnemy = theApp->level.enemies[i];
597 }
598 }
599 break;
600
601 default:
602 break;
603 }
604 }
605
ParseArg(char * args[],int argc)606 int HCed::ParseArg(char *args[], int argc)
607 {
608 printf("Parsing %d args %s\n", argc, args[0]);
609 if (args[0][0] != '-')
610 {
611 // Argument without score, treat it as the story name
612 storyName = args[0];
613
614 if (argc < 2)
615 {
616 levelNumber = 1;
617
618 return 0; // no aditional arguments needed
619 }
620
621 levelNumber = atoi(args[1]);
622
623 if (levelNumber <= 0)
624 {
625 levelNumber = 1;
626 fprintf(stderr, "HCed: The level number must be greater than 0, defaulting to 1.\n");
627 }
628 return 1; // 1 aditional argument used
629 }
630
631 switch (args[0][1])
632 {
633 // '-t themeName' option
634 case 't':
635 if (argc<2)
636 return -2;
637 themeName = args[1];
638 return 1;
639
640 // '-r numRows' option
641 case 'r':
642 if (argc<2)
643 return -2;
644 defRows = atoi(args[1]);
645
646 if (defRows <= 0)
647 {
648 defRows = 10;
649 }
650 return 1;
651
652 // '-c numCols' option
653 case 'c':
654 if (argc<2)
655 return -2;
656 defCols = atoi(args[1]);
657
658 if (defCols <= 0)
659 {
660 defCols = 10;
661 }
662 return 1;
663 }
664
665 return JApp::ParseArg(args, argc);
666 }
667
ParseArgs(s32 argc,char ** argv)668 void HCed::ParseArgs(s32 argc, char **argv)
669 {
670 JApp::ParseArgs(argc, argv);
671
672 char str[4096];
673
674 #ifndef _WIN32
675 char *home;
676 home = getenv("HOME");
677 if (home != NULL)
678 {
679 snprintf(str, sizeof(str), "%s/.holotz-castle", home);
680 mkdir(str, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
681 snprintf(str, sizeof(str), "%s/.holotz-castle/stories", home);
682 mkdir(str, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
683 snprintf(str, sizeof(str), "%s/.holotz-castle/stories/%s", home, storyName.Str());
684 mkdir(str, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
685 }
686 #endif
687
688 if (!HCUtil::FindFile("stories"))
689 {
690 fprintf(stderr, "Directory 'stories' not found. Check manual.\n");
691 exit(-1);
692 }
693 else
694 {
695 // Assigns this directory as the default working directory
696 storyDir = HCUtil::File();
697 storyDir += "/";
698 }
699
700 if (levelNumber == -1)
701 {
702 // Selects the next file name from the requested story
703 levelNumber = 1;
704
705 snprintf(str, sizeof(str), "%s%s/level%03d.hlv", storyDir.Str(), storyName.Str(), levelNumber);
706 while (JFile::Exists(str))
707 {
708 snprintf(str, sizeof(str), "%s%s/level%03d.hlv", storyDir.Str(), storyName.Str(), ++levelNumber);
709 }
710
711 filename = str;
712 }
713 else
714 {
715 // Sets the name of the requested story and level number.
716 snprintf(str, sizeof(str), "%s%s/level%03d.hlv", storyDir.Str(), storyName.Str(), levelNumber);
717 filename = str;
718 }
719
720 OnFilenameChange();
721 }
722
HCed()723 HCed::HCed() : JApp("HCed v1.1", HCED_REFAPPWIDTH, HCED_REFAPPHEIGHT, false)
724 {
725 state = HCEDSTATE_FLOOR;
726 SetOnMouseUp(&OnMouseUp);
727 SetOnMouseDown(&OnMouseDown);
728 SetOnKeyUp(&OnKeyUp);
729 SetOnKeyDown(&OnKeyDown);
730 doInput = true;
731 curObject = 0;
732 curRope = 0;
733 curEnemy = 0;
734 defRows = defCols = 10;
735 enemyParam1 = 1;
736 enemyParam2 = 1;
737 imgNewStory = 0;
738 imgGravity = 0;
739 imgCharVx = 0;
740 imgCharVy = 0;
741 imgCharJumpRows = 0;
742 imgMapSize = 0;
743 levelNumber = -1;
744 menuMain = 0;
745 menuEnemyType = 0;
746 menuEnemyParam1 = 0;
747 menuEnemyParam2 = 0;
748 menuOpenStory = 0;
749 menuTheme = 0;
750 menuRopePeriod = 0;
751 menuMainSubtype = 0;
752 menuBreakSubtype = 0;
753 menuObjectSubtype = 0;
754 menuFloorSubtype = 0;
755 menuContFloorSubtype = 0;
756 menuBarSubtype = 0;
757 menuLadderSubtype = 0;
758 menuRopeSubtype = 0;
759 appBackColor = 0;
760
761 for (s32 i = 0; i < HCENEMYTYPE_COUNT; ++i)
762 {
763 menuEnemySubtype[i] = 0;
764 }
765
766 storyName = "unknown";
767 themeName = "default";
768 inputNewStory = 0;
769 }
770
LoadLevel()771 s32 HCed::LoadLevel()
772 {
773 // If the level exists, loads it
774 bool ok = false;
775
776 // Sets the timer font
777 level.SetTimerFont(&fontNormal);
778
779 if (JFile::Exists(filename))
780 {
781 JRW f;
782 if (f.Create(filename, "rb"))
783 {
784 if (0 == level.Load(f, filename))
785 {
786 if (InitLoadedLevel())
787 {
788 ok = true;
789 }
790 else
791 {
792 fprintf(stderr, "Could not init the loaded level.\n");
793 }
794 }
795 else
796 {
797 fprintf(stderr, "Could not load level from %s.\n", filename.Str());
798 }
799 }
800 else
801 {
802 fprintf(stderr, "Could not open file %s for reading.\n", filename.Str());
803 }
804 }
805 else
806 {
807 fprintf(stderr, "HCed: File %s doesn't exist, creating a new level.\n", filename.Str());
808 }
809
810 // If an error occurred or the file didn't exist, tries to load the default level/theme
811 if (!ok)
812 {
813 if (!InitDefaultLevel())
814 {
815 fprintf(stderr, "Could not init default level.\n");
816 return -1;
817 }
818 }
819
820 // Places the level
821 level.Pos(HCED_MARGIN, HCED_MARGIN);
822
823 // Prepares the current gravity and main character's parameters to be shown
824 OnGravityChange();
825 OnCharVxChange();
826 OnCharVyChange();
827 OnCharJumpRowsChange();
828 OnMapSizeChange();
829
830 // Types and theme subtypes
831 enemyType = HCENEMYTYPE_BALL;
832 enemySubtype = 0;
833 floorSubtype = 0;
834 contFloorSubtype = 0;
835 breakSubtype = 0;
836 barSubtype = 0;
837 ladderSubtype = 0;
838 objectSubtype = 0;
839 ropeSubtype = 0;
840 ropePeriod = 0;
841
842 // Re-initializes level/theme dependant menus
843 return InitLevelMenus();
844 }
845
InitLevelMenus()846 s32 HCed::InitLevelMenus()
847 {
848 imgMouse = 0;
849
850 // Inits the main character subtype menu
851 if (!InitMainSubtypeMenu())
852 {
853 fprintf(stderr, "Failed to init main character's subtype menu.\n");
854
855 return -1;
856 }
857
858 // Inits the break subtypes menu
859 if (!InitBreakSubtypeMenu())
860 {
861 fprintf(stderr, "Failed to init break subtypes menu.\n");
862
863 return -1;
864 }
865
866 // Inits the object subtypes menu
867 if (!InitObjectSubtypeMenu())
868 {
869 fprintf(stderr, "Failed to init object subtypes menu.\n");
870
871 return -1;
872 }
873
874 // Inits the enemy representations menu
875 if (!InitEnemySubtypeMenu())
876 {
877 fprintf(stderr, "Failed to init enemy subtypes menu.\n");
878
879 return -1;
880 }
881
882 // Inits the floor subtypes menu
883 if (!InitFloorSubtypeMenu())
884 {
885 fprintf(stderr, "Failed to init floor subtypes menu.\n");
886
887 return false;
888 }
889
890
891 // Inits the continuous floor subtypes menu
892 if (!InitContFloorSubtypeMenu())
893 {
894 fprintf(stderr, "Failed to init floor subtypes menu.\n");
895
896 return false;
897 }
898
899 // Inits the bar subtypes menu
900 if (!InitBarSubtypeMenu())
901 {
902 fprintf(stderr, "Failed to init bar subtypes menu.\n");
903
904 return false;
905 }
906
907 // Inits the ladder subtypes menu
908 if (!InitLadderSubtypeMenu())
909 {
910 fprintf(stderr, "Failed to init ladder subtypes menu.\n");
911
912 return false;
913 }
914
915 // Inits the rope subtypes menu
916 if (!InitRopeSubtypeMenu())
917 {
918 fprintf(stderr, "Failed to init rope subtypes menu.\n");
919
920 return false;
921 }
922
923 // Inits the open story menu
924 if (!InitOpenStoryMenu())
925 {
926 fprintf(stderr, "Failed to init stories menu.\n");
927
928 return false;
929 }
930
931 // Inits the theme subtypes menu
932 if (!InitThemeMenu())
933 {
934 fprintf(stderr, "Failed to init stories menu.\n");
935
936 return false;
937 }
938
939 return 0;
940 }
941
NextLevel()942 s32 HCed::NextLevel()
943 {
944 char str[256];
945
946 // Sets the name of the story with the next level number.
947 snprintf(str, sizeof(str), "%s%s/level%03d.hlv", storyDir.Str(), storyName.Str(), ++levelNumber);
948 filename = str;
949
950 if (0 == LoadLevel())
951 {
952 themeName = level.Theme().Name();
953 OnFilenameChange();
954
955 if (0 == InitLevelMenus())
956 {
957 OnFloor(this);
958
959 return 0;
960 }
961 }
962
963 return -1;
964 }
965
PrevLevel()966 s32 HCed::PrevLevel()
967 {
968 if (levelNumber > 1)
969 {
970 char str[256];
971
972 // Sets the name of the story with the next level number.
973 snprintf(str, sizeof(str), "%s%s/level%03d.hlv", storyDir.Str(), storyName.Str(), --levelNumber);
974 filename = str;
975
976 if (0 == LoadLevel())
977 {
978 OnFilenameChange();
979
980 if (0 == InitLevelMenus())
981 {
982 OnFloor(this);
983
984 return 0;
985 }
986 }
987
988 return -1;
989 }
990
991 // No more levels before
992 return 1;
993 }
994
NewLevel()995 s32 HCed::NewLevel()
996 {
997 if (0 == LoadLevel())
998 {
999 OnFilenameChange();
1000
1001 if (0 == InitLevelMenus())
1002 {
1003 OnFloor(this);
1004
1005 return 0;
1006 }
1007 }
1008
1009 return -1;
1010 }
1011
Init(int argc,char ** argv)1012 bool HCed::Init(int argc, char **argv)
1013 {
1014 // Parse standard JApp args and HCed args
1015 ParseArgs(argc, argv);
1016
1017 // Inits base system
1018 if (!JApp::Init())
1019 {
1020 fprintf(stderr, "Failed to init base system. Check manual.\n");
1021
1022 return false;
1023 }
1024
1025 // Loads preferences
1026 // if (0 != prefs.Load())
1027 // {
1028 // fprintf(stderr, "Error loading preferences. Check manual.\n");
1029 // }
1030
1031 // In edition mode the time must be that of the highest level of difficulty
1032 prefs.Difficulty(HCPREFERENCES_HARD);
1033
1034 // Initializes fonts
1035 if (!JFile::Exists(HC_DATA_DIR "font/font.ttf"))
1036 {
1037 fprintf(stderr,
1038 "Could not find data directory.\n\n"
1039 "Posible solutions are:\n"
1040 " - Open folder JLib-1.3.1/Games/HolotzCastle and double.\n"
1041 " click 'holotz-castle' application icon.\n"
1042 " - Maybe you did 'make' but didn't do 'make install'.\n"
1043 " - Else, try to reinstall the game.\n");
1044 return false;
1045 }
1046
1047 if (!fontSmall.Open(HC_DATA_DIR "font/font.ttf", 12) ||
1048 !fontNormal.Open(HC_DATA_DIR "font/font.ttf", 18) ||
1049 !fontLarge.Open(HC_DATA_DIR "font/font.ttf", 24))
1050 {
1051 fprintf(stderr, "Couldn't open fonts. Check manual.\n");
1052 return false;
1053 }
1054
1055 // Loads the level
1056 if (0 != LoadLevel())
1057 {
1058 fprintf(stderr, "Couldn't load level. Check manual.\n");
1059 return false;
1060 }
1061
1062 // Inits the main menu
1063 if (!InitMainMenu())
1064 {
1065 fprintf(stderr, "Failed to init main menu.\n");
1066
1067 return false;
1068 }
1069
1070 // Inits the level dependant menus
1071 if (0 != InitLevelMenus())
1072 {
1073 fprintf(stderr, "Failed to init level menus.\n");
1074
1075 return false;
1076 }
1077
1078 // Inits the rope periods menu
1079 if (!InitRopePeriodMenu())
1080 {
1081 fprintf(stderr, "Failed to init rope periods menu.\n");
1082
1083 return false;
1084 }
1085
1086 // Inits the enemy types menu
1087 if (!InitEnemyTypeMenu())
1088 {
1089 fprintf(stderr, "Failed to init enemy types menu.\n");
1090
1091 return false;
1092 }
1093
1094 // Inits the enemy param 1 menu
1095 if (!InitEnemyParam1Menu())
1096 {
1097 fprintf(stderr, "Failed to init enemy param 1 menu.\n");
1098
1099 return false;
1100 }
1101
1102 // Inits the enemy param 2 menu
1103 if (!InitEnemyParam2Menu())
1104 {
1105 fprintf(stderr, "Failed to init enemy param 2 menu.\n");
1106
1107 return false;
1108 }
1109
1110 appBackColor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x07);
1111
1112 OnFloor(this);
1113
1114 return true;
1115 }
1116
InitLoadedLevel()1117 bool HCed::InitLoadedLevel()
1118 {
1119 // Nothing for the moment
1120 return true;
1121 }
1122
InitDefaultLevel()1123 bool HCed::InitDefaultLevel()
1124 {
1125 // Destroy a previous level
1126 level.Destroy();
1127
1128 // Sets the timer font
1129 level.SetTimerFont(&fontNormal);
1130
1131 // Loads the theme
1132 printf("Using theme %s\n", themeName.Str());
1133 if (!level.Theme().Load(themeName.Str()))
1134 {
1135 fprintf(stderr, "Error loading theme.\n");
1136 return false;
1137 }
1138
1139 // Initializes the map
1140 level.Map().Init(level.Theme());
1141 level.Map().Resize(defRows, defCols);
1142 level.Map().CellWidth(level.Theme().Floor(0).Width());
1143 level.Map().CellHeight(level.Theme().Floor(0).Height());
1144
1145 // Initializes the character
1146 level.character.Pos(20, 20);
1147
1148 if (!level.Init())
1149 {
1150 fprintf(stderr, "Error initializing level.\n");
1151 return false;
1152 }
1153
1154 return true;
1155 }
1156
InitMainMenu()1157 bool HCed::InitMainMenu()
1158 {
1159 JDELETE(menuMain);
1160 menuMain = new JImageMenu;
1161
1162 char str[256];
1163
1164 for (s32 i = 0; i < HCEDSTATE_COUNT; ++i)
1165 {
1166 snprintf(str, sizeof(str), HCED_DATA_DIR "MainMenu/%d.tga", i);
1167 //fprintf(stderr, "Loading UI Item " HCED_DATA_DIR "MainMenu/%d.tga\n", i);
1168
1169 if (!imgMenu[i].Load(str))
1170 {
1171 return false;
1172 }
1173
1174 snprintf(str, sizeof(str), HCED_DATA_DIR "MainMenu/%d_hi.tga", i);
1175 //fprintf(stderr, "Loading UI Item " HCED_DATA_DIR "MainMenu/%d_hi.tga\n", i);
1176
1177 if (!imgMenuOver[i].Load(str))
1178 {
1179 return false;
1180 }
1181 }
1182
1183 // Adds the options
1184 JTree<JImageMenuEntry *>::Iterator *it = menuMain->Menu();
1185
1186 it->Data(new JImageMenuEntry(&imgMenu[HCEDSTATE_FLOOR],
1187 &imgMenuOver[HCEDSTATE_FLOOR], &OnFloor, this));
1188 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_CONTFLOOR],
1189 &imgMenuOver[HCEDSTATE_CONTFLOOR], &OnContFloor, this));
1190 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_LADDER],
1191 &imgMenuOver[HCEDSTATE_LADDER], &OnLadder, this));
1192 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_BAR],
1193 &imgMenuOver[HCEDSTATE_BAR], &OnBar, this));
1194 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_BREAK],
1195 &imgMenuOver[HCEDSTATE_BREAK], &OnBreak, this));
1196 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_OBJECT],
1197 &imgMenuOver[HCEDSTATE_OBJECT], &OnObject, this));
1198 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_ROPE],
1199 &imgMenuOver[HCEDSTATE_ROPE], &OnRope, this));
1200 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_START],
1201 &imgMenuOver[HCEDSTATE_START], &OnStart, this));
1202 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_EXIT],
1203 &imgMenuOver[HCEDSTATE_EXIT], &OnExit, this));
1204 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_ENEMY],
1205 &imgMenuOver[HCEDSTATE_ENEMY], &OnEnemy, this));
1206 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_SAVE],
1207 &imgMenuOver[HCEDSTATE_SAVE], &OnSave, this));
1208 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_OPENSTORY],
1209 &imgMenuOver[HCEDSTATE_OPENSTORY], &OnOpenStory, this));
1210 it->AddNodeGo(new JImageMenuEntry(&imgMenu[HCEDSTATE_APPEXIT],
1211 &imgMenuOver[HCEDSTATE_APPEXIT], &OnAppExit, this));
1212 it->Root();
1213
1214 JImageMenuConfig cfg;
1215 cfg.layout = JIMAGEMENU_LEFT;
1216 cfg.trackMouse = true;
1217 cfg.trackKeyboard = false;
1218 cfg.autoEnter = false;
1219
1220 if (!menuMain->Init(cfg))
1221 {
1222 return false;
1223 }
1224
1225 menuMain->Pos(0, 0);
1226 menuMain->Menu()->Root();
1227
1228 return true;
1229 }
1230
InitMainSubtypeMenu()1231 bool HCed::InitMainSubtypeMenu()
1232 {
1233 DestroyMainSubtypeMenu();
1234 menuMainSubtype = new JImageMenu;
1235
1236 // Adds the options
1237 JTree<JImageMenuEntry *>::Iterator *it = menuMainSubtype->Menu();
1238 it->Root();
1239
1240 it->Data(new JImageMenuEntry(new JImage(*((JImage*)level.theme.MainChar(0)[HCCDT_STOP].Frame(0))),
1241 new JImage(*((JImage*)level.theme.MainChar(0)[HCCDT_LEFT].Frame(0))),
1242 &OnMainSubtype, (void *)0));
1243
1244 // Adds the subtypes
1245 for (s32 i = 1; i < level.theme.NumMainChars(); ++i)
1246 {
1247 it->AddNodeGo(new JImageMenuEntry(new JImage(*((JImage*)level.theme.MainChar(i)[HCCDT_STOP].Frame(0))),
1248 new JImage(*((JImage*)level.theme.MainChar(i)[HCCDT_LEFT].Frame(0))),
1249 &OnMainSubtype, JCAST_S32_TO_VOIDPTR(i)));
1250 }
1251
1252 it->Root();
1253
1254 JImageMenuConfig cfg;
1255 cfg.layout = JIMAGEMENU_SAMELINE;
1256 cfg.trackMouse = true;
1257 cfg.trackKeyboard = false;
1258 cfg.autoEnter = false;
1259 menuMainSubtype->Pos(HCED_MARGIN, 0);
1260
1261 if (!menuMainSubtype->Init(cfg))
1262 {
1263 return false;
1264 }
1265
1266 menuMainSubtype->Menu()->Root();
1267
1268 return true;
1269 }
1270
InitFloorSubtypeMenu()1271 bool HCed::InitFloorSubtypeMenu()
1272 {
1273 JDELETE(menuFloorSubtype);
1274 menuFloorSubtype = new JImageMenu;
1275
1276 // Adds the options
1277 JTree<JImageMenuEntry *>::Iterator *it = menuFloorSubtype->Menu();
1278
1279 // At least we must have this
1280 it->Data(new JImageMenuEntry(&level.Theme().Floor(0), &level.Theme().Floor(0), &OnFloorSubtype, (void *)0));
1281
1282 // Adds the rest of subtypes
1283 for (s32 i = 1; i < level.Theme().NumFloors(); ++i)
1284 {
1285 it->AddNodeGo(new JImageMenuEntry(&level.Theme().Floor(i), &level.Theme().Floor(i), &OnFloorSubtype, JCAST_S32_TO_VOIDPTR(i)));
1286 }
1287
1288 it->Root();
1289
1290 JImageMenuConfig cfg;
1291 cfg.layout = JIMAGEMENU_SAMELINE;
1292 cfg.trackMouse = true;
1293 cfg.trackKeyboard = false;
1294 cfg.autoEnter = false;
1295
1296 if (!menuFloorSubtype->Init(cfg))
1297 {
1298 fprintf(stderr, "Error\n");
1299 return false;
1300 }
1301
1302 menuFloorSubtype->Pos(HCED_MARGIN, 0);
1303 menuFloorSubtype->Menu()->Root();
1304
1305 return true;
1306 }
1307
InitContFloorSubtypeMenu()1308 bool HCed::InitContFloorSubtypeMenu()
1309 {
1310 JDELETE(menuContFloorSubtype);
1311 menuContFloorSubtype = new JImageMenu;
1312
1313 // Adds the options
1314 JTree<JImageMenuEntry *>::Iterator *it = menuContFloorSubtype->Menu();
1315
1316 // At least we must have this
1317 it->Data(new JImageMenuEntry(&level.Theme().ContFloor(0)[HCFDT_I], &level.Theme().ContFloor(0)[HCFDT_I], &OnContFloorSubtype, (void *)0));
1318
1319 // Adds the rest of subtypes
1320 for (s32 i = 1; i < level.Theme().NumContFloors(); ++i)
1321 {
1322 it->AddNodeGo(new JImageMenuEntry(&level.Theme().ContFloor(i)[HCFDT_I], &level.Theme().ContFloor(i)[HCFDT_I], &OnContFloorSubtype, JCAST_S32_TO_VOIDPTR(i)));
1323 }
1324
1325 it->Root();
1326
1327 JImageMenuConfig cfg;
1328 cfg.layout = JIMAGEMENU_SAMELINE;
1329 cfg.trackMouse = true;
1330 cfg.trackKeyboard = false;
1331 cfg.autoEnter = false;
1332
1333 if (!menuContFloorSubtype->Init(cfg))
1334 {
1335 fprintf(stderr, "Error\n");
1336 return false;
1337 }
1338
1339 menuContFloorSubtype->Pos(HCED_MARGIN, 0);
1340 menuContFloorSubtype->Menu()->Root();
1341
1342 return true;
1343 }
1344
InitBreakSubtypeMenu()1345 bool HCed::InitBreakSubtypeMenu()
1346 {
1347 DestroyBreakSubtypeMenu();
1348 menuBreakSubtype = new JImageMenu;
1349
1350 // Adds the options
1351 JTree<JImageMenuEntry *>::Iterator *it = menuBreakSubtype->Menu();
1352 it->Root();
1353
1354 // At least we must have this
1355 it->Data(new JImageMenuEntry(new JImage(*((JImage*)level.Theme().Break(0)[HCBDT_NORMAL].Frame(0))),
1356 new JImage(*((JImage*)level.Theme().Break(0)[HCBDT_NORMAL].Frame(0))),
1357 &OnBreakSubtype, (void *)0));
1358
1359 // Adds the rest of subtypes
1360 for (s32 i = 1; i < level.Theme().NumBreaks(); ++i)
1361 {
1362 it->AddNodeGo(new JImageMenuEntry(new JImage(*((JImage*)level.Theme().Break(i)[HCBDT_NORMAL].Frame(0))),
1363 new JImage(*((JImage*)level.Theme().Break(i)[HCBDT_NORMAL].Frame(0))),
1364 &OnBreakSubtype, JCAST_S32_TO_VOIDPTR(i)));
1365 }
1366
1367 it->Root();
1368
1369 JImageMenuConfig cfg;
1370 cfg.layout = JIMAGEMENU_SAMELINE;
1371 cfg.trackMouse = true;
1372 cfg.trackKeyboard = false;
1373 cfg.autoEnter = false;
1374 menuBreakSubtype->Pos(HCED_MARGIN, 0);
1375
1376 if (!menuBreakSubtype->Init(cfg))
1377 {
1378 return false;
1379 }
1380
1381 menuBreakSubtype->Menu()->Root();
1382
1383 return true;
1384 }
1385
InitBarSubtypeMenu()1386 bool HCed::InitBarSubtypeMenu()
1387 {
1388 JDELETE(menuBarSubtype);
1389 menuBarSubtype = new JImageMenu;
1390
1391 // Adds the options
1392 JTree<JImageMenuEntry *>::Iterator *it = menuBarSubtype->Menu();
1393
1394 // At least we must have this
1395 it->Data(new JImageMenuEntry(&level.Theme().Bar(0), &level.Theme().Bar(0), &OnBarSubtype, (void *)0));
1396
1397 // Adds the rest of subtypes
1398 for (s32 i = 1; i < level.Theme().NumBars(); ++i)
1399 {
1400 it->AddNodeGo(new JImageMenuEntry(&level.Theme().Bar(i), &level.Theme().Bar(i), &OnBarSubtype, JCAST_S32_TO_VOIDPTR(i)));
1401 }
1402
1403 it->Root();
1404
1405 JImageMenuConfig cfg;
1406 cfg.layout = JIMAGEMENU_SAMELINE;
1407 cfg.trackMouse = true;
1408 cfg.trackKeyboard = false;
1409 cfg.autoEnter = false;
1410 menuBarSubtype->Pos(HCED_MARGIN, 0);
1411
1412 if (!menuBarSubtype->Init(cfg))
1413 {
1414 return false;
1415 }
1416
1417 menuBarSubtype->Menu()->Root();
1418
1419 return true;
1420 }
1421
InitLadderSubtypeMenu()1422 bool HCed::InitLadderSubtypeMenu()
1423 {
1424 JDELETE(menuLadderSubtype);
1425 menuLadderSubtype = new JImageMenu;
1426
1427 // Adds the options
1428 JTree<JImageMenuEntry *>::Iterator *it = menuLadderSubtype->Menu();
1429
1430 // At least we must have this
1431 it->Data(new JImageMenuEntry(&level.Theme().Ladder(0), &level.Theme().Ladder(0), &OnLadderSubtype, (void *)0));
1432
1433 // Adds the rest of subtypes
1434 for (s32 i = 1; i < level.Theme().NumLadders(); ++i)
1435 {
1436 it->AddNodeGo(new JImageMenuEntry(&level.Theme().Ladder(i), &level.Theme().Ladder(i), &OnLadderSubtype, JCAST_S32_TO_VOIDPTR(i)));
1437 }
1438
1439 it->Root();
1440
1441 JImageMenuConfig cfg;
1442 cfg.layout = JIMAGEMENU_SAMELINE;
1443 cfg.trackMouse = true;
1444 cfg.trackKeyboard = false;
1445 cfg.autoEnter = false;
1446 menuLadderSubtype->Pos(HCED_MARGIN, 0);
1447
1448 if (!menuLadderSubtype->Init(cfg))
1449 {
1450 return false;
1451 }
1452
1453 menuLadderSubtype->Menu()->Root();
1454
1455 return true;
1456 }
1457
InitObjectSubtypeMenu()1458 bool HCed::InitObjectSubtypeMenu()
1459 {
1460 DestroyObjectSubtypeMenu();
1461 menuObjectSubtype = new JImageMenu;
1462
1463 // Adds the options
1464 JTree<JImageMenuEntry *>::Iterator *it = menuObjectSubtype->Menu();
1465 it->Root();
1466
1467 // At least we must have this
1468 it->Data(new JImageMenuEntry(new JImage(*((JImage*)level.Theme().Object(0)[HCODT_NORMAL].Frame(0))),
1469 new JImage(*((JImage*)level.Theme().Object(0)[HCODT_ACQUIRED].Frame(0))),
1470 &OnObjectSubtype,
1471 (void *)0));
1472
1473 // Adds the rest of subtypes
1474 for (s32 i = 1; i < level.Theme().NumObjects(); ++i)
1475 {
1476 it->AddNodeGo(new JImageMenuEntry(new JImage(*((JImage*)level.Theme().Object(i)[HCODT_NORMAL].Frame(0))),
1477 new JImage(*((JImage*)level.Theme().Object(i)[HCODT_ACQUIRED].Frame(0))),
1478 &OnObjectSubtype,
1479 JCAST_S32_TO_VOIDPTR(i)));
1480 }
1481
1482 it->Root();
1483
1484 JImageMenuConfig cfg;
1485 cfg.layout = JIMAGEMENU_SAMELINE;
1486 cfg.trackMouse = true;
1487 cfg.trackKeyboard = false;
1488 cfg.autoEnter = false;
1489 menuObjectSubtype->Pos(HCED_MARGIN, 0);
1490
1491 if (!menuObjectSubtype->Init(cfg))
1492 {
1493 return false;
1494 }
1495
1496 menuObjectSubtype->Menu()->Root();
1497
1498 return true;
1499 }
1500
InitRopeSubtypeMenu()1501 bool HCed::InitRopeSubtypeMenu()
1502 {
1503 JDELETE(menuRopeSubtype);
1504 menuRopeSubtype = new JImageMenu;
1505
1506 // Adds the options
1507 JTree<JImageMenuEntry *>::Iterator *it = menuRopeSubtype->Menu();
1508
1509 // At least we must have this
1510 it->Data(new JImageMenuEntry(&level.Theme().Rope(0)[HCRDT_TOP], &level.Theme().Rope(0)[HCRDT_TOP], &OnRopeSubtype, (void *)0));
1511
1512 // Adds the rest of subtypes
1513 for (s32 i = 1; i < level.Theme().NumRopes(); ++i)
1514 {
1515 it->AddNodeGo(new JImageMenuEntry(&level.Theme().Rope(i)[HCRDT_TOP], &level.Theme().Rope(i)[HCRDT_TOP], &OnRopeSubtype, JCAST_S32_TO_VOIDPTR(i)));
1516 }
1517
1518 it->Root();
1519
1520 JImageMenuConfig cfg;
1521 cfg.layout = JIMAGEMENU_SAMELINE;
1522 cfg.trackMouse = true;
1523 cfg.trackKeyboard = false;
1524 cfg.autoEnter = false;
1525 menuRopeSubtype->Pos(HCED_MARGIN, 0);
1526
1527 if (!menuRopeSubtype->Init(cfg))
1528 {
1529 return false;
1530 }
1531
1532 menuRopeSubtype->Menu()->Root();
1533
1534 return true;
1535 }
1536
InitRopePeriodMenu()1537 bool HCed::InitRopePeriodMenu()
1538 {
1539 JDELETE(menuRopePeriod);
1540 menuRopePeriod = new JTextMenu;
1541
1542 // Crea el men�
1543 JTree<JTextMenuEntry *>::Iterator *it = menuRopePeriod->Menu();
1544
1545 it->Data(new JTextMenuEntry("1", &OnRopePeriod, (void *)1));
1546 it->AddNodeGo(new JTextMenuEntry("2", &OnRopePeriod, (void *)2));
1547 it->AddNodeGo(new JTextMenuEntry("3", &OnRopePeriod, (void *)3));
1548 it->AddNodeGo(new JTextMenuEntry("4", &OnRopePeriod, (void *)4));
1549 it->AddNodeGo(new JTextMenuEntry("5", &OnRopePeriod, (void *)5));
1550 it->AddNodeGo(new JTextMenuEntry("6", &OnRopePeriod, (void *)6));
1551 it->AddNodeGo(new JTextMenuEntry("7", &OnRopePeriod, (void *)7));
1552 it->AddNodeGo(new JTextMenuEntry("8", &OnRopePeriod, (void *)8));
1553 it->AddNodeGo(new JTextMenuEntry("9", &OnRopePeriod, (void *)9));
1554 it->AddNodeGo(new JTextMenuEntry("10", &OnRopePeriod, (void *)10));
1555 it->Root();
1556
1557 JTextMenuConfig cfg;
1558 cfg.font = &fontSmall;
1559 cfg.color.r = 0x00;
1560 cfg.color.g = 0x00;
1561 cfg.color.b = 0x00;
1562 cfg.backColor.r = 0xff;
1563 cfg.backColor.g = 0xcc;
1564 cfg.backColor.b = 0x00;
1565 cfg.hiColor.r = 0xff;
1566 cfg.hiColor.g = 0xff;
1567 cfg.hiColor.b = 0xff;
1568 cfg.hiBackColor.r = 0xff;
1569 cfg.hiBackColor.g = 0xdd;
1570 cfg.hiBackColor.b = 0x00;
1571 cfg.renderMode = JTEXTMENU_SHADED;
1572 cfg.layout = JTEXTMENU_SAMELINE;
1573 cfg.trackKeyboard = true;
1574 cfg.trackMouse = true;
1575 cfg.autoEnter = true;
1576
1577 if (!menuRopePeriod->Init(cfg))
1578 {
1579 return false;
1580 }
1581
1582 menuRopePeriod->Pos(Width() - (12 * HCED_MARGIN), 0);
1583 menuRopePeriod->Menu()->Root();
1584
1585 return true;
1586 }
1587
InitEnemySubtypeMenu()1588 bool HCed::InitEnemySubtypeMenu()
1589 {
1590 DestroyEnemySubtypeMenu();
1591
1592 for (s32 n = 0; n < HCENEMYTYPE_COUNT; ++n)
1593 {
1594 menuEnemySubtype[n] = new JImageMenu;
1595
1596 // Adds the options
1597 JTree<JImageMenuEntry *>::Iterator *it = menuEnemySubtype[n]->Menu();
1598 it->Root();
1599
1600 it->Data(new JImageMenuEntry(new JImage(*((JImage*)GetEnemySprites((HCEnemyType)n, 0)[HCCDT_STOP].Frame(0))),
1601 new JImage(*((JImage*)GetEnemySprites((HCEnemyType)n, 0)[HCCDT_STOP].Frame(0))),
1602 &OnEnemySubtype, (void *)0));
1603
1604 // Adds the subtypes
1605 for (s32 i = 1; i < GetNumEnemySprites((HCEnemyType)n); ++i)
1606 {
1607 // Scales the normal and highlighted images to fit 20 and 32 pixels, respectively
1608 it->AddNodeGo(new JImageMenuEntry(new JImage(*((JImage*)GetEnemySprites((HCEnemyType)n, i)[HCCDT_STOP].Frame(0))),
1609 new JImage(*((JImage*)GetEnemySprites((HCEnemyType)n, i)[HCCDT_STOP].Frame(0))),
1610 &OnEnemySubtype, JCAST_S32_TO_VOIDPTR(i)));
1611 }
1612
1613 it->Root();
1614
1615 JImageMenuConfig cfg;
1616 cfg.layout = JIMAGEMENU_SAMELINE;
1617 cfg.trackMouse = true;
1618 cfg.trackKeyboard = false;
1619 cfg.autoEnter = false;
1620 menuEnemySubtype[n]->Pos(HCED_MARGIN, 0);
1621
1622 if (!menuEnemySubtype[n]->Init(cfg))
1623 {
1624 return false;
1625 }
1626
1627 menuEnemySubtype[n]->Menu()->Root();
1628 }
1629
1630 return true;
1631 }
1632
InitEnemyTypeMenu()1633 bool HCed::InitEnemyTypeMenu()
1634 {
1635 char str[256];
1636
1637 if (!menuEnemyType)
1638 {
1639 for (s32 i = 0; i < HCENEMYTYPE_COUNT; ++i)
1640 {
1641 snprintf(str, sizeof(str), HCED_DATA_DIR "EnemyMenu/%d.tga", i);
1642 //fprintf(stderr, "Loading UI Item " HCED_DATA_DIR "EnemyMenu/%d.tga\n", i);
1643
1644 if (!imgEnemy[i].Load(str))
1645 {
1646 return false;
1647 }
1648
1649 snprintf(str, sizeof(str), HCED_DATA_DIR "EnemyMenu/%d_hi.tga", i);
1650 //fprintf(stderr, "Loading UI Item " HCED_DATA_DIR "EnemyMenu/%d_hi.tga\n", i);
1651
1652 if (!imgEnemyOver[i].Load(str))
1653 {
1654 return false;
1655 }
1656 }
1657 }
1658
1659 JDELETE(menuEnemyType);
1660 menuEnemyType = new JImageMenu;
1661
1662 // Adds the options
1663 JTree<JImageMenuEntry *>::Iterator *it = menuEnemyType->Menu();
1664
1665 it->Data(new JImageMenuEntry(&imgEnemy[HCENEMYTYPE_BALL],
1666 &imgEnemyOver[HCENEMYTYPE_BALL],
1667 &OnEnemyType, (void *)HCENEMYTYPE_BALL));
1668 it->AddNodeGo(new JImageMenuEntry(&imgEnemy[HCENEMYTYPE_RANDOM],
1669 &imgEnemyOver[HCENEMYTYPE_RANDOM],
1670 &OnEnemyType, (void *)HCENEMYTYPE_RANDOM));
1671 it->AddNodeGo(new JImageMenuEntry(&imgEnemy[HCENEMYTYPE_STATIC],
1672 &imgEnemyOver[HCENEMYTYPE_STATIC],
1673 &OnEnemyType, (void *)HCENEMYTYPE_STATIC));
1674 it->AddNodeGo(new JImageMenuEntry(&imgEnemy[HCENEMYTYPE_MAKER],
1675 &imgEnemyOver[HCENEMYTYPE_MAKER],
1676 &OnEnemyType, (void *)HCENEMYTYPE_MAKER));
1677 it->AddNodeGo(new JImageMenuEntry(&imgEnemy[HCENEMYTYPE_CHASER],
1678 &imgEnemyOver[HCENEMYTYPE_CHASER],
1679 &OnEnemyType, (void *)HCENEMYTYPE_CHASER));
1680 it->Root();
1681
1682 JImageMenuConfig cfg;
1683 cfg.layout = JIMAGEMENU_SAMELINE;
1684 cfg.trackMouse = true;
1685 cfg.trackKeyboard = false;
1686 cfg.autoEnter = false;
1687 menuEnemyType->Pos(Width() - (5 * HCED_MARGIN), 0);
1688
1689 if (!menuEnemyType->Init(cfg))
1690 {
1691 return false;
1692 }
1693
1694 menuEnemyType->Menu()->Root();
1695
1696 return true;
1697 }
1698
InitEnemyParam1Menu()1699 bool HCed::InitEnemyParam1Menu()
1700 {
1701 JDELETE(menuEnemyParam1);
1702 menuEnemyParam1 = new JTextMenu;
1703
1704 // Crea el men�
1705 JTree<JTextMenuEntry *>::Iterator *it = menuEnemyParam1->Menu();
1706
1707 it->Data(new JTextMenuEntry(" 1 ", &OnEnemyParam1, (void *)1));
1708 it->AddNodeGo(new JTextMenuEntry(" 2 ", &OnEnemyParam1, (void *)2));
1709 it->AddNodeGo(new JTextMenuEntry(" 3 ", &OnEnemyParam1, (void *)3));
1710 it->AddNodeGo(new JTextMenuEntry(" 4 ", &OnEnemyParam1, (void *)4));
1711 it->AddNodeGo(new JTextMenuEntry(" 5 ", &OnEnemyParam1, (void *)5));
1712 it->AddNodeGo(new JTextMenuEntry(" 6 ", &OnEnemyParam1, (void *)6));
1713 it->AddNodeGo(new JTextMenuEntry(" 7 ", &OnEnemyParam1, (void *)7));
1714 it->AddNodeGo(new JTextMenuEntry(" 8 ", &OnEnemyParam1, (void *)8));
1715 it->AddNodeGo(new JTextMenuEntry(" 9 ", &OnEnemyParam1, (void *)9));
1716 it->AddNodeGo(new JTextMenuEntry(" 10 ", &OnEnemyParam1, (void *)10));
1717 it->Root();
1718
1719 JTextMenuConfig cfg;
1720 cfg.font = &fontSmall;
1721 cfg.color.r = 0x00;
1722 cfg.color.g = 0x00;
1723 cfg.color.b = 0x00;
1724 cfg.backColor.r = 0x00;
1725 cfg.backColor.g = 0x00;
1726 cfg.backColor.b = 0xcc;
1727 cfg.hiColor.r = 0xff;
1728 cfg.hiColor.g = 0xff;
1729 cfg.hiColor.b = 0xff;
1730 cfg.hiBackColor.r = 0x00;
1731 cfg.hiBackColor.g = 0x00;
1732 cfg.hiBackColor.b = 0xcc;
1733 cfg.renderMode = JTEXTMENU_SHADED;
1734 cfg.layout = JTEXTMENU_SAMELINE;
1735 cfg.trackKeyboard = true;
1736 cfg.trackMouse = true;
1737 cfg.autoEnter = true;
1738
1739 if (!menuEnemyParam1->Init(cfg))
1740 {
1741 return false;
1742 }
1743
1744 menuEnemyParam1->Pos(Width() - (20 * 20), 32);
1745 menuEnemyParam1->Menu()->Root();
1746
1747 return true;
1748 }
1749
InitEnemyParam2Menu()1750 bool HCed::InitEnemyParam2Menu()
1751 {
1752 JDELETE(menuEnemyParam2);
1753 menuEnemyParam2 = new JTextMenu;
1754
1755 // Crea el men�
1756 JTree<JTextMenuEntry *>::Iterator *it = menuEnemyParam2->Menu();
1757
1758 it->Data(new JTextMenuEntry(" 1 ", &OnEnemyParam2, (void *)1));
1759 it->AddNodeGo(new JTextMenuEntry(" 2 ", &OnEnemyParam2, (void *)2));
1760 it->AddNodeGo(new JTextMenuEntry(" 3 ", &OnEnemyParam2, (void *)3));
1761 it->AddNodeGo(new JTextMenuEntry(" 4 ", &OnEnemyParam2, (void *)4));
1762 it->AddNodeGo(new JTextMenuEntry(" 5 ", &OnEnemyParam2, (void *)5));
1763 it->AddNodeGo(new JTextMenuEntry(" 6 ", &OnEnemyParam2, (void *)6));
1764 it->AddNodeGo(new JTextMenuEntry(" 7 ", &OnEnemyParam2, (void *)7));
1765 it->AddNodeGo(new JTextMenuEntry(" 8 ", &OnEnemyParam2, (void *)8));
1766 it->AddNodeGo(new JTextMenuEntry(" 9 ", &OnEnemyParam2, (void *)9));
1767 it->AddNodeGo(new JTextMenuEntry(" 10 ", &OnEnemyParam2, (void *)10));
1768 it->Root();
1769
1770 JTextMenuConfig cfg;
1771 cfg.font = &fontSmall;
1772 cfg.color.r = 0x00;
1773 cfg.color.g = 0x00;
1774 cfg.color.b = 0x00;
1775 cfg.backColor.r = 0x00;
1776 cfg.backColor.g = 0xcc;
1777 cfg.backColor.b = 0x00;
1778 cfg.hiColor.r = 0xff;
1779 cfg.hiColor.g = 0xff;
1780 cfg.hiColor.b = 0xff;
1781 cfg.hiBackColor.r = 0x00;
1782 cfg.hiBackColor.g = 0xcc;
1783 cfg.hiBackColor.b = 0x00;
1784 cfg.renderMode = JTEXTMENU_SHADED;
1785 cfg.layout = JTEXTMENU_SAMELINE;
1786 cfg.trackKeyboard = true;
1787 cfg.trackMouse = true;
1788 cfg.autoEnter = true;
1789
1790 if (!menuEnemyParam2->Init(cfg))
1791 {
1792 return false;
1793 }
1794
1795 menuEnemyParam2->Pos(Width() - (10 * 20), 32);
1796 menuEnemyParam2->Menu()->Root();
1797
1798 return true;
1799 }
1800
InitOpenStoryMenu()1801 bool HCed::InitOpenStoryMenu()
1802 {
1803 HCUtil::FindStories(true);
1804
1805 JDELETE(menuOpenStory);
1806 menuOpenStory = new JTextMenu;
1807
1808 // Crea el men�
1809 JTree<JTextMenuEntry *>::Iterator *it = menuOpenStory->Menu();
1810
1811 if (HCUtil::Stories().size() > 0)
1812 {
1813 it->Data(new JTextMenuEntry(HCUtil::Stories()[0], &OnSelectStory, (void *)0));
1814
1815 for (u32 i = 1; i < HCUtil::Stories().size(); ++i)
1816 {
1817 it->AddNodeGo(new JTextMenuEntry(HCUtil::Stories()[i], &OnSelectStory, JCAST_S32_TO_VOIDPTR(i)));
1818 }
1819
1820 it->AddNodeGo(new JTextMenuEntry("<------>", &OnSelectStory, (void *)-1));
1821 }
1822 else
1823 {
1824 it->Data(new JTextMenuEntry("<------>", &OnSelectStory, (void *)-1));
1825 }
1826
1827 it->Root();
1828
1829 JTextMenuConfig cfg;
1830 cfg.font = &fontSmall;
1831 cfg.lineDistance = 3;
1832 cfg.color.r = 0xff;
1833 cfg.color.g = 0xcc;
1834 cfg.color.b = 0x00;
1835 cfg.backColor.r = 0x00;
1836 cfg.backColor.g = 0x00;
1837 cfg.backColor.b = 0x00;
1838 cfg.hiColor.r = 0xff;
1839 cfg.hiColor.g = 0xff;
1840 cfg.hiColor.b = 0xff;
1841 cfg.hiBackColor.r = 0x00;
1842 cfg.hiBackColor.g = 0x00;
1843 cfg.hiBackColor.b = 0x00;
1844 cfg.renderMode = JTEXTMENU_BLENDED;
1845 cfg.layout = JTEXTMENU_CENTER;
1846 cfg.layoutV = JTEXTMENU_CENTER;
1847 cfg.trackKeyboard = true;
1848 cfg.trackMouse = true;
1849 cfg.autoEnter = false;
1850
1851 if (!menuOpenStory->Init(cfg))
1852 {
1853 return false;
1854 }
1855
1856 menuOpenStory->Pos(Width()/2, Height()/2);
1857 menuOpenStory->Menu()->Root();
1858
1859 return true;
1860 }
1861
InitThemeMenu()1862 bool HCed::InitThemeMenu()
1863 {
1864 if (HCUtil::FindThemes())
1865 {
1866 JDELETE(menuTheme);
1867 menuTheme = new JTextMenu;
1868
1869 // Crea el men�
1870 JTree<JTextMenuEntry *>::Iterator *it = menuTheme->Menu();
1871
1872 it->Data(new JTextMenuEntry(HCUtil::Themes()[0], &OnSelectTheme, 0));
1873 for (u32 i = 1; i < HCUtil::Themes().size(); ++i)
1874 {
1875 it->AddNodeGo(new JTextMenuEntry(HCUtil::Themes()[i], &OnSelectTheme, JCAST_S32_TO_VOIDPTR(i)));
1876 }
1877
1878 it->Root();
1879
1880 JTextMenuConfig cfg;
1881 cfg.font = &fontSmall;
1882 cfg.lineDistance = 3;
1883 cfg.color.r = 0xff;
1884 cfg.color.g = 0xcc;
1885 cfg.color.b = 0x00;
1886 cfg.backColor.r = 0x00;
1887 cfg.backColor.g = 0x00;
1888 cfg.backColor.b = 0x00;
1889 cfg.hiColor.r = 0xff;
1890 cfg.hiColor.g = 0xff;
1891 cfg.hiColor.b = 0xff;
1892 cfg.hiBackColor.r = 0x00;
1893 cfg.hiBackColor.g = 0x00;
1894 cfg.hiBackColor.b = 0x00;
1895 cfg.renderMode = JTEXTMENU_BLENDED;
1896 cfg.layout = JTEXTMENU_CENTER;
1897 cfg.layoutV = JTEXTMENU_CENTER;
1898 cfg.trackKeyboard = true;
1899 cfg.trackMouse = true;
1900 cfg.autoEnter = false;
1901
1902 if (!menuTheme->Init(cfg))
1903 {
1904 return false;
1905 }
1906
1907 menuTheme->Pos(Width()/2, Height()/2);
1908 menuTheme->Menu()->Root();
1909
1910 return true;
1911 }
1912
1913 return false;
1914 }
1915
Draw()1916 bool HCed::Draw()
1917 {
1918 bool ret = true;
1919
1920 SDL_FillRect(screen, 0, appBackColor);
1921 level.Draw();
1922 menuMain->Draw();
1923
1924 // Draws the mouse cursor
1925 imgMouse->Draw(MouseX() + imgMouse->Width(), MouseY() + imgMouse->Height());
1926
1927 switch (state)
1928 {
1929 case HCEDSTATE_FLOOR:
1930 ret = DrawFloor();
1931 break;
1932 case HCEDSTATE_CONTFLOOR:
1933 ret = DrawContFloor();
1934 break;
1935 case HCEDSTATE_LADDER:
1936 ret = DrawLadder();
1937 break;
1938 case HCEDSTATE_BAR:
1939 ret = DrawBar();
1940 break;
1941 case HCEDSTATE_BREAK:
1942 ret = DrawBreak();
1943 break;
1944 case HCEDSTATE_OBJECT:
1945 ret = DrawObject();
1946 break;
1947 case HCEDSTATE_ROPE:
1948 ret = DrawRope();
1949 break;
1950 case HCEDSTATE_START:
1951 ret = DrawStart();
1952 break;
1953 case HCEDSTATE_EXIT:
1954 ret = DrawExit();
1955 break;
1956 case HCEDSTATE_ENEMY:
1957 ret = DrawEnemy();
1958 break;
1959 case HCEDSTATE_SAVE:
1960 ret = DrawSave();
1961 break;
1962 case HCEDSTATE_OPENSTORY:
1963 ret = DrawOpenStory();
1964 break;
1965 case HCEDSTATE_APPEXIT:
1966 ret = DrawAppExit();
1967 break;
1968 default:
1969 break;
1970 }
1971
1972 level.levelTimer.Y(13 * 32);
1973 level.levelTimer.Draw();
1974
1975 imgGravity->Y(14 * 32);
1976 imgGravity->Draw();
1977
1978 imgCharVx->Y(15 * 32);
1979 imgCharVx->Draw();
1980
1981 imgCharVy->Y(16 * 32);
1982 imgCharVy->Draw();
1983
1984 imgCharJumpRows->Y(17 * 32);
1985 imgCharJumpRows->Draw();
1986
1987 imgMapSize->Y(18 * 32);
1988 imgMapSize->Draw();
1989
1990 // Swaps buffers!
1991 Flip();
1992
1993 return ret;
1994 }
1995
DrawFloor()1996 bool HCed::DrawFloor()
1997 {
1998 menuFloorSubtype->Draw();
1999
2000 return true;
2001 }
2002
DrawContFloor()2003 bool HCed::DrawContFloor()
2004 {
2005 menuContFloorSubtype->Draw();
2006
2007 return true;
2008 }
2009
DrawLadder()2010 bool HCed::DrawLadder()
2011 {
2012 menuLadderSubtype->Draw();
2013
2014 return true;
2015 }
2016
DrawBar()2017 bool HCed::DrawBar()
2018 {
2019 menuBarSubtype->Draw();
2020
2021 return true;
2022 }
2023
DrawBreak()2024 bool HCed::DrawBreak()
2025 {
2026 menuBreakSubtype->Draw();
2027
2028 return true;
2029 }
2030
DrawObject()2031 bool HCed::DrawObject()
2032 {
2033 menuObjectSubtype->Draw();
2034
2035 return true;
2036 }
2037
DrawRope()2038 bool HCed::DrawRope()
2039 {
2040 menuRopeSubtype->Draw();
2041 menuRopePeriod->Draw();
2042
2043 return true;
2044 }
2045
DrawStart()2046 bool HCed::DrawStart()
2047 {
2048 menuMainSubtype->Draw();
2049
2050 return true;
2051 }
2052
DrawExit()2053 bool HCed::DrawExit()
2054 {
2055 return true;
2056 }
2057
DrawEnemy()2058 bool HCed::DrawEnemy()
2059 {
2060 menuEnemyType->Draw();
2061 menuEnemySubtype[enemyType]->Draw();
2062 menuEnemyParam1->Draw();
2063 menuEnemyParam2->Draw();
2064
2065 return true;
2066 }
2067
DrawSave()2068 bool HCed::DrawSave()
2069 {
2070
2071
2072 return true;
2073 }
2074
DrawOpenStory()2075 bool HCed::DrawOpenStory()
2076 {
2077 switch (inputNewStory)
2078 {
2079 case 0:
2080 // Selecting story
2081 menuOpenStory->Draw();
2082 break;
2083
2084 case HCED_INPUT_STORY:
2085 // Input of new story name
2086 if (imgNewStory)
2087 {
2088 imgNewStory->Draw();
2089 }
2090 break;
2091
2092 case HCED_INPUT_THEME:
2093 // Selection of theme
2094 if (imgNewStory)
2095 {
2096 imgNewStory->Draw();
2097 }
2098
2099 menuTheme->Draw();
2100 break;
2101 }
2102
2103 return true;
2104 }
2105
DrawAppExit()2106 bool HCed::DrawAppExit()
2107 {
2108
2109
2110 return true;
2111 }
2112
Update()2113 bool HCed::Update()
2114 {
2115 menuMain->Update();
2116
2117 // Updates the objects
2118 for (s32 i = 0; i < level.numObjects; ++i)
2119 {
2120 level.objects[i]->Update();
2121 }
2122
2123 // Updates the ropes
2124 for (s32 i = 0; i < level.numRopes; ++i)
2125 {
2126 level.ropes[i]->Update();
2127 }
2128
2129 // Updates the enemies
2130 //for (s32 i = 0; i < level.numEnemies; ++i)
2131 //{
2132 // level.enemies[i]->Update();
2133 //}
2134
2135 // Updates the exit
2136 level.levelExit.Update();
2137
2138 switch (state)
2139 {
2140 case HCEDSTATE_FLOOR:
2141 return UpdateFloor();
2142 case HCEDSTATE_CONTFLOOR:
2143 return UpdateContFloor();
2144 case HCEDSTATE_LADDER:
2145 return UpdateLadder();
2146 case HCEDSTATE_BAR:
2147 return UpdateBar();
2148 case HCEDSTATE_BREAK:
2149 return UpdateBreak();
2150 case HCEDSTATE_OBJECT:
2151 return UpdateObject();
2152 case HCEDSTATE_ROPE:
2153 return UpdateRope();
2154 case HCEDSTATE_START:
2155 return UpdateStart();
2156 case HCEDSTATE_EXIT:
2157 return UpdateExit();
2158 case HCEDSTATE_ENEMY:
2159 return UpdateEnemy();
2160 case HCEDSTATE_SAVE:
2161 return UpdateSave();
2162 case HCEDSTATE_OPENSTORY:
2163 return UpdateOpenStory();
2164 case HCEDSTATE_APPEXIT:
2165 return UpdateAppExit();
2166
2167 default:
2168 break;
2169 }
2170
2171 return true;
2172 }
2173
UpdateEraseFloor()2174 void HCed::UpdateEraseFloor()
2175 {
2176 if (MouseBt() & SDL_BUTTON_RIGHT)
2177 {
2178 s32 row = level.Map().ToRow(MouseY()), col = level.Map().ToCol(MouseX());
2179
2180 // Put a blank cell border around the map, do no let edit those cells
2181 if (row > 0 && col > 0 && row < level.Map().Rows() - 1 && col < level.Map().Cols() - 1)
2182 {
2183 HCCell ***cells = level.Map().Cells();
2184 float x = cells[row][col]->X(), y = cells[row][col]->Y();
2185
2186 // Erases the floor
2187 JDELETE(cells[row][col]);
2188 cells[row][col] = new HCCell;
2189 level.Map().BuildContFloor(row, col);
2190 cells[row][col]->Subtype(0);
2191 cells[row][col]->Pos((s32)x, (s32)y);
2192 level.Map().BuildCellLinkList();
2193 }
2194 }
2195 }
2196
UpdateFloor()2197 bool HCed::UpdateFloor()
2198 {
2199 // Button down
2200 if (MouseOverMap())
2201 {
2202 if (MouseBt() & SDL_BUTTON_LEFT)
2203 {
2204 s32 row = level.Map().ToRow(MouseY()), col = level.Map().ToCol(MouseX());
2205
2206 // Put a blank cell border around the map, do no let edit those cells
2207 if (row > 0 && col > 0 && row < level.Map().Rows() - 1 && col < level.Map().Cols() - 1)
2208 {
2209 HCCell ***cells = level.Map().Cells();
2210 float x = cells[row][col]->X(), y = cells[row][col]->Y();
2211
2212 if (cells[row][col]->Type() != HCCELLTYPE_FLOOR ||
2213 cells[row][col]->Subtype() != floorSubtype)
2214 {
2215 // Substitutes the floor
2216 JDELETE(cells[row][col]);
2217 cells[row][col] = new HCFloorCell(&level.Theme().Floor(floorSubtype));
2218 cells[row][col]->Subtype(floorSubtype);
2219 cells[row][col]->Pos((s32)x, (s32)y);
2220 }
2221
2222 level.Map().BuildContFloor(row, col);
2223 level.Map().BuildCellLinkList();
2224 }
2225 }
2226 else
2227 {
2228 UpdateEraseFloor();
2229 }
2230 }
2231
2232 return true;
2233 }
2234
UpdateContFloor()2235 bool HCed::UpdateContFloor()
2236 {
2237 // Button down
2238 if (MouseOverMap())
2239 {
2240 if (MouseBt() & SDL_BUTTON_LEFT)
2241 {
2242 s32 row = level.Map().ToRow(MouseY()), col = level.Map().ToCol(MouseX());
2243
2244 // Put a blank cell border around the map, do no let edit those cells
2245 if (row > 0 && col > 0 && row < level.Map().Rows() - 1 && col < level.Map().Cols() - 1)
2246 {
2247 HCCell ***cells = level.Map().Cells();
2248 float x = cells[row][col]->X(), y = cells[row][col]->Y();
2249
2250 if (cells[row][col]->Type() != HCCELLTYPE_CONTFLOOR ||
2251 cells[row][col]->Subtype() != contFloorSubtype)
2252 {
2253 // Substitutes the floor
2254 JDELETE(cells[row][col]);
2255 cells[row][col] = new HCContFloor;
2256 cells[row][col]->Subtype(contFloorSubtype);
2257 level.Map().BuildContFloor(row, col);
2258 cells[row][col]->Pos((s32)x, (s32)y);
2259 level.Map().BuildCellLinkList();
2260 }
2261 }
2262 }
2263 else
2264 {
2265 UpdateEraseFloor();
2266 }
2267 }
2268
2269 return true;
2270 }
2271
UpdateLadder()2272 bool HCed::UpdateLadder()
2273 {
2274 // Button down
2275 if (MouseOverMap())
2276 {
2277 if (MouseBt() & SDL_BUTTON_LEFT)
2278 {
2279 s32 row = level.Map().ToRow(MouseY()), col = level.Map().ToCol(MouseX());
2280
2281 // Put a blank cell border around the map, do no let edit those cells
2282 if (row > 0 && col > 0 && row < level.Map().Rows() - 1 && col < level.Map().Cols() - 1)
2283 {
2284 HCCell ***cells = level.Map().Cells();
2285 float x = cells[row][col]->X(), y = cells[row][col]->Y();
2286
2287 if (cells[row][col]->Type() != HCCELLTYPE_LADDER ||
2288 cells[row][col]->Subtype() != ladderSubtype)
2289 {
2290 // Substitutes the ladder
2291 JDELETE(cells[row][col]);
2292 cells[row][col] = new HCLadderCell(&level.Theme().Ladder(ladderSubtype));
2293 cells[row][col]->Subtype(ladderSubtype);
2294 cells[row][col]->Pos((s32)x, (s32)y);
2295 level.Map().BuildCellLinkList();
2296 }
2297
2298 level.Map().BuildContFloor(row, col);
2299 }
2300 }
2301 else
2302 {
2303 UpdateEraseFloor();
2304 }
2305 }
2306
2307 return true;
2308 }
2309
UpdateBar()2310 bool HCed::UpdateBar()
2311 {
2312 // Button down
2313 if (MouseOverMap())
2314 {
2315 if (MouseBt() & SDL_BUTTON_LEFT)
2316 {
2317 s32 row = level.Map().ToRow(MouseY()), col = level.Map().ToCol(MouseX());
2318
2319 // Put a blank cell border around the map, do no let edit those cells
2320 if (row > 0 && col > 0 && row < level.Map().Rows() - 1 && col < level.Map().Cols() - 1)
2321 {
2322 HCCell ***cells = level.Map().Cells();
2323 float x = cells[row][col]->X(), y = cells[row][col]->Y();
2324
2325 if (cells[row][col]->Type() != HCCELLTYPE_BAR ||
2326 cells[row][col]->Subtype() != barSubtype)
2327 {
2328 // Substitutes the bar
2329 JDELETE(cells[row][col]);
2330 cells[row][col] = new HCBarCell(&level.Theme().Bar(barSubtype));
2331 cells[row][col]->Subtype(barSubtype);
2332 cells[row][col]->Pos((s32)x, (s32)y);
2333 }
2334
2335 level.Map().BuildContFloor(row, col);
2336 level.Map().BuildCellLinkList();
2337 }
2338 }
2339 else
2340 {
2341 UpdateEraseFloor();
2342 }
2343 }
2344
2345 return true;
2346 }
2347
UpdateBreak()2348 bool HCed::UpdateBreak()
2349 {
2350 // Button down
2351 if (MouseOverMap())
2352 {
2353 if (MouseBt() & SDL_BUTTON_LEFT)
2354 {
2355 s32 row = level.Map().ToRow(MouseY()), col = level.Map().ToCol(MouseX());
2356
2357 // Put a blank cell border around the map, do no let edit those cells
2358 if (row > 0 && col > 0 && row < level.Map().Rows() - 1 && col < level.Map().Cols() - 1)
2359 {
2360 HCCell ***cells = level.Map().Cells();
2361 float x = cells[row][col]->X(), y = cells[row][col]->Y();
2362
2363 if (cells[row][col]->Type() != HCCELLTYPE_BREAK ||
2364 cells[row][col]->Subtype() != breakSubtype)
2365 {
2366 // Substitutes the cell
2367 JDELETE(cells[row][col]);
2368 cells[row][col] = new HCBreak(level.Theme().Break(breakSubtype));
2369 cells[row][col]->Subtype(breakSubtype);
2370 cells[row][col]->Pos((s32)x, (s32)y);
2371 }
2372
2373 level.Map().BuildContFloor(row, col);
2374 level.Map().BuildCellLinkList();
2375 }
2376 }
2377 else
2378 {
2379 UpdateEraseFloor();
2380 }
2381 }
2382
2383 return true;
2384 }
2385
UpdateObject()2386 bool HCed::UpdateObject()
2387 {
2388 // Button down
2389 if (MouseOverMap())
2390 {
2391 if (MouseBt() & SDL_BUTTON_LEFT)
2392 {
2393 if (curObject != 0)
2394 {
2395 // An object is selected
2396 if (KeyMods() & KMOD_CTRL)
2397 {
2398 // Adjust to map cell
2399 s32 row = level.Map().ToRow(MouseY()), col = level.Map().ToCol(MouseX());
2400 curObject->Pos(level.Map().ToX(col), level.Map().ToY(row));
2401 }
2402 else
2403 {
2404 // Place the object right bellow the mouse cursor
2405 curObject->Pos(MouseX(), MouseY());
2406 }
2407 }
2408 else
2409 {
2410 // No selected object, create it
2411 AddObject(MouseX(), MouseY());
2412 }
2413 }
2414 else
2415 // Erase object
2416 if (MouseBt() & SDL_BUTTON_RIGHT)
2417 {
2418 // Delete the current object
2419 DeleteObject();
2420 }
2421 }
2422
2423 return true;
2424 }
2425
UpdateRope()2426 bool HCed::UpdateRope()
2427 {
2428 // Button down
2429 if (MouseOverMap())
2430 {
2431 if (MouseBt() & SDL_BUTTON_LEFT)
2432 {
2433 if (curRope != 0)
2434 {
2435 // A rope is selected
2436 if (KeyMods() & KMOD_CTRL)
2437 {
2438 // Adjust to map cell ('ceiling')
2439 s32 row = level.Map().ToRow(MouseY()), col = level.Map().ToCol(MouseX());
2440 curRope->Pos(level.Map().ToX(col) - level.Map().CellWidth()/2,
2441 level.Map().ToY(row) - level.Map().CellHeight() + 1);
2442 }
2443 else
2444 if (KeyMods() & KMOD_SHIFT)
2445 {
2446 // Adjust the amplitude
2447 s32 amplitude = abs(MouseX() - (s32)curRope->X());
2448 curRope->Init(ropePeriod, amplitude, curRope->Length(), level.Theme());
2449 }
2450 else
2451 if (KeyMods() & KMOD_ALT)
2452 {
2453 // Adjust the length
2454 s32 length = abs(MouseY() - (s32)curRope->Y());
2455 curRope->Init(ropePeriod, curRope->Amplitude(), length, level.Theme());
2456 }
2457 else
2458 {
2459 // Place the rope right bellow the mouse cursor
2460 curRope->Pos(MouseX(), MouseY());
2461 }
2462 }
2463 else
2464 {
2465 // No selected rope, create it
2466 AddRope(MouseX(), MouseY());
2467 }
2468 }
2469 else
2470 // Erase rope
2471 if (MouseBt() & SDL_BUTTON_RIGHT)
2472 {
2473 // Delete the current rope
2474 DeleteRope();
2475 }
2476 }
2477
2478 return true;
2479 }
2480
UpdateStart()2481 bool HCed::UpdateStart()
2482 {
2483 if (MouseOverMap())
2484 {
2485 if (MouseBt() & SDL_BUTTON_LEFT)
2486 {
2487 HCMap *m = &level.Map();
2488
2489 if ((KeyMods() & KMOD_CTRL))
2490 {
2491 s32 row = m->ToRow(MouseY()), col = m->ToCol(MouseX());
2492 level.character.Pos(m->ToX(col), m->ToY(row));
2493 }
2494 else
2495 {
2496 level.character.Pos(MouseX(), MouseY());
2497 }
2498 }
2499 }
2500
2501 return true;
2502 }
2503
UpdateExit()2504 bool HCed::UpdateExit()
2505 {
2506 if (MouseOverMap())
2507 {
2508 if (MouseBt() & SDL_BUTTON_LEFT)
2509 {
2510 HCMap *m = &level.Map();
2511 s32 row = m->ToRow(MouseY()), col = m->ToCol(MouseX());
2512 if (m->ExitRow() != row || m->ExitCol() != col)
2513 {
2514 m->ExitRow(row);
2515 m->ExitCol(col);
2516 level.levelExit.Init(m, 100);
2517 }
2518 }
2519 }
2520
2521 return true;
2522 }
2523
UpdateEnemy()2524 bool HCed::UpdateEnemy()
2525 {
2526 // Button down
2527 if (MouseOverMap())
2528 {
2529 if (MouseBt() & SDL_BUTTON_LEFT)
2530 {
2531 if (curEnemy != 0)
2532 {
2533 // An enemy is selected
2534 if (KeyMods() & KMOD_CTRL)
2535 {
2536 // Adjust to map cell
2537 s32 row = level.Map().ToRow(MouseY()), col = level.Map().ToCol(MouseX());
2538 curEnemy->Pos(level.Map().ToX(col), level.Map().ToY(row));
2539 }
2540 else
2541 {
2542 // Place the enemy right bellow the mouse cursor
2543 curEnemy->Pos(MouseX(), MouseY());
2544 }
2545
2546 curEnemy->Param1(enemyParam1);
2547 curEnemy->Param2(enemyParam2);
2548 }
2549 else
2550 {
2551 // No selected enemy, create it
2552 AddEnemy(MouseX(), MouseY());
2553 }
2554 }
2555 else
2556 // Erase enemy
2557 if (MouseBt() & SDL_BUTTON_RIGHT)
2558 {
2559 // Delete the current enemy
2560 DeleteEnemy();
2561 }
2562 }
2563
2564 return true;
2565 }
2566
UpdateSave()2567 bool HCed::UpdateSave()
2568 {
2569
2570
2571 return true;
2572 }
2573
UpdateOpenStory()2574 bool HCed::UpdateOpenStory()
2575 {
2576 switch (inputNewStory)
2577 {
2578 case 0:
2579 // Only updates if not waiting for user input on new story
2580 menuOpenStory->Update();
2581 break;
2582
2583 case 2:
2584 menuTheme->Update();
2585 break;
2586
2587 default:
2588 break;
2589 }
2590
2591 return true;
2592 }
2593
UpdateAppExit()2594 bool HCed::UpdateAppExit()
2595 {
2596
2597
2598 return true;
2599 }
2600
OnFilenameChange()2601 void HCed::OnFilenameChange()
2602 {
2603 JString strTitle;
2604 strTitle.Format("%s - HCed v1.0", filename.Str());
2605 Title(strTitle);
2606 }
2607
OnGravityChange()2608 void HCed::OnGravityChange()
2609 {
2610 SDL_Color c;
2611
2612 float maxG = theApp->level.Map().CellHeight()/5.0f;
2613
2614 // Forces the gravity value to be between maxG and -maxG
2615 float g = theApp->level.Map().Gravity();
2616 JClamp(g, -maxG, maxG);
2617 theApp->level.Map().Gravity(g);
2618
2619 // Positive values tend to green, negative ones tend to red.
2620 c.unused = 255;
2621 c.b = 0;
2622 c.g = (u8)(255 * ((g + maxG)/(2.0f * maxG)));
2623 c.r = 255 - c.g;
2624
2625 JDELETE(theApp->imgGravity);
2626 theApp->imgGravity = theApp->fontSmall.PrintfBlended(JFONTALIGN_LEFT, c, "g: %1.1f", theApp->level.Map().Gravity());
2627 }
2628
OnCharVxChange()2629 void HCed::OnCharVxChange()
2630 {
2631 SDL_Color c;
2632
2633 float maxVx = theApp->level.Map().CellWidth() - 1;
2634
2635 // Forces the Vx value to be between maxVx and 0
2636 float v = theApp->level.character.MaxVeloccity().x;
2637 JClamp(v, 0.0f, maxVx);
2638 theApp->level.character.MaxVeloccity().x = v;
2639
2640 // Greater values tend to yellow, small ones tend to grey.
2641 c.unused = 255;
2642 c.b = 127 + (u8)(255 * (v/(2.0f * maxVx)));
2643 c.r = c.g;
2644 c.g = 127;
2645
2646 JDELETE(theApp->imgCharVx);
2647 theApp->imgCharVx = theApp->fontSmall.PrintfBlended(JFONTALIGN_LEFT, c, "vX:%1.1f", theApp->level.character.MaxVeloccity().x);
2648 }
2649
OnCharVyChange()2650 void HCed::OnCharVyChange()
2651 {
2652 SDL_Color c;
2653
2654 float maxVy = theApp->level.Map().CellWidth() - 1;
2655
2656 // Forces the Vy value to be between maxVy and 0
2657 float v = theApp->level.character.MaxVeloccity().y;
2658 JClamp(v, 0.0f, maxVy);
2659 theApp->level.character.MaxVeloccity().y = v;
2660
2661 // Greater values tend to yellow, small ones tend to grey.
2662 c.unused = 255;
2663 c.g = 127 + (u8)(255 * (v/(2.0f * maxVy)));
2664 c.r = c.g;
2665 c.b = 127;
2666
2667 JDELETE(theApp->imgCharVy);
2668 theApp->imgCharVy = theApp->fontSmall.PrintfBlended(JFONTALIGN_LEFT, c, "vY:%1.1f", theApp->level.character.MaxVeloccity().y);
2669 }
2670
OnCharJumpRowsChange()2671 void HCed::OnCharJumpRowsChange()
2672 {
2673 SDL_Color c;
2674
2675 s32 maxJ = theApp->level.Map().Rows();
2676
2677 // Forces the jump value to be between maxJ and 0
2678 s32 j = theApp->level.character.MaxJumpRows();
2679 JClamp(j, 0, maxJ);
2680 theApp->level.character.MaxJumpRows(j);
2681
2682 // Greater values tend to light blue, small ones tend to dark blue.
2683 c.unused = 255;
2684 c.b = 127 + (u8)(255 * (j/(2.0f * maxJ)));
2685 c.r = c.b/2;
2686 c.g = 127;
2687
2688 JDELETE(theApp->imgCharJumpRows);
2689 theApp->imgCharJumpRows = theApp->fontSmall.PrintfBlended(JFONTALIGN_LEFT, c, "Jump:%d", theApp->level.character.MaxJumpRows());
2690 }
2691
OnMapSizeChange()2692 void HCed::OnMapSizeChange()
2693 {
2694 SDL_Color c;
2695
2696 c.unused = 255;
2697 c.r = 0xff;
2698 c.b = 0xcc;
2699 c.g = 0x00;
2700
2701 JDELETE(theApp->imgMapSize);
2702 theApp->imgMapSize = theApp->fontSmall.PrintfBlended(JFONTALIGN_LEFT,
2703 c,
2704 "Map: %dx%d",
2705 theApp->level.map.Rows(),
2706 theApp->level.map.Cols());
2707 }
2708
OnFloor(void * data)2709 void HCed::OnFloor(void *data)
2710 {
2711 theApp->state = HCEDSTATE_FLOOR;
2712 theApp->imgMouse = &theApp->level.Theme().Floor(theApp->floorSubtype);
2713 }
2714
OnContFloor(void * data)2715 void HCed::OnContFloor(void *data)
2716 {
2717 theApp->state = HCEDSTATE_CONTFLOOR;
2718 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2719 }
2720
OnLadder(void * data)2721 void HCed::OnLadder(void *data)
2722 {
2723 theApp->state = HCEDSTATE_LADDER;
2724 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2725 }
2726
OnBar(void * data)2727 void HCed::OnBar(void *data)
2728 {
2729 theApp->state = HCEDSTATE_BAR;
2730 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2731 }
2732
OnBreak(void * data)2733 void HCed::OnBreak(void *data)
2734 {
2735 theApp->state = HCEDSTATE_BREAK;
2736 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2737 }
2738
OnObject(void * data)2739 void HCed::OnObject(void *data)
2740 {
2741 theApp->state = HCEDSTATE_OBJECT;
2742 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2743 }
2744
OnRope(void * data)2745 void HCed::OnRope(void *data)
2746 {
2747 theApp->state = HCEDSTATE_ROPE;
2748 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2749 }
2750
OnStart(void * data)2751 void HCed::OnStart(void *data)
2752 {
2753 theApp->state = HCEDSTATE_START;
2754 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2755 }
2756
OnExit(void * data)2757 void HCed::OnExit(void *data)
2758 {
2759 theApp->state = HCEDSTATE_EXIT;
2760 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2761 }
2762
OnEnemy(void * data)2763 void HCed::OnEnemy(void *data)
2764 {
2765 theApp->state = HCEDSTATE_ENEMY;
2766 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2767 }
2768
OnSave(void * data)2769 void HCed::OnSave(void *data)
2770 {
2771 HCedState old = theApp->state;
2772 theApp->state = HCEDSTATE_SAVE;
2773 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2774 theApp->Draw();
2775
2776 // Save in the command line-given file.
2777 JRW file;
2778
2779 if (file.Create(theApp->filename, "wb"))
2780 {
2781 fprintf(stderr, "Saving the level to %s.\n", theApp->filename.Str());
2782
2783 if (0 != theApp->level.Save(file))
2784 {
2785 fprintf(stderr, "Could not save the level to %s.\n", theApp->filename.Str());
2786 }
2787
2788 file.Close();
2789 }
2790 else
2791 {
2792 fprintf(stderr, "Could not open file %s.\n", theApp->filename.Str());
2793 }
2794
2795 // Return to the old state
2796 theApp->state = old;
2797 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2798 }
2799
OnOpenStory(void * data)2800 void HCed::OnOpenStory(void *data)
2801 {
2802 theApp->state = HCEDSTATE_OPENSTORY;
2803 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2804 theApp->strNewStoryName = "";
2805 }
2806
OnSelectStory(void * data)2807 void HCed::OnSelectStory(void *data)
2808 {
2809 if ((long)data < 0)
2810 {
2811 theApp->inputNewStory = HCED_INPUT_STORY;
2812
2813 // Updates the input string
2814 JDELETE(theApp->imgNewStory);
2815 SDL_Color fg = {0xff, 0xcc, 0x00, 0x00};
2816 SDL_Color bg = {0x00, 0x00, 0x00, 0x00};
2817 theApp->imgNewStory = theApp->fontLarge.RenderTextShaded("Name: ", fg, bg);
2818 }
2819 else
2820 {
2821 theApp->OnFloor(0);
2822 theApp->filename = JString(theApp->storyDir) + JString(HCUtil::Stories()[(long)data]) + "/level001.hlv";
2823
2824 if (0 != theApp->NewLevel())
2825 {
2826 fprintf(stderr, "Error loading %s\n", theApp->filename.Str());
2827 return;
2828 }
2829
2830 theApp->storyName = HCUtil::Stories()[(long)data];
2831 theApp->levelNumber = 1;
2832 }
2833 }
2834
OnSelectTheme(void * data)2835 void HCed::OnSelectTheme(void *data)
2836 {
2837 s32 rt = HCUtil::CreateStory(theApp->strNewStoryName);
2838
2839 switch (rt)
2840 {
2841 case 0:
2842 {
2843 // Creates new story
2844 JString oldName = theApp->filename;
2845 JString oldTheme = theApp->themeName;
2846 s32 oldLevel = theApp->levelNumber;
2847 theApp->filename = JString(theApp->storyDir) + JString(theApp->strNewStoryName) + JString("/level001.hlv");
2848 theApp->themeName = HCUtil::Themes()[(long)data];
2849 theApp->levelNumber = 1;
2850
2851 if (0 != theApp->NewLevel())
2852 {
2853 fprintf(stderr, "Error initializing new level %s\n", (const char *)theApp->filename);
2854 theApp->filename = oldName;
2855 theApp->themeName = oldTheme;
2856 theApp->levelNumber = oldLevel;
2857 }
2858 else
2859 {
2860 // All went right
2861 JDELETE(theApp->imgNewStory);
2862 theApp->storyName = theApp->strNewStoryName;
2863 theApp->inputNewStory = 0;
2864 }
2865 }
2866 break;
2867
2868 case 1: fprintf(stderr, "Story %s already exists, give it a different name, please.\n", (const char *)theApp->strNewStoryName); break;
2869 case 2: fprintf(stderr, "Directory 'stories' could not be found, check installation.\n"); break;
2870 case 3: fprintf(stderr, "Couldn't create story. Check permissions for %s.\n", (const char *)theApp->strNewStoryName); break;
2871 default:
2872 break;
2873 }
2874 }
2875
OnAppExit(void * data)2876 void HCed::OnAppExit(void *data)
2877 {
2878 theApp->state = HCEDSTATE_APPEXIT;
2879 theApp->imgMouse = &theApp->imgMenu[theApp->state];
2880 theApp->Exit();
2881 }
2882
OnMainSubtype(void * data)2883 void HCed::OnMainSubtype(void *data)
2884 {
2885 theApp->level.character.subtype = (long)data;
2886 theApp->level.character.Init(theApp->level.theme.MainChar((long)data), &theApp->level.map);
2887 }
2888
OnFloorSubtype(void * data)2889 void HCed::OnFloorSubtype(void *data)
2890 {
2891 theApp->floorSubtype = (long)data;
2892 theApp->imgMouse = &(theApp->level.Theme().Floor(theApp->floorSubtype));
2893 }
2894
OnContFloorSubtype(void * data)2895 void HCed::OnContFloorSubtype(void *data)
2896 {
2897 theApp->contFloorSubtype = (long)data;
2898 theApp->imgMouse = &(theApp->level.Theme().ContFloor(theApp->contFloorSubtype)[HCFDT_I]);
2899 }
2900
OnBarSubtype(void * data)2901 void HCed::OnBarSubtype(void *data)
2902 {
2903 theApp->barSubtype = (long)data;
2904 theApp->imgMouse = &(theApp->level.Theme().Bar(theApp->barSubtype));
2905 }
2906
OnLadderSubtype(void * data)2907 void HCed::OnLadderSubtype(void *data)
2908 {
2909 theApp->ladderSubtype = (long)data;
2910 theApp->imgMouse = &(theApp->level.Theme().Ladder(theApp->ladderSubtype));
2911 }
2912
OnBreakSubtype(void * data)2913 void HCed::OnBreakSubtype(void *data)
2914 {
2915 theApp->breakSubtype = (long)data;
2916 theApp->imgMouse = (JImage *)(theApp->level.Theme().Break(theApp->breakSubtype)[HCBDT_NORMAL].Frame(0));
2917 }
2918
OnObjectSubtype(void * data)2919 void HCed::OnObjectSubtype(void *data)
2920 {
2921 theApp->objectSubtype = (long)data;
2922 theApp->imgMouse = (JImage *)(theApp->level.Theme().Object(theApp->objectSubtype)[HCODT_NORMAL].Frame(0));
2923 }
2924
OnRopeSubtype(void * data)2925 void HCed::OnRopeSubtype(void *data)
2926 {
2927 theApp->ropeSubtype = (long)data;
2928 theApp->imgMouse = &(theApp->level.Theme().Rope(theApp->ropeSubtype)[HCRDT_TOP]);
2929 }
2930
OnRopePeriod(void * data)2931 void HCed::OnRopePeriod(void *data)
2932 {
2933 theApp->ropePeriod = 0.5f + (0.25f * float((long)data));
2934 }
2935
OnEnemySubtype(void * data)2936 void HCed::OnEnemySubtype(void *data)
2937 {
2938 theApp->enemySubtype = (long)data;
2939 theApp->imgMouse = (JImage *)(theApp->GetEnemySprites(theApp->enemyType, theApp->enemySubtype)[HCCDT_STOP].Frame(0));
2940 }
2941
OnEnemyType(void * data)2942 void HCed::OnEnemyType(void *data)
2943 {
2944 // 0 always is a valid subtype so init to that
2945 theApp->enemySubtype = 0;
2946
2947 theApp->enemyType = (HCEnemyType)(long)data;
2948 theApp->imgMouse = (JImage *)(theApp->GetEnemySprites(theApp->enemyType, theApp->enemySubtype)[HCCDT_STOP].Frame(0));
2949 }
2950
OnEnemyParam1(void * data)2951 void HCed::OnEnemyParam1(void *data)
2952 {
2953 theApp->enemyParam1 = (long)data;
2954 }
2955
OnEnemyParam2(void * data)2956 void HCed::OnEnemyParam2(void *data)
2957 {
2958 theApp->enemyParam2 = (long)data;
2959 }
2960
MouseOverMap()2961 bool HCed::MouseOverMap()
2962 {
2963 return (MouseX() > HCED_MARGIN &&
2964 MouseX() < (level.Map().X() + (level.Map().Cols() * level.Map().CellWidth())) &&
2965 MouseY() > HCED_MARGIN &&
2966 MouseY() < (level.Map().Y() + (level.Map().Rows() * level.Map().CellHeight())));
2967 }
2968
AddObject(s32 x,s32 y)2969 void HCed::AddObject(s32 x, s32 y)
2970 {
2971 curObject = new HCObject;
2972
2973 // Update the level objects
2974 HCObject **tmp = level.objects;
2975
2976 level.objects = new HCObject *[level.numObjects + 1];
2977
2978 // Copies the old elements
2979 s32 i = 0;
2980 for (i = 0; i < level.numObjects; ++i)
2981 {
2982 level.objects[i] = tmp[i];
2983 }
2984
2985 // New and last element
2986 level.objects[i] = curObject;
2987
2988 ++level.numObjects;
2989
2990 curObject->Subtype(objectSubtype);
2991 curObject->Init(&level.Theme());
2992 curObject->Pos(x, y);
2993
2994 // If it exists, deletes the old array of pointers (not the pointers inside)
2995 JDELETE_ARRAY(tmp);
2996 }
2997
AddRope(s32 x,s32 y)2998 void HCed::AddRope(s32 x, s32 y)
2999 {
3000 curRope = new HCRope();
3001 curRope->Init(ropePeriod, 0, 0, level.Theme());
3002 curRope->Subtype(ropeSubtype);
3003
3004 // Update the level ropes
3005 HCRope **tmp = level.ropes;
3006
3007 level.ropes = new HCRope *[level.numRopes + 1];
3008
3009 // Copies the old elements
3010 s32 i = 0;
3011 for (i = 0; i < level.numRopes; ++i)
3012 {
3013 level.ropes[i] = tmp[i];
3014 }
3015
3016 // New and last element
3017 level.ropes[i] = curRope;
3018
3019 ++level.numRopes;
3020
3021 curRope->Pos(x, y);
3022
3023 // If it exists, deletes the old array of pointers (not the pointers inside)
3024 JDELETE_ARRAY(tmp);
3025 }
3026
AddEnemy(s32 x,s32 y)3027 void HCed::AddEnemy(s32 x, s32 y)
3028 {
3029 switch (enemyType)
3030 {
3031 default:
3032 case HCENEMYTYPE_BALL:
3033 curEnemy = new HCEnemyBall();
3034 curEnemy->Init(level.Theme().Ball(enemySubtype), &level.Map());
3035 break;
3036
3037 case HCENEMYTYPE_RANDOM:
3038 curEnemy = new HCEnemyRandom();
3039 curEnemy->Init(level.Theme().Random(enemySubtype), &level.Map());
3040 break;
3041
3042 case HCENEMYTYPE_STATIC:
3043 curEnemy = new HCEnemyStatic();
3044 curEnemy->Init(level.Theme().Static(enemySubtype), &level.Map());
3045 break;
3046
3047 case HCENEMYTYPE_MAKER:
3048 curEnemy = new HCEnemyMaker();
3049 curEnemy->Init(level.Theme().Maker(enemySubtype), &level.Map());
3050 break;
3051
3052 case HCENEMYTYPE_CHASER:
3053 curEnemy = new HCEnemyChaser();
3054 curEnemy->Init(level.Theme().Chaser(enemySubtype), &level.Map());
3055 break;
3056 }
3057
3058 curEnemy->Subtype(enemySubtype);
3059 curEnemy->Param1(enemyParam1);
3060 curEnemy->Param2(enemyParam2);
3061
3062 // Update the level enemies
3063 HCEnemy **tmp = level.enemies;
3064
3065 level.enemies = new HCEnemy *[level.numEnemies + 1];
3066
3067 // Copies the old elements
3068 s32 i = 0;
3069 for (i = 0; i < level.numEnemies; ++i)
3070 {
3071 level.enemies[i] = tmp[i];
3072 }
3073
3074 // New and last element
3075 level.enemies[i] = curEnemy;
3076
3077 ++level.numEnemies;
3078
3079 curEnemy->Pos(x, y);
3080
3081 // If it exists, deletes the old array of pointers (not the pointers inside)
3082 JDELETE_ARRAY(tmp);
3083 }
3084
DeleteObject()3085 void HCed::DeleteObject()
3086 {
3087 if (curObject)
3088 {
3089 // Update the level objects
3090 HCObject **tmp = level.objects;
3091
3092 level.objects = new HCObject *[level.numObjects - 1];
3093
3094 // Copies all but the selected object
3095 for (s32 i = 0, newI = 0; i < level.numObjects; ++i, ++newI)
3096 {
3097 if (tmp[i] != curObject)
3098 {
3099 level.objects[newI] = tmp[i];
3100 }
3101 else
3102 {
3103 // Note that tmp is 1 element larger than level.objects
3104 // we have to delay 1 its index after curObject has been found inside of it
3105 --newI;
3106 }
3107 }
3108
3109 --level.numObjects;
3110
3111 // If it exists, deletes the old array of pointers (not the pointers inside)
3112 JDELETE_ARRAY(tmp);
3113
3114 delete curObject;
3115 curObject = 0;
3116 }
3117 }
3118
DeleteRope()3119 void HCed::DeleteRope()
3120 {
3121 if (curRope)
3122 {
3123 // Update the level ropes
3124 HCRope **tmp = level.ropes;
3125
3126 level.ropes = new HCRope *[level.numRopes - 1];
3127
3128 // Copies all but the selected rope
3129 for (s32 i = 0, newI = 0; i < level.numRopes; ++i, ++newI)
3130 {
3131 if (tmp[i] != curRope)
3132 {
3133 level.ropes[newI] = tmp[i];
3134 }
3135 else
3136 {
3137 // Note that tmp is 1 element larger than level.ropes
3138 // we have to delay 1 its index after curRope has been found inside of it
3139 --newI;
3140 }
3141 }
3142
3143 --level.numRopes;
3144
3145 // If it exists, deletes the old array of pointers (not the pointers inside)
3146 JDELETE_ARRAY(tmp);
3147
3148 delete curRope;
3149 curRope = 0;
3150 }
3151 }
3152
DeleteEnemy()3153 void HCed::DeleteEnemy()
3154 {
3155 if (curEnemy)
3156 {
3157 // Update the level enemies
3158 HCEnemy **tmp = level.enemies;
3159
3160 level.enemies = new HCEnemy *[level.numEnemies - 1];
3161
3162 // Copies all but the selected enemy
3163 for (s32 i = 0, newI = 0; i < level.numEnemies; ++i, ++newI)
3164 {
3165 if (tmp[i] != curEnemy)
3166 {
3167 level.enemies[newI] = tmp[i];
3168 }
3169 else
3170 {
3171 // Note that tmp is 1 element larger than level.enemies
3172 // we have to delay 1 its index after curEnemy has been found inside of it
3173 --newI;
3174 }
3175 }
3176
3177 --level.numEnemies;
3178
3179 // If it exists, deletes the old array of pointers (not the pointers inside)
3180 JDELETE_ARRAY(tmp);
3181
3182 delete curEnemy;
3183 curEnemy = 0;
3184 }
3185 }
3186
GetEnemySprites(HCEnemyType t,s32 s)3187 JImageSprite * HCed::GetEnemySprites(HCEnemyType t, s32 s)
3188 {
3189 switch (t)
3190 {
3191 case HCENEMYTYPE_BALL:
3192 return level.theme.Ball(s);
3193
3194 case HCENEMYTYPE_STATIC:
3195 return level.theme.Static(s);
3196
3197 case HCENEMYTYPE_MAKER:
3198 return level.theme.Maker(s);
3199
3200 case HCENEMYTYPE_RANDOM:
3201 return level.theme.Random(s);
3202
3203 case HCENEMYTYPE_CHASER:
3204 return level.theme.Chaser(s);
3205
3206 default:
3207 break;
3208 }
3209
3210 return 0;
3211 }
3212
GetNumEnemySprites(HCEnemyType t)3213 s32 HCed::GetNumEnemySprites(HCEnemyType t)
3214 {
3215 switch (t)
3216 {
3217 case HCENEMYTYPE_BALL:
3218 return level.theme.NumBalls();
3219
3220 case HCENEMYTYPE_STATIC:
3221 return level.theme.NumStatics();
3222
3223 case HCENEMYTYPE_MAKER:
3224 return level.theme.NumMakers();
3225
3226 case HCENEMYTYPE_RANDOM:
3227 return level.theme.NumRandoms();
3228
3229 case HCENEMYTYPE_CHASER:
3230 return level.theme.NumChasers();
3231
3232 default:
3233 break;
3234 }
3235
3236 return 0;
3237 }
3238
DestroyMainSubtypeMenu()3239 void HCed::DestroyMainSubtypeMenu()
3240 {
3241 if (menuMainSubtype)
3242 {
3243 JImage *img;
3244 JTree<JImageMenuEntry *>::Iterator *it;
3245 it = menuMainSubtype->Menu();
3246 it->Root();
3247
3248 do
3249 {
3250 if (it->Data())
3251 {
3252 img = it->Data()->Image();
3253 JDELETE(img);
3254 img = it->Data()->HiImage();
3255 JDELETE(img);
3256 }
3257 } while (it->Next());
3258
3259 delete menuMainSubtype;
3260 }
3261 }
3262
DestroyBreakSubtypeMenu()3263 void HCed::DestroyBreakSubtypeMenu()
3264 {
3265 if (menuBreakSubtype)
3266 {
3267 JImage *img;
3268 JTree<JImageMenuEntry *>::Iterator *it;
3269 it = menuBreakSubtype->Menu();
3270 it->Root();
3271
3272 do
3273 {
3274 if (it->Data())
3275 {
3276 img = it->Data()->Image();
3277 JDELETE(img);
3278 img = it->Data()->HiImage();
3279 JDELETE(img);
3280 }
3281 } while (it->Next());
3282
3283 delete menuBreakSubtype;
3284 }
3285 }
3286
DestroyObjectSubtypeMenu()3287 void HCed::DestroyObjectSubtypeMenu()
3288 {
3289 if (menuObjectSubtype)
3290 {
3291 JImage *img;
3292 JTree<JImageMenuEntry *>::Iterator *it;
3293 it = menuObjectSubtype->Menu();
3294 it->Root();
3295
3296 do
3297 {
3298 if (it->Data())
3299 {
3300 img = it->Data()->Image();
3301 JDELETE(img);
3302 img = it->Data()->HiImage();
3303 JDELETE(img);
3304 }
3305 } while (it->Next());
3306
3307 delete menuObjectSubtype;
3308 }
3309 }
3310
DestroyEnemySubtypeMenu()3311 void HCed::DestroyEnemySubtypeMenu()
3312 {
3313 JImage *img;
3314 JTree<JImageMenuEntry *>::Iterator *it;
3315
3316 for (s32 n = 0; n < HCENEMYTYPE_COUNT; ++n)
3317 {
3318 if (menuEnemySubtype[n])
3319 {
3320 it = menuEnemySubtype[n]->Menu();
3321 it->Root();
3322
3323 do
3324 {
3325 if (it->Data())
3326 {
3327 img = it->Data()->Image();
3328 JDELETE(img);
3329 img = it->Data()->HiImage();
3330 JDELETE(img);
3331 }
3332 } while (it->Next());
3333
3334 delete menuEnemySubtype[n];
3335 }
3336 }
3337 }
3338
DestroyLevelMenus()3339 void HCed::DestroyLevelMenus()
3340 {
3341 DestroyMainSubtypeMenu();
3342 DestroyBreakSubtypeMenu();
3343 DestroyObjectSubtypeMenu();
3344 DestroyEnemySubtypeMenu();
3345 }
3346
~HCed()3347 HCed::~HCed()
3348 {
3349 JDELETE(menuMain);
3350 JDELETE(menuEnemyType);
3351 JDELETE(menuEnemyParam1);
3352 JDELETE(menuEnemyParam2);
3353 JDELETE(menuRopePeriod);
3354 JDELETE(menuFloorSubtype);
3355 JDELETE(menuContFloorSubtype);
3356 JDELETE(menuBarSubtype);
3357 JDELETE(menuLadderSubtype);
3358 JDELETE(menuRopeSubtype);
3359 JDELETE(menuOpenStory);
3360 JDELETE(menuTheme);
3361
3362 DestroyLevelMenus();
3363
3364 JDELETE(imgGravity);
3365 JDELETE(imgCharVx);
3366 JDELETE(imgCharVy);
3367 JDELETE(imgCharJumpRows);
3368 JDELETE(imgMapSize);
3369 }
3370
main(s32 argc,char ** argv)3371 int main(s32 argc, char **argv)
3372 {
3373 HCed editor;
3374
3375 theApp = &editor;
3376
3377 if (!editor.Init(argc, argv))
3378 {
3379 fprintf(stderr, "Error launching the editor.\n");
3380
3381 return -1;
3382 }
3383
3384 return editor.MainLoop();
3385 }
3386