#define use_fatal_failure #include #include #include #define challenges_per_day (10) #define attribute_minimum (1) #define attribute_maximum (10) typedef enum { special_strength, special_perception, special_edurance, special_charisma, special_intelligence, special_agility, special_luck, special_count } special_enumeration; typedef struct { natural count; natural limit; natural * completed; natural * * requirement; natural * type; character * * class; character * * description; } challenge_structure; static character * special_name [special_count] = { "strength", "perception", "edurance", "charisma", "intelligence", "agility", "luck" }; static boolean challenge_is_repeatable (challenge_structure * challenges, natural index) { return ((challenges->type [index] == special_strength + 1) || (challenges->type [index] == special_edurance + 1) || (challenges->type [index] == special_agility + 1)); } static boolean challenge_is_available (challenge_structure * challenges, natural index) { return ((challenges->completed [index] == 0) || (challenge_is_repeatable (challenges, index) == true)); } static boolean challenge_is_completable (natural * special, challenge_structure * challenges, natural index) { if (challenge_is_available (challenges, index) == false) { return (false); } for (natural check = 0; check < special_count; ++check) { if (challenges->requirement [index] [check] > special [check]) { return (false); } } return (true); } static procedure render_challenge_list (terminal_structure * terminal, natural * special, challenge_structure * challenges, natural x, natural y) { natural count = 0; natural * array = allocate (challenges->count * sizeof (* array)); for (natural index = 0; index < challenges->count; ++index) { if (challenge_is_completable (special, challenges, index) == true) { ++count; array [count - 1] = index; } } terminal_render_format (terminal, "Count of unlocked challenges: /2/B%i", x, y + 0, count); terminal_render_format (terminal, "Count of locked challenges: /1/B%i", x, y + 1, challenges->count - count); for (natural index = 0; index < count; ++index) { terminal_render_character (terminal, '[', colour_grey, effect_bold, x, y + index + 2); for (natural value = 0; value < special_count; ++value) { terminal_render_number (terminal, challenges->requirement [array [index]] [value], value + 1, effect_bold, x + 3 * value + 2, y + index + 2); } terminal_render_character (terminal, ']', colour_grey, effect_bold, x + special_count * 3 + 1, y + index + 2); terminal_render_format (terminal, "/7/B%s/0/B --- /-%s", x + special_count * 3 + 3, y + index + 2, challenges->class [array [index]], challenges->description [array [index]]); } array = deallocate (array); } static procedure render_special_attributes (terminal_structure * terminal, natural * special, natural offset, natural selection) { for (natural index = 0; index < special_count; ++index) { natural effect = (selection == index) ? effect_bold : effect_normal; natural length = attribute_maximum + 3; character name [32] = ""; string_copy_limit (name, special_name [index], sizeof (name)); terminal_render_fill_bar (terminal, special [index], 10, '+', index + 1, effect, 0, index + offset); terminal_render_string (terminal, capitalize (name), index + 1, effect, length, index + offset); } } static procedure prompt_special_attributes (natural * special) { terminal_structure * terminal = terminal_initialize (); natural selection = 0; boolean show_help = false; character * main_messages [] = { "Press H or Tab to toggle help.", "Press Q or Escape to use default settings.", "Press S or Enter to save changes.", "", "Choose your SPECIAL attributes:" }; character * help_messages [] = { "Show help - H or Tab", "Use default settings - Q or Escape", "Save and quit - S or Enter", "Move up - J, Up arrow key or KP8", "Move down - K, Down arrow key or KP2", "Increase attribute - P, Right arrow key or KP6", "Decrease attribute - N, Left arrow key or KP4" }; for (natural index = 0; index < special_count; ++index) { special [index] = 5; } while (terminal->active == true) { terminal_render_background (terminal, ' ', colour_white, effect_normal); for (natural index = 0; index < array_length (main_messages); ++index) { terminal_render_string (terminal, main_messages [index], colour_white, effect_normal, 0, index); } render_special_attributes (terminal, special, array_length (main_messages) + 1, selection); if (show_help == true) { for (natural index = 0; index < array_length (help_messages); ++index) { natural offset = array_length (main_messages) + special_count + 2; terminal_render_string (terminal, help_messages [index], colour_white, effect_normal, 0, index + offset); } } terminal_synchronize (terminal); if ((terminal->signal [signal_tabulator] == true) || (terminal->signal [signal_h] == true)) { show_help = ! show_help; } else if ((terminal->signal [signal_escape] == true) || (terminal->signal [signal_q] == true)) { for (natural index = 0; index < special_count; ++index) { special [index] = 5; } break; } else if ((terminal->signal [signal_return] == true) || (terminal->signal [signal_s] == true)) { break; } else if ((terminal->signal [signal_arrow_up] == true) || (terminal->signal [signal_j] == true)) { selection = (selection - 1 + special_count) % special_count; } else if ((terminal->signal [signal_arrow_down] == true) || (terminal->signal [signal_k] == true)) { selection = (selection + 1) % special_count; } else if ((terminal->signal [signal_arrow_left] == true) || (terminal->signal [signal_h] == true)) { --special [selection]; special [selection] = (special [selection] < 1) ? 1 : special [selection]; } else if ((terminal->signal [signal_arrow_right] == true) || (terminal->signal [signal_l] == true)) { ++special [selection]; special [selection] = (special [selection] > 10) ? 10 : special [selection]; } } terminal = terminal_deinitialize (terminal); } static procedure import_user_configuration (natural * special, natural challenge_count, natural * * daily_challenges, boolean * * completition) { boolean special_defined [special_count] = { false }; boolean daily_challenges_defined = false; boolean completition_defined = false; script_structure * information = allocate (sizeof (* information)); script_data_structure * structure = script_open (configuration_format ("xhallenge.cfg")); for (script_word_enumeration word = script_parser (structure); word != script_end; word = script_parser (structure)) { if (word == script_marker) { if (script_compare (structure, "challenges") == true) { natural check = 0; script_failure (structure, daily_challenges_defined == true, "Challenge array was already defined."); (* daily_challenges) = script_expect_ordered_array (information, structure, & check); daily_challenges_defined = true; script_failure (structure, check != challenges_per_day, "Ordered array 'daily_challenges' is incomplete."); for (natural index = 0; index < challenges_per_day; ++index) { script_failure (structure, (* daily_challenges) [index] >= challenge_count, "Invalid index."); } } else if (script_compare (structure, "completition") == true) { natural check = 0; script_failure (structure, completition_defined == true, "Completition array was already defined."); (* completition) = script_expect_ordered_array (information, structure, & check); completition_defined = true; script_failure (structure, check != challenges_per_day, "Ordered array 'completition' is incomplete."); } else for (natural index = 0; index < special_count; ++index) { if (script_compare (structure, special_name [index]) == true) { script_failure (structure, special_defined [index] == true, "Attribute was already defined."); special [index] = script_expect_number (structure); script_failure (structure, special [index] > attribute_maximum, "Attribute exceeded maximum value."); script_failure (structure, special [index] < attribute_minimum, "Attribute exceeded minimum value."); special_defined [index] = true; } } } else if ((word == script_end) || (word == script_comment)) { continue; } else { script_failure (structure, true, "Expected 'marker = number' in configuration script."); } } structure = script_close (structure); information = deallocate (information); } static procedure export_user_configuration (natural * special, natural * daily_challenges, boolean * completition) { character buffer [4096] = ""; for (natural index = 0; index < special_count; ++index) { string_concatenate (buffer, format ("%s = %i\n", special_name [index], special [index])); } string_concatenate (buffer, "challenges = ("); for (natural index = 0; index < challenges_per_day; ++index) { string_concatenate (buffer, number_to_string (daily_challenges [index])); if (index < challenges_per_day - 1) { string_concatenate (buffer, ", "); } } string_concatenate (buffer, ")\n"); string_concatenate (buffer, "completition = ("); for (natural index = 0; index < challenges_per_day; ++index) { string_concatenate (buffer, number_to_string (completition [index])); if (index < challenges_per_day - 1) { string_concatenate (buffer, ", "); } } string_concatenate (buffer, ")\n"); configuration_export ("xhallenge.cfg", buffer); } static challenge_structure * challenges_initialize (natural limit) { challenge_structure * challenges = allocate (sizeof (* challenges)); challenges->limit = limit; return (challenges); } static challenge_structure * challenges_deinitialize (challenge_structure * challenges) { for (natural index = 0; index < challenges->count; ++index) { challenges->requirement [index] = deallocate (challenges->requirement [index]); challenges->description [index] = deallocate (challenges->description [index]); challenges->class [index] = deallocate (challenges->class [index]); } challenges->completed = deallocate (challenges->completed); challenges->requirement = deallocate (challenges->requirement); challenges->type = deallocate (challenges->type); challenges->class = deallocate (challenges->class); challenges->description = deallocate (challenges->description); return (deallocate (challenges)); } static procedure import_challenges (challenge_structure * challenges) { boolean completed_defined = false; boolean requirement_defined = false; boolean type_defined = false; boolean class_defined = false; boolean description_defined = false; script_structure * information = allocate (sizeof (* information)); script_data_structure * structure = script_open (configuration_format ("xhallenge_list.cfg")); for (script_word_enumeration word = script_parser (structure); word != script_end; word = script_parser (structure)) { if (word == script_header) { if (challenges->count > 0) { script_warning (structure, (completed_defined == false) || (requirement_defined == false) || (type_defined == false) || (class_defined == false) || (description_defined == false), "Some fields were left uninitialized and default to zero."); } ++challenges->count; challenges->completed = reallocate (challenges->completed, challenges->count * sizeof (* challenges->completed)); challenges->requirement = reallocate (challenges->requirement, challenges->count * sizeof (* challenges->requirement)); challenges->type = reallocate (challenges->type, challenges->count * sizeof (* challenges->type)); challenges->class = reallocate (challenges->class, challenges->count * sizeof (* challenges->class)); challenges->description = reallocate (challenges->description, challenges->count * sizeof (* challenges->description)); completed_defined = false; requirement_defined = false; type_defined = false; class_defined = false; description_defined = false; } else if (word == script_marker) { natural current = challenges->count - 1; if (script_compare (structure, "completed") == true) { script_failure (structure, completed_defined == true, "Marker 'completed' already defined."); challenges->completed [current] = script_expect_number (structure); completed_defined = true; } else if (script_compare (structure, "requirement") == true) { natural check = 0; script_failure (structure, requirement_defined == true, "Marker 'requirement' already defined."); challenges->requirement [current] = script_expect_ordered_array (information, structure, & check); requirement_defined = true; script_failure (structure, check != special_count, "Ordered array doesn't have enough elements."); } else if (script_compare (structure, "type") == true) { script_failure (structure, type_defined == true, "Marker 'type' already defined."); challenges->type [current] = script_expect_number (structure); type_defined = true; } else if (script_compare (structure, "class") == true) { script_failure (structure, class_defined == true, "Marker 'class' already defined."); challenges->class [current] = script_expect_string (structure); class_defined = true; } else if (script_compare (structure, "description") == true) { script_failure (structure, description_defined == true, "Marker 'description' already defined."); challenges->description [current] = script_expect_string (structure); description_defined = true; } else { script_failure (structure, true, "Expected name, faction, statistic or ability."); } } else if ((word == script_end) || (word == script_comment)) { continue; } else { script_failure (structure, true, "Expected 'marker = number/string/ordered_array' in configuration script."); } } structure = script_close (structure); information = deallocate (information); } static procedure export_challenges (challenge_structure * challenges) { integer file = file_open (configuration_format ("xhallenge_list.cfg"), file_flag_edit | file_flag_truncate); for (natural index = 0; index < challenges->count; ++index) { file_echo (file, "[] "); file_echo (file, format ("completed = %i ", challenges->completed [index])); file_echo (file, "requirement = ("); for (natural subindex = 0; subindex < special_count; ++subindex) { file_echo (file, number_to_string (challenges->requirement [index] [subindex])); if (subindex < special_count - 1) { file_echo (file, ", "); } } file_echo (file, ") "); file_echo (file, format ("type = %i ", challenges->type [index])); file_echo (file, format ("class = \"%s\" ", challenges->class [index])); file_echo (file, format ("description = \"%s\"\n", challenges->description [index])); } file = file_close (file); } static natural generate_challenge (natural * special, challenge_structure * challenges) { natural index = random_natural (0, challenges->count - 1); boolean valid = false; while (valid == false) { valid = true; index = random_natural (0, challenges->count - 1); if ((challenges->completed [index] > 0) && (challenges->type [index] != 1) && (challenges->type [index] != 3)) continue; for (natural check = 0; check < special_count; ++check) { if (challenges->requirement [index] [check] > special [check]) { valid = false; break; } } } return (index); } static procedure generate_challenges (natural * special, challenge_structure * challenges, natural * * daily_challenges, boolean * * completition) { (* daily_challenges) = allocate (challenges_per_day * sizeof (* * daily_challenges)); (* completition) = allocate (challenges_per_day * sizeof (* * completition)); for (natural index = 0; index < challenges_per_day; ++index) { (* daily_challenges) [index] = generate_challenge (special, challenges); } } static procedure render_challenges (natural * special, challenge_structure * challenges, natural * daily_challenges, boolean * completition) { terminal_structure * terminal = terminal_initialize (); natural selection = 0; boolean show_help = false; boolean show_list = false; character * main_messages [] = { "Press H or Tab to toggle help.", "Press Q or Escape to quit the program without saving changes.", "Press S or Enter to save changes and quit the program.", "", "Your daily challenges:" }; character * help_messages [] = { "Show help - H or Tab", "Quit - Q or Escape", "Save and quit - S or Enter", "Move up - J, Up arrow key or KP8", "Move down - K, Down arrow key or KP2", "(Un)Mark challenge - M or Space", "Change challenge - C or Backspace", "Reset challenges - R", "List challenges - L" }; while (terminal->active == true) { terminal_render_background (terminal, ' ', colour_white, effect_normal); for (natural index = 0; index < array_length (main_messages); ++index) { terminal_render_string (terminal, main_messages [index], colour_white, effect_normal, 0, index); } for (natural index = 0; index < challenges_per_day; ++index) { natural type = challenges->type [daily_challenges [index]]; character * class = challenges->class [daily_challenges [index]]; character * description = challenges->description [daily_challenges [index]]; natural effect = (selection == index) ? effect_bold : effect_normal; natural offset = array_length (main_messages) + 2; natural alignment = string_length (class) + 4; terminal_render_toggle (terminal, completition [index], 0, index + offset); terminal_render_string (terminal, class, type, effect, 4, index + offset); terminal_render_string (terminal, " --- ", colour_grey, effect_bold, alignment + 0, index + offset); terminal_render_string (terminal, description, colour_white, effect, alignment + 5, index + offset); } if (show_list == true) { render_challenge_list (terminal, special, challenges, 80, 0); } if (show_help == true) { natural offset = array_length (main_messages) + challenges_per_day + 3; for (natural index = 0; index < array_length (help_messages); ++index) { terminal_render_string (terminal, help_messages [index], colour_white, effect_normal, 0, index + offset); } render_special_attributes (terminal, special, offset + array_length (help_messages) + 1, special_count); } terminal_synchronize (terminal); if ((terminal->signal [signal_tabulator] == true) || (terminal->signal [signal_h] == true)) { show_help = ! show_help; } else if (/*(terminal->signal [signal_tabulator] == true) || */(terminal->signal [signal_l] == true)) { show_list = ! show_list; } else if ((terminal->signal [signal_return] == true) || (terminal->signal [signal_s] == true)) { export_user_configuration (special, daily_challenges, completition); break; } else if ((terminal->signal [signal_escape] == true) || (terminal->signal [signal_q] == true)) { break; } else if ((terminal->signal [signal_arrow_up] == true) || (terminal->signal [signal_j] == true)) { selection = (selection - 1 + challenges_per_day) % challenges_per_day; } else if ((terminal->signal [signal_arrow_down] == true) || (terminal->signal [signal_k] == true)) { selection = (selection + 1) % challenges_per_day; } else if ((terminal->signal [signal_space] == true) || (terminal->signal [signal_m] == true)) { completition [selection] = ! completition [selection]; challenges->completed [daily_challenges [selection]] += (completition [selection] == true) ? 1 : -1; } else if ((terminal->signal [signal_backspace] == true) || (terminal->signal [signal_c] == true)) { daily_challenges [selection] = generate_challenge (special, challenges); completition [selection] = false; } else if (/*(terminal->signal [signal_backspace] == true) || */(terminal->signal [signal_r] == true)) { for (natural index = 0; index < challenges_per_day; ++index) { daily_challenges [index] = generate_challenge (special, challenges); completition [index] = false; } } } terminal = terminal_deinitialize (terminal); } integer main (integer argc, character * * argv) { natural special [special_count] = { 0 }; natural * daily_challenges = null; boolean * completition = null; challenge_structure * challenges = challenges_initialize (1024); random_integer_seed_by_time (); if (argc == 2) { if (string_compare (argv [1], "-r") == true) { configuration_remove ("xhallenge.cfg"); } } import_challenges (challenges); if (configuration_exists ("xhallenge.cfg") == true) { import_user_configuration (special, challenges->count, & daily_challenges, & completition); } else { prompt_special_attributes (special); generate_challenges (special, challenges, & daily_challenges, & completition); } render_challenges (special, challenges, daily_challenges, completition); export_challenges (challenges); challenges = challenges_deinitialize (challenges); daily_challenges = deallocate (daily_challenges); completition = deallocate (completition); return (log_success); }