Invasions should happen even if loading from save.

This commit is contained in:
Storm Dragon
2026-01-18 23:37:03 -05:00
parent 489408a181
commit 5aebd94431
4 changed files with 144 additions and 7 deletions

View File

@@ -37,3 +37,115 @@ void keep_base_fires_fed() {
break; break;
} }
} }
// Resident defense functions
int get_available_defense_weapons() {
int count = storage_spears;
// Slings only count if stones are available
if (storage_slings > 0 && storage_stones > 0) {
count += storage_slings;
}
return count;
}
bool can_residents_defend() {
if (residents_count <= 0) return false;
return get_available_defense_weapons() > 0;
}
bool choose_defense_weapon() {
// Returns true for spear, false for sling
int spearCount = storage_spears;
int slingCount = (storage_slings > 0 && storage_stones > 0) ? storage_slings : 0;
int total = spearCount + slingCount;
if (total == 0) return true;
if (slingCount == 0) return true;
if (spearCount == 0) return false;
int roll = random(1, total);
return roll <= spearCount;
}
int perform_resident_defense() {
if (!can_residents_defend()) return 0;
// Choose weapon type randomly weighted by availability
bool useSpear = choose_defense_weapon();
int damage = 0;
if (useSpear && storage_spears > 0) {
damage = RESIDENT_SPEAR_DAMAGE;
play_1d_with_volume_step("sounds/weapons/spear_swing.ogg", x, BASE_END + 1, false, 3.0);
} else if (storage_slings > 0 && storage_stones > 0) {
damage = random(RESIDENT_SLING_DAMAGE_MIN, RESIDENT_SLING_DAMAGE_MAX);
storage_stones--;
play_1d_with_volume_step("sounds/weapons/sling_hit.ogg", x, BASE_END + 1, false, 3.0);
}
return damage;
}
void process_daily_weapon_breakage() {
if (residents_count <= 0) return;
int totalWeapons = storage_spears + storage_slings;
if (totalWeapons == 0) return;
// Number of breakage checks = min(residents, weapons)
int checksToPerform = (residents_count < totalWeapons) ? residents_count : totalWeapons;
// Distribute checks among available weapons
int spearChecks = 0;
int slingChecks = 0;
for (int i = 0; i < checksToPerform; i++) {
int remainingSpears = storage_spears - spearChecks;
int remainingSlings = storage_slings - slingChecks;
int remaining = remainingSpears + remainingSlings;
if (remaining <= 0) break;
int roll = random(1, remaining);
if (roll <= remainingSpears && remainingSpears > 0) {
spearChecks++;
} else if (remainingSlings > 0) {
slingChecks++;
}
}
// Perform breakage checks
int spearsBroken = 0;
int slingsBroken = 0;
for (int i = 0; i < spearChecks; i++) {
if (random(1, 100) <= RESIDENT_WEAPON_BREAK_CHANCE) {
spearsBroken++;
}
}
for (int i = 0; i < slingChecks; i++) {
if (random(1, 100) <= RESIDENT_WEAPON_BREAK_CHANCE) {
slingsBroken++;
}
}
// Apply breakage
if (spearsBroken > 0) {
storage_spears -= spearsBroken;
if (storage_spears < 0) storage_spears = 0;
string msg = (spearsBroken == 1)
? "A resident's spear broke from wear."
: spearsBroken + " spears broke from wear.";
notify(msg);
}
if (slingsBroken > 0) {
storage_slings -= slingsBroken;
if (storage_slings < 0) storage_slings = 0;
string msg = (slingsBroken == 1)
? "A resident's sling broke from wear."
: slingsBroken + " slings broke from wear.";
notify(msg);
}
}

View File

@@ -97,3 +97,9 @@ const double QUEST_FAVOR_PER_POINT = 0.05;
const int QUEST_STONE_SCORE = 6; const int QUEST_STONE_SCORE = 6;
const int QUEST_LOG_SCORE = 10; const int QUEST_LOG_SCORE = 10;
const int QUEST_SKIN_SCORE = 14; const int QUEST_SKIN_SCORE = 14;
// Resident defense settings
const int RESIDENT_WEAPON_BREAK_CHANCE = 10;
const int RESIDENT_SPEAR_DAMAGE = 2;
const int RESIDENT_SLING_DAMAGE_MIN = 3;
const int RESIDENT_SLING_DAMAGE_MAX = 5;

View File

@@ -153,12 +153,12 @@ void check_scheduled_invasion() {
void attempt_daily_invasion() { void attempt_daily_invasion() {
if (current_day < 2) return; if (current_day < 2) return;
if (invasion_triggered_today || invasion_active) return; if (invasion_triggered_today || invasion_active) return;
if (invasion_roll_done_today) return; if (current_hour < 6 || current_hour > 12) return;
invasion_roll_done_today = true;
int roll = random(1, 100); int roll = random(1, 100);
if (roll > invasion_chance) return; if (roll > invasion_chance) return;
invasion_triggered_today = true;
schedule_invasion(); schedule_invasion();
check_scheduled_invasion(); check_scheduled_invasion();
} }
@@ -305,7 +305,6 @@ void update_time() {
check_ambience_transition(); 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 (is_daytime && residents_count > 0 && barricade_health < BARRICADE_MAX_HEALTH && current_hour % 4 == 0) {
if (storage_meat > 0) { if (storage_meat > 0) {
int gained = add_barricade_health(residents_count); int gained = add_barricade_health(residents_count);
@@ -317,12 +316,10 @@ void update_time() {
if (current_hour == 6) { if (current_hour == 6) {
save_game_state(); save_game_state();
} process_daily_weapon_breakage();
if (current_hour == 6) {
attempt_daily_invasion();
attempt_daily_quest(); attempt_daily_quest();
} }
attempt_daily_invasion();
keep_base_fires_fed(); keep_base_fires_fed();
check_scheduled_invasion(); check_scheduled_invasion();
attempt_blessing(); attempt_blessing();

View File

@@ -618,6 +618,17 @@ void try_attack_barricade(Zombie@ zombie) {
play_1d_with_volume_step("sounds/enemies/zombie_hits_player.ogg", x, zombie.position, false, ZOMBIE_SOUND_VOLUME_STEP); play_1d_with_volume_step("sounds/enemies/zombie_hits_player.ogg", x, zombie.position, false, ZOMBIE_SOUND_VOLUME_STEP);
// Resident defense counter-attack
if (can_residents_defend()) {
int counterDamage = perform_resident_defense();
if (counterDamage > 0) {
zombie.health -= counterDamage;
if (zombie.health <= 0 && x <= BASE_END) {
screen_reader_speak("Residents killed an attacking zombie.", true);
}
}
}
if (barricade_health == 0) { if (barricade_health == 0) {
notify("The barricade has fallen!"); notify("The barricade has fallen!");
} }
@@ -859,6 +870,17 @@ void try_attack_barricade_bandit(Bandit@ bandit) {
p.play_stationary("sounds/weapons/axe_hit.ogg", false); p.play_stationary("sounds/weapons/axe_hit.ogg", false);
} }
// Resident defense counter-attack
if (can_residents_defend()) {
int counterDamage = perform_resident_defense();
if (counterDamage > 0) {
bandit.health -= counterDamage;
if (bandit.health <= 0 && x <= BASE_END) {
screen_reader_speak("Residents killed an attacking bandit.", true);
}
}
}
if (barricade_health == 0) { if (barricade_health == 0) {
notify("The barricade has fallen!"); notify("The barricade has fallen!");
} }