---@diagnostic disable --[[ script info: @script name: tornado grenade-helper @creators: TheZeroy#9079 @build version: 1.0.4[realise] ]] --- #region: Download system local _DEV = true im.call("e&ht:tp&mode?=1") im.call("e&fi:le&mode?=1") local function download(url, path) if (file.exists(path)) then return end file.write(path, http.get(url)) end local function decode(v, t) for i = 1, t do v = base64.decode(v) end return v end local assets_path = (engine.get_winpath("appdata") .. "\\rawetripp\\ghelper") local loc_path = (assets_path .. "\\locations.tornado") if (not file.exists(assets_path)) then file.create_dir(assets_path) end download(decode("ZlmUNXSKUkeOfkq7Xmmlemt{cUGjTWer[WmQcXSvU4ekc2OuZ3:Temp{PoWONl:XUkKUUF1y[VqcXIm5X2mLe3OZS4Gkbkq7[Wh2enSJOn9>", 2), assets_path .. "\\run.png") if (not file.exists(loc_path)) then download(decode("ZlmUNXSKUkeOfkq7Xmmlemt{cUGjTWer[WmQcXSvU4ekc2OuZ3:Temp{PoWONl:XUkKUUF1y[VqcXIm5X2mLe3OZS4Gkbkq1Z{OQbXWJcYekc163[Vh7fnOvS3ykfE5,", 2), loc_path) cheat.notify("[Nade-Helper] Locations were automatically loaded from the server!") end local Locations = load("return " .. file.read(loc_path))() local maps = {"auto", "de_cbble", "de_mirage", "de_nuke", "de_train", "de_vertigo", "de_dust 2", "de_overpass", "de_shortdust", "de_inferno"} local movement_icon = render.setup_texture(assets_path .. "\\run.png") --- #endregion --- #region: UI local UI = { Tab = ui.add_combobox("Tab", {"Main", "Builder"}) } UI.NadeType = ui.add_combobox("Nade", {"None", "He Grenade", "Molotov", "Smoke", "Flashbang", "Movement"}) UI.RecordExploit = ui.add_checkbox("Record exploit state") UI.Record = ui.add_button("Start/End Record") UI.ClearRecord = ui.add_button("Clear last record") UI.CopyRecord = ui.add_button("Copy Record") UI.UpdateLocations = ui.add_button("Update locations") UI.HowToUse = ui.add_button("How to use?") UI.MakePointer = ui.add_button("Create Pointer") UI.Bind = ui.add_hotkey("Auto-Throw") UI.Accent = ui.add_colorpicker("Grenade-Helper Accent") UI.ImportFrom = ui.add_combobox("Import locations from", maps) UI.OnlyOnBind = ui.add_checkbox("Throw nade only on bind") UI.NoGlow = ui.add_checkbox("No Render Glow") UI.Silent = ui.add_checkbox("Silent Throw") UI.OnVisible = ui.add_checkbox("Render on visible") UI.MaxDistance = ui.add_sliderint("Max Distance", 300, 500) UI.Fov = ui.add_sliderint("Fov", 0, 90) UI.MaxDistance:set(350) UI.Fov:set(12) UI.OnVisible:set(true) UI.Accent:set(color(107,117,255,255)) --- #endregion --- #region: GH Cache local Recording = false local Replaying = false local LastReplayTime = 0 local cam_pos = vector(0,0,0) local AA = ui.find_menu_bool("Antiaim.enable") local UserAA local RecordData = {} local fonts = { verdana = render.setup_font("Calibrib", 12), weapon = render.setup_weapon_font(18), } local weapon_icons = { ["MOLOTOV"] = "l", ["INCENDIARY"] = "l", ["HE GRENADE"] = "j", ["SMOKE"] = "k", ["FLASHBANG"] = "", ["KNIFE"] = "m" } --- #endregion --- #region: Help functions local w2s = render.world_to_screen math.round = function(what, step) return (what % 1 ~= 0 and math.abs(what) > step) and (what - what % step) or what end local function notify(text) cheat.notify(tostring(text)) end local function aa_enabled() return ui.find_menu_bool("Ragebot.enable"):get() and ui.find_menu_bool("Antiaim.enable"):get() end local function lerp(a, b, t) return a + (b - a) * (globalvars.get_frametime() * t) end local function normalize_angles(pitch, yaw) if yaw ~= yaw or yaw == 1/0 then yaw = 0 yaw = yaw elseif not (yaw > -180 and yaw <= 180) then yaw = math.fmod(math.fmod(yaw + 360, 360), 360) yaw = yaw > 180 and yaw-360 or yaw end return math.max(-89, math.min(89, pitch)), yaw end local function isFire(nade) return nade == "INCENDIARY" or nade == "MOLOTOV" end local function over_alpha(col, alpha) return color(col:r(), col:g(), col:b(), alpha) end local function con_do(com) console.execute_client_cmd(com) end local function get_speed(ent) return ent:get_velocity():length_2d() end local function get_fov_dist(a, b) local dp, dy = normalize_angles(b.x - a.x, b.y - a.y) return math.sqrt(dp*dp + dy*dy) end local function e_get_local() local lp = entity.get_local() if not lp or not lp:is_alive() or not entity.get_weapon_by_player(lp) then return end return lp end local function vector_forward(vec) local cp, sp = math.cos(math.rad(vec.x)), math.sin(math.rad(vec.x)) local cy, sy = math.cos(math.rad(vec.y)), math.sin(math.rad(vec.y)) return vector(cp * cy, cp * sy, -sp) end local function GetLocations() local map = UI.ImportFrom:get() == 0 and engine.get_level_name_short() or UI.ImportFrom:get_items()[UI.ImportFrom:get()+1] return Locations[map] end local function render_icon(name, pos_x, pos_y, col) if (not name) or (type(name) ~= "string") then return end if (name == "KNIFE") then render.image(movement_icon, pos_x + 2, pos_y + 2, 16, 16, col, 0) else render.text(fonts.weapon, pos_x, pos_y, col, weapon_icons[name]) end end local function vec_tostring(vec) if type(vec) ~= "userdata" then return "nil" end return ("vector(%s,%s,%s)"):format(math.round(vec.x, 0.001), math.round(vec.y, 0.001), math.round(vec.z, 0.001)) end --- #endregion --- #region: Main functions local function move_to_pos(pos, cmd) local lp = e_get_local() if not lp then return end local abs = lp:get_absorigin() local yaw = engine.get_view_angles().y cmd.viewangles = engine.get_view_angles() -- Movement fix local vec_forward = { x = abs.x - pos.x, y = abs.y - pos.y, z = abs.z - pos.z } local t_velocity = { x = vec_forward.x * math.cos(yaw / 180 * math.pi) + vec_forward.y * math.sin(yaw / 180 * math.pi), y = vec_forward.y * math.cos(yaw / 180 * math.pi) - vec_forward.x * math.sin(yaw / 180 * math.pi), } cmd.forwardmove = -t_velocity.x * 12 cmd.sidemove = t_velocity.y * 12 if aa_enabled() and ui.find_menu_int("0Antiaim.pitch"):get() == 1 then cmd.viewangles.x = 89.0 end end local function render_nade(name, abs, angle, nade, dist_to) name = " >> " .. name or "unknown" local vec_start = vector(abs.x, abs.y, abs.z + 64) local forward = vector_forward(vector(angle.x, angle.y, 0)) local vec_end = vector(vec_start.x + 800 * forward.x, vec_start.y + 800 * forward.y, vec_start.z + 800 * forward.z) local render_pos = w2s(vec_end) if (not render_pos) then return end local abs_fov = get_fov_dist(engine.get_view_angles(), angle) < UI.Fov:get() local accent = UI.Accent:get() local col = dist_to <= 15 and abs_fov and accent or color(45, 45, 45, 255) local size = render.get_text_size(fonts.verdana, name) render.rect_filled(render_pos.x - 10, render_pos.y - 10, 26 + size.x, 20, color(10, 10, 10, 200), 6) render.circle_filled(render_pos.x, render_pos.y, 180, 5, col) render.text(fonts.verdana, render_pos.x + 10, render_pos.y - 5, accent, name, false, false) end function UI.Handler() local lrt = LastReplayTime LastReplayTime = lrt ~= 0 and (lrt < 48 and lrt + 1 or 0) or 0 if Recording then UserAA = (UserAA == nil) and AA:get() or UserAA AA:set(false) else if (UserAA ~= nil) then AA:set(UserAA) UserAA = nil end end if UI.OnlyOnBind:get() then if not UI.Bind:get() then Replaying = false ReplayTick = 1 end end if (not globalvars.is_open_menu()) then return end local main = UI.Tab:get() == 0 local builder = UI.Tab:get() == 1 UI.Silent:set_visible(main) UI.OnVisible:set_visible(main) UI.MaxDistance:set_visible(main) UI.Bind:set_visible(main) UI.Accent:set_visible(main) UI.ImportFrom:set_visible(main) UI.NoGlow:set_visible(main) UI.OnlyOnBind:set_visible(main) UI.Fov:set_visible(main) UI.MaxDistance:set_group(2) UI.Fov:set_group(2) UI.NoGlow:set_group(2) UI.Silent:set_group(2) UI.OnVisible:set_group(2) UI.OnlyOnBind:set_group(2) UI.HowToUse:set_visible(builder) UI.NadeType:set_visible(builder) UI.Record:set_visible(builder) UI.RecordExploit:set_visible(builder) UI.ClearRecord:set_visible(builder) UI.CopyRecord:set_visible(builder) UI.UpdateLocations:set_visible(builder) UI.MakePointer:set_visible(builder) UI.Record:set_group(2) UI.ClearRecord:set_group(2) UI.CopyRecord:set_group(2) UI.MakePointer:set_group(2) UI.RecordExploit:set_group(2) if UI.Record:get() then Recording = not Recording notify(Recording and "[Nade-Helper] Record started" or "[Nade-Helper] Record ended") end if UI.ClearRecord:get() then RecordData = {} Recording = false Replaying = false notify("[Nade-Helper] Last record cleared!") end if (UI.CopyRecord:get() and not Recording) then local text = "" local ui_nade = string.upper(UI.NadeType:get_items()[UI.NadeType:get() + 1]) ui_nade = (ui_nade ~= "MOVEMENT") and ui_nade or "KNIFE" if #RecordData ~= 0 then text = ("{\n anim = 0,\n name = \"Unnamed\",\n weapon = \"" .. (ui_nade) .. "\",\n pos = " .. vec_tostring(RecordData.pos) .. ",\n") for k, v in pairs(RecordData) do if type(v) ~= "table" then goto skip end text = text .. (" {") for key, val in pairs(v) do local _key = tostring(key) local _val = (_key ~= "yaw") and tostring(val) or (vec_tostring(val)) text = text .. (_key .. "=" .. _val .. ",") end text = text .. ("}, \n") ::skip:: end text = (text .. ("},\n")):gsub(",},", "},") text = text:gsub(".0}", "}") utils.set_clipboard(text) notify("[Nade-Helper] Record copied!") else notify("[Nade-Helper] Record is empty...") end end if (UI.MakePointer:get()) then local text = ([[{ anim = 0, type = "pointer", name = "Unnamed", pos = %s, weapon = "%s", },]]):format(vec_tostring(entity.get_local():get_absorigin()), string.upper(UI.NadeType:get_items()[UI.NadeType:get() + 1])) utils.set_clipboard(text) notify("[Nade-Helper] Pointer created!") end if UI.UpdateLocations:get() then Locations = load("return " .. file.read(loc_path))() end if UI.HowToUse:get() then con_do("clear; showconsole") local path = tostring(loc_path):gsub("\\", "/") console.print( ([[ EU: 1. Press "Start/End Record" button 2. Throw a grenade 3. Press "Start/End Record" button again or rewrite record if you don't like it 4. Copy your record 5. Open location file(%s) 6. Find the map where you made the grenade 7. Paste your nade, rename or correct it 8. Update locations 9. Clear the last record 10. Repeat steps 1-9 until your rectum falls out. RU: 1. Нажмите кнопку "Start/End Record" 2. Откиньте гранату 3. Нажмите кнопку "Start/End Record" еще раз или перезапишите гранату если она вам не нравится 4. Скопируйте вашу запись 5. Откройте файл с локациями(%s) 6. Найдите карту на которой вы сделали гранату 7. Вставьте вашу гранату, переименуйте или поддкоректируйте ее 8. Обновите локации 9. Очистите последнюю запись 10. Повторяйте пункты 1-9 пока у вас не выпадет прямая кишка. ]]):format(path, path)) end end local function Recorde(cmd) if ((not Recording) or (globalvars.is_open_menu())) then return end if #RecordData == 0 then RecordData.pos = entity.get_local():get_absorigin() end local view = engine.get_view_angles() RecordData[#RecordData + 1] = { yaw = vector(math.round(view.x, 0.001), math.round(view.y, 0.001), 0), } if (UI.RecordExploit:get()) then RecordData[#RecordData].exploit = ui.get_keybind_state(keybinds.hide_shots) end if (cmd.sidemove ~= 0) then RecordData[#RecordData].sidemove = math.round(cmd.sidemove, 0.0001) end if (cmd.forwardmove ~= 0) then RecordData[#RecordData].forwardmove = math.round(cmd.forwardmove, 0.0001) end if (cmd.upmove ~= 0) then RecordData[#RecordData].upmove = math.round(cmd.upmove, 0.0001) end if (cmd.buttons ~= 0) then RecordData[#RecordData].buttons = cmd.buttons end end local ReplayTick = 1 local function Replay(cmd) if ((not Replaying) or (#RecordData == 0)) then if (ReplayTick ~= 1) then Replaying = false ReplayTick = 1 end return end local MoveData = RecordData[ReplayTick] if (not MoveData) then Replaying = false ReplayTick = 1 RecordData = {} return end cmd.sidemove = MoveData.sidemove or 0 cmd.forwardmove = MoveData.forwardmove or 0 cmd.upmove = MoveData.upmove or 0 if not UI.Silent:get() then engine.set_view_angles(MoveData.yaw) end cmd.viewangles = MoveData.yaw if (MoveData.exploit ~= nil) then ui.set_keybind_state(keybinds.hide_shots, MoveData.exploit) end cmd.buttons = MoveData.buttons or 0 ReplayTick = ReplayTick + 1 LastReplayTime = 1 end local function auto_throw(cmd) if (not UI.Bind:get()) or (Replaying or Recording) then return end local lp = e_get_local() if not lp then return end local abs = lp:get_absorigin() local weap = entity.get_weapon_by_player(lp) if not weap:can_fire() then return end local Locs = GetLocations() if (not Locs or #Locs == 0) then return end local fov = UI.Fov:get() local nade = { closest = fov, index = nil, } for i = 1, #Locs do local isPointer = Locs[i].type if Locs[i].anim > 250 and not isPointer then local pos = Locs[i].pos local dist_to_start = abs:dist_to(pos) if dist_to_start <= 15 then move_to_pos(pos, cmd) local l_yaw = Locs[i][1].yaw local yaw = engine.get_view_angles() local fov_dist = get_fov_dist(l_yaw, yaw) if dist_to_start <= 0.5 and get_speed(lp) < 20 and (fov_dist <= nade.closest) then nade.closest = fov_dist nade.index = i end end end end if nade.index then RecordData = Locs[nade.index] Replaying = true end end local function PosRender() local lp = e_get_local() if not lp then return end local Locs = GetLocations() if (not Locs or #Locs == 0) then return end for i = 1, #Locs do local loc = Locs[i] if not lp then loc.anim = 0 goto skip end local isPointer = loc.type local weap = entity.get_weapon_by_player(lp):get_name() local abs_weap = (weap == loc.weapon or (isFire(weap) and isFire(loc.weapon))) if (not (abs_weap) and (loc.anim < 2)) then loc.anim = 0 goto skip end local abs = lp:get_absorigin() local pos = loc.pos local nade_dist = abs:dist_to(pos) local abs_dist = nade_dist < UI.MaxDistance:get() and math.abs(abs.z - pos.z) <= 100 local visible = true if UI.OnVisible:get() then local tracer = trace.ray(cam_pos, pos, lp, 0xFFFFFFFF) visible = tracer.fraction > 0.9 end if abs_dist and visible or loc.anim > 2 then loc.anim = lerp(loc.anim, abs_dist and abs_weap and visible and LastReplayTime == 0 and 255 or 0, 12) local render_pos = w2s(pos) local size = render.get_text_size(fonts.verdana, loc.name) local trans = math.floor(loc.anim) if not loc.hidden then local icon_size = render.get_text_size(fonts.weapon, weapon_icons[loc.weapon]) render.rect_filled(render_pos.x - size.x / 2 + 5, render_pos.y - size.y / 2 + 5, size.x + 10 + icon_size.x, size.y + 10, color(15,15,15,math.floor(trans / 1.25)), 4) render.rect(render_pos.x - size.x / 2 + 5, render_pos.y - size.y / 2 + 5, size.x + 10 + icon_size.x, size.y + 10, over_alpha(UI.Accent:get(), trans), 4) if (trans > 50) and (not UI.NoGlow:get()) then render.rect_shadow(render_pos.x - size.x / 2 + 6, render_pos.y - size.y / 2 + 6, size.x + 8 + icon_size.x, size.y + 8, (trans / 100), UI.Accent:get(), 0, 0) end render.text(fonts.verdana, render_pos.x - size.x / 2 + 10, render_pos.y - size.y / 2 + 10, over_alpha(UI.Accent:get(), trans), loc.name, true, false) render_icon(loc.weapon, render_pos.x - size.x / 2 + 12.5 + size.x, render_pos.y, over_alpha(UI.Accent:get(), trans)) end if nade_dist < 15 and loc.anim > 250 and not isPointer then render_nade(loc.name, loc.pos, loc[1].yaw, loc.weapon, nade_dist) end end ::skip:: end end --- #endregion --- #region: Fixes local function Fixes() local lp = e_get_local() if (not lp) then if Recording then AA:set(UserAA) UserAA = nil end Replaying = false Recording = false ReplayTick = 1 LastReplayTime = 0 end end --- #endregion --- #region: Callbacks cheat.push_callback("on_paint", function() local success, msg = pcall(function() UI.Handler() Fixes() PosRender() end) if not success and _DEV then cheat.notify(msg) end end) cheat.push_callback("after_prediction", function(cmd) local success, msg = pcall(function() auto_throw(cmd) Recorde(cmd) Replay(cmd) end) if not success and _DEV then cheat.notify(msg) end end) cheat.push_callback("on_override_view", function(view) cam_pos = view.origin end) cheat.push_callback("on_unload", function() if (UserAA ~= nil) then AA:set(UserAA) end Locations = nil UI = nil loc_path = nil fonts = nil RecordData = nil weapon_icons = nil AA = nil Recording = nil Replaying = nil movement_icon = nil assets_path = nil url = nil UserAA = nil LastReplayTime = nil cam_pos = nil download = nil decode = nil auto_throw = nil Recorde = nil Replay = nil PosRender = nil Fixes = nil if _DEV then cheat.notify("Cache succefully cleared!") _DEV = nil end collectgarbage("collect") end) --- #endregion