Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ Version 1.4.0 (Lupin): Not yet released
- Bagman (`BAG`)
- Team Bagman (`TBAG`)
- Last Miner Standing (`LMS`)
- Add sprays with bindable `Spray` control
- `cl_sprays` console command to toggle local display and dedicated server `[sprays]` config section
- `spray` console command to select spray, and in-game spray picker in advanced options

### Minor features, changes, and enhancements
[@GooberRF](https://github.com/GooberRF)
Expand Down Expand Up @@ -76,6 +79,7 @@ Version 1.4.0 (Lupin): Not yet released
- Fix unbounded read when a request to play a sound above `g_num_sounds` is made
- Fix camera angle snapping when switching between free look and third person camera modes
- Fix a server crash that could be triggered by a zero-length UDP packet in the packet receive pump
- Fix overlapping decals rendering with the oldest on top instead of the newest

[@is-this-c](https://github.com/is-this-c)
- Clear cached server config output after a shuffle of a server's rotation
Expand Down
4 changes: 4 additions & 0 deletions game_patch/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ set(SRCS
multi/rounds.cpp
multi/lms.h
multi/lms.cpp
multi/sprays.h
multi/sprays.cpp
multi/kill.cpp
multi/network.cpp
multi/network.h
Expand Down Expand Up @@ -275,6 +277,8 @@ set(SRCS
misc/destruction.h
misc/camera.cpp
misc/ui.cpp
misc/spray_picker.cpp
misc/spray_picker.h
misc/game.cpp
misc/level.cpp
misc/waypoints.cpp
Expand Down
110 changes: 72 additions & 38 deletions game_patch/input/key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "../rf/os/os.h"
#include "../rf/ui.h"
#include "../multi/alpine_packets.h"
#include "../multi/sprays.h"
#include "../os/console.h"
#include "input.h"

Expand Down Expand Up @@ -278,49 +279,79 @@ CodeInjection control_config_init_patch{
rf::AlpineControlConfigAction::AF_ACTION_SPECTATE_TOGGLE_FREELOOK);
alpine_control_config_add_item(ccp, "Toggle Spectate", false, rf::KEY_DIVIDE, -1, -1,
rf::AlpineControlConfigAction::AF_ACTION_SPECTATE_TOGGLE);
alpine_control_config_add_item(ccp, "Spray", 0, rf::KEY_Z, -1, -1,
rf::AlpineControlConfigAction::AF_ACTION_SPRAY);
},
};

// alpine controls that activate only when local player is alive (multi or single)
// Handles alpine controls that activate only when the local player is alive (multi or single).
static void execute_alive_alpine_control(int action_index)
{
// only intercept alpine controls
if (action_index < starting_alpine_control_index) {
return;
}

if (action_index == static_cast<int>(get_af_control(rf::AlpineControlConfigAction::AF_ACTION_FLASHLIGHT))
&& !rf::is_multi) {
if (g_headlamp_toggle_enabled) {
(rf::entity_headlamp_is_on(rf::local_player_entity))
? rf::entity_headlamp_turn_off(rf::local_player_entity)
: rf::entity_headlamp_turn_on(rf::local_player_entity);
grant_achievement_sp(AchievementName::UseFlashlight);
}
}
else if (action_index == starting_alpine_control_index +
static_cast<int>(rf::AlpineControlConfigAction::AF_ACTION_SELF_KILL) &&
rf::is_multi) {
rf::player_kill_self(rf::local_player);
if (gt_is_run()) {
multi_hud_reset_run_gt_timer(true);
}
}
else if (action_index == starting_alpine_control_index +
static_cast<int>(rf::AlpineControlConfigAction::AF_ACTION_DROP_FLAG) &&
rf::is_multi && !rf::is_server) {
send_chat_line_packet("/dropflag", nullptr);
}
else if (action_index == starting_alpine_control_index +
static_cast<int>(rf::AlpineControlConfigAction::AF_ACTION_PING_LOCATION)) {
ping_looked_at_location();
}
else if (action_index == starting_alpine_control_index +
static_cast<int>(rf::AlpineControlConfigAction::AF_ACTION_SPRAY)) {
sprays_handle_spray_action();
}
else if (action_index == starting_alpine_control_index +
static_cast<int>(rf::AlpineControlConfigAction::AF_ACTION_INSPECT_WEAPON)) {
fpgun_play_random_idle_anim();
}
}

CodeInjection player_execute_action_patch{
0x004A6283,
[](auto& regs) {
rf::ControlConfigAction action = regs.ebp;
int action_index = static_cast<int>(action);
//xlog::warn("executing action {}", action_index);
int action_index = static_cast<int>(regs.ebp);
if (starting_alpine_control_index != -1 &&
action_index >= starting_alpine_control_index) {
execute_alive_alpine_control(action_index);
regs.eip = 0x004A681B;
}
},
};

// only intercept alpine controls
if (action_index >= starting_alpine_control_index) {
if (action_index == static_cast<int>(get_af_control(rf::AlpineControlConfigAction::AF_ACTION_FLASHLIGHT))
&& !rf::is_multi) {
if (g_headlamp_toggle_enabled) {
(rf::entity_headlamp_is_on(rf::local_player_entity))
? rf::entity_headlamp_turn_off(rf::local_player_entity)
: rf::entity_headlamp_turn_on(rf::local_player_entity);
grant_achievement_sp(AchievementName::UseFlashlight);
}
}
else if (action_index == starting_alpine_control_index +
static_cast<int>(rf::AlpineControlConfigAction::AF_ACTION_SELF_KILL) &&
rf::is_multi) {
rf::player_kill_self(rf::local_player);
if (gt_is_run()) {
multi_hud_reset_run_gt_timer(true);
}
}
else if (action_index == starting_alpine_control_index +
static_cast<int>(rf::AlpineControlConfigAction::AF_ACTION_DROP_FLAG) &&
rf::is_multi && !rf::is_server) {
send_chat_line_packet("/dropflag", nullptr);
}
else if (action_index == starting_alpine_control_index +
static_cast<int>(rf::AlpineControlConfigAction::AF_ACTION_PING_LOCATION)) {
ping_looked_at_location();
}
else if (action_index == starting_alpine_control_index +
static_cast<int>(rf::AlpineControlConfigAction::AF_ACTION_INSPECT_WEAPON)) {
fpgun_play_random_idle_anim();
}
// Stock player_execute_action rejects action indices above 0x3c (60).
// Alpine controls at or past index 0x3d (61) never reach that dispatch,
// so handle them here and return before the stock guard runs.
CodeInjection player_execute_action_high_index_patch{
0x004A6272,
[](auto& regs) {
int action_index = static_cast<int>(regs.ebp);
if (starting_alpine_control_index != -1 &&
action_index > 0x3c &&
action_index >= starting_alpine_control_index) {
execute_alive_alpine_control(action_index);
regs.eip = 0x004A681B;
}
},
};
Expand Down Expand Up @@ -423,8 +454,10 @@ CodeInjection controls_process_patch{
0x00430E4C,
[](auto& regs) {
int index = regs.edi;
if (index >= starting_alpine_control_index &&
index <= static_cast<int>(rf::AlpineControlConfigAction::_AF_ACTION_LAST_VARIANT)) {
if (starting_alpine_control_index != -1 &&
index >= starting_alpine_control_index &&
index <= starting_alpine_control_index +
static_cast<int>(rf::AlpineControlConfigAction::_AF_ACTION_LAST_VARIANT)) {
//xlog::warn("passing control {}", index);
regs.eip = 0x00430E24;
}
Expand Down Expand Up @@ -543,6 +576,7 @@ void key_apply_patch()
// Handle Alpine controls
control_config_init_patch.install();
player_execute_action_patch.install();
player_execute_action_high_index_patch.install();
player_execute_action_patch2.install();
player_execute_action_patch3.install();
controls_process_patch.install();
Expand Down
2 changes: 2 additions & 0 deletions game_patch/main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "../fflink/fflink.h"
#include "../misc/misc.h"
#include "../misc/achievements.h"
#include "../misc/spray_picker.h"
#include "../misc/alpine_options.h"
#include "../misc/alpine_settings.h"
#include "../misc/vpackfile.h"
Expand Down Expand Up @@ -200,6 +201,7 @@ CodeInjection after_frame_render_hook{
achievement_system_do_frame();
fullscreen_overlay_do_frame();
gas_region_transition_do_frame();
spray_picker_render();
#if !defined(NDEBUG) && defined(HAS_EXPERIMENTAL)
experimental_render();
#endif
Expand Down
10 changes: 10 additions & 0 deletions game_patch/misc/alpine_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,14 @@ bool alpine_player_settings_load(rf::Player* player)
g_alpine_game_config.play_hit_sounds = std::stoi(settings["PlayHitsounds"]);
processed_keys.insert("PlayHitsounds");
}
if (settings.count("SprayDisplay")) {
g_alpine_game_config.spray_display = std::stoi(settings["SprayDisplay"]);
processed_keys.insert("SprayDisplay");
}
if (settings.count("SpraySelection")) {
g_alpine_game_config.set_selected_spray_index(std::stoi(settings["SpraySelection"]));
processed_keys.insert("SpraySelection");
}
if (settings.count("KillfeedEnabled")) {
g_alpine_game_config.killfeed_enabled = std::stoi(settings["KillfeedEnabled"]);
processed_keys.insert("KillfeedEnabled");
Expand Down Expand Up @@ -1474,6 +1482,8 @@ void alpine_player_settings_save(rf::Player* player)
file << "WorldHUDTeamLabels=" << g_alpine_game_config.world_hud_team_player_labels << "\n";
file << "ShowLocationPings=" << g_alpine_game_config.show_location_pings << "\n";
file << "PlayHitsounds=" << g_alpine_game_config.play_hit_sounds << "\n";
file << "SprayDisplay=" << g_alpine_game_config.spray_display << "\n";
file << "SpraySelection=" << g_alpine_game_config.selected_spray_index << "\n";
file << "KillfeedEnabled=" << g_alpine_game_config.killfeed_enabled << "\n";
file << "HitSoundIntervalMs=" << g_alpine_game_config.hit_sound_min_interval_ms << "\n";
file << "PlayTaunts=" << g_alpine_game_config.play_taunt_sounds << "\n";
Expand Down
11 changes: 11 additions & 0 deletions game_patch/misc/alpine_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

extern bool g_loaded_alpine_settings_file;

// forward declaration (in sprays.cpp)
int spray_count();
Comment thread
GooberRF marked this conversation as resolved.

struct AlpineGameSettings
{
// fov
Expand Down Expand Up @@ -96,6 +99,14 @@ struct AlpineGameSettings
bool show_location_pings = true;
bool play_hit_sounds = true;

bool spray_display = true;
int selected_spray_index = 0;
void set_selected_spray_index(int index)
{
const int count = spray_count();
selected_spray_index = (count > 0) ? std::clamp(index, 0, count - 1) : 0;
}

static constexpr int min_hit_sound_interval_ms = 0;
static constexpr int max_hit_sound_interval_ms = 1000;
int hit_sound_min_interval_ms = 20;
Expand Down
2 changes: 2 additions & 0 deletions game_patch/misc/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "../multi/gametype.h"
#include "../multi/server_internal.h"
#include "../multi/bagman.h"
#include "../multi/sprays.h"
#include "../multi/lms.h"
#include "../hud/multi_spectate.h"
#include "../hud/hud_internal.h"
Expand Down Expand Up @@ -249,6 +250,7 @@ FunHook<void(rf::Player*)> player_destroy_hook{
[](rf::Player* player) {
multi_spectate_on_destroy_player(player);
bagman_on_player_disconnect(player);
sprays_on_player_destroyed(player);
lms_on_player_disconnect(player);
if (rf::is_server) {
remove_ready_player_silent(player);
Expand Down
Loading
Loading