diff --git a/README.md b/README.md index 0023930..5a2465c 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,111 @@ # Draugnorak -A survival audio game built with NVGT. +A survival audio game built with NVGT. Explore, gather, craft, and defend your base as day and night cycle on. + +## Quick Start +- Move with Left/Right, jump with Up. +- Hold Shift for 1 second to search the current area. +- Craft basic tools in the base with C. +- Set snares and build fires to survive. ## Controls -- **Arrow Keys**: Move (Left/Right) and Jump (Up). -- **Shift**: Search the current area for resources. -- **Control**: Attack with equipped weapon. -- **C**: Open Crafting Menu (Base area only). -- **E**: Open Equipment Menu. -- **A**: Open Action Menu (Place objects, feed fire). -- **I**: Check Inventory. +- **Left/Right**: Move. +- **Up**: Jump, climb trees, start rope climbs when prompted. +- **Down**: Climb down trees or descend rope climbs when prompted. +- **Shift (hold)**: Search area (1-second hold, 1-second delay). +- **Control (hold/release)**: Attack with equipped weapon. Sling uses a charge window on release. +- **A**: Action menu (place snare, feed fire, burn incense). +- **C**: Crafting menu (base only). +- **I**: Inventory. +- **E**: Equipment. - **P**: Character info. +- **B**: Base info (base only). - **Q**: Quests menu (base only). -- **H**: Check Health. -- **T**: Check Time. -- **X**: Check Coordinates. -- **B**: Base info menu (base only). -- **Escape**: Exit game. +- **S**: Altar menu (base only). +- **Tab**: Adventure menu (outside base, once per day). +- **H**: Health. +- **T**: Time. +- **X**: Coordinates + terrain. +- **1-0**: Quick slots (equip bound items). +- **Backspace**: Pause/Resume. +- **Escape**: Exit game (or close menus). -## Gameplay -- **Gathering**: Search in grass for sticks and vines. Search in gravel for stones. -- **Crafting**: Create tools like Spears, Axes, Snares, and Firepits (base only). -- **Combat**: Defend yourself or hunt. -- **Survival**: Build fires and set snares to survive. -- **Fire Management**: - - Build a firepit (9 stones) in the base area - it's crafted in place - - Build fires (2 sticks + 1 log) only on tiles with a firepit - - Fires burn out over time and need fuel (vines, sticks, or logs) - - Stand next to a fire and press A to feed it - - Standing on a fire damages your health! +### History Shortcuts +- **\\**: Repeat most recent notification. +- **[ / ]**: Previous/next notification. +- **, / .**: Previous/next speech history message. + +## Menus +- **Up/Down**: Move selection. +- **Enter**: Confirm. +- **Escape**: Close. +- **Tab (in most menus)**: Perform “max” actions (craft all, deposit all, etc.). + +## Gathering and Resources +- **Grass/Forest/Deep Forest**: Sticks and vines (search). +- **Gravel/Stone**: Stones (search). +- **Streams/Water edges**: Reeds and clay (search). +- **World drops**: Found on the ground after hunting; search picks them up. +- **Trees**: + - Search near trees for sticks/vines. + - Use an axe to chop down a tree for logs (and extra sticks/vines). + - Trees regrow over time. + - You can climb trees (Up) and climb down (Down). + +## Crafting (Base Only) +Crafting is only available in the base (C). + +### Categories +- **Weapons**: Spear, Sling +- **Tools**: Knife, Snare, Stone Axe, Fishing Pole, Rope, Quiver, Reed Basket, Clay Pot +- **Materials**: Arrows, Butcher Game, Incense +- **Clothing**: Skin gear, Moccasins, Pouch, Backpack +- **Buildings**: Firepit, Fire, Herb Garden, Storage, Pasture, Stable, Altar +- **Barricade**: Reinforcement options +- **Runes**: Unlocks later + +## Combat and Hunting +- Equip weapons in the Equipment menu (E) or bind them to quick slots. +- **Spear**: 1-tile range. +- **Axe**: Melee; also chops trees. +- **Sling**: Ranged (uses stones). Hold Control to charge; release on the “in-range” audio cue. +- Enemies include zombies (night), bandits (invasions), and boars (daytime). + - Bow combat is planned; arrows are stockpiled for future bow use. + +## Base and Survival +- **Passive healing** in the base when below max health. +- **Herb Garden** increases healing speed (base only, one per base). +- **Fires** require a nearby firepit and need fuel (sticks, vines, logs). +- **Barricade** starts at 100 health, capped at 500; reinforces via Crafting > Barricade. +- **Storage** enables base inventory management and helps attract residents. +- **Residents** (if recruited) consume meat, help defend, repair the barricade, and collect resources. + +## Time, Weather, and Events +- **1 real minute = 1 in-game hour**. +- Day/night ambience crossfades around sunrise/sunset. +- Weather transitions between clear, windy, rainy, and stormy. +- **Invasions** can begin after day 2 and expand the map eastward. +- Expansions can be normal biomes or mountain ranges. + +## Mountains and Rope Climbing +- Mountains have elevation; steep slopes require a rope. +- When prompted, press Up to climb up or Down to climb down. +- Moving left/right during a rope climb cancels and causes a fall. +- Falling from height can cause damage. + +## Quests, Adventures, and Runes +- **Quests** become available in the base once you have an altar and enough favor. +- **Adventures** are accessed with Tab outside the base (once per day). +- The mountain adventure features the **Unicorn boss**. +- Defeating the Unicorn unlocks the **Rune of Swiftness**. +- Runes are engraved in the base (Crafting > Runes) and require a knife, clay, and favor. + +## Inventory and Storage +- Personal inventory stacks are capped at 9. +- Skin pouches add +2 stack capacity, backpacks add +9. +- Arrows require quivers. Each quiver adds capacity for 12 arrows. +- Storage is separate and accessed in the base (Inventory menu when storage exists). + +## Saving and Loading +- The game auto-saves daily at 6 AM (in-game time). +- Load from the main menu. Starting a new game prompts if a save exists. diff --git a/src/base_system.nvgt b/src/base_system.nvgt index fb74e77..5b6b147 100644 --- a/src/base_system.nvgt +++ b/src/base_system.nvgt @@ -1,7 +1,7 @@ // Base automation helpers int get_daily_food_requirement() { if (residents_count <= 0) return 0; - return (residents_count + 1) / 2; + return residents_count * 2; } void consume_food_for_residents() { diff --git a/src/crafting/craft_materials.nvgt b/src/crafting/craft_materials.nvgt index 7b22ca4..ed5cf9c 100644 --- a/src/crafting/craft_materials.nvgt +++ b/src/crafting/craft_materials.nvgt @@ -5,6 +5,7 @@ void run_materials_menu() { int selection = 0; string[] options = { "Butcher Game [Requires Game, Knife, Fire nearby]", + "Arrows (2 Sticks, 4 Feathers, 2 Stones) [Requires Quiver]", "Incense (6 Sticks, 2 Vines, 1 Reed) [Requires Altar]" }; @@ -30,18 +31,91 @@ void run_materials_menu() { if (key_pressed(KEY_RETURN)) { if (selection == 0) butcher_small_game(); - else if (selection == 1) craft_incense(); + else if (selection == 1) craft_arrows(); + else if (selection == 2) craft_incense(); break; } if (key_pressed(KEY_TAB)) { if (selection == 0) butcher_small_game_max(); - else if (selection == 1) craft_incense_max(); + else if (selection == 1) craft_arrows_max(); + else if (selection == 2) craft_incense_max(); break; } } } +void craft_arrows() { + if (get_personal_count(ITEM_QUIVERS) <= 0) { + speak_with_history("You need a quiver to craft arrows.", true); + return; + } + + int currentArrows = get_personal_count(ITEM_ARROWS); + int capacity = get_arrow_limit() - currentArrows; + if (capacity < ARROWS_PER_CRAFT) { + speak_with_history("Not enough quiver capacity for arrows.", true); + return; + } + + string missing = ""; + if (get_personal_count(ITEM_STICKS) < 2) missing += "2 sticks "; + if (get_personal_count(ITEM_FEATHERS) < 4) missing += "4 feathers "; + if (get_personal_count(ITEM_STONES) < 2) missing += "2 stones "; + + if (missing == "") { + simulate_crafting(8); + add_personal_count(ITEM_STICKS, -2); + add_personal_count(ITEM_FEATHERS, -4); + add_personal_count(ITEM_STONES, -2); + add_personal_count(ITEM_ARROWS, ARROWS_PER_CRAFT); + speak_with_history("Crafted " + ARROWS_PER_CRAFT + " arrows.", true); + } else { + speak_with_history("Missing: " + missing, true); + } +} + +void craft_arrows_max() { + if (get_personal_count(ITEM_QUIVERS) <= 0) { + speak_with_history("You need a quiver to craft arrows.", true); + return; + } + + int currentArrows = get_personal_count(ITEM_ARROWS); + int capacity = get_arrow_limit() - currentArrows; + int maxByCapacity = capacity / ARROWS_PER_CRAFT; + if (maxByCapacity <= 0) { + speak_with_history("Not enough quiver capacity for arrows.", true); + return; + } + + int maxBySticks = get_personal_count(ITEM_STICKS) / 2; + int maxByFeathers = get_personal_count(ITEM_FEATHERS) / 4; + int maxByStones = get_personal_count(ITEM_STONES) / 2; + int maxCraft = maxBySticks; + if (maxByFeathers < maxCraft) maxCraft = maxByFeathers; + if (maxByStones < maxCraft) maxCraft = maxByStones; + if (maxByCapacity < maxCraft) maxCraft = maxByCapacity; + + if (maxCraft <= 0) { + string missing = ""; + if (get_personal_count(ITEM_STICKS) < 2) missing += "2 sticks "; + if (get_personal_count(ITEM_FEATHERS) < 4) missing += "4 feathers "; + if (get_personal_count(ITEM_STONES) < 2) missing += "2 stones "; + speak_with_history("Missing: " + missing, true); + return; + } + + int totalCost = maxCraft * 8; // 2 sticks + 4 feathers + 2 stones per bundle + int craftTime = (totalCost < maxCraft * 4) ? maxCraft * 4 : totalCost; + simulate_crafting(craftTime); + add_personal_count(ITEM_STICKS, -(maxCraft * 2)); + add_personal_count(ITEM_FEATHERS, -(maxCraft * 4)); + add_personal_count(ITEM_STONES, -(maxCraft * 2)); + add_personal_count(ITEM_ARROWS, ARROWS_PER_CRAFT * maxCraft); + speak_with_history("Crafted " + (ARROWS_PER_CRAFT * maxCraft) + " arrows.", true); +} + void craft_incense() { if (world_altars.length() == 0) { speak_with_history("You need an altar to craft incense.", true); diff --git a/src/crafting/craft_tools.nvgt b/src/crafting/craft_tools.nvgt index d884b63..ed0d169 100644 --- a/src/crafting/craft_tools.nvgt +++ b/src/crafting/craft_tools.nvgt @@ -9,6 +9,7 @@ void run_tools_menu() { "Stone Axe (1 Stick, 1 Vine, 2 Stones) [Requires Knife]", "Fishing Pole (1 Stick, 2 Vines)", "Rope (3 Vines)", + "Quiver (2 Skins, 2 Vines)", "Reed Basket (3 Reeds)", "Clay Pot (3 Clay)" }; @@ -39,8 +40,9 @@ void run_tools_menu() { else if (selection == 2) craft_axe(); else if (selection == 3) craft_fishing_pole(); else if (selection == 4) craft_rope(); - else if (selection == 5) craft_reed_basket(); - else if (selection == 6) craft_clay_pot(); + else if (selection == 5) craft_quiver(); + else if (selection == 6) craft_reed_basket(); + else if (selection == 7) craft_clay_pot(); break; } @@ -50,8 +52,9 @@ void run_tools_menu() { else if (selection == 2) craft_axe_max(); else if (selection == 3) craft_fishing_pole_max(); else if (selection == 4) craft_rope_max(); - else if (selection == 5) craft_reed_basket_max(); - else if (selection == 6) craft_clay_pot_max(); + else if (selection == 5) craft_quiver_max(); + else if (selection == 6) craft_reed_basket_max(); + else if (selection == 7) craft_clay_pot_max(); break; } } @@ -242,6 +245,57 @@ void craft_rope_max() { speak_with_history("Crafted " + max_craft + " Rope.", true); } +void craft_quiver() { + string missing = ""; + if (get_personal_count(ITEM_SKINS) < 2) missing += "2 skins "; + if (get_personal_count(ITEM_VINES) < 2) missing += "2 vines "; + + if (missing == "") { + if (get_personal_count(ITEM_QUIVERS) >= get_personal_stack_limit()) { + speak_with_history("You can't carry any more quivers.", true); + return; + } + simulate_crafting(4); + add_personal_count(ITEM_SKINS, -2); + add_personal_count(ITEM_VINES, -2); + add_personal_count(ITEM_QUIVERS, 1); + speak_with_history("Crafted a Quiver.", true); + } else { + speak_with_history("Missing: " + missing, true); + } +} + +void craft_quiver_max() { + if (get_personal_count(ITEM_QUIVERS) >= get_personal_stack_limit()) { + speak_with_history("You can't carry any more quivers.", true); + return; + } + + int maxBySkins = get_personal_count(ITEM_SKINS) / 2; + int maxByVines = get_personal_count(ITEM_VINES) / 2; + int maxCraft = maxBySkins; + if (maxByVines < maxCraft) maxCraft = maxByVines; + + int space = get_personal_stack_limit() - get_personal_count(ITEM_QUIVERS); + if (maxCraft > space) maxCraft = space; + + if (maxCraft <= 0) { + string missing = ""; + if (get_personal_count(ITEM_SKINS) < 2) missing += "2 skins "; + if (get_personal_count(ITEM_VINES) < 2) missing += "2 vines "; + speak_with_history("Missing: " + missing, true); + return; + } + + int totalCost = maxCraft * 4; // 2 skins + 2 vines per quiver + int craftTime = (totalCost < maxCraft * 4) ? maxCraft * 4 : totalCost; + simulate_crafting(craftTime); + add_personal_count(ITEM_SKINS, -(maxCraft * 2)); + add_personal_count(ITEM_VINES, -(maxCraft * 2)); + add_personal_count(ITEM_QUIVERS, maxCraft); + speak_with_history("Crafted " + maxCraft + " Quivers.", true); +} + void craft_reed_basket() { string missing = ""; if (get_personal_count(ITEM_REEDS) < 3) missing += "3 reeds "; diff --git a/src/inventory_items.nvgt b/src/inventory_items.nvgt index 8c7f1a6..2ea762f 100644 --- a/src/inventory_items.nvgt +++ b/src/inventory_items.nvgt @@ -63,6 +63,19 @@ int get_arrow_limit() { return quivers * ARROW_CAPACITY_PER_QUIVER; } +void clamp_arrows_to_quiver_limit() { + int maxArrows = get_arrow_limit(); + int currentArrows = get_personal_count(ITEM_ARROWS); + if (currentArrows <= maxArrows) return; + + set_personal_count(ITEM_ARROWS, maxArrows); + if (maxArrows == 0) { + speak_with_history("You need a quiver to carry arrows.", true); + } else { + speak_with_history("You can only carry " + maxArrows + " arrows with your current quivers.", true); + } +} + string get_equipment_name(int equip_type) { if (equip_type == EQUIP_SPEAR) return "Spear"; if (equip_type == EQUIP_AXE) return "Stone Axe"; @@ -274,5 +287,6 @@ void cleanup_equipment_after_inventory_change() { if (get_personal_count(ITEM_MOCCASINS) <= 0) equipped_feet = EQUIP_NONE; if (get_personal_count(ITEM_SKIN_POUCHES) <= 0 && equipped_arms == EQUIP_POUCH) equipped_arms = EQUIP_NONE; if (get_personal_count(ITEM_BACKPACKS) <= 0 && equipped_arms == EQUIP_BACKPACK) equipped_arms = EQUIP_NONE; + clamp_arrows_to_quiver_limit(); update_max_health_from_equipment(); } diff --git a/src/menus/altar_menu.nvgt b/src/menus/altar_menu.nvgt index 6fe8769..92436ea 100644 --- a/src/menus/altar_menu.nvgt +++ b/src/menus/altar_menu.nvgt @@ -30,38 +30,15 @@ void sacrifice_item(int item_type) { return; } - if (item_type == ITEM_STICKS) add_personal_count(ITEM_STICKS, -1); - else if (item_type == ITEM_VINES) add_personal_count(ITEM_VINES, -1); - else if (item_type == ITEM_REEDS) add_personal_count(ITEM_REEDS, -1); - else if (item_type == ITEM_STONES) add_personal_count(ITEM_STONES, -1); - else if (item_type == ITEM_LOGS) add_personal_count(ITEM_LOGS, -1); - else if (item_type == ITEM_CLAY) add_personal_count(ITEM_CLAY, -1); - else if (item_type == ITEM_SMALL_GAME) { + if (item_type == ITEM_SMALL_GAME) { add_personal_count(ITEM_SMALL_GAME, -1); if (personal_small_game_types.length() > 0) { personal_small_game_types.remove_at(0); } } - else if (item_type == ITEM_MEAT) add_personal_count(ITEM_MEAT, -1); - else if (item_type == ITEM_SKINS) add_personal_count(ITEM_SKINS, -1); - else if (item_type == ITEM_FEATHERS) add_personal_count(ITEM_FEATHERS, -1); - else if (item_type == ITEM_DOWN) add_personal_count(ITEM_DOWN, -1); - else if (item_type == ITEM_INCENSE) add_personal_count(ITEM_INCENSE, -1); - else if (item_type == ITEM_SPEARS) add_personal_count(ITEM_SPEARS, -1); - else if (item_type == ITEM_SLINGS) add_personal_count(ITEM_SLINGS, -1); - else if (item_type == ITEM_AXES) add_personal_count(ITEM_AXES, -1); - else if (item_type == ITEM_SNARES) add_personal_count(ITEM_SNARES, -1); - else if (item_type == ITEM_KNIVES) add_personal_count(ITEM_KNIVES, -1); - else if (item_type == ITEM_FISHING_POLES) add_personal_count(ITEM_FISHING_POLES, -1); - else if (item_type == ITEM_ROPES) add_personal_count(ITEM_ROPES, -1); - else if (item_type == ITEM_REED_BASKETS) add_personal_count(ITEM_REED_BASKETS, -1); - else if (item_type == ITEM_CLAY_POTS) add_personal_count(ITEM_CLAY_POTS, -1); - else if (item_type == ITEM_SKIN_HATS) add_personal_count(ITEM_SKIN_HATS, -1); - else if (item_type == ITEM_SKIN_GLOVES) add_personal_count(ITEM_SKIN_GLOVES, -1); - else if (item_type == ITEM_SKIN_PANTS) add_personal_count(ITEM_SKIN_PANTS, -1); - else if (item_type == ITEM_SKIN_TUNICS) add_personal_count(ITEM_SKIN_TUNICS, -1); - else if (item_type == ITEM_MOCCASINS) add_personal_count(ITEM_MOCCASINS, -1); - else if (item_type == ITEM_SKIN_POUCHES) add_personal_count(ITEM_SKIN_POUCHES, -1); + else { + add_personal_count(item_type, -1); + } cleanup_equipment_after_inventory_change(); double favor_gain = get_item_favor_value(item_type); @@ -82,36 +59,13 @@ void sacrifice_item_max(int item_type) { double favor_per_item = get_item_favor_value(item_type); - if (item_type == ITEM_STICKS) { set_personal_count(ITEM_STICKS, 0); } - else if (item_type == ITEM_VINES) { set_personal_count(ITEM_VINES, 0); } - else if (item_type == ITEM_REEDS) { set_personal_count(ITEM_REEDS, 0); } - else if (item_type == ITEM_STONES) { set_personal_count(ITEM_STONES, 0); } - else if (item_type == ITEM_LOGS) { set_personal_count(ITEM_LOGS, 0); } - else if (item_type == ITEM_CLAY) { set_personal_count(ITEM_CLAY, 0); } - else if (item_type == ITEM_SMALL_GAME) { + if (item_type == ITEM_SMALL_GAME) { set_personal_count(ITEM_SMALL_GAME, 0); personal_small_game_types.resize(0); } - else if (item_type == ITEM_MEAT) { set_personal_count(ITEM_MEAT, 0); } - else if (item_type == ITEM_SKINS) { set_personal_count(ITEM_SKINS, 0); } - else if (item_type == ITEM_FEATHERS) { set_personal_count(ITEM_FEATHERS, 0); } - else if (item_type == ITEM_DOWN) { set_personal_count(ITEM_DOWN, 0); } - else if (item_type == ITEM_INCENSE) { set_personal_count(ITEM_INCENSE, 0); } - else if (item_type == ITEM_SPEARS) { set_personal_count(ITEM_SPEARS, 0); } - else if (item_type == ITEM_SLINGS) { set_personal_count(ITEM_SLINGS, 0); } - else if (item_type == ITEM_AXES) { set_personal_count(ITEM_AXES, 0); } - else if (item_type == ITEM_SNARES) { set_personal_count(ITEM_SNARES, 0); } - else if (item_type == ITEM_KNIVES) { set_personal_count(ITEM_KNIVES, 0); } - else if (item_type == ITEM_FISHING_POLES) { set_personal_count(ITEM_FISHING_POLES, 0); } - else if (item_type == ITEM_ROPES) { set_personal_count(ITEM_ROPES, 0); } - else if (item_type == ITEM_REED_BASKETS) { set_personal_count(ITEM_REED_BASKETS, 0); } - else if (item_type == ITEM_CLAY_POTS) { set_personal_count(ITEM_CLAY_POTS, 0); } - else if (item_type == ITEM_SKIN_HATS) { set_personal_count(ITEM_SKIN_HATS, 0); } - else if (item_type == ITEM_SKIN_GLOVES) { set_personal_count(ITEM_SKIN_GLOVES, 0); } - else if (item_type == ITEM_SKIN_PANTS) { set_personal_count(ITEM_SKIN_PANTS, 0); } - else if (item_type == ITEM_SKIN_TUNICS) { set_personal_count(ITEM_SKIN_TUNICS, 0); } - else if (item_type == ITEM_MOCCASINS) { set_personal_count(ITEM_MOCCASINS, 0); } - else if (item_type == ITEM_SKIN_POUCHES) { set_personal_count(ITEM_SKIN_POUCHES, 0); } + else { + set_personal_count(item_type, 0); + } cleanup_equipment_after_inventory_change(); double total_favor = favor_per_item * available; diff --git a/src/menus/storage_menu.nvgt b/src/menus/storage_menu.nvgt index 2843ff3..5d9ad02 100644 --- a/src/menus/storage_menu.nvgt +++ b/src/menus/storage_menu.nvgt @@ -166,7 +166,16 @@ void withdraw_item(int item_type) { speak_with_history("Nothing to withdraw.", true); return; } - int capacity = get_personal_stack_limit() - get_personal_count(item_type); + int capacity = 0; + if (item_type == ITEM_ARROWS) { + capacity = get_arrow_limit() - get_personal_count(ITEM_ARROWS); + if (capacity <= 0) { + speak_with_history("You can't carry any more arrows.", true); + return; + } + } else { + capacity = get_personal_stack_limit() - get_personal_count(item_type); + } if (capacity <= 0) { speak_with_history("You can't carry any more " + get_item_label(item_type) + ".", true); return; @@ -212,9 +221,20 @@ void withdraw_item_max(int item_type) { return; } - int personal_limit = get_personal_stack_limit(); - int current_personal = get_personal_count(item_type); - int space = personal_limit - current_personal; + int personalLimit = 0; + int currentPersonal = 0; + if (item_type == ITEM_ARROWS) { + personalLimit = get_arrow_limit(); + currentPersonal = get_personal_count(ITEM_ARROWS); + if (personalLimit <= 0) { + speak_with_history("You need a quiver to carry arrows.", true); + return; + } + } else { + personalLimit = get_personal_stack_limit(); + currentPersonal = get_personal_count(item_type); + } + int space = personalLimit - currentPersonal; if (space <= 0) { speak_with_history("Can't carry any more.", true); diff --git a/src/time_system.nvgt b/src/time_system.nvgt index 7c74d19..f9fd4ad 100644 --- a/src/time_system.nvgt +++ b/src/time_system.nvgt @@ -406,6 +406,8 @@ void update_time() { attempt_resident_collection(); } + ensure_ambience_running(); + // Proactive resident defense with slings attempt_resident_sling_defense(); @@ -533,6 +535,9 @@ void complete_crossfade() { } if (night_sound_handle != -1) p.update_sound_start_values(night_sound_handle, 0.0, 0.0, 1.0); is_daytime = false; + if (night_sound_handle == -1 || !p.sound_is_active(night_sound_handle)) { + update_ambience(false); + } } else { // Destroy night sound, ensure day is at full volume if (night_sound_handle != -1) { @@ -541,6 +546,23 @@ void complete_crossfade() { } if (day_sound_handle != -1) p.update_sound_start_values(day_sound_handle, 0.0, 0.0, 1.0); is_daytime = true; + if (day_sound_handle == -1 || !p.sound_is_active(day_sound_handle)) { + update_ambience(false); + } + } +} + +void ensure_ambience_running() { + if (crossfade_active) return; + + if (is_daytime) { + if (day_sound_handle == -1 || !p.sound_is_active(day_sound_handle)) { + update_ambience(false); + } + } else { + if (night_sound_handle == -1 || !p.sound_is_active(night_sound_handle)) { + update_ambience(false); + } } }