diff --git a/data/menu/nullifiedcat/misc.xml b/data/menu/nullifiedcat/misc.xml
index 6ad6c7f7..cd49315c 100755
--- a/data/menu/nullifiedcat/misc.xml
+++ b/data/menu/nullifiedcat/misc.xml
@@ -8,5 +8,6 @@
+
diff --git a/data/menu/nullifiedcat/misc/spellforcer.xml b/data/menu/nullifiedcat/misc/spellforcer.xml
new file mode 100644
index 00000000..00768cc3
--- /dev/null
+++ b/data/menu/nullifiedcat/misc/spellforcer.xml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/include/core/netvars.hpp b/include/core/netvars.hpp
index 24af4f05..9cc9135a 100644
--- a/include/core/netvars.hpp
+++ b/include/core/netvars.hpp
@@ -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
diff --git a/include/playerresource.h b/include/playerresource.h
index c54002e5..7abb62c7 100755
--- a/include/playerresource.h
+++ b/include/playerresource.h
@@ -29,6 +29,7 @@ public:
int getClass(int idx);
int getTeam(int idx);
bool isAlive(int idx);
+ bool isValid(int idx);
int entity;
};
diff --git a/include/sdk/CGameRules.h b/include/sdk/CGameRules.h
index 6b4095fa..94043007 100755
--- a/include/sdk/CGameRules.h
+++ b/include/sdk/CGameRules.h
@@ -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));
diff --git a/src/core/netvars.cpp b/src/core/netvars.cpp
index 44c0ca6b..de92d12b 100644
--- a/src/core/netvars.cpp
+++ b/src/core/netvars.cpp
@@ -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]");
diff --git a/src/crits.cpp b/src/crits.cpp
index f4242969..b23a97a2 100644
--- a/src/crits.cpp
+++ b/src/crits.cpp
@@ -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
diff --git a/src/hacks/CMakeLists.txt b/src/hacks/CMakeLists.txt
index 7eb9e198..1523f167 100755
--- a/src/hacks/CMakeLists.txt
+++ b/src/hacks/CMakeLists.txt
@@ -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")
diff --git a/src/hacks/Misc.cpp b/src/hacks/Misc.cpp
index ec698252..00756d9e 100644
--- a/src/hacks/Misc.cpp
+++ b/src/hacks/Misc.cpp
@@ -958,8 +958,6 @@ static InitRoutine init_pyrovision(
{
if (HasCondition(LOCAL_E))
RemoveCondition(LOCAL_E);
- if (HasCondition(LOCAL_E))
- RemoveCondition(LOCAL_E);
}
},
"remove_cart_cond");
diff --git a/src/hacks/NavBot.cpp b/src/hacks/NavBot.cpp
index dd6ef646..b6531b85 100644
--- a/src/hacks/NavBot.cpp
+++ b/src/hacks/NavBot.cpp
@@ -266,7 +266,7 @@ static std::pair 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;
diff --git a/src/hacks/SpellForcer.cpp b/src/hacks/SpellForcer.cpp
new file mode 100644
index 00000000..8680f27d
--- /dev/null
+++ b/src/hacks/SpellForcer.cpp
@@ -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(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 noisy_maps{ "" };
+
+static std::unordered_map spellmap_normal({ { 0, 4206970 }, { 1, 4206969 }, { 2, 4206972 }, { 3, 4206997 }, { 4, 4206971 }, { 5, 4206977 }, { 7, 4206973 }, { 8, 4206982 }, { 9, 4206976 } });
+static std::unordered_map spellmap_normal_rare({ { 7, 4206987 }, { 8, 4206982 }, { 9, 4207006 }, { 10, 4206983 }, { 11, 4206984 } });
+
+static std::unordered_map spellmap_helltower({ { 0, 4206998 }, { 1, 4206984 }, { 2, 4206985 }, { 3, 4206986 }, { 4, 4206987 }, { 5, 4206982 }, { 6, 4206983 } });
+
+static std::unordered_map spellmap_doomsday_normal({ { 0, 4206982 }, { 2, 4207006 }, { 4, 4206983 }, { 5, 4206984 } });
+static std::unordered_map spellmap_doomsday_rare({ { 7, 4207009 }, { 8, 4207014 }, { 9, 4207008 }, { 10, 4207007 } });
+
+static std::unordered_map spellmap_bumpercar({ { 12, 4206982 }, { 13, 4206983 }, { 14, 4206987 }, { 15, 4206988 } });
+// static std::unordered_map 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 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 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
diff --git a/src/hacks/Warp.cpp b/src/hacks/Warp.cpp
index cca19bca..6ac3d62d 100644
--- a/src/hacks/Warp.cpp
+++ b/src/hacks/Warp.cpp
@@ -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");
diff --git a/src/hooks/GetUserCmd.cpp b/src/hooks/GetUserCmd.cpp
index 375b213c..af976b7b 100644
--- a/src/hooks/GetUserCmd.cpp
+++ b/src/hooks/GetUserCmd.cpp
@@ -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
diff --git a/src/itemtypes.cpp b/src/itemtypes.cpp
index b76f6c4b..0e4a7bec 100644
--- a/src/itemtypes.cpp
+++ b/src/itemtypes.cpp
@@ -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");
+ });
diff --git a/src/nospread.cpp b/src/nospread.cpp
index 0933b03e..ea86ef42 100644
--- a/src/nospread.cpp
+++ b/src/nospread.cpp
@@ -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(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();
diff --git a/src/playerresource.cpp b/src/playerresource.cpp
index 969d500a..2f858370 100755
--- a/src/playerresource.cpp
+++ b/src/playerresource.cpp
@@ -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);