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:
BenCat07 2021-10-11 22:13:00 +02:00
parent 6463ff25da
commit 9132b8470e
16 changed files with 569 additions and 98 deletions

View File

@ -8,5 +8,6 @@
<Include path="nullifiedcat/misc/autoparty.xml"/>
<Include path="nullifiedcat/misc/autoload.xml"/>
<Include path="nullifiedcat/misc/datacenters.xml"/>
<Include path="nullifiedcat/misc/spellforcer.xml"/>
</TabContainer>
</Tab>

View 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>

View File

@ -171,6 +171,7 @@ public:
offset_t res_iTeam;
offset_t res_iScore;
offset_t res_bAlive;
offset_t res_bValid;
offset_t m_nChargeResistType;
offset_t m_hHealingTarget;
offset_t m_flChargeLevel; // Medigun

View File

@ -29,6 +29,7 @@ public:
int getClass(int idx);
int getTeam(int idx);
bool isAlive(int idx);
bool isValid(int idx);
int entity;
};

View File

@ -15,4 +15,21 @@ public:
int winning_team; // 56 | 4 bytes | 60
char pad2[974]; // 60 | 974 bytes | 1034
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));

View File

@ -39,6 +39,7 @@ void NetVars::Init()
this->m_flMaxspeed = gNetvars.get_offset("DT_BasePlayer", "m_flMaxspeed");
res_iTeam = gNetvars.get_offset("DT_TFPlayerResource", "baseclass", "m_iTeam");
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");
m_angEyeAngles = gNetvars.get_offset("DT_TFPlayer", "tfnonlocaldata", "m_angEyeAngles[0]");
m_angEyeAnglesLocal = gNetvars.get_offset("DT_TFPlayer", "tflocaldata", "m_angEyeAngles[0]");

View File

@ -1160,7 +1160,7 @@ static CatCommand debug_print_crit_info("debug_print_crit_info", "Print a bunch
static InitRoutine init(
[]()
{
EC::Register(EC::CreateMoveLate, CreateMove, "crit_cm");
EC::Register(EC::CreateMoveLate, CreateMove, "crit_cm", EC::late);
#if ENABLE_VISUALS
EC::Register(EC::Draw, Draw, "crit_draw");
#endif

View File

@ -37,6 +37,7 @@ set(files "${CMAKE_CURRENT_LIST_DIR}/AutoJoin.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Noisemaker.cpp"
"${CMAKE_CURRENT_LIST_DIR}/PureBypass.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Trigger.cpp"
"${CMAKE_CURRENT_LIST_DIR}/SpellForcer.cpp"
"${CMAKE_CURRENT_LIST_DIR}/UberSpam.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Walkbot.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Warp.cpp")

View File

@ -958,8 +958,6 @@ static InitRoutine init_pyrovision(
{
if (HasCondition<TFCond_HalloweenKartNoTurn>(LOCAL_E))
RemoveCondition<TFCond_HalloweenKartNoTurn>(LOCAL_E);
if (HasCondition<TFCond_FreezeInput>(LOCAL_E))
RemoveCondition<TFCond_FreezeInput>(LOCAL_E);
}
},
"remove_cart_cond");

View File

@ -266,7 +266,7 @@ static std::pair<CachedEntity *, float> getNearestPlayerDistance()
for (int i = 1; i <= g_IEngine->GetMaxClients(); 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());
best_ent = ent;

321
src/hacks/SpellForcer.cpp Normal file
View 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

View File

@ -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_right{ "warp.on-hit.right", "true" };
static settings::Boolean debug_seqout{ "debug.warp_seqout", "false" };
// Hidden control rvars for communtiy servers
static settings::Int maxusrcmdprocessticks("warp.maxusrcmdprocessticks", "24");

View File

@ -10,10 +10,6 @@ namespace hooked_methods
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];
else
return original::GetUserCmd(this_, sequence_number);
return &GetCmds(this_)[sequence_number % VERIFIED_CMD_SIZE];
}
} // namespace hooked_methods

View File

