Files
draugnorak/draugnorak.nvgt
2026-01-21 10:37:14 -05:00

416 lines
14 KiB
Plaintext

#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/weather.nvgt"
#include "src/audio_utils.nvgt"
#include "src/creature_audio.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");
init_flying_creature_configs();
bool game_started = false;
while (!game_started) {
int selection = run_main_menu();
if (selection == 0) {
// Check if save file exists and confirm before overwriting
if (has_save_game()) {
int confirm = ui_question("", "Save found. Are you sure you want to start a new game?");
if (confirm != 1) {
continue; // Return to main menu
}
}
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);
// Pause toggle
if(key_pressed(KEY_BACK))
{
game_paused = !game_paused;
if (game_paused) {
p.pause_all();
screen_reader_speak("Paused. Press backspace to resume.", true);
} else {
p.resume_all();
screen_reader_speak("Resumed.", true);
}
continue;
}
// Skip all game logic while paused
if (game_paused) {
continue;
}
if(key_pressed(KEY_ESCAPE))
{
int really_exit = ui_question("", "Really exit?");
if (really_exit == 1) {
exit();
}
}
// Time & Environment updates
update_time();
update_crossfade();
update_weather();
update_environment();
update_snares();
update_streams();
update_fires();
update_zombies();
update_bandits();
update_boars();
update_flying_creatures();
update_world_drops();
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_altar_menu(x);
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);
}
}