373 lines
11 KiB
Plaintext
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();
|
|
}
|
|
}
|
|
}
|