A ton of changes including bug fixes, new craftable items, altar with favor system added. Info for player and base added.
This commit is contained in:
@@ -12,11 +12,23 @@ bool is_daytime = true;
|
||||
bool sun_setting_warned = false;
|
||||
bool sunrise_warned = false;
|
||||
|
||||
// Crossfade state
|
||||
bool crossfade_active = false;
|
||||
bool crossfade_to_night = false; // true = fading to night, false = fading to day
|
||||
timer crossfade_timer;
|
||||
const int CROSSFADE_DURATION = 60000; // 1 minute (1 game hour)
|
||||
const float CROSSFADE_MIN_VOLUME = -40.0; // dB, effectively silent but not extreme
|
||||
const float CROSSFADE_MAX_VOLUME = 0.0; // dB, full volume
|
||||
|
||||
// Expansion and invasion tracking
|
||||
bool area_expanded_today = false;
|
||||
bool invasion_active = false;
|
||||
int invasion_start_hour = -1;
|
||||
string[] expanded_terrain_types;
|
||||
int invasion_chance = 25;
|
||||
bool invasion_triggered_today = false;
|
||||
bool invasion_roll_done_today = false;
|
||||
int invasion_scheduled_hour = -1;
|
||||
|
||||
void init_time() {
|
||||
current_hour = 8;
|
||||
@@ -25,23 +37,29 @@ void init_time() {
|
||||
is_daytime = true;
|
||||
sun_setting_warned = false;
|
||||
sunrise_warned = false;
|
||||
crossfade_active = false;
|
||||
crossfade_to_night = false;
|
||||
area_expanded_today = false;
|
||||
invasion_active = false;
|
||||
invasion_start_hour = -1;
|
||||
invasion_chance = 25;
|
||||
invasion_triggered_today = false;
|
||||
invasion_roll_done_today = false;
|
||||
invasion_scheduled_hour = -1;
|
||||
update_ambience(true); // Force start
|
||||
}
|
||||
|
||||
void expand_area() {
|
||||
if (expanded_area_start != -1) {
|
||||
return; // Already expanded
|
||||
}
|
||||
|
||||
// Play invasion sound
|
||||
p.play_stationary("sounds/enemies/invasion.ogg", false);
|
||||
|
||||
// Calculate new area
|
||||
expanded_area_start = MAP_SIZE;
|
||||
expanded_area_end = MAP_SIZE + EXPANSION_SIZE - 1;
|
||||
int new_start = MAP_SIZE;
|
||||
int new_end = MAP_SIZE + EXPANSION_SIZE - 1;
|
||||
if (expanded_area_start == -1) {
|
||||
expanded_area_start = new_start;
|
||||
}
|
||||
expanded_area_end = new_end;
|
||||
MAP_SIZE += EXPANSION_SIZE;
|
||||
|
||||
// Generate a single terrain type for the entire new area
|
||||
@@ -55,9 +73,8 @@ void expand_area() {
|
||||
terrain_type = "snow";
|
||||
}
|
||||
|
||||
expanded_terrain_types.resize(EXPANSION_SIZE);
|
||||
for (int i = 0; i < EXPANSION_SIZE; i++) {
|
||||
expanded_terrain_types[i] = terrain_type;
|
||||
expanded_terrain_types.insert_last(terrain_type);
|
||||
}
|
||||
|
||||
// Place exactly one feature: either a stream or a tree
|
||||
@@ -66,7 +83,7 @@ void expand_area() {
|
||||
if (place_stream) {
|
||||
int stream_width = random(1, 5);
|
||||
int stream_start = random(0, EXPANSION_SIZE - stream_width);
|
||||
int actual_start = expanded_area_start + stream_start;
|
||||
int actual_start = new_start + stream_start;
|
||||
add_world_stream(actual_start, stream_width);
|
||||
|
||||
string width_desc = "very small";
|
||||
@@ -77,7 +94,7 @@ void expand_area() {
|
||||
|
||||
notify("A " + width_desc + " stream flows through the new area at x " + actual_start + ".");
|
||||
} else {
|
||||
int tree_pos = random(expanded_area_start, expanded_area_end);
|
||||
int tree_pos = random(new_start, new_end);
|
||||
Tree@ t = Tree(tree_pos);
|
||||
trees.insert_last(t);
|
||||
}
|
||||
@@ -87,16 +104,97 @@ void expand_area() {
|
||||
}
|
||||
|
||||
void start_invasion() {
|
||||
expand_area();
|
||||
invasion_active = true;
|
||||
invasion_start_hour = current_hour;
|
||||
notify("Bandits are invading from the new area!");
|
||||
}
|
||||
|
||||
void update_invasion_chance_for_new_day() {
|
||||
if (current_day == 2) {
|
||||
invasion_chance = 100;
|
||||
return;
|
||||
}
|
||||
if (current_day > 2) {
|
||||
if (invasion_triggered_today) {
|
||||
invasion_chance = 25;
|
||||
} else {
|
||||
invasion_chance += 25;
|
||||
if (invasion_chance > 100) invasion_chance = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get_random_invasion_hour(int min_hour) {
|
||||
if (min_hour < 6) min_hour = 6;
|
||||
if (min_hour > 11) return -1;
|
||||
return random(min_hour, 11);
|
||||
}
|
||||
|
||||
void schedule_invasion() {
|
||||
if (invasion_scheduled_hour != -1) return;
|
||||
int hour = get_random_invasion_hour(current_hour);
|
||||
if (hour == -1) return;
|
||||
invasion_scheduled_hour = hour;
|
||||
}
|
||||
|
||||
void check_scheduled_invasion() {
|
||||
if (invasion_active || invasion_triggered_today) return;
|
||||
if (invasion_scheduled_hour == -1) return;
|
||||
if (current_hour == invasion_scheduled_hour) {
|
||||
invasion_scheduled_hour = -1;
|
||||
invasion_triggered_today = true;
|
||||
start_invasion();
|
||||
} else if (current_hour > 11) {
|
||||
invasion_scheduled_hour = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void attempt_daily_invasion() {
|
||||
if (current_day < 2) return;
|
||||
if (invasion_triggered_today || invasion_active) return;
|
||||
if (invasion_roll_done_today) return;
|
||||
invasion_roll_done_today = true;
|
||||
|
||||
int roll = random(1, 100);
|
||||
if (roll > invasion_chance) return;
|
||||
|
||||
schedule_invasion();
|
||||
check_scheduled_invasion();
|
||||
}
|
||||
|
||||
void attempt_resident_recruitment() {
|
||||
if (barricade_health <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int chance = random(25, 35);
|
||||
int roll = random(1, 100);
|
||||
if (roll > chance) {
|
||||
return;
|
||||
}
|
||||
|
||||
int added = random(1, 3);
|
||||
residents_count += added;
|
||||
string join_message = (added == 1) ? "A survivor joins your base." : "" + added + " survivors join your base.";
|
||||
notify(join_message);
|
||||
}
|
||||
|
||||
void end_invasion() {
|
||||
invasion_active = false;
|
||||
invasion_start_hour = -1;
|
||||
clear_bandits();
|
||||
transition_bandits_to_wandering();
|
||||
notify("The bandit invasion has ended.");
|
||||
attempt_resident_recruitment();
|
||||
}
|
||||
|
||||
void transition_bandits_to_wandering() {
|
||||
for (uint i = 0; i < bandits.length(); i++) {
|
||||
bandits[i].behavior_state = "wandering";
|
||||
bandits[i].wander_direction = random(-1, 1);
|
||||
bandits[i].wander_direction_change_interval = random(BANDIT_WANDER_DIRECTION_CHANGE_MIN, BANDIT_WANDER_DIRECTION_CHANGE_MAX);
|
||||
bandits[i].wander_direction_timer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
void check_invasion_status() {
|
||||
@@ -114,18 +212,62 @@ void check_invasion_status() {
|
||||
}
|
||||
|
||||
void manage_bandits_during_invasion() {
|
||||
if (!invasion_active) return;
|
||||
if (expanded_area_start == -1) return;
|
||||
|
||||
// Bandits only appear during daytime (6 AM to 7 PM)
|
||||
// Clear ALL bandits at nighttime (undead eat them)
|
||||
if (!is_daytime) {
|
||||
clear_bandits();
|
||||
return;
|
||||
}
|
||||
|
||||
// Maintain BANDIT_MAX_COUNT bandits during invasion
|
||||
while (bandits.length() < BANDIT_MAX_COUNT) {
|
||||
spawn_bandit(expanded_area_start, expanded_area_end);
|
||||
// During daytime: if invasion is active, maintain bandit count
|
||||
if (invasion_active && expanded_area_start != -1) {
|
||||
while (bandits.length() < BANDIT_MAX_COUNT) {
|
||||
spawn_bandit(expanded_area_start, expanded_area_end);
|
||||
}
|
||||
}
|
||||
// If invasion not active, wandering bandits persist during daytime
|
||||
}
|
||||
|
||||
void update_blessings() {
|
||||
if (blessing_speed_active && blessing_speed_timer.elapsed >= BLESSING_SPEED_DURATION) {
|
||||
blessing_speed_active = false;
|
||||
update_max_health_from_equipment();
|
||||
screen_reader_speak("The speed blessing fades.", true);
|
||||
}
|
||||
}
|
||||
|
||||
void attempt_blessing() {
|
||||
if (favor < 1.0) return;
|
||||
int roll = random(1, 100);
|
||||
if (roll > BLESSING_TRIGGER_CHANCE) return;
|
||||
|
||||
int[] options;
|
||||
if (player_health < max_health) options.insert_last(0);
|
||||
if (!blessing_speed_active) options.insert_last(1);
|
||||
if (barricade_health < BARRICADE_MAX_HEALTH) options.insert_last(2);
|
||||
if (options.length() == 0) return;
|
||||
|
||||
int choice = options[random(0, options.length() - 1)];
|
||||
favor -= 1.0;
|
||||
if (favor < 0) favor = 0;
|
||||
|
||||
if (choice == 0) {
|
||||
int before = player_health;
|
||||
player_health += BLESSING_HEAL_AMOUNT;
|
||||
if (player_health > max_health) player_health = max_health;
|
||||
int healed = player_health - before;
|
||||
string bonus = (healed > 0) ? "You feel restored. +" + healed + " health." : "You feel restored.";
|
||||
notify("The gods' favor shines upon you. " + bonus);
|
||||
} else if (choice == 1) {
|
||||
blessing_speed_active = true;
|
||||
blessing_speed_timer.restart();
|
||||
update_max_health_from_equipment();
|
||||
notify("The gods' favor shines upon you. You feel swift for a while.");
|
||||
} else if (choice == 2) {
|
||||
int gained = add_barricade_health(BLESSING_BARRICADE_REPAIR);
|
||||
string bonus = (gained > 0)
|
||||
? "A divine force repairs the barricade. +" + gained + " health."
|
||||
: "A divine force surrounds the barricade.";
|
||||
notify("The gods' favor shines upon you. " + bonus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +279,12 @@ void update_time() {
|
||||
current_hour = 0;
|
||||
current_day++;
|
||||
area_expanded_today = false; // Reset for new day
|
||||
update_invasion_chance_for_new_day();
|
||||
invasion_triggered_today = false;
|
||||
invasion_roll_done_today = false;
|
||||
invasion_scheduled_hour = -1;
|
||||
quest_roll_done_today = false;
|
||||
consume_food_for_residents();
|
||||
}
|
||||
|
||||
if (current_hour == 18 && !sun_setting_warned) {
|
||||
@@ -152,24 +300,32 @@ void update_time() {
|
||||
sunrise_warned = false;
|
||||
}
|
||||
|
||||
// Check for area expansion (day 2+, daytime morning before noon, not yet expanded today)
|
||||
if (current_day >= 2 && current_hour >= 6 && current_hour < 12 && !area_expanded_today && expanded_area_start == -1) {
|
||||
int roll = random(1, 100);
|
||||
if (roll <= EXPANSION_CHANCE) {
|
||||
expand_area();
|
||||
// Start invasion immediately after expansion (morning, during daytime)
|
||||
start_invasion();
|
||||
}
|
||||
}
|
||||
|
||||
// Check invasion status
|
||||
check_invasion_status();
|
||||
|
||||
check_ambience_transition();
|
||||
|
||||
// TODO: add resident defense using stored weapons once storage exists.
|
||||
if (is_daytime && residents_count > 0 && barricade_health < BARRICADE_MAX_HEALTH && current_hour % 4 == 0) {
|
||||
if (storage_meat > 0) {
|
||||
int gained = add_barricade_health(residents_count);
|
||||
if (gained > 0 && x <= BASE_END) {
|
||||
screen_reader_speak("Residents repaired the barricade. +" + gained + " health.", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current_hour == 6) {
|
||||
save_game_state();
|
||||
}
|
||||
|
||||
if (current_hour == 6) {
|
||||
attempt_daily_invasion();
|
||||
attempt_daily_quest();
|
||||
}
|
||||
keep_base_fires_fed();
|
||||
check_scheduled_invasion();
|
||||
attempt_blessing();
|
||||
}
|
||||
|
||||
// Manage bandits during active invasion
|
||||
@@ -199,40 +355,108 @@ string get_time_string() {
|
||||
}
|
||||
|
||||
void check_ambience_transition() {
|
||||
// Definition of Day: 6 AM to 7 PM (19:00) ?
|
||||
// Let's say Day is 6 (6AM) to 19 (7PM). Night is 20 (8PM) to 5 (5AM).
|
||||
// Or simpler: 6 to 18 (6PM).
|
||||
|
||||
bool now_day = (current_hour >= 6 && current_hour < 19);
|
||||
|
||||
if (now_day != is_daytime) {
|
||||
is_daytime = now_day;
|
||||
update_ambience(false);
|
||||
// Day is 6 (6AM) to 18 (6PM inclusive, so transition starts at hour 18)
|
||||
// Night is 19 (7PM) to 5 (5AM inclusive, so transition starts at hour 5)
|
||||
// Crossfade begins at hour 18 (sunset) and hour 5 (sunrise)
|
||||
|
||||
// Start crossfade to night at hour 18
|
||||
if (current_hour == 18 && is_daytime && !crossfade_active) {
|
||||
start_crossfade(true); // Fade to night
|
||||
}
|
||||
// Start crossfade to day at hour 5
|
||||
else if (current_hour == 5 && !is_daytime && !crossfade_active) {
|
||||
start_crossfade(false); // Fade to day
|
||||
}
|
||||
}
|
||||
|
||||
void start_crossfade(bool to_night) {
|
||||
crossfade_active = true;
|
||||
crossfade_to_night = to_night;
|
||||
crossfade_timer.restart();
|
||||
|
||||
// Start the incoming sound at minimum volume (silent)
|
||||
if (to_night) {
|
||||
// Starting night sound
|
||||
if (night_sound_handle == -1 || !p.sound_is_active(night_sound_handle)) {
|
||||
night_sound_handle = p.play_stationary("sounds/nature/night.ogg", true);
|
||||
}
|
||||
p.update_sound_start_values(night_sound_handle, 0.0, CROSSFADE_MIN_VOLUME, 1.0);
|
||||
} else {
|
||||
// Starting day sound
|
||||
if (day_sound_handle == -1 || !p.sound_is_active(day_sound_handle)) {
|
||||
day_sound_handle = p.play_stationary("sounds/nature/day.ogg", true);
|
||||
}
|
||||
p.update_sound_start_values(day_sound_handle, 0.0, CROSSFADE_MIN_VOLUME, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
void update_crossfade() {
|
||||
if (!crossfade_active) return;
|
||||
|
||||
float progress = float(crossfade_timer.elapsed) / float(CROSSFADE_DURATION);
|
||||
if (progress > 1.0) progress = 1.0;
|
||||
|
||||
// Volume interpolation: fade out goes 0 -> -40, fade in goes -40 -> 0
|
||||
float volume_range = CROSSFADE_MAX_VOLUME - CROSSFADE_MIN_VOLUME; // 40 dB range
|
||||
float fade_out_vol = CROSSFADE_MAX_VOLUME - (volume_range * progress); // 0 -> -40
|
||||
float fade_in_vol = CROSSFADE_MIN_VOLUME + (volume_range * progress); // -40 -> 0
|
||||
|
||||
if (crossfade_to_night) {
|
||||
// Fading day out, night in
|
||||
if (day_sound_handle != -1) p.update_sound_start_values(day_sound_handle, 0.0, fade_out_vol, 1.0);
|
||||
if (night_sound_handle != -1) p.update_sound_start_values(night_sound_handle, 0.0, fade_in_vol, 1.0);
|
||||
} else {
|
||||
// Fading night out, day in
|
||||
if (night_sound_handle != -1) p.update_sound_start_values(night_sound_handle, 0.0, fade_out_vol, 1.0);
|
||||
if (day_sound_handle != -1) p.update_sound_start_values(day_sound_handle, 0.0, fade_in_vol, 1.0);
|
||||
}
|
||||
|
||||
// Complete the crossfade
|
||||
if (progress >= 1.0) {
|
||||
complete_crossfade();
|
||||
}
|
||||
}
|
||||
|
||||
void complete_crossfade() {
|
||||
crossfade_active = false;
|
||||
|
||||
if (crossfade_to_night) {
|
||||
// Destroy day sound, ensure night is at full volume
|
||||
if (day_sound_handle != -1) {
|
||||
p.destroy_sound(day_sound_handle);
|
||||
day_sound_handle = -1;
|
||||
}
|
||||
if (night_sound_handle != -1) p.update_sound_start_values(night_sound_handle, 0.0, 0.0, 1.0);
|
||||
is_daytime = false;
|
||||
} else {
|
||||
// Destroy night sound, ensure day is at full volume
|
||||
if (night_sound_handle != -1) {
|
||||
p.destroy_sound(night_sound_handle);
|
||||
night_sound_handle = -1;
|
||||
}
|
||||
if (day_sound_handle != -1) p.update_sound_start_values(day_sound_handle, 0.0, 0.0, 1.0);
|
||||
is_daytime = true;
|
||||
}
|
||||
}
|
||||
|
||||
void update_ambience(bool force_restart) {
|
||||
if (is_daytime) {
|
||||
// Transition to Day
|
||||
// Start day ambience
|
||||
if (night_sound_handle != -1) {
|
||||
p.destroy_sound(night_sound_handle);
|
||||
night_sound_handle = -1;
|
||||
}
|
||||
if (day_sound_handle == -1 || !p.sound_is_active(day_sound_handle)) {
|
||||
// Play looped, stationary (or relative to player x?)
|
||||
// Usually ambience is 2D/Global. play_stationary_extended allows looping.
|
||||
// Or play_1d at player position if we want panning?
|
||||
// "sounds/nature/day.ogg"
|
||||
day_sound_handle = p.play_stationary("sounds/nature/day.ogg", true);
|
||||
day_sound_handle = p.play_stationary("sounds/nature/day.ogg", true);
|
||||
}
|
||||
} else {
|
||||
// Transition to Night
|
||||
// Start night ambience
|
||||
if (day_sound_handle != -1) {
|
||||
p.destroy_sound(day_sound_handle);
|
||||
day_sound_handle = -1;
|
||||
}
|
||||
if (night_sound_handle == -1 || !p.sound_is_active(night_sound_handle)) {
|
||||
night_sound_handle = p.play_stationary("sounds/nature/night.ogg", true);
|
||||
night_sound_handle = p.play_stationary("sounds/nature/night.ogg", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user