#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 { uint count; uint limit; uint * completed; uint * * requirement; uint * type; char * * class; char * * description; } challenge_structure; static char * special_name [special_count] = { "strength", "perception", "edurance", "charisma", "intelligence", "agility", "luck" }; static bool challenge_is_repeatable (challenge_structure * challenges, uint index) { return ((challenges->type [index] == special_strength + 1) || (challenges->type [index] == special_edurance + 1) || (challenges->type [index] == special_agility + 1)); } static bool challenge_is_available (challenge_structure * challenges, uint index) { return ((challenges->completed [index] == 0) || (challenge_is_repeatable (challenges, index) == true)); } static bool challenge_is_completable (uint * special, challenge_structure * challenges, uint index) { if (challenge_is_available (challenges, index) == false) { return (false); } for (uint check = 0; check < special_count; ++check) { if (challenges->requirement [index] [check] > special [check]) { return (false); } } return (true); } static void render_challenge_list (terminal_structure * terminal, uint * special, challenge_structure * challenges, uint x, uint y) { uint count = 0; uint * array = allocate (challenges->count * sizeof (* array)); for (uint 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 (uint index = 0; index < count; ++index) { terminal_render_character (terminal, '[', colour_grey, effect_bold, x, y + index + 2); for (uint 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 void render_special_attributes (terminal_structure * terminal, uint * special, uint offset, uint selection) { for (uint index = 0; index < special_count; ++index) { uint effect = (selection == index) ? effect_bold : effect_normal; uint length = attribute_maximum + 3; char 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 void prompt_special_attributes (uint * special) { terminal_structure * terminal = terminal_initialize (); uint selection = 0; bool show_help = false; char * 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:" }; char * 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 (uint index = 0; index < special_count; ++index) { special [index] = 5; } while (terminal->active == true) { terminal_render_background (terminal, ' ', colour_white, effect_normal); for (uint 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 (uint index = 0; index < array_length (help_messages); ++index) { uint 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 (uint 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 void import_user_configuration (uint * special, uint challenge_count, uint * * daily_challenges, bool * * completition) { bool special_defined [special_count] = { false }; bool daily_challenges_defined = false; bool completition_defined = false; script_information * information = allocate (sizeof (* information)); script_structure * structure = script_open (configuration_format ("xhallenge.cfg")); for (script_word_type word = script_parser (structure); word != script_end; word = script_parser (structure)) { if (word == script_marker) { if (script_compare (structure, "challenges") == true) { uint 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 (uint index = 0; index < challenges_per_day; ++index) { script_failure (structure, (* daily_challenges) [index] >= challenge_count, "Invalid index."); } } else if (script_compare (structure, "completition") == true) { uint 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 (uint 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 void export_user_configuration (uint * special, uint * daily_challenges, bool * completition) { char buffer [4096] = ""; for (uint index = 0; index < special_count; ++index) { string_concatenate (buffer, format ("%s = %i\n", special_name [index], special [index])); } string_concatenate (buffer, "challenges = ("); for (uint 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 (uint 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 (uint limit) { challenge_structure * challenges = allocate (sizeof (* challenges)); challenges->limit = limit; return (challenges); } static challenge_structure * challenges_deinitialize (challenge_structure * challenges) { for (uint 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 void import_challenges (challenge_structure * challenges) { bool completed_defined = false; bool requirement_defined = false; bool type_defined = false; bool class_defined = false; bool description_defined = false; script_information * information = allocate (sizeof (* information)); script_structure * structure = script_open (configuration_format ("xhallenge_list.cfg")); for (script_word_type 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) { uint 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) { uint 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 void export_challenges (challenge_structure * challenges) { int file = file_open (configuration_format ("xhallenge_list.cfg"), file_flag_edit | file_flag_truncate); for (uint index = 0; index < challenges->count; ++index) { file_echo (file, "[] "); file_echo (file, format ("completed = %i ", challenges->completed [index])); file_echo (file, "requirement = ("); for (uint 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 uint generate_challenge (uint * special, challenge_structure * challenges) { uint index = urandomize (0, challenges->count - 1); bool valid = false; while (valid == false) { valid = true; index = urandomize (0, challenges->count - 1); if ((challenges->completed [index] > 0) && (challenges->type [index] != 1) && (challenges->type [index] != 3)) continue; for (uint check = 0; check < special_count; ++check) { if (challenges->requirement [index] [check] > special [check]) { valid = false; break; } } } return (index); } static void generate_challenges (uint * special, challenge_structure * challenges, uint * * daily_challenges, bool * * completition) { (* daily_challenges) = allocate (challenges_per_day * sizeof (* * daily_challenges)); (* completition) = allocate (challenges_per_day * sizeof (* * completition)); for (uint index = 0; index < challenges_per_day; ++index) { (* daily_challenges) [index] = generate_challenge (special, challenges); } } static void render_challenges (uint * special, challenge_structure * challenges, uint * daily_challenges, bool * completition) { terminal_structure * terminal = terminal_initialize (); uint selection = 0; bool show_help = false; bool show_list = false; char * 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:" }; char * 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 (uint index = 0; index < array_length (main_messages); ++index) { terminal_render_string (terminal, main_messages [index], colour_white, effect_normal, 0, index); } for (uint index = 0; index < challenges_per_day; ++index) { uint type = challenges->type [daily_challenges [index]]; char * class = challenges->class [daily_challenges [index]]; char * description = challenges->description [daily_challenges [index]]; uint effect = (selection == index) ? effect_bold : effect_normal; uint offset = array_length (main_messages) + 2; uint 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) { uint offset = array_length (main_messages) + challenges_per_day + 3; for (uint 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 (uint index = 0; index < challenges_per_day; ++index) { daily_challenges [index] = generate_challenge (special, challenges); completition [index] = false; } } } terminal = terminal_deinitialize (terminal); } int main (int argc, char * * argv) { uint special [special_count] = { 0 }; uint * daily_challenges = null; bool * completition = null; challenge_structure * challenges = challenges_initialize (1024); randomize_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); }