Oh wow, I thought I already pushed some of this stuff. Let's see if I can remember it all. Undead residents added. Whights added, Vampyrs added. Bandit Hideout adventure added.
This commit is contained in:
@@ -11,7 +11,7 @@ chmod +x draugnorak
|
|||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
- Move with Left/Right, jump with Up.
|
- Move with Left/Right, jump with Up.
|
||||||
- Hold Shift for 1 second to search the current area.
|
- Hold Shift, /, or Z for 1 second to search the current area.
|
||||||
- Craft basic tools in the base with C.
|
- Craft basic tools in the base with C.
|
||||||
- Set snares and build fires to survive.
|
- Set snares and build fires to survive.
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ Some of the first things you will want are a stone knife, spear, stone axe, and
|
|||||||
- **Left/Right**: Move.
|
- **Left/Right**: Move.
|
||||||
- **Up**: Jump, climb trees, start rope climbs when prompted.
|
- **Up**: Jump, climb trees, start rope climbs when prompted.
|
||||||
- **Down**: Climb down trees or descend rope climbs when prompted.
|
- **Down**: Climb down trees or descend rope climbs when prompted.
|
||||||
- **Shift (hold)**: Search area (1-second hold, 1-second delay).
|
- **Shift, /, or Z (hold)**: Search area (1-second hold, 1-second delay).
|
||||||
- **Control (hold/release)**: Attack with equipped weapon. Sling uses a charge window.
|
- **Control (hold/release)**: Attack with equipped weapon. Sling uses a charge window.
|
||||||
- **A**: Action menu (place snare, feed fire, burn incense).
|
- **A**: Action menu (place snare, feed fire, burn incense).
|
||||||
- **C**: Crafting menu (base only).
|
- **C**: Crafting menu (base only).
|
||||||
|
|||||||
@@ -394,12 +394,13 @@ void run_game()
|
|||||||
|
|
||||||
// Searching Logic
|
// Searching Logic
|
||||||
bool shift_down = (key_down(KEY_LSHIFT) || key_down(KEY_RSHIFT));
|
bool shift_down = (key_down(KEY_LSHIFT) || key_down(KEY_RSHIFT));
|
||||||
if (!shift_down && !searching) {
|
bool search_key_down = shift_down || key_down(KEY_SLASH) || key_down(KEY_Z);
|
||||||
|
if (!search_key_down && !searching) {
|
||||||
search_timer.restart();
|
search_timer.restart();
|
||||||
}
|
}
|
||||||
// Apply rune gathering bonus to search time
|
// Apply rune gathering bonus to search time
|
||||||
int search_time = apply_rune_gather_bonus(2000);
|
int search_time = apply_rune_gather_bonus(2000);
|
||||||
if (shift_down && search_timer.elapsed > search_time && !searching)
|
if (search_key_down && search_timer.elapsed > search_time && !searching)
|
||||||
{
|
{
|
||||||
searching = true;
|
searching = true;
|
||||||
search_delay_timer.restart();
|
search_delay_timer.restart();
|
||||||
|
|||||||
BIN
sounds/enemies/undead_resident1.ogg
LFS
Normal file
BIN
sounds/enemies/undead_resident1.ogg
LFS
Normal file
Binary file not shown.
BIN
sounds/enemies/undead_resident_dies.ogg
LFS
Normal file
BIN
sounds/enemies/undead_resident_dies.ogg
LFS
Normal file
Binary file not shown.
BIN
sounds/enemies/vampyr1.ogg
LFS
Normal file
BIN
sounds/enemies/vampyr1.ogg
LFS
Normal file
Binary file not shown.
BIN
sounds/enemies/vampyr2.ogg
LFS
Normal file
BIN
sounds/enemies/vampyr2.ogg
LFS
Normal file
Binary file not shown.
BIN
sounds/enemies/vampyr3.ogg
LFS
Normal file
BIN
sounds/enemies/vampyr3.ogg
LFS
Normal file
Binary file not shown.
BIN
sounds/enemies/vampyr4.ogg
LFS
Normal file
BIN
sounds/enemies/vampyr4.ogg
LFS
Normal file
Binary file not shown.
BIN
sounds/enemies/vampyr_dies.ogg
LFS
Normal file
BIN
sounds/enemies/vampyr_dies.ogg
LFS
Normal file
Binary file not shown.
BIN
sounds/enemies/wight1.ogg
LFS
Normal file
BIN
sounds/enemies/wight1.ogg
LFS
Normal file
Binary file not shown.
BIN
sounds/enemies/wight_dies.ogg
LFS
Normal file
BIN
sounds/enemies/wight_dies.ogg
LFS
Normal file
Binary file not shown.
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "src/bosses/adventure_combat.nvgt"
|
#include "src/bosses/adventure_combat.nvgt"
|
||||||
#include "src/bosses/unicorn/unicorn_boss.nvgt"
|
#include "src/bosses/unicorn/unicorn_boss.nvgt"
|
||||||
|
#include "src/bosses/bandit_hideout.nvgt"
|
||||||
|
|
||||||
void check_adventure_menu(int player_x) {
|
void check_adventure_menu(int player_x) {
|
||||||
if (key_pressed(KEY_TAB)) {
|
if (key_pressed(KEY_TAB)) {
|
||||||
@@ -26,13 +27,18 @@ void run_adventure_menu(int player_x) {
|
|||||||
|
|
||||||
// Check available adventures based on terrain
|
// Check available adventures based on terrain
|
||||||
string[] options;
|
string[] options;
|
||||||
int[] adventure_ids; // 1 = Unicorn
|
int[] adventure_ids; // 1 = Unicorn, 2 = Bandit's Hideout
|
||||||
|
|
||||||
if (mountain !is null) {
|
if (mountain !is null) {
|
||||||
// Mountain terrain
|
// Mountain terrain
|
||||||
options.insert_last("Unicorn Hunt (Mountain Boss)");
|
options.insert_last("Unicorn Hunt (Mountain Boss)");
|
||||||
adventure_ids.insert_last(1);
|
adventure_ids.insert_last(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mountain is null && (terrain == "forest" || terrain == "deep_forest")) {
|
||||||
|
options.insert_last("Bandit's Hideout");
|
||||||
|
adventure_ids.insert_last(ADVENTURE_BANDIT_HIDEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
if (options.length() == 0) {
|
if (options.length() == 0) {
|
||||||
speak_with_history("No adventures found in this area.", true);
|
speak_with_history("No adventures found in this area.", true);
|
||||||
@@ -78,5 +84,7 @@ void start_adventure(int adventure_id) {
|
|||||||
last_adventure_day = current_day;
|
last_adventure_day = current_day;
|
||||||
if (adventure_id == 1) {
|
if (adventure_id == 1) {
|
||||||
run_unicorn_adventure();
|
run_unicorn_adventure();
|
||||||
|
} else if (adventure_id == ADVENTURE_BANDIT_HIDEOUT) {
|
||||||
|
run_bandit_hideout_adventure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
669
src/bosses/bandit_hideout.nvgt
Normal file
669
src/bosses/bandit_hideout.nvgt
Normal file
@@ -0,0 +1,669 @@
|
|||||||
|
// Bandit's Hideout Adventure logic
|
||||||
|
// Terrain: Forest / Deep Forest
|
||||||
|
// Objective: Break the barricade at the enemy base.
|
||||||
|
|
||||||
|
const int ADVENTURE_BANDIT_HIDEOUT = 2;
|
||||||
|
const int BANDIT_HIDEOUT_MAP_SIZE = 100;
|
||||||
|
const int BANDIT_HIDEOUT_SEGMENT_MIN = 5;
|
||||||
|
const int BANDIT_HIDEOUT_SEGMENT_MAX = 10;
|
||||||
|
const int BANDIT_HIDEOUT_BANDIT_COUNT = 5;
|
||||||
|
const int BANDIT_HIDEOUT_BASE_SPAWN_RANGE = 10;
|
||||||
|
const int BANDIT_HIDEOUT_START_SPAWN_RANGE = 10;
|
||||||
|
const int BANDIT_HIDEOUT_BARRICADE_HP_PER_DAY = 34;
|
||||||
|
const int BANDIT_HIDEOUT_BARRICADE_HP_MAX = 500;
|
||||||
|
const double BANDIT_HIDEOUT_FAVOR_PER_KILL = 0.2;
|
||||||
|
const double BANDIT_HIDEOUT_BASE_FAVOR = 3.0;
|
||||||
|
const double BANDIT_HIDEOUT_FAVOR_MAX = 10.0;
|
||||||
|
|
||||||
|
class HideoutBandit {
|
||||||
|
int position;
|
||||||
|
int health;
|
||||||
|
string alertSound;
|
||||||
|
string weaponType; // "spear" or "axe"
|
||||||
|
int soundHandle;
|
||||||
|
bool inWeaponRange;
|
||||||
|
timer moveTimer;
|
||||||
|
timer attackTimer;
|
||||||
|
int moveInterval;
|
||||||
|
|
||||||
|
HideoutBandit(int pos, const string&in alert, const string&in weapon, int interval) {
|
||||||
|
position = pos;
|
||||||
|
health = BANDIT_HEALTH;
|
||||||
|
alertSound = alert;
|
||||||
|
weaponType = weapon;
|
||||||
|
moveInterval = interval;
|
||||||
|
soundHandle = -1;
|
||||||
|
inWeaponRange = false;
|
||||||
|
moveTimer.restart();
|
||||||
|
attackTimer.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HideoutBandit@[] hideoutBandits;
|
||||||
|
string[] hideoutTerrain;
|
||||||
|
int hideoutPlayerX = 0;
|
||||||
|
int hideoutPlayerFacing = 1; // 0 = west, 1 = east
|
||||||
|
int hideoutBaseX = BANDIT_HIDEOUT_MAP_SIZE - 1;
|
||||||
|
int hideoutBarricadeHealth = 0;
|
||||||
|
int hideoutBarricadeMax = 0;
|
||||||
|
int hideoutBanditsKilled = 0;
|
||||||
|
timer hideoutWalkTimer;
|
||||||
|
timer hideoutAttackTimer;
|
||||||
|
timer hideoutSearchTimer;
|
||||||
|
timer hideoutSearchDelayTimer;
|
||||||
|
bool hideoutSearching = false;
|
||||||
|
|
||||||
|
string pick_hideout_terrain() {
|
||||||
|
int roll = random(0, 2);
|
||||||
|
if (roll == 0) return "grass";
|
||||||
|
if (roll == 1) return "gravel";
|
||||||
|
return "stone";
|
||||||
|
}
|
||||||
|
|
||||||
|
void build_hideout_terrain() {
|
||||||
|
hideoutTerrain.resize(BANDIT_HIDEOUT_MAP_SIZE);
|
||||||
|
int index = 0;
|
||||||
|
while (index < BANDIT_HIDEOUT_MAP_SIZE) {
|
||||||
|
int segmentLength = random(BANDIT_HIDEOUT_SEGMENT_MIN, BANDIT_HIDEOUT_SEGMENT_MAX);
|
||||||
|
string terrain = pick_hideout_terrain();
|
||||||
|
for (int i = 0; i < segmentLength && index < BANDIT_HIDEOUT_MAP_SIZE; i++) {
|
||||||
|
hideoutTerrain[index] = terrain;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hideoutBaseX >= 0 && hideoutBaseX < int(hideoutTerrain.length())) {
|
||||||
|
hideoutTerrain[hideoutBaseX] = "stone";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_hideout_terrain_at(int pos) {
|
||||||
|
if (pos < 0 || pos >= int(hideoutTerrain.length())) return "grass";
|
||||||
|
string terrain = hideoutTerrain[pos];
|
||||||
|
if (terrain == "") return "grass";
|
||||||
|
return terrain;
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_hideout_footstep_sound(int pos) {
|
||||||
|
string terrain = get_hideout_terrain_at(pos);
|
||||||
|
if (terrain == "stone") return "sounds/terrain/stone.ogg";
|
||||||
|
if (terrain == "gravel") return "sounds/terrain/gravel.ogg";
|
||||||
|
return "sounds/terrain/grass.ogg";
|
||||||
|
}
|
||||||
|
|
||||||
|
void play_hideout_player_footstep() {
|
||||||
|
string soundFile = get_hideout_footstep_sound(hideoutPlayerX);
|
||||||
|
if (file_exists(soundFile)) {
|
||||||
|
p.play_stationary(soundFile, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void play_hideout_positional_footstep(int listenerX, int stepX, int maxDistance, float volumeStep) {
|
||||||
|
if (abs(stepX - listenerX) > maxDistance) return;
|
||||||
|
string soundFile = get_hideout_footstep_sound(stepX);
|
||||||
|
if (file_exists(soundFile)) {
|
||||||
|
play_1d_with_volume_step(soundFile, listenerX, stepX, false, volumeStep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_hideout_bandits() {
|
||||||
|
for (uint i = 0; i < hideoutBandits.length(); i++) {
|
||||||
|
if (hideoutBandits[i].soundHandle != -1) {
|
||||||
|
p.destroy_sound(hideoutBandits[i].soundHandle);
|
||||||
|
hideoutBandits[i].soundHandle = -1;
|
||||||
|
}
|
||||||
|
hideoutBandits[i].inWeaponRange = false;
|
||||||
|
}
|
||||||
|
hideoutBandits.resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
HideoutBandit@ get_hideout_bandit_at(int pos) {
|
||||||
|
for (uint i = 0; i < hideoutBandits.length(); i++) {
|
||||||
|
if (hideoutBandits[i].position == pos) {
|
||||||
|
return @hideoutBandits[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int clamp_hideout_spawn_start(int startX) {
|
||||||
|
if (startX < 0) return 0;
|
||||||
|
if (startX >= BANDIT_HIDEOUT_MAP_SIZE) return BANDIT_HIDEOUT_MAP_SIZE - 1;
|
||||||
|
return startX;
|
||||||
|
}
|
||||||
|
|
||||||
|
int clamp_hideout_spawn_end(int endX) {
|
||||||
|
if (endX < 0) return 0;
|
||||||
|
if (endX >= BANDIT_HIDEOUT_MAP_SIZE) return BANDIT_HIDEOUT_MAP_SIZE - 1;
|
||||||
|
return endX;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pick_hideout_spawn_position(int startX, int endX) {
|
||||||
|
int startClamp = clamp_hideout_spawn_start(startX);
|
||||||
|
int endClamp = clamp_hideout_spawn_end(endX);
|
||||||
|
if (startClamp > endClamp) {
|
||||||
|
int swapTemp = startClamp;
|
||||||
|
startClamp = endClamp;
|
||||||
|
endClamp = swapTemp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spawnX = -1;
|
||||||
|
for (int attempt = 0; attempt < 20; attempt++) {
|
||||||
|
int candidate = random(startClamp, endClamp);
|
||||||
|
if (candidate == hideoutPlayerX) continue;
|
||||||
|
if (get_hideout_bandit_at(candidate) != null) continue;
|
||||||
|
spawnX = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (spawnX == -1) {
|
||||||
|
spawnX = random(startClamp, endClamp);
|
||||||
|
}
|
||||||
|
return spawnX;
|
||||||
|
}
|
||||||
|
|
||||||
|
void spawn_hideout_bandit_in_range(int startX, int endX) {
|
||||||
|
int spawnX = pick_hideout_spawn_position(startX, endX);
|
||||||
|
string alertSound = pick_invader_alert_sound("bandit");
|
||||||
|
if (alertSound == "") alertSound = "sounds/enemies/bandit1.ogg";
|
||||||
|
string weaponType = (random(0, 1) == 0) ? "spear" : "axe";
|
||||||
|
int moveInterval = random(BANDIT_MOVE_INTERVAL_MIN, BANDIT_MOVE_INTERVAL_MAX);
|
||||||
|
|
||||||
|
HideoutBandit@ bandit = HideoutBandit(spawnX, alertSound, weaponType, moveInterval);
|
||||||
|
hideoutBandits.insert_last(bandit);
|
||||||
|
bandit.soundHandle = play_1d_with_volume_step(bandit.alertSound, hideoutPlayerX, bandit.position, true, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spawn_hideout_bandits_initial() {
|
||||||
|
int baseSpawnStart = hideoutBaseX - (BANDIT_HIDEOUT_BASE_SPAWN_RANGE - 1);
|
||||||
|
int baseSpawnEnd = hideoutBaseX;
|
||||||
|
for (int i = 0; i < BANDIT_HIDEOUT_BANDIT_COUNT; i++) {
|
||||||
|
spawn_hideout_bandit_in_range(baseSpawnStart, baseSpawnEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void respawn_hideout_bandit() {
|
||||||
|
int startSpawnStart = 0;
|
||||||
|
int startSpawnEnd = BANDIT_HIDEOUT_START_SPAWN_RANGE - 1;
|
||||||
|
if (startSpawnEnd > hideoutBaseX) startSpawnEnd = hideoutBaseX;
|
||||||
|
|
||||||
|
int baseSpawnStart = hideoutBaseX - (BANDIT_HIDEOUT_BASE_SPAWN_RANGE - 1);
|
||||||
|
int baseSpawnEnd = hideoutBaseX;
|
||||||
|
|
||||||
|
int roll = random(0, 1);
|
||||||
|
if (roll == 0) {
|
||||||
|
spawn_hideout_bandit_in_range(startSpawnStart, startSpawnEnd);
|
||||||
|
} else {
|
||||||
|
spawn_hideout_bandit_in_range(baseSpawnStart, baseSpawnEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_bandit_hideout_adventure() {
|
||||||
|
reset_adventure_combat_state();
|
||||||
|
hideoutPlayerX = 0;
|
||||||
|
hideoutPlayerFacing = 1;
|
||||||
|
hideoutBaseX = BANDIT_HIDEOUT_MAP_SIZE - 1;
|
||||||
|
hideoutBanditsKilled = 0;
|
||||||
|
|
||||||
|
int barricadeBase = current_day * BANDIT_HIDEOUT_BARRICADE_HP_PER_DAY;
|
||||||
|
if (barricadeBase < BANDIT_HIDEOUT_BARRICADE_HP_PER_DAY) barricadeBase = BANDIT_HIDEOUT_BARRICADE_HP_PER_DAY;
|
||||||
|
if (barricadeBase > BANDIT_HIDEOUT_BARRICADE_HP_MAX) barricadeBase = BANDIT_HIDEOUT_BARRICADE_HP_MAX;
|
||||||
|
hideoutBarricadeMax = barricadeBase;
|
||||||
|
hideoutBarricadeHealth = barricadeBase;
|
||||||
|
|
||||||
|
build_hideout_terrain();
|
||||||
|
clear_hideout_bandits();
|
||||||
|
spawn_hideout_bandits_initial();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup_bandit_hideout_adventure() {
|
||||||
|
clear_hideout_bandits();
|
||||||
|
reset_adventure_combat_state();
|
||||||
|
p.destroy_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_bandit_hideout_adventure() {
|
||||||
|
// Stop main game sounds
|
||||||
|
p.destroy_all();
|
||||||
|
|
||||||
|
init_bandit_hideout_adventure();
|
||||||
|
|
||||||
|
string[] intro;
|
||||||
|
intro.insert_last("=== Bandit's Hideout ===");
|
||||||
|
intro.insert_last("");
|
||||||
|
intro.insert_last("You find a hidden bandit base deep in the forest.");
|
||||||
|
intro.insert_last("The base lies far to the east, guarded by a barricade.");
|
||||||
|
intro.insert_last("");
|
||||||
|
intro.insert_last("Objective:");
|
||||||
|
intro.insert_last(" - Reach the base and destroy the barricade");
|
||||||
|
intro.insert_last("");
|
||||||
|
intro.insert_last("Bandits will, of course, not take this lying down.");
|
||||||
|
text_reader_lines(intro, "Adventure", true);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
wait(5);
|
||||||
|
|
||||||
|
if (key_pressed(KEY_ESCAPE)) {
|
||||||
|
cleanup_bandit_hideout_adventure();
|
||||||
|
speak_with_history("You flee the hideout.", true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard game keys
|
||||||
|
check_quick_slot_keys();
|
||||||
|
check_notification_keys();
|
||||||
|
check_speech_history_keys();
|
||||||
|
|
||||||
|
if (key_pressed(KEY_H)) {
|
||||||
|
speak_with_history(player_health + " health of " + max_health, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_pressed(KEY_X)) {
|
||||||
|
int distanceToBase = hideoutBaseX - hideoutPlayerX;
|
||||||
|
if (distanceToBase < 0) distanceToBase = 0;
|
||||||
|
string terrain = get_hideout_terrain_at(hideoutPlayerX);
|
||||||
|
speak_with_history("x " + hideoutPlayerX + ", terrain " + terrain + ". Base " + distanceToBase + " tiles east. Barricade " + hideoutBarricadeHealth + " of " + hideoutBarricadeMax + ".", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_hideout_player_movement();
|
||||||
|
handle_hideout_player_actions();
|
||||||
|
update_hideout_search();
|
||||||
|
|
||||||
|
update_hideout_bandits();
|
||||||
|
adventure_update_bow_shot(hideoutPlayerX);
|
||||||
|
|
||||||
|
if (hideoutBarricadeHealth <= 0) {
|
||||||
|
cleanup_bandit_hideout_adventure();
|
||||||
|
p.play_stationary("sounds/actions/break_snare.ogg", false);
|
||||||
|
give_bandit_hideout_rewards();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player_health <= 0) {
|
||||||
|
cleanup_bandit_hideout_adventure();
|
||||||
|
speak_with_history("The bandits cut you down.", true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.update_listener_1d(hideoutPlayerX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_hideout_player_movement() {
|
||||||
|
if (key_pressed(KEY_LEFT) && hideoutPlayerFacing != 0) {
|
||||||
|
hideoutPlayerFacing = 0;
|
||||||
|
speak_with_history("west", true);
|
||||||
|
hideoutWalkTimer.restart();
|
||||||
|
}
|
||||||
|
if (key_pressed(KEY_RIGHT) && hideoutPlayerFacing != 1) {
|
||||||
|
hideoutPlayerFacing = 1;
|
||||||
|
speak_with_history("east", true);
|
||||||
|
hideoutWalkTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hideoutWalkTimer.elapsed > walk_speed) {
|
||||||
|
if (key_down(KEY_LEFT) && hideoutPlayerX > 0) {
|
||||||
|
hideoutPlayerFacing = 0;
|
||||||
|
hideoutPlayerX--;
|
||||||
|
hideoutWalkTimer.restart();
|
||||||
|
if (player_health > 0) play_hideout_player_footstep();
|
||||||
|
} else if (key_down(KEY_RIGHT) && hideoutPlayerX < hideoutBaseX) {
|
||||||
|
hideoutPlayerFacing = 1;
|
||||||
|
hideoutPlayerX++;
|
||||||
|
hideoutWalkTimer.restart();
|
||||||
|
if (player_health > 0) play_hideout_player_footstep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_hideout_player_actions() {
|
||||||
|
bool ctrlDown = (key_down(KEY_LCTRL) || key_down(KEY_RCTRL));
|
||||||
|
|
||||||
|
// Bow draw detection
|
||||||
|
if (bow_equipped) {
|
||||||
|
if (ctrlDown && !bow_drawing) {
|
||||||
|
if (get_personal_count(ITEM_ARROWS) > 0) {
|
||||||
|
bow_drawing = true;
|
||||||
|
bow_draw_timer.restart();
|
||||||
|
p.play_stationary("sounds/weapons/bow_draw.ogg", false);
|
||||||
|
} else {
|
||||||
|
speak_ammo_blocked("No arrows.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bow_drawing && !ctrlDown) {
|
||||||
|
adventure_release_bow_attack(hideoutPlayerX, hideoutPlayerFacing, @bandit_hideout_ranged_attack);
|
||||||
|
bow_drawing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!bow_equipped && bow_drawing) {
|
||||||
|
bow_drawing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sling charge detection
|
||||||
|
if (!bow_equipped && sling_equipped && ctrlDown && !sling_charging) {
|
||||||
|
if (get_personal_count(ITEM_STONES) > 0) {
|
||||||
|
sling_charging = true;
|
||||||
|
sling_charge_timer.restart();
|
||||||
|
sling_sound_handle = p.play_stationary("sounds/weapons/sling_swing.ogg", true);
|
||||||
|
last_sling_stage = -1;
|
||||||
|
} else {
|
||||||
|
speak_ammo_blocked("No stones.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sling_charging && ctrlDown) {
|
||||||
|
update_sling_charge();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sling_charging && !ctrlDown) {
|
||||||
|
adventure_release_sling_attack(hideoutPlayerX, hideoutPlayerFacing, @bandit_hideout_ranged_attack);
|
||||||
|
sling_charging = false;
|
||||||
|
safe_destroy_sound(sling_sound_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bow_equipped && !bow_drawing && !sling_equipped && !sling_charging) {
|
||||||
|
if (fishing_pole_equipped) return;
|
||||||
|
int weaponType = get_hideout_melee_weapon_type();
|
||||||
|
if (weaponType == -1) return;
|
||||||
|
|
||||||
|
int attackCooldown = 1000;
|
||||||
|
if (weaponType == ADVENTURE_WEAPON_SPEAR) attackCooldown = 800;
|
||||||
|
if (weaponType == ADVENTURE_WEAPON_AXE) attackCooldown = 1600;
|
||||||
|
|
||||||
|
if (ctrlDown && hideoutAttackTimer.elapsed > attackCooldown) {
|
||||||
|
hideoutAttackTimer.restart();
|
||||||
|
play_hideout_melee_swing(weaponType);
|
||||||
|
if (hideout_melee_hit(weaponType)) {
|
||||||
|
play_hideout_melee_hit(weaponType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_hideout_search() {
|
||||||
|
bool shiftDown = (key_down(KEY_LSHIFT) || key_down(KEY_RSHIFT));
|
||||||
|
if (shiftDown) {
|
||||||
|
if (key_pressed(KEY_COMMA) || key_pressed(KEY_PERIOD)) {
|
||||||
|
hideoutSearching = false;
|
||||||
|
hideoutSearchDelayTimer.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool searchKeyDown = shiftDown || key_down(KEY_SLASH) || key_down(KEY_Z);
|
||||||
|
if (!searchKeyDown && !hideoutSearching) {
|
||||||
|
hideoutSearchTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int searchTime = apply_rune_gather_bonus(2000);
|
||||||
|
if (searchKeyDown && hideoutSearchTimer.elapsed > searchTime && !hideoutSearching) {
|
||||||
|
hideoutSearching = true;
|
||||||
|
hideoutSearchDelayTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hideoutSearching && hideoutSearchDelayTimer.elapsed >= 1000) {
|
||||||
|
hideoutSearching = false;
|
||||||
|
hideoutSearchTimer.restart();
|
||||||
|
perform_hideout_search();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void perform_hideout_search() {
|
||||||
|
if (random(1, 100) <= 10) {
|
||||||
|
speak_with_history("Found nothing.", true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string terrain = get_hideout_terrain_at(hideoutPlayerX);
|
||||||
|
if (terrain == "stone" || terrain == "gravel") {
|
||||||
|
if (try_search_for_terrain(terrain)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
speak_with_history("Found nothing.", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_hideout_melee_weapon_type() {
|
||||||
|
if (spear_equipped) return ADVENTURE_WEAPON_SPEAR;
|
||||||
|
if (axe_equipped) return ADVENTURE_WEAPON_AXE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void play_hideout_melee_swing(int weaponType) {
|
||||||
|
if (weaponType == ADVENTURE_WEAPON_SPEAR) {
|
||||||
|
p.play_stationary("sounds/weapons/spear_swing.ogg", false);
|
||||||
|
} else if (weaponType == ADVENTURE_WEAPON_AXE) {
|
||||||
|
p.play_stationary("sounds/weapons/axe_swing.ogg", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void play_hideout_melee_hit(int weaponType) {
|
||||||
|
if (weaponType == ADVENTURE_WEAPON_SPEAR) {
|
||||||
|
p.play_stationary("sounds/weapons/spear_hit.ogg", false);
|
||||||
|
} else if (weaponType == ADVENTURE_WEAPON_AXE) {
|
||||||
|
p.play_stationary("sounds/weapons/axe_hit.ogg", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hideout_melee_hit(int weaponType) {
|
||||||
|
int range = (weaponType == ADVENTURE_WEAPON_SPEAR) ? 1 : 0;
|
||||||
|
int damage = (weaponType == ADVENTURE_WEAPON_SPEAR) ? SPEAR_DAMAGE : AXE_DAMAGE;
|
||||||
|
|
||||||
|
for (int offset = -range; offset <= range; offset++) {
|
||||||
|
int targetX = hideoutPlayerX + offset;
|
||||||
|
if (damage_hideout_bandit_at(targetX, damage)) {
|
||||||
|
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", hideoutPlayerX, targetX, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abs(hideoutPlayerX - hideoutBaseX) <= range) {
|
||||||
|
if (apply_hideout_barricade_damage(damage)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool apply_hideout_barricade_damage(int damage) {
|
||||||
|
if (damage <= 0) return false;
|
||||||
|
if (hideoutBarricadeHealth <= 0) return false;
|
||||||
|
hideoutBarricadeHealth -= damage;
|
||||||
|
if (hideoutBarricadeHealth < 0) hideoutBarricadeHealth = 0;
|
||||||
|
play_1d_with_volume_step("sounds/weapons/axe_hit.ogg", hideoutPlayerX, hideoutBaseX, false, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_hideout_ranged_target(int playerX, int direction, int range) {
|
||||||
|
for (int dist = 1; dist <= range; dist++) {
|
||||||
|
int checkX = playerX + (dist * direction);
|
||||||
|
if (checkX < 0 || checkX >= BANDIT_HIDEOUT_MAP_SIZE) break;
|
||||||
|
|
||||||
|
if (get_hideout_bandit_at(checkX) != null) {
|
||||||
|
return checkX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hideoutBarricadeHealth > 0 && checkX == hideoutBaseX) {
|
||||||
|
return checkX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bandit_hideout_ranged_attack(int playerX, int direction, int range, int weaponType, int damage) {
|
||||||
|
int targetX = find_hideout_ranged_target(playerX, direction, range);
|
||||||
|
if (targetX == -1) return -1;
|
||||||
|
|
||||||
|
if (targetX == hideoutBaseX) {
|
||||||
|
apply_hideout_barricade_damage(damage);
|
||||||
|
return targetX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (damage_hideout_bandit_at(targetX, damage)) {
|
||||||
|
return targetX;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool damage_hideout_bandit_at(int pos, int damage) {
|
||||||
|
for (uint i = 0; i < hideoutBandits.length(); i++) {
|
||||||
|
if (hideoutBandits[i].position == pos) {
|
||||||
|
hideoutBandits[i].health -= damage;
|
||||||
|
if (hideoutBandits[i].health <= 0) {
|
||||||
|
if (hideoutBandits[i].soundHandle != -1) {
|
||||||
|
p.destroy_sound(hideoutBandits[i].soundHandle);
|
||||||
|
hideoutBandits[i].soundHandle = -1;
|
||||||
|
}
|
||||||
|
if (hideoutBandits[i].inWeaponRange) {
|
||||||
|
play_weapon_range_sound("sounds/enemies/exit_range.ogg", hideoutBandits[i].position);
|
||||||
|
}
|
||||||
|
play_creature_death_sounds("sounds/enemies/enemy_falls.ogg", hideoutBandits[i].alertSound, hideoutPlayerX, pos, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
hideoutBandits.remove_at(i);
|
||||||
|
hideoutBanditsKilled++;
|
||||||
|
respawn_hideout_bandit();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_hideout_bandit_attack_player(HideoutBandit@ bandit) {
|
||||||
|
if (player_health <= 0) return false;
|
||||||
|
if (abs(bandit.position - hideoutPlayerX) > 1) return false;
|
||||||
|
if (bandit.attackTimer.elapsed < BANDIT_ATTACK_INTERVAL) return false;
|
||||||
|
|
||||||
|
bandit.attackTimer.restart();
|
||||||
|
|
||||||
|
if (bandit.weaponType == "spear") {
|
||||||
|
play_creature_attack_sound("sounds/weapons/spear_swing.ogg", hideoutPlayerX, bandit.position, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
} else {
|
||||||
|
play_creature_attack_sound("sounds/weapons/axe_swing.ogg", hideoutPlayerX, bandit.position, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
int damage = random(BANDIT_DAMAGE_MIN, BANDIT_DAMAGE_MAX);
|
||||||
|
player_health -= damage;
|
||||||
|
if (player_health < 0) player_health = 0;
|
||||||
|
|
||||||
|
if (bandit.weaponType == "spear") {
|
||||||
|
play_creature_attack_sound("sounds/weapons/spear_hit.ogg", hideoutPlayerX, bandit.position, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
} else {
|
||||||
|
play_creature_attack_sound("sounds/weapons/axe_hit.ogg", hideoutPlayerX, bandit.position, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
}
|
||||||
|
play_player_damage_sound();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_hideout_bandit_audio(HideoutBandit@ bandit) {
|
||||||
|
if (bandit.soundHandle != -1 && p.sound_is_active(bandit.soundHandle)) {
|
||||||
|
p.update_sound_1d(bandit.soundHandle, bandit.position);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bandit.soundHandle != -1) {
|
||||||
|
p.destroy_sound(bandit.soundHandle);
|
||||||
|
}
|
||||||
|
bandit.soundHandle = play_1d_with_volume_step(bandit.alertSound, hideoutPlayerX, bandit.position, true, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_hideout_bandit(HideoutBandit@ bandit) {
|
||||||
|
update_weapon_range_audio_with_listener(hideoutPlayerX, bandit.position, bandit.inWeaponRange);
|
||||||
|
|
||||||
|
if (try_hideout_bandit_attack_player(bandit)) {
|
||||||
|
update_hideout_bandit_audio(bandit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bandit.moveTimer.elapsed < bandit.moveInterval) {
|
||||||
|
update_hideout_bandit_audio(bandit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bandit.moveTimer.restart();
|
||||||
|
|
||||||
|
int direction = 0;
|
||||||
|
if (hideoutPlayerX > bandit.position) direction = 1;
|
||||||
|
else if (hideoutPlayerX < bandit.position) direction = -1;
|
||||||
|
|
||||||
|
if (direction != 0) {
|
||||||
|
int targetX = bandit.position + direction;
|
||||||
|
if (targetX >= 0 && targetX < BANDIT_HIDEOUT_MAP_SIZE) {
|
||||||
|
if (get_hideout_bandit_at(targetX) == null) {
|
||||||
|
bandit.position = targetX;
|
||||||
|
play_hideout_positional_footstep(hideoutPlayerX, bandit.position, BANDIT_FOOTSTEP_MAX_DISTANCE, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_hideout_bandit_audio(bandit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_hideout_bandits() {
|
||||||
|
for (uint i = 0; i < hideoutBandits.length(); i++) {
|
||||||
|
update_hideout_bandit(hideoutBandits[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_hideout_storage_item(int itemType, int amount) {
|
||||||
|
if (amount <= 0) return 0;
|
||||||
|
int capacity = BASE_STORAGE_MAX - get_storage_count(itemType);
|
||||||
|
if (capacity <= 0) return 0;
|
||||||
|
int addedAmount = amount;
|
||||||
|
if (addedAmount > capacity) addedAmount = capacity;
|
||||||
|
if (addedAmount <= 0) return 0;
|
||||||
|
|
||||||
|
add_storage_count(itemType, addedAmount);
|
||||||
|
|
||||||
|
if (itemType == ITEM_SMALL_GAME) {
|
||||||
|
for (int i = 0; i < addedAmount; i++) {
|
||||||
|
storage_small_game_types.insert_last("small game");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (itemType == ITEM_FISH) {
|
||||||
|
for (int i = 0; i < addedAmount; i++) {
|
||||||
|
add_storage_fish_weight(get_default_fish_weight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return addedAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void give_bandit_hideout_rewards() {
|
||||||
|
speak_with_history("Victory!", true);
|
||||||
|
|
||||||
|
string[] rewards;
|
||||||
|
rewards.insert_last("=== Victory Rewards ===");
|
||||||
|
rewards.insert_last("");
|
||||||
|
rewards.insert_last("Bandits defeated: " + hideoutBanditsKilled + ".");
|
||||||
|
|
||||||
|
if (world_altars.length() > 0) {
|
||||||
|
double favorReward = BANDIT_HIDEOUT_BASE_FAVOR + (hideoutBanditsKilled * BANDIT_HIDEOUT_FAVOR_PER_KILL);
|
||||||
|
if (favorReward > BANDIT_HIDEOUT_FAVOR_MAX) favorReward = BANDIT_HIDEOUT_FAVOR_MAX;
|
||||||
|
if (favorReward < BANDIT_HIDEOUT_BASE_FAVOR) favorReward = BANDIT_HIDEOUT_BASE_FAVOR;
|
||||||
|
favor += favorReward;
|
||||||
|
rewards.insert_last("Favor awarded: " + format_favor(favorReward) + ".");
|
||||||
|
} else {
|
||||||
|
rewards.insert_last("Altar not built. No favor awarded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
rewards.insert_last("");
|
||||||
|
|
||||||
|
if (world_storages.length() == 0) {
|
||||||
|
rewards.insert_last("Storage not built. No item rewards.");
|
||||||
|
} else {
|
||||||
|
rewards.insert_last("Storage rewards:");
|
||||||
|
bool anyItems = false;
|
||||||
|
for (int itemType = 0; itemType < ITEM_COUNT; itemType++) {
|
||||||
|
int roll = random(0, 9);
|
||||||
|
if (roll <= 0) continue;
|
||||||
|
int addedAmount = add_hideout_storage_item(itemType, roll);
|
||||||
|
if (addedAmount <= 0) continue;
|
||||||
|
rewards.insert_last(get_item_display_name(itemType) + ": +" + addedAmount + ".");
|
||||||
|
anyItems = true;
|
||||||
|
}
|
||||||
|
if (!anyItems) {
|
||||||
|
rewards.insert_last("No items were recovered.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text_reader_lines(rewards, "Bandit's Hideout", true);
|
||||||
|
}
|
||||||
@@ -63,6 +63,25 @@ const int ZOMBIE_FOOTSTEP_MAX_DISTANCE = 5;
|
|||||||
const float ZOMBIE_SOUND_VOLUME_STEP = 3.0;
|
const float ZOMBIE_SOUND_VOLUME_STEP = 3.0;
|
||||||
const int ZOMBIE_ATTACK_MAX_HEIGHT = 6;
|
const int ZOMBIE_ATTACK_MAX_HEIGHT = 6;
|
||||||
|
|
||||||
|
// Undead resident settings (stronger than zombies)
|
||||||
|
const int UNDEAD_RESIDENT_HEALTH = 16;
|
||||||
|
const int UNDEAD_RESIDENT_DAMAGE_MIN = 5;
|
||||||
|
const int UNDEAD_RESIDENT_DAMAGE_MAX = 7;
|
||||||
|
|
||||||
|
// Wight settings (undead elite)
|
||||||
|
const int WIGHT_HEALTH = 40;
|
||||||
|
const int WIGHT_DAMAGE_MIN = 6;
|
||||||
|
const int WIGHT_DAMAGE_MAX = 8;
|
||||||
|
const int WIGHT_SPAWN_CHANCE_START = 5;
|
||||||
|
const int WIGHT_SPAWN_CHANCE_STEP = 5;
|
||||||
|
|
||||||
|
// Vampyr settings (undead abductor)
|
||||||
|
const int VAMPYR_HEALTH = 40;
|
||||||
|
const int VAMPYR_SPAWN_CHANCE_START = 5;
|
||||||
|
const int VAMPYR_SPAWN_CHANCE_STEP = 5;
|
||||||
|
const int VAMPYR_CAPTURE_CHANCE = 50;
|
||||||
|
const int VAMPYR_CAPTURE_INTERVAL = 1000;
|
||||||
|
|
||||||
// Boar settings
|
// Boar settings
|
||||||
const int BOAR_HEALTH = 4;
|
const int BOAR_HEALTH = 4;
|
||||||
const int BOAR_MAX_COUNT = 1;
|
const int BOAR_MAX_COUNT = 1;
|
||||||
|
|||||||
@@ -1,32 +1,113 @@
|
|||||||
// Undead creatures (zombies, vampires, ghosts, etc.)
|
// Undead creatures (zombies, wights, vampyrs, ghosts, etc.)
|
||||||
// Currently only zombies are implemented
|
// Currently zombies, wights, vampyrs, and undead residents are implemented
|
||||||
|
|
||||||
string[] undead_zombie_sounds = {"sounds/enemies/zombie1.ogg"};
|
string[] undead_zombie_sounds = {"sounds/enemies/zombie1.ogg"};
|
||||||
|
string[] undead_wight_sounds = {"sounds/enemies/wight1.ogg"};
|
||||||
|
string[] undead_vampyr_sounds = {
|
||||||
|
"sounds/enemies/vampyr1.ogg",
|
||||||
|
"sounds/enemies/vampyr2.ogg",
|
||||||
|
"sounds/enemies/vampyr3.ogg",
|
||||||
|
"sounds/enemies/vampyr4.ogg"
|
||||||
|
};
|
||||||
|
string[] undead_resident_sounds = {"sounds/enemies/undead_resident1.ogg"};
|
||||||
|
|
||||||
|
int wight_spawn_chance = WIGHT_SPAWN_CHANCE_START;
|
||||||
|
bool wight_spawned_this_night = false;
|
||||||
|
int vampyr_spawn_chance = VAMPYR_SPAWN_CHANCE_START;
|
||||||
|
bool vampyr_spawned_this_night = false;
|
||||||
|
|
||||||
|
int get_undead_base_health(const string &in undead_type) {
|
||||||
|
if (undead_type == "wight") return WIGHT_HEALTH;
|
||||||
|
if (undead_type == "vampyr") return VAMPYR_HEALTH;
|
||||||
|
if (undead_type == "undead_resident") return UNDEAD_RESIDENT_HEALTH;
|
||||||
|
return ZOMBIE_HEALTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_undead_damage_min(const string &in undead_type) {
|
||||||
|
if (undead_type == "wight") return WIGHT_DAMAGE_MIN;
|
||||||
|
if (undead_type == "vampyr") return WIGHT_DAMAGE_MIN;
|
||||||
|
if (undead_type == "undead_resident") return UNDEAD_RESIDENT_DAMAGE_MIN;
|
||||||
|
return ZOMBIE_DAMAGE_MIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_undead_damage_max(const string &in undead_type) {
|
||||||
|
if (undead_type == "wight") return WIGHT_DAMAGE_MAX;
|
||||||
|
if (undead_type == "vampyr") return WIGHT_DAMAGE_MAX;
|
||||||
|
if (undead_type == "undead_resident") return UNDEAD_RESIDENT_DAMAGE_MAX;
|
||||||
|
return ZOMBIE_DAMAGE_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
string pick_undead_voice_sound(const string &in undead_type) {
|
||||||
|
if (undead_type == "wight") {
|
||||||
|
int sound_index = random(0, undead_wight_sounds.length() - 1);
|
||||||
|
return undead_wight_sounds[sound_index];
|
||||||
|
}
|
||||||
|
if (undead_type == "vampyr") {
|
||||||
|
int sound_index = random(0, undead_vampyr_sounds.length() - 1);
|
||||||
|
return undead_vampyr_sounds[sound_index];
|
||||||
|
}
|
||||||
|
if (undead_type == "undead_resident") {
|
||||||
|
int sound_index = random(0, undead_resident_sounds.length() - 1);
|
||||||
|
return undead_resident_sounds[sound_index];
|
||||||
|
}
|
||||||
|
int sound_index = random(0, undead_zombie_sounds.length() - 1);
|
||||||
|
return undead_zombie_sounds[sound_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_undead_label(const string &in undead_type) {
|
||||||
|
if (undead_type == "wight") return "wight";
|
||||||
|
if (undead_type == "vampyr") return "vampyr";
|
||||||
|
if (undead_type == "undead_resident") return "undead resident";
|
||||||
|
return "zombie";
|
||||||
|
}
|
||||||
|
|
||||||
class Undead {
|
class Undead {
|
||||||
int position;
|
int position;
|
||||||
int health;
|
int health;
|
||||||
string undead_type; // "zombie", future: "vampire", "ghost", etc.
|
string undead_type; // "zombie", "wight", "vampyr", "undead_resident", future: "ghost", etc.
|
||||||
string voice_sound;
|
string voice_sound;
|
||||||
int sound_handle;
|
int sound_handle;
|
||||||
bool in_weapon_range;
|
bool in_weapon_range;
|
||||||
|
bool retreating;
|
||||||
|
bool suppress_voice;
|
||||||
|
bool should_despawn;
|
||||||
timer move_timer;
|
timer move_timer;
|
||||||
timer attack_timer;
|
timer attack_timer;
|
||||||
|
|
||||||
Undead(int pos, string type = "zombie") {
|
Undead(int pos, string type = "zombie") {
|
||||||
position = pos;
|
position = pos;
|
||||||
undead_type = type;
|
undead_type = type;
|
||||||
health = ZOMBIE_HEALTH;
|
health = get_undead_base_health(undead_type);
|
||||||
int sound_index = random(0, undead_zombie_sounds.length() - 1);
|
voice_sound = pick_undead_voice_sound(undead_type);
|
||||||
voice_sound = undead_zombie_sounds[sound_index];
|
|
||||||
sound_handle = -1;
|
sound_handle = -1;
|
||||||
in_weapon_range = false;
|
in_weapon_range = false;
|
||||||
|
retreating = false;
|
||||||
|
suppress_voice = false;
|
||||||
|
should_despawn = false;
|
||||||
move_timer.restart();
|
move_timer.restart();
|
||||||
attack_timer.restart();
|
attack_timer.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Undead@[] undeads;
|
Undead@[] undeads;
|
||||||
|
|
||||||
|
bool has_wight() {
|
||||||
|
for (uint i = 0; i < undeads.length(); i++) {
|
||||||
|
if (undeads[i].undead_type == "wight") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_vampyr() {
|
||||||
|
for (uint i = 0; i < undeads.length(); i++) {
|
||||||
|
if (undeads[i].undead_type == "vampyr") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void update_undead_weapon_range_audio() {
|
void update_undead_weapon_range_audio() {
|
||||||
for (uint i = 0; i < undeads.length(); i++) {
|
for (uint i = 0; i < undeads.length(); i++) {
|
||||||
update_weapon_range_audio(undeads[i].position, undeads[i].in_weapon_range);
|
update_weapon_range_audio(undeads[i].position, undeads[i].in_weapon_range);
|
||||||
@@ -61,7 +142,7 @@ Undead@ get_undead_at(int pos) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void spawn_undead() {
|
void spawn_undead(const string &in undead_type = "zombie") {
|
||||||
int spawn_x = -1;
|
int spawn_x = -1;
|
||||||
for (int attempts = 0; attempts < 20; attempts++) {
|
for (int attempts = 0; attempts < 20; attempts++) {
|
||||||
int candidate = random(BASE_END + 1, MAP_SIZE - 1);
|
int candidate = random(BASE_END + 1, MAP_SIZE - 1);
|
||||||
@@ -74,14 +155,18 @@ void spawn_undead() {
|
|||||||
spawn_x = random(BASE_END + 1, MAP_SIZE - 1);
|
spawn_x = random(BASE_END + 1, MAP_SIZE - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Undead@ z = Undead(spawn_x, "zombie");
|
Undead@ undead = Undead(spawn_x, undead_type);
|
||||||
undeads.insert_last(z);
|
undeads.insert_last(undead);
|
||||||
// Play looping sound that follows the zombie
|
// Play looping sound that follows the undead
|
||||||
int[] areaStarts;
|
int[] areaStarts;
|
||||||
int[] areaEnds;
|
int[] areaEnds;
|
||||||
get_active_audio_areas(areaStarts, areaEnds);
|
get_active_audio_areas(areaStarts, areaEnds);
|
||||||
if (areaStarts.length() == 0 || range_overlaps_active_areas(spawn_x, spawn_x, areaStarts, areaEnds)) {
|
if (areaStarts.length() == 0 || range_overlaps_active_areas(spawn_x, spawn_x, areaStarts, areaEnds)) {
|
||||||
z.sound_handle = play_1d_with_volume_step(z.voice_sound, x, spawn_x, true, ZOMBIE_SOUND_VOLUME_STEP);
|
bool loop_voice = (undead_type != "vampyr");
|
||||||
|
if (undead_type == "vampyr") {
|
||||||
|
undead.voice_sound = pick_undead_voice_sound(undead_type);
|
||||||
|
}
|
||||||
|
undead.sound_handle = play_1d_with_volume_step(undead.voice_sound, x, spawn_x, loop_voice, ZOMBIE_SOUND_VOLUME_STEP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +175,7 @@ void try_attack_barricade_undead(Undead@ undead) {
|
|||||||
if (undead.attack_timer.elapsed < ZOMBIE_ATTACK_INTERVAL) return;
|
if (undead.attack_timer.elapsed < ZOMBIE_ATTACK_INTERVAL) return;
|
||||||
|
|
||||||
undead.attack_timer.restart();
|
undead.attack_timer.restart();
|
||||||
int damage = random(ZOMBIE_DAMAGE_MIN, ZOMBIE_DAMAGE_MAX);
|
int damage = random(get_undead_damage_min(undead.undead_type), get_undead_damage_max(undead.undead_type));
|
||||||
barricade_health -= damage;
|
barricade_health -= damage;
|
||||||
if (barricade_health < 0) barricade_health = 0;
|
if (barricade_health < 0) barricade_health = 0;
|
||||||
|
|
||||||
@@ -103,7 +188,7 @@ void try_attack_barricade_undead(Undead@ undead) {
|
|||||||
int before_health = undead.health;
|
int before_health = undead.health;
|
||||||
damage_undead_at(undead.position, counterDamage);
|
damage_undead_at(undead.position, counterDamage);
|
||||||
if (before_health - counterDamage <= 0 && x <= BASE_END) {
|
if (before_health - counterDamage <= 0 && x <= BASE_END) {
|
||||||
speak_with_history("Residents killed an attacking zombie.", true);
|
speak_with_history("Residents killed an attacking " + get_undead_label(undead.undead_type) + ".", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,6 +203,10 @@ bool can_undead_attack_player(Undead@ undead) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (undead.undead_type == "vampyr") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (barricade_health > 0 && x <= BASE_END) {
|
if (barricade_health > 0 && x <= BASE_END) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -142,7 +231,7 @@ bool try_attack_player_undead(Undead@ undead) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
undead.attack_timer.restart();
|
undead.attack_timer.restart();
|
||||||
int damage = random(ZOMBIE_DAMAGE_MIN, ZOMBIE_DAMAGE_MAX);
|
int damage = random(get_undead_damage_min(undead.undead_type), get_undead_damage_max(undead.undead_type));
|
||||||
player_health -= damage;
|
player_health -= damage;
|
||||||
if (player_health < 0) {
|
if (player_health < 0) {
|
||||||
player_health = 0;
|
player_health = 0;
|
||||||
@@ -152,9 +241,42 @@ bool try_attack_player_undead(Undead@ undead) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void start_vampyr_retreat(Undead@ undead) {
|
||||||
|
undead.retreating = true;
|
||||||
|
undead.suppress_voice = true;
|
||||||
|
if (undead.sound_handle != -1) {
|
||||||
|
p.destroy_sound(undead.sound_handle);
|
||||||
|
undead.sound_handle = -1;
|
||||||
|
}
|
||||||
|
undead.move_timer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void try_capture_resident_vampyr(Undead@ undead) {
|
||||||
|
if (undead.attack_timer.elapsed < VAMPYR_CAPTURE_INTERVAL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
undead.attack_timer.restart();
|
||||||
|
|
||||||
|
if (residents_count <= 0) {
|
||||||
|
start_vampyr_retreat(undead);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (random(1, 100) <= VAMPYR_CAPTURE_CHANCE) {
|
||||||
|
residents_count--;
|
||||||
|
if (residents_count < 0) residents_count = 0;
|
||||||
|
undead_residents_pending++;
|
||||||
|
speak_with_history("A resident has been taken.", true);
|
||||||
|
start_vampyr_retreat(undead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void update_undead(Undead@ undead, bool audio_active) {
|
void update_undead(Undead@ undead, bool audio_active) {
|
||||||
|
bool is_vampyr = (undead.undead_type == "vampyr");
|
||||||
|
bool loop_voice = !is_vampyr;
|
||||||
|
|
||||||
// Update looping sound position
|
// Update looping sound position
|
||||||
if (!audio_active) {
|
if (!audio_active || undead.suppress_voice) {
|
||||||
if (undead.sound_handle != -1) {
|
if (undead.sound_handle != -1) {
|
||||||
p.destroy_sound(undead.sound_handle);
|
p.destroy_sound(undead.sound_handle);
|
||||||
undead.sound_handle = -1;
|
undead.sound_handle = -1;
|
||||||
@@ -166,23 +288,49 @@ void update_undead(Undead@ undead, bool audio_active) {
|
|||||||
if (undead.sound_handle != -1) {
|
if (undead.sound_handle != -1) {
|
||||||
p.destroy_sound(undead.sound_handle);
|
p.destroy_sound(undead.sound_handle);
|
||||||
}
|
}
|
||||||
undead.sound_handle = play_1d_with_volume_step(undead.voice_sound, x, undead.position, true, ZOMBIE_SOUND_VOLUME_STEP);
|
if (is_vampyr) {
|
||||||
|
undead.voice_sound = pick_undead_voice_sound(undead.undead_type);
|
||||||
|
}
|
||||||
|
undead.sound_handle = play_1d_with_volume_step(undead.voice_sound, x, undead.position, loop_voice, ZOMBIE_SOUND_VOLUME_STEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try_attack_player_undead(undead)) {
|
if (try_attack_player_undead(undead)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (undead.undead_type == "vampyr" && !undead.retreating && barricade_health > 0 && undead.position == BASE_END + 1) {
|
||||||
|
try_capture_resident_vampyr(undead);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (undead.move_timer.elapsed < ZOMBIE_MOVE_INTERVAL) return;
|
if (undead.move_timer.elapsed < ZOMBIE_MOVE_INTERVAL) return;
|
||||||
undead.move_timer.restart();
|
undead.move_timer.restart();
|
||||||
|
|
||||||
if (barricade_health > 0 && undead.position == BASE_END + 1) {
|
if (undead.undead_type != "vampyr" && barricade_health > 0 && undead.position == BASE_END + 1) {
|
||||||
try_attack_barricade_undead(undead);
|
try_attack_barricade_undead(undead);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int direction = 0;
|
int direction = 0;
|
||||||
if (x > BASE_END) {
|
if (undead.undead_type == "vampyr") {
|
||||||
|
if (undead.retreating) {
|
||||||
|
if (undead.position >= MAP_SIZE - 1) {
|
||||||
|
undead.should_despawn = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
direction = 1;
|
||||||
|
} else if (undead.position > 0) {
|
||||||
|
direction = -1;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (undead.undead_type == "wight") {
|
||||||
|
if (undead.position > 0) {
|
||||||
|
direction = -1;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (x > BASE_END) {
|
||||||
if (x > undead.position) {
|
if (x > undead.position) {
|
||||||
direction = 1;
|
direction = 1;
|
||||||
} else if (x < undead.position) {
|
} else if (x < undead.position) {
|
||||||
@@ -198,7 +346,7 @@ void update_undead(Undead@ undead, bool audio_active) {
|
|||||||
int target_x = undead.position + direction;
|
int target_x = undead.position + direction;
|
||||||
if (target_x < 0 || target_x >= MAP_SIZE) return;
|
if (target_x < 0 || target_x >= MAP_SIZE) return;
|
||||||
|
|
||||||
if (target_x <= BASE_END && barricade_health > 0) {
|
if (undead.undead_type != "vampyr" && target_x <= BASE_END && barricade_health > 0) {
|
||||||
try_attack_barricade_undead(undead);
|
try_attack_barricade_undead(undead);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -223,8 +371,24 @@ void update_undeads() {
|
|||||||
int maxCount = ZOMBIE_MAX_COUNT + extra;
|
int maxCount = ZOMBIE_MAX_COUNT + extra;
|
||||||
if (maxCount > ZOMBIE_MAX_COUNT_CAP) maxCount = ZOMBIE_MAX_COUNT_CAP;
|
if (maxCount > ZOMBIE_MAX_COUNT_CAP) maxCount = ZOMBIE_MAX_COUNT_CAP;
|
||||||
|
|
||||||
while (undeads.length() < maxCount) {
|
int zombie_count = 0;
|
||||||
spawn_undead();
|
int undead_resident_count = 0;
|
||||||
|
for (uint i = 0; i < undeads.length(); i++) {
|
||||||
|
if (undeads[i].undead_type == "zombie") {
|
||||||
|
zombie_count++;
|
||||||
|
} else if (undeads[i].undead_type == "undead_resident") {
|
||||||
|
undead_resident_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (zombie_count < maxCount) {
|
||||||
|
spawn_undead("zombie");
|
||||||
|
zombie_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (undead_resident_count < undead_residents_count) {
|
||||||
|
spawn_undead("undead_resident");
|
||||||
|
undead_resident_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] areaStarts;
|
int[] areaStarts;
|
||||||
@@ -236,6 +400,88 @@ void update_undeads() {
|
|||||||
bool audio_active = !limit_audio || range_overlaps_active_areas(undeads[i].position, undeads[i].position, areaStarts, areaEnds);
|
bool audio_active = !limit_audio || range_overlaps_active_areas(undeads[i].position, undeads[i].position, areaStarts, areaEnds);
|
||||||
update_undead(undeads[i], audio_active);
|
update_undead(undeads[i], audio_active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = int(undeads.length()) - 1; i >= 0; i--) {
|
||||||
|
if (undeads[i].should_despawn) {
|
||||||
|
if (undeads[i].sound_handle != -1) {
|
||||||
|
p.destroy_sound(undeads[i].sound_handle);
|
||||||
|
undeads[i].sound_handle = -1;
|
||||||
|
}
|
||||||
|
undeads.remove_at(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void attempt_hourly_wight_spawn() {
|
||||||
|
if (current_hour == 19) {
|
||||||
|
wight_spawned_this_night = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_daytime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_vampyr()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wight_spawned_this_night) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_wight()) {
|
||||||
|
wight_spawned_this_night = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int roll = random(1, 100);
|
||||||
|
if (roll <= wight_spawn_chance) {
|
||||||
|
spawn_undead("wight");
|
||||||
|
wight_spawned_this_night = true;
|
||||||
|
wight_spawn_chance = WIGHT_SPAWN_CHANCE_START;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wight_spawn_chance += WIGHT_SPAWN_CHANCE_STEP;
|
||||||
|
if (wight_spawn_chance > 100) wight_spawn_chance = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
void attempt_hourly_vampyr_spawn() {
|
||||||
|
if (current_hour == 19) {
|
||||||
|
vampyr_spawned_this_night = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_daytime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (residents_count <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_wight()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vampyr_spawned_this_night) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_vampyr()) {
|
||||||
|
vampyr_spawned_this_night = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int roll = random(1, 100);
|
||||||
|
if (roll <= vampyr_spawn_chance) {
|
||||||
|
spawn_undead("vampyr");
|
||||||
|
vampyr_spawned_this_night = true;
|
||||||
|
vampyr_spawn_chance = VAMPYR_SPAWN_CHANCE_START;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vampyr_spawn_chance += VAMPYR_SPAWN_CHANCE_STEP;
|
||||||
|
if (vampyr_spawn_chance > 100) vampyr_spawn_chance = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool damage_undead_at(int pos, int damage) {
|
bool damage_undead_at(int pos, int damage) {
|
||||||
@@ -246,6 +492,10 @@ bool damage_undead_at(int pos, int damage) {
|
|||||||
if (world_altars.length() > 0) {
|
if (world_altars.length() > 0) {
|
||||||
favor += 0.2;
|
favor += 0.2;
|
||||||
}
|
}
|
||||||
|
if (undeads[i].undead_type == "undead_resident") {
|
||||||
|
undead_residents_count--;
|
||||||
|
if (undead_residents_count < 0) undead_residents_count = 0;
|
||||||
|
}
|
||||||
if (undeads[i].sound_handle != -1) {
|
if (undeads[i].sound_handle != -1) {
|
||||||
p.destroy_sound(undeads[i].sound_handle);
|
p.destroy_sound(undeads[i].sound_handle);
|
||||||
undeads[i].sound_handle = -1;
|
undeads[i].sound_handle = -1;
|
||||||
@@ -265,4 +515,4 @@ Undead@ get_zombie_at(int pos) { return get_undead_at(pos); }
|
|||||||
bool damage_zombie_at(int pos, int damage) { return damage_undead_at(pos, damage); }
|
bool damage_zombie_at(int pos, int damage) { return damage_undead_at(pos, damage); }
|
||||||
void update_zombies() { update_undeads(); }
|
void update_zombies() { update_undeads(); }
|
||||||
void clear_zombies() { clear_undeads(); }
|
void clear_zombies() { clear_undeads(); }
|
||||||
void spawn_zombie() { spawn_undead(); }
|
void spawn_zombie() { spawn_undead("zombie"); }
|
||||||
|
|||||||
@@ -606,6 +606,8 @@ void reset_game_state() {
|
|||||||
barricade_health = 0;
|
barricade_health = 0;
|
||||||
barricade_initialized = false;
|
barricade_initialized = false;
|
||||||
residents_count = 0;
|
residents_count = 0;
|
||||||
|
undead_residents_count = 0;
|
||||||
|
undead_residents_pending = 0;
|
||||||
|
|
||||||
current_hour = 8;
|
current_hour = 8;
|
||||||
current_day = 1;
|
current_day = 1;
|
||||||
@@ -868,6 +870,10 @@ bool save_game_state() {
|
|||||||
saveData.set("time_invasion_scheduled_hour", invasion_scheduled_hour);
|
saveData.set("time_invasion_scheduled_hour", invasion_scheduled_hour);
|
||||||
saveData.set("time_invasion_enemy_type", invasion_enemy_type);
|
saveData.set("time_invasion_enemy_type", invasion_enemy_type);
|
||||||
saveData.set("quest_roll_done_today", quest_roll_done_today);
|
saveData.set("quest_roll_done_today", quest_roll_done_today);
|
||||||
|
saveData.set("wight_spawn_chance", wight_spawn_chance);
|
||||||
|
saveData.set("wight_spawned_this_night", wight_spawned_this_night);
|
||||||
|
saveData.set("vampyr_spawn_chance", vampyr_spawn_chance);
|
||||||
|
saveData.set("vampyr_spawned_this_night", vampyr_spawned_this_night);
|
||||||
string[] questData;
|
string[] questData;
|
||||||
for (uint i = 0; i < quest_queue.length(); i++) {
|
for (uint i = 0; i < quest_queue.length(); i++) {
|
||||||
questData.insert_last("" + quest_queue[i]);
|
questData.insert_last("" + quest_queue[i]);
|
||||||
@@ -882,6 +888,8 @@ bool save_game_state() {
|
|||||||
saveData.set("world_barricade_initialized", barricade_initialized);
|
saveData.set("world_barricade_initialized", barricade_initialized);
|
||||||
saveData.set("world_barricade_health", barricade_health);
|
saveData.set("world_barricade_health", barricade_health);
|
||||||
saveData.set("world_residents_count", residents_count);
|
saveData.set("world_residents_count", residents_count);
|
||||||
|
saveData.set("world_undead_residents_count", undead_residents_count);
|
||||||
|
saveData.set("world_undead_residents_pending", undead_residents_pending);
|
||||||
saveData.set("world_expanded_terrain_types", join_string_array(expanded_terrain_types));
|
saveData.set("world_expanded_terrain_types", join_string_array(expanded_terrain_types));
|
||||||
|
|
||||||
string[] treeData;
|
string[] treeData;
|
||||||
@@ -1019,6 +1027,10 @@ bool load_game_state_from_file(const string&in filename) {
|
|||||||
barricade_health = int(get_number(saveData, "world_barricade_health", BARRICADE_BASE_HEALTH));
|
barricade_health = int(get_number(saveData, "world_barricade_health", BARRICADE_BASE_HEALTH));
|
||||||
residents_count = int(get_number(saveData, "world_residents_count", 0));
|
residents_count = int(get_number(saveData, "world_residents_count", 0));
|
||||||
if (residents_count < 0) residents_count = 0;
|
if (residents_count < 0) residents_count = 0;
|
||||||
|
undead_residents_count = int(get_number(saveData, "world_undead_residents_count", 0));
|
||||||
|
if (undead_residents_count < 0) undead_residents_count = 0;
|
||||||
|
undead_residents_pending = int(get_number(saveData, "world_undead_residents_pending", 0));
|
||||||
|
if (undead_residents_pending < 0) undead_residents_pending = 0;
|
||||||
if (!barricade_initialized) {
|
if (!barricade_initialized) {
|
||||||
init_barricade();
|
init_barricade();
|
||||||
} else {
|
} else {
|
||||||
@@ -1276,6 +1288,14 @@ bool load_game_state_from_file(const string&in filename) {
|
|||||||
if (invasion_scheduled_hour < -1) invasion_scheduled_hour = -1;
|
if (invasion_scheduled_hour < -1) invasion_scheduled_hour = -1;
|
||||||
if (invasion_scheduled_hour > 23) invasion_scheduled_hour = -1;
|
if (invasion_scheduled_hour > 23) invasion_scheduled_hour = -1;
|
||||||
quest_roll_done_today = get_bool(saveData, "quest_roll_done_today", false);
|
quest_roll_done_today = get_bool(saveData, "quest_roll_done_today", false);
|
||||||
|
wight_spawn_chance = int(get_number(saveData, "wight_spawn_chance", WIGHT_SPAWN_CHANCE_START));
|
||||||
|
wight_spawned_this_night = get_bool(saveData, "wight_spawned_this_night", false);
|
||||||
|
if (wight_spawn_chance < WIGHT_SPAWN_CHANCE_START) wight_spawn_chance = WIGHT_SPAWN_CHANCE_START;
|
||||||
|
if (wight_spawn_chance > 100) wight_spawn_chance = 100;
|
||||||
|
vampyr_spawn_chance = int(get_number(saveData, "vampyr_spawn_chance", VAMPYR_SPAWN_CHANCE_START));
|
||||||
|
vampyr_spawned_this_night = get_bool(saveData, "vampyr_spawned_this_night", false);
|
||||||
|
if (vampyr_spawn_chance < VAMPYR_SPAWN_CHANCE_START) vampyr_spawn_chance = VAMPYR_SPAWN_CHANCE_START;
|
||||||
|
if (vampyr_spawn_chance > 100) vampyr_spawn_chance = 100;
|
||||||
|
|
||||||
quest_queue.resize(0);
|
quest_queue.resize(0);
|
||||||
string[] loadedQuests = get_string_list_or_split(saveData, "quest_queue");
|
string[] loadedQuests = get_string_list_or_split(saveData, "quest_queue");
|
||||||
|
|||||||
@@ -517,6 +517,10 @@ void update_time() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (current_hour == 6) {
|
if (current_hour == 6) {
|
||||||
|
if (undead_residents_pending > 0) {
|
||||||
|
undead_residents_count += undead_residents_pending;
|
||||||
|
undead_residents_pending = 0;
|
||||||
|
}
|
||||||
process_daily_weapon_breakage();
|
process_daily_weapon_breakage();
|
||||||
attempt_daily_quest();
|
attempt_daily_quest();
|
||||||
attempt_resident_butchering();
|
attempt_resident_butchering();
|
||||||
@@ -529,6 +533,8 @@ void update_time() {
|
|||||||
update_incense_burning();
|
update_incense_burning();
|
||||||
attempt_hourly_flying_creature_spawn();
|
attempt_hourly_flying_creature_spawn();
|
||||||
attempt_hourly_boar_spawn();
|
attempt_hourly_boar_spawn();
|
||||||
|
attempt_hourly_wight_spawn();
|
||||||
|
attempt_hourly_vampyr_spawn();
|
||||||
check_scheduled_invasion();
|
check_scheduled_invasion();
|
||||||
attempt_blessing();
|
attempt_blessing();
|
||||||
check_weather_transition();
|
check_weather_transition();
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
int barricade_health = 0;
|
int barricade_health = 0;
|
||||||
bool barricade_initialized = false;
|
bool barricade_initialized = false;
|
||||||
int residents_count = 0;
|
int residents_count = 0;
|
||||||
|
int undead_residents_count = 0;
|
||||||
|
int undead_residents_pending = 0;
|
||||||
|
|
||||||
void init_barricade() {
|
void init_barricade() {
|
||||||
if (barricade_initialized) {
|
if (barricade_initialized) {
|
||||||
|
|||||||
Reference in New Issue
Block a user