Initial commit.
This commit is contained in:
278
src/time_system.nvgt
Normal file
278
src/time_system.nvgt
Normal file
@@ -0,0 +1,278 @@
|
||||
// Time System
|
||||
// 1 real minute = 1 in-game hour
|
||||
const int MS_PER_HOUR = 60000;
|
||||
|
||||
int current_hour = 8; // Start at 8 AM
|
||||
int current_day = 1; // Track current day
|
||||
timer hour_timer;
|
||||
|
||||
int day_sound_handle = -1;
|
||||
int night_sound_handle = -1;
|
||||
bool is_daytime = true;
|
||||
bool sun_setting_warned = false;
|
||||
bool sunrise_warned = false;
|
||||
|
||||
// Expansion and invasion tracking
|
||||
bool area_expanded_today = false;
|
||||
bool invasion_active = false;
|
||||
int invasion_start_hour = -1;
|
||||
string[] expanded_terrain_types;
|
||||
|
||||
void init_time() {
|
||||
current_hour = 8;
|
||||
current_day = 1;
|
||||
hour_timer.restart();
|
||||
is_daytime = true;
|
||||
sun_setting_warned = false;
|
||||
sunrise_warned = false;
|
||||
area_expanded_today = false;
|
||||
invasion_active = false;
|
||||
invasion_start_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;
|
||||
MAP_SIZE += EXPANSION_SIZE;
|
||||
|
||||
// Generate random terrain for the 30 new tiles
|
||||
expanded_terrain_types.resize(EXPANSION_SIZE);
|
||||
for (int i = 0; i < EXPANSION_SIZE; i++) {
|
||||
int terrain_roll = random(0, 2);
|
||||
if (terrain_roll == 0) {
|
||||
expanded_terrain_types[i] = "stone";
|
||||
} else if (terrain_roll == 1) {
|
||||
expanded_terrain_types[i] = "grass";
|
||||
} else {
|
||||
expanded_terrain_types[i] = "snow";
|
||||
}
|
||||
}
|
||||
|
||||
// Generate streams (30% chance for a stream)
|
||||
int stream_roll = random(1, 100);
|
||||
if (stream_roll <= 30) {
|
||||
// Determine stream width (1-5 tiles)
|
||||
int stream_width = random(1, 5);
|
||||
|
||||
// Find a valid starting position for the stream
|
||||
// Stream can only be in grass, stone, or snow areas
|
||||
int attempts = 0;
|
||||
int stream_start = -1;
|
||||
while (attempts < 50) {
|
||||
int candidate_start = random(0, EXPANSION_SIZE - stream_width);
|
||||
bool valid_position = true;
|
||||
|
||||
// Check if all tiles in this range are valid for a stream
|
||||
for (int i = 0; i < stream_width; i++) {
|
||||
string terrain = expanded_terrain_types[candidate_start + i];
|
||||
// Streams can be in any terrain type
|
||||
if (terrain != "grass" && terrain != "stone" && terrain != "snow") {
|
||||
valid_position = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_position) {
|
||||
stream_start = candidate_start;
|
||||
break;
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
|
||||
// Create the stream if we found a valid position
|
||||
if (stream_start != -1) {
|
||||
int actual_start = expanded_area_start + stream_start;
|
||||
add_world_stream(actual_start, stream_width);
|
||||
|
||||
string width_desc = "very small";
|
||||
if (stream_width == 2) width_desc = "small";
|
||||
else if (stream_width == 3) width_desc = "medium";
|
||||
else if (stream_width == 4) width_desc = "wide";
|
||||
else if (stream_width == 5) width_desc = "very wide";
|
||||
|
||||
notify("A " + width_desc + " stream flows through the new area at x " + actual_start + ".");
|
||||
}
|
||||
}
|
||||
|
||||
// Generate trees in grass areas (50% chance for each grass tile)
|
||||
for (int i = 0; i < EXPANSION_SIZE; i++) {
|
||||
if (expanded_terrain_types[i] == "grass") {
|
||||
int tile_pos = expanded_area_start + i;
|
||||
|
||||
// Skip if this position has a stream
|
||||
if (is_position_in_water(tile_pos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 50% chance to spawn a tree
|
||||
int tree_roll = random(1, 100);
|
||||
if (tree_roll <= 50) {
|
||||
Tree@ t = Tree(tile_pos);
|
||||
trees.insert_last(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
area_expanded_today = true;
|
||||
notify("The area has expanded! New territory discovered to the east.");
|
||||
}
|
||||
|
||||
void start_invasion() {
|
||||
invasion_active = true;
|
||||
invasion_start_hour = current_hour;
|
||||
notify("Bandits are invading from the new area!");
|
||||
}
|
||||
|
||||
void end_invasion() {
|
||||
invasion_active = false;
|
||||
invasion_start_hour = -1;
|
||||
clear_bandits();
|
||||
notify("The bandit invasion has ended.");
|
||||
}
|
||||
|
||||
void check_invasion_status() {
|
||||
if (!invasion_active) return;
|
||||
|
||||
// Check if invasion duration has elapsed
|
||||
int hours_elapsed = current_hour - invasion_start_hour;
|
||||
if (hours_elapsed < 0) {
|
||||
hours_elapsed += 24; // Handle day wrap
|
||||
}
|
||||
|
||||
if (hours_elapsed >= INVASION_DURATION_HOURS) {
|
||||
end_invasion();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void update_time() {
|
||||
if (hour_timer.elapsed >= MS_PER_HOUR) {
|
||||
hour_timer.restart();
|
||||
current_hour++;
|
||||
if (current_hour >= 24) {
|
||||
current_hour = 0;
|
||||
current_day++;
|
||||
area_expanded_today = false; // Reset for new day
|
||||
}
|
||||
|
||||
if (current_hour == 18 && !sun_setting_warned) {
|
||||
notify("The sun is setting.");
|
||||
sun_setting_warned = true;
|
||||
} else if (current_hour == 19) {
|
||||
sun_setting_warned = false;
|
||||
}
|
||||
if (current_hour == 5 && !sunrise_warned) {
|
||||
notify("Sunrise isn't far away.");
|
||||
sunrise_warned = true;
|
||||
} else if (current_hour == 6) {
|
||||
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();
|
||||
|
||||
if (current_hour == 6) {
|
||||
save_game_state();
|
||||
}
|
||||
}
|
||||
|
||||
// Manage bandits during active invasion
|
||||
manage_bandits_during_invasion();
|
||||
}
|
||||
|
||||
void check_time_input() {
|
||||
if (key_pressed(KEY_T)) {
|
||||
screen_reader_speak(get_time_string(), true);
|
||||
}
|
||||
}
|
||||
|
||||
string get_time_string() {
|
||||
int display_hour = current_hour;
|
||||
string period = "am";
|
||||
|
||||
if (display_hour == 0) {
|
||||
display_hour = 12;
|
||||
} else if (display_hour == 12) {
|
||||
period = "pm";
|
||||
} else if (display_hour > 12) {
|
||||
display_hour -= 12;
|
||||
period = "pm";
|
||||
}
|
||||
|
||||
return display_hour + " oclock " + period;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void update_ambience(bool force_restart) {
|
||||
if (is_daytime) {
|
||||
// Transition to Day
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
// Transition to Night
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user