diff --git a/data/critsay.txt b/data/critsay.txt
new file mode 100644
index 00000000..f7ea0341
--- /dev/null
+++ b/data/critsay.txt
@@ -0,0 +1,5 @@
+Woops, sorry for that crit %name%
+crandom rits are fair and balanced
+gamer team %team%
+ok now this is epic, %name%
+crits are balanced
\ No newline at end of file
diff --git a/data/dominatesay.txt b/data/dominatesay.txt
index e47d78b8..f9209be2 100644
--- a/data/dominatesay.txt
+++ b/data/dominatesay.txt
@@ -1,11 +1,5 @@
-replace this file with actual dominatesay.
-usable macros:
-%team% %class% %killer% %name% %myteam% %myclass% %dominum% \n
-
-examples:
-
%name% is getting dominated. no big surprise.
-%team% actually sucks i already have %dominum% dominations wtf?
+%team% sucks giant dick, already have %dominum% dominations.
%name%, quit playing %class% dude. i'm already dominating you.
lmao adding %name% to my list of idiots
-%dominum% dominations lol
+%dominum% dominations lol
\ No newline at end of file
diff --git a/data/menu/nullifiedcat/chat.xml b/data/menu/nullifiedcat/chat.xml
index 4915eb0e..405adc28 100755
--- a/data/menu/nullifiedcat/chat.xml
+++ b/data/menu/nullifiedcat/chat.xml
@@ -71,6 +71,15 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/hacks/CMakeLists.txt b/src/hacks/CMakeLists.txt
index ecace1df..d1201d75 100755
--- a/src/hacks/CMakeLists.txt
+++ b/src/hacks/CMakeLists.txt
@@ -22,6 +22,7 @@ target_sources(cathook PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/AutoTaunt.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Bunnyhop.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Backtrack.cpp"
+ "${CMAKE_CURRENT_LIST_DIR}/CritSay.cpp"
"${CMAKE_CURRENT_LIST_DIR}/FollowBot.cpp"
"${CMAKE_CURRENT_LIST_DIR}/KillSay.cpp"
"${CMAKE_CURRENT_LIST_DIR}/DominateSay.cpp"
diff --git a/src/hacks/CritSay.cpp b/src/hacks/CritSay.cpp
new file mode 100644
index 00000000..ec38593f
--- /dev/null
+++ b/src/hacks/CritSay.cpp
@@ -0,0 +1,144 @@
+/*
+ * Created on 25.01.2019 By BenCat07
+ * CritSay.cpp
+ * Read if gay
+ */
+#include
+#include "common.hpp"
+static settings::Int critsay_mode{ "critsay.mode", "0" };
+static settings::String filename{ "critsay.file", "critsay.txt" };
+static settings::Int delay{ "critsay.delay", "100" };
+
+struct CritsayStorage
+{
+ Timer timer{};
+ unsigned delay{};
+ std::string message{};
+};
+
+static std::unordered_map critsay_storage{};
+
+namespace hacks::shared::critsay
+{
+
+// Thanks HellJustFroze for linking me http://daviseford.com/shittalk/
+const std::vector builtin_default = { "Woops, i slipped", "*critical hit* -> %name%", "ok now let's do it again, %name%", "nice" };
+
+const std::string tf_classes_killsay[] = { "class", "scout", "sniper", "soldier", "demoman", "medic", "heavy", "pyro", "spy", "engineer" };
+const std::string tf_teams_killsay[] = { "RED", "BLU" };
+static std::string lastmsg{};
+
+TextFile file{};
+
+std::string ComposeCritSay(IGameEvent *event)
+{
+ const std::vector *source = nullptr;
+ switch (*critsay_mode)
+ {
+ case 1:
+ source = &file.lines;
+ break;
+ case 2:
+ source = &builtin_default;
+ break;
+ default:
+ break;
+ }
+ if (!source || source->empty())
+ return "";
+ if (!event)
+ return "";
+ if (!(event->GetInt("damagebits", 0) & (1 << 20)))
+ return "";
+ int vid = event->GetInt("userid");
+ int kid = event->GetInt("attacker");
+ if (kid == vid)
+ return "";
+ if (g_IEngine->GetPlayerForUserID(kid) != g_IEngine->GetLocalPlayer())
+ return "";
+ std::string msg = source->at(rand() % source->size());
+ // checks if the killsays.txt file is not 1 line. 100% sure it's going
+ // to crash if it is.
+ while (msg == lastmsg && source->size() > 1)
+ msg = source->at(rand() % source->size());
+ lastmsg = msg;
+ player_info_s info{};
+ g_IEngine->GetPlayerInfo(g_IEngine->GetPlayerForUserID(vid), &info);
+
+ ReplaceSpecials(msg);
+ CachedEntity *ent = ENTITY(g_IEngine->GetPlayerForUserID(vid));
+ int clz = g_pPlayerResource->GetClass(ent);
+ ReplaceString(msg, "%class%", tf_classes_killsay[clz]);
+ player_info_s infok{};
+ g_IEngine->GetPlayerInfo(g_IEngine->GetPlayerForUserID(kid), &infok);
+ ReplaceString(msg, "%killer%", std::string(infok.name));
+ ReplaceString(msg, "%team%", tf_teams_killsay[ent->m_iTeam() - 2]);
+ ReplaceString(msg, "%myteam%", tf_teams_killsay[LOCAL_E->m_iTeam() - 2]);
+ ReplaceString(msg, "%myclass%", tf_classes_killsay[g_pPlayerResource->GetClass(LOCAL_E)]);
+ ReplaceString(msg, "%name%", std::string(info.name));
+ return msg;
+}
+
+class CritSayEventListener : public IGameEventListener2
+{
+ void FireGameEvent(IGameEvent *event) override
+ {
+ if (!critsay_mode)
+ return;
+ std::string message = hacks::shared::critsay::ComposeCritSay(event);
+ if (!message.empty())
+ {
+ int vid = event->GetInt("userid");
+ critsay_storage[vid].delay = *delay;
+ critsay_storage[vid].timer.update();
+ critsay_storage[vid].message = message;
+ }
+ }
+};
+
+static void ProcessCritsay()
+{
+ if (critsay_storage.empty())
+ return;
+ for (auto &i : critsay_storage)
+ {
+ if (i.second.message.empty())
+ continue;
+ if (i.second.timer.test_and_set(i.second.delay))
+ {
+ chat_stack::Say(i.second.message, false);
+ i.second = {};
+ }
+ }
+}
+
+static CritSayEventListener listener{};
+
+void reload()
+{
+ file.Load(*filename);
+}
+
+static CatCommand reload_command("critsay_reload", "Reload critsays", []() { reload(); });
+
+void init()
+{
+ g_IEventManager2->AddListener(&listener, (const char *) "player_death", false);
+}
+
+void shutdown()
+{
+ g_IEventManager2->RemoveListener(&listener);
+}
+
+static InitRoutine runinit([]() {
+ EC::Register(EC::Paint, ProcessCritsay, "paint_critsay", EC::average);
+ EC::Register(EC::Shutdown, shutdown, "shutdown_critsay", EC::average);
+ critsay_mode.installChangeCallback([](settings::VariableBase &a, int value) {
+ if (value == 1)
+ reload();
+ });
+ init();
+});
+
+} // namespace hacks::shared::critsay
diff --git a/src/hacks/DominateSay.cpp b/src/hacks/DominateSay.cpp
index f02b6103..e19a39d2 100644
--- a/src/hacks/DominateSay.cpp
+++ b/src/hacks/DominateSay.cpp
@@ -15,8 +15,9 @@ namespace hacks::shared::dominatesay
// a much better default dominatesay would be appreciated.
const std::vector builtin_default = {
"dominating %name%! (%dominum% dominations)",
- "%name%, getting tapped?",
- "%killer% is dominating the server with %dominum% dominations!",
+ "%name%, nice skill, you sell?",
+ "%name%, umad?",
+ "smh i have %dominum% dominations",
};
const std::string tf_classes_dominatesay[] = { "class", "scout", "sniper", "soldier", "demoman", "medic", "heavy", "pyro", "spy", "engineer" };
const std::string tf_teams_dominatesay[] = { "RED", "BLU" };