Initial attempt at boars added. Needs work, lots of work.
This commit is contained in:
BIN
sounds/game/boar.ogg
LFS
Normal file
BIN
sounds/game/boar.ogg
LFS
Normal file
Binary file not shown.
@@ -91,7 +91,6 @@ int perform_resident_defense() {
|
|||||||
|
|
||||||
// Proactive resident sling defense
|
// Proactive resident sling defense
|
||||||
timer resident_sling_timer;
|
timer resident_sling_timer;
|
||||||
const int RESIDENT_SLING_COOLDOWN = 4000; // 4 seconds between shots
|
|
||||||
|
|
||||||
void attempt_resident_sling_defense() {
|
void attempt_resident_sling_defense() {
|
||||||
// Only if residents exist and have slings with stones
|
// Only if residents exist and have slings with stones
|
||||||
@@ -213,7 +212,6 @@ void process_daily_weapon_breakage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Resident resource collection
|
// Resident resource collection
|
||||||
const int RESIDENT_COLLECTION_CHANCE = 10; // 10% chance per basket per hour
|
|
||||||
|
|
||||||
void attempt_resident_collection() {
|
void attempt_resident_collection() {
|
||||||
// Only during daytime
|
// Only during daytime
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ int attack_enemy_ranged(int start_x, int end_x, int damage) {
|
|||||||
if (damage_zombie_at(check_x, damage)) {
|
if (damage_zombie_at(check_x, damage)) {
|
||||||
return check_x;
|
return check_x;
|
||||||
}
|
}
|
||||||
|
// Then check boars
|
||||||
|
if (damage_boar_at(check_x, damage)) {
|
||||||
|
return check_x;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -30,6 +34,10 @@ bool attack_enemy(int target_x, int damage) {
|
|||||||
if (damage_bandit_at(target_x, damage)) {
|
if (damage_bandit_at(target_x, damage)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
// Check boars
|
||||||
|
if (damage_boar_at(target_x, damage)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// Then check zombies
|
// Then check zombies
|
||||||
return damage_zombie_at(target_x, damage);
|
return damage_zombie_at(target_x, damage);
|
||||||
}
|
}
|
||||||
@@ -43,6 +51,8 @@ void perform_spear_attack(int current_x) {
|
|||||||
// Play hit sound based on enemy type (both use same hit sound for now)
|
// Play hit sound based on enemy type (both use same hit sound for now)
|
||||||
if (get_bandit_at(hit_pos) != null) {
|
if (get_bandit_at(hit_pos) != null) {
|
||||||
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, BANDIT_SOUND_VOLUME_STEP);
|
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
} else if (get_boar_at(hit_pos) != null) {
|
||||||
|
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, BOAR_SOUND_VOLUME_STEP);
|
||||||
} else {
|
} else {
|
||||||
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, ZOMBIE_SOUND_VOLUME_STEP);
|
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, ZOMBIE_SOUND_VOLUME_STEP);
|
||||||
}
|
}
|
||||||
@@ -61,6 +71,8 @@ void perform_axe_attack(int current_x) {
|
|||||||
// Play hit sound based on enemy type
|
// Play hit sound based on enemy type
|
||||||
if (get_bandit_at(current_x) != null) {
|
if (get_bandit_at(current_x) != null) {
|
||||||
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, current_x, BANDIT_SOUND_VOLUME_STEP);
|
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, current_x, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
} else if (get_boar_at(current_x) != null) {
|
||||||
|
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, current_x, BOAR_SOUND_VOLUME_STEP);
|
||||||
} else {
|
} else {
|
||||||
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, current_x, ZOMBIE_SOUND_VOLUME_STEP);
|
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, current_x, ZOMBIE_SOUND_VOLUME_STEP);
|
||||||
}
|
}
|
||||||
@@ -131,6 +143,7 @@ void release_sling_attack(int player_x) {
|
|||||||
int target_x = -1;
|
int target_x = -1;
|
||||||
bool hit_bandit = false;
|
bool hit_bandit = false;
|
||||||
bool hit_flying_creature = false;
|
bool hit_flying_creature = false;
|
||||||
|
bool hit_boar = false;
|
||||||
|
|
||||||
// Priority: Find nearest enemy (bandit or zombie) first
|
// Priority: Find nearest enemy (bandit or zombie) first
|
||||||
for (int dist = 1; dist <= SLING_RANGE; dist++) {
|
for (int dist = 1; dist <= SLING_RANGE; dist++) {
|
||||||
@@ -144,6 +157,14 @@ void release_sling_attack(int player_x) {
|
|||||||
hit_bandit = true;
|
hit_bandit = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then check for boar
|
||||||
|
GameBoar@ boar = get_boar_at(check_x);
|
||||||
|
if (boar != null) {
|
||||||
|
target_x = check_x;
|
||||||
|
hit_boar = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Then check for zombie
|
// Then check for zombie
|
||||||
Zombie@ zombie = get_zombie_at(check_x);
|
Zombie@ zombie = get_zombie_at(check_x);
|
||||||
@@ -190,6 +211,10 @@ void release_sling_attack(int player_x) {
|
|||||||
damage_bandit_at(target_x, damage);
|
damage_bandit_at(target_x, damage);
|
||||||
play_1d_with_volume_step("sounds/weapons/sling_hit.ogg", player_x, target_x, false, PLAYER_WEAPON_SOUND_VOLUME_STEP);
|
play_1d_with_volume_step("sounds/weapons/sling_hit.ogg", player_x, target_x, false, PLAYER_WEAPON_SOUND_VOLUME_STEP);
|
||||||
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", player_x, target_x, BANDIT_SOUND_VOLUME_STEP);
|
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", player_x, target_x, BANDIT_SOUND_VOLUME_STEP);
|
||||||
|
} else if (hit_boar) {
|
||||||
|
damage_boar_at(target_x, damage);
|
||||||
|
play_1d_with_volume_step("sounds/weapons/sling_hit.ogg", player_x, target_x, false, PLAYER_WEAPON_SOUND_VOLUME_STEP);
|
||||||
|
play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", player_x, target_x, BOAR_SOUND_VOLUME_STEP);
|
||||||
} else if (hit_flying_creature) {
|
} else if (hit_flying_creature) {
|
||||||
damage_flying_creature_at(target_x, damage);
|
damage_flying_creature_at(target_x, damage);
|
||||||
play_1d_with_volume_step("sounds/weapons/sling_hit.ogg", player_x, target_x, false, PLAYER_WEAPON_SOUND_VOLUME_STEP);
|
play_1d_with_volume_step("sounds/weapons/sling_hit.ogg", player_x, target_x, false, PLAYER_WEAPON_SOUND_VOLUME_STEP);
|
||||||
|
|||||||
@@ -32,20 +32,12 @@ const int SLING_DAMAGE_MIN = 5;
|
|||||||
const int SLING_DAMAGE_MAX = 8;
|
const int SLING_DAMAGE_MAX = 8;
|
||||||
const int SLING_RANGE = 8;
|
const int SLING_RANGE = 8;
|
||||||
|
|
||||||
// Bow settings (for future implementation)
|
// Bow settings
|
||||||
// Option 1: Longer range, similar damage
|
const int BOW_DAMAGE_MIN = 6;
|
||||||
// const int BOW_DAMAGE_MIN = 6;
|
const int BOW_DAMAGE_MAX = 9;
|
||||||
// const int BOW_DAMAGE_MAX = 9;
|
const int BOW_RANGE = 12;
|
||||||
// const int BOW_RANGE = 12; // 50% more range than sling
|
const int ARROWS_PER_CRAFT = 12;
|
||||||
//
|
const int ARROW_CAPACITY_PER_QUIVER = 12;
|
||||||
// Option 2: Much longer range, slightly more damage
|
|
||||||
// const int BOW_DAMAGE_MIN = 7;
|
|
||||||
// const int BOW_DAMAGE_MAX = 10;
|
|
||||||
// const int BOW_RANGE = 15; // Nearly double sling range
|
|
||||||
//
|
|
||||||
// Recommendation: Bows should have BOTH more range AND more damage than slings
|
|
||||||
// to justify the likely higher resource cost and complexity to craft.
|
|
||||||
// Suggested balance: BOW_RANGE = 12, damage 6-9 (average 7.5 vs sling's 6.5)
|
|
||||||
|
|
||||||
// Zombie settings
|
// Zombie settings
|
||||||
const int ZOMBIE_HEALTH = 12;
|
const int ZOMBIE_HEALTH = 12;
|
||||||
@@ -60,6 +52,21 @@ 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;
|
||||||
|
|
||||||
|
// Boar settings
|
||||||
|
const int BOAR_HEALTH = 4;
|
||||||
|
const int BOAR_MAX_COUNT = 3;
|
||||||
|
const int BOAR_MOVE_INTERVAL_MIN = 800;
|
||||||
|
const int BOAR_MOVE_INTERVAL_MAX = 1500;
|
||||||
|
const int BOAR_ATTACK_INTERVAL = 1500;
|
||||||
|
const int BOAR_DAMAGE_MIN = 1;
|
||||||
|
const int BOAR_DAMAGE_MAX = 3;
|
||||||
|
const int BOAR_SOUND_MIN_DELAY = 3000;
|
||||||
|
const int BOAR_SOUND_MAX_DELAY = 6000;
|
||||||
|
const int BOAR_FOOTSTEP_MAX_DISTANCE = 5;
|
||||||
|
const float BOAR_SOUND_VOLUME_STEP = 3.0;
|
||||||
|
const int BOAR_SIGHT_RANGE = 4;
|
||||||
|
const int BOAR_CHARGE_SPEED = 500; // ms per tile when charging
|
||||||
|
|
||||||
// Barricade configuration
|
// Barricade configuration
|
||||||
const int BARRICADE_BASE_HEALTH = 100;
|
const int BARRICADE_BASE_HEALTH = 100;
|
||||||
const int BARRICADE_MAX_HEALTH = 500;
|
const int BARRICADE_MAX_HEALTH = 500;
|
||||||
@@ -188,10 +195,10 @@ const int WIND_GUST_MIN_DELAY = 30000; // Min 30 seconds between gusts
|
|||||||
const int WIND_GUST_MAX_DELAY = 60000; // Max 60 seconds between gusts
|
const int WIND_GUST_MAX_DELAY = 60000; // Max 60 seconds between gusts
|
||||||
const int THUNDER_MIN_INTERVAL = 8000; // Min 8 seconds between thunder
|
const int THUNDER_MIN_INTERVAL = 8000; // Min 8 seconds between thunder
|
||||||
const int THUNDER_MAX_INTERVAL = 35000; // Max 35 seconds between thunder
|
const int THUNDER_MAX_INTERVAL = 35000; // Max 35 seconds between thunder
|
||||||
const int THUNDER_MOVEMENT_SPEED = 2000; // ms per tile movement (slow roll across sky)
|
const int THUNDER_MOVEMENT_SPEED = 250; // ms per tile movement (faster roll across sky)
|
||||||
const float THUNDER_SOUND_VOLUME_STEP = 2.0; // Gentler volume falloff
|
const float THUNDER_SOUND_VOLUME_STEP = 0.5; // Gentler volume falloff
|
||||||
const int THUNDER_SPAWN_DISTANCE_MIN = 20; // Min distance from player
|
const int THUNDER_SPAWN_DISTANCE_MIN = 0; // Min distance from player
|
||||||
const int THUNDER_SPAWN_DISTANCE_MAX = 40; // Max distance from player
|
const int THUNDER_SPAWN_DISTANCE_MAX = 20; // Max distance from player
|
||||||
const int CHANCE_CLEAR_TO_WINDY = 15;
|
const int CHANCE_CLEAR_TO_WINDY = 15;
|
||||||
const int CHANCE_CLEAR_TO_RAINY = 6;
|
const int CHANCE_CLEAR_TO_RAINY = 6;
|
||||||
const int CHANCE_CLEAR_TO_STORMY = 5;
|
const int CHANCE_CLEAR_TO_STORMY = 5;
|
||||||
@@ -202,3 +209,27 @@ const int CHANCE_RAINY_STAY = 40;
|
|||||||
const int CHANCE_RAINY_TO_STORMY = 35;
|
const int CHANCE_RAINY_TO_STORMY = 35;
|
||||||
const int CHANCE_STORMY_STAY = 40;
|
const int CHANCE_STORMY_STAY = 40;
|
||||||
const int CHANCE_STORMY_TO_RAINY = 35;
|
const int CHANCE_STORMY_TO_RAINY = 35;
|
||||||
|
|
||||||
|
// Weather States
|
||||||
|
const int WEATHER_CLEAR = 0;
|
||||||
|
const int WEATHER_WINDY = 1;
|
||||||
|
const int WEATHER_RAINY = 2;
|
||||||
|
const int WEATHER_STORMY = 3;
|
||||||
|
|
||||||
|
// Weather Intensity levels (0 = none, 1-3 = low/medium/high)
|
||||||
|
const int INTENSITY_NONE = 0;
|
||||||
|
const int INTENSITY_LOW = 1;
|
||||||
|
const int INTENSITY_MEDIUM = 2;
|
||||||
|
const int INTENSITY_HIGH = 3;
|
||||||
|
|
||||||
|
const string RAIN_SOUND = "sounds/nature/rain.ogg";
|
||||||
|
|
||||||
|
// Environment / Fall Damage
|
||||||
|
const int SAFE_FALL_HEIGHT = 10;
|
||||||
|
const int FALL_DAMAGE_MIN = 0;
|
||||||
|
const int FALL_DAMAGE_MAX = 4;
|
||||||
|
|
||||||
|
// Base Automation
|
||||||
|
const int RESIDENT_SLING_COOLDOWN = 4000; // 4 seconds between shots
|
||||||
|
const int RESIDENT_COLLECTION_CHANCE = 10; // 10% chance per basket per hour
|
||||||
|
|
||||||
|
|||||||
@@ -949,8 +949,8 @@ void butcher_small_game() {
|
|||||||
// Check for knife
|
// Check for knife
|
||||||
if (inv_knives < 1) missing += "Stone Knife ";
|
if (inv_knives < 1) missing += "Stone Knife ";
|
||||||
|
|
||||||
// Check for small game
|
// Check for small game or boar
|
||||||
if (inv_small_game < 1) missing += "Small Game ";
|
if (inv_small_game < 1 && inv_boar_carcasses < 1) missing += "Game ";
|
||||||
|
|
||||||
// Check for fire within 3 tiles (can hear it)
|
// Check for fire within 3 tiles (can hear it)
|
||||||
WorldFire@ fire = get_fire_within_range(x, 3);
|
WorldFire@ fire = get_fire_within_range(x, 3);
|
||||||
@@ -970,15 +970,26 @@ void butcher_small_game() {
|
|||||||
}
|
}
|
||||||
simulate_crafting(1);
|
simulate_crafting(1);
|
||||||
|
|
||||||
string game_type = inv_small_game_types[0];
|
string game_type = "";
|
||||||
inv_small_game_types.remove_at(0);
|
if (inv_boar_carcasses > 0) {
|
||||||
inv_small_game--;
|
game_type = "boar carcass";
|
||||||
|
inv_boar_carcasses--;
|
||||||
|
} else {
|
||||||
|
game_type = inv_small_game_types[0];
|
||||||
|
inv_small_game_types.remove_at(0);
|
||||||
|
inv_small_game--;
|
||||||
|
}
|
||||||
|
|
||||||
if (game_type == "goose") {
|
if (game_type == "goose") {
|
||||||
inv_meat++;
|
inv_meat++;
|
||||||
inv_feathers += random(3, 6);
|
inv_feathers += random(3, 6);
|
||||||
inv_down += random(1, 3);
|
inv_down += random(1, 3);
|
||||||
screen_reader_speak("Butchered goose. Got 1 meat, feathers, and down.", true);
|
screen_reader_speak("Butchered goose. Got 1 meat, feathers, and down.", true);
|
||||||
|
} else if (game_type == "boar carcass") {
|
||||||
|
inv_meat += random(2, 3);
|
||||||
|
inv_skins += 3;
|
||||||
|
inv_sinew += 2;
|
||||||
|
screen_reader_speak("Butchered boar. Got meat, 3 skins, and 2 sinew.", true);
|
||||||
} else {
|
} else {
|
||||||
inv_meat++;
|
inv_meat++;
|
||||||
inv_skins++;
|
inv_skins++;
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
// Safe fall height is 10 feet or less
|
// Safe fall height is 10 feet or less
|
||||||
// Each foot above 10 has a chance to deal 0-4 damage
|
// Each foot above 10 has a chance to deal 0-4 damage
|
||||||
// This means falling from great heights is VERY dangerous but not guaranteed fatal
|
// This means falling from great heights is VERY dangerous but not guaranteed fatal
|
||||||
const int SAFE_FALL_HEIGHT = 10;
|
|
||||||
const int FALL_DAMAGE_MIN = 0;
|
|
||||||
const int FALL_DAMAGE_MAX = 4;
|
|
||||||
|
|
||||||
void apply_falling_damage(int fall_height) {
|
void apply_falling_damage(int fall_height) {
|
||||||
// Always play the hit ground sound
|
// Always play the hit ground sound
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ int inv_skins = 0;
|
|||||||
int inv_feathers = 0;
|
int inv_feathers = 0;
|
||||||
int inv_down = 0;
|
int inv_down = 0;
|
||||||
int inv_incense = 0;
|
int inv_incense = 0;
|
||||||
|
int inv_bows = 0;
|
||||||
|
int inv_arrows = 0;
|
||||||
|
int inv_quivers = 0;
|
||||||
|
int inv_bowstrings = 0;
|
||||||
|
int inv_sinew = 0;
|
||||||
|
int inv_boar_carcasses = 0;
|
||||||
|
|
||||||
int inv_spears = 0;
|
int inv_spears = 0;
|
||||||
int inv_snares = 0;
|
int inv_snares = 0;
|
||||||
@@ -43,6 +49,12 @@ int storage_skins = 0;
|
|||||||
int storage_feathers = 0;
|
int storage_feathers = 0;
|
||||||
int storage_down = 0;
|
int storage_down = 0;
|
||||||
int storage_incense = 0;
|
int storage_incense = 0;
|
||||||
|
int storage_bows = 0;
|
||||||
|
int storage_arrows = 0;
|
||||||
|
int storage_quivers = 0;
|
||||||
|
int storage_bowstrings = 0;
|
||||||
|
int storage_sinew = 0;
|
||||||
|
int storage_boar_carcasses = 0;
|
||||||
|
|
||||||
int storage_spears = 0;
|
int storage_spears = 0;
|
||||||
int storage_snares = 0;
|
int storage_snares = 0;
|
||||||
@@ -63,11 +75,13 @@ int storage_skin_pouches = 0;
|
|||||||
bool spear_equipped = false;
|
bool spear_equipped = false;
|
||||||
bool axe_equipped = false;
|
bool axe_equipped = false;
|
||||||
bool sling_equipped = false;
|
bool sling_equipped = false;
|
||||||
|
bool bow_equipped = false;
|
||||||
int[] quick_slots;
|
int[] quick_slots;
|
||||||
const int EQUIP_NONE = -1;
|
const int EQUIP_NONE = -1;
|
||||||
const int EQUIP_SPEAR = 0;
|
const int EQUIP_SPEAR = 0;
|
||||||
const int EQUIP_AXE = 1;
|
const int EQUIP_AXE = 1;
|
||||||
const int EQUIP_SLING = 2;
|
const int EQUIP_SLING = 2;
|
||||||
|
const int EQUIP_BOW = 9; // Next available ID
|
||||||
const int EQUIP_HAT = 3;
|
const int EQUIP_HAT = 3;
|
||||||
const int EQUIP_GLOVES = 4;
|
const int EQUIP_GLOVES = 4;
|
||||||
const int EQUIP_PANTS = 5;
|
const int EQUIP_PANTS = 5;
|
||||||
@@ -101,6 +115,12 @@ const int ITEM_CLAY_POTS = 23;
|
|||||||
const int ITEM_FEATHERS = 24;
|
const int ITEM_FEATHERS = 24;
|
||||||
const int ITEM_DOWN = 25;
|
const int ITEM_DOWN = 25;
|
||||||
const int ITEM_INCENSE = 26;
|
const int ITEM_INCENSE = 26;
|
||||||
|
const int ITEM_BOWS = 27;
|
||||||
|
const int ITEM_ARROWS = 28;
|
||||||
|
const int ITEM_QUIVERS = 29;
|
||||||
|
const int ITEM_BOWSTRINGS = 30;
|
||||||
|
const int ITEM_SINEW = 31;
|
||||||
|
const int ITEM_BOAR_CARCASSES = 32;
|
||||||
const int HAT_MAX_HEALTH_BONUS = 1;
|
const int HAT_MAX_HEALTH_BONUS = 1;
|
||||||
const int GLOVES_MAX_HEALTH_BONUS = 1;
|
const int GLOVES_MAX_HEALTH_BONUS = 1;
|
||||||
const int PANTS_MAX_HEALTH_BONUS = 3;
|
const int PANTS_MAX_HEALTH_BONUS = 3;
|
||||||
@@ -128,10 +148,18 @@ int get_personal_stack_limit() {
|
|||||||
return limit;
|
return limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int get_arrow_limit() {
|
||||||
|
// Quiver required to hold arrows
|
||||||
|
// Each quiver holds 12 arrows
|
||||||
|
if (inv_quivers == 0) return 0;
|
||||||
|
return inv_quivers * ARROW_CAPACITY_PER_QUIVER;
|
||||||
|
}
|
||||||
|
|
||||||
string get_equipment_name(int equip_type) {
|
string get_equipment_name(int equip_type) {
|
||||||
if (equip_type == EQUIP_SPEAR) return "Spear";
|
if (equip_type == EQUIP_SPEAR) return "Spear";
|
||||||
if (equip_type == EQUIP_AXE) return "Stone Axe";
|
if (equip_type == EQUIP_AXE) return "Stone Axe";
|
||||||
if (equip_type == EQUIP_SLING) return "Sling";
|
if (equip_type == EQUIP_SLING) return "Sling";
|
||||||
|
if (equip_type == EQUIP_BOW) return "Bow";
|
||||||
if (equip_type == EQUIP_HAT) return "Skin Hat";
|
if (equip_type == EQUIP_HAT) return "Skin Hat";
|
||||||
if (equip_type == EQUIP_GLOVES) return "Skin Gloves";
|
if (equip_type == EQUIP_GLOVES) return "Skin Gloves";
|
||||||
if (equip_type == EQUIP_PANTS) return "Skin Pants";
|
if (equip_type == EQUIP_PANTS) return "Skin Pants";
|
||||||
@@ -145,6 +173,7 @@ bool equipment_available(int equip_type) {
|
|||||||
if (equip_type == EQUIP_SPEAR) return inv_spears > 0;
|
if (equip_type == EQUIP_SPEAR) return inv_spears > 0;
|
||||||
if (equip_type == EQUIP_AXE) return inv_axes > 0;
|
if (equip_type == EQUIP_AXE) return inv_axes > 0;
|
||||||
if (equip_type == EQUIP_SLING) return inv_slings > 0;
|
if (equip_type == EQUIP_SLING) return inv_slings > 0;
|
||||||
|
if (equip_type == EQUIP_BOW) return inv_bows > 0;
|
||||||
if (equip_type == EQUIP_HAT) return inv_skin_hats > 0;
|
if (equip_type == EQUIP_HAT) return inv_skin_hats > 0;
|
||||||
if (equip_type == EQUIP_GLOVES) return inv_skin_gloves > 0;
|
if (equip_type == EQUIP_GLOVES) return inv_skin_gloves > 0;
|
||||||
if (equip_type == EQUIP_PANTS) return inv_skin_pants > 0;
|
if (equip_type == EQUIP_PANTS) return inv_skin_pants > 0;
|
||||||
@@ -155,10 +184,11 @@ bool equipment_available(int equip_type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void equip_equipment_type(int equip_type) {
|
void equip_equipment_type(int equip_type) {
|
||||||
if (equip_type == EQUIP_SPEAR || equip_type == EQUIP_AXE || equip_type == EQUIP_SLING) {
|
if (equip_type == EQUIP_SPEAR || equip_type == EQUIP_AXE || equip_type == EQUIP_SLING || equip_type == EQUIP_BOW) {
|
||||||
spear_equipped = (equip_type == EQUIP_SPEAR);
|
spear_equipped = (equip_type == EQUIP_SPEAR);
|
||||||
axe_equipped = (equip_type == EQUIP_AXE);
|
axe_equipped = (equip_type == EQUIP_AXE);
|
||||||
sling_equipped = (equip_type == EQUIP_SLING);
|
sling_equipped = (equip_type == EQUIP_SLING);
|
||||||
|
bow_equipped = (equip_type == EQUIP_BOW);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +204,7 @@ bool equipment_is_equipped(int equip_type) {
|
|||||||
if (equip_type == EQUIP_SPEAR) return spear_equipped;
|
if (equip_type == EQUIP_SPEAR) return spear_equipped;
|
||||||
if (equip_type == EQUIP_AXE) return axe_equipped;
|
if (equip_type == EQUIP_AXE) return axe_equipped;
|
||||||
if (equip_type == EQUIP_SLING) return sling_equipped;
|
if (equip_type == EQUIP_SLING) return sling_equipped;
|
||||||
|
if (equip_type == EQUIP_BOW) return bow_equipped;
|
||||||
if (equip_type == EQUIP_HAT) return equipped_head == EQUIP_HAT;
|
if (equip_type == EQUIP_HAT) return equipped_head == EQUIP_HAT;
|
||||||
if (equip_type == EQUIP_TUNIC) return equipped_torso == EQUIP_TUNIC;
|
if (equip_type == EQUIP_TUNIC) return equipped_torso == EQUIP_TUNIC;
|
||||||
if (equip_type == EQUIP_GLOVES) return equipped_hands == EQUIP_GLOVES;
|
if (equip_type == EQUIP_GLOVES) return equipped_hands == EQUIP_GLOVES;
|
||||||
@@ -190,6 +221,8 @@ void unequip_equipment_type(int equip_type) {
|
|||||||
axe_equipped = false;
|
axe_equipped = false;
|
||||||
} else if (equip_type == EQUIP_SLING) {
|
} else if (equip_type == EQUIP_SLING) {
|
||||||
sling_equipped = false;
|
sling_equipped = false;
|
||||||
|
} else if (equip_type == EQUIP_BOW) {
|
||||||
|
bow_equipped = false;
|
||||||
} else if (equip_type == EQUIP_HAT && equipped_head == EQUIP_HAT) {
|
} else if (equip_type == EQUIP_HAT && equipped_head == EQUIP_HAT) {
|
||||||
equipped_head = EQUIP_NONE;
|
equipped_head = EQUIP_NONE;
|
||||||
} else if (equip_type == EQUIP_TUNIC && equipped_torso == EQUIP_TUNIC) {
|
} else if (equip_type == EQUIP_TUNIC && equipped_torso == EQUIP_TUNIC) {
|
||||||
@@ -286,6 +319,12 @@ int get_personal_count(int item_type) {
|
|||||||
if (item_type == ITEM_FEATHERS) return inv_feathers;
|
if (item_type == ITEM_FEATHERS) return inv_feathers;
|
||||||
if (item_type == ITEM_DOWN) return inv_down;
|
if (item_type == ITEM_DOWN) return inv_down;
|
||||||
if (item_type == ITEM_INCENSE) return inv_incense;
|
if (item_type == ITEM_INCENSE) return inv_incense;
|
||||||
|
if (item_type == ITEM_BOWS) return inv_bows;
|
||||||
|
if (item_type == ITEM_ARROWS) return inv_arrows;
|
||||||
|
if (item_type == ITEM_QUIVERS) return inv_quivers;
|
||||||
|
if (item_type == ITEM_BOWSTRINGS) return inv_bowstrings;
|
||||||
|
if (item_type == ITEM_SINEW) return inv_sinew;
|
||||||
|
if (item_type == ITEM_BOAR_CARCASSES) return inv_boar_carcasses;
|
||||||
if (item_type == ITEM_SPEARS) return inv_spears;
|
if (item_type == ITEM_SPEARS) return inv_spears;
|
||||||
if (item_type == ITEM_SLINGS) return inv_slings;
|
if (item_type == ITEM_SLINGS) return inv_slings;
|
||||||
if (item_type == ITEM_AXES) return inv_axes;
|
if (item_type == ITEM_AXES) return inv_axes;
|
||||||
@@ -317,6 +356,12 @@ int get_storage_count(int item_type) {
|
|||||||
if (item_type == ITEM_FEATHERS) return storage_feathers;
|
if (item_type == ITEM_FEATHERS) return storage_feathers;
|
||||||
if (item_type == ITEM_DOWN) return storage_down;
|
if (item_type == ITEM_DOWN) return storage_down;
|
||||||
if (item_type == ITEM_INCENSE) return storage_incense;
|
if (item_type == ITEM_INCENSE) return storage_incense;
|
||||||
|
if (item_type == ITEM_BOWS) return storage_bows;
|
||||||
|
if (item_type == ITEM_ARROWS) return storage_arrows;
|
||||||
|
if (item_type == ITEM_QUIVERS) return storage_quivers;
|
||||||
|
if (item_type == ITEM_BOWSTRINGS) return storage_bowstrings;
|
||||||
|
if (item_type == ITEM_SINEW) return storage_sinew;
|
||||||
|
if (item_type == ITEM_BOAR_CARCASSES) return storage_boar_carcasses;
|
||||||
if (item_type == ITEM_SPEARS) return storage_spears;
|
if (item_type == ITEM_SPEARS) return storage_spears;
|
||||||
if (item_type == ITEM_SLINGS) return storage_slings;
|
if (item_type == ITEM_SLINGS) return storage_slings;
|
||||||
if (item_type == ITEM_AXES) return storage_axes;
|
if (item_type == ITEM_AXES) return storage_axes;
|
||||||
@@ -348,6 +393,12 @@ string get_item_label(int item_type) {
|
|||||||
if (item_type == ITEM_FEATHERS) return "feathers";
|
if (item_type == ITEM_FEATHERS) return "feathers";
|
||||||
if (item_type == ITEM_DOWN) return "down";
|
if (item_type == ITEM_DOWN) return "down";
|
||||||
if (item_type == ITEM_INCENSE) return "incense";
|
if (item_type == ITEM_INCENSE) return "incense";
|
||||||
|
if (item_type == ITEM_BOWS) return "bows";
|
||||||
|
if (item_type == ITEM_ARROWS) return "arrows";
|
||||||
|
if (item_type == ITEM_QUIVERS) return "quivers";
|
||||||
|
if (item_type == ITEM_BOWSTRINGS) return "bowstrings";
|
||||||
|
if (item_type == ITEM_SINEW) return "sinew";
|
||||||
|
if (item_type == ITEM_BOAR_CARCASSES) return "boar carcasses";
|
||||||
if (item_type == ITEM_SPEARS) return "spears";
|
if (item_type == ITEM_SPEARS) return "spears";
|
||||||
if (item_type == ITEM_SLINGS) return "slings";
|
if (item_type == ITEM_SLINGS) return "slings";
|
||||||
if (item_type == ITEM_AXES) return "axes";
|
if (item_type == ITEM_AXES) return "axes";
|
||||||
@@ -368,11 +419,7 @@ string get_item_label(int item_type) {
|
|||||||
|
|
||||||
string format_favor(double value) {
|
string format_favor(double value) {
|
||||||
if (value < 0) value = 0;
|
if (value < 0) value = 0;
|
||||||
int scaled = int(value * 100 + 0.5);
|
return "" + int(value);
|
||||||
int whole = scaled / 100;
|
|
||||||
int frac = scaled % 100;
|
|
||||||
string frac_text = (frac < 10) ? "0" + frac : "" + frac;
|
|
||||||
return "" + whole + "." + frac_text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string get_item_label_singular(int item_type) {
|
string get_item_label_singular(int item_type) {
|
||||||
@@ -388,6 +435,12 @@ string get_item_label_singular(int item_type) {
|
|||||||
if (item_type == ITEM_FEATHERS) return "feather";
|
if (item_type == ITEM_FEATHERS) return "feather";
|
||||||
if (item_type == ITEM_DOWN) return "down";
|
if (item_type == ITEM_DOWN) return "down";
|
||||||
if (item_type == ITEM_INCENSE) return "incense stick";
|
if (item_type == ITEM_INCENSE) return "incense stick";
|
||||||
|
if (item_type == ITEM_BOWS) return "bow";
|
||||||
|
if (item_type == ITEM_ARROWS) return "arrow";
|
||||||
|
if (item_type == ITEM_QUIVERS) return "quiver";
|
||||||
|
if (item_type == ITEM_BOWSTRINGS) return "bowstring";
|
||||||
|
if (item_type == ITEM_SINEW) return "piece of sinew";
|
||||||
|
if (item_type == ITEM_BOAR_CARCASSES) return "boar carcass";
|
||||||
if (item_type == ITEM_SPEARS) return "spear";
|
if (item_type == ITEM_SPEARS) return "spear";
|
||||||
if (item_type == ITEM_SLINGS) return "sling";
|
if (item_type == ITEM_SLINGS) return "sling";
|
||||||
if (item_type == ITEM_AXES) return "axe";
|
if (item_type == ITEM_AXES) return "axe";
|
||||||
@@ -419,6 +472,12 @@ double get_item_favor_value(int item_type) {
|
|||||||
if (item_type == ITEM_FEATHERS) return 0.05;
|
if (item_type == ITEM_FEATHERS) return 0.05;
|
||||||
if (item_type == ITEM_DOWN) return 0.05;
|
if (item_type == ITEM_DOWN) return 0.05;
|
||||||
if (item_type == ITEM_INCENSE) return 0.10;
|
if (item_type == ITEM_INCENSE) return 0.10;
|
||||||
|
if (item_type == ITEM_BOWS) return 2.50;
|
||||||
|
if (item_type == ITEM_ARROWS) return 0.05;
|
||||||
|
if (item_type == ITEM_QUIVERS) return 1.50;
|
||||||
|
if (item_type == ITEM_BOWSTRINGS) return 0.20;
|
||||||
|
if (item_type == ITEM_SINEW) return 0.10;
|
||||||
|
if (item_type == ITEM_BOAR_CARCASSES) return 1.50;
|
||||||
if (item_type == ITEM_SPEARS) return 1.00;
|
if (item_type == ITEM_SPEARS) return 1.00;
|
||||||
if (item_type == ITEM_SLINGS) return 2.00;
|
if (item_type == ITEM_SLINGS) return 2.00;
|
||||||
if (item_type == ITEM_AXES) return 1.50;
|
if (item_type == ITEM_AXES) return 1.50;
|
||||||
@@ -441,6 +500,7 @@ string get_equipped_weapon_name() {
|
|||||||
if (spear_equipped) return "Spear";
|
if (spear_equipped) return "Spear";
|
||||||
if (axe_equipped) return "Stone Axe";
|
if (axe_equipped) return "Stone Axe";
|
||||||
if (sling_equipped) return "Sling";
|
if (sling_equipped) return "Sling";
|
||||||
|
if (bow_equipped) return "Bow";
|
||||||
return "None";
|
return "None";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,6 +514,7 @@ void cleanup_equipment_after_inventory_change() {
|
|||||||
if (inv_spears <= 0) spear_equipped = false;
|
if (inv_spears <= 0) spear_equipped = false;
|
||||||
if (inv_axes <= 0) axe_equipped = false;
|
if (inv_axes <= 0) axe_equipped = false;
|
||||||
if (inv_slings <= 0) sling_equipped = false;
|
if (inv_slings <= 0) sling_equipped = false;
|
||||||
|
if (inv_bows <= 0) bow_equipped = false;
|
||||||
if (inv_skin_hats <= 0) equipped_head = EQUIP_NONE;
|
if (inv_skin_hats <= 0) equipped_head = EQUIP_NONE;
|
||||||
if (inv_skin_gloves <= 0) equipped_hands = EQUIP_NONE;
|
if (inv_skin_gloves <= 0) equipped_hands = EQUIP_NONE;
|
||||||
if (inv_skin_pants <= 0) equipped_legs = EQUIP_NONE;
|
if (inv_skin_pants <= 0) equipped_legs = EQUIP_NONE;
|
||||||
|
|||||||
@@ -2,18 +2,6 @@
|
|||||||
// Provides ambient wind, rain, and thunder effects
|
// Provides ambient wind, rain, and thunder effects
|
||||||
// Tunable constants are in src/constants.nvgt
|
// Tunable constants are in src/constants.nvgt
|
||||||
|
|
||||||
// Weather states
|
|
||||||
const int WEATHER_CLEAR = 0;
|
|
||||||
const int WEATHER_WINDY = 1;
|
|
||||||
const int WEATHER_RAINY = 2;
|
|
||||||
const int WEATHER_STORMY = 3;
|
|
||||||
|
|
||||||
// Intensity levels (0 = none, 1-3 = low/medium/high)
|
|
||||||
const int INTENSITY_NONE = 0;
|
|
||||||
const int INTENSITY_LOW = 1;
|
|
||||||
const int INTENSITY_MEDIUM = 2;
|
|
||||||
const int INTENSITY_HIGH = 3;
|
|
||||||
|
|
||||||
// State variables
|
// State variables
|
||||||
int weather_state = WEATHER_CLEAR;
|
int weather_state = WEATHER_CLEAR;
|
||||||
int wind_intensity = INTENSITY_NONE;
|
int wind_intensity = INTENSITY_NONE;
|
||||||
@@ -37,26 +25,30 @@ timer rain_fade_timer;
|
|||||||
// Thunder object state
|
// Thunder object state
|
||||||
class ThunderStrike {
|
class ThunderStrike {
|
||||||
int position;
|
int position;
|
||||||
int direction; // -1 = moving west, 1 = moving east
|
int direction; // -1 = moving west, 1 = moving east, 0 = stationary
|
||||||
int sound_handle;
|
int sound_handle;
|
||||||
timer movement_timer;
|
timer movement_timer;
|
||||||
|
int movement_speed;
|
||||||
|
|
||||||
ThunderStrike(int pos, int dir, int handle) {
|
ThunderStrike(int pos, int dir, int handle, int speed) {
|
||||||
position = pos;
|
position = pos;
|
||||||
direction = dir;
|
direction = dir;
|
||||||
sound_handle = handle;
|
sound_handle = handle;
|
||||||
|
movement_speed = speed;
|
||||||
movement_timer.restart();
|
movement_timer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void update() {
|
void update() {
|
||||||
if (movement_timer.elapsed >= THUNDER_MOVEMENT_SPEED) {
|
if (direction == 0) return; // Stationary thunder
|
||||||
|
|
||||||
|
if (movement_timer.elapsed >= movement_speed) {
|
||||||
position += direction;
|
position += direction;
|
||||||
movement_timer.restart();
|
movement_timer.restart();
|
||||||
}
|
|
||||||
|
// Update sound position
|
||||||
// Update sound position
|
if (sound_handle != -1 && p.sound_is_active(sound_handle)) {
|
||||||
if (sound_handle != -1 && p.sound_is_active(sound_handle)) {
|
p.update_sound_1d(sound_handle, position);
|
||||||
p.update_sound_1d(sound_handle, position);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,8 +75,6 @@ string[] thunder_sounds = {
|
|||||||
"sounds/nature/thunder_high.ogg"
|
"sounds/nature/thunder_high.ogg"
|
||||||
};
|
};
|
||||||
|
|
||||||
const string RAIN_SOUND = "sounds/nature/rain.ogg";
|
|
||||||
|
|
||||||
void init_weather() {
|
void init_weather() {
|
||||||
weather_state = WEATHER_CLEAR;
|
weather_state = WEATHER_CLEAR;
|
||||||
wind_intensity = INTENSITY_NONE;
|
wind_intensity = INTENSITY_NONE;
|
||||||
@@ -385,15 +375,23 @@ void spawn_thunder() {
|
|||||||
|
|
||||||
// Spawn thunder at random distance from player
|
// Spawn thunder at random distance from player
|
||||||
int distance = random(THUNDER_SPAWN_DISTANCE_MIN, THUNDER_SPAWN_DISTANCE_MAX);
|
int distance = random(THUNDER_SPAWN_DISTANCE_MIN, THUNDER_SPAWN_DISTANCE_MAX);
|
||||||
// Randomly place to left or right of player
|
|
||||||
int direction = random(0, 1) == 0 ? -1 : 1;
|
// Determine movement: 20% stationary, 80% moving
|
||||||
int thunder_pos = x + (distance * direction);
|
int direction = 0;
|
||||||
|
if (random(1, 100) > 20) {
|
||||||
|
direction = random(0, 1) == 0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Random speed: 50ms (fast rip) to 600ms (slow roll)
|
||||||
|
int speed = random(50, 600);
|
||||||
|
|
||||||
|
int thunder_pos = x + (distance * ((direction == 0) ? (random(0, 1) == 0 ? -1 : 1) : direction));
|
||||||
|
|
||||||
// Play sound at position with custom volume step
|
// Play sound at position with custom volume step
|
||||||
int handle = play_1d_with_volume_step(thunder_file, x, thunder_pos, false, THUNDER_SOUND_VOLUME_STEP);
|
int handle = play_1d_with_volume_step(thunder_file, x, thunder_pos, false, THUNDER_SOUND_VOLUME_STEP);
|
||||||
|
|
||||||
if (handle != -1) {
|
if (handle != -1) {
|
||||||
ThunderStrike@ strike = ThunderStrike(thunder_pos, direction, handle);
|
ThunderStrike@ strike = ThunderStrike(thunder_pos, direction, handle, speed);
|
||||||
active_thunder.insert_last(strike);
|
active_thunder.insert_last(strike);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ int residents_count = 0;
|
|||||||
string[] zombie_sounds = {"sounds/enemies/zombie1.ogg"};
|
string[] zombie_sounds = {"sounds/enemies/zombie1.ogg"};
|
||||||
string[] bandit_sounds = {"sounds/enemies/bandit1.ogg", "sounds/enemies/bandit2.ogg"};
|
string[] bandit_sounds = {"sounds/enemies/bandit1.ogg", "sounds/enemies/bandit2.ogg"};
|
||||||
string[] goose_sounds = {"sounds/game/goose.ogg"};
|
string[] goose_sounds = {"sounds/game/goose.ogg"};
|
||||||
|
string[] boar_sounds = {"sounds/game/boar.ogg"};
|
||||||
|
|
||||||
class Zombie {
|
class Zombie {
|
||||||
int position;
|
int position;
|
||||||
@@ -83,6 +84,42 @@ class Bandit {
|
|||||||
}
|
}
|
||||||
Bandit@[] bandits;
|
Bandit@[] bandits;
|
||||||
|
|
||||||
|
class GameBoar {
|
||||||
|
int position;
|
||||||
|
int health;
|
||||||
|
int sound_handle;
|
||||||
|
timer move_timer;
|
||||||
|
timer sound_timer;
|
||||||
|
timer attack_timer;
|
||||||
|
int next_move_delay;
|
||||||
|
int next_sound_delay;
|
||||||
|
string voice_sound;
|
||||||
|
string state; // "wandering" or "charging"
|
||||||
|
int area_start;
|
||||||
|
int area_end;
|
||||||
|
int wander_direction; // -1, 0, 1
|
||||||
|
|
||||||
|
GameBoar(int pos, int start, int end) {
|
||||||
|
position = pos;
|
||||||
|
area_start = start;
|
||||||
|
area_end = end;
|
||||||
|
health = BOAR_HEALTH;
|
||||||
|
sound_handle = -1;
|
||||||
|
state = "wandering";
|
||||||
|
wander_direction = 0;
|
||||||
|
|
||||||
|
voice_sound = boar_sounds[random(0, boar_sounds.length() - 1)];
|
||||||
|
|
||||||
|
move_timer.restart();
|
||||||
|
sound_timer.restart();
|
||||||
|
attack_timer.restart();
|
||||||
|
|
||||||
|
next_move_delay = random(BOAR_MOVE_INTERVAL_MIN, BOAR_MOVE_INTERVAL_MAX);
|
||||||
|
next_sound_delay = random(BOAR_SOUND_MIN_DELAY, BOAR_SOUND_MAX_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GameBoar@[] boars;
|
||||||
|
|
||||||
class FlyingCreatureConfig {
|
class FlyingCreatureConfig {
|
||||||
string id;
|
string id;
|
||||||
string drop_type;
|
string drop_type;
|
||||||
@@ -247,6 +284,15 @@ bool try_pickup_world_drop(WorldDrop@ drop) {
|
|||||||
if (get_flying_creature_config_by_drop_type(drop.type) !is null) {
|
if (get_flying_creature_config_by_drop_type(drop.type) !is null) {
|
||||||
return try_pickup_small_game(drop.type);
|
return try_pickup_small_game(drop.type);
|
||||||
}
|
}
|
||||||
|
if (drop.type == "boar carcass") {
|
||||||
|
if (inv_boar_carcasses >= get_personal_stack_limit()) {
|
||||||
|
screen_reader_speak("You can't carry any more boar carcasses.", true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inv_boar_carcasses++;
|
||||||
|
screen_reader_speak("Picked up boar carcass.", true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
screen_reader_speak("Picked up " + drop.type + ".", true);
|
screen_reader_speak("Picked up " + drop.type + ".", true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -960,6 +1006,175 @@ Bandit@ get_bandit_at(int pos) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Boar Functions
|
||||||
|
void clear_boars() {
|
||||||
|
if (boars.length() == 0) return;
|
||||||
|
|
||||||
|
for (uint i = 0; i < boars.length(); i++) {
|
||||||
|
if (boars[i].sound_handle != -1) {
|
||||||
|
p.destroy_sound(boars[i].sound_handle);
|
||||||
|
boars[i].sound_handle = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boars.resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameBoar@ get_boar_at(int pos) {
|
||||||
|
for (uint i = 0; i < boars.length(); i++) {
|
||||||
|
if (boars[i].position == pos) {
|
||||||
|
return @boars[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void spawn_boar(int expansion_start, int expansion_end) {
|
||||||
|
int spawn_x = -1;
|
||||||
|
// Try to find a valid spawn position in grass/snow (expanded area)
|
||||||
|
for (int attempts = 0; attempts < 20; attempts++) {
|
||||||
|
int candidate = random(expansion_start, expansion_end);
|
||||||
|
|
||||||
|
// Don't spawn too close to base (keep away from BASE_END)
|
||||||
|
if (candidate <= BASE_END + 5) continue;
|
||||||
|
|
||||||
|
if (get_boar_at(candidate) == null) {
|
||||||
|
spawn_x = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (spawn_x == -1) return; // Failed to find spot
|
||||||
|
|
||||||
|
GameBoar@ b = GameBoar(spawn_x, expansion_start, expansion_end);
|
||||||
|
boars.insert_last(b);
|
||||||
|
b.sound_handle = play_creature_voice(b.voice_sound, x, spawn_x, BOAR_SOUND_VOLUME_STEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_boar_attack_player(GameBoar@ boar) {
|
||||||
|
if (player_health <= 0) return false;
|
||||||
|
|
||||||
|
// Check if player is on ground (boars can't fly/climb)
|
||||||
|
if (y > 0) return false;
|
||||||
|
|
||||||
|
if (abs(boar.position - x) > 1) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_attack_player_boar(GameBoar@ boar) {
|
||||||
|
if (!can_boar_attack_player(boar)) return false;
|
||||||
|
|
||||||
|
if (boar.attack_timer.elapsed < BOAR_ATTACK_INTERVAL) return false;
|
||||||
|
|
||||||
|
boar.attack_timer.restart();
|
||||||
|
|
||||||
|
// Attack!
|
||||||
|
// TODO: Add specific boar attack sound? For now re-use zombie hit as generic impact
|
||||||
|
play_creature_attack_sound("sounds/enemies/zombie_hits_player.ogg", x, boar.position, BOAR_SOUND_VOLUME_STEP);
|
||||||
|
|
||||||
|
int damage = random(BOAR_DAMAGE_MIN, BOAR_DAMAGE_MAX);
|
||||||
|
player_health -= damage;
|
||||||
|
if (player_health < 0) player_health = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_boar(GameBoar@ boar) {
|
||||||
|
// Sound logic
|
||||||
|
if (boar.sound_timer.elapsed > boar.next_sound_delay) {
|
||||||
|
boar.sound_timer.restart();
|
||||||
|
boar.next_sound_delay = random(BOAR_SOUND_MIN_DELAY, BOAR_SOUND_MAX_DELAY);
|
||||||
|
// Only play if wandering or occasionally while charging
|
||||||
|
boar.sound_handle = play_creature_voice(boar.voice_sound, x, boar.position, BOAR_SOUND_VOLUME_STEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combat logic
|
||||||
|
if (try_attack_player_boar(boar)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Movement logic
|
||||||
|
int move_speed = (boar.state == "charging") ? BOAR_CHARGE_SPEED : boar.next_move_delay;
|
||||||
|
|
||||||
|
if (boar.move_timer.elapsed < move_speed) return;
|
||||||
|
boar.move_timer.restart();
|
||||||
|
if (boar.state == "wandering") {
|
||||||
|
boar.next_move_delay = random(BOAR_MOVE_INTERVAL_MIN, BOAR_MOVE_INTERVAL_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detection Logic
|
||||||
|
int dist_to_player = x - boar.position;
|
||||||
|
int abs_dist = abs(dist_to_player);
|
||||||
|
|
||||||
|
// If player is close, on ground, and boar can see them -> Charge
|
||||||
|
if (y == 0 && abs_dist <= BOAR_SIGHT_RANGE && x > BASE_END) {
|
||||||
|
boar.state = "charging";
|
||||||
|
} else {
|
||||||
|
boar.state = "wandering";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boar.state == "charging") {
|
||||||
|
int dir = (dist_to_player > 0) ? 1 : -1;
|
||||||
|
int target = boar.position + dir;
|
||||||
|
|
||||||
|
// Don't leave area or enter base
|
||||||
|
if (target >= boar.area_start && target <= boar.area_end && target > BASE_END) {
|
||||||
|
boar.position = target;
|
||||||
|
play_creature_footstep(x, boar.position, BASE_END, GRASS_END, BOAR_FOOTSTEP_MAX_DISTANCE, BOAR_SOUND_VOLUME_STEP);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Wandering
|
||||||
|
if (random(1, 100) <= 20) {
|
||||||
|
boar.wander_direction = random(-1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boar.wander_direction != 0) {
|
||||||
|
int target = boar.position + boar.wander_direction;
|
||||||
|
// Don't leave area or enter base
|
||||||
|
if (target >= boar.area_start && target <= boar.area_end && target > BASE_END) {
|
||||||
|
boar.position = target;
|
||||||
|
play_creature_footstep(x, boar.position, BASE_END, GRASS_END, BOAR_FOOTSTEP_MAX_DISTANCE, BOAR_SOUND_VOLUME_STEP);
|
||||||
|
} else {
|
||||||
|
boar.wander_direction = -boar.wander_direction; // Turn around
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_boars() {
|
||||||
|
// Only spawn if map is expanded
|
||||||
|
if (expanded_area_start != -1) {
|
||||||
|
while (boars.length() < BOAR_MAX_COUNT) {
|
||||||
|
spawn_boar(expanded_area_start, expanded_area_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint i = 0; i < boars.length(); i++) {
|
||||||
|
update_boar(boars[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool damage_boar_at(int pos, int damage) {
|
||||||
|
for (uint i = 0; i < boars.length(); i++) {
|
||||||
|
if (boars[i].position == pos) {
|
||||||
|
boars[i].health -= damage;
|
||||||
|
if (boars[i].health <= 0) {
|
||||||
|
if (boars[i].sound_handle != -1) {
|
||||||
|
p.destroy_sound(boars[i].sound_handle);
|
||||||
|
boars[i].sound_handle = -1;
|
||||||
|
}
|
||||||
|
play_creature_death_sound("sounds/game/game_falls.ogg", x, pos, BOAR_SOUND_VOLUME_STEP);
|
||||||
|
|
||||||
|
// Drop carcass
|
||||||
|
add_world_drop(pos, "boar carcass");
|
||||||
|
|
||||||
|
boars.remove_at(i);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void spawn_bandit(int expansion_start, int expansion_end) {
|
void spawn_bandit(int expansion_start, int expansion_end) {
|
||||||
int spawn_x = -1;
|
int spawn_x = -1;
|
||||||
for (int attempts = 0; attempts < 20; attempts++) {
|
for (int attempts = 0; attempts < 20; attempts++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user