Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement cut stock game gibbing feature #294

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 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
1 change: 1 addition & 0 deletions common/include/common/config/GameConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct GameConfig
CfgVar<bool> mesh_static_lighting = true;
CfgVar<bool> glares = true;
CfgVar<bool> show_enemy_bullets = true;
CfgVar<bool> gibs = false;

static constexpr float min_fov = 75.0f;
static constexpr float max_fov = 160.0f;
Expand Down
1 change: 1 addition & 0 deletions common/src/config/GameConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ bool GameConfig::visit_vars(T&& visitor, bool is_save)
result &= visitor(dash_faction_key, "Swap Assault Rifle Controls", swap_assault_rifle_controls);
result &= visitor(dash_faction_key, "Swap Grenade Controls", swap_grenade_controls);
result &= visitor(dash_faction_key, "Glares", glares);
result &= visitor(dash_faction_key, "Gibs", gibs);
result &= visitor(dash_faction_key, "Linear Pitch", linear_pitch);
result &= visitor(dash_faction_key, "Show Enemy Bullets", show_enemy_bullets);
result &= visitor(dash_faction_key, "Keep Launcher Open", keep_launcher_open);
Expand Down
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Version 1.9.0 (not released yet)
- Properly handle WM_PAINT in dedicated server, may improve performance (DF bug)
- Fix crash when `verify_level` command is run without a level being loaded
- Fix cockpit not rendering for any jeeps after the first one entered each level load
- Add gibbing when enemies die from explosives (enable with `gibs` command)

Version 1.8.0 (released 2022-09-17)
-----------------------------------
Expand Down
109 changes: 109 additions & 0 deletions game_patch/object/entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
#include <patch_common/CallHook.h>
#include <patch_common/AsmWriter.h>
#include <xlog/xlog.h>
#include "../os/console.h"
#include "../rf/entity.h"
#include "../rf/corpse.h"
#include "../rf/multi.h"
#include "../rf/weapon.h"
#include "../rf/player/player.h"
#include "../rf/particle_emitter.h"
Expand Down Expand Up @@ -201,6 +203,104 @@ CodeInjection entity_process_pre_hide_riot_shield_injection{
},
};

// initialize new AiInfo field used in `entity_damage` to store if on death, the last damage dealt was explosive
CodeInjection ai_init_ai_info_injection{
0x00403022,
[](auto& regs) {
rf::AiInfo* aip = regs.esi;
if (aip) {
aip->explosive_last_damage = false;
}
}
};

bool consider_gibs() {
GooberRF marked this conversation as resolved.
Show resolved Hide resolved
return g_game_config.gibs && !rf::is_multi;
}

FunHook<void(int)> entity_blood_throw_gibs_hook{
0x0042E3C0, [](int handle) {
if (!consider_gibs()) {
return;
}

rf::Object* objp = rf::obj_from_handle(handle);

if (!objp) {
return; // invalid object
}

int explode_vclip_index = rf::vclip_lookup("bloodsplat");
int chunk_explode_vclip_index = rf::vclip_lookup("bloodsplat");
float explode_vclip_radius = 1.0f;
const char* debris_filename = "df_meatchunks0.V3D";

static const int debris_max_lifetime = 7000; // ms
static const float debris_velocity = 8.5f;
static const float damage_scale = 1.0f;
static rf::String cust_snd_set = "gib bounce";

if (objp->type == rf::OT_ENTITY) { // use overrides from associated entity.tbl class if present
rf::Entity* ep = static_cast<rf::Entity*>(objp);

explode_vclip_index = (ep->info->explode_vclip_index > 0)
? ep->info->explode_vclip_index : explode_vclip_index;

explode_vclip_radius = (ep->info->explode_vclip_radius > 0.0f)
? ep->info->explode_vclip_radius : explode_vclip_radius;

debris_filename = (!ep->info->debris_filename.empty())
? ep->info->debris_filename.c_str() : debris_filename;
}
else if (objp->type != rf::OT_CORPSE) { // do not gib anything except entities and corpses
return;
}

rf::game_do_explosion(
explode_vclip_index, objp->room, 0, &objp->pos, explode_vclip_radius, damage_scale, 0);

rf::debris_spawn_from_object(
objp, debris_filename, chunk_explode_vclip_index, debris_max_lifetime, debris_velocity, &cust_snd_set);
}
};

CodeInjection entity_damage_explosive_death_injection{
0x0041A413,
[](auto& regs) {
rf::Entity* ep = regs.esi;
int damage_type = regs.ebp;
ep->ai.explosive_last_damage = (damage_type == 3); // explosive damage
GooberRF marked this conversation as resolved.
Show resolved Hide resolved
if (consider_gibs() && ep) {
// using body temp > 90 (from entity.tbl class) as the determining factor for if an entity should gib
// in stock game, this includes only humanoid enemies and reeper/baby reeper
// in mods, this lets the mod author control which of their mod's entities gib
if (ep->ai.explosive_last_damage && ep->info->body_temp >= 90.0f) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this body temp check deserve a comment maybe, but still I'm not sure it's a good way to approach this. I've read something that you wanted to check "humanoid" flag but I think it's not necessary a good thing to check. But what about checking if material is flash?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basing it on body_temp appears, based on my analysis of the stock game files, the only realistic way to ensure this behaves properly in the stock game. The only entities that have body_temp > 90 (and would therefore gib) are humanoid characters and the reeper/baby_reeper. This avoids a situation where entities that have custom death animations and/or are enormous (like the rock snake) but have material flesh are gibbed. body_temp perfectly limits the entities affected in the stock game and provides a very straightforward and sensical way for custom mod authors to make custom entities in their mod compatible with this feature.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me it's not sensical to depend on some magic number that happens to work for stock game entities and has no other explanation. If what you want is to exclude entities with custom death animations or enormous one why not specify it directly? Body temp field was added with the intention to define color in IR scanners (e.g. railgun), not to determine how entity dies.
You also repeated that condition twice... Please follow DRY principle.

ep->death_anim_index = -1; // no death anim needed if entity is gibbing
}
}
}
};

