Files
draugnorak/src/fishing.nvgt
T

373 lines
11 KiB
Plaintext

// Fishing system
const int FISHING_NEAR_STREAM_RANGE = 2;
const int FISHING_CAST_MOVE_MS = 300;
const int FISHING_CATCH_CHANCE_STEP = 15;
const int FISHING_CATCH_CHANCE_MAX = 85;
const int FISHING_BAD_CAST_BREAK_CHANCE = 40;
const int FISHING_EARLY_ESCAPE_CHANCE = 50;
const int FISHING_LATE_ESCAPE_CHANCE = 50;
const int FISHING_CAST_RANGE = 7;
const int FISHING_REEL_RETURN_RANGE = 7;
string[] fish_types = {"trout", "bass", "salmon", "catfish"};
bool is_ctrl_down() {
return key_down(KEY_LCTRL) || key_down(KEY_RCTRL);
}
bool is_ctrl_pressed() {
return key_pressed(KEY_LCTRL) || key_pressed(KEY_RCTRL);
}
void stop_fishing_sound() {
if (cast_sound_handle != -1) {
p.destroy_sound(cast_sound_handle);
cast_sound_handle = -1;
}
}
void reset_fishing_session() {
stop_fishing_sound();
is_casting = false;
line_in_water = false;
fish_on_line = false;
is_reeling = false;
cast_position = -1;
reel_position = -1;
cast_origin_x = -1;
cast_direction = 0;
reel_direction = 0;
reel_start_direction = 0;
target_stream_start = -1;
target_stream_end = -1;
catch_chance = 0;
hooked_fish_type = "";
}
bool get_nearby_stream(int pos, int range, int &out stream_start, int &out stream_end) {
int best_distance = range + 1;
for (uint i = 0; i < world_streams.length(); i++) {
int distance = 0;
if (pos < world_streams[i].start_position) {
distance = world_streams[i].start_position - pos;
} else if (pos > world_streams[i].end_position) {
distance = pos - world_streams[i].end_position;
}
if (distance <= range && distance < best_distance) {
best_distance = distance;
stream_start = world_streams[i].start_position;
stream_end = world_streams[i].end_position;
}
}
return best_distance <= range;
}
int get_cast_target_position(int origin_x, int stream_start, int stream_end) {
if (origin_x < stream_start) return stream_start;
if (origin_x > stream_end) return stream_end;
return (facing == 1) ? stream_end : stream_start;
}
int get_random_stream_tile() {
if (target_stream_start < 0 || target_stream_end < 0) return x;
if (target_stream_end < target_stream_start) return target_stream_start;
return random(target_stream_start, target_stream_end);
}
string get_random_fish_type() {
bool is_night = (current_hour >= 18 || current_hour < 7);
if (is_night) {
int roll = random(0, 99);
if (roll < 40) return "catfish";
else if (roll < 60) return "trout";
else if (roll < 80) return "bass";
else return "salmon";
}
return fish_types[random(0, 3)];
}
string get_fish_size_label(int weight) {
int clamped = clamp_fish_weight(weight);
if (clamped <= 7) return "small";
if (clamped <= 17) return "medium sized";
if (clamped <= 27) return "large";
return "monster";
}
void break_fishing_pole(string message) {
if (get_personal_count(ITEM_FISHING_POLES) > 0) {
add_personal_count(ITEM_FISHING_POLES, -1);
}
fishing_pole_equipped = false;
p.play_stationary("sounds/actions/fishpole_break.ogg", false);
reset_fishing_session();
if (message == "") {
message = "Your fishing pole broke.";
}
speak_with_history(message, true);
}
void lose_fish(string message) {
reset_fishing_session();
if (message != "") {
speak_with_history(message, true);
}
}
void start_casting() {
if (is_casting || line_in_water || fish_on_line || is_reeling) return;
if (!fishing_pole_equipped) {
speak_with_history("You need a fishing pole equipped.", true);
return;
}
int stream_start = -1;
int stream_end = -1;
if (!get_nearby_stream(x, FISHING_NEAR_STREAM_RANGE, stream_start, stream_end)) {
speak_with_history("You need to be within 2 tiles of a stream.", true);
return;
}
target_stream_start = stream_start;
target_stream_end = stream_end;
cast_origin_x = x;
cast_position = x;
cast_direction = 0;
cast_move_timer.restart();
p.play_stationary("sounds/actions/fishpole.ogg", false);
stop_fishing_sound();
is_casting = true;
}
void update_casting() {
if (!is_casting) return;
if (cast_move_timer.elapsed < FISHING_CAST_MOVE_MS) return;
cast_move_timer.restart();
if (cast_direction == 0) cast_direction = (facing == 1) ? 1 : -1;
cast_position += cast_direction;
int offset = cast_position - cast_origin_x;
if (offset > FISHING_CAST_RANGE) {
cast_position = cast_origin_x + FISHING_CAST_RANGE;
cast_direction = -1;
} else if (offset < -FISHING_CAST_RANGE) {
cast_position = cast_origin_x - FISHING_CAST_RANGE;
cast_direction = 1;
}
if (cast_position < 0) {
cast_position = 0;
cast_direction = 1;
} else if (cast_position > MAP_SIZE - 1) {
cast_position = MAP_SIZE - 1;
cast_direction = -1;
}
play_1d_with_volume_step("sounds/actions/cast_strength.ogg", x, cast_position, false, PLAYER_WEAPON_SOUND_VOLUME_STEP);
}
void release_cast() {
if (!is_casting) return;
is_casting = false;
stop_fishing_sound();
if (is_position_in_water(cast_position)) {
line_in_water = true;
catch_chance = 0;
fishing_timer.restart();
p.play_stationary("sounds/actions/start_fishing.ogg", false);
} else {
p.play_stationary("sounds/actions/bad_cast.ogg", false);
if (random(1, 100) <= FISHING_BAD_CAST_BREAK_CHANCE) {
break_fishing_pole("Your fishing pole broke.");
return;
}
}
cast_position = -1;
cast_origin_x = -1;
cast_direction = 0;
}
void update_waiting_for_fish() {
if (!line_in_water) return;
int minutes_waited = fishing_timer.elapsed / 60000;
int new_chance = minutes_waited * FISHING_CATCH_CHANCE_STEP;
if (new_chance > FISHING_CATCH_CHANCE_MAX) new_chance = FISHING_CATCH_CHANCE_MAX;
catch_chance = new_chance;
if (catch_chance > 0 && random(1, 100) <= catch_chance) {
line_in_water = false;
fish_on_line = true;
reel_position = get_random_stream_tile();
reel_direction = 0;
hooked_fish_type = get_random_fish_type();
speak_with_history("A fish is on the line!", true);
}
}
void start_reeling() {
if (!fish_on_line || is_reeling) return;
is_reeling = true;
reel_start_direction = (reel_position < x) ? 1 : -1;
reel_direction = reel_start_direction;
cast_move_timer.restart();
stop_fishing_sound();
play_1d_with_volume_step("sounds/actions/cast_strength.ogg", x, reel_position, false, PLAYER_WEAPON_SOUND_VOLUME_STEP);
}
void update_reeling() {
if (!is_reeling) return;
if (cast_move_timer.elapsed < FISHING_CAST_MOVE_MS) return;
cast_move_timer.restart();
reel_position += reel_direction;
int offset = reel_position - x;
if (offset > FISHING_REEL_RETURN_RANGE) {
reel_position = x + FISHING_REEL_RETURN_RANGE;
reel_direction = -1;
} else if (offset < -FISHING_REEL_RETURN_RANGE) {
reel_position = x - FISHING_REEL_RETURN_RANGE;
reel_direction = 1;
}
if (reel_position < 0) reel_position = 0;
if (reel_position > MAP_SIZE - 1) reel_position = MAP_SIZE - 1;
play_1d_with_volume_step("sounds/actions/cast_strength.ogg", x, reel_position, false, PLAYER_WEAPON_SOUND_VOLUME_STEP);
}
void catch_fish() {
if (get_personal_count(ITEM_FISH) >= get_personal_stack_limit()) {
speak_with_history("You can't carry any more fish.", true);
reset_fishing_session();
return;
}
int weight = random(FISH_WEIGHT_MIN, FISH_WEIGHT_MAX);
string size_label = get_fish_size_label(weight);
add_personal_count(ITEM_FISH, 1);
add_personal_fish_weight(weight);
p.play_stationary("sounds/items/miscellaneous.ogg", false);
string fish_name = hooked_fish_type;
if (fish_name == "") fish_name = "fish";
speak_with_history("Caught a " + size_label + " " + fish_name + ".", true);
reset_fishing_session();
}
void release_reel() {
if (!is_reeling) return;
stop_fishing_sound();
is_reeling = false;
if (!fish_on_line) return;
if (reel_position == x) {
catch_fish();
return;
}
if (is_position_in_water(reel_position)) {
if (random(1, 100) <= FISHING_EARLY_ESCAPE_CHANCE) {
lose_fish("The fish slipped off in the water.");
} else {
speak_with_history("The fish is still on the line.", true);
}
return;
}
if (reel_start_direction == 0) {
reel_start_direction = (reel_position < x) ? 1 : -1;
}
bool before_player = (reel_start_direction == 1 && reel_position < x) ||
(reel_start_direction == -1 && reel_position > x);
bool past_player = (reel_start_direction == 1 && reel_position > x) ||
(reel_start_direction == -1 && reel_position < x);
if (past_player) {
p.play_stationary("sounds/actions/bad_cast.ogg", false);
if (random(1, 100) <= FISHING_LATE_ESCAPE_CHANCE) {
lose_fish("The fish got away.");
} else {
catch_fish();
}
return;
}
if (before_player) {
if (random(1, 100) <= FISHING_EARLY_ESCAPE_CHANCE) {
lose_fish("The fish got away.");
} else {
speak_with_history("The fish is still on the line.", true);
}
return;
}
}
bool handle_fishing_breaks() {
if (!line_in_water && !fish_on_line && !is_reeling) return false;
if (!fishing_pole_equipped) {
break_fishing_pole("You switched weapons and your fishing pole broke.");
return true;
}
if (key_down(KEY_LEFT) || key_down(KEY_RIGHT) || key_pressed(KEY_UP) || jumping) {
if (fish_on_line || is_reeling) {
break_fishing_pole("You moved and the fish got away. Your fishing pole broke.");
} else {
break_fishing_pole("You moved and your fishing pole broke.");
}
return true;
}
return false;
}
void update_fishing() {
bool ctrl_down = is_ctrl_down();
bool ctrl_pressed = is_ctrl_pressed();
if (handle_fishing_breaks()) return;
if (fishing_pole_equipped && ctrl_pressed && !is_casting && !line_in_water && !fish_on_line && !is_reeling) {
start_casting();
}
if (is_casting) {
update_casting();
if (!ctrl_down) {
release_cast();
}
}
if (line_in_water) {
update_waiting_for_fish();
}
if (fish_on_line && ctrl_pressed && !is_reeling) {
start_reeling();
}
if (is_reeling) {
if (ctrl_down) {
update_reeling();
} else {
release_reel();
}
}
}