--[[
Simple and easy system that allows you to catch a player who using cheat codes in a single player game.
Configuration:
— Use the AntiCheat.debug boolean property to enable/disable debug messages (enabled by default).
— You can change AntiCheat.punish(cheat) function to your liking to create any reaction to entering a cheat code.
This function takes a cheat code string as an argument, which allows you to customize the reaction for each cheat code.
— The AntiCheat.list table stores a list of cheat codes, you can remove any from there or add something if necessary.
Control:
— To initialize the system, you need to call the AntiCheat.init() method.
I would not recommend calling this function too early, since the code must access some default frames that may not have time to initialize, which will lead to a game crash.
I took this problem into account and tried to make it more secure by adding a delay timer and checking for access to frames.
After that, I injected the function into main() without any problems in my tests, but I still think I should warn you about it.
— To destroy the system, you can call the AntiCheat.destroy() function.
— To temporarily disable the system, you can use the AntiCheat.setEnable(false) function.
Accordingly, to enable triggers later, you should call AntiCheat.setEnable(true).
--]]
AntiCheat = {}
AntiCheat.debug = true
function AntiCheat.punish(cheat)
-- You can add any reaction to entering a cheat code to this function
-- In this example, the player gets defeat
AntiCheat.displayMessage(cheat)
CustomDefeatBJ(GetLocalPlayer(), "Good luck next time!")
end
AntiCheat.list = { -- Table of known cheat codes in Warcraft 3
"whosyourdaddy", -- All units and buildings gain full invulnerability, units will be able to 1-hit kill any opponent or enemy structure (does not effect friendly fire)
"iseedeadpeople", -- Full map is revealed, fog of war disabled
"allyourbasearebelongtous", -- Instantly win the current mission
"somebodysetupthebomb", -- Instantly lose the current mission
"thereisnospoon", -- All units gain infinite mana
"greedisgood", -- Instantly obtain set number of lumber and gold
"keysersoze", -- Instantly obtain set number of gold
"leafittome", -- Instantly obtain set number of lumber
"iocanepowder", -- Enables fast acting death/decay of bodies
"pointbreak", -- Disables food limit for creating new units
"sharpandshiny", -- Instantly grants all upgrades
"synergy", -- Unlocks the full tech tree
"whoisjohngalt", -- Enables faster research time for upgrades
"warpten", -- Enables faster building times
"thedudeabides", -- Enables faster spell cooldown times
"riseandshine", -- Sets time of day to morning
"lightsout", -- Sets time of day to evening
"daylightsavings", -- Switches from day to night, halts or restarts the flow of the day/night cycle
"strengthandhonor", -- Disables game over screen after losing objectives in campaign mode
"itvexesme", -- Disables victory conditions
"motherland", -- Selects a mission number for the chosen race to warp to
"tenthleveltaurenchieftan", -- Plays a special music track "Power of the Horde"
-- Source: https://www.ign.com/wikis/warcraft-3/PC_Cheats_and_Secrets_-_List_of_Warcraft_3_Cheat_Codes
}
-- Debug Messages
AntiCheat.success = "|c0000FF80Anti-Cheat system is enabled.|r"
AntiCheat.cancel = "|c00EDED12Not a single-player mode. Initialization of the Anti-Cheat system was canceled.|r"
AntiCheat.error = "|c00FF0000Error when trying to access Edit Box.\r\nThe Anti-Cheat system was not enabled.|r"
AntiCheat.detect = "|c00FF0000\"CHEATCODE\" cheat code detected|r"
AntiCheat.states = {}
function AntiCheat.init()
if not ReloadGameCachesFromDisk() then
AntiCheat.displayMessage("cancel")
return
end
local saveLoadedTrigger, textChangedTrigger, textEnteredTrigger = CreateTrigger(), CreateTrigger(), CreateTrigger()
local states, cheats, punish = AntiCheat.states, AntiCheat.list, AntiCheat.punish
TriggerRegisterGameEvent(saveLoadedTrigger, EVENT_GAME_LOADED)
TriggerAddCondition(saveLoadedTrigger, Condition(AntiCheat.setBoxEvents))
TriggerAddCondition(textChangedTrigger, Condition(function()
states[2] = states[1]
states[1] = BlzGetTriggerFrameText()
end))
TriggerAddCondition(textEnteredTrigger, Condition(function()
local enteredText = string.lower(states[2])
for i = 1, #cheats do
if string.find(enteredText, cheats[i]) then
punish(cheats[i])
break
end
end
end))
AntiCheat.triggers = {textChangedTrigger, textEnteredTrigger, saveLoadedTrigger}
AntiCheat.setBoxEvents()
end
function AntiCheat.setBoxEvents()
-- Event registration has been moved to a separate function,
-- since when loading a saved game it will need to be done again.
local t, ac = CreateTimer(), AntiCheat
-- Accessing to the frame while the map is initializing can result in a fatal error, so delay is needed
TimerStart(t, .2, false, function()
local eBox = ac.getEditBox()
if eBox then
BlzTriggerRegisterFrameEvent(ac.triggers[1], eBox, FRAMEEVENT_EDITBOX_TEXT_CHANGED)
BlzTriggerRegisterFrameEvent(ac.triggers[2], eBox, FRAMEEVENT_EDITBOX_ENTER)
ac.displayMessage("success")
else
ac.displayMessage("error")
ac.destroy()
end
DestroyTimer(t)
end)
end
function AntiCheat.setEnable(enable)
local triggers = AntiCheat.triggers
if not triggers then return end
if enable then
EnableTrigger(triggers[2])
return
end
DisableTrigger(triggers[2])
end
function AntiCheat.getEditBox()
-- Includes frame access checks
local nullFrame = BlzGetOriginFrame(ConvertOriginFrameType(93242), 0);
local gameUI = BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0); if (gameUI == nullFrame) then return nil end
nullFrame = BlzFrameGetChild(gameUI, 93242);
local msgFrame = BlzFrameGetChild(gameUI, 11); if (msgFrame == nullFrame) then return nil end
local editBox = BlzFrameGetChild(msgFrame, 1); if (editBox == nullFrame) then return nil end
return editBox
end
function AntiCheat.displayMessage(mType)
if AntiCheat.debug then
if AntiCheat[mType] then
print(AntiCheat[mType])
return
end
for _, v in ipairs(AntiCheat.list) do
if v == mType then
local message = AntiCheat.detect:gsub("CHEATCODE", mType)
print(message)
return
end
end
end
end
function AntiCheat.destroy()
local triggers = AntiCheat.triggers
if triggers then
for _, v in ipairs(triggers) do
DestroyTrigger(v)
end
end
end