Files
draugnorak/src/combat.nvgt
2026-01-20 02:12:19 -05:00

190 lines
6.2 KiB
Plaintext

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);
}
}