Skip to content

Commit

Permalink
Implement weapon model glow feature
Browse files Browse the repository at this point in the history
  • Loading branch information
danielkrupinski committed Dec 2, 2024
1 parent 61b3989 commit 2eead5c
Show file tree
Hide file tree
Showing 22 changed files with 408 additions and 108 deletions.
2 changes: 2 additions & 0 deletions Source/CS2/Classes/Entities/C_CSWeaponBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

#include <cstdint>

#include <CS2/Classes/SceneObjectUpdaterHandle_t.h>
#include "C_BaseModelEntity.h"

namespace cs2
{

struct C_CSWeaponBase : C_BaseModelEntity {
using m_iClip1 = std::int32_t;
using sceneObjectUpdaterHandle = SceneObjectUpdaterHandle_t*;
};

}
5 changes: 5 additions & 0 deletions Source/Endpoints.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ std::uint64_t PlayerPawn_sceneObjectUpdater_cpp(cs2::C_CSPlayerPawn* playerPawn,
return GlobalContext::instance().playerPawnSceneObjectUpdater(playerPawn, unknown, unknownBool);
}

std::uint64_t Weapon_sceneObjectUpdater_cpp(cs2::C_CSWeaponBase* weapon, void* unknown, bool unknownBool) noexcept
{
return GlobalContext::instance().weaponSceneObjectUpdater(weapon, unknown, unknownBool);
}

}
7 changes: 7 additions & 0 deletions Source/Entities/BaseEntity.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ class BaseEntity {
{
}

[[nodiscard]] EntityTypeInfo classify() const noexcept
{
if (entity)
return hookContext.entityClassifier().classifyEntity(hookContext.gameDependencies().entitiesVMTs, entity->vmt);
return {};
}

template <template <typename> typename T>
[[nodiscard]] decltype(auto) as() const noexcept
{
Expand Down
18 changes: 18 additions & 0 deletions Source/Entities/BaseWeapon.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class BaseWeapon {
{
}

using RawType = cs2::C_CSWeaponBase;

template <template <typename...> typename EntityType>
[[nodiscard]] bool is() const noexcept
{
Expand All @@ -34,7 +36,23 @@ class BaseWeapon {
return hookContext.clientPatternSearchResults().template get<OffsetToClipAmmo>().of(baseWeapon).toOptional();
}

[[nodiscard]] auto getSceneObjectUpdater() const noexcept
{
return reinterpret_cast<std::uint64_t(*)(cs2::C_CSWeaponBase*, void*, bool)>(sceneObjectUpdaterHandle() ? sceneObjectUpdaterHandle()->updaterFunction : nullptr);
}

void setSceneObjectUpdater(auto x) const noexcept
{
if (sceneObjectUpdaterHandle())
sceneObjectUpdaterHandle()->updaterFunction = reinterpret_cast<std::uint64_t(*)(void*, void*, bool)>(x);
}

private:
[[nodiscard]] auto sceneObjectUpdaterHandle() const noexcept
{
return hookContext.clientPatternSearchResults().template get<OffsetToWeaponSceneObjectUpdaterHandle>().of(baseWeapon).valueOr(nullptr);
}

HookContext& hookContext;
cs2::C_CSWeaponBase* baseWeapon;
};
6 changes: 3 additions & 3 deletions Source/FeatureHelpers/EntityClassifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <Utils/TypeIndex.h>

struct EntityTypeInfo {
std::uint8_t typeIndex;
std::uint8_t typeIndex{static_cast<std::uint8_t>(-1)};

[[nodiscard]] constexpr bool isWeapon() const noexcept
{
Expand Down Expand Up @@ -75,12 +75,12 @@ class EntityClassifier {
[[nodiscard]] EntityTypeInfo classifyEntity(const EntitiesVMTs& entitiesVMTs, const void* vmt) noexcept
{
if (vmt == nullptr)
return EntityTypeInfo{static_cast<std::uint8_t>(-1)};
return {};

const auto it = std::ranges::lower_bound(sortedVmtIndices, vmt, {}, [&entitiesVMTs](const auto index) { return entitiesVMTs.vmts[index]; });
if (it != sortedVmtIndices.end() && entitiesVMTs.vmts[*it] == vmt)
return EntityTypeInfo{*it};
return EntityTypeInfo{static_cast<std::uint8_t>(-1)};
return {};
}

private:
Expand Down
4 changes: 2 additions & 2 deletions Source/FeatureHelpers/RenderingHookEntityLoop.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ class RenderingHookEntityLoop {
{
auto&& baseEntity = hookContext.template make<BaseEntity>(static_cast<cs2::C_BaseEntity*>(&entity));

const auto entityTypeInfo = hookContext.entityClassifier().classifyEntity(hookContext.gameDependencies().entitiesVMTs, entity.vmt);
const auto entityTypeInfo = baseEntity.classify();

if (entityTypeInfo.template is<cs2::C_CSPlayerPawn>()) {
auto&& playerPawn = hookContext.template make<PlayerPawn>(static_cast<cs2::C_CSPlayerPawn*>(&entity));
playerInformationThroughWalls.drawPlayerInformation(playerPawn);
hookContext.template make<ModelGlow>().updateSceneObjectUpdaterHook(playerPawn);
}

if (entityTypeInfo.isModelEntity())
hookContext.template make<OutlineGlow>().applyGlowToEntity(entityTypeInfo, baseEntity.template as<BaseModelEntity>());
hookContext.template make<ModelGlow>().updateSceneObjectUpdaterHook(entityTypeInfo, baseEntity);
}

HookContext& hookContext;
Expand Down
126 changes: 30 additions & 96 deletions Source/Features/Visuals/ModelGlow/ModelGlow.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

#include "ModelGlowState.h"

extern "C" std::uint64_t PlayerPawn_sceneObjectUpdater_asm(cs2::C_CSPlayerPawn* playerPawn, void* unknown, bool unknownBool) noexcept;
#include <Entities/PlayerPawn.h>

#include "PlayerModelGlow/PlayerModelGlow.h"
#include "WeaponModelGlow/WeaponModelGlow.h"

template <typename HookContext>
class ModelGlow {
Expand All @@ -15,133 +18,64 @@ class ModelGlow {
{
}

void updateSceneObjectUpdaterHook(auto&& playerPawn) const noexcept
void updateSceneObjectUpdaterHook(EntityTypeInfo entityTypeInfo, auto&& entity) const noexcept
{
if (!shouldUpdateSceneObjectUpdaterHook())
return;

if (shouldGlowPlayerModel(playerPawn))
hookPlayerSceneObjectUpdater(playerPawn);
else
unhookPlayerSceneObjectUpdater(playerPawn);
if (entityTypeInfo.is<cs2::C_CSPlayerPawn>())
hookContext.template make<PlayerModelGlow>().updateSceneObjectUpdaterHook(entity.template as<PlayerPawn>());
else if (entityTypeInfo.isWeapon() && !entityTypeInfo.is<cs2::C_C4>())
hookContext.template make<WeaponModelGlow>().updateSceneObjectUpdaterHook(entity.template as<BaseWeapon>());
}

void applyModelGlow(auto&& playerPawn) const noexcept
void applyPlayerModelGlow(auto&& playerPawn) const noexcept
{
if (shouldRun() && shouldGlowPlayerModel(playerPawn))
playerPawn.baseEntity().applySpawnProtectionEffectRecursively(getColor(playerPawn));
if (shouldRun())
hookContext.template make<PlayerModelGlow>().applyModelGlow(playerPawn);
}

void applyWeaponModelGlow(auto&& weapon) const noexcept
{
if (shouldRun())
hookContext.template make<WeaponModelGlow>().applyModelGlow(weapon);
}

void onEntityListTraversed() const noexcept
{
if (state().masterSwitch == ModelGlowState::State::Disabling)
state().masterSwitch = ModelGlowState::State::Disabled;

if (state().playerModelGlow == ModelGlowState::State::Disabling)
state().playerModelGlow = ModelGlowState::State::Disabled;
hookContext.template make<PlayerModelGlow>().onEntityListTraversed();
hookContext.template make<WeaponModelGlow>().onEntityListTraversed();
}

void onUnload(auto&& playerPawn) const noexcept
void onUnload(EntityTypeInfo entityTypeInfo, auto&& entity) const noexcept
{
if (state().masterSwitch != ModelGlowState::State::Disabled && state().playerModelGlow != ModelGlowState::State::Disabled)
unhookPlayerSceneObjectUpdater(playerPawn);
if (state().masterSwitch == ModelGlowState::State::Disabled)
return;

if (entityTypeInfo.is<cs2::C_CSPlayerPawn>())
hookContext.template make<PlayerModelGlow>().onUnload(entity.template as<PlayerPawn>());
else if (entityTypeInfo.isWeapon() && !entityTypeInfo.is<cs2::C_C4>())
hookContext.template make<WeaponModelGlow>().onUnload(entity.template as<BaseWeapon>());
}

private:
[[nodiscard]] bool shouldUpdateSceneObjectUpdaterHook() const noexcept
{
return state().masterSwitch != ModelGlowState::State::Disabled && state().playerModelGlow != ModelGlowState::State::Disabled;
return state().masterSwitch != ModelGlowState::State::Disabled;
}

[[nodiscard]] bool shouldRun() const noexcept
{
return state().masterSwitch == ModelGlowState::State::Enabled && state().playerModelGlow == ModelGlowState::State::Enabled;
}

void hookPlayerSceneObjectUpdater(auto&& playerPawn) const noexcept
{
if (!hasSceneObjectUpdaterHooked(playerPawn)) {
storeOriginalSceneObjectUpdater(playerPawn);
hookSceneObjectUpdater(playerPawn);
}
}

void unhookPlayerSceneObjectUpdater(auto&& playerPawn) const noexcept
{
if (hasSceneObjectUpdaterHooked(playerPawn))
playerPawn.setSceneObjectUpdater(state().originalPlayerPawnSceneObjectUpdater);
}

void storeOriginalSceneObjectUpdater(auto&& playerPawn) const noexcept
{
if (state().originalPlayerPawnSceneObjectUpdater == nullptr)
state().originalPlayerPawnSceneObjectUpdater = playerPawn.getSceneObjectUpdater();
}

[[nodiscard]] bool hasSceneObjectUpdaterHooked(auto&& playerPawn) const noexcept
{
return playerPawn.getSceneObjectUpdater() == &PlayerPawn_sceneObjectUpdater_asm;
}

void hookSceneObjectUpdater(auto&& playerPawn) const noexcept
{
playerPawn.setSceneObjectUpdater(&PlayerPawn_sceneObjectUpdater_asm);
}

[[nodiscard]] bool shouldGlowPlayerModel(auto&& playerPawn) const noexcept
{
return playerPawn.isAlive().value_or(true)
&& playerPawn.health().greaterThan(0).valueOr(true)
&& !playerPawn.isControlledByLocalPlayer()
&& playerPawn.isTTorCT()
&& (!state().showOnlyEnemies || playerPawn.isEnemy().value_or(true));
return state().masterSwitch == ModelGlowState::State::Enabled;
}

[[nodiscard]] auto& state() const noexcept
{
return hookContext.featuresStates().visualFeaturesStates.modelGlowState;
}

[[nodiscard]] cs2::Color getColor(auto&& playerPawn) const noexcept
{
if (playerPawn.hasImmunity().valueOr(false))
return getColorHalfSaturated(playerPawn);
return getColorSaturated(playerPawn);
}

[[nodiscard]] cs2::Color getColorSaturated(auto&& playerPawn) const noexcept
{
if (state().playerModelGlowColorType == PlayerModelGlowColorType::HealthBased)
return playerPawn.healthColor(1.0f).value_or(cs2::kColorWhite);

if (state().playerModelGlowColorType == PlayerModelGlowColorType::PlayerOrTeamColor) {
if (const auto playerColor = playerPawn.playerController().getPlayerColorSaturated(); playerColor.has_value())
return *playerColor;
}

switch (playerPawn.teamNumber()) {
case TeamNumber::TT: return cs2::Color{255, 179, 0};
case TeamNumber::CT: return cs2::Color{0, 127, 255};
default: return cs2::kColorWhite;
}
}

[[nodiscard]] cs2::Color getColorHalfSaturated(auto&& playerPawn) const noexcept
{
if (state().playerModelGlowColorType == PlayerModelGlowColorType::HealthBased)
return playerPawn.healthColor(0.5f).value_or(cs2::kColorWhite);

if (state().playerModelGlowColorType == PlayerModelGlowColorType::PlayerOrTeamColor) {
if (const auto playerColor = playerPawn.playerController().getPlayerColorHalfSaturated(); playerColor.has_value())
return *playerColor;
}

switch (playerPawn.teamNumber()) {
case TeamNumber::TT: return cs2::Color{255, 217, 128};
case TeamNumber::CT: return cs2::Color{128, 191, 255};
default: return cs2::kColorWhite;
}
}

HookContext& hookContext;
};
2 changes: 2 additions & 0 deletions Source/Features/Visuals/ModelGlow/ModelGlowState.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ struct ModelGlowState {
State playerModelGlow{State::Enabled};
PlayerModelGlowColorType playerModelGlowColorType{PlayerModelGlowColorType::PlayerOrTeamColor};
bool showOnlyEnemies{true};
State weaponModelGlow{State::Enabled};

std::uint64_t(*originalPlayerPawnSceneObjectUpdater)(cs2::C_CSPlayerPawn* playerPawn, void*, bool){nullptr};
std::uint64_t(*originalWeaponSceneObjectUpdater)(cs2::C_CSWeaponBase* baseWeapon, void*, bool){nullptr};
};
13 changes: 13 additions & 0 deletions Source/Features/Visuals/ModelGlow/ModelGlowToggle.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ class ModelGlowToggle {
}
}

void updateWeaponModelGlowToggle(char option) noexcept
{
switch (option) {
case '0':
state().weaponModelGlow = ModelGlowState::State::Enabled;
break;
case '1':
if (state().weaponModelGlow == ModelGlowState::State::Enabled)
state().weaponModelGlow = ModelGlowState::State::Disabling;
break;
}
}

private:
[[nodiscard]] auto& state() const noexcept
{
Expand Down
Loading

0 comments on commit 2eead5c

Please sign in to comment.