void perform_attack(int current_x) { if (sling_equipped) { perform_sling_attack(current_x); } else if (spear_equipped) { perform_spear_attack(current_x); } else if (axe_equipped) { perform_axe_attack(current_x); } else { // Optional: Punch logic // p.play_stationary("sounds/weapons/fist_swing.ogg", false); } } int attack_enemy_ranged(int start_x, int end_x, int damage) { for (int check_x = start_x; check_x <= end_x; check_x++) { // Check for bandits first (priority during daytime) if (damage_bandit_at(check_x, damage)) { return check_x; } // Then check zombies if (damage_zombie_at(check_x, damage)) { return check_x; } } return -1; } bool attack_enemy(int target_x, int damage) { // Check for bandits first if (damage_bandit_at(target_x, damage)) { return true; } // Then check zombies return damage_zombie_at(target_x, damage); } void perform_spear_attack(int current_x) { p.play_stationary("sounds/weapons/spear_swing.ogg", false); int hit_pos = attack_enemy_ranged(current_x - 1, current_x + 1, SPEAR_DAMAGE); if (hit_pos != -1) { p.play_stationary("sounds/weapons/spear_hit.ogg", false); // Play hit sound based on enemy type (both use same hit sound for now) if (get_bandit_at(hit_pos) != null) { play_1d_with_volume_step("sounds/enemies/zombie_hit.ogg", x, hit_pos, false, BANDIT_SOUND_VOLUME_STEP); } else { play_1d_with_volume_step("sounds/enemies/zombie_hit.ogg", x, hit_pos, false, ZOMBIE_SOUND_VOLUME_STEP); } return; } // Hit tree with spear (sound only, 0 damage) hit_tree_with_spear(current_x); } void perform_axe_attack(int current_x) { p.play_stationary("sounds/weapons/axe_swing.ogg", false); if (attack_enemy(current_x, AXE_DAMAGE)) { p.play_stationary("sounds/weapons/axe_hit.ogg", false); // Play hit sound based on enemy type if (get_bandit_at(current_x) != null) { play_1d_with_volume_step("sounds/enemies/zombie_hit.ogg", x, current_x, false, BANDIT_SOUND_VOLUME_STEP); } else { play_1d_with_volume_step("sounds/enemies/zombie_hit.ogg", x, current_x, false, ZOMBIE_SOUND_VOLUME_STEP); } return; } // Axe Attack Logic // Range: current_x (0 distance) // Damage: 4 // Target: Trees damage_tree(current_x, 4); } void perform_sling_attack(int current_x) { // Sling uses charge/release mechanism, not direct attack // This function is called from main loop when sling is released release_sling_attack(current_x); } void hit_tree_with_spear(int target_x) { Tree@ target = get_tree_at(target_x); if (@target != null && !target.is_chopped) { p.play_stationary("sounds/weapons/spear_hit.ogg", false); } } void update_sling_charge() { int elapsed = sling_charge_timer.elapsed; int cycle_time = 1500; // 1.5 seconds int stage_duration = 500; // 0.5 seconds per stage // Loop the charge cycle int time_in_cycle = elapsed % cycle_time; // Determine stage: 0=low, 1=in-range, 2=high int current_stage = time_in_cycle / stage_duration; // Play stage indicator sounds (only once per stage change) if (current_stage != last_sling_stage) { if (current_stage == 1) { // Entering in-range window p.play_stationary("sounds/weapons/sling_low_range.ogg", false); } else if (current_stage == 2) { // Entering too-high window p.play_stationary("sounds/weapons/sling_high_range.ogg", false); } last_sling_stage = current_stage; } } void release_sling_attack(int player_x) { // Consume stone inv_stones--; int elapsed = sling_charge_timer.elapsed; int cycle_time = 1500; int time_in_cycle = elapsed % cycle_time; int stage = time_in_cycle / 500; // 0=low, 1=in-range, 2=high // Only hit if released during in-range window (stage 1) if (stage != 1) { screen_reader_speak("Stone missed.", true); return; } // Find target in facing direction int search_direction = (facing == 1) ? 1 : -1; int target_x = -1; bool hit_bandit = false; // Priority: Find nearest enemy (bandit or zombie) first for (int dist = 1; dist <= SLING_RANGE; dist++) { int check_x = player_x + (dist * search_direction); if (check_x < 0 || check_x >= MAP_SIZE) break; // Check for bandit first Bandit@ bandit = get_bandit_at(check_x); if (bandit != null) { target_x = check_x; hit_bandit = true; break; } // Then check for zombie Zombie@ zombie = get_zombie_at(check_x); if (zombie != null) { target_x = check_x; hit_bandit = false; break; } } // If no enemy found, check for trees (but don't damage them) if (target_x == -1) { for (int dist = 1; dist <= SLING_RANGE; dist++) { int check_x = player_x + (dist * search_direction); if (check_x < 0 || check_x >= MAP_SIZE) break; Tree@ tree = get_tree_at(check_x); if (tree != null && !tree.is_chopped) { // Stone hits tree but doesn't damage it p.play_1d("sounds/weapons/sling_hit.ogg", player_x, check_x, false); screen_reader_speak("Stone hit tree at " + check_x + ".", true); return; } } } // No target found if (target_x == -1) { screen_reader_speak("Stone missed.", true); return; } int damage = random(SLING_DAMAGE_MIN, SLING_DAMAGE_MAX); // Damage the correct enemy type if (hit_bandit) { damage_bandit_at(target_x, damage); p.play_1d("sounds/weapons/sling_hit.ogg", player_x, target_x, false); play_1d_with_volume_step("sounds/enemies/zombie_hit.ogg", player_x, target_x, false, BANDIT_SOUND_VOLUME_STEP); } else { damage_zombie_at(target_x, damage); p.play_1d("sounds/weapons/sling_hit.ogg", player_x, target_x, false); play_1d_with_volume_step("sounds/enemies/zombie_hit.ogg", player_x, target_x, false, ZOMBIE_SOUND_VOLUME_STEP); } }