supinus

NHES2

Nov 10th, 2024 (edited)
118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 51.75 KB | None | 0 0
  1. local config = ac.configValues({
  2.   hubUrl = ""
  3. })
  4.  
  5. ---@class NoHesiClient
  6. local NoHesiClient = class("NoHesiClient")
  7.  
  8.  
  9. local closestcar = 1
  10.  
  11.  
  12. -- Import the WebBrowser library
  13. local WebBrowser = require('shared/web/browser')
  14. ui.setAsynchronousImagesLoading(true)
  15.  
  16. -- Configure the WebBrowser
  17. WebBrowser.configure({
  18.   targetFPS = 240,
  19. })
  20.  
  21. -- Create an instance of the browser with initial parameters
  22. local browser = WebBrowser({
  23.   size = vec2(500, 400),  -- Set the size of the browser window
  24.   backgroundColor = rgbm(0,0,0,0),  -- Set the background color
  25.   directRender = true,  -- Enabl direct rendering
  26.   redirectAudio = true,  -- Enabele audio redirection
  27. })
  28.  
  29. -- Store the last size of the browser window
  30. local lastSize = vec2(450, 420)
  31. -- Initialize mouse buttons states
  32. local mouseButtons = {false, false, false}
  33.  
  34.  
  35. function checkAndLoadVideo()
  36. local rootFolder = ac.getFolder(ac.FolderID.Root) .. "\\cache\\remote_assets\\nohesivideo"
  37. local targetFile = rootFolder .. "\\2_90FPS_2.wmv"
  38. print("Checking if target file exists: " .. targetFile)
  39.  
  40. -- Check if the file exists
  41. if not io.fileExists(targetFile) then
  42.     print("File does not exist, starting download process.")
  43.     local function onAssetsLoaded(err, folder)
  44.         if err then
  45.             print("Error loading assets: " .. err)
  46.             return
  47.         else
  48.             print("Assets loaded successfully.")
  49.         end
  50.  
  51.         -- Path to the file to move
  52.         local sourceFile = folder .. "\\2_90FPS_2.wmv"
  53.         print("Source file path: " .. sourceFile)
  54.  
  55.         -- Create the directory if it doesn't exist
  56.         if not io.dirExists(rootFolder) then
  57.             print("Target directory does not exist, creating: " .. rootFolder)
  58.             io.createDir(rootFolder)
  59.         else
  60.             print("Target directory already exists.")
  61.         end
  62.  
  63.         -- Attempt to move the file
  64.         print("Attempting to move file from " .. sourceFile .. " to " .. targetFile)
  65.         if io.move(sourceFile, targetFile) then
  66.             print("File successfully moved to " .. targetFile)
  67.             loadVideoInPlayer(targetFile)
  68.         else
  69.             print("Failed to move the file. Checking permissions and file locks.")
  70.         end
  71.     end
  72.     -- Start downloading and unpacking the assets
  73.     print("Initiating download from URL.")
  74.     web.loadRemoteAssets("https://cdn.discordapp.com/attachments/880053607024717854/1209263927045652591/2_90FPS_2.zip?ex=65e649cb&is=65d3d4cb&hm=33f584a885ac8ea30f635cea6c28831a9ea8bf0e17e4799a51597b2bc1b81d00&", onAssetsLoaded)
  75. else
  76.     print("File already exists, loading into MediaPlayer.")
  77.     loadVideoInPlayer(targetFile)
  78. end
  79. end
  80.  
  81.  
  82.  
  83.  
  84. local htmlContent = [[
  85. <!DOCTYPE html>
  86. <html lang='en'>
  87. <head>
  88. <meta charset='UTF-8'>
  89. <link rel="preconnect" href="https://fonts.googleapis.com">
  90. <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  91. <link href="https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,slnt,wdth,wght,GRAD,XTRA,YOPQ,YTAS,YTDE,YTFI,YTLC,[email protected],-10..0,25..151,100..1000,-200..150,323..603,25..135,649..854,-305..-98,560..788,416..570,528..760&display=swap" rel="stylesheet">
  92.  
  93. <style>
  94.  
  95.  
  96.  
  97. .shake-top {
  98.   animation: shake-top 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both;
  99. }
  100.  
  101. @keyframes shake-top {
  102.   0%, 100% {
  103.     transform: rotate(0deg);
  104.     transform-origin: 50% 0;
  105.   }
  106.   10% {
  107.     transform: rotate(2deg);
  108.   }
  109.   20%, 40%, 60% {
  110.     transform: rotate(-4deg);
  111.   }
  112.   30%, 50%, 70% {
  113.     transform: rotate(4deg);
  114.   }
  115.   80% {
  116.     transform: rotate(-2deg);
  117.   }
  118.   90% {
  119.     transform: rotate(2deg);
  120.   }
  121. }
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128. .blink-2 {
  129.   animation: blink-2 0.4s both;
  130. }
  131.  
  132. @keyframes blink-2 {
  133.   0% {
  134.     opacity: 1;
  135.   }
  136.   50% {
  137.     opacity: 0.1;
  138.   }
  139.   100% {
  140.     opacity: 1;
  141.   }
  142. }
  143.  
  144.  
  145.  
  146.  
  147.  
  148.  
  149. :root {
  150.   --font-family: 'Roboto Flex', sans-serif;
  151.   --second-family: 'Roboto', sans-serif;
  152.   --main-color: #B130FF; /* Purple color */
  153. }
  154.  
  155.  
  156. body, html {
  157.   height: 100%;
  158.   margin: 0;
  159.   display: flex;
  160.   justify-content: left;
  161.   align-items: baseline;
  162.   background-color: rgba(0, 0, 0, 0.0)/* Dark background */
  163.  
  164. }
  165.  
  166. .dashboard {
  167.   justify-content: flex-start; /* Это изменение выровняет все содержимое dashboard, включая logo, слева */
  168. }
  169.  
  170. .logo {
  171.   justify-content: flex-start;
  172.   width: 100%;
  173. }
  174.  
  175.  
  176.  
  177. .logo-text {
  178.    
  179.   margin-left: 10px;
  180.   font-size: 24px;
  181.   font-weight: 700;
  182.   font-family: var(--second-family);
  183. }
  184.  
  185. .score-section {
  186.   display: flex;
  187.   align-items: center; /* This will vertically center the items */
  188.   margin-top: 20px; /* Adjust as necessary */
  189. }
  190.  
  191. .score-text {
  192.    margin-top: -13px; /* Поднимает элемент на 10 пикселей вверх */
  193.   font-family: 'Roboto Flex', sans-serif;
  194.   font-weight: 1000; /* Extra Black - Обратите внимание, что максимальный доступный вес может быть 900 для некоторых шрифтов */
  195.   font-size: 26px;
  196.   line-height: 106%;
  197.   letter-spacing: 0.03em;
  198.   text-transform: uppercase;
  199.   color: #fff;
  200.  
  201.   /* Применение настроек переменных шрифтов */
  202.   font-variation-settings: 'wdth' 111, 'GRAD' 0, 'slnt' 0, 'XTRA' 496, 'XOPQ' 96, 'YOPQ' 79, 'YTLC' 416, 'YTUC' 712, 'YTAS' 649, 'YTDE' -203, 'YTFI' 738, 'opsz' 79;
  203. }
  204. .score-badge {
  205.   margin-top: -40px; /* Поднимает элемент вверх */
  206.   margin-left: 20px; /* Поднимает элемент вверх */
  207.   border: 1.2px solid rgba(255, 255, 255, 0.13); /* Оставляем рамку как есть, если она вам нужна */
  208.   border-radius: 4px;
  209.   padding: 4px 8px 4px 4px; /* Измененные паддинги */
  210.   width: 71px; /* Измененная ширина */
  211.   height: 21px; /* Измененная высота */
  212.   background: rgba(1, 1, 1, 1); /* Новый цвет фона */
  213.   display: flex;
  214.   justify-content: center;
  215.   align-items: center;
  216.   gap: 6px; /* Добавляем пространство между SVG и текстом */
  217. }
  218.  
  219. .score-badge-text {
  220.   font-family: 'Roboto Flex', sans-serif;
  221.   font-weight: 740; /* Medium */
  222.   font-size: 16px; /* Размер шрифта оставляем как был */
  223.   color: rgba(238, 237, 238, 0.87); /* Цвет текста оставляем как был */
  224.   font-variation-settings: 'wdth' 151, 'GRAD' 0, 'slnt' 0, 'XTRA' 468, 'XOPQ' 96, 'YOPQ' 79, 'YTLC' 514, 'YTUC' 712, 'YTAS' 750, 'YTDE' -203, 'YTFI' 738, 'opsz' 21;
  225. }
  226.  
  227.  
  228. .speed-up {
  229.   margin-top: -20px; /* Поднимает элемент на 10 пикселей вверх */
  230.   font-family: 'Roboto Flex', sans-serif;
  231.   font-weight: 700; /* Bold */
  232.   font-stretch: expanded;
  233.   font-style: oblique 10deg; /* Для курсива */
  234.   font-size: 11px;
  235.   line-height: 82%;
  236.   text-transform: uppercase;
  237.   color: #ff2074;
  238.   border: 0.49px solid rgba(255, 0, 95, 0.68);
  239.   border-radius: 8px;
  240.   padding: 7px 13px;
  241.   width: 210px;
  242.   height: 15px;
  243.   background: rgba(255, 32, 116, 0.15);
  244.   display: flex;
  245.   justify-content: center;
  246.   align-items: center;
  247.  
  248.  
  249.   /* Использование переменных шрифтов */
  250.   font-variation-settings: 'wdth' 151, 'slnt' -10, 'XTRA' 468, 'XOPQ' 96, 'YOPQ' 79, 'YTLC' 514, 'YTUC' 712, 'YTAS' 750, 'YTDE' -203, 'YTFI' 738;
  251. }
  252.  
  253. .points-info {
  254.   font-weight: 1000; /* Extra Black - убедитесь, что шрифт поддерживает это значение */
  255.   font-size: 22px;
  256.   line-height: 100%;
  257.   text-transform: uppercase;
  258.   color: #fff; /* White color */
  259.   font-family: 'Roboto Flex', sans-serif;
  260.   font-variation-settings: 'wdth' 111, 'GRAD' 0, 'slnt' 0, 'XTRA' 496, 'XOPQ' 96, 'YOPQ' 79, 'YTLC' 416, 'YTUC' 712, 'YTAS' 649, 'YTDE' -203, 'YTFI' 738, 'opsz' 79;
  261.   margin-top: 40px; /* Adjust space from the SPEED UP block */
  262. }
  263.  
  264. .block-model {
  265. display: flex;
  266. justify-content: flex-start; /* Выравнивает элементы по левому краю */
  267. gap: 9px; /* Использует переменную для расстояния между элементами */
  268. margin-top: 10px; /* Отступ сверху */
  269. padding-left: var(--padding-left-block-model); /* Если нужен отступ слева для всего контейнера */
  270. width: 100%; /* Занимает всю доступную ширину */
  271. }
  272.  
  273.  
  274. .item {
  275. display: flex;
  276. flex-direction: column; /* Устанавливает направление элементов в колонку */
  277. align-items: center; /* Центрирует элементы по горизонтали */
  278. }
  279.  
  280. .combo-text {
  281. font-weight: 700;
  282. font-size: 12px;
  283. line-height: 0%;
  284. letter-spacing: -0.02em;
  285. text-transform: uppercase;
  286. text-align: center;
  287. color: var(--combo-text-color, rgba(238, 237, 238, 0.68)); /* Значение по умолчанию */
  288. font-family: 'Roboto Flex', sans-serif;
  289. margin-bottom: 8px; /* Отступ между текстом COMBO и объектом */
  290. font-variation-settings: 'wdth' 151, 'slnt' -10, 'GRAD' 0, 'XTRA' 468, 'XOPQ' 96, 'YOPQ' 79, 'YTLC' 514, 'YTUC' 712, 'YTAS' 750, 'YTDE' -203, 'YTFI' 738, 'opsz' 10;
  291.  
  292. }
  293.  
  294.  
  295. .object {
  296. border: 0.86px solid rgba(255, 255, 255, 0.13);
  297. border-radius: 6px;
  298. padding: 5px 6px;
  299. width: 61px;
  300. height: 20px;
  301. background: rgba(255, 255, 255, 0.08);
  302. display: flex;
  303. justify-content: center;
  304. align-items: center;
  305. }
  306.  
  307. .object-text {
  308. font-weight: 600;
  309. font-size: 14px;
  310. line-height: 143%;
  311. text-align: center;
  312. color: rgba(238, 237, 238, 0.87);
  313. font-family: 'Roboto Flex', sans-serif;
  314. }
  315.  
  316. .speed-warning {
  317. color: rgba(255, 32, 116, 1);
  318. font-family: 'Roboto Flex', sans-serif;
  319. font-size: 14px;
  320. font-weight: 700;
  321. line-height: 10px;
  322. letter-spacing: 0em;
  323. text-transform: uppercase; /* Делает весь текст в верхнем регистре */
  324. text-align: left; /* Центрирует текст по центру */
  325. font-variation-settings: 'wdth' 151, 'GRAD' 0, 'slnt' -10, 'XTRA' 468, 'XOPQ' 96, 'YOPQ' 79, 'YTLC' 514, 'YTUC' 712, 'YTAS' 750, 'YTDE' -203, 'YTFI' 738;
  326. margin-top: 20px; /* Отступ сверху, чтобы отделить текст от объектов */
  327. margin-left: 20px; /* Отступ сверху, чтобы отделить текст от объектов */
  328. display: block; /* Гарантирует, что элемент будет на новой строке */
  329.  
  330. }
  331.  
  332.  
  333.  
  334. .content {
  335. visibility: hidden; /* Содержимое скрыто, но прогружается */
  336. opacity: 0;
  337. transition: visibility 0s, opacity 1s linear; /* Плавное появление */
  338. transition-delay: 2s; /* Задержка перед началом анимации появления */
  339. }
  340.  
  341. .logo-text {
  342. position: absolute;
  343.  
  344. opacity: 0; /* Изначально текст невидим */
  345. margin-top: 30px
  346.  
  347. }
  348.  
  349.  
  350.  
  351.  
  352. </style>
  353. </head>
  354. <body>
  355.  
  356.  
  357.  
  358. </div>
  359. </div>
  360. </div>
  361. <div class="content">
  362. <div class='dashboard'>
  363. <div class='logo'>
  364.   <svg width='90' height='28' viewBox='0 0 90 28' fill='none' xmlns='http://www.w3.org/2000/svg'>
  365.       <path d='M12.7547 12.1201V15.4593L12.1611 15.7031L2.91113 6.43064V16.2103L7.57387 20.8441H3.42526L0 17.4321V0.24427L0.592587 0L12.7547 12.1201ZM17.0469 27.4792L17.6395 27.2349V10.047L14.2142 6.6351H10.0903L14.7283 11.2689V21.0485L5.47835 11.7761L4.88477 12.0199V15.359L17.0469 27.4792Z' fill='#B130FF' />
  366.       <path d='M87.2604 18.2395V9.70853H89.856V18.2395H87.2604Z' fill='#B130FF' />
  367.       <path d='M75.7939 15.9837L75.7646 15.5091H78.0614L78.1024 15.7435C78.1688 16.1224 78.4208 16.388 78.8583 16.5403C79.2958 16.6927 80.0164 16.7689 81.0203 16.7689C81.989 16.7689 82.6707 16.6907 83.0652 16.5345C83.4636 16.3743 83.6628 16.1556 83.6628 15.8783C83.6628 15.6439 83.4558 15.4681 83.0417 15.3509C82.6277 15.2337 81.7 15.0931 80.2586 14.9291C78.6415 14.7259 77.5204 14.4076 76.8954 13.974C76.2705 13.5404 75.958 12.9565 75.958 12.2221C75.958 11.3706 76.3544 10.7104 77.1474 10.2417C77.9403 9.76907 79.1219 9.53275 80.6922 9.53275C82.3093 9.53275 83.53 9.73977 84.3542 10.1538C85.1784 10.564 85.6256 11.187 85.696 12.0229L85.7194 12.3276H83.4519L83.4167 12.1811C83.3699 11.9155 83.1648 11.6948 82.8015 11.519C82.4382 11.3393 81.7488 11.2495 80.7332 11.2495C79.8739 11.2495 79.2704 11.3198 78.9227 11.4604C78.579 11.601 78.4071 11.7866 78.4071 12.017C78.4071 12.2084 78.5985 12.3686 78.9813 12.4975C79.368 12.6264 80.3074 12.7729 81.7996 12.9369C83.3777 13.1362 84.489 13.4076 85.1335 13.7514C85.778 14.0951 86.1002 14.6888 86.1002 15.5326C86.1002 16.4544 85.6764 17.1653 84.8288 17.6653C83.9851 18.1653 82.7332 18.4153 81.073 18.4153C79.409 18.4153 78.1259 18.2161 77.2236 17.8176C76.3251 17.4192 75.8486 16.8079 75.7939 15.9837Z' fill='#B130FF' />
  368.       <path d='M66.2493 18.2395V9.70853H74.8682V11.6596H68.6398V13.1186H74.3116V14.7826H68.6398V16.3704H74.9736V18.2395H66.2493Z' fill='#B130FF' />
  369.       <path d='M54.2848 18.2395V9.70853H56.8453V12.7612H61.6967V9.70853H64.2572V18.2395H61.6967V14.8998H56.8453V18.2395H54.2848Z' fill='#B130FF' />
  370.       <path d='M36.4553 13.8686V13.9623C36.4553 12.5014 37.0509 11.3979 38.2423 10.6519C39.4337 9.90578 40.9083 9.53275 42.666 9.53275C44.4199 9.53275 45.8964 9.90578 47.0956 10.6519C48.2947 11.3979 48.8943 12.5014 48.8943 13.9623V13.8686C48.8943 15.388 48.2947 16.5267 47.0956 17.2845C45.8964 18.0383 44.4199 18.4153 42.666 18.4153C40.9122 18.4153 39.4376 18.0383 38.2423 17.2845C37.0509 16.5267 36.4553 15.388 36.4553 13.8686ZM39.1329 13.9037C39.1329 14.8295 39.4571 15.5267 40.1055 15.9954C40.7579 16.4603 41.6153 16.6985 42.6777 16.7103C43.7324 16.7259 44.5859 16.4935 45.2382 16.013C45.8905 15.5326 46.2167 14.8334 46.2167 13.9154V13.9623C46.2167 13.0873 45.8905 12.4291 45.2382 11.9877C44.5898 11.5424 43.7363 11.312 42.6777 11.2964C41.6074 11.2846 40.7481 11.5073 40.0997 11.9643C39.4552 12.4213 39.1329 13.0854 39.1329 13.9564V13.9037Z' fill='#B130FF' />
  371.       <path d='M25.5982 18.2395V9.70853H28.0766L32.7874 14.8822C32.846 14.9408 32.8929 14.9857 32.928 15.017C32.9671 15.0482 33.0003 15.0795 33.0276 15.1107H33.0569C33.0491 15.0248 33.0433 14.9564 33.0394 14.9056C33.0394 14.8549 33.0394 14.7865 33.0394 14.7006V9.70853H35.1955V18.2395H32.7933L28.0356 12.9955C27.9731 12.9291 27.9204 12.8784 27.8774 12.8432C27.8383 12.8041 27.8012 12.7612 27.7661 12.7143H27.7426C27.7465 12.7573 27.7485 12.8002 27.7485 12.8432C27.7524 12.8823 27.7544 12.9174 27.7544 12.9487L27.7661 18.2395H25.5982Z' fill='#B130FF' />
  372.     </svg>
  373. </div>
  374. <div class='score-section'>
  375.   <p class='score-text'>PB 0 PTS</p>
  376.   <div class='score-badge'>
  377.   <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  378.     <path d="M15.4259 10.5856V13.502L14.9075 13.7149L6.82868 5.61646V14.1579L10.9011 18.205H7.27772L4.28613 15.225V0.213343L4.80369 0L15.4259 10.5856ZM19.1747 24L19.6922 23.7867V8.77496L16.7007 5.79503H13.0989L17.1497 9.8421V18.3835L9.07086 10.2851L8.55244 10.498V13.4144L19.1747 24Z" fill="#08060A" fill-opacity="0.83" />
  379.   </svg>
  380.   <span class='score-badge-text'>#1</span>
  381. </div>
  382.  
  383.   </div>
  384. </div>
  385. <!-- Новый элемент для SPEED UP -->
  386. <div class='speed-up'>SPEED UP!</div>
  387. <div class='points-info' id='pointsDisplay'>PB 0 PTS</div>
  388. <div class='block-model'>
  389. <div class='item'>
  390.   <p class='combo-text'>COMBO</p>
  391.   <div class='object'>
  392.     <div class='object-text' style='color: rgba(255, 214, 67, 0.87);'>0x</div>
  393.   </div>
  394. </div>
  395. <div class='item'>
  396.   <p class='combo-text'>SPEED</p>
  397.   <div class='object'>
  398.     <div class='object-text' style='color: rgba(106, 255, 103, 0.87);'>0x</div> <!-- Пример другого цвета -->
  399.   </div>
  400. </div>
  401. <div class='item'>
  402.   <p class='combo-text'>Proximity</p>
  403.   <div class='object'>
  404.     <div class='object-text' style='color: rgba(238, 237, 238, 0.87);'>0x</div> <!-- И еще один цвет -->
  405.   </div>
  406.  
  407.  
  408. </div>
  409.  
  410. </div>
  411. <p class='speed-warning'>KEEP SPEED ABOVE 95KM/H</p>
  412. </div>
  413. </div>
  414. </div>
  415.  
  416. <script>
  417. // Функция для плавного обновления числа
  418. // Функция для плавного обновления числа с поддержкой условного форматирования текста
  419. // Универсальная функция анимации для обновления числовых значений
  420. function animateValueUpdate({ selector, oldValue, newValue, isPB = false, precision = 0, unit }) {
  421.   const duration = 700;
  422.   const start = performance.now();
  423.  
  424.   function updateNumber(timestamp) {
  425.     const elapsedTime = timestamp - start;
  426.     const progress = Math.min(elapsedTime / duration, 1);
  427.     const currentNumber = oldValue + (newValue - oldValue) * progress;
  428.     // Использование локали 'en-US' для формата с запятой как разделителем тысяч
  429.     const formattedNumber = currentNumber.toLocaleString('en-US', { minimumFractionDigits: precision, maximumFractionDigits: precision });
  430.     const displayValue = isPB ? `PB: ${formattedNumber} ${unit}` : `${formattedNumber}${unit}`;
  431.  
  432.     document.querySelector(selector).textContent = displayValue;
  433.  
  434.     if (progress < 1) {
  435.       requestAnimationFrame(updateNumber);
  436.     }
  437.   }
  438.  
  439.   requestAnimationFrame(updateNumber);
  440. }
  441.  
  442. // Обработка обновления различных значений
  443. function updateValue(type, newValue) {
  444.   const selectorMap = {
  445.     bestScore: '.score-text',
  446.     currentScore: '#pointsDisplay',
  447.     combo: '.item:nth-child(1) .object-text',
  448.     speed: '.item:nth-child(2) .object-text',
  449.     proximity: '.item:nth-child(3) .object-text'
  450.   };
  451.  
  452.   const isPB = type === 'bestScore';
  453.   const selector = selectorMap[type];
  454.   const currentValueText = document.querySelector(selector)?.textContent.replace(/[^\d.-]/g, '') || "0";
  455.   const currentValue = parseFloat(currentValueText);
  456.   const precision = ['combo', 'speed', 'proximity'].includes(type) ? 1 : 0;
  457.   const unit = ['combo', 'speed', 'proximity'].includes(type) ? "x" : "PTS"; // Удаление пробела для X
  458.  
  459.   animateValueUpdate({ selector, oldValue: currentValue, newValue: parseFloat(newValue), isPB, precision, unit });
  460. }
  461.  
  462.  
  463. // Очередь для хранения данных обновлений
  464. const updateQueue = [];
  465. let isAnimating = false; // Флаг, показывающий, идет ли в данный момент анимация
  466.  
  467. function processNextUpdate() {
  468.   if (updateQueue.length === 0 || isAnimating) {
  469.     return; // Выходим, если анимация уже идёт или нет заданий в очереди
  470.   }
  471.  
  472.   isAnimating = true; // Устанавливаем флаг анимации
  473.   const data = updateQueue.shift(); // Извлекаем первый элемент очереди
  474.  
  475.   const speedUpElement = document.querySelector('.speed-up');
  476.   if (!speedUpElement) {
  477.     console.error("Элемент для обновления сообщения о ускорении не найден.");
  478.     isAnimating = false;
  479.     processNextUpdate(); // Пытаемся обработать следующий элемент в очереди
  480.     return;
  481.   }
  482.  
  483.   // Удаляем предыдущие классы анимации и добавляем новый
  484.   speedUpElement.classList.remove('blink-2', 'shake-top');
  485.   const animationClass = data.color.r === 1 && data.color.g === 0 && data.color.b === 0 ? 'shake-top' : 'blink-2';
  486.   speedUpElement.classList.add(animationClass);
  487.  
  488.   // Изменяем цвет в середине анимации blink-2
  489.   setTimeout(() => {
  490.     speedUpElement.style.background = `rgba(${data.color.r * 255}, ${data.color.g * 255}, ${data.color.b * 255}, 0.2)`;
  491.     speedUpElement.style.border = `0.49px solid rgba(${data.color.r * 255}, ${data.color.g * 255}, ${data.color.b * 255}, 0.68)`;
  492.     speedUpElement.style.color = `rgba(${data.color.r * 255}, ${data.color.g * 255}, ${data.color.b * 255}, 1)`;
  493.   }, 200); // 200ms для достижения 50% анимации blink-2
  494.  
  495.   // Обновляем текст в середине анимации
  496.   setTimeout(() => {
  497.     speedUpElement.textContent = data.message;
  498.   }, 200); // Обновляем текст одновременно с изменением цвета
  499.  
  500.   // Завершаем анимацию и удаляем классы анимации с небольшой задержкой после полного цикла
  501.   setTimeout(() => {
  502.     speedUpElement.classList.remove('blink-2', 'shake-top');
  503.     isAnimating = false;
  504.     processNextUpdate(); // Обрабатываем следующее обновление в очереди
  505.   }, 400); // Полная длительность анимации blink-2
  506. }
  507.  
  508. function updateSpeedUpMessage(data) {
  509.   updateQueue.push(data); // Добавляем данные в очередь
  510.   if (!isAnimating) {
  511.     processNextUpdate(); // Запускаем обработку очереди, если анимация не активна
  512.   }
  513. }
  514.  
  515.  
  516.  
  517.  
  518.  
  519.  
  520.  
  521.  
  522.  
  523. function updatePlace(newPlace) {
  524. console.log("updatePlace called with:", newPlace); // Отладочная информация
  525.  
  526. // Поиск элемента, содержащего текст места
  527. const placeElement = document.querySelector('.score-badge-text');
  528. if (!placeElement) {
  529.   console.error("Элемент для обновления места не найден.");
  530.   return;
  531. }
  532.  
  533. // Обновление текста элемента
  534. placeElement.textContent = `${newPlace}`;
  535. }
  536.  
  537. function updateBadgeStyle() {
  538. const badge = document.querySelector('.score-badge');
  539. const badgeText = badge.querySelector('.score-badge-text');
  540. const svg = badge.querySelector('svg');
  541.  
  542. // Определение стилей для разных позиций
  543. const styleConfigs = {
  544.   1: {
  545.     backgroundColor: 'rgba(221, 3, 85, 1)',
  546.     textColor: 'rgba(8, 6, 10, 1)',
  547.     logoColor: 'rgba(8, 6, 10, 1)',
  548.     fontSize: '16px'
  549.   },
  550.   2: {
  551.     backgroundColor: 'rgba(238, 237, 238, 0.87)',
  552.     textColor: 'rgba(8, 6, 10, 1)',
  553.     logoColor: 'rgba(8, 6, 10, 1)',
  554.     fontSize: '16px'
  555.   },
  556.   3: {
  557.     backgroundColor: 'rgba(255, 129, 38, 1)',
  558.     textColor: 'rgba(8, 6, 10, 1)',
  559.     logoColor: 'rgba(8, 6, 10, 1)',
  560.     fontSize: '16px'
  561.   },
  562.   default: {
  563.     backgroundColor: 'rgba(255, 255, 255, 0.17)',
  564.     textColor: 'rgba(238, 237, 238, 0.87)',
  565.     logoColor: 'rgba(238, 237, 238, 0.52)',
  566.     fontSize: ['16px', '14px', '12px'] // Используйте динамическое определение размера шрифта
  567.   }
  568. };
  569.  
  570. const position = parseInt(badgeText.textContent.replace(/[^\d]/g, '')) || 0;
  571. const config = styleConfigs[position] || styleConfigs.default;
  572.  
  573. // Динамическое определение размера шрифта на основе позиции
  574. const fontSizeIndex = position < 100 ? 0 : position < 1000 ? 1 : 2;
  575. const finalFontSize = config.fontSize instanceof Array ? config.fontSize[fontSizeIndex] : config.fontSize;
  576.  
  577. // Применение стилей
  578. badge.style.background = config.backgroundColor;
  579. badgeText.style.color = config.textColor;
  580. badgeText.style.fontSize = finalFontSize;
  581. svg.querySelectorAll('path').forEach(path => path.setAttribute('fill', config.logoColor));
  582. }
  583.  
  584.  
  585. // Функция для управления видимостью предупреждения о скорости
  586. function speedWarningDisplay(show) {
  587. const speedWarningElement = document.querySelector('.speed-warning');
  588. if (!speedWarningElement) return; // Выходим, если элемент не найден
  589.  
  590. // Устанавливаем свойство display в зависимости от аргумента show
  591. speedWarningElement.style.display = show ? 'block' : 'none';
  592. }
  593.  
  594.  
  595.  
  596. // Обработчик события, получающий данные от AC
  597. AC.onReceive('speedblock', arg => {
  598. // Проверяем значение arg, чтобы определить, показывать или скрывать предупреждение
  599. const shouldShow = arg === 'show'; // Пример условия, arg должен быть 'show' для отображения
  600. speedWarningDisplay(shouldShow);
  601. });
  602.  
  603.  
  604.  
  605.  
  606. function setupEventHandlers() {
  607. AC.onReceive('updateBestScore', newValue => updateValue('bestScore', newValue));
  608. AC.onReceive('updateScore', newValue => updateValue('currentScore', newValue));
  609. AC.onReceive('updateCombo', newValue => updateValue('combo', newValue));
  610. AC.onReceive('updateSpeed', newValue => updateValue('speed', newValue));
  611. AC.onReceive('updateProximity', newValue => updateValue('proximity', newValue));
  612. AC.onReceive('messages', updateSpeedUpMessage);
  613. AC.onReceive('speedblock', arg => speedWarningDisplay(arg === 'show'));
  614. AC.onReceive('updatePlace', newPlace => {
  615.   updatePlace(newPlace);
  616.   updateBadgeStyle(); // Вызов для обновления стилей после изменения места
  617. });
  618.  
  619. AC.onReceive('webanim', arg => {
  620.   if(arg === "startweb") {
  621.       showContent();
  622.   }
  623. });
  624. }
  625.  
  626. function showContent() {
  627. const content = document.querySelector('.content');
  628. if(content) {
  629.   content.style.visibility = 'visible';
  630.   content.style.opacity = '1';
  631. }
  632. }
  633.  
  634. document.addEventListener('DOMContentLoaded', setupEventHandlers);
  635.  
  636.  
  637. </script>
  638.  
  639.  
  640.  
  641.  
  642. </body>
  643. </html>
  644. ]]
  645.  
  646.  
  647.  
  648. function checkAndLoadVideo()
  649. local rootFolder = ac.getFolder(ac.FolderID.Root) .. "\\cache\\remote_assets\\nohesivideo"
  650. local targetFile = rootFolder .. "\\2_90FPS_2.wmv"
  651. -- Check if the file exists
  652. if not io.fileExists(targetFile) then
  653.     -- If the file doesn't exist, first download it
  654.     local function onAssetsLoaded(err, folder)
  655.         if err then
  656.             print("Error loading assets: " .. err)
  657.             return
  658.         end
  659.  
  660.         -- Path to the file to move
  661.         local sourceFile = folder .. "\\2_90FPS_2.wmv"
  662.  
  663.         -- Create the directory if it doesn't exist
  664.         if not io.dirExists(rootFolder) then
  665.             io.createDir(rootFolder)
  666.         end
  667.  
  668.         -- Move the file
  669.         if io.move(sourceFile, targetFile) then
  670.             print("File successfully moved to " .. targetFile)
  671.             loadVideoInPlayer(targetFile)
  672.         else
  673.             print("Failed to move the file.")
  674.         end
  675.     end
  676.     -- Start downloading and unpacking the assets
  677.     web.loadRemoteAssets("https://cdn.discordapp.com/attachments/880053607024717854/1209263927045652591/2_90FPS_2.zip?ex=65e649cb&is=65d3d4cb&hm=33f584a885ac8ea30f635cea6c28831a9ea8bf0e17e4799a51597b2bc1b81d00&", onAssetsLoaded)
  678. else
  679.     -- If the file already exists, load it into MediaPlayer
  680.     loadVideoInPlayer(targetFile)
  681. end
  682. end
  683.  
  684.  
  685. ---@param address string Hub address
  686. function NoHesiClient:initialize(address)
  687.   self.address = address .. "/api/"
  688. end
  689.  
  690. function NoHesiClient:postSettings(Uix, Uiy, Uis, fpsCheck, cpuoCheck, UIToggle, UIClear)
  691.   local payload = string.format("{\"uix\": %s, \"uiy\": %s, \"uis\": %s, \"steamid\": \"%s\", \"fpscheck\": %s, \"cpuocheck\": %s, \"uitoggle\": %s, \"uiclear\": %s}", Uix, Uiy, Uis, ac.getUserSteamID(), fpsCheck, cpuoCheck, UIToggle, UIClear)
  692.   local success, err = pcall(function()
  693.       ac.signBlob(payload, function (data)
  694.           local urlEncodedData = urlEncode(ac.encodeBase64(data))
  695.           local url = self.address .. "settings?data=" .. ac.encodeBase64(payload) .. "&ip=" ..  ac.encodeBase64(ac.getServerIP()) .. "&port=" ..  ac.encodeBase64(ac.getServerPortTCP()) .. "&steamid=" ..  ac.encodeBase64(ac.getUserSteamID()) .. "&serverinfo=" ..  ac.encodeBase64(tostring(car.sessionID)..tostring(sim.randomSeed)) .. "&sig=" ..  urlEncodedData
  696.           web.post(url, function (err, response)
  697.               ac.debug("err", err)
  698.           end)
  699.       end)
  700.   end)
  701.   if not success then
  702.       ac.debug("Error occurred:", err)
  703.   end
  704. end
  705.  
  706. function NoHesiClient:getSettings(callback)
  707.   local payload = ac.getUserSteamID()
  708.   local success, err = pcall(function()
  709.       ac.signBlob(payload, function (data)
  710.           local urlEncodedData = urlEncode(ac.encodeBase64(data))
  711.           local url = self.address .. "settings?data=" .. ac.encodeBase64(payload) .. "&ip=" ..  ac.encodeBase64(ac.getServerIP()) .. "&port=" ..  ac.encodeBase64(ac.getServerPortTCP()) .. "&steamid=" ..  ac.encodeBase64(ac.getUserSteamID()) .. "&serverinfo=" ..  ac.encodeBase64(tostring(car.sessionID)..tostring(sim.randomSeed)) .. "&sig=" ..  urlEncodedData
  712.           web.get(url, function (err, response)
  713.               ac.debug("err", err)
  714.               callback(JSON.parse(response.body))
  715.           end)
  716.       end)
  717.   end)
  718.  
  719.   if not success then
  720.       ac.debug("Error occurred:", err)
  721.   end
  722. end
  723.  
  724. function NoHesiClient:postScore(score, combo, speed, time, distance, callback)
  725.   local payload = string.format("{\"score\": \"%s\", \"combo\": %s, \"avg_speed\": %s, \"run_time\": %s, \"run_distance\": %s, \"steamid\": \"%s\", \"input\": %s, \"car_model\": \"%s\", \"server\": \"%s\"}", score, combo, speed, time, distance, ac.getUserSteamID(), sim.inputMode, ac.getCarID(0):lower(), ac.getServerIP() .. ":" .. ac.getServerPortTCP())
  726.   local success, err = pcall(function()
  727.       ac.signBlob(payload, function (data)
  728.           local urlEncodedData = urlEncode(ac.encodeBase64(data))
  729.           local url = self.address .. "score?data=" .. ac.encodeBase64(payload) .. "&ip=" ..  ac.encodeBase64(ac.getServerIP()) .. "&port=" ..  ac.encodeBase64(ac.getServerPortTCP()) .. "&steamid=" ..  ac.encodeBase64(ac.getUserSteamID()) .. "&serverinfo=" ..  ac.encodeBase64(tostring(car.sessionID)..tostring(sim.randomSeed)) .. "&sig=" ..  urlEncodedData
  730.           web.post(url, function (err, response)
  731.               ac.debug("err", err)
  732.               callback(JSON.parse(response.body))
  733.           end)
  734.       end)
  735.   end)
  736.  
  737.   if not success then
  738.       ac.debug("Error occurred:", err)
  739.   end
  740. end
  741.  
  742. function NoHesiClient:getScore(callback)
  743.   local payload = ac.getUserSteamID()
  744.   local success, err = pcall(function()
  745.       ac.signBlob(payload, function (data)
  746.           local urlEncodedData = urlEncode(ac.encodeBase64(data))
  747.           local url = self.address .. "score?data=" .. ac.encodeBase64(payload) .. "&ip=" ..  ac.encodeBase64(ac.getServerIP()) .. "&port=" ..  ac.encodeBase64(ac.getServerPortTCP()) .. "&steamid=" ..  ac.encodeBase64(ac.getUserSteamID()) .. "&serverinfo=" ..  ac.encodeBase64(tostring(car.sessionID)..tostring(sim.randomSeed)) .. "&sig=" ..  urlEncodedData
  748.           web.get(url, function (err, response)
  749.               ac.debug("err", err)
  750.               callback(JSON.parse(response.body))
  751.           end)
  752.       end)
  753.   end)
  754.  
  755.   if not success then
  756.       ac.debug("Error occurred:", err)
  757.   end
  758. end
  759.  
  760. function urlEncode(str)
  761.   if str then
  762.     str = string.gsub(str, "\n", "\r\n")
  763.     str = string.gsub(str, "([^%w ])",
  764.       function(c) return string.format("%%%02X", string.byte(c)) end)
  765.     str = string.gsub(str, " ", "+")
  766.   end
  767.   return str
  768. end
  769.  
  770. function script.prepare(dt)
  771. ac.log('speed' + ac.getCar(1).speedKmh)
  772. return ac.getCarState(1).speedKmh > 60
  773. end
  774.  
  775. local nohesiclient = NoHesiClient(config.hubUrl)
  776. local requiredSpeed = 95
  777. local sim = ac.getSim()
  778.  
  779. local maxcombo = 0
  780. local averageSpeed = 0
  781. local startdistance = 0
  782. local rundistance = 0
  783. local starttime = 0
  784. local runtime = 0
  785. local distancedriven = 0
  786. local timePassed = 0
  787. local speedMessageTimer = 0
  788. local mackMessageTimer = 0
  789. local totalScore = 0
  790. local comboMeter = 1
  791. local speedComboMeter = 1
  792. local proxiComboMeter = 1
  793. local comboColor = 0
  794. local dangerouslySlowTimer = 0
  795. local carsState = {}
  796. local wheelsWarningTimeout = 0
  797. local personalBest = 0
  798. local ownRank = 0
  799. local cheatcounter = 0
  800. local leaderboardByName = {}
  801. local MackMessages = { 'MAAAACK!!!!', 'MACKSAUCE!!', 'You Hesitated....', 'bRUH', 'No Shot...'}
  802. --new
  803. local CloseMessages = { 'IN THAT!!!!!', 'IN THERE.', 'D I V E', 'SKRRT!!!' }
  804. local NormalMessages = { 'Overtake', 'Smooth Move', 'Easy Breeze' }
  805. local FPS = sim.fps
  806. local CPUO = sim.cpuOccupancy
  807. local Uix, Uiy, Uis, fpsCheck, cpuoCheck, UIToggle, UIClear = nil, nil, nil, nil, nil, nil, nil
  808. local logoCheck = true
  809. --new
  810. local previousBestScore = nil
  811. local previousTotalScore = nil
  812. local previousComboMeter = nil
  813. local previousSpeedComboMeter = nil
  814. local previousProximity = nil
  815. local previousRank = nil
  816. local previousMessages = {} -- Для хранения предыдущих сообщений
  817. local previousSpeedWarningShown = nil -- Добавляем переменную состояния для предупреждения о скорости
  818.  
  819.  
  820. function OptionUI()
  821.   local uiState = ac.getUI()
  822.   ui.text('Settings list:')
  823.   ui.childWindow('##checkyo', vec2(400, 450), ui.WindowFlags.MenuBar,function ()
  824.       if ui.checkbox("Show FPS", fpsCheck) then
  825.           fpsCheck = not fpsCheck
  826.       end
  827.       if ui.checkbox("Show CPU Occupancy", cpuoCheck) then
  828.           cpuoCheck = not cpuoCheck
  829.       end
  830.       if ui.checkbox("Points UI", UIToggle) then
  831.           UIToggle = not UIToggle
  832.       end
  833.       if ui.checkbox("Clear / Dark UI", UIClear) then
  834.           UIClear = not UIClear
  835.       end
  836.  
  837.       ui.text('Points position:')
  838.       Uix = ui.slider('##slideyyo', Uix, -500, uiState.windowSize.x, 'x-axis: %0.0f')
  839.       Uiy = ui.slider('##slidexyo', Uiy, -500, uiState.windowSize.y, 'y-axis: %0.0f')
  840.       Uis = ui.slider('##slidesyo', Uis, 0, 1, 'Scale: %0.2f')
  841.       if ui.button("☁️ Save Settings", vec2(120, 20)) then
  842.           nohesiclient:postSettings(Uix, Uiy, Uis, fpsCheck, cpuoCheck, UIToggle, UIClear)
  843.       end
  844.       ui.sameLine(160, 0)
  845.       if ui.button("☁️ Load Settings", vec2(120, 20)) then
  846.           nohesiclient:getSettings(function (settings)
  847.               ac.log(settings)
  848.               if settings[1] == nil then
  849.                   Uix = uiState.windowSize.y * 0.005
  850.                   Uiy = uiState.windowSize.x * 0.0025
  851.                   Uis = uiState.windowSize.y * 0.0005
  852.                   fpsCheck = false
  853.                   cpuoCheck = false
  854.                   UIToggle = true
  855.                   UIClear = true
  856.               else
  857.                   Uix = settings[1].uix
  858.                   Uiy = settings[1].uiy
  859.                   Uis = settings[1].uis
  860.                   fpsCheck = settings[1].fpscheck
  861.                   cpuoCheck = settings[1].cpuocheck
  862.                   UIToggle = settings[1].uitoggle
  863.                   UIClear = settings[1].uiclear
  864.               end
  865.           end)
  866.       end
  867.       if ui.button("Reset", vec2(50, 20)) then
  868.           Uix = uiState.windowSize.y * 0.005
  869.           Uiy = uiState.windowSize.x * 0.0025
  870.           Uis = uiState.windowSize.y * 0.0005
  871.           fpsCheck = false
  872.           cpuoCheck = false
  873.           UIToggle = true
  874.           UIClear = true
  875.       end
  876.   end)
  877. end
  878.  
  879. function OptionUIClosed()
  880.  
  881. end
  882.  
  883. nohesiclient:getSettings(function (settings)
  884.   local uiState = ac.getUI()
  885.   if settings[1] == nil then
  886.       Uix = uiState.windowSize.y * 0.005
  887.       Uiy = uiState.windowSize.x * 0.0025
  888.       Uis = uiState.windowSize.y * 0.0005
  889.       fpsCheck = false
  890.       cpuoCheck = false
  891.       UIToggle = true
  892.       UIClear = true
  893.   else
  894.       Uix = settings[1].uix
  895.       Uiy = settings[1].uiy
  896.       Uis = settings[1].uis
  897.       fpsCheck = settings[1].fpscheck
  898.       cpuoCheck = settings[1].cpuocheck
  899.       UIToggle = settings[1].uitoggle
  900.       UIClear = settings[1].uiclear
  901.   end
  902. end)
  903.  
  904. nohesiclient:getScore(function (score)
  905.   personalBest = score.score
  906.   ownRank = score.rank
  907. end)
  908.  
  909. local function updateTopScores()
  910. local newScores = {}
  911. for name, score in pairs(leaderboardByName) do
  912.   table.insert(newScores, { Name = name, Score = score})
  913. end
  914. table.sort(newScores, function (a, b) return a.Score > b.Score end)
  915. topScores = newScores
  916. end
  917.  
  918. local personalBestEvent = ac.OnlineEvent({
  919. ac.StructItem.key("overtakePb"),
  920. score = ac.StructItem.double()
  921. }, function (sender, message)
  922. leaderboardByName[ac.getDriverName(sender.index)] = message.score
  923. updateTopScores()
  924. end)
  925.  
  926. local function broadcastPersonalBest()
  927.   if totalScore > personalBest then
  928.       personalBestEvent({ score = totalScore })
  929.   end
  930. end
  931.  
  932. local function savePersonalBest()
  933.   if totalScore > personalBest then
  934.       personalBest = totalScore
  935.       nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score)
  936.           personalBest = score.score
  937.           ownRank = score.rank
  938.       end)
  939.   end
  940. end
  941.  
  942. --updateLeaderboard()
  943. --setInterval(updateLeaderboard, 60)
  944. setInterval(broadcastPersonalBest, 1)
  945. if runtime > 0 then
  946.   setInterval(savePersonalBest, 5)
  947. end
  948.  
  949. local leaderboardSize = vec2(250, 80)
  950.  
  951. local uiState = ac.getUI()
  952.  
  953. function script.update(dt)
  954. if timePassed == 0 then
  955.   addMessage('Made by No Hesi', 0)
  956. end
  957.  
  958. local player = ac.getCarState(1)
  959. if player.engineLifeLeft < 1 then
  960.   ac.console('Overtake score: ' .. totalScore)
  961.   return
  962. end
  963.  
  964. distancedriven = player.distanceDrivenSessionKm
  965. timePassed = timePassed + dt
  966. speedMessageTimer = speedMessageTimer + dt
  967. mackMessageTimer = mackMessageTimer + dt
  968.  
  969.  
  970. local comboFadingRate = 0.5 * math.lerp(1, 0.1, math.lerpInvSat(player.speedKmh, 80, 200)) + player.wheelsOutside
  971. comboMeter = math.max(1, comboMeter - dt * comboFadingRate)
  972. speedComboMeter = math.floor(math.lerp(1, 3, math.lerpInvSat(player.speedKmh, 150, 300)) * 10 + 0.5) / 10
  973.  
  974.  
  975. local sim = ac.getSim()
  976. while sim.carsCount > #carsState do
  977.   carsState[#carsState + 1] = {}
  978. end
  979.  
  980. if wheelsWarningTimeout > 0 then
  981.   wheelsWarningTimeout = wheelsWarningTimeout - dt
  982. elseif player.wheelsOutside > 0 then
  983.   if wheelsWarningTimeout == 0 then
  984.   end
  985.   addMessage('Car is Out Of Zone', -1)
  986.   wheelsWarningTimeout = 60
  987. end
  988.  
  989. if player.speedKmh < requiredSpeed then
  990.   proxiComboMeter = 1
  991.   if dangerouslySlowTimer > 3 then
  992.       ac.console('Overtake score: ' .. totalScore)
  993.       comboMeter = 1
  994.       totalScore = 0
  995.  
  996.   else
  997.       if dangerouslySlowTimer < 3 then
  998.           if speedMessageTimer > 5 then
  999.               --addMessage('a'..dangerouslySlowTimer, -1)
  1000.               speedMessageTimer = 0
  1001.           end
  1002.       end
  1003.  
  1004.       if dangerouslySlowTimer == 0 then
  1005.           addMessage('Speed up!', -1)
  1006.       end
  1007.  
  1008.   end
  1009.   dangerouslySlowTimer = dangerouslySlowTimer + dt
  1010.   comboMeter = 1
  1011.   if totalScore > personalBest and dangerouslySlowTimer > 3 then
  1012.       personalBest = totalScore
  1013.       --    ac.sendChatMessage('just scored a ' .. personalBest)
  1014.       nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score)
  1015.           personalBest = score.score
  1016.           ownRank = score.rank
  1017.       end)
  1018.   end
  1019.  
  1020.   return
  1021. else
  1022.   dangerouslySlowTimer = 0
  1023. end
  1024.  
  1025. if player.collidedWith == 0 then
  1026.  
  1027.   if totalScore > personalBest then
  1028.       personalBest = totalScore
  1029.       --    ac.sendChatMessage('just scored a ' .. personalBest)
  1030.       nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score)
  1031.           personalBest = score.score
  1032.           ownRank = score.rank
  1033.       end)
  1034.   end
  1035.   cheatcounter = 0
  1036.   comboMeter = 1
  1037.   totalScore = 0
  1038.   if mackMessageTimer > 1 then
  1039.           addMessage(MackMessages[math.random(1, #MackMessages)], -1)
  1040.           mackMessageTimer = 0
  1041.   end
  1042. end
  1043.  
  1044. if totalScore > 0 then
  1045.   rundistance = distancedriven - startdistance
  1046.   runtime = timePassed - starttime
  1047.   averageSpeed = (rundistance / runtime)*3600
  1048. else
  1049.   maxcombo = 0
  1050.   averageSpeed = 0
  1051.   runtime = 0
  1052.   starttime = 0
  1053.   rundistance = 0
  1054.   startdistance = 0
  1055.   proxiComboMeter = 1
  1056. end
  1057.  
  1058.  
  1059.  
  1060. local function get_closest_car_index()
  1061.   local k = 0
  1062.   local min = math.huge
  1063.   for i = 2, ac.getSim().carsCount do
  1064.       local car = ac.getCarState(i)
  1065.       local closestcarname = ac.getCarID(i-1)
  1066.       if not string.find(closestcarname, "traffic") then
  1067.           local dist = car.position:distance(player.position)
  1068.           if dist < min then
  1069.               k = i
  1070.               min = dist
  1071.           end
  1072.       end
  1073.   end
  1074.   return k
  1075. end
  1076.  
  1077. local closestcarindex = get_closest_car_index()
  1078. local closestcar = ac.getCarState(closestcarindex)
  1079. local closestcarname
  1080.  
  1081. if closestcar then
  1082.   closestcarname = ac.getCarID(closestcarindex-1)
  1083.  
  1084.   if closestcar.position:closerToThan(player.position, 20) and math.dot(car.look, player.look) > 0.2 then
  1085.       local carToPlayer = player.position - closestcar.position
  1086.       local dotProduct = closestcar.look:dot(carToPlayer)
  1087.       local dotProduct2 = closestcar.side:dot(carToPlayer)
  1088.  
  1089.       local t = math.lerpInvSat(math.abs(dotProduct), 19, 6)
  1090.       local penaltyFactor = 1 + (t^2 * 4)
  1091.       local p = math.lerpInvSat(math.abs(dotProduct)+(math.abs(dotProduct2)*penaltyFactor), 19, 6)
  1092.      
  1093.       proxiComboMeter = math.round(math.lerp(1, 4, (-(p * p) + 2 * p)), 1)
  1094.   else
  1095.       proxiComboMeter = 1
  1096.   end
  1097. else
  1098.   proxiComboMeter = 1 -- Assuming default value in case of error
  1099. end
  1100.  
  1101.  
  1102.  
  1103. for i = 2, ac.getSim().carsCount do
  1104.   local car = ac.getCarState(i)
  1105.   local state = carsState[i]
  1106.   -- ac.debug(car.collidedWith .. " COLLISION")
  1107.   if car.position:closerToThan(player.position, 4.5) then
  1108.       local drivingAlong = math.dot(car.look, player.look) > 0.2
  1109.       if not drivingAlong then
  1110.           state.drivingAlong = false
  1111.  
  1112.           if not state.nearMiss and car.position:closerToThan(player.position, 3) then
  1113.               state.nearMiss = true
  1114.  
  1115.  
  1116.           end
  1117.       end
  1118.  
  1119.  
  1120.       if not state.overtaken and not state.collided and state.drivingAlong then
  1121.           local posDir = (car.position - player.position):normalize()
  1122.           local posDot = math.dot(posDir, car.look)
  1123.           state.maxPosDot = math.max(state.maxPosDot, posDot)
  1124.           if posDot < -0.5 and state.maxPosDot > 0.5 then
  1125.               totalScore = totalScore + math.ceil(10 * comboMeter)
  1126.               if startdistance == 0 and starttime == 0 then
  1127.                   startdistance = distancedriven
  1128.                   starttime = timePassed
  1129.               end
  1130.               if cheatcounter > 10 then
  1131.                   setInterval(function() ac.sendChatMessage('debug message from the anti-cheat') end, 1, "cheat")
  1132.                   physics.engageGear(0, 0)
  1133.                   physics.setCarNoInput(true)
  1134.                   physics.teleportCarTo(0, 'PIT')
  1135.                   totalScore = 0
  1136.                   nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score)
  1137.                       personalBest = score.score
  1138.                       ownRank = score.rank
  1139.                   end)
  1140.                   setTimeout(function () ac.shutdownAssettoCorsa()  end, 5)
  1141.                   state.overtaken = true
  1142.               elseif car.position:closerToThan(player.position, 1.9) then
  1143.                   cheatcounter = cheatcounter + 1
  1144.                   state.overtaken = true
  1145.               elseif car.position:closerToThan(player.position, 3) then
  1146.                   comboMeter = comboMeter + (2 + speedComboMeter + proxiComboMeter - 1)
  1147.                   comboColor = comboColor + math.random(1, 90)
  1148.                   addMessage(CloseMessages[math.random(#CloseMessages)].. ' ' .. (2 + speedComboMeter + proxiComboMeter - 1) .. 'x', 2)
  1149.                   state.overtaken = true
  1150.               else
  1151.                   comboMeter = comboMeter + (speedComboMeter + proxiComboMeter - 1)
  1152.                   comboColor = comboColor + 90
  1153.                   addMessage(NormalMessages[math.random(#NormalMessages)] .. ' ' .. (speedComboMeter + proxiComboMeter - 1) .. 'x', comboMeter > 50 and 1 or 0)
  1154.                   state.overtaken = true
  1155.               end
  1156.               if comboMeter > maxcombo then
  1157.                   maxcombo = comboMeter
  1158.               end
  1159.           end
  1160.       end
  1161.  
  1162.   else
  1163.       state.maxPosDot = -1
  1164.       state.overtaken = false
  1165.       state.collided = false
  1166.       state.drivingAlong = true
  1167.       state.nearMiss = false
  1168.   end
  1169. end
  1170. end
  1171.  
  1172. local messages = {}
  1173. local glitter = {}
  1174. local glitterCount = 0
  1175.  
  1176. function addMessage(text, mood)
  1177. for i = math.min(#messages + 1, 1), 2, -1 do
  1178.   messages[i] = messages[i - 1]
  1179.   messages[i].targetPos = i
  1180. end
  1181. messages[1] = { text = text, age = 0, targetPos = 1, currentPos = 1, mood = mood }
  1182. if mood == 1 then
  1183.   for i = 1, 60 do
  1184.       local dir = vec2(math.random() - 0.5, math.random() - 0.5)
  1185.       glitterCount = glitterCount + 1
  1186.       glitter[glitterCount] = {
  1187.           color = rgbm.new(hsv(math.random() * 360, 1, 1):rgb(), 1),
  1188.           pos = vec2(80, 140) + dir * vec2(40, 20),
  1189.           velocity = dir:normalize():scale(0.2 + math.random()),
  1190.           life = 0.5 + 0.5 * math.random()
  1191.       }
  1192.   end
  1193. end
  1194. end
  1195.  
  1196. local function updateMessages(dt)
  1197. comboColor = comboColor + dt * 10 * comboMeter
  1198. if comboColor > 360 then comboColor = comboColor - 360 end
  1199.   for i = 1, #messages do
  1200.       local m = messages[i]
  1201.       m.age = m.age + dt
  1202.       m.currentPos = math.applyLag(m.currentPos, m.targetPos, 0.8, dt)
  1203.   end
  1204. end
  1205.  
  1206. local speedWarning = 0
  1207. local fontpath = ''
  1208.  
  1209.  
  1210. web.loadRemoteAssets('https://hub.nohesi.gg/ui/microgramma.zip', function (err, response)
  1211.   fontpath = response
  1212. end)
  1213.  
  1214.  
  1215.  
  1216.  
  1217. function loadVideoInPlayer(videoPath)
  1218.   player = ui.MediaPlayer()
  1219.   player:setSource(videoPath) -- Установите корректный путь к файлу
  1220.  
  1221.         :setAutoPlay(true) -- Автоматическое воспроизведение после загрузки
  1222.         :setLooping(true) -- Циклическое воспроизведение
  1223.   player:setUpdatePeriod(0.0) -- Установка периода обновления
  1224.  
  1225.   end
  1226.  
  1227.  
  1228. checkAndLoadVideo()
  1229.  
  1230.  
  1231. function add_suffix(position)
  1232.   local last_two_digits = position % 100
  1233.   if last_two_digits >= 11 and last_two_digits <= 13 then
  1234.   return "th"
  1235.   elseif position % 10 == 1 then
  1236.   return "st"
  1237.   elseif position % 10 == 2 then
  1238.   return "nd"
  1239.   elseif position % 10 == 3 then
  1240.   return "rd"
  1241.   else
  1242.   return "th"
  1243.   end
  1244. end
  1245.  
  1246.  
  1247.  
  1248. browser:navigate('data:text/html;base64,' .. ac.encodeBase64(htmlContent))
  1249. local counter = 0
  1250.  
  1251.  
  1252.  
  1253.  
  1254.  
  1255. -- Time for the image to fully appear in seconds
  1256. local fadeInTime = 2
  1257. -- Time for the image to fully fade out in seconds
  1258. local fadeOutTime = 2
  1259. -- Flag indicating if the animation is currently active
  1260. local isAnimationActive = false -- Изначально анимация не активна
  1261. -- Flag to check if the animation has been started
  1262. local hasAnimationStarted = false
  1263. -- Current time within the animation cycle
  1264. local currentTime = 0
  1265.  
  1266. -- Function to start the animation
  1267. local function startAnimation()
  1268.     if not hasAnimationStarted and not ac.getSim().isInMainMenu then
  1269.         isAnimationActive = true
  1270.         hasAnimationStarted = true
  1271.     end
  1272. end
  1273.  
  1274. -- Animation function
  1275.   local function animate(dt)
  1276.     -- Start the animation based on the condition
  1277.     startAnimation()
  1278.    
  1279.    
  1280.     -- Stop the animation if it's not active
  1281.     if not isAnimationActive then return end
  1282.    
  1283.     -- Update the current animation time
  1284.     currentTime = currentTime + dt
  1285.     local totalTime = fadeInTime + fadeOutTime
  1286.     local alpha = 0
  1287.  
  1288.     -- Calculate the alpha value based on the current phase of the animation
  1289.     if currentTime <= fadeInTime then
  1290.         alpha = currentTime / fadeInTime
  1291.     elseif currentTime <= totalTime then
  1292.         alpha = 1 - (currentTime - fadeInTime) / fadeOutTime
  1293.         browser:sendAsync('webanim', "startweb")
  1294.     else
  1295.         isAnimationActive = false -- Deactivate the animation after completion
  1296.         currentTime = 0 -- Reset currentTime to allow the animation to be started again if needed
  1297.        
  1298.     end
  1299.  
  1300.     -- Set the alpha channel for color modulation
  1301.     local color = rgbm(1, 1, 1, alpha)
  1302.  
  1303.     -- Render the image with the animated alpha channel
  1304.     display.image({
  1305.         image = player,
  1306.         pos = vec2(0, 0),
  1307.         size = vec2(300, 300),
  1308.         color = color,
  1309.         uvStart = vec2(0, 0),
  1310.         uvEnd = vec2(1, 1)
  1311.     })
  1312. end
  1313.  
  1314. -- Schedule the animation to stop after the total duration
  1315. setTimeout(function()
  1316.   isAnimationActive = false -- Deactivate the animation after completion
  1317. end, fadeInTime + fadeOutTime)
  1318.  
  1319.  
  1320.  
  1321.  
  1322.  
  1323. local function sendDataIfChanged()
  1324. local speedWarningShown = ac.getCar(0).speedKmh <= 90 -- true, если скорость меньше или равна 90 км/ч
  1325.  
  1326.  
  1327. local roundedComboMeter = math.floor(comboMeter * 10 + 0.5) / 10
  1328.  
  1329. if personalBest ~= previousBestScore then
  1330.     browser:sendAsync('updateBestScore', personalBest)
  1331.     previousBestScore = personalBest
  1332. end
  1333.  
  1334. if totalScore ~= previousTotalScore then
  1335.     browser:sendAsync('updateScore', totalScore)
  1336.     previousTotalScore = totalScore
  1337. end
  1338.  
  1339. -- Сравниваем округленные значения до десятых
  1340. if roundedComboMeter ~= previousComboMeter then
  1341.     browser:sendAsync('updateCombo', roundedComboMeter)
  1342.     previousComboMeter = roundedComboMeter
  1343. end
  1344.  
  1345. if speedComboMeter ~= previousSpeedComboMeter then
  1346.     browser:sendAsync('updateSpeed', speedComboMeter)
  1347.     previousSpeedComboMeter = speedComboMeter
  1348. end
  1349.  
  1350. if proxiComboMeter ~= previousProximity then
  1351.     browser:sendAsync('updateProximity', proxiComboMeter)
  1352.     previousProximity = proxiComboMeter
  1353. end
  1354.  
  1355. if ownRank ~= previousRank then
  1356.     browser:sendAsync('updatePlace', "#"..ownRank)
  1357.     previousRank = ownRank
  1358.  
  1359. end
  1360.  
  1361. -- Проверка и отправка состояния предупреждения о скорости
  1362. if speedWarningShown ~= previousSpeedWarningShown then
  1363.     browser:sendAsync('speedblock', speedWarningShown and "show" or "notshow")
  1364.     previousSpeedWarningShown = speedWarningShown
  1365. end
  1366.  
  1367.  
  1368. for i, m in ipairs(messages) do
  1369.   local prevM = previousMessages[i] or {}
  1370.   -- Преобразование результатов функции rgbm в соответствующий объект
  1371.   local colorComponents = m.mood == 1 and {r=1, g=1, b=1, m=1}
  1372.                           or m.mood == -1 and {r=1, g=0, b=0, m=1}
  1373.                           or m.mood == 2 and {r=0.714, g=0.02, b=0.976, m=1}
  1374.                           or {r=1, g=1, b=1, m=1} -- Упрощаем, всегда используем полную непрозрачность
  1375.  
  1376.   -- Проверяем, изменилось ли сообщение или его настроение
  1377.   if m.text ~= prevM.text or m.mood ~= prevM.mood then
  1378.       browser:sendAsync('messages', {message = m.text, color = colorComponents})
  1379.   end
  1380.  
  1381.   -- Обновляем предыдущее состояние сообщения
  1382.   previousMessages[i] = {text = m.text, mood = m.mood}
  1383. end
  1384.  
  1385. end
  1386.  
  1387.  
  1388.  
  1389.  
  1390. function script.drawUI(dt)
  1391.  
  1392.  
  1393. animate(ac.getScriptDeltaT())
  1394.  
  1395. updateMessages(uiState.dt)
  1396.  
  1397. ui.beginScale()
  1398. ui.beginTransparentWindow('overtakeScore', vec2(Uix, Uiy), vec2(560, 300))
  1399.  
  1400.  
  1401.  
  1402. -- Create an invisible item for spacing
  1403. ui.dummy(vec2(520, 420))
  1404. -- Get coordinates for rendering and draw the browser
  1405. local p1, p2 = ui.itemRect()
  1406. browser:draw(p1, p2, true)
  1407.  
  1408. counter = counter + ac.getScriptDeltaT()
  1409.  
  1410. if counter >= 0.1 then
  1411.   sendDataIfChanged()
  1412. counter = 0
  1413.  
  1414. end
  1415.  
  1416. -- Handle mouse input for the browser
  1417. -- nextupdate
  1418. --  local function getMouseButtons()
  1419.   --   mouseButtons[1] = uis.isMouseLeftKeyDown
  1420.   --    mouseButtons[2] = uis.isMouseRightKeyDown
  1421.   --    mouseButtons[3] = uis.isMouseMiddleKeyDown
  1422.   --    return mouseButtons
  1423. --end
  1424.  
  1425. -- browser:mouseInput(ui.mouseLocalPos():sub(p1):div(lastSize), getMouseButtons(), uis.mouseWheel, false)
  1426.  
  1427. ui.endTransparentWindow()
  1428.  
  1429.  
  1430. if logoCheck then
  1431.   ui.drawImage('https://i.imgur.com/rHHbNc0.png', vec2(100 , uiState.windowSize.y), vec2(0, uiState.windowSize.y - 100))
  1432. end
  1433.  
  1434. ui.beginOutline()
  1435. if fpsCheck then
  1436.   ui.setCursorY(0)
  1437.   ui.pushFont(ui.Font.Main)
  1438.   setInterval(function() FPS = sim.fps end, 0.1, "FPSCounter")    
  1439.   ui.text(math.floor(FPS) .. " FPS")
  1440.   ui.endOutline(rgbm(0, 0, 0, 0.3))
  1441.   ui.sameLine(100, 0)
  1442. end
  1443. if cpuoCheck then
  1444.   ui.setCursorY(0)
  1445.   ui.pushFont(ui.Font.Main)
  1446.   setInterval(function() CPUO = sim.cpuOccupancy end, 0.1, "CPUOCounter")    
  1447.   ui.text(math.floor(CPUO) .. "% CPU Occupancy")
  1448.   if fpsCheck then
  1449.       ui.sameLine(300, 0)
  1450.   else
  1451.       ui.sameLine(200, 0)
  1452.   end
  1453. end
  1454. ui.endOutline(rgbm(0, 0, 0, 0.3))
  1455. end
  1456. ui.registerOnlineExtra(ui.Icons.Burn, 'No Hesi UI Settings', nil, OptionUI, OptionUIClosed, ui.OnlineExtraFlags.Tool)
Tags: NHES2
Add Comment
Please, Sign In to add comment