@ -83,6 +83,9 @@ ItemManager::ItemManager() : mapper()
RegisterModelMapping("models/items/crystal_ball_pickup_major.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
RegisterModelMapping("models/props_halloween/ghost.mdl", HALLOWEEN_GHOST);
RegisterModelMapping("models/props_halloween/ghost_no_hat_red.mdl", HALLOWEEN_GHOST);
@ -118,90 +121,93 @@ ItemManager::ItemManager() : mapper()
RegisterModelMapping("models/items/medkit_overheal.mdl", ITEM_TF2C_PILL);
// TF2C spawners
specials.push_back([](CachedEntity *entity) -> k_EItemType {
static ItemModelMapper tf2c_weapon_mapper = []() -> ItemModelMapper {
ItemModelMapper result;
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_supershotgun_mercenary.mdl", ITEM_TF2C_W_SUPERSHOTGUN_MERCENARY);
result.RegisterItem("models/weapons/w_models/w_rocketbeta.mdl", ITEM_TF2C_W_ROCKETBETA);
result.RegisterItem("models/weapons/w_models/w_grenade_grenadelauncher.mdl", ITEM_TF2C_W_GRENADE_GRENADELAUNCHER);
result.RegisterItem("models/weapons/w_models/w_grenadelauncher.mdl", ITEM_TF2C_W_GRENADELAUNCHER);
result.RegisterItem("models/weapons/w_models/w_grenade_pipebomb.mdl", ITEM_TF2C_W_GRENADE_PIPEBOMB);
result.RegisterItem("models/weapons/w_models/w_cigarette_case.mdl", ITEM_TF2C_W_CIGARETTE_CASE);
result.RegisterItem("models/weapons/w_models/w_brandingiron.mdl", ITEM_TF2C_W_BRANDINGIRON);
result.RegisterItem("models/weapons/w_models/w_banhammer.mdl", ITEM_TF2C_W_BANHAMMER);
result.RegisterItem("models/weapons/w_models/w_sniperrifle.mdl", ITEM_TF2C_W_SNIPERRIFLE);
result.RegisterItem("models/weapons/w_models/w_grenade_heal.mdl", ITEM_TF2C_W_GRENADE_HEAL);
result.RegisterItem("models/weapons/w_models/w_hammerfists.mdl", ITEM_TF2C_W_HAMMERFISTS);
result.RegisterItem("models/weapons/w_models/w_grenade_nail.mdl", ITEM_TF2C_W_GRENADE_NAIL);
result.RegisterItem("models/weapons/w_models/w_dart.mdl", ITEM_TF2C_W_DART);
result.RegisterItem("models/weapons/w_models/w_rpg.mdl", ITEM_TF2C_W_RPG);
result.RegisterItem("models/weapons/w_models/w_umbrella_civilian.mdl", ITEM_TF2C_W_UMBRELLA_CIVILIAN);
result.RegisterItem("models/weapons/w_models/w_grenade_frag.mdl", ITEM_TF2C_W_GRENADE_FRAG);
result.RegisterItem("models/weapons/w_models/w_grenade_mirv.mdl", ITEM_TF2C_W_GRENADE_MIRV);
result.RegisterItem("models/weapons/w_models/w_medigun.mdl", ITEM_TF2C_W_MEDIGUN);
result.RegisterItem("models/weapons/w_models/w_grenade_mirv_demo.mdl", ITEM_TF2C_W_GRENADE_MIRV_DEMO);
result.RegisterItem("models/weapons/w_models/w_pickaxe.mdl", ITEM_TF2C_W_PICKAXE);
result.RegisterItem("models/weapons/w_models/w_syringegun.mdl", ITEM_TF2C_W_SYRINGEGUN);
result.RegisterItem("models/weapons/w_models/w_scattergun.mdl", ITEM_TF2C_W_SCATTERGUN);
result.RegisterItem("models/weapons/w_models/w_stengun.mdl", ITEM_TF2C_W_STENGUN);
result.RegisterItem("models/weapons/w_models/w_snubnose.mdl", ITEM_TF2C_W_SNUBNOSE);
result.RegisterItem("models/weapons/w_models/w_minigun.mdl", ITEM_TF2C_W_MINIGUN);
result.RegisterItem("models/weapons/w_models/w_wrench.mdl", ITEM_TF2C_W_WRENCH);
result.RegisterItem("models/weapons/w_models/w_bat.mdl", ITEM_TF2C_W_BAT);
result.RegisterItem("models/weapons/w_models/w_sapper.mdl", ITEM_TF2C_W_SAPPER);
result.RegisterItem("models/weapons/w_models/w_rocket.mdl", ITEM_TF2C_W_ROCKET);
result.RegisterItem("models/weapons/w_models/w_hammer.mdl", ITEM_TF2C_W_HAMMER);
result.RegisterItem("models/weapons/w_models/w_fishwhacker.mdl", ITEM_TF2C_W_FISHWHACKER);
result.RegisterItem("models/weapons/w_models/w_kritzkrieg.mdl", ITEM_TF2C_W_KRITZKRIEG);
result.RegisterItem("models/weapons/w_models/w_flaregun.mdl", ITEM_TF2C_W_FLAREGUN);
result.RegisterItem("models/weapons/w_models/w_shovel.mdl", ITEM_TF2C_W_SHOVEL);
result.RegisterItem("models/weapons/w_models/w_pistol.mdl", ITEM_TF2C_W_PISTOL);
result.RegisterItem("models/weapons/w_models/w_smg.mdl", ITEM_TF2C_W_SMG);
result.RegisterItem("models/weapons/w_models/w_leadpipe.mdl", ITEM_TF2C_W_LEADPIPE);
result.RegisterItem("models/weapons/w_models/w_flaregun_shell.mdl", ITEM_TF2C_W_FLAREGUN_SHELL);
result.RegisterItem("models/weapons/w_models/w_grenade_emp.mdl", ITEM_TF2C_W_GRENADE_EMP);
result.RegisterItem("models/weapons/w_models/w_cyclops.mdl", ITEM_TF2C_W_CYCLOPS);
result.RegisterItem("models/weapons/w_models/w_machete.mdl", ITEM_TF2C_W_MACHETE);
result.RegisterItem("models/weapons/w_models/w_shotgun.mdl", ITEM_TF2C_W_SHOTGUN);
result.RegisterItem("models/weapons/w_models/w_revolver.mdl", ITEM_TF2C_W_REVOLVER);
result.RegisterItem("models/weapons/w_models/w_grenade_gas.mdl", ITEM_TF2C_W_GRENADE_GAS);
result.RegisterItem("models/weapons/w_models/w_grenade_beartrap.mdl", ITEM_TF2C_W_GRENADE_BEARTRAP);
result.RegisterItem("models/weapons/w_models/w_fireaxe.mdl", ITEM_TF2C_W_FIREAXE);
result.RegisterItem("models/weapons/w_models/w_grenade_conc.mdl", ITEM_TF2C_W_GRENADE_CONC);
result.RegisterItem("models/weapons/w_models/w_nailgun.mdl", ITEM_TF2C_W_NAILGUN);
result.RegisterItem("models/weapons/w_models/w_bonesaw.mdl", ITEM_TF2C_W_BONESAW);
result.RegisterItem("models/weapons/w_models/w_stickybomb_launcher.mdl", ITEM_TF2C_W_STICKYBOMB_LAUNCHER);
result.RegisterItem("models/weapons/w_models/w_grenade_bomblet.mdl", ITEM_TF2C_W_GRENADE_BOMBLET);
result.RegisterItem("models/weapons/w_models/w_tranq.mdl", ITEM_TF2C_W_TRANQ);
result.RegisterItem("models/weapons/w_models/w_dynamite.mdl", ITEM_TF2C_W_DYNAMITE);
result.RegisterItem("models/weapons/w_models/w_club.mdl", ITEM_TF2C_W_CLUB);
result.RegisterItem("models/weapons/w_models/w_tommygun.mdl", ITEM_TF2C_W_TOMMYGUN);
result.RegisterItem("models/weapons/w_models/w_coffepot.mdl", ITEM_TF2C_W_COFFEPOT);
result.RegisterItem("models/weapons/w_models/w_stickybomb.mdl", ITEM_TF2C_W_STICKYBOMB);
result.RegisterItem("models/weapons/w_models/w_rocketlauncher.mdl", ITEM_TF2C_W_ROCKETLAUNCHER);
result.RegisterItem("models/weapons/w_models/w_flamethrower.mdl", ITEM_TF2C_W_FLAMETHROWER);
result.RegisterItem("models/weapons/w_models/w_crowbar.mdl", ITEM_TF2C_W_CROWBAR);
result.RegisterItem("models/weapons/w_models/w_builder.mdl", ITEM_TF2C_W_BUILDER);
result.RegisterItem("models/weapons/w_models/w_heavy_artillery.mdl", ITEM_TF2C_W_HEAVY_ARTILLERY);
result.RegisterItem("models/weapons/w_models/w_knife.mdl", ITEM_TF2C_W_KNIFE);
result.RegisterItem("models/weapons/w_models/w_pda_engineer.mdl", ITEM_TF2C_W_PDA_ENGINEER);
result.RegisterItem("models/weapons/w_models/w_bottle_broken.mdl", ITEM_TF2C_W_BOTTLE_BROKEN);
result.RegisterItem("models/weapons/w_models/w_toolbox.mdl", ITEM_TF2C_W_TOOLBOX);
result.RegisterItem("models/weapons/w_models/w_syringe_proj.mdl", ITEM_TF2C_W_SYRINGE_PROJ);
result.RegisterItem("models/weapons/w_models/w_nail.mdl", ITEM_TF2C_W_NAIL);
result.RegisterItem("models/weapons/w_models/w_syringe.mdl", ITEM_TF2C_W_SYRINGE);
result.RegisterItem("models/weapons/w_models/w_overhealer.mdl", ITEM_TF2C_W_OVERHEALER);
return result;
}();
if (entity->m_iClassID() == CL_CLASS(CWeaponSpawner))
specials.push_back(
[](CachedEntity *entity) -> k_EItemType
{
return tf2c_weapon_mapper.GetItemType(entity);
}
return ITEM_NONE;
});
static ItemModelMapper tf2c_weapon_mapper = []() -> ItemModelMapper
{
ItemModelMapper result;
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_supershotgun_mercenary.mdl", ITEM_TF2C_W_SUPERSHOTGUN_MERCENARY);
result.RegisterItem("models/weapons/w_models/w_rocketbeta.mdl", ITEM_TF2C_W_ROCKETBETA);
result.RegisterItem("models/weapons/w_models/w_grenade_grenadelauncher.mdl", ITEM_TF2C_W_GRENADE_GRENADELAUNCHER);
result.RegisterItem("models/weapons/w_models/w_grenadelauncher.mdl", ITEM_TF2C_W_GRENADELAUNCHER);
result.RegisterItem("models/weapons/w_models/w_grenade_pipebomb.mdl", ITEM_TF2C_W_GRENADE_PIPEBOMB);
result.RegisterItem("models/weapons/w_models/w_cigarette_case.mdl", ITEM_TF2C_W_CIGARETTE_CASE);
result.RegisterItem("models/weapons/w_models/w_brandingiron.mdl", ITEM_TF2C_W_BRANDINGIRON);
result.RegisterItem("models/weapons/w_models/w_banhammer.mdl", ITEM_TF2C_W_BANHAMMER);
result.RegisterItem("models/weapons/w_models/w_sniperrifle.mdl", ITEM_TF2C_W_SNIPERRIFLE);
result.RegisterItem("models/weapons/w_models/w_grenade_heal.mdl", ITEM_TF2C_W_GRENADE_HEAL);
result.RegisterItem("models/weapons/w_models/w_hammerfists.mdl", ITEM_TF2C_W_HAMMERFISTS);
result.RegisterItem("models/weapons/w_models/w_grenade_nail.mdl", ITEM_TF2C_W_GRENADE_NAIL);
result.RegisterItem("models/weapons/w_models/w_dart.mdl", ITEM_TF2C_W_DART);
result.RegisterItem("models/weapons/w_models/w_rpg.mdl", ITEM_TF2C_W_RPG);
result.RegisterItem("models/weapons/w_models/w_umbrella_civilian.mdl", ITEM_TF2C_W_UMBRELLA_CIVILIAN);
result.RegisterItem("models/weapons/w_models/w_grenade_frag.mdl", ITEM_TF2C_W_GRENADE_FRAG);
result.RegisterItem("models/weapons/w_models/w_grenade_mirv.mdl", ITEM_TF2C_W_GRENADE_MIRV);
result.RegisterItem("models/weapons/w_models/w_medigun.mdl", ITEM_TF2C_W_MEDIGUN);
result.RegisterItem("models/weapons/w_models/w_grenade_mirv_demo.mdl", ITEM_TF2C_W_GRENADE_MIRV_DEMO);
result.RegisterItem("models/weapons/w_models/w_pickaxe.mdl", ITEM_TF2C_W_PICKAXE);
result.RegisterItem("models/weapons/w_models/w_syringegun.mdl", ITEM_TF2C_W_SYRINGEGUN);
result.RegisterItem("models/weapons/w_models/w_scattergun.mdl", ITEM_TF2C_W_SCATTERGUN);
result.RegisterItem("models/weapons/w_models/w_stengun.mdl", ITEM_TF2C_W_STENGUN);
result.RegisterItem("models/weapons/w_models/w_snubnose.mdl", ITEM_TF2C_W_SNUBNOSE);
result.RegisterItem("models/weapons/w_models/w_minigun.mdl", ITEM_TF2C_W_MINIGUN);
result.RegisterItem("models/weapons/w_models/w_wrench.mdl", ITEM_TF2C_W_WRENCH);
result.RegisterItem("models/weapons/w_models/w_bat.mdl", ITEM_TF2C_W_BAT);
result.RegisterItem("models/weapons/w_models/w_sapper.mdl", ITEM_TF2C_W_SAPPER);
result.RegisterItem("models/weapons/w_models/w_rocket.mdl", ITEM_TF2C_W_ROCKET);
result.RegisterItem("models/weapons/w_models/w_hammer.mdl", ITEM_TF2C_W_HAMMER);
result.RegisterItem("models/weapons/w_models/w_fishwhacker.mdl", ITEM_TF2C_W_FISHWHACKER);
result.RegisterItem("models/weapons/w_models/w_kritzkrieg.mdl", ITEM_TF2C_W_KRITZKRIEG);
result.RegisterItem("models/weapons/w_models/w_flaregun.mdl", ITEM_TF2C_W_FLAREGUN);
result.RegisterItem("models/weapons/w_models/w_shovel.mdl", ITEM_TF2C_W_SHOVEL);
result.RegisterItem("models/weapons/w_models/w_pistol.mdl", ITEM_TF2C_W_PISTOL);
result.RegisterItem("models/weapons/w_models/w_smg.mdl", ITEM_TF2C_W_SMG);
result.RegisterItem("models/weapons/w_models/w_leadpipe.mdl", ITEM_TF2C_W_LEADPIPE);
result.RegisterItem("models/weapons/w_models/w_flaregun_shell.mdl", ITEM_TF2C_W_FLAREGUN_SHELL);
result.RegisterItem("models/weapons/w_models/w_grenade_emp.mdl", ITEM_TF2C_W_GRENADE_EMP);
result.RegisterItem("models/weapons/w_models/w_cyclops.mdl", ITEM_TF2C_W_CYCLOPS);
result.RegisterItem("models/weapons/w_models/w_machete.mdl", ITEM_TF2C_W_MACHETE);
result.RegisterItem("models/weapons/w_models/w_shotgun.mdl", ITEM_TF2C_W_SHOTGUN);
result.RegisterItem("models/weapons/w_models/w_revolver.mdl", ITEM_TF2C_W_REVOLVER);
result.RegisterItem("models/weapons/w_models/w_grenade_gas.mdl", ITEM_TF2C_W_GRENADE_GAS);
result.RegisterItem("models/weapons/w_models/w_grenade_beartrap.mdl", ITEM_TF2C_W_GRENADE_BEARTRAP);
result.RegisterItem("models/weapons/w_models/w_fireaxe.mdl", ITEM_TF2C_W_FIREAXE);
result.RegisterItem("models/weapons/w_models/w_grenade_conc.mdl", ITEM_TF2C_W_GRENADE_CONC);
result.RegisterItem("models/weapons/w_models/w_nailgun.mdl", ITEM_TF2C_W_NAILGUN);
result.RegisterItem("models/weapons/w_models/w_bonesaw.mdl", ITEM_TF2C_W_BONESAW);
result.RegisterItem("models/weapons/w_models/w_stickybomb_launcher.mdl", ITEM_TF2C_W_STICKYBOMB_LAUNCHER);
result.RegisterItem("models/weapons/w_models/w_grenade_bomblet.mdl", ITEM_TF2C_W_GRENADE_BOMBLET);
result.RegisterItem("models/weapons/w_models/w_tranq.mdl", ITEM_TF2C_W_TRANQ);
result.RegisterItem("models/weapons/w_models/w_dynamite.mdl", ITEM_TF2C_W_DYNAMITE);
result.RegisterItem("models/weapons/w_models/w_club.mdl", ITEM_TF2C_W_CLUB);
result.RegisterItem("models/weapons/w_models/w_tommygun.mdl", ITEM_TF2C_W_TOMMYGUN);
result.RegisterItem("models/weapons/w_models/w_coffepot.mdl", ITEM_TF2C_W_COFFEPOT);
result.RegisterItem("models/weapons/w_models/w_stickybomb.mdl", ITEM_TF2C_W_STICKYBOMB);
result.RegisterItem("models/weapons/w_models/w_rocketlauncher.mdl", ITEM_TF2C_W_ROCKETLAUNCHER);
result.RegisterItem("models/weapons/w_models/w_flamethrower.mdl", ITEM_TF2C_W_FLAMETHROWER);
result.RegisterItem("models/weapons/w_models/w_crowbar.mdl", ITEM_TF2C_W_CROWBAR);
result.RegisterItem("models/weapons/w_models/w_builder.mdl", ITEM_TF2C_W_BUILDER);
result.RegisterItem("models/weapons/w_models/w_heavy_artillery.mdl", ITEM_TF2C_W_HEAVY_ARTILLERY);
result.RegisterItem("models/weapons/w_models/w_knife.mdl", ITEM_TF2C_W_KNIFE);
result.RegisterItem("models/weapons/w_models/w_pda_engineer.mdl", ITEM_TF2C_W_PDA_ENGINEER);
result.RegisterItem("models/weapons/w_models/w_bottle_broken.mdl", ITEM_TF2C_W_BOTTLE_BROKEN);
result.RegisterItem("models/weapons/w_models/w_toolbox.mdl", ITEM_TF2C_W_TOOLBOX);
result.RegisterItem("models/weapons/w_models/w_syringe_proj.mdl", ITEM_TF2C_W_SYRINGE_PROJ);
result.RegisterItem("models/weapons/w_models/w_nail.mdl", ITEM_TF2C_W_NAIL);
result.RegisterItem("models/weapons/w_models/w_syringe.mdl", ITEM_TF2C_W_SYRINGE);
result.RegisterItem("models/weapons/w_models/w_overhealer.mdl", ITEM_TF2C_W_OVERHEALER);
return result;
}();
if (entity->m_iClassID() == CL_CLASS(CWeaponSpawner))
{
return tf2c_weapon_mapper.GetItemType(entity);
}
return ITEM_NONE;
});
}
void ItemManager::RegisterModelMapping(std::string path, k_EItemType type)
@ -266,7 +272,9 @@ k_EItemType ItemModelMapper::GetItemType(CachedEntity *entity)
}
ItemManager g_ItemManager;
static InitRoutine init_itemtypes([]() {
EC::Register(
EC::LevelInit, []() { g_ItemManager = ItemManager{}; }, "clear_itemtypes");
});
static InitRoutine init_itemtypes(
[]()
{
EC::Register(
EC::LevelInit, []() { g_ItemManager = ItemManager{}; }, "clear_itemtypes");
});

View File

@ -609,7 +609,7 @@ void CL_SendMove_hook()
current_weapon_spread = 0.0;
// 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
CL_SendMove_t original = (CL_SendMove_t) cl_nospread_sendmovedetour.GetOriginalFunc();

View File

@ -190,6 +190,16 @@ bool TFPlayerResource::isAlive(int 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)
{
IClientEntity *ent = g_IEntityList->GetClientEntity(entity);