Total darkness now possible if players survive long enough. Undead raids added if invasion happens in darkness.

This commit is contained in:
Storm Dragon
2026-06-25 03:00:25 -04:00
parent aa866ba83d
commit 7c5aff28bb
13 changed files with 345 additions and 48 deletions
+2 -1
View File
@@ -117,6 +117,7 @@ time.event.mountain_discovered='n Bergreeks is in die ooste ontdek!
time.event.invasion_source_mountains=die berge
time.event.invasion_source_new_area=die nuwe area
time.event.invasion_start={enemy_plural} val in vanaf {source}!
time.event.undead_resident_raid=Die dooies onthou waar mense nog leef. {count} ondode inwoners kom.
time.event.zombie_swarm_spotted='n Swerm zombies is gewaar.
time.event.survivor_joins_one='n Oorlewende sluit by jou basis aan.
time.event.survivor_joins_many={count} oorlewendes sluit by jou basis aan.
@@ -1031,7 +1032,7 @@ system.goals.mastery.crafting.name=Handwerk Bemeestering
system.goals.mastery.leadership.level1=Inwoners is versigtig en breek minder gereedskap en wapens.
system.goals.mastery.leadership.level2=Inwoners is georganiseer en slaag meer gereeld met werk.
system.goals.mastery.leadership.level3=Inwoners tree vinniger op tydens verdediging en dringende werk.
system.goals.mastery.leadership.level4=Inwoners rantsoeneer kos en eet elke 12 uur in plaas van elke 8.
system.goals.mastery.leadership.level4=Inwoners rantsoeneer kos en bly werk na nagval.
system.goals.mastery.leadership.name=Leierskap Bemeestering
system.goals.mastery.taming.level1=Troeteldiere kan gevalle items haal.
system.goals.mastery.taming.level2=Troeteldiere is sterker en kry een maksimum gesondheid.
+7 -6
View File
@@ -71,6 +71,7 @@ time.event.mountain_discovered=A mountain range has been discovered to the east!
time.event.invasion_source_mountains=the mountains
time.event.invasion_source_new_area=the new area
time.event.invasion_start={enemy_plural} are invading from {source}!
time.event.undead_resident_raid=The dead remember where people still live. {count} undead residents are coming.
time.event.zombie_swarm_spotted=A swarm of zombies has been spotted.
time.event.survivor_joins_one=A survivor joins your base.
time.event.survivor_joins_many={count} survivors join your base.
@@ -293,7 +294,7 @@ goals.mastery.crafting.level4=Personal tools, weapons, and clothing no longer we
goals.mastery.leadership.level1=Residents are careful and break fewer tools and weapons.
goals.mastery.leadership.level2=Residents are organized and succeed at work more often.
goals.mastery.leadership.level3=Residents act faster during defense and urgent work.
goals.mastery.leadership.level4=Residents ration food, eating every 12 hours instead of every 8.
goals.mastery.leadership.level4=Residents ration food and keep working after nightfall.
goals.mastery.taming.level1=Pets can retrieve dropped items.
goals.mastery.taming.level2=Pets are heartier and gain one maximum health.
goals.mastery.taming.level3=Pets recover from knockout faster.
@@ -976,7 +977,7 @@ msg.5cac31311668=Pasture is full. No livestock were recovered.
msg.5cc7429297c8=that can be destroyed with an axe.
; seed:learn_sounds_label:sounds/actions/cast_strength.ogg
msg.5dfc869b7588=cast strength
; src/time_system.nvgt:801:notify[0]
; src/time_system.nvgt:861:notify[0]
msg.5ea84b1201bb={arg1} favor grants you the eyes of an eagle.
; seed:learn_sounds_label:sounds/menu/menu_select.ogg
; seed:learn_sounds_label:sounds/menu.bak/menu_select.ogg
@@ -1070,8 +1071,8 @@ msg.803e4c41bbd2=sticks
msg.80655da8d80a=tree
; src/item_registry.nvgt:item_plural:canoes
msg.806baaf8e039=canoes
; src/time_system.nvgt:783:notify[0]
; src/time_system.nvgt:793:notify[0]
; src/time_system.nvgt:843:notify[0]
; src/time_system.nvgt:853:notify[0]
msg.81df04a81e24={arg1} favor shines upon you. {arg2}
; src/quests/catch_the_boomerang_game.nvgt:22:insert_last[0]
msg.823be948275f=- Better timing earns more points (up to 4)
@@ -1097,7 +1098,7 @@ msg.86e257c3efcb=Oldest notification. {arg1}
msg.87383ce4344d=Bowstrings
; src/item_registry.nvgt:item_plural:ropes
msg.87cdb5c91437=ropes
; src/time_system.nvgt:797:notify[0]
; src/time_system.nvgt:857:notify[0]
msg.8818f1d55166={arg1} radiance fills residents with purpose.
; src/quests/bat_invasion_game.nvgt:7:insert_last[0]
msg.88749dbbbf09==== Bat Invasion ===
@@ -1115,7 +1116,7 @@ msg.8f993ac20023=You enter a narrow mountain pass. A massive Unicorn blocks your
msg.917ee46db0cd=bee
; seed:learn_sounds_label:sounds/player_male_damage.ogg
msg.924e0eb6b6f1=player male damage
; src/time_system.nvgt:788:notify[0]
; src/time_system.nvgt:848:notify[0]
msg.9281d2fe5f08={arg1} favor shines upon you. You feel swift for a while.
; seed:learn_sounds_label:sounds/actions/climb_rope.ogg
msg.92abc6d6953f=climb rope
+7 -6
View File
@@ -71,6 +71,7 @@ time.event.mountain_discovered=A mountain range has been discovered to the east!
time.event.invasion_source_mountains=the mountains
time.event.invasion_source_new_area=the new area
time.event.invasion_start={enemy_plural} are invading from {source}!
time.event.undead_resident_raid=The dead remember where people still live. {count} undead residents are coming.
time.event.zombie_swarm_spotted=A swarm of zombies has been spotted.
time.event.survivor_joins_one=A survivor joins your base.
time.event.survivor_joins_many={count} survivors join your base.
@@ -293,7 +294,7 @@ goals.mastery.crafting.level4=Personal tools, weapons, and clothing no longer we
goals.mastery.leadership.level1=Residents are careful and break fewer tools and weapons.
goals.mastery.leadership.level2=Residents are organized and succeed at work more often.
goals.mastery.leadership.level3=Residents act faster during defense and urgent work.
goals.mastery.leadership.level4=Residents ration food, eating every 12 hours instead of every 8.
goals.mastery.leadership.level4=Residents ration food and keep working after nightfall.
goals.mastery.taming.level1=Pets can retrieve dropped items.
goals.mastery.taming.level2=Pets are heartier and gain one maximum health.
goals.mastery.taming.level3=Pets recover from knockout faster.
@@ -976,7 +977,7 @@ msg.5cac31311668=Pasture is full. No livestock were recovered.
msg.5cc7429297c8=that can be destroyed with an axe.
; seed:learn_sounds_label:sounds/actions/cast_strength.ogg
msg.5dfc869b7588=cast strength
; src/time_system.nvgt:801:notify[0]
; src/time_system.nvgt:861:notify[0]
msg.5ea84b1201bb={arg1} favor grants you the eyes of an eagle.
; seed:learn_sounds_label:sounds/menu/menu_select.ogg
; seed:learn_sounds_label:sounds/menu.bak/menu_select.ogg
@@ -1070,8 +1071,8 @@ msg.803e4c41bbd2=sticks
msg.80655da8d80a=tree
; src/item_registry.nvgt:item_plural:canoes
msg.806baaf8e039=canoes
; src/time_system.nvgt:783:notify[0]
; src/time_system.nvgt:793:notify[0]
; src/time_system.nvgt:843:notify[0]
; src/time_system.nvgt:853:notify[0]
msg.81df04a81e24={arg1} favor shines upon you. {arg2}
; src/quests/catch_the_boomerang_game.nvgt:22:insert_last[0]
msg.823be948275f=- Better timing earns more points (up to 4)
@@ -1097,7 +1098,7 @@ msg.86e257c3efcb=Oldest notification. {arg1}
msg.87383ce4344d=Bowstrings
; src/item_registry.nvgt:item_plural:ropes
msg.87cdb5c91437=ropes
; src/time_system.nvgt:797:notify[0]
; src/time_system.nvgt:857:notify[0]
msg.8818f1d55166={arg1} radiance fills residents with purpose.
; src/quests/bat_invasion_game.nvgt:7:insert_last[0]
msg.88749dbbbf09==== Bat Invasion ===
@@ -1115,7 +1116,7 @@ msg.8f993ac20023=You enter a narrow mountain pass. A massive Unicorn blocks your
msg.917ee46db0cd=bee
; seed:learn_sounds_label:sounds/player_male_damage.ogg
msg.924e0eb6b6f1=player male damage
; src/time_system.nvgt:788:notify[0]
; src/time_system.nvgt:848:notify[0]
msg.9281d2fe5f08={arg1} favor shines upon you. You feel swift for a while.
; seed:learn_sounds_label:sounds/actions/climb_rope.ogg
msg.92abc6d6953f=climb rope
+2 -1
View File
@@ -117,6 +117,7 @@ time.event.mountain_discovered=Se ha descubierto una cordillera al este!
time.event.invasion_source_mountains=las montanas
time.event.invasion_source_new_area=el area nueva
time.event.invasion_start={enemy_plural} estan invadiendo desde {source}!
time.event.undead_resident_raid=Los muertos recuerdan donde aun vive la gente. Vienen {count} residentes no muertos.
time.event.zombie_swarm_spotted=Se ha detectado una horda de zombis.
time.event.survivor_joins_one=Un superviviente se une a tu base.
time.event.survivor_joins_many={count} supervivientes se unen a tu base.
@@ -1031,7 +1032,7 @@ system.goals.mastery.crafting.name=Maestría de Fabricación
system.goals.mastery.leadership.level1=Los residentes son cuidadosos y rompen menos herramientas y armas.
system.goals.mastery.leadership.level2=Los residentes son organizados y tienen éxito en el trabajo más a menudo.
system.goals.mastery.leadership.level3=Los residentes actúan más rápido durante la defensa y el trabajo urgente.
system.goals.mastery.leadership.level4=Los residentes racionan comida y comen cada 12 horas en vez de cada 8.
system.goals.mastery.leadership.level4=Los residentes racionan comida y siguen trabajando después del anochecer.
system.goals.mastery.leadership.name=Maestría de Liderazgo
system.goals.mastery.taming.level1=Las mascotas pueden recuperar objetos caídos.
system.goals.mastery.taming.level2=Las mascotas son más resistentes y ganan una salud máxima.
+3 -1
View File
@@ -562,6 +562,8 @@ def write_catalog(entries: Dict[str, Dict[str, object]], output_path: Path) -> N
("system.time.event.invasion_source_mountains", "the mountains"),
("system.time.event.invasion_source_new_area", "the new area"),
("system.time.event.invasion_start", "{enemy_plural} are invading from {source}!"),
("system.time.event.undead_resident_raid",
"The dead remember where people still live. {count} undead residents are coming."),
("system.time.event.zombie_swarm_spotted", "A swarm of zombies has been spotted."),
("system.time.event.survivor_joins_one", "A survivor joins your base."),
("system.time.event.survivor_joins_many", "{count} survivors join your base."),
@@ -796,7 +798,7 @@ def write_catalog(entries: Dict[str, Dict[str, object]], output_path: Path) -> N
("system.goals.mastery.leadership.level1", "Residents are careful and break fewer tools and weapons."),
("system.goals.mastery.leadership.level2", "Residents are organized and succeed at work more often."),
("system.goals.mastery.leadership.level3", "Residents act faster during defense and urgent work."),
("system.goals.mastery.leadership.level4", "Residents ration food, eating every 12 hours instead of every 8."),
("system.goals.mastery.leadership.level4", "Residents ration food and keep working after nightfall."),
("system.goals.mastery.taming.level1", "Pets can retrieve dropped items."),
("system.goals.mastery.taming.level2", "Pets are heartier and gain one maximum health."),
("system.goals.mastery.taming.level3", "Pets recover from knockout faster."),
+5 -8
View File
@@ -155,7 +155,7 @@ void consume_food_for_residents() {
}
void attempt_resident_fishing() {
if (!is_daytime)
if (!is_daytime && !leadership_mastery_allows_night_work())
return;
if (residents_count <= 0)
return;
@@ -216,7 +216,7 @@ void attempt_resident_fishing() {
}
void attempt_resident_fish_smoking() {
if (!is_daytime)
if (!is_daytime && !leadership_mastery_allows_night_work())
return;
if (residents_count <= 0)
return;
@@ -741,8 +741,7 @@ void attempt_resident_clothing_repairs() {
// Resident snare retrieval
void attempt_resident_snare_retrieval() {
// Only during daytime
if (!is_daytime)
if (!is_daytime && !leadership_mastery_allows_night_work())
return;
// Need residents
@@ -926,8 +925,7 @@ void attempt_resident_butchering() {
// Resident resource collection
void attempt_resident_collection() {
// Only during daytime
if (!is_daytime)
if (!is_daytime && !leadership_mastery_allows_night_work())
return;
// Need residents
@@ -1005,8 +1003,7 @@ void attempt_resident_collection() {
// Resident foraging - produces baskets of fruits and nuts from reed baskets
void attempt_resident_foraging() {
// Only during daytime
if (!is_daytime)
if (!is_daytime && !leadership_mastery_allows_night_work())
return;
// Need residents
+3
View File
@@ -86,6 +86,9 @@ const int ZOMBIE_ATTACK_MAX_HEIGHT = 6;
const int UNDEAD_RESIDENT_HEALTH = 16;
const int UNDEAD_RESIDENT_DAMAGE_MIN = 5;
const int UNDEAD_RESIDENT_DAMAGE_MAX = 7;
const int UNDEAD_RESIDENT_RAID_BASE_COUNT = 2;
const int UNDEAD_RESIDENT_RAID_DAYS_PER_EXTRA = 18;
const int UNDEAD_RESIDENT_RAID_MAX_COUNT = 8;
// Wight settings (undead elite)
const int WIGHT_HEALTH = 40;
+222
View File
@@ -0,0 +1,222 @@
// Corpse worms are weak opportunistic enemies that begin appearing after day 1.
string[] corpse_worm_sounds = {"sounds/enemies/corpse_worm.ogg"};
timer corpse_worm_spawn_timer;
class CorpseWorm {
int position;
int health;
int sound_handle;
bool in_weapon_range;
timer move_timer;
timer attack_timer;
string voice_sound;
CorpseWorm(int pos) {
position = pos;
health = CORPSE_WORM_HEALTH;
sound_handle = -1;
in_weapon_range = false;
voice_sound = corpse_worm_sounds[random(0, corpse_worm_sounds.length() - 1)];
move_timer.restart();
attack_timer.restart();
}
} CorpseWorm @[] corpse_worms;
void update_corpse_worm_weapon_range_audio() {
for (uint i = 0; i < corpse_worms.length(); i++) {
update_weapon_range_audio(corpse_worms[i].position, corpse_worms[i].in_weapon_range);
}
}
bool corpse_worm_range_audio_registered = false;
void ensure_corpse_worm_range_audio_registration() {
if (corpse_worm_range_audio_registered)
return;
corpse_worm_range_audio_registered = register_weapon_range_audio_callback(@update_corpse_worm_weapon_range_audio);
}
void clear_corpse_worms() {
for (uint i = 0; i < corpse_worms.length(); i++) {
force_weapon_range_exit(corpse_worms[i].position, corpse_worms[i].in_weapon_range);
if (corpse_worms[i].sound_handle != -1) {
p.destroy_sound(corpse_worms[i].sound_handle);
corpse_worms[i].sound_handle = -1;
}
}
corpse_worms.resize(0);
corpse_worm_spawn_timer.restart();
}
CorpseWorm @get_corpse_worm_at(int pos) {
for (uint i = 0; i < corpse_worms.length(); i++) {
if (corpse_worms[i].position == pos) {
return @corpse_worms[i];
}
}
return null;
}
bool can_spawn_corpse_worm_at(int pos) {
if (pos <= BASE_END || pos < 0 || pos >= MAP_SIZE)
return false;
if (pos == x)
return false;
if (get_corpse_worm_at(pos) != null)
return false;
if (get_bandit_at(pos) != null)
return false;
if (get_boar_at(pos) != null)
return false;
if (get_zombie_at(pos) != null)
return false;
if (get_flying_creature_at(pos) != null)
return false;
return true;
}
int pick_corpse_worm_spawn_position() {
for (int attempts = 0; attempts < 20; attempts++) {
int distance = random(CORPSE_WORM_SPAWN_MIN_DISTANCE, CORPSE_WORM_SPAWN_MAX_DISTANCE);
int direction = (random(0, 1) == 0) ? -1 : 1;
int candidate = x + (distance * direction);
if (can_spawn_corpse_worm_at(candidate))
return candidate;
}
for (int candidate = BASE_END + 1; candidate < MAP_SIZE; candidate++) {
if (can_spawn_corpse_worm_at(candidate))
return candidate;
}
return -1;
}
void spawn_corpse_worm() {
int spawn_x = pick_corpse_worm_spawn_position();
if (spawn_x == -1)
return;
CorpseWorm @worm = CorpseWorm(spawn_x);
corpse_worms.insert_last(worm);
int[] areaStarts;
int[] areaEnds;
get_active_audio_areas(areaStarts, areaEnds);
if (areaStarts.length() == 0 || range_overlaps_active_areas(spawn_x, spawn_x, areaStarts, areaEnds)) {
worm.sound_handle =
play_1d_with_volume_step(worm.voice_sound, x, spawn_x, true, CORPSE_WORM_SOUND_VOLUME_STEP);
}
}
void attempt_corpse_worm_spawn() {
if (current_day < CORPSE_WORM_START_DAY)
return;
if (x <= BASE_END)
return;
if (corpse_worms.length() >= CORPSE_WORM_MAX_COUNT)
return;
if (corpse_worm_spawn_timer.elapsed < CORPSE_WORM_SPAWN_ROLL_INTERVAL)
return;
corpse_worm_spawn_timer.restart();
if (random(1, 100) <= CORPSE_WORM_SPAWN_CHANCE) {
spawn_corpse_worm();
}
}
bool try_attack_player_corpse_worm(CorpseWorm @worm) {
if (player_health <= 0)
return false;
if (x <= BASE_END)
return false;
if (y != 0)
return false;
if (worm.position != x)
return false;
if (worm.attack_timer.elapsed < CORPSE_WORM_ATTACK_INTERVAL)
return false;
worm.attack_timer.restart();
player_health -= CORPSE_WORM_DAMAGE;
if (player_health < 0)
player_health = 0;
play_player_damage_sound();
return true;
}
void update_corpse_worm(CorpseWorm @worm, bool audio_active) {
if (!audio_active) {
if (worm.sound_handle != -1) {
p.destroy_sound(worm.sound_handle);
worm.sound_handle = -1;
}
} else if (worm.sound_handle != -1 && p.sound_is_active(worm.sound_handle)) {
p.update_sound_1d(worm.sound_handle, worm.position);
} else if (worm.sound_handle == -1 || !p.sound_is_active(worm.sound_handle)) {
if (worm.sound_handle != -1) {
p.destroy_sound(worm.sound_handle);
}
worm.sound_handle =
play_1d_with_volume_step(worm.voice_sound, x, worm.position, true, CORPSE_WORM_SOUND_VOLUME_STEP);
}
if (try_attack_player_corpse_worm(worm))
return;
if (worm.move_timer.elapsed < CORPSE_WORM_MOVE_INTERVAL)
return;
worm.move_timer.restart();
int target_x = (x > BASE_END) ? x : BASE_END + 1;
if (target_x == worm.position)
return;
int direction = (target_x > worm.position) ? 1 : -1;
int next_pos = worm.position + direction;
if (next_pos <= BASE_END || next_pos < 0 || next_pos >= MAP_SIZE)
return;
if (get_corpse_worm_at(next_pos) != null)
return;
worm.position = next_pos;
if (audio_active) {
play_creature_footstep(x, worm.position, BASE_END, GRASS_END, CORPSE_WORM_FOOTSTEP_MAX_DISTANCE,
CORPSE_WORM_SOUND_VOLUME_STEP);
}
}
void update_corpse_worms() {
ensure_corpse_worm_range_audio_registration();
attempt_corpse_worm_spawn();
int[] areaStarts;
int[] areaEnds;
get_active_audio_areas(areaStarts, areaEnds);
bool limit_audio = (areaStarts.length() > 0);
for (uint i = 0; i < corpse_worms.length(); i++) {
bool audio_active =
!limit_audio || range_overlaps_active_areas(corpse_worms[i].position, corpse_worms[i].position, areaStarts,
areaEnds);
update_corpse_worm(corpse_worms[i], audio_active);
}
}
bool damage_corpse_worm_at(int pos, int damage) {
for (uint i = 0; i < corpse_worms.length(); i++) {
if (corpse_worms[i].position == pos) {
corpse_worms[i].health -= damage;
if (corpse_worms[i].health <= 0) {
force_weapon_range_exit(corpse_worms[i].position, corpse_worms[i].in_weapon_range);
if (corpse_worms[i].sound_handle != -1) {
p.destroy_sound(corpse_worms[i].sound_handle);
corpse_worms[i].sound_handle = -1;
}
play_creature_death_sounds("sounds/enemies/enemy_falls.ogg", corpse_worms[i].voice_sound, x, pos,
CORPSE_WORM_SOUND_VOLUME_STEP);
corpse_worms.remove_at(i);
}
return true;
}
}
return false;
}
+8 -5
View File
@@ -79,6 +79,7 @@ class Undead {
bool retreating;
bool suppress_voice;
bool should_despawn;
bool counts_as_lost_resident;
timer move_timer;
timer attack_timer;
@@ -92,6 +93,7 @@ class Undead {
retreating = false;
suppress_voice = false;
should_despawn = false;
counts_as_lost_resident = (undead_type == "undead_resident");
move_timer.restart();
attack_timer.restart();
}
@@ -257,7 +259,7 @@ int pick_undead_spawn_near_player(int min_distance, int max_distance) {
return candidate;
}
void spawn_undead(const string& in undead_type = "zombie") {
void spawn_undead(const string& in undead_type = "zombie", bool counts_as_lost_resident = true) {
int spawn_x = -1;
if (undead_type == "zombie" || undead_type == "undead_resident") {
spawn_x = pick_undead_spawn_near_player(ZOMBIE_SPAWN_MIN_DISTANCE, ZOMBIE_SPAWN_MAX_DISTANCE);
@@ -269,6 +271,7 @@ void spawn_undead(const string& in undead_type = "zombie") {
return;
Undead @undead = Undead(spawn_x, undead_type);
undead.counts_as_lost_resident = (undead_type == "undead_resident" && counts_as_lost_resident);
undeads.insert_last(undead);
// Play looping sound that follows the undead
int[] areaStarts;
@@ -526,7 +529,7 @@ void update_undeads() {
for (uint i = 0; i < undeads.length(); i++) {
if (undeads[i].undead_type == "zombie") {
zombie_count++;
} else if (undeads[i].undead_type == "undead_resident") {
} else if (undeads[i].undead_type == "undead_resident" && undeads[i].counts_as_lost_resident) {
undead_resident_count++;
}
}
@@ -566,7 +569,7 @@ void update_undeads() {
}
void attempt_hourly_wight_spawn() {
if (current_hour == 19) {
if (current_hour == get_current_night_start_hour()) {
wight_spawned_this_night_count = 0;
}
@@ -602,7 +605,7 @@ void attempt_hourly_wight_spawn() {
}
void attempt_hourly_vampyr_spawn() {
if (current_hour == 19) {
if (current_hour == get_current_night_start_hour()) {
vampyr_spawned_this_night_count = 0;
}
@@ -649,7 +652,7 @@ bool damage_undead_at(int pos, int damage) {
if (world_altars.length() > 0) {
favor += 0.2;
}
if (undeads[i].undead_type == "undead_resident") {
if (undeads[i].undead_type == "undead_resident" && undeads[i].counts_as_lost_resident) {
undead_residents_count--;
if (undead_residents_count < 0)
undead_residents_count = 0;
+1 -1
View File
@@ -105,7 +105,7 @@ int get_random_stream_tile() {
}
string get_random_fish_type() {
bool is_night = (current_hour >= 18 || current_hour < 7);
bool is_night = is_night_hour(current_hour);
if (is_night) {
int roll = random(0, 99);
+4
View File
@@ -484,6 +484,10 @@ int get_resident_food_interval_hours() {
return 8;
}
bool leadership_mastery_allows_night_work() {
return leadershipMasteryLevel >= 4;
}
bool taming_mastery_allows_retrieval() {
return tamingMasteryLevel >= 1;
}
+1 -1
View File
@@ -1638,7 +1638,7 @@ bool load_game_state_from_file(const string& in filename) {
if (current_day < 1)
current_day = 1;
is_daytime = (current_hour >= 6 && current_hour < 19);
is_daytime = is_day_hour_for_day(current_hour, current_day);
hour_timer.restart();
string weather_data;
+80 -18
View File
@@ -1,6 +1,9 @@
// Time System
// 1 real minute = 1 in-game hour
const int MS_PER_HOUR = 60000;
const int DAY_START_HOUR = 6;
const int NIGHT_START_BASE_HOUR = 19;
const int NIGHT_START_SHIFT_DAYS = 9;
int current_hour = 8; // Start at 8 AM
int current_day = 1; // Track current day
@@ -361,6 +364,24 @@ void start_invasion() {
notify(trf("system.time.event.invasion_start", args));
}
int get_undead_resident_raid_count() {
int count = UNDEAD_RESIDENT_RAID_BASE_COUNT + (current_day / UNDEAD_RESIDENT_RAID_DAYS_PER_EXTRA);
if (count > UNDEAD_RESIDENT_RAID_MAX_COUNT)
count = UNDEAD_RESIDENT_RAID_MAX_COUNT;
return count;
}
void start_undead_resident_raid() {
int count = get_undead_resident_raid_count();
for (int i = 0; i < count; i++) {
spawn_undead("undead_resident", false);
}
dictionary args;
args.set("count", count);
notify(trf("system.time.event.undead_resident_raid", args));
}
void update_invasion_chance_for_new_day() {
if (current_day == 2) {
invasion_chance = 100;
@@ -385,17 +406,52 @@ int get_random_invasion_hour(int min_hour) {
return random(min_hour, 11);
}
int get_night_start_hour_for_day(int day) {
if (day < 1)
day = 1;
int nightStartHour = NIGHT_START_BASE_HOUR - (day / NIGHT_START_SHIFT_DAYS);
if (nightStartHour < DAY_START_HOUR)
nightStartHour = DAY_START_HOUR;
return nightStartHour;
}
int get_current_night_start_hour() {
return get_night_start_hour_for_day(current_day);
}
bool is_night_hour_for_day(int hour, int day) {
int nightStartHour = get_night_start_hour_for_day(day);
return hour < DAY_START_HOUR || hour >= nightStartHour;
}
bool is_night_hour(int hour) {
return hour < 6 || hour >= 19;
return is_night_hour_for_day(hour, current_day);
}
bool is_day_hour_for_day(int hour, int day) {
return !is_night_hour_for_day(hour, day);
}
bool is_day_hour(int hour) {
return !is_night_hour(hour);
}
bool has_daylight_for_day(int day) {
return get_night_start_hour_for_day(day) > DAY_START_HOUR;
}
bool has_daylight_today() {
return has_daylight_for_day(current_day);
}
int get_random_zombie_swarm_hour(int min_hour) {
if (!is_night_hour(min_hour))
return -1;
if (min_hour >= 19) {
int nightStartHour = get_current_night_start_hour();
if (min_hour >= nightStartHour) {
return random(min_hour, 23);
}
return random(min_hour, 5);
return random(min_hour, DAY_START_HOUR - 1);
}
void schedule_invasion() {
@@ -416,7 +472,11 @@ void check_scheduled_invasion() {
if (current_hour == invasion_scheduled_hour) {
invasion_scheduled_hour = -1;
invasion_triggered_today = true;
start_invasion();
if (is_daytime) {
start_invasion();
} else {
start_undead_resident_raid();
}
} else if (current_hour > 11) {
invasion_scheduled_hour = -1;
}
@@ -849,16 +909,18 @@ void update_time() {
}
handle_pet_hourly_update(current_hour);
if (current_hour == 18 && !sun_setting_warned) {
int nightStartHour = get_current_night_start_hour();
int sunsetWarningHour = nightStartHour - 1;
if (has_daylight_today() && current_hour == sunsetWarningHour && !sun_setting_warned) {
notify(tr("system.time.event.sun_setting"));
sun_setting_warned = true;
} else if (current_hour == 19) {
} else if (current_hour == nightStartHour) {
sun_setting_warned = false;
}
if (current_hour == 5 && !sunrise_warned) {
if (has_daylight_today() && current_hour == DAY_START_HOUR - 1 && !sunrise_warned) {
notify(tr("system.time.event.sky_brightening"));
sunrise_warned = true;
} else if (current_hour == 6) {
} else if (current_hour == DAY_START_HOUR) {
sunrise_warned = false;
}
@@ -869,7 +931,7 @@ void update_time() {
check_ambience_transition();
// Safety: if crossfade failed or was skipped, align day/night with the current hour.
if (!crossfade_active) {
bool expected_daytime = (current_hour >= 6 && current_hour < 19);
bool expected_daytime = is_day_hour(current_hour);
if (expected_daytime != is_daytime) {
is_daytime = expected_daytime;
update_ambience(true);
@@ -877,7 +939,7 @@ void update_time() {
}
if (is_daytime && residents_count > 0 && barricade_health < BARRICADE_MAX_HEALTH) {
const int day_start_hour = 6;
const int day_start_hour = DAY_START_HOUR;
const int day_end_hour = 18; // Exclusive for repair scheduling (12-hour window)
if (current_hour >= day_start_hour && current_hour < day_end_hour) {
int repair_window_hours = day_end_hour - day_start_hour;
@@ -898,7 +960,7 @@ void update_time() {
}
}
if (current_hour == 6) {
if (current_hour == DAY_START_HOUR) {
if (undead_residents_pending > 0) {
undead_residents_count += undead_residents_pending;
undead_residents_pending = 0;
@@ -930,7 +992,7 @@ void update_time() {
attempt_resident_fishing();
attempt_resident_fish_smoking();
attempt_livestock_production();
if (current_hour == 6) {
if (current_hour == DAY_START_HOUR) {
save_game_state();
}
}
@@ -1021,16 +1083,16 @@ string get_time_string() {
}
void check_ambience_transition() {
// 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)
int nightStartHour = get_current_night_start_hour();
if (!has_daylight_today())
return;
// Start crossfade to night at hour 18
if (current_hour == 18 && is_daytime && !crossfade_active) {
// Start crossfade to night one hour before the current day's night boundary.
if (current_hour == nightStartHour - 1 && 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) {
else if (current_hour == DAY_START_HOUR - 1 && !is_daytime && !crossfade_active) {
start_crossfade(false); // Fade to day
}
}