CodeInjection entity_dying_frame_explode_injection{
0x0041EE4C,
[](auto& regs) {
rf::Entity* ep = regs.esi;
if (consider_gibs() && ep && ep->ai.explosive_last_damage && ep->info->body_temp >= 90.0f) {
regs.eip = 0x0041EE55;
}
},
};

ConsoleCommand2 gibs_cmd{
"gibs",
[]() {
g_game_config.gibs = !g_game_config.gibs;
g_game_config.save();
rf::console::print("Gibs are {}.", g_game_config.gibs ? "enabled" : "disabled");
},
"Make enemies and corpses explode into chunks from explosives (single player only)",
};

void entity_do_patch()
{
// Fix player being stuck to ground when jumping, especially when FPS is greater than 200
Expand Down Expand Up @@ -252,4 +352,13 @@ void entity_do_patch()

// Hide riot shield third person model if entity is hidden (e.g. in cutscenes)
entity_process_pre_hide_riot_shield_injection.install();

// Restore cut stock game feature for entities and corpses exploding into chunks
ai_init_ai_info_injection.install();
entity_blood_throw_gibs_hook.install();
entity_damage_explosive_death_injection.install();
entity_dying_frame_explode_injection.install();

// Commands
gibs_cmd.register_cmd();
}
3 changes: 3 additions & 0 deletions game_patch/rf/ai.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ namespace rf
float movement_radius;
float movement_height;
bool use_custom_attack_range;
#ifdef DASH_FACTION
bool explosive_last_damage;
GooberRF marked this conversation as resolved.
Show resolved Hide resolved
#endif
float custom_attack_range;
int ai_flags;
};
Expand Down
3 changes: 3 additions & 0 deletions game_patch/rf/entity.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,9 @@ namespace rf
static auto& entity_turn_weapon_on = addr_as_ref<void __cdecl(int entity_handle, int weapon_type, bool alt_fire)>(0x0041A870);
static auto& entity_turn_weapon_off = addr_as_ref<void __cdecl(int entity_handle, int weapon_type)>(0x0041AE70);
static auto& entity_restore_mesh = addr_as_ref<void(Entity *ep, const char *mesh_name)>(0x0042C570);
static auto& game_do_explosion = addr_as_ref<void(int vclip, GRoom* src_room, const Vector3* src_pos,
const Vector3* pos, float radius, float damage_scale, const Vector3* dir)>(0x00436490);
static auto& vclip_lookup = addr_as_ref<int(const char*)>(0x004C1D00);

static auto& entity_list = addr_as_ref<Entity>(0x005CB060);
static auto& local_player_entity = addr_as_ref<Entity*>(0x005CB054);
Expand Down
3 changes: 3 additions & 0 deletions game_patch/rf/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ namespace rf
static auto& obj_light_calculate = addr_as_ref<void()>(0x0048B0E0);

static auto& object_list = addr_as_ref<Object>(0x0073D880);

static auto& debris_spawn_from_object = addr_as_ref<void(Object* objp, const char* debris_v3d_filename,
int explode_index, int max_lifetime_ms, float velocity, rf::String* cust_snd_set)>(0x004133C0);
GooberRF marked this conversation as resolved.
Show resolved Hide resolved
}

template<>
Expand Down
2 changes: 2 additions & 0 deletions resources/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ add_packfile(dashfaction.vpp
images/bullet_icon_powercell_1.tga
images/bullet_icon_rocket_1.tga
images/bullet_icon_shotgun_1.tga
images/df_gore1.tga
images/enviro0_1.tga
images/enviro10_1.tga
images/enviro20_1.tga
Expand Down Expand Up @@ -87,6 +88,7 @@ add_packfile(dashfaction.vpp
meshes/Vat1.v3m
meshes/coffeesmokedtbl2.v3m
meshes/coffeesmokedtblAlt.v3m
meshes/df_meatchunks0.v3m
GooberRF marked this conversation as resolved.
Show resolved Hide resolved

standard_vs:${CMAKE_BINARY_DIR}/shaders/standard_vs.bin
character_vs:${CMAKE_BINARY_DIR}/shaders/character_vs.bin
Expand Down
Binary file added resources/images/df_gore1.tga
Binary file not shown.
1 change: 1 addition & 0 deletions resources/licensing-info.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Chris "Goober" Parsons:
* HUD textures for the big mode
* symbol glyphs in ttf fonts
* launcher header
* texture and mesh for gibs
GooberRF marked this conversation as resolved.
Show resolved Hide resolved

natalie <[email protected]>:
* code contributions
Expand Down
Binary file added resources/meshes/df_meatchunks0.v3m
Binary file not shown.