2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 /* this stores the caves. */
25 /* the data of the caveset: name, highscore, max number of lives, etc. */
26 GdCavesetData *gd_caveset_data;
28 /* is set to true, when the caveset was edited since the last save. */
29 boolean gd_caveset_edited;
31 /* last selected-to-play cave */
32 int gd_caveset_last_selected;
33 int gd_caveset_last_selected_level;
35 /* list of possible extensions which can be opened */
36 char *gd_caveset_extensions[] =
46 #define CAVESET_OFFSET(property) (STRUCT_OFFSET(GdCavesetData, property))
48 const GdStructDescriptor gd_caveset_properties[] =
51 {"", GD_TAB, 0, N_("Caveset data")},
52 {"Name", GD_TYPE_STRING, 0, N_("Name"), CAVESET_OFFSET(name), 1, N_("Name of the game")},
53 {"Description", GD_TYPE_STRING, 0, N_("Description"), CAVESET_OFFSET(description), 1, N_("Some words about the game")},
54 {"Author", GD_TYPE_STRING, 0, N_("Author"), CAVESET_OFFSET(author), 1, N_("Name of author")},
55 {"Date", GD_TYPE_STRING, 0, N_("Date"), CAVESET_OFFSET(date), 1, N_("Date of creation")},
56 {"WWW", GD_TYPE_STRING, 0, N_("WWW"), CAVESET_OFFSET(www), 1, N_("Web page or e-mail address")},
57 {"Difficulty", GD_TYPE_STRING, 0, N_("Difficulty"), CAVESET_OFFSET(difficulty), 1, N_("Difficulty (informative)")},
59 {"Lives", GD_TYPE_INT, 0, N_("Initial lives"), CAVESET_OFFSET(initial_lives), 1, N_("Number of lives you get at game start."), 3, 9},
60 {"Lives", GD_TYPE_INT, 0, N_("Maximum lives"), CAVESET_OFFSET(maximum_lives), 1, N_("Maximum number of lives you can have by collecting bonus points."), 3, 99},
61 {"BonusLife", GD_TYPE_INT, 0, N_("Bonus life score"), CAVESET_OFFSET(bonus_life_score), 1, N_("Number of points to collect for a bonus life."), 100, 5000},
63 {"Story", GD_TYPE_LONGSTRING, 0, N_("Story"), CAVESET_OFFSET(story), 1, N_("Long description of the game.")},
64 {"Remark", GD_TYPE_LONGSTRING, 0, N_("Remark"), CAVESET_OFFSET(remark), 1, N_("Remark (informative).")},
66 {"TitleScreen", GD_TYPE_LONGSTRING, GD_DONT_SHOW_IN_EDITOR, N_("Title screen"), CAVESET_OFFSET(title_screen), 1, N_("Title screen image")},
67 {"TitleScreenScroll", GD_TYPE_LONGSTRING, GD_DONT_SHOW_IN_EDITOR, N_("Title screen, scrolling"), CAVESET_OFFSET(title_screen_scroll), 1, N_("Scrolling background for title screen image")},
72 static GdPropertyDefault caveset_defaults[] =
75 {CAVESET_OFFSET(initial_lives), 3},
76 {CAVESET_OFFSET(maximum_lives), 9},
77 {CAVESET_OFFSET(bonus_life_score), 500},
82 GdCavesetData *gd_caveset_data_new(void)
86 data = checked_calloc(sizeof(GdCavesetData));
88 gd_struct_set_defaults_from_array(data, gd_caveset_properties, caveset_defaults);
93 void gd_caveset_data_free(GdCavesetData *data)
98 for (i = 0; gd_caveset_properties[i].identifier != NULL; i++)
99 if (gd_caveset_properties[i].type == GD_TYPE_LONGSTRING)
100 checked_free(STRUCT_MEMBER(char *, data, gd_caveset_properties[i].offset));
105 /******************************************************************************
107 * Misc caveset functions
111 /** Clears all caves in the caveset. also to be called at application start */
112 void gd_caveset_clear(void)
116 list_foreach(gd_caveset, (list_fn) gd_cave_free, NULL);
117 list_free(gd_caveset);
123 free(gd_caveset_data);
124 gd_caveset_data = NULL;
127 /* always newly create this */
128 /* create pseudo cave containing default values */
129 gd_caveset_data = gd_caveset_data_new();
130 gd_strcpy(gd_caveset_data->name, _("New caveset"));
133 /* return number of caves currently in memory. */
134 int gd_caveset_count(void)
136 return list_length(gd_caveset);
139 /* return index of first selectable cave */
140 static int caveset_first_selectable_cave_index(void)
145 for (i = 0, iter = gd_caveset; iter != NULL; i++, iter = iter->next)
147 GdCave *cave = (GdCave *)iter->data;
149 if (cave->selectable)
153 Warn("no selectable cave in caveset!");
155 /* and return the first one. */
159 /* return a cave identified by its index */
160 GdCave *gd_return_nth_cave(const int cave)
162 return list_nth_data(gd_caveset, cave);
165 /* get a selected cave from the loaded caveset (original, unmodified cave) */
166 GdCave *gd_get_original_cave_from_caveset(const int cave)
168 /* get specified cave from caveset already stored in memory */
169 GdCave *original_cave = gd_return_nth_cave(cave);
171 return original_cave;
174 /* get a selected cave from the loaded caveset (cave prepared for playing) */
175 GdCave *gd_get_prepared_cave_from_caveset(const int cave, const int level)
177 /* get specified cave from caveset already stored in memory */
178 GdCave *original_cave = gd_return_nth_cave(cave);
180 /* get prepared cave from original cave */
181 GdCave *prepared_cave = gd_get_prepared_cave(original_cave, level);
183 return prepared_cave;
186 /* get a cave prepared for playing from a given original, unmodified cave (with seed) */
187 GdCave *gd_get_prepared_cave(const GdCave *original_cave, const int level)
189 /* get rendered cave using the selected seed for playing */
190 GdCave *prepared_cave = gd_cave_new_rendered(original_cave, level, game_bd.random_seed);
192 /* initialize some cave variables (like player position) */
193 gd_cave_setup_for_game(prepared_cave);
195 return prepared_cave;
198 /* colors: 4: purple 3: ciklamen 2: orange 1: blue 0: green */
199 static GdElement brc_import_table[] =
202 O_SPACE, O_DIRT, O_BRICK, O_MAGIC_WALL, O_PRE_OUTBOX, O_OUTBOX, O_UNKNOWN, O_STEEL,
203 O_H_EXPANDING_WALL, O_H_EXPANDING_WALL /* scanned */, O_FIREFLY_1 /* scanned */, O_FIREFLY_1 /* scanned */, O_FIREFLY_1, O_FIREFLY_2, O_FIREFLY_3, O_FIREFLY_4,
206 O_BUTTER_1 /* scanned */, O_BUTTER_1 /* scanned */, O_BUTTER_1, O_BUTTER_2, O_BUTTER_3, O_BUTTER_4, O_PLAYER, O_PLAYER /* scanned */,
207 O_STONE, O_STONE /* scanned */, O_STONE_F, O_STONE_F /* scanned */, O_DIAMOND, O_DIAMOND /* scanned */, O_DIAMOND_F, O_DIAMOND_F /* scanned */,
210 O_NONE /* WILL_EXPLODE_THING */, O_EXPLODE_1, O_EXPLODE_2, O_EXPLODE_3, O_EXPLODE_4, O_EXPLODE_5, O_NONE /* WILL EXPLODE TO DIAMOND_THING */, O_PRE_DIA_1,
211 O_PRE_DIA_2, O_PRE_DIA_3, O_PRE_DIA_4, O_PRE_DIA_5, O_AMOEBA, O_AMOEBA /* scanned */, O_SLIME, O_NONE,
214 O_CLOCK, O_NONE /* clock eaten */, O_INBOX, O_PRE_PL_1, O_PRE_PL_2, O_PRE_PL_3, O_NONE, O_NONE,
215 O_NONE, O_NONE, O_V_EXPANDING_WALL, O_NONE, O_VOODOO, O_UNKNOWN, O_EXPANDING_WALL, O_EXPANDING_WALL /* sc */,
218 O_FALLING_WALL, O_FALLING_WALL_F, O_FALLING_WALL_F /* scanned */, O_UNKNOWN, O_ACID, O_ACID /* scanned */, O_NITRO_PACK, O_NITRO_PACK /* scanned */,
219 O_NITRO_PACK_F, O_NITRO_PACK_F /* scanned */, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE,
222 O_NONE /* bomb explosion utolso */, O_UNKNOWN, O_NONE /* solid bomb glued */, O_UNKNOWN, O_STONE_GLUED, O_UNKNOWN, O_DIAMOND_GLUED, O_UNKNOWN,
223 O_UNKNOWN, O_UNKNOWN, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE, O_NONE,
226 O_ALT_FIREFLY_1 /* scanned */, O_ALT_FIREFLY_1 /* scanned */, O_ALT_FIREFLY_1, O_ALT_FIREFLY_2, O_ALT_FIREFLY_3, O_ALT_FIREFLY_4, O_PLAYER_BOMB, O_PLAYER_BOMB /* scanned */,
227 O_BOMB, O_BOMB_TICK_1, O_BOMB_TICK_2, O_BOMB_TICK_3, O_BOMB_TICK_4, O_BOMB_TICK_5, O_BOMB_TICK_6, O_BOMB_TICK_7,
230 O_BOMB_TICK_7, O_BOMB_EXPL_1, O_BOMB_EXPL_2, O_BOMB_EXPL_3, O_BOMB_EXPL_4, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN,
231 O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN,
234 static GdElement brc_effect_table[] =
236 O_STEEL, O_DIRT, O_SPACE, O_STONE, O_STONE_F, O_STONE_GLUED, O_DIAMOND, O_DIAMOND_F, O_DIAMOND_GLUED, O_PRE_DIA_1,
237 O_PLAYER, O_PRE_PL_1, O_PLAYER_BOMB, O_PRE_OUTBOX, O_OUTBOX, O_FIREFLY_1, O_FIREFLY_2, O_FIREFLY_3, O_FIREFLY_4,
238 O_BUTTER_1, O_BUTTER_2, O_BUTTER_3, O_BUTTER_4, O_BRICK, O_MAGIC_WALL, O_H_EXPANDING_WALL, O_V_EXPANDING_WALL, O_EXPANDING_WALL,
239 O_FALLING_WALL, O_FALLING_WALL_F, O_AMOEBA, O_SLIME, O_ACID, O_VOODOO, O_CLOCK, O_BOMB, O_UNKNOWN, O_UNKNOWN, O_UNKNOWN,
240 O_ALT_FIREFLY_1, O_ALT_FIREFLY_2, O_ALT_FIREFLY_3, O_ALT_FIREFLY_4, O_ALT_BUTTER_1, O_ALT_BUTTER_2, O_ALT_BUTTER_3, O_ALT_BUTTER_4,
241 O_EXPLODE_1, O_BOMB_EXPL_1, O_UNKNOWN,
244 static GdColor brc_color_table[] =
246 0x518722, 0x3a96fa, 0xdb7618, 0xff3968,
247 0x9b5fff, 0x0ee06c, 0xc25ea6, 0xf54826,
251 static GdColor brc_color_table_comp[] =
253 0x582287, 0xfa9d39, 0x187ddb, 0x38ffd1,
254 0xc1ff5e, 0xe00d81, 0x5dc27a, 0x27d3f5,
258 static GdElement brc_effect(byte byt)
260 if (byt >= ARRAY_SIZE(brc_effect_table))
262 Warn("invalid element identifier for brc effect: %02x", byt);
267 return brc_effect_table[byt];
270 static void brc_import(byte *data)
275 /* we import 100 caves, and the put them in the correct order. */
276 GdCave *imported[100];
277 boolean import_effect;
281 /* this is some kind of a version number */
282 import_effect = FALSE;
292 import_effect = TRUE;
296 Warn("unknown brc version %02x", data[23]);
300 for (level = 0; level < 5; level++)
305 for (cavenum = 0; cavenum < 20; cavenum++)
309 /* 5 levels, 20 caves, 24 bytes - max 40*2 properties for each cave */
312 int datapos = (cavenum * 5 +level) * 24 + 22;
315 cave = gd_cave_new();
316 imported[level * 20 + cavenum] = cave;
319 snprintf(cave->name, sizeof(GdString), "Cave %c/%d", 'A' + cavenum,
322 snprintf(cave->name, sizeof(GdString), "Intermission %d/%d",
323 cavenum - 15, level + 1);
325 /* fixed intermission caves; are smaller. */
332 cave->map = gd_cave_map_new(cave, GdElement);
334 for (y = 0; y < cave->h; y++)
336 for (x = 0; x < cave->w; x++)
340 import = data[y + level * 24 + cavenum * 24 * 5 + x * 24 * 5 * 20];
342 // if (i == printcave) g_print("%2x", import);
343 if (import < ARRAY_SIZE(brc_import_table))
344 cave->map[y][x] = brc_import_table[import];
346 cave->map[y][x] = O_UNKNOWN;
350 for (i = 0; i < 5; i++)
352 cave->level_time[i] = data[0 * c + datapos];
353 cave->level_diamonds[i] = data[1 * c + datapos];
354 cave->level_magic_wall_time[i] = data[4 * c + datapos];
355 cave->level_amoeba_time[i] = data[5 * c + datapos];
356 cave->level_amoeba_threshold[i] = data[6 * c + datapos];
358 /* bonus time: 100 was added, so it could also be negative */
359 cave->level_bonus_time[i] = (int)data[11 * c + datapos + 1] - 100;
360 cave->level_hatching_delay_frame[i] = data[10 * c + datapos];
362 /* this was not set in boulder remake. */
363 cave->level_speed[i] = 150;
366 cave->diamond_value = data[2 * c + datapos];
367 cave->extra_diamond_value = data[3 * c +datapos];
369 /* BRC PROBABILITIES */
370 /* a typical code example:
371 46:if (random(slime*4)<4) and (tab[x,y+2] = 0) then
\r
372 Begin tab[x,y]:=0;col[x,y+2]:=col[x,y];tab[x,y+2]:=27;mat[x,y+2]:=9;Voice4:=2;end;
\r
373 where slime is the byte loaded from the file as it is.
374 pascal random function generates a random number between 0..limit-1, inclusive, for random(limit).
376 so a random number between 0..limit*4-1 is generated.
377 for limit=1, 0..3, which is always < 4, so P=1.
378 for limit=2, 0..7, 0..7 is < 4 in P=50%.
379 for limit=3, 0..11, is < 4 in P=33%.
380 So the probability is exactly 100%/limit.
381 just make sure we do not divide by zero for some broken input.
384 if (data[7 * c + datapos] == 0)
385 Warn("amoeba growth cannot be zero, error at byte %d",
386 data[7 * c + datapos]);
388 cave->amoeba_growth_prob = 1E6 / data[7 * c + datapos] + 0.5; /* 0.5 for rounding */
390 if (data[8 * c + datapos] == 0)
391 Warn("amoeba growth cannot be zero, error at byte %d",
392 data[8 * c + datapos]);
394 cave->amoeba_fast_growth_prob = 1E6 / data[8 * c + datapos] + 0.5; /* 0.5 for rounding */
396 cave->slime_predictable = FALSE;
398 for (i = 0; i < 5; i++)
399 cave->level_slime_permeability[i] = 1E6 / data[9 * c + datapos] + 0.5; /* 0.5 for rounding */
401 /* probability -> *1E6 */
402 cave->acid_spread_ratio = 1E6 / data[10 * c + datapos] + 0.5;
404 /* br only allowed values 1..8 in here, but works the same way. prob -> *1E6 */
405 cave->pushing_stone_prob = 1E6 / data[11 * c + datapos] + 0.5;
407 cave->magic_wall_stops_amoeba = (data[12 * c + datapos + 1] != 0);
408 cave->intermission = (cavenum >= 16 || data[14 * c + datapos + 1] != 0);
411 colind = data[31 * c + datapos] % ARRAY_SIZE(brc_color_table);
412 cave->colorb = 0x000000; /* fixed rgb black */
413 cave->color0 = 0x000000; /* fixed rgb black */
414 cave->color1 = brc_color_table[colind];
415 cave->color2 = brc_color_table_comp[colind]; /* complement */
416 cave->color3 = 0xffffff; /* white for brick */
417 cave->color4 = 0xe5ad23; /* fixed for amoeba */
418 cave->color5 = 0x8af713; /* fixed for slime */
422 cave->amoeba_enclosed_effect = brc_effect(data[14 * c + datapos + 1]);
423 cave->amoeba_too_big_effect = brc_effect(data[15 * c + datapos + 1]);
424 cave->explosion_effect = brc_effect(data[16 * c + datapos + 1]);
425 cave->bomb_explosion_effect = brc_effect(data[17 * c + datapos + 1]);
427 /* 18 solid bomb explode to */
428 cave->diamond_birth_effect = brc_effect(data[19 * c + datapos + 1]);
429 cave->stone_bouncing_effect = brc_effect(data[20 * c + datapos + 1]);
430 cave->diamond_bouncing_effect = brc_effect(data[21 * c + datapos + 1]);
431 cave->magic_diamond_to = brc_effect(data[22 * c + datapos + 1]);
432 cave->acid_eats_this = brc_effect(data[23 * c + datapos + 1]);
435 (diamond,boulder,bomb),
439 cave->amoeba_enclosed_effect = brc_effect(data[14 * c + datapos + 1]);
444 /* put them in the caveset - take correct order into consideration. */
445 for (level = 0; level < 5; level++)
449 for (cavenum = 0; cavenum < 20; cavenum++)
451 static const int reorder[] =
453 0, 1, 2, 3, 16, 4, 5, 6, 7, 17, 8, 9, 10, 11, 18, 12, 13, 14, 15, 19
455 GdCave *cave = imported[level * 20 + reorder[cavenum]];
459 /* check if cave contains only dirt.
460 that is an empty cave, and do not import. */
463 for (y = 1; y < cave->h - 1 && only_dirt; y++)
464 for (x = 1; x < cave->w - 1 && only_dirt; x++)
465 if (cave->map[y][x] != O_DIRT)
468 /* append to caveset or forget it. */
470 gd_caveset = list_append(gd_caveset, cave);
477 static void caveset_name_set_from_filename(char *filename)
482 /* make up a caveset name from the filename. */
483 name = getBaseName(filename);
484 gd_strcpy(gd_caveset_data->name, name);
487 /* convert underscores to spaces */
488 while ((c = strchr (gd_caveset_data->name, '_')) != NULL)
491 /* remove extension */
492 if ((c = strrchr (gd_caveset_data->name, '.')) != NULL)
496 /* Load caveset from file.
497 Loads the caveset from a file.
499 File type is autodetected by extension.
500 param filename: Name of file.
501 result: FALSE if failed
503 boolean gd_caveset_load_from_file(char *filename)
511 if (stat(filename, &st) != 0)
513 Warn("cannot stat() file");
518 if (st.st_size > 1048576)
520 Warn("file bigger than 1MiB, refusing to load");
525 if (!(file = openFile(filename, MODE_READ)))
527 Warn("cannot open file '%s'", filename);
532 buf = checked_malloc(st.st_size + 1);
533 length = readFile(file, buf, 1, st.st_size);
538 if (length < st.st_size)
540 Warn("cannot read file '%s'", filename);
545 if (strSuffix(filename, ".brc") ||
546 strSuffix(filename, ".BRC"))
548 /* loading a boulder remake file */
551 Warn("BRC files must be 96000 bytes long");
557 if (strSuffix(filename, ".brc") ||
558 strSuffix(filename, ".BRC"))
560 brc_import((byte *) buf);
561 gd_caveset_edited = FALSE; /* newly loaded cave is not edited */
562 gd_caveset_last_selected = caveset_first_selectable_cave_index();
563 gd_caveset_last_selected_level = 0;
565 caveset_name_set_from_filename(filename);
571 if (gd_caveset_imported_get_format((byte *) buf) == GD_FORMAT_UNKNOWN)
573 /* try to load as bdcff */
576 /* bdcff: start another function */
577 result = gd_caveset_load_from_bdcff(buf);
579 /* newly loaded file is not edited. */
580 gd_caveset_edited = FALSE;
582 gd_caveset_last_selected = caveset_first_selectable_cave_index();
583 gd_caveset_last_selected_level = 0;
589 /* try to load as a binary file, as we know the format */
590 new_caveset = gd_caveset_import_from_buffer ((byte *) buf, length);
593 /* if unable to load, exit here. error was reported by import_from_buffer() */
597 /* no serious error :) */
599 /* only clear caveset here. if file read was unsuccessful, caveset remains in memory. */
602 gd_caveset = new_caveset;
604 /* newly loaded cave is not edited */
605 gd_caveset_edited = FALSE;
607 gd_caveset_last_selected = caveset_first_selectable_cave_index();
608 gd_caveset_last_selected_level = 0;
609 caveset_name_set_from_filename(filename);
614 boolean gd_caveset_save_to_file(const char *filename)
616 GdPtrArray *saved = gd_caveset_save_to_bdcff();
621 if ((file = openFile(filename, MODE_WRITE)) != NULL)
623 for (i = 0; i < saved->size; i++)
625 writeFile(file, saved->data[i], 1, strlen(saved->data[i]));
626 writeFile(file, "\n", 1, 1);
631 /* remember that it is saved */
632 gd_caveset_edited = FALSE;
638 Warn("cannot open file '%s'", filename);
643 gd_ptr_array_free(saved, TRUE);
649 int gd_cave_check_replays(GdCave *cave, boolean report, boolean remove, boolean repair)
654 riter = cave->replays;
655 while (riter != NULL)
657 GdReplay *replay = (GdReplay *)riter->data;
658 unsigned int checksum;
660 List *next = riter->next;
662 rendered = gd_cave_new_rendered(cave, replay->level, replay->seed);
663 checksum = gd_cave_adler_checksum(rendered);
664 gd_cave_free(rendered);
666 replay->wrong_checksum = FALSE;
668 /* count wrong ones... the checksum might be changed later to "repair" */
669 if (replay->checksum != 0 && checksum != replay->checksum)
672 if (replay->checksum == 0 || repair)
674 /* if no checksum found, add one. or if repair requested, overwrite old one. */
675 replay->checksum = checksum;
679 /* if has a checksum, compare with this one. */
680 if (replay->checksum != checksum)
682 replay->wrong_checksum = TRUE;
685 Warn("%s: replay played by %s at %s has wrong checksum",
686 cave->name, replay->player_name, replay->date);
691 cave->replays = list_remove_link(cave->replays, riter);
692 gd_replay_free(replay);
697 /* advance to next list item which we remembered. the current one might have been deleted */
704 boolean gd_caveset_has_replays(void)
709 for (citer = gd_caveset; citer != NULL; citer = citer->next)
711 GdCave *cave = (GdCave *)citer->data;
717 /* if neither of the caves had a replay, */