This comprehensive guide will walk you through porting Lua scripts (like uber2.lua) into the Amalgam C++ codebase. This guide is based on the successful porting of uber2.lua and covers all the quirks, fixes, and SDK integration points you'll encounter.
- Prerequisites
- Understanding the Amalgam Architecture
- Initial Analysis
- Setting Up the Feature Structure
- Common API Mappings
- SDK Integration Points
- Drawing and Rendering
- Project Integration
- Common Issues and Fixes
- Testing and Debugging
Before starting, ensure you have:
- Basic understanding of C++ and the Amalgam codebase structure
- The Lua script you want to port
- Visual Studio with the Amalgam solution loaded
- Understanding of TF2 game mechanics related to your script
Amalgam/
├── src/
│ ├── Features/ # All cheat features
│ │ ├── Visuals/ # Visual features (ESP, Chams, etc.)
│ │ ├── Aimbot/ # Aimbot features
│ │ ├── Misc/ # Miscellaneous features
│ │ └── ...
│ ├── SDK/ # TF2 SDK definitions and helpers
│ │ ├── Definitions/ # Class definitions, interfaces
│ │ ├── Helpers/ # Drawing, entities, utilities
│ │ └── Vars.h # Configuration variables
│ ├── Hooks/ # Game function hooks
│ └── Utils/ # Utility classes
All features follow this pattern:
- Header file (
FeatureName.h) - Class definition withADD_FEATUREmacro - Implementation file (
FeatureName.cpp) - Feature logic - Hook integration - Added to appropriate hook (usually
IEngineVGui_Paint.cpp) - Project file integration - Added to
Amalgam.vcxproj
Before porting, analyze:
- What game data does it access? (entities, netvars, etc.)
- What does it display? (text, boxes, colors)
- When does it run? (every frame, on events, etc.)
- What are the key algorithms? (calculations, state tracking)
Map Lua concepts to TF2 SDK equivalents:
- Entities →
CBaseEntity,CTFPlayer,CTFWeaponBase - Classes →
TF_CLASS_MEDIC,TF_CLASS_SOLDIER, etc. - Teams →
TF_TEAM_RED,TF_TEAM_BLUE - Weapons → Weapon IDs, netvars, properties
Determine where your feature belongs:
- Visuals - ESP, trackers, overlays, HUD elements
- Aimbot - Targeting, auto-actions
- Misc - Game modifications, utilities
src/Features/Visuals/YourFeature/
├── YourFeature.h
└── YourFeature.cpp
#pragma once
#include "../../../SDK/SDK.h"
#include <map> // Add STL includes as needed
// Data structures for your feature
struct YourDataStruct
{
std::string SomeData;
float SomeValue;
bool SomeFlag;
};
class CYourFeature
{
private:
// Configuration constants
static constexpr bool ENABLE_FEATURE = true;
static constexpr float SOME_THRESHOLD = 50.0f;
// State tracking variables
std::map<int, float> m_StateTracker;
std::vector<YourDataStruct> m_DataCache;
// Helper functions
void ProcessData();
void DrawElements();
public:
void Draw(); // Main entry point
};
ADD_FEATURE(CYourFeature, YourFeature)#include "YourFeature.h"
#include "../../../SDK/SDK.h"
void CYourFeature::Draw()
{
// Early exits
if (I::EngineVGui->IsGameUIVisible())
return;
auto pLocal = H::Entities.GetLocal();
if (!pLocal)
return;
// Your feature logic here
ProcessData();
DrawElements();
}
void CYourFeature::ProcessData()
{
// Process game data
}
void CYourFeature::DrawElements()
{
// Draw visual elements
}| Lua Concept | C++ Equivalent | Example |
|---|---|---|
entities.FindByClass("CTFPlayer") |
H::Entities.GetGroup(EGroupType::PLAYERS_ALL) |
Get all players |
entity:GetClassNum() |
pPlayer->m_iClass() |
Get player class |
entity:GetTeamNumber() |
pEntity->m_iTeamNum() |
Get team number |
entity:IsAlive() |
pPlayer->IsAlive() |
Check if alive |
entity:GetIndex() |
pEntity->entindex() |
Get entity index |
| Lua Concept | C++ Equivalent | Example |
|---|---|---|
entity:GetWeaponFromSlot(1) |
pPlayer->GetWeaponFromSlot(SLOT_SECONDARY) |
Get secondary weapon |
weapon:GetItemDefIndex() |
pWeapon->m_iItemDefinitionIndex() |
Get item definition index |
medigun.m_flChargeLevel |
pMedigun->As<CWeaponMedigun>()->m_flChargeLevel() |
Get uber charge |
| Lua Constant | C++ Constant | Notes |
|---|---|---|
CLASS_MEDIC |
TF_CLASS_MEDIC |
Player classes |
| Team numbers (2, 3) | TF_TEAM_RED, TF_TEAM_BLUE |
Team constants |
SLOT_SECONDARY |
SLOT_SECONDARY |
Weapon slots |
The main SDK header includes everything:
#include "../../../SDK/SDK.h"// Get local player
auto pLocal = H::Entities.GetLocal();
// Get all players
for (auto pEntity : H::Entities.GetGroup(EGroupType::PLAYERS_ALL))
{
auto pPlayer = pEntity->As<CTFPlayer>();
}// Colors
Color_t red = {255, 0, 0, 255};
// Basic shapes
H::Draw.FillRect(x, y, width, height, color);
H::Draw.LineRect(x, y, width, height, color);
// Text rendering
H::Draw.String(H::Fonts.GetFont(FONT_ESP), x, y, color, ALIGN_CENTER, "Text");// Game globals
float currentTime = I::GlobalVars->curtime;
// Screen size
int screenW, screenH;
I::MatSystemSurface->GetScreenSize(screenW, screenH);
// UI state
if (I::EngineVGui->IsGameUIVisible())
return;CBaseEntity
├── CBasePlayer
│ └── CTFPlayer // Players
├── CBaseCombatWeapon
│ └── CTFWeaponBase // Weapons
│ └── CWeaponMedigun // Medigun specifically
└── CBaseObject // Buildings
// Access netvars using the () operator
int teamNum = pEntity->m_iTeamNum();
float charge = pMedigun->m_flChargeLevel();
bool isAlive = pPlayer->IsAlive(); // Some have helper methodsAvailable fonts:
FONT_ESP // Standard ESP font
FONT_INDICATORS // Smaller indicator fontALIGN_TOPLEFT ALIGN_TOP ALIGN_TOPRIGHT
ALIGN_LEFT ALIGN_CENTER ALIGN_RIGHT
ALIGN_BOTTOMLEFT ALIGN_BOTTOM ALIGN_BOTTOMRIGHT// RGBA format
Color_t color = {red, green, blue, alpha}; // 0-255 values
// Predefined colors (check Vars::Colors for more)
Vars::Colors::Health.Value
Vars::Colors::TeamRed.Value
Vars::Colors::TeamBlu.Valuevoid DrawInfoBox(int x, int y, const std::vector<std::string>& lines)
{
// Background
int boxHeight = 25 + (lines.size() * 15);
H::Draw.FillRect(x-5, y-5, 200, boxHeight, {0, 0, 0, 100});
// Text lines
for (size_t i = 0; i < lines.size(); ++i)
{
int yPos = y + (i * 15);
H::Draw.String(H::Fonts.GetFont(FONT_ESP), x, yPos,
{255, 255, 255, 255}, ALIGN_TOPLEFT, lines[i].c_str());
}
}Edit Amalgam.vcxproj:
Add source file:
<ClCompile Include="src\Features\Visuals\YourFeature\YourFeature.cpp" />Add header file:
<ClInclude Include="src\Features\Visuals\YourFeature\YourFeature.h" />Add to src/Hooks/IEngineVGui_Paint.cpp:
Include header:
#include "../Features/Visuals/YourFeature/YourFeature.h"Add to drawing loop:
if (auto pLocal = H::Entities.GetLocal())
{
// ... other features ...
F::YourFeature.Draw();
// ... more features ...
}The ADD_FEATURE macro automatically registers your feature. The naming convention:
- Class:
CYourFeature - Instance:
F::YourFeature - Macro:
ADD_FEATURE(CYourFeature, YourFeature)
Problem: std::map, std::vector not recognized
Solution: Add includes to header
#include <map>
#include <vector>
#include <string>Problem: GetWeaponID() returns weapon class ID, not item definition index
Solution: Use netvar for item definition index to distinguish weapon types
// Wrong - All mediguns return same ID (50)
int weaponID = pWeapon->GetWeaponID();
// Correct - Distinguishes between weapon types (35=KRITZ, 411=QUICKFIX, etc.)
int itemDefIndex = pWeapon->m_iItemDefinitionIndex();Problem: GetName() method doesn't exist on entities
Solution: Use PlayerInfo and PlayerUtils for proper name access
// Wrong
std::string name = pPlayer->GetName();
// Correct
PlayerInfo_t pi{};
if (I::EngineClient->GetPlayerInfo(pPlayer->entindex(), &pi))
std::string name = F::PlayerUtils.GetPlayerName(pPlayer->entindex(), pi.name);
else
std::string name = "Player " + std::to_string(pPlayer->entindex());
// Don't forget to include PlayerUtils
#include "../../Players/PlayerUtils.h"Problem: FillRectangle(), FONT_MENU not found
Solution: Use correct Amalgam drawing API
// Wrong
H::Draw.FillRectangle(...)
// Correct
H::Draw.FillRect(...)
// Wrong
FONT_MENU
// Correct
H::Fonts.GetFont(FONT_ESP)Problem: CLASS_MEDIC, team numbers as raw integers
Solution: Use TF2 constants
// Wrong
if (playerClass == 5) // Raw number
// Correct
if (pPlayer->m_iClass() == TF_CLASS_MEDIC)
// Wrong
if (team == 2)
// Correct
if (pEntity->m_iTeamNum() == TF_TEAM_RED)Problem: Direct member access failing Solution: Use netvar accessor methods
// Wrong (direct access)
pMedigun->m_flChargeLevel
// Correct (netvar method)
pMedigun->m_flChargeLevel()Problem: Method not available on base entity Solution: Cast to specific type
// Get entity as specific type
auto pPlayer = pEntity->As<CTFPlayer>();
auto pMedigun = pWeapon->As<CWeaponMedigun>();Problem: Need to identify what values are actually being returned Solution: Use console output for debugging
// Add temporary debug output to see actual values
I::CVar->ConsolePrintf("DEBUG: Weapon ID = %d\n", pWeapon->GetWeaponID());
I::CVar->ConsolePrintf("DEBUG: Item Def Index = %d\n", pWeapon->m_iItemDefinitionIndex());
// View results in game console (~ key)
// Remove debug output once you identify the correct valuesclass CYourFeature
{
private:
std::map<int, float> m_PreviousValues;
std::map<int, std::string> m_EntityStates;
public:
void UpdateState(int entityIndex, float newValue)
{
float oldValue = m_PreviousValues[entityIndex];
if (newValue != oldValue)
{
// Handle state change
m_EntityStates[entityIndex] = "CHANGED";
}
m_PreviousValues[entityIndex] = newValue;
}
};// Timer-based calculations
float timeDelta = I::GlobalVars->curtime - m_LastUpdateTime;
if (timeDelta > UPDATE_INTERVAL)
{
// Perform expensive calculations
m_LastUpdateTime = I::GlobalVars->curtime;
}
// Rate calculations (like uber build rates)
float buildRate = baseRate * (isOverhealed ? OVERHEAL_PENALTY : 1.0f);
float projectedUber = currentUber + (buildRate * timeDelta);void CYourFeature::ProcessEntities()
{
std::vector<EntityData> teamData[4]; // Support up to 4 teams
for (auto pEntity : H::Entities.GetGroup(EGroupType::PLAYERS_ALL))
{
auto pPlayer = pEntity->As<CTFPlayer>();
if (!pPlayer || !ShouldTrackEntity(pPlayer))
continue;
EntityData data;
data.Entity = pPlayer;
data.Value = GetRelevantValue(pPlayer);
int team = pPlayer->m_iTeamNum();
if (team >= 0 && team < 4)
teamData[team].push_back(data);
}
// Process team-specific data
AnalyzeTeamData(teamData[TF_TEAM_RED], teamData[TF_TEAM_BLUE]);
}// Console output for debugging
#ifdef _DEBUG
I::CVar->ConsolePrintf("Debug: Value = %f\n", someValue);
#endifvoid DrawDebugInfo()
{
if (!Vars::Debug::Info.Value)
return;
H::Draw.String(H::Fonts.GetFont(FONT_ESP), 10, 10, {255, 255, 0, 255},
ALIGN_TOPLEFT, std::format("Debug: {}", debugValue).c_str());
}bool IsValidEntity(CBaseEntity* pEntity)
{
return pEntity && !pEntity->IsDormant() && pEntity->IsAlive();
}
// Always check before accessing
if (IsValidEntity(pPlayer))
{
// Safe to access pPlayer methods
}- Cache expensive calculations
- Limit entity iteration frequency
- Use early returns to avoid unnecessary processing
- Only update when values actually change
- Keep drawing and logic separate
- Use const for read-only data
- Follow Amalgam naming conventions
- Comment complex algorithms
- Use static constexpr for compile-time constants
- Make features easily configurable
- Follow existing Vars:: patterns if adding menu options
- Always check for null pointers
- Validate entity states before access
- Handle edge cases (no entities, game state changes)
Here's how the uber2.lua was successfully ported following this guide:
- Analysis: Identified it tracked medic uber charges and displayed advantage calculations
- Structure: Created
UberTracker.h/cppinFeatures/Visuals/ - API Mapping:
entities.FindByClass("CTFPlayer")→H::Entities.GetGroup(EGroupType::PLAYERS_ALL)entity:GetClassNum() == CLASS_MEDIC→pPlayer->m_iClass() == TF_CLASS_MEDICmedigun.m_flChargeLevel→pMedigun->m_flChargeLevel()
- Drawing Integration: Used
H::Draw.FillRect()andH::Draw.String() - Project Integration: Added to
.vcxprojandIEngineVGui_Paint.cpp - Fixes Applied:
- Added
#include <map>for STL containers - Changed
GetWeaponID()→m_iItemDefinitionIndex()for proper weapon type detection - Added PlayerUtils include for proper name access
- Used correct TF2 constants
- Added
The result was a perfectly integrated feature that provided all the original Lua functionality natively in C++.
Following this guide, the esp.lua health bar system was successfully ported as a standalone always-on feature:
- Purpose: Health bar ESP with overheal support for TF2 players
- Key Features: Medic mode, visibility checking, distance filtering, health-based alpha
- Category: Visuals feature (always-on like UberTracker)
// Header: src/Features/Visuals/HealthBarESP/HealthBarESP.h
class CHealthBarESP
{
private:
static constexpr int ALPHA = 70;
static constexpr int BAR_HEIGHT = 10;
static constexpr int BAR_WIDTH = 90;
static constexpr float MAX_DISTANCE_SQR = 3500.0f * 3500.0f;
static constexpr bool MEDIC_MODE = true;
static constexpr Color_t OVERHEAL_COLOR = {71, 166, 255, 255};
public:
void Draw();
};
ADD_FEATURE(CHealthBarESP, HealthBarESP);entities.FindByClass("CTFPlayer")→H::Entities.GetGroup(EGroupType::PLAYERS_ALL)entity:GetClassNum() == CLASS_MEDIC→pPlayer->m_iClass() == TF_CLASS_MEDICentity:InCond(CLOAK_COND)→pPlayer->InCond(TF_COND_STEALTHED)client.WorldToScreen()→SDK::W2S()draw.FilledRect()→H::Draw.FillRect()
- Trace Structure: Fixed
trace.flFraction→trace.fraction - Visibility Improvements: Changed
MASK_SHOT→MASK_VISIBLEto reduce flickering - Alpha Logic: Corrected health-based visibility formula for proper brightening on damage
- No Wobble Mode: Removed dynamic bounding box calculations, kept fixed positioning only
- Project Files: Added to
Amalgam.vcxproj(both .h and .cpp) - Hook Integration: Added include and
F::HealthBarESP.Draw()toIEngineVGui_Paint.cpp - Always-On Design: No Vars checks, follows UberTracker pattern
- Always-enabled health bars with no menu toggles
- Medic mode: shows teammates when playing medic, enemies otherwise
- Health-responsive visibility: dimmer when healthy, brighter when wounded
- Fixed 90px width bars positioned 30px below player center
- No flickering during combat, stable visibility checking
Another successful port following this guide was the critheals.lua medic assistance system:
- Purpose: Visual indicators for crit heal eligibility and uber build rate warnings
- Key Features: Triangle indicators, damage tracking, uber build penalty warnings
- Category: Medic-specific visuals feature (always-on when playing medic)
// Always-on medic assistance with event integration
class CCritHeals
{
private:
static constexpr float CRIT_HEAL_TIME = 10.0f;
static constexpr float UBER_PENALTY_THRESHOLD = 1.425f;
std::unordered_map<int, float> m_LastDamageTimes;
public:
void Draw();
void OnPlayerHurt(int victimIndex); // Event-driven damage tracking
};- Event system integration: Added
player_hurtevent handling to track damage times - Class-specific activation: Only runs when playing medic (
pLocal->m_iClass() == TF_CLASS_MEDIC) - Visual positioning: Aligned uber warnings with UberTracker display area
- Entity casting: Proper
CBaseEntity→CTFPlayer/CTFWeaponBasecasting
- Entity type safety: Fixed
CBaseEntity->IsAlive()→CTFPlayer->IsAlive()casting - Event integration: Added to existing
player_hurtevent dispatcher inEvents.cpp - Visual consistency: Positioned warnings to align with UberTracker layout
- Visibility improvements: Changed
MASK_SHOT_HULL→MASK_VISIBLEto reduce flickering - Performance optimization: Used damage event tracking instead of continuous health monitoring
- Triangle indicators above players eligible for crit heals (10+ seconds since damage)
- "Reduced Uber Build Rate!" warning when healing overhealed players (142.5%+ health)
- Integrated with existing event system for efficient damage tracking
- Seamless visual integration with UberTracker positioning
- No flickering during combat, stable triangle indicators
This example demonstrates event-driven features and proper medic-specific functionality integration.
Another successful integration following this guide was the trails.lua movement tracking system:
- Purpose: Visual movement trails for enemy players showing their recent paths
- Key Features: Colored trails, visibility-based display, fade-out effects, distance filtering
- Category: Always-on visuals feature for enemy tracking
// Always-on enemy trail tracking with performance optimizations
class CPlayerTrails
{
private:
static constexpr int TRAIL_LENGTH = 19;
static constexpr float FADE_TIME = 7.0f;
static constexpr float MAX_TRAIL_DISTANCE = 1850.0f;
std::unordered_map<std::string, PlayerTrailData> m_PlayerData;
public:
void Draw(); // Main drawing loop with visibility and distance checks
};- Entity iteration: Used
H::Entities.GetGroup(EGroupType::PLAYERS_ALL)for efficient player access - Visibility system: Integrated with existing trace filtering using
MASK_VISIBLE - Color generation: Unique trail colors per player using SteamID-based hashing
- Performance optimization: Cached visibility checks, distance filtering, cleanup routines
- API corrections: Fixed
trace.entity→trace.m_pEntfor proper entity access - Type safety: Corrected
W2Sfunction calls fromVec2→Vec3parameters - Lifestate tracking: Simplified respawn detection using
IsAlive()instead of raw lifestate values - ViewOffset access: Fixed player view offset access using proper casting
pLocal->As<CTFPlayer>() - Color narrowing: Used
byte()cast for proper RGBA color component conversion
- Always-enabled colored trails behind enemy players only
- 19-position trails with 7-second fade time and 1850-unit distance limit
- Visibility-responsive display: trails show during and briefly after losing sight
- Performance-optimized with cached checks and periodic cleanup
- Unique colors per player for easy identification during combat
- Seamless integration with existing visual systems
This example demonstrates comprehensive entity tracking, performance optimization patterns, and proper visual effect integration.
The final integration following this guide was the sticky.lua stickybomb ESP system:
- Purpose: Always-on ESP for enemy stickybombs with visibility-based coloring and performance optimization
- Key Features: 2D/3D box ESP, enemy-only filtering, distance filtering, visibility checks, caching systems
- Category: Always-on visual ESP feature that leverages existing native systems
// Stickybomb ESP with native system integration
class CStickyESP
{
private:
static constexpr bool ENEMY_ONLY = true;
static constexpr float MAX_DISTANCE = 2800.0f;
static constexpr Color_t BOX_COLOR_VISIBLE = {0, 255, 0, 255}; // Green
static constexpr Color_t BOX_COLOR_INVISIBLE = {255, 0, 0, 255}; // Red
public:
void Draw(); // Mirrors Lua stickybomb_esp() function exactly
};- Native entity groups: Used
H::Entities.GetGroup(EGroupType::WORLD_PROJECTILES)instead ofentities.FindByClass() - Existing chams system: Leveraged native projectile chams (no custom implementation needed)
- Native drawing API: Used
H::Draw.Line()instead ofdraw.Line()for box rendering - SDK trace system: Integrated with existing
SDK::Trace()andMASK_SHOTconstants
- Entity filtering: Used
pSticky->m_iType() == 1netvar instead ofGetPropInt("m_iType") - Class identification: Used
ETFClassID::CTFGrenadePipebombProjectileinstead of string class names - Distance calculation: Used squared distance comparison for performance (avoiding sqrt)
- API consistency: Used
pEntity->m_iTeamNum()instead ofGetTeamNumber() - Native integration: No custom chams - existing projectile chams system handles stickybombs automatically
- Always-enabled ESP for enemy stickybombs only (2800 unit range)
- 2D box ESP (20px boxes) with visibility-based green/red coloring
- Optional 3D bounding box ESP using entity bounds calculation
- Direct processing every frame (no caching) for stable, flicker-free rendering
- Seamless integration with existing projectile chams system (user-controllable)
- Exact mirror of Lua functionality using native drawing and entity systems
- Clean, minimal implementation focused purely on Lua script behavior
- Avoid over-optimization: Initial implementation had complex caching systems that caused flickering
- Mirror Lua exactly: Best results came from direct 1:1 translation of Lua logic
- Follow existing patterns: HealthBarESP's direct processing approach worked perfectly
- Keep it simple: Removed all caching, timers, and performance optimizations for stable rendering
- Native drawing: Using
H::Draw.Line()directly matches Luadraw.Line()behavior
This example demonstrates that the best ports often prioritize exact behavior over performance optimization.
The latest successful integration was the focusfire.lua multi-targeting detection system:
- Purpose: Visual tracking system for enemies being targeted by multiple teammates
- Key Features: Event-driven damage tracking, visual box ESP, automatic cleanup, team coordination assistance
- Category: Always-on tactical visuals feature for team coordination
// Multi-targeting tracker with event integration
class CFocusFire
{
private:
static constexpr int MIN_ATTACKERS = 2;
static constexpr float TRACKER_TIME_WINDOW = 4.5f;
static constexpr Color_t BOX_COLOR = {255, 0, 0, 190}; // Red corners
std::unordered_map<int, TargetInfo> m_TargetData;
public:
void Draw(); // Box ESP drawing
void OnPlayerHurt(int victimIndex, int attackerIndex); // Event tracking
};- Event-driven tracking: Connected to
player_hurtevents inEvents.cppfor real-time damage tracking - Visual coordination: Red corner boxes highlight enemies being focused by 2+ teammates
- Performance optimization: Time-windowed tracking (4.5s) with automatic cleanup routines
- Team filtering: Only highlights enemy players, ignores friendly fire
- Event handling:
callbacks.Register("FireGameEvent")→Events.cppintegration - Multi-targeting logic:
targetData[entIndex].attackers→std::unordered_map<int, TargetInfo> - Box drawing:
draw.Line()corner system →H::Draw.Line()with thickness - Entity filtering:
entity:GetTeamNumber()→pPlayer->m_iTeamNum()
- Project structure: Added to
Features/Visuals/FocusFire/following established patterns - Event integration: Added
OnPlayerHurtcall to existingplayer_hurtevent handler - Drawing integration: Added
F::FocusFire.Draw()to main drawing loop inIEngineVGui_Paint.cpp - Always-on design: No configuration options, automatic activation like UberTracker
- Always-enabled focus fire detection for tactical team coordination
- Red corner boxes appear around enemies being shot by 2+ teammates within 4.5 seconds
- Event-driven performance with automatic target cleanup and memory management
- Seamless integration with existing visual systems and drawing pipeline
- Real-time tactical awareness for coordinated team pushes
- Event integration: Player damage events provide reliable real-time tracking data
- Time-windowed tracking: 4.5-second windows effectively capture coordinated attacks
- Visual clarity: Red corner boxes provide clear focus target indication without screen clutter
- Memory management: Automatic cleanup prevents memory leaks during long gameplay sessions
- Team coordination: Visual feedback enhances team tactics without being distracting
This example demonstrates successful event-driven feature integration with real-time tactical applications.
The most recent integration was the sentryline.lua sentry gun ESP and targeting system:
- Purpose: Advanced sentry gun ESP with aim line visualization and targeting detection
- Key Features: Corner-style boxes, aim trajectory lines, targeting detection, special chams for threats
- Category: Always-on tactical ESP feature with comprehensive configuration options
// Advanced sentry ESP with full menu configuration
class CSentryESP
{
private:
std::unordered_set<int> m_SentriesTargetingLocal;
std::unordered_map<int, bool> m_mEntities; // Chams tracking
public:
void Draw(); // Main ESP rendering
void UpdateChamsEntities(); // Chams entity population
};- Entity iteration: Used
H::Entities.GetGroup(EGroupType::BUILDINGS_ALL)for building access - Bone matrix analysis: Real-time sentry muzzle position and aim angle calculation
- Chams integration: Followed StickyESP pattern with
UpdateChamsEntities()andm_mEntitiesmap - Menu system: Complete configuration with 14 customizable options
- Entity access:
entities.FindByClass("CObjectSentrygun")→EGroupType::BUILDINGS_ALLfiltering - Bone matrices:
SetupBones()withmatrix3x4 aBones[MAXSTUDIOBONES]for muzzle positioning - Target detection:
pSentry->m_hEnemy().Get()for real-time target tracking - Chams system:
F::SentryESP.m_mEntities[entityIndex]pattern following StickyESP
- Entity alive check: Removed
pEntity->IsAlive()(not available onCBaseEntity) - buildings useIsDormant()andm_bBuilding()checks - Matrix type correction: Fixed
matrix3x4_t→matrix3x4for proper bone matrix handling - Chams integration: Replaced
ShouldChams()method withUpdateChamsEntities()andm_mEntitiesmap pattern - Entity group access: Fixed
EGroupType::WORLD_BUILDINGS→EGroupType::BUILDINGS_ALL - Math functions: Used
Math::VectorAngles()andMath::AngleVectors()instead ofSDK::
- Main toggle: Added to
Vars::Competitive::Features::SentryESPin main features list - Configuration section: Added complete "Sentry ESP" submenu with all 14 options
- Menu UI: Added toggles, sliders, and color pickers to
Menu.cppfollowing established patterns - Variable definitions: Added comprehensive
SUBNAMESPACE_BEGIN(SentryESP)section inVars.h
- Always-enabled sentry gun ESP with real-time targeting detection
- Corner-style boxes with level indicators (M for mini, 1-3 for upgrade levels)
- Aim line visualization showing sentry firing trajectories (configurable length)
- Color-coded threat system: green (safe), red (targeting), grey (hidden)
- Special chams highlighting for sentries actively targeting the player
- Complete menu configuration with 14 customizable options
- Seamless integration with existing building chams and ESP systems
- Chams patterns: StickyESP/FocusFire pattern with
UpdateChamsEntities()andm_mEntitiesmap is required for working chams - Entity type handling: Buildings and players have different API patterns - always check existing implementations
- Menu integration: Both main toggle AND detailed configuration section required for proper menu visibility
- Bone matrix analysis: Real-time bone matrix calculations enable precise targeting detection beyond basic entity relationships
- API validation: Always verify method availability on specific entity types (
IsAlive()only exists onCTFPlayer, notCBaseEntity)
This example demonstrates the most comprehensive ESP feature integration, showcasing advanced targeting detection, complete menu configuration, and proper chams system integration.
The most recent integration was the pylon.lua medic tracking system:
- Purpose: Vertical pylon indicators above enemy medics behind walls for enhanced situational awareness
- Key Features: Segmented alpha-fade rendering, visibility caching, performance optimization, distance filtering
- Category: Always-on tactical ESP feature for medic tracking
// Medic pylon system with visibility caching
class CPylonESP
{
private:
static constexpr float PYLON_HEIGHT = 350.0f;
static constexpr int SEGMENTS = 10;
static constexpr float MIN_DISTANCE = 800.0f;
static constexpr float VISIBILITY_PERSISTENCE = 0.2f;
std::unordered_map<int, MedicPosition> m_MedicPositions;
std::unordered_map<std::string, VisibilityCache> m_VisibilityCache;
public:
void Draw(); // Main pylon rendering with segment-based visibility
};- Performance caching: Visibility persistence system prevents flashing during rapid visibility changes
- Segmented rendering: 10-segment pylons with alpha fade from bottom (200) to top (25)
- Distance filtering: Only shows pylons for medics 800+ units away to avoid close-range clutter
- Wall-penetration detection: Only renders when medic is behind walls/obstacles
- Visibility tracing:
TraceLine(fromPos, targetPos, MASK_VISIBLE)→SDK::Trace()withMASK_VISIBLE - Segmented drawing: Multi-segment line rendering →
H::Draw.Line()with alpha variations - Position caching: Update interval system → time-based position storage with cleanup
- Class filtering:
player:GetPropInt("m_iClass") == TF2_Medic→pPlayer->m_iClass() == TF_CLASS_MEDIC
- Visibility caching: 0.2-second persistence prevents rapid visibility recalculation
- Position updates: 0.5-second intervals reduce unnecessary position calculations
- Two-pass rendering: First check if any segments visible, then render only visible ones
- Memory management: Automatic cleanup of stale entries and visibility cache
- Structured approach: Created
PylonESPclass with clear separation of concerns - Drawing integration: Added to main drawing loop after other ESP systems
- Performance consideration: Positioned after essential systems, before debug info
- Always-on design: No configuration UI, automatic medic detection and filtering
- Always-enabled vertical pylons above enemy medics behind walls (800+ unit range)
- Red segmented pylons with smooth alpha fade (200 to 25) for depth perception
- Visibility-cached rendering prevents flickering during combat
- Automatic position updates with performance-optimized cleanup routines
- Enhanced medic tracking for tactical awareness without performance impact
- Visibility caching: Short-term persistence (0.2s) dramatically improves visual stability
- Segmented rendering: Breaking complex visuals into segments allows selective visibility
- Distance thresholds: Minimum distance filters prevent close-range visual clutter
- Two-pass algorithms: Check visibility first, then render only necessary elements
- Memory lifecycle: Proactive cleanup prevents memory leaks during extended gameplay
This example demonstrates advanced ESP techniques with performance-conscious visibility management.
The most complex integration was the enemycam.lua picture-in-picture camera system:
- Purpose: Real-time enemy perspective camera window with multiple targeting modes
- Key Features: Render-to-texture system, multiple view modes, target selection algorithms, overlay UI
- Category: Advanced visual feature leveraging existing CameraWindow infrastructure
// Enemy camera system with render-to-texture capability
class CEnemyCam
{
private:
static constexpr int CAMERA_WIDTH = 320;
static constexpr int CAMERA_HEIGHT = 240;
ECameraMode m_eMode = ECameraMode::CLOSEST;
CTFPlayer* m_pTargetPlayer = nullptr;
IMaterial* m_pCameraMaterial = nullptr;
ITexture* m_pCameraTexture = nullptr;
public:
void RenderView(void* ecx, const CViewSetup& view); // Render-to-texture
void Draw(); // Screen display and overlay
};- Existing infrastructure leverage: Built on CameraWindow's render-to-texture system
- Multiple targeting modes: Closest enemy, healed players, medics, top score, random selection
- Advanced rendering: Custom viewsetup with offset camera modes for better visibility
- Real-time overlay: Player information, health bars, medic relationships
- Render system:
render.Push3DView()→CViewRender_RenderViewhook integration - Material system:
materials.Create()→F::Materials.Create()with KeyValues - Target selection: Complex enemy filtering →
H::Entities.GetGroup(EGroupType::PLAYERS_ALL) - Health detection: Direct medigun target checking →
pMedigun->m_hHealingTarget().Get()
- Healing detection: Replaced non-existent
TF_COND_HEALINGwith direct medigun target analysis - Class constants: Fixed
TF_CLASS_HEAVYWEAPONS→TF_CLASS_HEAVYSDK compatibility - Score access: Corrected
GetScore()→m_iTotalScore()netvar method usage - Render integration: Proper hook placement in both render and draw pipelines
- Infrastructure reuse: Leveraged existing CameraWindow render-to-texture framework
- Dual hook integration: Added to both
CViewRender_RenderViewandIEngineVGui_Paint - Material lifecycle: Proper initialization in
Materials.cppwith cleanup handling - Target algorithms: Implemented sophisticated enemy selection with auto-switching
- Real-time 320x240 enemy perspective camera in upper-right corner
- Five targeting modes: closest, healed players, medics, top score, random
- Raw and offset view modes with intelligent pitch-based camera positioning
- Live overlay with player names, classes, health bars, and medic relationships
- Automatic target switching every 3 seconds with performance-optimized search intervals
- Full integration with existing material and rendering systems
- Infrastructure leverage: Reusing existing systems dramatically reduces implementation complexity
- SDK compatibility: Always verify constants and method names against actual SDK definitions
- Dual hook integration: Complex features often require multiple integration points
- Performance considerations: Even with C++ performance, smart algorithms matter for user experience
- Error handling: Graceful fallbacks when ideal targets aren't available improve reliability
This example demonstrates the most advanced visual feature integration, showcasing render-to-texture capabilities and complex target selection algorithms.
This guide provides the foundation for porting any Lua script to Amalgam's C++ codebase. The key is understanding the API mappings, following the established patterns, and methodically fixing compilation issues. Each successful port makes future ports easier as you build familiarity with the SDK and common patterns.
Best Practices Learned from All Examples:
- Start simple: Direct 1:1 Lua translation without optimization
- Follow existing patterns: HealthBarESP, UberTracker, PlayerTrails approaches
- Avoid premature optimization: Caching and performance optimizations can cause flickering
- Use native systems: Leverage existing ESP, chams, and drawing systems when possible
- Test iteratively: Build, test, fix issues, repeat until behavior matches exactly
- Chams integration: Use StickyESP/FocusFire pattern with
UpdateChamsEntities()andm_mEntitiesmap for working chams - Entity type validation: Always verify method availability (
IsAlive()only onCTFPlayer, notCBaseEntity) - Menu integration: Both main toggle AND detailed configuration section required for visibility
- Bone matrix analysis: Use
matrix3x4 aBones[MAXSTUDIOBONES]for precise positioning calculations
Remember: when in doubt, look at existing similar features in the codebase. The ESP system is particularly good reference for entity access and drawing patterns. For always-on features, use the UberTracker as a reference implementation.