A few bug fixes, new item, canoe, added.
This commit is contained in:
142
AGENTS.md
142
AGENTS.md
@@ -8,21 +8,49 @@
|
|||||||
|
|
||||||
- `src/` - Game source code modules
|
- `src/` - Game source code modules
|
||||||
- `constants.nvgt` - Game configuration and constants
|
- `constants.nvgt` - Game configuration and constants
|
||||||
- `player.nvgt` - Player state and variables
|
- `player.nvgt` - Player state and global timers
|
||||||
- `world_state.nvgt` - World objects (snares, fires, firepits, herb gardens)
|
- `world_state.nvgt` - World object orchestration (legacy coordinator, modules are in `src/world/` and `src/enemies/`)
|
||||||
- `inventory.nvgt` - Inventory, crafting, equipment menus
|
- `item_registry.nvgt` - Item definitions and inventory arrays
|
||||||
- `environment.nvgt` - Trees and environmental interactions
|
- `inventory_items.nvgt` - Equipment, quick slots, stack limits
|
||||||
- `combat.nvgt` - Combat system
|
- `inventory_menus.nvgt` - Menu orchestrator for inventory/storage/equipment/actions
|
||||||
- `time_system.nvgt` - In-game time tracking
|
- `crafting.nvgt` - Crafting orchestrator
|
||||||
|
- `quest_system.nvgt` - Quest queue + mini-game menu
|
||||||
|
- `bosses/adventure_system.nvgt` - Adventure menu + boss routing
|
||||||
|
- `inventory.nvgt` - Inventory system orchestrator (item registry, menus, crafting, runes)
|
||||||
|
- `environment.nvgt` - Trees, climbing, falling damage, rope climbing, and environmental interactions
|
||||||
|
- `combat.nvgt` - Combat system (spear/axe/sling)
|
||||||
|
- `time_system.nvgt` - Time, day/night, invasions, expansions, blessings, weather ticks
|
||||||
|
- `weather.nvgt` - Wind/rain/thunder ambience system
|
||||||
|
- `save_system.nvgt` - Save/load (AES encrypted, versioned)
|
||||||
|
- `base_system.nvgt` - Base automation (residents, food, defense, collection)
|
||||||
|
- `ui.nvgt` - UI helpers, terrain lookup
|
||||||
- `audio_utils.nvgt` - Audio helper functions
|
- `audio_utils.nvgt` - Audio helper functions
|
||||||
- `notify.nvgt` - Notification system
|
- `creature_audio.nvgt` - Creature voice/footstep/attack audio helpers
|
||||||
|
- `notify.nvgt` - Notification system (queue + history)
|
||||||
|
- `speech_history.nvgt` - Screen reader message history (comma/period navigation)
|
||||||
|
- `text_reader.nvgt` - Accessible text reader window
|
||||||
|
- `src/world/` - World objects
|
||||||
|
- `world_fires.nvgt`, `world_snares.nvgt`, `world_drops.nvgt`
|
||||||
|
- `world_buildings.nvgt` - Firepit, Herb Garden, Storage, Pasture, Stable, Altar
|
||||||
|
- `world_streams.nvgt` - Streams + water audio
|
||||||
|
- `mountains.nvgt` - Mountain ranges, elevation, streams
|
||||||
|
- `barricade.nvgt` - Base barricade health and reinforcement
|
||||||
|
- `src/enemies/` - Enemies and creatures
|
||||||
|
- `undead.nvgt` (zombies), `bandit.nvgt` (invasions/wandering), `ground_game.nvgt` (boars), `flying_creatures.nvgt` (geese)
|
||||||
|
- `src/crafting/` - Crafting categories and recipes
|
||||||
|
- `crafting_core.nvgt`, `craft_weapons.nvgt`, `craft_tools.nvgt`, `craft_materials.nvgt`, `craft_clothing.nvgt`, `craft_buildings.nvgt`, `craft_barricade.nvgt`, `craft_runes.nvgt`
|
||||||
|
- `src/menus/` - Menu subsystems (inventory, storage, equipment, action, base info, altar, character info)
|
||||||
|
- `src/runes/` - Rune data + effects
|
||||||
|
- `src/quests/` - Quest mini-games
|
||||||
|
- `src/bosses/` - Adventure/boss encounters
|
||||||
- `sounds/` - Game audio assets
|
- `sounds/` - Game audio assets
|
||||||
- `draugnorak.nvgt` - Main game file
|
- `draugnorak.nvgt` - Main game file
|
||||||
|
|
||||||
## Game Mechanics
|
## Game Mechanics
|
||||||
|
|
||||||
### Inventory Limits
|
### Inventory Limits
|
||||||
- All item stacks are capped at 9 until backpacks are implemented
|
- Personal inventory stacks are capped at 9
|
||||||
|
- Skin pouches add +2 stack capacity, backpacks add +9
|
||||||
|
|
||||||
### Map Layout
|
### Map Layout
|
||||||
- Base area: x 0-4 (wood terrain)
|
- Base area: x 0-4 (wood terrain)
|
||||||
@@ -41,21 +69,23 @@
|
|||||||
- New fires start with 12 minutes (720000ms) of fuel
|
- New fires start with 12 minutes (720000ms) of fuel
|
||||||
- Warning at 30 seconds remaining
|
- Warning at 30 seconds remaining
|
||||||
- Fire goes out when fuel depletes
|
- Fire goes out when fuel depletes
|
||||||
- Fire sound plays within 2 tiles distance
|
- Fire sound plays within 3 tiles distance
|
||||||
- Jumping over fire prevents damage
|
- Jumping over fire prevents damage
|
||||||
|
- Feeding fire is done from the Action menu (A)
|
||||||
|
|
||||||
### Snare System
|
### Snare System
|
||||||
- Snares become active when player moves away
|
- Snares become active when player moves away
|
||||||
- Check every minute for catching/escaping rabbits
|
- Check every minute for catching/escaping small game
|
||||||
- Catch chance starts at 5%, increases by 5% per minute (max 75%)
|
- Catch chance starts at 5%, increases by 5% per minute (max 75%)
|
||||||
- Escape chance starts at 5%, increases by 5% per minute (max 95%)
|
- Escape chance starts at 5%, increases by 5% per minute (max 95%)
|
||||||
- Snare sound plays within 2 tiles distance
|
- Snare sound plays within 2 tiles distance
|
||||||
|
|
||||||
### Tree System
|
### Tree System
|
||||||
- Trees regenerate after 4 minutes (240000ms) when depleted or chopped
|
- Trees regenerate fully after ~5 minutes (minute-by-minute refill logic)
|
||||||
- Tree ambient sound plays within 3 tiles distance
|
- Tree ambient sound plays within 4 tiles distance
|
||||||
- Trees only play sound when not chopped
|
- Trees only play sound when not chopped
|
||||||
- Climb down only when in a tree (Down arrow does nothing during jumps or when not in a tree)
|
- Climb down only when in a tree (Down arrow does nothing during jumps or when not in a tree)
|
||||||
|
- Trees have height; falling damage is applied when falling from height
|
||||||
|
|
||||||
### Notification System
|
### Notification System
|
||||||
- Important events use `notify()` function (plays sounds/notify.ogg + speaks message)
|
- Important events use `notify()` function (plays sounds/notify.ogg + speaks message)
|
||||||
@@ -65,6 +95,7 @@
|
|||||||
- `]` - Next (newer) notification
|
- `]` - Next (newer) notification
|
||||||
- `[` (Shift+Comma) - Previous (older) notification
|
- `[` (Shift+Comma) - Previous (older) notification
|
||||||
- Navigating history speaks message without notification sound
|
- Navigating history speaks message without notification sound
|
||||||
|
- Speech history (screen_reader_speak wrapper) uses `,` and `.` for previous/next message
|
||||||
|
|
||||||
### Search System
|
### Search System
|
||||||
- Hold Shift for 1 second to search
|
- Hold Shift for 1 second to search
|
||||||
@@ -75,7 +106,7 @@
|
|||||||
- Zombies cannot enter the base while barricade health > 0
|
- Zombies cannot enter the base while barricade health > 0
|
||||||
- Zombies attack the barricade for 4-6 damage when they reach it; play `sounds/enemies/zombie_hit.ogg`
|
- Zombies attack the barricade for 4-6 damage when they reach it; play `sounds/enemies/zombie_hit.ogg`
|
||||||
- Zombies vanish at daybreak
|
- Zombies vanish at daybreak
|
||||||
- Player can hit zombies with spear (1-tile range) or sling (5-tile range)
|
- Player can hit zombies with spear (1-tile range) or sling (8-tile range)
|
||||||
|
|
||||||
### Barricade System
|
### Barricade System
|
||||||
- Base starts with 100 barricade health, capped at 500
|
- Base starts with 100 barricade health, capped at 500
|
||||||
@@ -84,25 +115,98 @@
|
|||||||
- Reinforcement costs and health: 3 sticks (+10), 5 vines (+15), 1 log (+30), 5 stones (+20)
|
- Reinforcement costs and health: 3 sticks (+10), 5 vines (+15), 1 log (+30), 5 stones (+20)
|
||||||
- Barricade health does not reset during gameplay
|
- Barricade health does not reset during gameplay
|
||||||
|
|
||||||
|
### Mountains, Rope Climbing, Falling
|
||||||
|
- Mountain ranges can appear during area expansion with elevation-based terrain
|
||||||
|
- Steep slopes require a rope; the player is prompted to press Up/Down to climb
|
||||||
|
- Moving left/right during rope climb cancels and causes a fall
|
||||||
|
- Falling damage is applied beyond a safe height (10 feet)
|
||||||
|
|
||||||
|
### World Expansion & Invasions
|
||||||
|
- After day 2, bandit invasions can trigger and expand the map to the east
|
||||||
|
- Expansion is either a 30-tile biome strip or a 60-tile mountain range
|
||||||
|
- Invasions last 1 hour and spawn bandits in the expanded area during daytime
|
||||||
|
|
||||||
|
### Weather & Ambience
|
||||||
|
- Weather transitions between clear, windy, rainy, stormy with wind/rain/thunder audio
|
||||||
|
- Day/night ambience crossfades on hour changes
|
||||||
|
|
||||||
|
### Inventory + Equipment
|
||||||
|
- Item definitions live in `src/item_registry.nvgt` (personal + storage inventories)
|
||||||
|
- Equipment includes spear, axe, sling, bow, clothing, pouches, backpacks
|
||||||
|
- Combat logic currently supports spear/axe/sling; bow attacks are not implemented yet
|
||||||
|
- Quivers gate arrow capacity (12 arrows per quiver)
|
||||||
|
- Quick slots (keys 1-0) bind equipment from the Equipment menu
|
||||||
|
|
||||||
|
### Buildings
|
||||||
|
- Firepit, Fire, Herb Garden, Storage, Pasture, Stable, Altar
|
||||||
|
- Storage enables base inventory menus and increases resident recruitment chance
|
||||||
|
|
||||||
|
### Residents and Base Automation
|
||||||
|
- Residents consume meat daily, can repair the barricade, defend with stored weapons, and collect resources
|
||||||
|
- Residents use spears or slings (requires stones) for defense
|
||||||
|
- Daily weapon breakage occurs based on resident count
|
||||||
|
|
||||||
|
### Favor, Altars, Incense, Blessings
|
||||||
|
- Altar sacrifices (S key, base only) convert items to Favor
|
||||||
|
- Burning incense (A key action) grants Favor per hour while active
|
||||||
|
- Blessings can trigger (heal, speed boost, barricade repair) and consume Favor
|
||||||
|
|
||||||
|
### Quests (Mini-Games)
|
||||||
|
- Quest menu (Q, base only) appears after altar/favor requirements
|
||||||
|
- Max 4 active quests; rewards grant Favor and resources
|
||||||
|
|
||||||
|
### Adventures & Bosses
|
||||||
|
- Adventure menu (Tab) available outside base; limited to once per day
|
||||||
|
- Mountain adventure: Unicorn boss; victory grants Favor and unlocks Rune of Swiftness
|
||||||
|
|
||||||
|
### Runes
|
||||||
|
- Rune system in `src/runes/` (data + effects)
|
||||||
|
- Runes are crafted in Crafting > Runes after unlock (requires knife + clay + favor)
|
||||||
|
- Rune of Swiftness grants move speed and gathering bonuses; runed items are tracked separately
|
||||||
|
|
||||||
## Menu Structure
|
## Menu Structure
|
||||||
|
|
||||||
### Crafting Menu (C key, base only)
|
### Crafting Menu (C key, base only)
|
||||||
Organized into three categories:
|
Organized into categories:
|
||||||
- **Weapons**: Spear
|
- **Weapons**: Spear, Sling
|
||||||
- **Tools**: Stone Knife, Snare, Stone Axe
|
- **Tools**: Knife, Snare, Stone Axe, Fishing Pole, Rope, Reed Basket, Clay Pot
|
||||||
- **Buildings**: Firepit, Fire, Herb Garden
|
- **Materials**: Butcher Game, Incense
|
||||||
|
- **Clothing**: Skin gear, Moccasins, Pouch, Backpack
|
||||||
|
- **Buildings**: Firepit, Fire, Herb Garden, Storage, Pasture, Stable, Altar
|
||||||
|
- **Barricade**: Reinforcement options
|
||||||
|
- **Runes**: Available after rune unlocks
|
||||||
|
|
||||||
### Equipment Menu (E key)
|
### Equipment Menu (E key)
|
||||||
- Only shows items player actually has
|
- Only shows items player actually has
|
||||||
- Shows equipped status
|
- Shows equipped status
|
||||||
- Says "Nothing to equip" if inventory is empty
|
- Says "Nothing to equip" if inventory is empty
|
||||||
|
|
||||||
|
### Inventory Menu (I key)
|
||||||
|
- Base + storage built: root menu for personal vs storage
|
||||||
|
- No storage: personal inventory only
|
||||||
|
|
||||||
|
### Action Menu (A key)
|
||||||
|
- Place snares, feed fires, burn incense (context-sensitive)
|
||||||
|
- Tab in menu performs “max” action for the selected option
|
||||||
|
|
||||||
|
### Base Info (B key)
|
||||||
|
- Barricade health, residents, storage totals, base buildings
|
||||||
|
|
||||||
|
### Quest Menu (Q key)
|
||||||
|
- Base-only quest selection for mini-games
|
||||||
|
|
||||||
|
### Adventure Menu (Tab key)
|
||||||
|
- Terrain-based adventures outside the base (once per day)
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
- Always use `./nvgt -c draugnorak.nvgt` to compile without opening the game window
|
- Always run `./nvgt -c draugnorak.nvgt` after code changes
|
||||||
- This prevents the window from taking over the terminal during testing
|
- This compiles without opening the game window and prevents it from taking over the terminal
|
||||||
|
|
||||||
## Code Standards
|
## Code Standards
|
||||||
|
|
||||||
- Use `notify()` for important game events that should be reviewable
|
- Use `notify()` for important game events that should be reviewable
|
||||||
- Use `screen_reader_speak()` for immediate feedback that doesn't need to be stored
|
- Use `screen_reader_speak()` for immediate feedback that doesn't need to be stored
|
||||||
|
- All menus must call `menu_background_tick()` each loop to keep game state updating
|
||||||
|
- See `docs/patterns.md` for module and creature patterns
|
||||||
|
- See `docs/rune_system.md` for rune data/effects/save requirements
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ Crafting is only available in the base (C).
|
|||||||
|
|
||||||
### Categories
|
### Categories
|
||||||
- **Weapons**: Spear, Sling
|
- **Weapons**: Spear, Sling
|
||||||
- **Tools**: Knife, Snare, Stone Axe, Fishing Pole, Rope, Quiver, Reed Basket, Clay Pot
|
- **Tools**: Knife, Snare, Stone Axe, Fishing Pole, Rope, Quiver, Canoe, Reed Basket, Clay Pot
|
||||||
- **Materials**: Arrows, Butcher Game, Incense
|
- **Materials**: Arrows, Butcher Game, Incense
|
||||||
- **Clothing**: Skin gear, Moccasins, Pouch, Backpack
|
- **Clothing**: Skin gear, Moccasins, Pouch, Backpack
|
||||||
- **Buildings**: Firepit, Fire, Herb Garden, Storage, Pasture, Stable, Altar
|
- **Buildings**: Firepit, Fire, Herb Garden, Storage, Pasture, Stable, Altar
|
||||||
|
|||||||
@@ -332,9 +332,10 @@ void main()
|
|||||||
if(key_down(KEY_LEFT) && x > 0 && !climbing && !falling && !rope_climbing)
|
if(key_down(KEY_LEFT) && x > 0 && !climbing && !falling && !rope_climbing)
|
||||||
{
|
{
|
||||||
facing = 0;
|
facing = 0;
|
||||||
// Check mountain movement
|
int target_x = x - 1;
|
||||||
if (can_move_mountain(x, x - 1)) {
|
// Check mountain movement and deep water
|
||||||
x--;
|
if (can_move_mountain(x, target_x) && can_enter_stream_tile(target_x)) {
|
||||||
|
x = target_x;
|
||||||
// Always update elevation to match ground (0 if not in mountain)
|
// Always update elevation to match ground (0 if not in mountain)
|
||||||
int new_elevation = get_mountain_elevation_at(x);
|
int new_elevation = get_mountain_elevation_at(x);
|
||||||
if (new_elevation != y) {
|
if (new_elevation != y) {
|
||||||
@@ -350,9 +351,10 @@ void main()
|
|||||||
else if(key_down(KEY_RIGHT) && x < MAP_SIZE - 1 && !climbing && !falling && !rope_climbing)
|
else if(key_down(KEY_RIGHT) && x < MAP_SIZE - 1 && !climbing && !falling && !rope_climbing)
|
||||||
{
|
{
|
||||||
facing = 1;
|
facing = 1;
|
||||||
// Check mountain movement
|
int target_x = x + 1;
|
||||||
if (can_move_mountain(x, x + 1)) {
|
// Check mountain movement and deep water
|
||||||
x++;
|
if (can_move_mountain(x, target_x) && can_enter_stream_tile(target_x)) {
|
||||||
|
x = target_x;
|
||||||
// Always update elevation to match ground (0 if not in mountain)
|
// Always update elevation to match ground (0 if not in mountain)
|
||||||
int new_elevation = get_mountain_elevation_at(x);
|
int new_elevation = get_mountain_elevation_at(x);
|
||||||
if (new_elevation != y) {
|
if (new_elevation != y) {
|
||||||
|
|||||||
BIN
sounds/terrain/deep_water.ogg
LFS
Normal file
BIN
sounds/terrain/deep_water.ogg
LFS
Normal file
Binary file not shown.
@@ -1,6 +1,9 @@
|
|||||||
string get_footstep_sound(int current_x, int base_end, int grass_end)
|
string get_footstep_sound(int current_x, int base_end, int grass_end)
|
||||||
{
|
{
|
||||||
// Check if in water first (regular streams or mountain streams)
|
// Check if in water first (regular streams or mountain streams)
|
||||||
|
if (is_deep_stream_at(current_x)) {
|
||||||
|
return "sounds/terrain/deep_water.ogg";
|
||||||
|
}
|
||||||
if (is_position_in_water(current_x) || is_mountain_stream_at(current_x)) {
|
if (is_position_in_water(current_x) || is_mountain_stream_at(current_x)) {
|
||||||
return "sounds/terrain/shallow_water.ogg";
|
return "sounds/terrain/shallow_water.ogg";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ const int ARROW_CAPACITY_PER_QUIVER = 12;
|
|||||||
// Zombie settings
|
// Zombie settings
|
||||||
const int ZOMBIE_HEALTH = 12;
|
const int ZOMBIE_HEALTH = 12;
|
||||||
const int ZOMBIE_MAX_COUNT = 5;
|
const int ZOMBIE_MAX_COUNT = 5;
|
||||||
|
const int ZOMBIE_MAX_COUNT_CAP = 12;
|
||||||
const int ZOMBIE_MOVE_INTERVAL = 1000;
|
const int ZOMBIE_MOVE_INTERVAL = 1000;
|
||||||
const int ZOMBIE_ATTACK_INTERVAL = 1600;
|
const int ZOMBIE_ATTACK_INTERVAL = 1600;
|
||||||
const int ZOMBIE_DAMAGE_MIN = 4;
|
const int ZOMBIE_DAMAGE_MIN = 4;
|
||||||
@@ -239,4 +240,3 @@ const int RESIDENT_COLLECTION_CHANCE = 10; // 10% chance per basket per hour
|
|||||||
int abs(int value) {
|
int abs(int value) {
|
||||||
return value < 0 ? -value : value;
|
return value < 0 ? -value : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ void run_tools_menu() {
|
|||||||
"Fishing Pole (1 Stick, 2 Vines)",
|
"Fishing Pole (1 Stick, 2 Vines)",
|
||||||
"Rope (3 Vines)",
|
"Rope (3 Vines)",
|
||||||
"Quiver (2 Skins, 2 Vines)",
|
"Quiver (2 Skins, 2 Vines)",
|
||||||
|
"Canoe (4 Logs, 11 Sticks, 11 Vines, 6 Skins, 2 Rope, 6 Reeds)",
|
||||||
"Reed Basket (3 Reeds)",
|
"Reed Basket (3 Reeds)",
|
||||||
"Clay Pot (3 Clay)"
|
"Clay Pot (3 Clay)"
|
||||||
};
|
};
|
||||||
@@ -41,8 +42,9 @@ void run_tools_menu() {
|
|||||||
else if (selection == 3) craft_fishing_pole();
|
else if (selection == 3) craft_fishing_pole();
|
||||||
else if (selection == 4) craft_rope();
|
else if (selection == 4) craft_rope();
|
||||||
else if (selection == 5) craft_quiver();
|
else if (selection == 5) craft_quiver();
|
||||||
else if (selection == 6) craft_reed_basket();
|
else if (selection == 6) craft_canoe();
|
||||||
else if (selection == 7) craft_clay_pot();
|
else if (selection == 7) craft_reed_basket();
|
||||||
|
else if (selection == 8) craft_clay_pot();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,8 +55,9 @@ void run_tools_menu() {
|
|||||||
else if (selection == 3) craft_fishing_pole_max();
|
else if (selection == 3) craft_fishing_pole_max();
|
||||||
else if (selection == 4) craft_rope_max();
|
else if (selection == 4) craft_rope_max();
|
||||||
else if (selection == 5) craft_quiver_max();
|
else if (selection == 5) craft_quiver_max();
|
||||||
else if (selection == 6) craft_reed_basket_max();
|
else if (selection == 6) craft_canoe_max();
|
||||||
else if (selection == 7) craft_clay_pot_max();
|
else if (selection == 7) craft_reed_basket_max();
|
||||||
|
else if (selection == 8) craft_clay_pot_max();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,6 +299,81 @@ void craft_quiver_max() {
|
|||||||
speak_with_history("Crafted " + maxCraft + " Quivers.", true);
|
speak_with_history("Crafted " + maxCraft + " Quivers.", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void craft_canoe() {
|
||||||
|
string missing = "";
|
||||||
|
if (get_personal_count(ITEM_LOGS) < 4) missing += "4 logs ";
|
||||||
|
if (get_personal_count(ITEM_STICKS) < 11) missing += "11 sticks ";
|
||||||
|
if (get_personal_count(ITEM_VINES) < 11) missing += "11 vines ";
|
||||||
|
if (get_personal_count(ITEM_SKINS) < 6) missing += "6 skins ";
|
||||||
|
if (get_personal_count(ITEM_ROPES) < 2) missing += "2 rope ";
|
||||||
|
if (get_personal_count(ITEM_REEDS) < 6) missing += "6 reeds ";
|
||||||
|
|
||||||
|
if (missing == "") {
|
||||||
|
if (get_personal_count(ITEM_CANOES) >= get_personal_stack_limit()) {
|
||||||
|
speak_with_history("You can't carry any more canoes.", true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
simulate_crafting(40);
|
||||||
|
add_personal_count(ITEM_LOGS, -4);
|
||||||
|
add_personal_count(ITEM_STICKS, -11);
|
||||||
|
add_personal_count(ITEM_VINES, -11);
|
||||||
|
add_personal_count(ITEM_SKINS, -6);
|
||||||
|
add_personal_count(ITEM_ROPES, -2);
|
||||||
|
add_personal_count(ITEM_REEDS, -6);
|
||||||
|
add_personal_count(ITEM_CANOES, 1);
|
||||||
|
speak_with_history("Crafted a Canoe.", true);
|
||||||
|
} else {
|
||||||
|
speak_with_history("Missing: " + missing, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void craft_canoe_max() {
|
||||||
|
if (get_personal_count(ITEM_CANOES) >= get_personal_stack_limit()) {
|
||||||
|
speak_with_history("You can't carry any more canoes.", true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxByLogs = get_personal_count(ITEM_LOGS) / 4;
|
||||||
|
int maxBySticks = get_personal_count(ITEM_STICKS) / 11;
|
||||||
|
int maxByVines = get_personal_count(ITEM_VINES) / 11;
|
||||||
|
int maxBySkins = get_personal_count(ITEM_SKINS) / 6;
|
||||||
|
int maxByRopes = get_personal_count(ITEM_ROPES) / 2;
|
||||||
|
int maxByReeds = get_personal_count(ITEM_REEDS) / 6;
|
||||||
|
int maxCraft = maxByLogs;
|
||||||
|
if (maxBySticks < maxCraft) maxCraft = maxBySticks;
|
||||||
|
if (maxByVines < maxCraft) maxCraft = maxByVines;
|
||||||
|
if (maxBySkins < maxCraft) maxCraft = maxBySkins;
|
||||||
|
if (maxByRopes < maxCraft) maxCraft = maxByRopes;
|
||||||
|
if (maxByReeds < maxCraft) maxCraft = maxByReeds;
|
||||||
|
|
||||||
|
int space = get_personal_stack_limit() - get_personal_count(ITEM_CANOES);
|
||||||
|
if (maxCraft > space) maxCraft = space;
|
||||||
|
|
||||||
|
if (maxCraft <= 0) {
|
||||||
|
string missing = "";
|
||||||
|
if (get_personal_count(ITEM_LOGS) < 4) missing += "4 logs ";
|
||||||
|
if (get_personal_count(ITEM_STICKS) < 11) missing += "11 sticks ";
|
||||||
|
if (get_personal_count(ITEM_VINES) < 11) missing += "11 vines ";
|
||||||
|
if (get_personal_count(ITEM_SKINS) < 6) missing += "6 skins ";
|
||||||
|
if (get_personal_count(ITEM_ROPES) < 2) missing += "2 rope ";
|
||||||
|
if (get_personal_count(ITEM_REEDS) < 6) missing += "6 reeds ";
|
||||||
|
speak_with_history("Missing: " + missing, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalCost = maxCraft * 40;
|
||||||
|
int craftTime = (totalCost < maxCraft * 4) ? maxCraft * 4 : totalCost;
|
||||||
|
simulate_crafting(craftTime);
|
||||||
|
add_personal_count(ITEM_LOGS, -(maxCraft * 4));
|
||||||
|
add_personal_count(ITEM_STICKS, -(maxCraft * 11));
|
||||||
|
add_personal_count(ITEM_VINES, -(maxCraft * 11));
|
||||||
|
add_personal_count(ITEM_SKINS, -(maxCraft * 6));
|
||||||
|
add_personal_count(ITEM_ROPES, -(maxCraft * 2));
|
||||||
|
add_personal_count(ITEM_REEDS, -(maxCraft * 6));
|
||||||
|
add_personal_count(ITEM_CANOES, maxCraft);
|
||||||
|
speak_with_history("Crafted " + maxCraft + " Canoes.", true);
|
||||||
|
}
|
||||||
|
|
||||||
void craft_reed_basket() {
|
void craft_reed_basket() {
|
||||||
string missing = "";
|
string missing = "";
|
||||||
if (get_personal_count(ITEM_REEDS) < 3) missing += "3 reeds ";
|
if (get_personal_count(ITEM_REEDS) < 3) missing += "3 reeds ";
|
||||||
|
|||||||
@@ -183,7 +183,14 @@ void update_undeads() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (undeads.length() < ZOMBIE_MAX_COUNT) {
|
int extra = 0;
|
||||||
|
if (MAP_SIZE > 35) {
|
||||||
|
extra = (MAP_SIZE - 35) / 15;
|
||||||
|
}
|
||||||
|
int maxCount = ZOMBIE_MAX_COUNT + extra;
|
||||||
|
if (maxCount > ZOMBIE_MAX_COUNT_CAP) maxCount = ZOMBIE_MAX_COUNT_CAP;
|
||||||
|
|
||||||
|
while (undeads.length() < maxCount) {
|
||||||
spawn_undead();
|
spawn_undead();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ class Tree {
|
|||||||
|
|
||||||
int areaStart = 0;
|
int areaStart = 0;
|
||||||
int areaEnd = 0;
|
int areaEnd = 0;
|
||||||
if (!get_tree_area_bounds_for_position(position, areaStart, areaEnd)) {
|
string areaTerrain = "";
|
||||||
|
if (!get_tree_area_bounds_for_position(position, areaStart, areaEnd, areaTerrain)) {
|
||||||
areaStart = BASE_END + 1;
|
areaStart = BASE_END + 1;
|
||||||
areaEnd = GRASS_END;
|
areaEnd = GRASS_END;
|
||||||
}
|
}
|
||||||
@@ -182,10 +183,38 @@ class Tree {
|
|||||||
}
|
}
|
||||||
Tree@[] trees;
|
Tree@[] trees;
|
||||||
|
|
||||||
bool get_tree_area_bounds_for_position(int pos, int &out areaStart, int &out areaEnd) {
|
string get_tree_area_terrain(int areaStart, int areaEnd) {
|
||||||
|
if (areaStart >= BASE_END + 1 && areaEnd <= GRASS_END) {
|
||||||
|
return "grass";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expanded_area_start != -1 && areaStart >= expanded_area_start && areaEnd <= expanded_area_end) {
|
||||||
|
int index = areaStart - expanded_area_start;
|
||||||
|
if (index >= 0 && index < int(expanded_terrain_types.length())) {
|
||||||
|
string terrain = expanded_terrain_types[index];
|
||||||
|
if (terrain.find("mountain:") == 0) {
|
||||||
|
terrain = terrain.substr(9);
|
||||||
|
}
|
||||||
|
return terrain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "grass";
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_tree_max_for_area(int areaStart, int areaEnd) {
|
||||||
|
string terrain = get_tree_area_terrain(areaStart, areaEnd);
|
||||||
|
if (terrain == "forest" || terrain == "deep_forest") {
|
||||||
|
return TREE_MAX_PER_AREA + 1;
|
||||||
|
}
|
||||||
|
return TREE_MAX_PER_AREA;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_tree_area_bounds_for_position(int pos, int &out areaStart, int &out areaEnd, string &out areaTerrain) {
|
||||||
if (pos >= BASE_END + 1 && pos <= GRASS_END) {
|
if (pos >= BASE_END + 1 && pos <= GRASS_END) {
|
||||||
areaStart = BASE_END + 1;
|
areaStart = BASE_END + 1;
|
||||||
areaEnd = GRASS_END;
|
areaEnd = GRASS_END;
|
||||||
|
areaTerrain = "grass";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,21 +224,36 @@ bool get_tree_area_bounds_for_position(int pos, int &out areaStart, int &out are
|
|||||||
|
|
||||||
int index = pos - expanded_area_start;
|
int index = pos - expanded_area_start;
|
||||||
if (index < 0 || index >= int(expanded_terrain_types.length())) return false;
|
if (index < 0 || index >= int(expanded_terrain_types.length())) return false;
|
||||||
if (expanded_terrain_types[index] != "grass") return false;
|
string terrain = expanded_terrain_types[index];
|
||||||
|
if (terrain.find("mountain:") == 0) {
|
||||||
|
terrain = terrain.substr(9);
|
||||||
|
}
|
||||||
|
if (terrain != "grass" && terrain != "forest" && terrain != "deep_forest") return false;
|
||||||
|
|
||||||
int left = index;
|
int left = index;
|
||||||
while (left > 0 && expanded_terrain_types[left - 1] == "grass") {
|
while (left > 0) {
|
||||||
|
string leftTerrain = expanded_terrain_types[left - 1];
|
||||||
|
if (leftTerrain.find("mountain:") == 0) {
|
||||||
|
leftTerrain = leftTerrain.substr(9);
|
||||||
|
}
|
||||||
|
if (leftTerrain != terrain) break;
|
||||||
left--;
|
left--;
|
||||||
}
|
}
|
||||||
|
|
||||||
int right = index;
|
int right = index;
|
||||||
int maxIndex = int(expanded_terrain_types.length()) - 1;
|
int maxIndex = int(expanded_terrain_types.length()) - 1;
|
||||||
while (right < maxIndex && expanded_terrain_types[right + 1] == "grass") {
|
while (right < maxIndex) {
|
||||||
|
string rightTerrain = expanded_terrain_types[right + 1];
|
||||||
|
if (rightTerrain.find("mountain:") == 0) {
|
||||||
|
rightTerrain = rightTerrain.substr(9);
|
||||||
|
}
|
||||||
|
if (rightTerrain != terrain) break;
|
||||||
right++;
|
right++;
|
||||||
}
|
}
|
||||||
|
|
||||||
areaStart = expanded_area_start + left;
|
areaStart = expanded_area_start + left;
|
||||||
areaEnd = expanded_area_start + right;
|
areaEnd = expanded_area_start + right;
|
||||||
|
areaTerrain = terrain;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +286,8 @@ bool tree_too_close_in_area(int pos, int areaStart, int areaEnd, Tree@ ignoreTre
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool place_tree_in_area(Tree@ tree, int areaStart, int areaEnd) {
|
bool place_tree_in_area(Tree@ tree, int areaStart, int areaEnd) {
|
||||||
if (count_trees_in_area(areaStart, areaEnd, tree) >= TREE_MAX_PER_AREA) {
|
int maxTrees = get_tree_max_for_area(areaStart, areaEnd);
|
||||||
|
if (count_trees_in_area(areaStart, areaEnd, tree) >= maxTrees) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +304,8 @@ bool place_tree_in_area(Tree@ tree, int areaStart, int areaEnd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool spawn_tree_in_area(int areaStart, int areaEnd) {
|
bool spawn_tree_in_area(int areaStart, int areaEnd) {
|
||||||
if (count_trees_in_area(areaStart, areaEnd, null) >= TREE_MAX_PER_AREA) {
|
int maxTrees = get_tree_max_for_area(areaStart, areaEnd);
|
||||||
|
if (count_trees_in_area(areaStart, areaEnd, null) >= maxTrees) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,7 +326,7 @@ void spawn_trees(int grass_start, int grass_end) {
|
|||||||
spawn_tree_in_area(grass_start, grass_end);
|
spawn_tree_in_area(grass_start, grass_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_grass_areas(int[]@ areaStarts, int[]@ areaEnds) {
|
void get_tree_areas(int[]@ areaStarts, int[]@ areaEnds) {
|
||||||
areaStarts.resize(0);
|
areaStarts.resize(0);
|
||||||
areaEnds.resize(0);
|
areaEnds.resize(0);
|
||||||
|
|
||||||
@@ -291,9 +337,18 @@ void get_grass_areas(int[]@ areaStarts, int[]@ areaEnds) {
|
|||||||
int total = int(expanded_terrain_types.length());
|
int total = int(expanded_terrain_types.length());
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while (index < total) {
|
while (index < total) {
|
||||||
if (expanded_terrain_types[index] == "grass") {
|
string terrain = expanded_terrain_types[index];
|
||||||
|
if (terrain.find("mountain:") == 0) {
|
||||||
|
terrain = terrain.substr(9);
|
||||||
|
}
|
||||||
|
if (terrain == "grass" || terrain == "forest" || terrain == "deep_forest") {
|
||||||
int segmentStart = index;
|
int segmentStart = index;
|
||||||
while (index + 1 < total && expanded_terrain_types[index + 1] == "grass") {
|
while (index + 1 < total) {
|
||||||
|
string nextTerrain = expanded_terrain_types[index + 1];
|
||||||
|
if (nextTerrain.find("mountain:") == 0) {
|
||||||
|
nextTerrain = nextTerrain.substr(9);
|
||||||
|
}
|
||||||
|
if (nextTerrain != terrain) break;
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
int segmentEnd = index;
|
int segmentEnd = index;
|
||||||
@@ -306,7 +361,8 @@ void get_grass_areas(int[]@ areaStarts, int[]@ areaEnds) {
|
|||||||
|
|
||||||
bool relocate_tree_to_any_area(Tree@ tree, int[]@ areaStarts, int[]@ areaEnds) {
|
bool relocate_tree_to_any_area(Tree@ tree, int[]@ areaStarts, int[]@ areaEnds) {
|
||||||
for (uint i = 0; i < areaStarts.length(); i++) {
|
for (uint i = 0; i < areaStarts.length(); i++) {
|
||||||
if (count_trees_in_area(areaStarts[i], areaEnds[i], tree) >= TREE_MAX_PER_AREA) continue;
|
int maxTrees = get_tree_max_for_area(areaStarts[i], areaEnds[i]);
|
||||||
|
if (count_trees_in_area(areaStarts[i], areaEnds[i], tree) >= maxTrees) continue;
|
||||||
if (place_tree_in_area(tree, areaStarts[i], areaEnds[i])) {
|
if (place_tree_in_area(tree, areaStarts[i], areaEnds[i])) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -317,13 +373,14 @@ bool relocate_tree_to_any_area(Tree@ tree, int[]@ areaStarts, int[]@ areaEnds) {
|
|||||||
void normalize_tree_positions() {
|
void normalize_tree_positions() {
|
||||||
int[] areaStarts;
|
int[] areaStarts;
|
||||||
int[] areaEnds;
|
int[] areaEnds;
|
||||||
get_grass_areas(areaStarts, areaEnds);
|
get_tree_areas(areaStarts, areaEnds);
|
||||||
if (areaStarts.length() == 0) return;
|
if (areaStarts.length() == 0) return;
|
||||||
|
|
||||||
for (uint i = 0; i < trees.length(); i++) {
|
for (uint i = 0; i < trees.length(); i++) {
|
||||||
int areaStart = 0;
|
int areaStart = 0;
|
||||||
int areaEnd = 0;
|
int areaEnd = 0;
|
||||||
if (!get_tree_area_bounds_for_position(trees[i].position, areaStart, areaEnd)) {
|
string areaTerrain = "";
|
||||||
|
if (!get_tree_area_bounds_for_position(trees[i].position, areaStart, areaEnd, areaTerrain)) {
|
||||||
if (!relocate_tree_to_any_area(trees[i], areaStarts, areaEnds)) {
|
if (!relocate_tree_to_any_area(trees[i], areaStarts, areaEnds)) {
|
||||||
if (trees[i].sound_handle != -1) {
|
if (trees[i].sound_handle != -1) {
|
||||||
p.destroy_sound(trees[i].sound_handle);
|
p.destroy_sound(trees[i].sound_handle);
|
||||||
@@ -337,6 +394,7 @@ void normalize_tree_positions() {
|
|||||||
for (uint areaIndex = 0; areaIndex < areaStarts.length(); areaIndex++) {
|
for (uint areaIndex = 0; areaIndex < areaStarts.length(); areaIndex++) {
|
||||||
int areaStart = areaStarts[areaIndex];
|
int areaStart = areaStarts[areaIndex];
|
||||||
int areaEnd = areaEnds[areaIndex];
|
int areaEnd = areaEnds[areaIndex];
|
||||||
|
int maxTrees = get_tree_max_for_area(areaStart, areaEnd);
|
||||||
|
|
||||||
int[] areaTreeIndices;
|
int[] areaTreeIndices;
|
||||||
for (uint i = 0; i < trees.length(); i++) {
|
for (uint i = 0; i < trees.length(); i++) {
|
||||||
@@ -345,7 +403,7 @@ void normalize_tree_positions() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (areaTreeIndices.length() > TREE_MAX_PER_AREA) {
|
while (areaTreeIndices.length() > maxTrees) {
|
||||||
uint treeIndex = areaTreeIndices[areaTreeIndices.length() - 1];
|
uint treeIndex = areaTreeIndices[areaTreeIndices.length() - 1];
|
||||||
Tree@ tree = trees[treeIndex];
|
Tree@ tree = trees[treeIndex];
|
||||||
if (!relocate_tree_to_any_area(tree, areaStarts, areaEnds)) {
|
if (!relocate_tree_to_any_area(tree, areaStarts, areaEnds)) {
|
||||||
@@ -880,16 +938,16 @@ bool can_move_mountain(int from_x, int to_x) {
|
|||||||
if (mountain.is_steep_section(from_x, to_x)) {
|
if (mountain.is_steep_section(from_x, to_x)) {
|
||||||
// Need rope
|
// Need rope
|
||||||
if (get_personal_count(ITEM_ROPES) < 1) {
|
if (get_personal_count(ITEM_ROPES) < 1) {
|
||||||
speak_with_history("You'll need a rope to climb there.", true);
|
speak_movement_blocked("You'll need a rope to climb there.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prompt for rope climb
|
// Prompt for rope climb
|
||||||
int elevation_change = mountain.get_elevation_change(from_x, to_x);
|
int elevation_change = mountain.get_elevation_change(from_x, to_x);
|
||||||
if (elevation_change > 0) {
|
if (elevation_change > 0) {
|
||||||
speak_with_history("Press up to climb up.", true);
|
speak_movement_blocked("Press up to climb up.");
|
||||||
} else {
|
} else {
|
||||||
speak_with_history("Press down to climb down.", true);
|
speak_movement_blocked("Press down to climb down.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store pending rope climb info
|
// Store pending rope climb info
|
||||||
@@ -901,6 +959,27 @@ bool can_move_mountain(int from_x, int to_x) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool can_enter_stream_tile(int pos) {
|
||||||
|
if (!is_deep_stream_at(pos)) return true;
|
||||||
|
if (get_personal_count(ITEM_CANOES) > 0) return true;
|
||||||
|
speak_movement_blocked("You need a canoe to cross deep water.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer movementBlockTimer;
|
||||||
|
string lastMovementBlockMessage = "";
|
||||||
|
const int MOVEMENT_BLOCK_COOLDOWN_MS = 3000;
|
||||||
|
|
||||||
|
void speak_movement_blocked(string message) {
|
||||||
|
if (message == lastMovementBlockMessage && movementBlockTimer.elapsed < MOVEMENT_BLOCK_COOLDOWN_MS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastMovementBlockMessage = message;
|
||||||
|
movementBlockTimer.restart();
|
||||||
|
speak_with_history(message, true);
|
||||||
|
}
|
||||||
|
|
||||||
// Rope climbing functions
|
// Rope climbing functions
|
||||||
void start_rope_climb(bool climbing_up, int target_x, int target_elevation) {
|
void start_rope_climb(bool climbing_up, int target_x, int target_elevation) {
|
||||||
rope_climbing = true;
|
rope_climbing = true;
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ const int ITEM_BOWSTRINGS = 30;
|
|||||||
const int ITEM_SINEW = 31;
|
const int ITEM_SINEW = 31;
|
||||||
const int ITEM_BOAR_CARCASSES = 32;
|
const int ITEM_BOAR_CARCASSES = 32;
|
||||||
const int ITEM_BACKPACKS = 33;
|
const int ITEM_BACKPACKS = 33;
|
||||||
const int ITEM_COUNT = 34; // Total number of item types
|
const int ITEM_CANOES = 34;
|
||||||
|
const int ITEM_COUNT = 35; // Total number of item types
|
||||||
|
|
||||||
// Item definition class
|
// Item definition class
|
||||||
class ItemDefinition {
|
class ItemDefinition {
|
||||||
@@ -116,6 +117,7 @@ void init_item_registry() {
|
|||||||
item_registry[ITEM_SINEW] = ItemDefinition(ITEM_SINEW, "sinew", "piece of sinew", "Sinew", 0.10);
|
item_registry[ITEM_SINEW] = ItemDefinition(ITEM_SINEW, "sinew", "piece of sinew", "Sinew", 0.10);
|
||||||
item_registry[ITEM_BOAR_CARCASSES] = ItemDefinition(ITEM_BOAR_CARCASSES, "boar carcasses", "boar carcass", "Boar Carcasses", 1.50);
|
item_registry[ITEM_BOAR_CARCASSES] = ItemDefinition(ITEM_BOAR_CARCASSES, "boar carcasses", "boar carcass", "Boar Carcasses", 1.50);
|
||||||
item_registry[ITEM_BACKPACKS] = ItemDefinition(ITEM_BACKPACKS, "backpacks", "backpack", "Backpacks", 2.50);
|
item_registry[ITEM_BACKPACKS] = ItemDefinition(ITEM_BACKPACKS, "backpacks", "backpack", "Backpacks", 2.50);
|
||||||
|
item_registry[ITEM_CANOES] = ItemDefinition(ITEM_CANOES, "canoes", "canoe", "Canoes", 4.00);
|
||||||
|
|
||||||
// Define display order for inventory menus
|
// Define display order for inventory menus
|
||||||
// This controls the order items appear in menus
|
// This controls the order items appear in menus
|
||||||
@@ -152,6 +154,7 @@ void init_item_registry() {
|
|||||||
ITEM_ROPES,
|
ITEM_ROPES,
|
||||||
ITEM_REED_BASKETS,
|
ITEM_REED_BASKETS,
|
||||||
ITEM_CLAY_POTS,
|
ITEM_CLAY_POTS,
|
||||||
|
ITEM_CANOES,
|
||||||
// Clothing
|
// Clothing
|
||||||
ITEM_SKIN_HATS,
|
ITEM_SKIN_HATS,
|
||||||
ITEM_SKIN_GLOVES,
|
ITEM_SKIN_GLOVES,
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ void try_burn_incense() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add_personal_count(ITEM_INCENSE, -1);
|
add_personal_count(ITEM_INCENSE, -1);
|
||||||
|
add_personal_count(ITEM_CLAY_POTS, -1);
|
||||||
incense_hours_remaining += INCENSE_HOURS_PER_STICK;
|
incense_hours_remaining += INCENSE_HOURS_PER_STICK;
|
||||||
incense_burning = true;
|
incense_burning = true;
|
||||||
speak_with_history("Incense burning. " + incense_hours_remaining + " hours remaining.", true);
|
speak_with_history("Incense burning. " + incense_hours_remaining + " hours remaining.", true);
|
||||||
|
|||||||
@@ -393,7 +393,6 @@ void update_time() {
|
|||||||
if (current_hour == 6) {
|
if (current_hour == 6) {
|
||||||
process_daily_weapon_breakage();
|
process_daily_weapon_breakage();
|
||||||
attempt_daily_quest();
|
attempt_daily_quest();
|
||||||
save_game_state();
|
|
||||||
}
|
}
|
||||||
attempt_daily_invasion();
|
attempt_daily_invasion();
|
||||||
keep_base_fires_fed();
|
keep_base_fires_fed();
|
||||||
@@ -404,6 +403,9 @@ void update_time() {
|
|||||||
attempt_blessing();
|
attempt_blessing();
|
||||||
check_weather_transition();
|
check_weather_transition();
|
||||||
attempt_resident_collection();
|
attempt_resident_collection();
|
||||||
|
if (current_hour == 6) {
|
||||||
|
save_game_state();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_ambience_running();
|
ensure_ambience_running();
|
||||||
|
|||||||
@@ -89,3 +89,9 @@ WorldStream@ get_stream_at(int pos) {
|
|||||||
bool is_position_in_water(int pos) {
|
bool is_position_in_water(int pos) {
|
||||||
return get_stream_at(pos) != null;
|
return get_stream_at(pos) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_deep_stream_at(int pos) {
|
||||||
|
WorldStream@ stream = get_stream_at(pos);
|
||||||
|
if (stream == null) return false;
|
||||||
|
return stream.get_width() > 3;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user