Add a spell forcer and fix a bunch of issues
- Crithack + nospread compatibility fixes - Automelee hitting air - Nospread ghost crash
This commit is contained in:
parent
6463ff25da
commit
9132b8470e
@ -8,5 +8,6 @@
|
|||||||
<Include path="nullifiedcat/misc/autoparty.xml"/>
|
<Include path="nullifiedcat/misc/autoparty.xml"/>
|
||||||
<Include path="nullifiedcat/misc/autoload.xml"/>
|
<Include path="nullifiedcat/misc/autoload.xml"/>
|
||||||
<Include path="nullifiedcat/misc/datacenters.xml"/>
|
<Include path="nullifiedcat/misc/datacenters.xml"/>
|
||||||
|
<Include path="nullifiedcat/misc/spellforcer.xml"/>
|
||||||
</TabContainer>
|
</TabContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
114
data/menu/nullifiedcat/misc/spellforcer.xml
Normal file
114
data/menu/nullifiedcat/misc/spellforcer.xml
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<Tab name="Spellforcer" padding="4 4 4 4">
|
||||||
|
<Box height="content" width="content" padding="12 6 6 6" name="Spellforcer">
|
||||||
|
<List width="250">
|
||||||
|
<AutoVariable width="fill" target="spellforce.enabled" label="Enable Spell forcer" tooltip="Attempts to force a specific spell for yourself, and a different one and onto enemies."/>
|
||||||
|
<LabeledObject width="fill" label="Default spell">
|
||||||
|
<Select target="spellforce.default_spell">
|
||||||
|
<Option name="Disabled" value="-1"/>
|
||||||
|
<Option name="Fireball" value="0"/>
|
||||||
|
<Option name="Bats" value="1"/>
|
||||||
|
<Option name="Uber" value="2"/>
|
||||||
|
<Option name="Pumpkin Bombs" value="3"/>
|
||||||
|
<Option name="Jump" value="4"/>
|
||||||
|
<Option name="Invisibility" value="5"/>
|
||||||
|
<Option name="Lighting Storm" value="7"/>
|
||||||
|
<Option name="Minify" value="8"/>
|
||||||
|
<Option name="Meteor" value="9"/>
|
||||||
|
</Select>
|
||||||
|
</LabeledObject>
|
||||||
|
<LabeledObject width="fill" label="Default Rare spell">
|
||||||
|
<Select target="spellforce.default_spell.rare">
|
||||||
|
<Option name="Disabled" value="-1"/>
|
||||||
|
<Option name="Lighting Storm" value="7"/>
|
||||||
|
<Option name="Minify" value="8"/>
|
||||||
|
<Option name="Meteor" value="9"/>
|
||||||
|
<Option name="Monoculus" value="10"/>
|
||||||
|
<Option name="Skeleton Army" value="11"/>
|
||||||
|
</Select>
|
||||||
|
</LabeledObject>
|
||||||
|
<LabeledObject width="fill" label="Default enemy spell" tooltip="Left side is the normal spell pickup, right side is the rare spell pickup.">
|
||||||
|
<Select target="spellforce.default_spell.enemies">
|
||||||
|
<Option name="Disabled" value="-1"/>
|
||||||
|
<Option name="Fireball/Meteors" value="0"/>
|
||||||
|
<Option name="Bats/Skeleton Army" value="1"/>
|
||||||
|
<Option name="Uber/Meteors" value="2"/>
|
||||||
|
<Option name="Pumpkin Bombs/Lightning Storm" value="3"/>
|
||||||
|
<Option name="Jump/Lightning Storm" value="4"/>
|
||||||
|
<Option name="Invisibility/Lightning Storm" value="5"/>
|
||||||
|
<Option name="Lighting Storm/Minify" value="7"/>
|
||||||
|
<Option name="Minify/Minify" value="8"/>
|
||||||
|
<Option name="Meteor/Meteor" value="9"/>
|
||||||
|
</Select>
|
||||||
|
</LabeledObject>
|
||||||
|
<LabeledObject width="fill" label="Helltower spell">
|
||||||
|
<Select target="spellforce.helltower_spell">
|
||||||
|
<Option name="Disabled" value="-1"/>
|
||||||
|
<Option name="Fireball" value="0"/>
|
||||||
|
<Option name="Bats" value="1"/>
|
||||||
|
<Option name="Uber" value="2"/>
|
||||||
|
<Option name="Pumpkin Bombs" value="3"/>
|
||||||
|
<Option name="Jump" value="4"/>
|
||||||
|
<Option name="Invisibility" value="5"/>
|
||||||
|
<Option name="Teleport" value="6"/>
|
||||||
|
</Select>
|
||||||
|
</LabeledObject>
|
||||||
|
<LabeledObject width="fill" label="Helltower enemy spell" tooltip="Left side is the normal spell pickup, right side is the rare spell pickup.">
|
||||||
|
<Select target="spellforce.helltower_spell.enemies">
|
||||||
|
<Option name="Disabled" value="-1"/>
|
||||||
|
<Option name="Fireball/Skeletons" value="0"/>
|
||||||
|
<Option name="Bats/Skeletons" value="1"/>
|
||||||
|
<Option name="Uber/Monoculus" value="2"/>
|
||||||
|
<Option name="Pumpkin Bombs/Monoculus" value="3"/>
|
||||||
|
<Option name="Jump/Lightning Storm" value="4"/>
|
||||||
|
<Option name="Invisibility/Minify" value="5"/>
|
||||||
|
<Option name="Teleport/Monoculus" value="6"/>
|
||||||
|
</Select>
|
||||||
|
</LabeledObject>
|
||||||
|
<LabeledObject width="fill" label="Carnival of Carnage spell">
|
||||||
|
<Select target="spellforce.doomsday_spell">
|
||||||
|
<Option name="Disabled" value="-1"/>
|
||||||
|
<Option name="Fireball" value="0"/>
|
||||||
|
<Option name="Uber" value="2"/>
|
||||||
|
<Option name="Jump" value="4"/>
|
||||||
|
<Option name="Invisibility" value="5"/>
|
||||||
|
</Select>
|
||||||
|
</LabeledObject>
|
||||||
|
<LabeledObject width="fill" label="Carnival of Carnage Rare spell">
|
||||||
|
<Select target="spellforce.doomsday_spell.rare">
|
||||||
|
<Option name="Disabled" value="-1"/>
|
||||||
|
<Option name="Lighting Storm" value="7"/>
|
||||||
|
<Option name="Minify" value="8"/>
|
||||||
|
<Option name="Meteor" value="9"/>
|
||||||
|
<Option name="Monoculus" value="10"/>
|
||||||
|
</Select>
|
||||||
|
</LabeledObject>
|
||||||
|
<LabeledObject width="fill" label="Carnival of Carnage enemy spell" tooltip="Left side is the normal spell pickup, right side is the rare spell pickup.">
|
||||||
|
<Select target="spellforce.doomsday_spell.enemies">
|
||||||
|
<Option name="Disabled" value="-1"/>
|
||||||
|
<Option name="Fireball/Meteor" value="0"/>
|
||||||
|
<Option name="Uber/Meteor" value="2"/>
|
||||||
|
<Option name="Jump/Monoculus" value="4"/>
|
||||||
|
<Option name="Invisibility/Minify" value="5"/>
|
||||||
|
</Select>
|
||||||
|
</LabeledObject>
|
||||||
|
<LabeledObject width="fill" label="Bumper Kart Spell">
|
||||||
|
<Select target="spellforce.bumper_spell">
|
||||||
|
<Option name="Disabled" value="-1"/>
|
||||||
|
<Option name="Boxing Rocket" value="12"/>
|
||||||
|
<Option name="B.A.S.E. Jump" value="13"/>
|
||||||
|
<Option name="Overheal" value="14"/>
|
||||||
|
<Option name="Bomb Head" value="15"/>
|
||||||
|
</Select>
|
||||||
|
</LabeledObject>
|
||||||
|
<LabeledObject width="fill" label="Bumper Kart Enemy Spell">
|
||||||
|
<Select target="spellforce.bumper_spell.enemies">
|
||||||
|
<Option name="Disabled" value="-1"/>
|
||||||
|
<Option name="Boxing Rocket" value="12"/>
|
||||||
|
<Option name="B.A.S.E. Jump" value="13"/>
|
||||||
|
<Option name="Overheal" value="14"/>
|
||||||
|
<Option name="Bomb Head" value="15"/>
|
||||||
|
</Select>
|
||||||
|
</LabeledObject>
|
||||||
|
</List>
|
||||||
|
</Box>
|
||||||
|
</Tab>
|
@ -171,6 +171,7 @@ public:
|
|||||||
offset_t res_iTeam;
|
offset_t res_iTeam;
|
||||||
offset_t res_iScore;
|
offset_t res_iScore;
|
||||||
offset_t res_bAlive;
|
offset_t res_bAlive;
|
||||||
|
offset_t res_bValid;
|
||||||
offset_t m_nChargeResistType;
|
offset_t m_nChargeResistType;
|
||||||
offset_t m_hHealingTarget;
|
offset_t m_hHealingTarget;
|
||||||
offset_t m_flChargeLevel; // Medigun
|
offset_t m_flChargeLevel; // Medigun
|
||||||
|
@ -29,6 +29,7 @@ public:
|
|||||||
int getClass(int idx);
|
int getClass(int idx);
|
||||||
int getTeam(int idx);
|
int getTeam(int idx);
|
||||||
bool isAlive(int idx);
|
bool isAlive(int idx);
|
||||||
|
bool isValid(int idx);
|
||||||
|
|
||||||
int entity;
|
int entity;
|
||||||
};
|
};
|
||||||
|
@ -15,4 +15,21 @@ public:
|
|||||||
int winning_team; // 56 | 4 bytes | 60
|
int winning_team; // 56 | 4 bytes | 60
|
||||||
char pad2[974]; // 60 | 974 bytes | 1034
|
char pad2[974]; // 60 | 974 bytes | 1034
|
||||||
bool isPVEMode; // 1034 | 1 byte | 1035
|
bool isPVEMode; // 1034 | 1 byte | 1035
|
||||||
};
|
char pad3[15]; // 1035 | 15 bytes | 1050
|
||||||
|
int isUsingSpells; // 1050 | 4 bytes | 1054
|
||||||
|
char pad4[790]; // 1054 | 790 bytes | 1844
|
||||||
|
int halloweenScenario; // 1844 | 4 bytes | 1848
|
||||||
|
|
||||||
|
bool isUsingSpells_fn()
|
||||||
|
{
|
||||||
|
auto tf_spells_enabled = g_ICvar->FindVar("tf_spells_enabled");
|
||||||
|
if (tf_spells_enabled->GetBool())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Hightower
|
||||||
|
if (halloweenScenario == 4)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return isUsingSpells;
|
||||||
|
}
|
||||||
|
} __attribute__((packed));
|
||||||
|
@ -39,6 +39,7 @@ void NetVars::Init()
|
|||||||
this->m_flMaxspeed = gNetvars.get_offset("DT_BasePlayer", "m_flMaxspeed");
|
this->m_flMaxspeed = gNetvars.get_offset("DT_BasePlayer", "m_flMaxspeed");
|
||||||
res_iTeam = gNetvars.get_offset("DT_TFPlayerResource", "baseclass", "m_iTeam");
|
res_iTeam = gNetvars.get_offset("DT_TFPlayerResource", "baseclass", "m_iTeam");
|
||||||
res_bAlive = gNetvars.get_offset("DT_TFPlayerResource", "baseclass", "m_bAlive");
|
res_bAlive = gNetvars.get_offset("DT_TFPlayerResource", "baseclass", "m_bAlive");
|
||||||
|
res_bValid = gNetvars.get_offset("DT_TFPlayerResource", "baseclass", "m_bValid");
|
||||||
this->res_iMaxBuffedHealth = gNetvars.get_offset("DT_TFPlayerResource", "m_iMaxBuffedHealth");
|
this->res_iMaxBuffedHealth = gNetvars.get_offset("DT_TFPlayerResource", "m_iMaxBuffedHealth");
|
||||||
m_angEyeAngles = gNetvars.get_offset("DT_TFPlayer", "tfnonlocaldata", "m_angEyeAngles[0]");
|
m_angEyeAngles = gNetvars.get_offset("DT_TFPlayer", "tfnonlocaldata", "m_angEyeAngles[0]");
|
||||||
m_angEyeAnglesLocal = gNetvars.get_offset("DT_TFPlayer", "tflocaldata", "m_angEyeAngles[0]");
|
m_angEyeAnglesLocal = gNetvars.get_offset("DT_TFPlayer", "tflocaldata", "m_angEyeAngles[0]");
|
||||||
|
@ -1160,7 +1160,7 @@ static CatCommand debug_print_crit_info("debug_print_crit_info", "Print a bunch
|
|||||||
static InitRoutine init(
|
static InitRoutine init(
|
||||||
[]()
|
[]()
|
||||||
{
|
{
|
||||||
EC::Register(EC::CreateMoveLate, CreateMove, "crit_cm");
|
EC::Register(EC::CreateMoveLate, CreateMove, "crit_cm", EC::late);
|
||||||
#if ENABLE_VISUALS
|
#if ENABLE_VISUALS
|
||||||
EC::Register(EC::Draw, Draw, "crit_draw");
|
EC::Register(EC::Draw, Draw, "crit_draw");
|
||||||
#endif
|
#endif
|
||||||
|
@ -37,6 +37,7 @@ set(files "${CMAKE_CURRENT_LIST_DIR}/AutoJoin.cpp"
|
|||||||
"${CMAKE_CURRENT_LIST_DIR}/Noisemaker.cpp"
|
"${CMAKE_CURRENT_LIST_DIR}/Noisemaker.cpp"
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/PureBypass.cpp"
|
"${CMAKE_CURRENT_LIST_DIR}/PureBypass.cpp"
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/Trigger.cpp"
|
"${CMAKE_CURRENT_LIST_DIR}/Trigger.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/SpellForcer.cpp"
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/UberSpam.cpp"
|
"${CMAKE_CURRENT_LIST_DIR}/UberSpam.cpp"
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/Walkbot.cpp"
|
"${CMAKE_CURRENT_LIST_DIR}/Walkbot.cpp"
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/Warp.cpp")
|
"${CMAKE_CURRENT_LIST_DIR}/Warp.cpp")
|
||||||
|
@ -958,8 +958,6 @@ static InitRoutine init_pyrovision(
|
|||||||
{
|
{
|
||||||
if (HasCondition<TFCond_HalloweenKartNoTurn>(LOCAL_E))
|
if (HasCondition<TFCond_HalloweenKartNoTurn>(LOCAL_E))
|
||||||
RemoveCondition<TFCond_HalloweenKartNoTurn>(LOCAL_E);
|
RemoveCondition<TFCond_HalloweenKartNoTurn>(LOCAL_E);
|
||||||
if (HasCondition<TFCond_FreezeInput>(LOCAL_E))
|
|
||||||
RemoveCondition<TFCond_FreezeInput>(LOCAL_E);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"remove_cart_cond");
|
"remove_cart_cond");
|
||||||
|
@ -266,7 +266,7 @@ static std::pair<CachedEntity *, float> getNearestPlayerDistance()
|
|||||||
for (int i = 1; i <= g_IEngine->GetMaxClients(); i++)
|
for (int i = 1; i <= g_IEngine->GetMaxClients(); i++)
|
||||||
{
|
{
|
||||||
CachedEntity *ent = ENTITY(i);
|
CachedEntity *ent = ENTITY(i);
|
||||||
if (CE_VALID(ent) && ent->m_vecDormantOrigin() && ent->m_bAlivePlayer() && ent->m_bEnemy() && g_pLocalPlayer->v_Origin.DistTo(ent->m_vecOrigin()) < distance && player_tools::shouldTarget(ent) && !IsPlayerInvisible(ent))
|
if (CE_VALID(ent) && ent->m_vecDormantOrigin() && g_pPlayerResource->isAlive(ent->m_IDX) && ent->m_bEnemy() && g_pLocalPlayer->v_Origin.DistTo(ent->m_vecOrigin()) < distance && player_tools::shouldTarget(ent) && !IsPlayerInvisible(ent))
|
||||||
{
|
{
|
||||||
distance = g_pLocalPlayer->v_Origin.DistTo(*ent->m_vecDormantOrigin());
|
distance = g_pLocalPlayer->v_Origin.DistTo(*ent->m_vecDormantOrigin());
|
||||||
best_ent = ent;
|
best_ent = ent;
|
||||||
|
321
src/hacks/SpellForcer.cpp
Normal file
321
src/hacks/SpellForcer.cpp
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "AntiCheatBypass.hpp"
|
||||||
|
|
||||||
|
namespace hacks::tf2::spellforcer
|
||||||
|
{
|
||||||
|
static settings::Boolean enabled("spellforce.enabled", "false");
|
||||||
|
|
||||||
|
static settings::Int default_spell("spellforce.default_spell", "-1");
|
||||||
|
static settings::Int default_spell_rare("spellforce.default_spell.rare", "-1");
|
||||||
|
static settings::Int default_spell_enemies("spellforce.default_spell.enemies", "-1");
|
||||||
|
|
||||||
|
static settings::Int doomsday_spell("spellforce.doomsday_spell", "-1");
|
||||||
|
static settings::Int doomsday_spell_rare("spellforce.doomsday_spell.rare", "-1");
|
||||||
|
static settings::Int doomsday_spell_enemies("spellforce.doomsday_spell.enemies", "-1");
|
||||||
|
|
||||||
|
static settings::Int helltower_spell("spellforce.helltower_spell", "-1");
|
||||||
|
static settings::Int helltower_spell_enemies("spellforce.helltower_spell.enemies", "-1");
|
||||||
|
|
||||||
|
static settings::Int bumper_spell("spellforce.bumper_spell", "-1");
|
||||||
|
static settings::Int bumper_spell_enemies("spellforce.bumper_spell.enemies", "-1");
|
||||||
|
|
||||||
|
static settings::Boolean debug_use_set_cmd("spellforce.debug.use_set_cmd", "false");
|
||||||
|
static settings::Boolean debug_inverse_enemy("spellforce.debug.inverse-enemy", "false");
|
||||||
|
|
||||||
|
enum spelltypes
|
||||||
|
{
|
||||||
|
BUMPER_CARS = 0,
|
||||||
|
DOOMSDAY = 1,
|
||||||
|
HELLTOWER = 2,
|
||||||
|
NORMAL = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum halloween_scenario
|
||||||
|
{
|
||||||
|
HALLOWEEN_SCENARIO_NONE = 0,
|
||||||
|
HALLOWEEN_SCENARIO_MANN_MANOR,
|
||||||
|
HALLOWEEN_SCENARIO_VIADUCT,
|
||||||
|
HALLOWEEN_SCENARIO_LAKESIDE,
|
||||||
|
HALLOWEEN_SCENARIO_HIGHTOWER,
|
||||||
|
HALLOWEEN_SCENARIO_DOOMSDAY
|
||||||
|
};
|
||||||
|
|
||||||
|
spelltypes getSpellMode()
|
||||||
|
{
|
||||||
|
if (HasCondition<TFCond_HalloweenKart>(LOCAL_E))
|
||||||
|
return BUMPER_CARS;
|
||||||
|
|
||||||
|
int scenario = g_pGameRules->halloweenScenario;
|
||||||
|
switch (scenario)
|
||||||
|
{
|
||||||
|
case HALLOWEEN_SCENARIO_DOOMSDAY:
|
||||||
|
return DOOMSDAY;
|
||||||
|
case HALLOWEEN_SCENARIO_HIGHTOWER:
|
||||||
|
return HELLTOWER;
|
||||||
|
default:
|
||||||
|
return NORMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Too much random noise on these maps to force spells
|
||||||
|
// static std::array<std::string, 1> noisy_maps{ "" };
|
||||||
|
|
||||||
|
static std::unordered_map<int, int> spellmap_normal({ { 0, 4206970 }, { 1, 4206969 }, { 2, 4206972 }, { 3, 4206997 }, { 4, 4206971 }, { 5, 4206977 }, { 7, 4206973 }, { 8, 4206982 }, { 9, 4206976 } });
|
||||||
|
static std::unordered_map<int, int> spellmap_normal_rare({ { 7, 4206987 }, { 8, 4206982 }, { 9, 4207006 }, { 10, 4206983 }, { 11, 4206984 } });
|
||||||
|
|
||||||
|
static std::unordered_map<int, int> spellmap_helltower({ { 0, 4206998 }, { 1, 4206984 }, { 2, 4206985 }, { 3, 4206986 }, { 4, 4206987 }, { 5, 4206982 }, { 6, 4206983 } });
|
||||||
|
|
||||||
|
static std::unordered_map<int, int> spellmap_doomsday_normal({ { 0, 4206982 }, { 2, 4207006 }, { 4, 4206983 }, { 5, 4206984 } });
|
||||||
|
static std::unordered_map<int, int> spellmap_doomsday_rare({ { 7, 4207009 }, { 8, 4207014 }, { 9, 4207008 }, { 10, 4207007 } });
|
||||||
|
|
||||||
|
static std::unordered_map<int, int> spellmap_bumpercar({ { 12, 4206982 }, { 13, 4206983 }, { 14, 4206987 }, { 15, 4206988 } });
|
||||||
|
// static std::unordered_map<int, int> hasslecastle_normal({ { 0, 4206990 }, { 1, 4206989 }, { 2, 4206986 }, { 3, 4206997 }, { 4, 4206984 }, { 5, 4206983 }, { 7, 4207000 }, { 8, 4206982 }, { 9, 4207027 } });
|
||||||
|
|
||||||
|
int getCommandForSpellID(int spellidx, int spelltype)
|
||||||
|
{
|
||||||
|
std::unordered_map<int, int> spellmap;
|
||||||
|
|
||||||
|
// if (levelname.find("koth_slasher") != levelname.npos || levelname.find("pl_bloodwater") != levelname.npos || levelname.find("pl_rumble_event") != levelname.npos)
|
||||||
|
|
||||||
|
auto spellmode = getSpellMode();
|
||||||
|
|
||||||
|
switch (spellmode)
|
||||||
|
{
|
||||||
|
case HELLTOWER:
|
||||||
|
spellmap = spellmap_helltower;
|
||||||
|
break;
|
||||||
|
case BUMPER_CARS:
|
||||||
|
spellmap = spellmap_bumpercar;
|
||||||
|
break;
|
||||||
|
case DOOMSDAY:
|
||||||
|
spellmap = spelltype ? spellmap_doomsday_rare : spellmap_doomsday_normal;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
spellmap = spelltype ? spellmap_normal_rare : spellmap_normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int value = spellmap[spellidx];
|
||||||
|
|
||||||
|
// "No entry" should be returned as -1
|
||||||
|
return value ? value : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doomsday spell list
|
||||||
|
|
||||||
|
static const int g_doomsdayNormalSpellIndexList[] = {
|
||||||
|
0, // Fireball
|
||||||
|
0, // Fireball x2
|
||||||
|
2, // overheal
|
||||||
|
4, // Jump
|
||||||
|
5, // Stealth
|
||||||
|
};
|
||||||
|
|
||||||
|
// Side note: 6 is the Teleport spell
|
||||||
|
|
||||||
|
static const int g_doomsdayRareSpellIndexList[] = {
|
||||||
|
7, // Lightning
|
||||||
|
8, // Mini
|
||||||
|
9, // Meteor
|
||||||
|
7, // Lightning
|
||||||
|
8, // Mini
|
||||||
|
9, // Meteor
|
||||||
|
10 // Boss / Monoculus. Smaller chance
|
||||||
|
};
|
||||||
|
|
||||||
|
// "Normal" Spell list
|
||||||
|
static const int g_generalSpellIndexList[] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 7, 8, 9 };
|
||||||
|
|
||||||
|
// Saves command that have a specific spell id
|
||||||
|
std::unordered_map<int, int> command_to_spell_map;
|
||||||
|
|
||||||
|
int current_index = 0;
|
||||||
|
|
||||||
|
int getStaticCmd()
|
||||||
|
{
|
||||||
|
return 4206982 + current_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
CachedEntity *getClosestSpell()
|
||||||
|
{
|
||||||
|
CachedEntity *ent = nullptr;
|
||||||
|
float best_dist = FLT_MAX;
|
||||||
|
bool is_dormant = false;
|
||||||
|
|
||||||
|
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
|
||||||
|
return ent;
|
||||||
|
for (int i = g_IEngine->GetMaxClients(); i < HIGHEST_ENTITY; i++)
|
||||||
|
{
|
||||||
|
CachedEntity *spell = ENTITY(i);
|
||||||
|
if (CE_INVALID(spell) || !spell->m_vecDormantOrigin() || (spell->m_ItemType() != ITEM_SPELL && spell->m_ItemType() != ITEM_SPELL_RARE))
|
||||||
|
continue;
|
||||||
|
float dist = spell->m_flDistance();
|
||||||
|
if (dist < best_dist || (is_dormant && !RAW_ENT(spell)->IsDormant() && dist <= 300.0f))
|
||||||
|
{
|
||||||
|
ent = spell;
|
||||||
|
best_dist = dist;
|
||||||
|
is_dormant = RAW_ENT(spell)->IsDormant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateMoveLate()
|
||||||
|
{
|
||||||
|
if (hacks::tf2::antianticheat::enabled || !g_pGameRules->isUsingSpells_fn())
|
||||||
|
return;
|
||||||
|
auto cmd = 0;
|
||||||
|
if (debug_use_set_cmd)
|
||||||
|
cmd = getStaticCmd();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto spell = getClosestSpell();
|
||||||
|
|
||||||
|
// Apply bad effects for enemies
|
||||||
|
if (debug_inverse_enemy || CE_BAD(spell) || spell->m_flDistance() > 300.0f)
|
||||||
|
{
|
||||||
|
int spellindex;
|
||||||
|
|
||||||
|
auto spellmode = getSpellMode();
|
||||||
|
switch (spellmode)
|
||||||
|
{
|
||||||
|
case BUMPER_CARS:
|
||||||
|
spellindex = *bumper_spell_enemies;
|
||||||
|
break;
|
||||||
|
case DOOMSDAY:
|
||||||
|
spellindex = *doomsday_spell_enemies;
|
||||||
|
break;
|
||||||
|
case HELLTOWER:
|
||||||
|
spellindex = *helltower_spell_enemies;
|
||||||
|
break;
|
||||||
|
case NORMAL:
|
||||||
|
spellindex = *default_spell_enemies;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = getCommandForSpellID(spellindex, 0);
|
||||||
|
}
|
||||||
|
else if (spell->m_ItemType() == ITEM_SPELL)
|
||||||
|
{
|
||||||
|
int spellindex;
|
||||||
|
|
||||||
|
auto spellmode = getSpellMode();
|
||||||
|
switch (spellmode)
|
||||||
|
{
|
||||||
|
case BUMPER_CARS:
|
||||||
|
spellindex = *bumper_spell;
|
||||||
|
break;
|
||||||
|
case DOOMSDAY:
|
||||||
|
spellindex = *doomsday_spell;
|
||||||
|
break;
|
||||||
|
case HELLTOWER:
|
||||||
|
spellindex = *helltower_spell;
|
||||||
|
break;
|
||||||
|
case NORMAL:
|
||||||
|
spellindex = *default_spell;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = getCommandForSpellID(spellindex, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int spellindex;
|
||||||
|
|
||||||
|
auto spellmode = getSpellMode();
|
||||||
|
switch (spellmode)
|
||||||
|
{
|
||||||
|
case DOOMSDAY:
|
||||||
|
spellindex = *doomsday_spell_rare;
|
||||||
|
break;
|
||||||
|
case HELLTOWER:
|
||||||
|
case BUMPER_CARS:
|
||||||
|
case NORMAL:
|
||||||
|
spellindex = *default_spell_rare;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cmd = getCommandForSpellID(spellindex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd != -1)
|
||||||
|
{
|
||||||
|
current_late_user_cmd->command_number = cmd;
|
||||||
|
current_late_user_cmd->random_seed = MD5_PseudoRandom(cmd) & 0x7FFFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ENABLE_VISUALS
|
||||||
|
static Timer draw_timer;
|
||||||
|
void Draw()
|
||||||
|
{
|
||||||
|
if (!enabled || !g_IEngine->IsInGame())
|
||||||
|
{
|
||||||
|
draw_timer.update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
static bool previously_highest = true;
|
||||||
|
// Is the player the highest IDX of all players?
|
||||||
|
bool is_player_highest = true;
|
||||||
|
|
||||||
|
int players_above = 0;
|
||||||
|
|
||||||
|
// Check if there is a valid player exceeding our IDX
|
||||||
|
for (int i = g_pLocalPlayer->entity_idx + 1; i <= g_IEngine->GetMaxClients(); i++)
|
||||||
|
{
|
||||||
|
if (g_pPlayerResource->isValid(i))
|
||||||
|
{
|
||||||
|
is_player_highest = false;
|
||||||
|
players_above++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_player_highest != previously_highest)
|
||||||
|
draw_timer.update();
|
||||||
|
|
||||||
|
previously_highest = is_player_highest;
|
||||||
|
|
||||||
|
if (!draw_timer.check(60000))
|
||||||
|
{
|
||||||
|
if (is_player_highest)
|
||||||
|
AddCenterString("Forcing Spells.", colors::green);
|
||||||
|
else
|
||||||
|
AddCenterString(format("Cannot Force Spells, entity idx is ", players_above, " too low :("), colors::red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CatCommand enter_spell_data("spellforcer_enter_data", "Debug",
|
||||||
|
[](const CCommand &args)
|
||||||
|
{
|
||||||
|
if (args.ArgC() < 2)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Grab spellidx
|
||||||
|
int spellidx = std::stoi(args.Arg(1));
|
||||||
|
|
||||||
|
if (command_to_spell_map[spellidx] == 0)
|
||||||
|
command_to_spell_map[spellidx] = getStaticCmd();
|
||||||
|
for (auto &entry : command_to_spell_map)
|
||||||
|
logging::Info("%d: %d", entry.first, entry.second);
|
||||||
|
current_index++;
|
||||||
|
}
|
||||||
|
catch (const std::invalid_argument &)
|
||||||
|
{
|
||||||
|
logging::Info("Invalid Steamid32 provided.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
static InitRoutine init(
|
||||||
|
[]()
|
||||||
|
{
|
||||||
|
#if ENABLE_VISUALS
|
||||||
|
EC::Register(EC::Draw, Draw, "spellforcer_draw", EC::early);
|
||||||
|
EC::Register(
|
||||||
|
EC::LevelInit, []() { draw_timer.update(); }, "spellforcer_lvlinit");
|
||||||
|
#endif
|
||||||
|
EC::Register(EC::CreateMoveLate, CreateMoveLate, "spellforcer_cml", EC::early);
|
||||||
|
});
|
||||||
|
|
||||||
|
} // namespace hacks::tf2::spellforcer
|
@ -45,6 +45,8 @@ static settings::Boolean warp_backwards{ "warp.on-hit.backwards", "false" };
|
|||||||
static settings::Boolean warp_left{ "warp.on-hit.left", "true" };
|
static settings::Boolean warp_left{ "warp.on-hit.left", "true" };
|
||||||
static settings::Boolean warp_right{ "warp.on-hit.right", "true" };
|
static settings::Boolean warp_right{ "warp.on-hit.right", "true" };
|
||||||
|
|
||||||
|
static settings::Boolean debug_seqout{ "debug.warp_seqout", "false" };
|
||||||
|
|
||||||
// Hidden control rvars for communtiy servers
|
// Hidden control rvars for communtiy servers
|
||||||
static settings::Int maxusrcmdprocessticks("warp.maxusrcmdprocessticks", "24");
|
static settings::Int maxusrcmdprocessticks("warp.maxusrcmdprocessticks", "24");
|
||||||
|
|
||||||
|
@ -10,10 +10,6 @@ namespace hooked_methods
|
|||||||
|
|
||||||
DEFINE_HOOKED_METHOD(GetUserCmd, CUserCmd *, IInput *this_, int sequence_number)
|
DEFINE_HOOKED_METHOD(GetUserCmd, CUserCmd *, IInput *this_, int sequence_number)
|
||||||
{
|
{
|
||||||
// We need to overwrite this if crithack is on
|
|
||||||
if (criticals::isEnabled())
|
|
||||||
return &GetCmds(this_)[sequence_number % VERIFIED_CMD_SIZE];
|
return &GetCmds(this_)[sequence_number % VERIFIED_CMD_SIZE];
|
||||||
else
|
|
||||||
return original::GetUserCmd(this_, sequence_number);
|
|
||||||
}
|
}
|
||||||
} // namespace hooked_methods
|
} // namespace hooked_methods
|
||||||
|
@ -83,6 +83,9 @@ ItemManager::ItemManager() : mapper()
|
|||||||
RegisterModelMapping("models/items/crystal_ball_pickup_major.mdl", ITEM_SPELL_RARE);
|
RegisterModelMapping("models/items/crystal_ball_pickup_major.mdl", ITEM_SPELL_RARE);
|
||||||
RegisterModelMapping("models/props_monster_mash/flask_vial_purple.mdl", ITEM_SPELL_RARE);
|
RegisterModelMapping("models/props_monster_mash/flask_vial_purple.mdl", ITEM_SPELL_RARE);
|
||||||
|
|
||||||
|
// Thanks valve
|
||||||
|
RegisterSpecialMapping([](CachedEntity *ent) -> bool { return g_ItemManager.mapper.GetItemType(ent) == ITEM_SPELL && ent->m_iClassID() == CL_CLASS(CDynamicProp); }, ITEM_SPELL_RARE);
|
||||||
|
|
||||||
// == GHOSTS
|
// == GHOSTS
|
||||||
RegisterModelMapping("models/props_halloween/ghost.mdl", HALLOWEEN_GHOST);
|
RegisterModelMapping("models/props_halloween/ghost.mdl", HALLOWEEN_GHOST);
|
||||||
RegisterModelMapping("models/props_halloween/ghost_no_hat_red.mdl", HALLOWEEN_GHOST);
|
RegisterModelMapping("models/props_halloween/ghost_no_hat_red.mdl", HALLOWEEN_GHOST);
|
||||||
@ -118,8 +121,11 @@ ItemManager::ItemManager() : mapper()
|
|||||||
RegisterModelMapping("models/items/medkit_overheal.mdl", ITEM_TF2C_PILL);
|
RegisterModelMapping("models/items/medkit_overheal.mdl", ITEM_TF2C_PILL);
|
||||||
// TF2C spawners
|
// TF2C spawners
|
||||||
|
|
||||||
specials.push_back([](CachedEntity *entity) -> k_EItemType {
|
specials.push_back(
|
||||||
static ItemModelMapper tf2c_weapon_mapper = []() -> ItemModelMapper {
|
[](CachedEntity *entity) -> k_EItemType
|
||||||
|
{
|
||||||
|
static ItemModelMapper tf2c_weapon_mapper = []() -> ItemModelMapper
|
||||||
|
{
|
||||||
ItemModelMapper result;
|
ItemModelMapper result;
|
||||||
result.RegisterItem("models/weapons/w_models/w_bottle.mdl", ITEM_TF2C_W_BOTTLE);
|
result.RegisterItem("models/weapons/w_models/w_bottle.mdl", ITEM_TF2C_W_BOTTLE);
|
||||||
result.RegisterItem("models/weapons/w_models/w_grenade_napalm.mdl", ITEM_TF2C_W_GRENADE_NAPALM);
|
result.RegisterItem("models/weapons/w_models/w_grenade_napalm.mdl", ITEM_TF2C_W_GRENADE_NAPALM);
|
||||||
@ -266,7 +272,9 @@ k_EItemType ItemModelMapper::GetItemType(CachedEntity *entity)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ItemManager g_ItemManager;
|
ItemManager g_ItemManager;
|
||||||
static InitRoutine init_itemtypes([]() {
|
static InitRoutine init_itemtypes(
|
||||||
|
[]()
|
||||||
|
{
|
||||||
EC::Register(
|
EC::Register(
|
||||||
EC::LevelInit, []() { g_ItemManager = ItemManager{}; }, "clear_itemtypes");
|
EC::LevelInit, []() { g_ItemManager = ItemManager{}; }, "clear_itemtypes");
|
||||||
});
|
});
|
||||||
|
@ -609,7 +609,7 @@ void CL_SendMove_hook()
|
|||||||
current_weapon_spread = 0.0;
|
current_weapon_spread = 0.0;
|
||||||
|
|
||||||
// first try to get the player and check if he is valid
|
// first try to get the player and check if he is valid
|
||||||
if (!RAW_ENT(LOCAL_E))
|
if (!RAW_ENT(LOCAL_E) || HasCondition<TFCond_HalloweenGhostMode>(LOCAL_E))
|
||||||
{
|
{
|
||||||
// don't set called_from_sendmove here cuz we don't care
|
// don't set called_from_sendmove here cuz we don't care
|
||||||
CL_SendMove_t original = (CL_SendMove_t) cl_nospread_sendmovedetour.GetOriginalFunc();
|
CL_SendMove_t original = (CL_SendMove_t) cl_nospread_sendmovedetour.GetOriginalFunc();
|
||||||
|
@ -190,6 +190,16 @@ bool TFPlayerResource::isAlive(int idx)
|
|||||||
return *(bool *) ((unsigned) ent + netvar.res_bAlive + idx);
|
return *(bool *) ((unsigned) ent + netvar.res_bAlive + idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TFPlayerResource::isValid(int idx)
|
||||||
|
{
|
||||||
|
IClientEntity *ent = g_IEntityList->GetClientEntity(entity);
|
||||||
|
if (!ent || ent->GetClientClass()->m_ClassID != RCC_PLAYERRESOURCE)
|
||||||
|
return 0;
|
||||||
|
if (idx >= MAX_PLAYERS || idx < 0)
|
||||||
|
return false;
|
||||||
|
return *(bool *) ((unsigned) ent + netvar.res_bValid + idx);
|
||||||
|
}
|
||||||
|
|
||||||
int TFPlayerResource::getClass(int idx)
|
int TFPlayerResource::getClass(int idx)
|
||||||
{
|
{
|
||||||
IClientEntity *ent = g_IEntityList->GetClientEntity(entity);
|
IClientEntity *ent = g_IEntityList->GetClientEntity(entity);
|
||||||
|
Reference in New Issue
Block a user