A few bug fixes, new item, canoe, added.

This commit is contained in:
Storm Dragon
2026-01-24 00:57:00 -05:00
parent 78e8d434a3
commit dfd6a0f3a1
13 changed files with 339 additions and 51 deletions

View File

@@ -1,6 +1,9 @@
string get_footstep_sound(int current_x, int base_end, int grass_end)
{
// 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)) {
return "sounds/terrain/shallow_water.ogg";
}

View File

@@ -43,6 +43,7 @@ const int ARROW_CAPACITY_PER_QUIVER = 12;
// Zombie settings
const int ZOMBIE_HEALTH = 12;
const int ZOMBIE_MAX_COUNT = 5;
const int ZOMBIE_MAX_COUNT_CAP = 12;
const int ZOMBIE_MOVE_INTERVAL = 1000;
const int ZOMBIE_ATTACK_INTERVAL = 1600;
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) {
return value < 0 ? -value : value;
}

View File

@@ -10,6 +10,7 @@ void run_tools_menu() {
"Fishing Pole (1 Stick, 2 Vines)",
"Rope (3 Vines)",
"Quiver (2 Skins, 2 Vines)",
"Canoe (4 Logs, 11 Sticks, 11 Vines, 6 Skins, 2 Rope, 6 Reeds)",
"Reed Basket (3 Reeds)",
"Clay Pot (3 Clay)"
};
@@ -41,8 +42,9 @@ void run_tools_menu() {
else if (selection == 3) craft_fishing_pole();
else if (selection == 4) craft_rope();
else if (selection == 5) craft_quiver();
else if (selection == 6) craft_reed_basket();
else if (selection == 7) craft_clay_pot();
else if (selection == 6) craft_canoe();
else if (selection == 7) craft_reed_basket();
else if (selection == 8) craft_clay_pot();
break;
}
@@ -53,8 +55,9 @@ void run_tools_menu() {
else if (selection == 3) craft_fishing_pole_max();
else if (selection == 4) craft_rope_max();
else if (selection == 5) craft_quiver_max();
else if (selection == 6) craft_reed_basket_max();
else if (selection == 7) craft_clay_pot_max();
else if (selection == 6) craft_canoe_max();
else if (selection == 7) craft_reed_basket_max();
else if (selection == 8) craft_clay_pot_max();
break;
}
}
@@ -296,6 +299,81 @@ void craft_quiver_max() {
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() {
string missing = "";
if (get_personal_count(ITEM_REEDS) < 3) missing += "3 reeds ";

View File

@@ -183,7 +183,14 @@ void update_undeads() {
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();
}

View File

@@ -65,7 +65,8 @@ class Tree {
int areaStart = 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;
areaEnd = GRASS_END;
}
@@ -182,10 +183,38 @@ class Tree {
}
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) {
areaStart = BASE_END + 1;
areaEnd = GRASS_END;
areaTerrain = "grass";
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;
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;
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--;
}
int right = index;
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++;
}
areaStart = expanded_area_start + left;
areaEnd = expanded_area_start + right;
areaTerrain = terrain;
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) {
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;
}
@@ -259,7 +304,8 @@ bool place_tree_in_area(Tree@ tree, 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;
}
@@ -280,7 +326,7 @@ void spawn_trees(int grass_start, int 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);
areaEnds.resize(0);
@@ -291,9 +337,18 @@ void get_grass_areas(int[]@ areaStarts, int[]@ areaEnds) {
int total = int(expanded_terrain_types.length());
int index = 0;
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;
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++;
}
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) {
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])) {
return true;
}
@@ -317,13 +373,14 @@ bool relocate_tree_to_any_area(Tree@ tree, int[]@ areaStarts, int[]@ areaEnds) {
void normalize_tree_positions() {
int[] areaStarts;
int[] areaEnds;
get_grass_areas(areaStarts, areaEnds);
get_tree_areas(areaStarts, areaEnds);
if (areaStarts.length() == 0) return;
for (uint i = 0; i < trees.length(); i++) {
int areaStart = 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 (trees[i].sound_handle != -1) {
p.destroy_sound(trees[i].sound_handle);
@@ -337,6 +394,7 @@ void normalize_tree_positions() {
for (uint areaIndex = 0; areaIndex < areaStarts.length(); areaIndex++) {
int areaStart = areaStarts[areaIndex];
int areaEnd = areaEnds[areaIndex];
int maxTrees = get_tree_max_for_area(areaStart, areaEnd);
int[] areaTreeIndices;
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];
Tree@ tree = trees[treeIndex];
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)) {
// Need rope
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;
}
// Prompt for rope climb
int elevation_change = mountain.get_elevation_change(from_x, to_x);
if (elevation_change > 0) {
speak_with_history("Press up to climb up.", true);
speak_movement_blocked("Press up to climb up.");
} else {
speak_with_history("Press down to climb down.", true);
speak_movement_blocked("Press down to climb down.");
}
// Store pending rope climb info
@@ -901,6 +959,27 @@ bool can_move_mountain(int from_x, int to_x) {
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
void start_rope_climb(bool climbing_up, int target_x, int target_elevation) {
rope_climbing = true;

View File

@@ -36,7 +36,8 @@ const int ITEM_BOWSTRINGS = 30;
const int ITEM_SINEW = 31;
const int ITEM_BOAR_CARCASSES = 32;
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
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_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_CANOES] = ItemDefinition(ITEM_CANOES, "canoes", "canoe", "Canoes", 4.00);
// Define display order for inventory menus
// This controls the order items appear in menus
@@ -152,6 +154,7 @@ void init_item_registry() {
ITEM_ROPES,
ITEM_REED_BASKETS,
ITEM_CLAY_POTS,
ITEM_CANOES,
// Clothing
ITEM_SKIN_HATS,
ITEM_SKIN_GLOVES,

View File

@@ -71,6 +71,7 @@ void try_burn_incense() {
}
add_personal_count(ITEM_INCENSE, -1);
add_personal_count(ITEM_CLAY_POTS, -1);
incense_hours_remaining += INCENSE_HOURS_PER_STICK;
incense_burning = true;
speak_with_history("Incense burning. " + incense_hours_remaining + " hours remaining.", true);

View File

@@ -393,7 +393,6 @@ void update_time() {
if (current_hour == 6) {
process_daily_weapon_breakage();
attempt_daily_quest();
save_game_state();
}
attempt_daily_invasion();
keep_base_fires_fed();
@@ -404,6 +403,9 @@ void update_time() {
attempt_blessing();
check_weather_transition();
attempt_resident_collection();
if (current_hour == 6) {
save_game_state();
}
}
ensure_ambience_running();

View File

@@ -89,3 +89,9 @@ WorldStream@ get_stream_at(int pos) {
bool is_position_in_water(int pos) {
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;
}