#include "sound_pool.nvgt" #include "virtual_dialogs.nvgt" // Audio sound_pool p(100); #include "src/constants.nvgt" #include "src/player.nvgt" #include "src/world_state.nvgt" #include "src/ui.nvgt" #include "src/inventory.nvgt" #include "src/quest_system.nvgt" #include "src/environment.nvgt" #include "src/combat.nvgt" #include "src/save_system.nvgt" #include "src/base_system.nvgt" #include "src/time_system.nvgt" #include "src/audio_utils.nvgt" #include "src/notify.nvgt" int run_main_menu() { screen_reader_speak("Draugnorak. Main menu.", true); int selection = 0; string load_label = has_save_game() ? "Load Game" : "Load Game (no save found)"; string[] options = {"New Game", load_label, "Exit"}; screen_reader_speak(options[selection], true); while(true) { wait(5); if (key_pressed(KEY_DOWN)) { selection++; if (selection >= options.length()) selection = 0; screen_reader_speak(options[selection], true); } if (key_pressed(KEY_UP)) { selection--; if (selection < 0) selection = options.length() - 1; screen_reader_speak(options[selection], true); } if (key_pressed(KEY_RETURN)) { return selection; } if (key_pressed(KEY_ESCAPE)) { return 2; } } return 2; } void main() { // Configure sound pool for better spatial audio p.volume_step = AUDIO_VOLUME_STEP / float(AUDIO_TILE_SCALE); // Default falloff in audio units p.pan_step = AUDIO_PAN_STEP; // Panning strength for scaled tile distances show_window("Draugnorak"); bool game_started = false; while (!game_started) { int selection = run_main_menu(); if (selection == 0) { start_new_game(); screen_reader_speak("New game started.", true); game_started = true; } else if (selection == 1) { if (load_game_state()) { screen_reader_speak("Game loaded.", true); game_started = true; } else { if (has_save_game()) { string message = last_save_error; if (message == "") message = "Unable to load save."; ui_info_box("Draugnorak", "Load Game", message); } else { ui_info_box("Draugnorak", "Load Game", "No save found."); } } } else { exit(); } } while(true) { wait(5); if(key_pressed(KEY_ESCAPE)) { int really_exit = ui_question("Draugnorak", "Really exit?"); if (really_exit == 1) { exit(); } } // Time & Environment updates update_time(); update_crossfade(); update_environment(); update_snares(); update_streams(); update_fires(); update_zombies(); update_bandits(); update_blessings(); update_notifications(); // Fire damage check (only if not jumping) WorldFire@ fire_on_tile = get_fire_at(x); if (fire_on_tile != null && !jumping && fire_damage_timer.elapsed > 1000) { player_health--; fire_damage_timer.restart(); screen_reader_speak("Burning! " + player_health + " health remaining.", true); } // Healing in base area if (x <= BASE_END && player_health < max_health) { WorldHerbGarden@ herb_garden = get_herb_garden_at_base(); int heal_interval = (herb_garden != null) ? 30000 : 150000; // 30 seconds with garden, 2.5 minutes without if (healing_timer.elapsed > heal_interval) { player_health++; healing_timer.restart(); screen_reader_speak(player_health + " health.", true); } } // Death check if (player_health <= 0) { screen_reader_speak("You have died.", true); wait(2000); exit(); } // Inventory & Actions check_inventory_keys(x); check_action_menu(x); check_crafting_menu(x, BASE_END); check_equipment_menu(); check_quest_menu(); check_quick_slot_keys(); check_time_input(); check_notification_keys(); // Health Key if (key_pressed(KEY_H)) { screen_reader_speak(player_health + " health of " + max_health, true); } // Coordinates Key if (key_pressed(KEY_X)) { string direction_label = (facing == 1) ? "east" : "west"; string terrain_info = ""; MountainRange@ mountain = get_mountain_at(x); if (mountain !is null) { terrain_info = ", elevation " + y + ", " + mountain.get_terrain_at(x); } screen_reader_speak(direction_label + ", x " + x + ", y " + y + terrain_info, true); } // Base Info Key (base only) if (key_pressed(KEY_B)) { run_base_info_menu(); } // Climbing and Falling Updates update_climbing(); update_falling(); update_rope_climbing(); check_rope_climb_fall(); update_mountains(); // Down arrow to climb down from tree or start rope climb down if (key_pressed(KEY_DOWN)) { // Check for pending rope climb (going down) if (pending_rope_climb_x != -1 && !rope_climbing && !climbing && !falling && !jumping) { int elevation_change = pending_rope_climb_elevation - y; if (elevation_change < 0) { start_rope_climb(false, pending_rope_climb_x, pending_rope_climb_elevation); pending_rope_climb_x = -1; } } // Tree climbing down else { Tree@ tree = get_tree_at(x); if (tree != null && !tree.is_chopped && y > 0 && !jumping && !climbing && !falling && !rope_climbing) { climb_down_tree(); } } } // Jumping Logic if(key_pressed(KEY_UP)) { // Check for pending rope climb (going up) if (pending_rope_climb_x != -1 && !rope_climbing && !climbing && !falling && !jumping) { int elevation_change = pending_rope_climb_elevation - y; if (elevation_change > 0) { start_rope_climb(true, pending_rope_climb_x, pending_rope_climb_elevation); pending_rope_climb_x = -1; } } else if(!jumping && !climbing && !falling && !rope_climbing) { // Check if on tree tile Tree@ tree = get_tree_at(x); if (tree != null && !tree.is_chopped && y == 0) { // Start climbing the tree start_climbing_tree(x); } else if (y == 0) { // Normal jump p.play_stationary("sounds/jump.ogg", false); jumping = true; jumptimer.restart(); } } } if(jumping && jumptimer.elapsed > 850) { jumping = false; y = 0; // Reset y after jump play_land_sound(x, BASE_END, GRASS_END); // Check for snare on landing? check_snare_collision(x); } // Set y to 3 during jump if(jumping && y == 0) { y = 3; } movetime = jumping ? jump_speed : walk_speed; // Movement Logic if (key_pressed(KEY_LEFT) && facing != 0 && !climbing && !falling && !rope_climbing) { facing = 0; screen_reader_speak("west", true); walktimer.restart(); // Cancel pending rope climb when changing direction pending_rope_climb_x = -1; } if (key_pressed(KEY_RIGHT) && facing != 1 && !climbing && !falling && !rope_climbing) { facing = 1; screen_reader_speak("east", true); walktimer.restart(); // Cancel pending rope climb when changing direction pending_rope_climb_x = -1; } if(walktimer.elapsed > movetime) { int old_x = x; // Check if trying to move left/right while in tree (not in mountain) MountainRange@ current_mountain = get_mountain_at(x); if((key_down(KEY_LEFT) || key_down(KEY_RIGHT)) && y > 0 && !jumping && !falling && !rope_climbing && current_mountain is null) { // Fall out of tree climbing = false; start_falling(); } if(key_down(KEY_LEFT) && x > 0 && !climbing && !falling && !rope_climbing) { facing = 0; // Check mountain movement if (can_move_mountain(x, x - 1)) { x--; // Update elevation if in mountain int new_elevation = get_mountain_elevation_at(x); if (new_elevation != y && get_mountain_at(x) !is null) { y = new_elevation; } walktimer.restart(); if(!jumping) { play_footstep(x, BASE_END, GRASS_END); check_snare_collision(x); } } } else if(key_down(KEY_RIGHT) && x < MAP_SIZE - 1 && !climbing && !falling && !rope_climbing) { facing = 1; // Check mountain movement if (can_move_mountain(x, x + 1)) { x++; // Update elevation if in mountain int new_elevation = get_mountain_elevation_at(x); if (new_elevation != y && get_mountain_at(x) !is null) { y = new_elevation; } walktimer.restart(); if(!jumping) { play_footstep(x, BASE_END, GRASS_END); check_snare_collision(x); } } } } // Reset search timer if shift is used with other keys if((key_down(KEY_LSHIFT) || key_down(KEY_RSHIFT))) { if(key_pressed(KEY_COMMA) || key_pressed(KEY_PERIOD)) { searching = false; search_delay_timer.restart(); } } // Searching Logic bool shift_down = (key_down(KEY_LSHIFT) || key_down(KEY_RSHIFT)); if (!shift_down) { if (searching) { searching = false; } search_timer.restart(); } if (shift_down && search_timer.elapsed > 2000 && !searching) { searching = true; search_delay_timer.restart(); } // Complete search after delay if(searching && search_delay_timer.elapsed >= 1000) { searching = false; search_timer.restart(); perform_search(x); } // Sling charge detection if (sling_equipped && (key_down(KEY_LCTRL) || key_down(KEY_RCTRL)) && !sling_charging) { if (inv_stones > 0) { sling_charging = true; sling_charge_timer.restart(); sling_sound_handle = p.play_stationary("sounds/weapons/sling_swing.ogg", true); last_sling_stage = -1; } else { screen_reader_speak("No stones.", true); } } // Update sling charge state while holding if (sling_charging && (key_down(KEY_LCTRL) || key_down(KEY_RCTRL))) { update_sling_charge(); } // Sling release detection if (sling_charging && (!key_down(KEY_LCTRL) && !key_down(KEY_RCTRL))) { release_sling_attack(x); sling_charging = false; if (sling_sound_handle != -1) { p.destroy_sound(sling_sound_handle); sling_sound_handle = -1; } } // Non-sling weapon attacks (existing pattern) if (!sling_equipped && !sling_charging) { int attack_cooldown = 1000; if (spear_equipped) attack_cooldown = 800; if (axe_equipped) attack_cooldown = 1600; if((key_down(KEY_LCTRL) || key_down(KEY_RCTRL)) && attack_timer.elapsed > attack_cooldown) { attack_timer.restart(); perform_attack(x); } } // Audio Listener Update p.update_listener_1d(x); } }