Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local config = ac.configValues({
- hubUrl = ""
- })
- ---@class NoHesiClient
- local NoHesiClient = class("NoHesiClient")
- local closestcar = 1
- -- Import the WebBrowser library
- local WebBrowser = require('shared/web/browser')
- ui.setAsynchronousImagesLoading(true)
- -- Configure the WebBrowser
- WebBrowser.configure({
- targetFPS = 240,
- })
- -- Create an instance of the browser with initial parameters
- local browser = WebBrowser({
- size = vec2(500, 400), -- Set the size of the browser window
- backgroundColor = rgbm(0,0,0,0), -- Set the background color
- directRender = true, -- Enabl direct rendering
- redirectAudio = true, -- Enabele audio redirection
- })
- -- Store the last size of the browser window
- local lastSize = vec2(450, 420)
- -- Initialize mouse buttons states
- local mouseButtons = {false, false, false}
- function checkAndLoadVideo()
- local rootFolder = ac.getFolder(ac.FolderID.Root) .. "\\cache\\remote_assets\\nohesivideo"
- local targetFile = rootFolder .. "\\2_90FPS_2.wmv"
- print("Checking if target file exists: " .. targetFile)
- -- Check if the file exists
- if not io.fileExists(targetFile) then
- print("File does not exist, starting download process.")
- local function onAssetsLoaded(err, folder)
- if err then
- print("Error loading assets: " .. err)
- return
- else
- print("Assets loaded successfully.")
- end
- -- Path to the file to move
- local sourceFile = folder .. "\\2_90FPS_2.wmv"
- print("Source file path: " .. sourceFile)
- -- Create the directory if it doesn't exist
- if not io.dirExists(rootFolder) then
- print("Target directory does not exist, creating: " .. rootFolder)
- io.createDir(rootFolder)
- else
- print("Target directory already exists.")
- end
- -- Attempt to move the file
- print("Attempting to move file from " .. sourceFile .. " to " .. targetFile)
- if io.move(sourceFile, targetFile) then
- print("File successfully moved to " .. targetFile)
- loadVideoInPlayer(targetFile)
- else
- print("Failed to move the file. Checking permissions and file locks.")
- end
- end
- -- Start downloading and unpacking the assets
- print("Initiating download from URL.")
- web.loadRemoteAssets("https://cdn.discordapp.com/attachments/880053607024717854/1209263927045652591/2_90FPS_2.zip?ex=65e649cb&is=65d3d4cb&hm=33f584a885ac8ea30f635cea6c28831a9ea8bf0e17e4799a51597b2bc1b81d00&", onAssetsLoaded)
- else
- print("File already exists, loading into MediaPlayer.")
- loadVideoInPlayer(targetFile)
- end
- end
- local htmlContent = [[
- <!DOCTYPE html>
- <html lang='en'>
- <head>
- <meta charset='UTF-8'>
- <link rel="preconnect" href="https://fonts.googleapis.com">
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
- <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">
- <style>
- .shake-top {
- animation: shake-top 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both;
- }
- @keyframes shake-top {
- 0%, 100% {
- transform: rotate(0deg);
- transform-origin: 50% 0;
- }
- 10% {
- transform: rotate(2deg);
- }
- 20%, 40%, 60% {
- transform: rotate(-4deg);
- }
- 30%, 50%, 70% {
- transform: rotate(4deg);
- }
- 80% {
- transform: rotate(-2deg);
- }
- 90% {
- transform: rotate(2deg);
- }
- }
- .blink-2 {
- animation: blink-2 0.4s both;
- }
- @keyframes blink-2 {
- 0% {
- opacity: 1;
- }
- 50% {
- opacity: 0.1;
- }
- 100% {
- opacity: 1;
- }
- }
- :root {
- --font-family: 'Roboto Flex', sans-serif;
- --second-family: 'Roboto', sans-serif;
- --main-color: #B130FF; /* Purple color */
- }
- body, html {
- height: 100%;
- margin: 0;
- display: flex;
- justify-content: left;
- align-items: baseline;
- background-color: rgba(0, 0, 0, 0.0)/* Dark background */
- }
- .dashboard {
- justify-content: flex-start; /* Это изменение выровняет все содержимое dashboard, включая logo, слева */
- }
- .logo {
- justify-content: flex-start;
- width: 100%;
- }
- .logo-text {
- margin-left: 10px;
- font-size: 24px;
- font-weight: 700;
- font-family: var(--second-family);
- }
- .score-section {
- display: flex;
- align-items: center; /* This will vertically center the items */
- margin-top: 20px; /* Adjust as necessary */
- }
- .score-text {
- margin-top: -13px; /* Поднимает элемент на 10 пикселей вверх */
- font-family: 'Roboto Flex', sans-serif;
- font-weight: 1000; /* Extra Black - Обратите внимание, что максимальный доступный вес может быть 900 для некоторых шрифтов */
- font-size: 26px;
- line-height: 106%;
- letter-spacing: 0.03em;
- text-transform: uppercase;
- color: #fff;
- /* Применение настроек переменных шрифтов */
- 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;
- }
- .score-badge {
- margin-top: -40px; /* Поднимает элемент вверх */
- margin-left: 20px; /* Поднимает элемент вверх */
- border: 1.2px solid rgba(255, 255, 255, 0.13); /* Оставляем рамку как есть, если она вам нужна */
- border-radius: 4px;
- padding: 4px 8px 4px 4px; /* Измененные паддинги */
- width: 71px; /* Измененная ширина */
- height: 21px; /* Измененная высота */
- background: rgba(1, 1, 1, 1); /* Новый цвет фона */
- display: flex;
- justify-content: center;
- align-items: center;
- gap: 6px; /* Добавляем пространство между SVG и текстом */
- }
- .score-badge-text {
- font-family: 'Roboto Flex', sans-serif;
- font-weight: 740; /* Medium */
- font-size: 16px; /* Размер шрифта оставляем как был */
- color: rgba(238, 237, 238, 0.87); /* Цвет текста оставляем как был */
- 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;
- }
- .speed-up {
- margin-top: -20px; /* Поднимает элемент на 10 пикселей вверх */
- font-family: 'Roboto Flex', sans-serif;
- font-weight: 700; /* Bold */
- font-stretch: expanded;
- font-style: oblique 10deg; /* Для курсива */
- font-size: 11px;
- line-height: 82%;
- text-transform: uppercase;
- color: #ff2074;
- border: 0.49px solid rgba(255, 0, 95, 0.68);
- border-radius: 8px;
- padding: 7px 13px;
- width: 210px;
- height: 15px;
- background: rgba(255, 32, 116, 0.15);
- display: flex;
- justify-content: center;
- align-items: center;
- /* Использование переменных шрифтов */
- font-variation-settings: 'wdth' 151, 'slnt' -10, 'XTRA' 468, 'XOPQ' 96, 'YOPQ' 79, 'YTLC' 514, 'YTUC' 712, 'YTAS' 750, 'YTDE' -203, 'YTFI' 738;
- }
- .points-info {
- font-weight: 1000; /* Extra Black - убедитесь, что шрифт поддерживает это значение */
- font-size: 22px;
- line-height: 100%;
- text-transform: uppercase;
- color: #fff; /* White color */
- font-family: 'Roboto Flex', sans-serif;
- 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;
- margin-top: 40px; /* Adjust space from the SPEED UP block */
- }
- .block-model {
- display: flex;
- justify-content: flex-start; /* Выравнивает элементы по левому краю */
- gap: 9px; /* Использует переменную для расстояния между элементами */
- margin-top: 10px; /* Отступ сверху */
- padding-left: var(--padding-left-block-model); /* Если нужен отступ слева для всего контейнера */
- width: 100%; /* Занимает всю доступную ширину */
- }
- .item {
- display: flex;
- flex-direction: column; /* Устанавливает направление элементов в колонку */
- align-items: center; /* Центрирует элементы по горизонтали */
- }
- .combo-text {
- font-weight: 700;
- font-size: 12px;
- line-height: 0%;
- letter-spacing: -0.02em;
- text-transform: uppercase;
- text-align: center;
- color: var(--combo-text-color, rgba(238, 237, 238, 0.68)); /* Значение по умолчанию */
- font-family: 'Roboto Flex', sans-serif;
- margin-bottom: 8px; /* Отступ между текстом COMBO и объектом */
- 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;
- }
- .object {
- border: 0.86px solid rgba(255, 255, 255, 0.13);
- border-radius: 6px;
- padding: 5px 6px;
- width: 61px;
- height: 20px;
- background: rgba(255, 255, 255, 0.08);
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .object-text {
- font-weight: 600;
- font-size: 14px;
- line-height: 143%;
- text-align: center;
- color: rgba(238, 237, 238, 0.87);
- font-family: 'Roboto Flex', sans-serif;
- }
- .speed-warning {
- color: rgba(255, 32, 116, 1);
- font-family: 'Roboto Flex', sans-serif;
- font-size: 14px;
- font-weight: 700;
- line-height: 10px;
- letter-spacing: 0em;
- text-transform: uppercase; /* Делает весь текст в верхнем регистре */
- text-align: left; /* Центрирует текст по центру */
- 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;
- margin-top: 20px; /* Отступ сверху, чтобы отделить текст от объектов */
- margin-left: 20px; /* Отступ сверху, чтобы отделить текст от объектов */
- display: block; /* Гарантирует, что элемент будет на новой строке */
- }
- .content {
- visibility: hidden; /* Содержимое скрыто, но прогружается */
- opacity: 0;
- transition: visibility 0s, opacity 1s linear; /* Плавное появление */
- transition-delay: 2s; /* Задержка перед началом анимации появления */
- }
- .logo-text {
- position: absolute;
- opacity: 0; /* Изначально текст невидим */
- margin-top: 30px
- }
- </style>
- </head>
- <body>
- </div>
- </div>
- </div>
- <div class="content">
- <div class='dashboard'>
- <div class='logo'>
- <svg width='90' height='40' viewBox='0 0 400 200' fill='none' xmlns='http://www.w3.org/2000/svg'>
- <path d='M79.212,143.7q-6.688,5.168-9.804,5.168q-1.672,0-2.47-1.634t-0.798-3.534q0-3.648,3.078-10.944t5.89-7.296q1.444,0,2.47,5.7t2.09,8.284q5.168-5.472,14.972-18.24q12.312-16.188,16.34-17.708q1.368-0.532,2.812-0.532t2.394,0.608t0.95,1.52q0,1.292-2.204,1.52q-2.66,1.444-8.208,6.992q-5.548,5.396-8.892,10.108q-8.132,11.324-16.72,18.468q4.256,6.308,13.376,6.308q0.988,0,2.964-0.152q21.128-1.976,29.944-18.316q3.572-6.536,3.572-12.92t-4.636-11.476q-3.724-4.18-8.094-5.168t-8.93-0.988q-11.704,0-21.584,5.396q-10.412,5.7-10.412,12.768q0,1.9,1.026,3.116t1.406,1.368l-0.456,0q-1.976-0.38-3.496-1.976q-1.672-1.976-1.672-4.18q0-7.6,9.538-13.072t22.382-5.472q5.928,0,12.16,1.368t11.324,6.536q5.852,6.08,5.852,13.756q0,5.7-3.344,11.78q-5.776,10.488-17.252,15.352q-9.196,3.876-19.836,3.876t-15.732-6.384z M68.876,143.396q0,2.356,0.912,4.104q3.268-1.444,7.904-6.004q-2.204-3.648-2.774-5.738t-0.798-5.51q-5.244,7.524-5.244,13.148z M79.744,122.116q0.076,0,0.152-0.076q0,0.152-0.038,0.152t-0.114-0.076z M207.36092,105.092q-0.38,0-8.132-1.216q-12.844,7.296-29.45,18.696t-26.866,21.052q11.628-1.292,23.636-2.356q1.368-0.076,3.952-0.076q9.5,0,14.972,1.824t5.472,4.864q0,1.824-2.204,4.028q-2.888,2.66-3.952,2.66q-0.38,0-0.38-0.646t0.874-1.444t0.874-1.482q0-2.356-7.068-4.332q-7.372-2.128-13.832-2.128t-15.01,1.254t-13.49,1.254l-0.532,0q-1.216,1.064-1.52,1.064q-0.152,0-0.152-0.266t0.456-1.026q-2.812-0.152-2.812-1.596q0-1.14,1.216-2.356t2.128-1.216q0.152,0,0.304,0.152q0.38,0.608,3.268,1.064q19.228-20.064,55.1-39.748l-2.28-0.456q-5.396-1.216-13.908-1.216t-14.82,2.28q-1.368,0.304-4.104,1.672q-4.408,2.204-4.408,5.244q0,0.456,0.19,1.102t-0.038,1.026q-0.304,0.456-1.026,0.456t-1.368-1.064t-0.646-2.28t0.38-2.508q1.444-4.864,9.348-7.144q4.028-1.14,13.49-1.14t18.734,1.672q1.14,0.076,3.8,0.684l4.18-2.28q0.608-0.304,2.66-0.304t2.052,0.76q0,0.304-0.684,0.532q-0.304,0.228-3.876,2.204q3.42,0.228,4.218,0.228t1.444-0.38t0.456-1.14t0.114-0.76q0.456,0,1.102,1.026t0.646,1.862q0,1.9-2.508,1.9z M257.98984,113.072q-2.812,1.14-4.826,1.14t-2.014-0.836q0-0.532,2.242-0.608t3.23-0.532q2.888-1.52,2.888-4.484q0-1.52-0.836-3.192q-2.432-4.332-11.78-4.332q-5.396,0-10.792,1.634t-9.044,5.016t-3.648,6.27q0,4.408,8.74,6.46l8.968,1.672q5.396,0.988,8.056,2.736q3.496,2.128,3.496,6.08q0,1.596-0.532,3.42q-2.432,8.208-12.54,12.54q-8.436,3.724-19.912,3.724q-3.268,0-5.7-0.304q2.432,1.368,6.384,2.356q0.304,0.076,0.304,0.304q0,0.684-1.596,0.684q-3.116,0-7.752-3.724q-6.308-0.988-9.424-3.496q-1.976-1.596-3.648-4.712t-1.672-5.624t0.988-4.18q2.28-4.028,5.776-4.028q2.66,0,3.116,4.484q1.064,8.664,1.444,9.88q1.14,3.724,3.876,6.308q3.192,0.684,6.916,0.684q8.208,0,16.112-3.268t11.4-9.272q1.444-2.356,1.444-4.18q0-3.572-6.308-5.548l-13.376-3.04q-4.104-0.988-6.574-3.268t-2.47-5.244q0-4.56,5.7-9.12q4.332-3.496,10.602-5.434t12.312-1.938t9.994,1.748q5.092,2.356,5.092,6.878t-4.636,6.346z M209.04584,146.968q-4.408-4.864-5.624-13.3q-0.684-4.788-1.292-4.788t-1.14,0.342t-1.064,1.482t-0.532,2.964q0,5.852,4.104,10.032q2.052,2.128,5.548,3.268z M260.20676,140.508q0-8.208,4.864-16.264q-5.624-3.572-5.624-10.564q0-8.436,7.448-14.972q8.36-7.372,21.584-7.372q6.08,0,13.072,1.748q1.596,0.38,13.604,5.89t17.252,5.51q3.496,0,5.244-1.976q0.912-0.988,0.912-2.128t-0.988-2.052q-0.532-0.76-0.532-0.988t0.418-0.228t1.444,1.33t1.026,3.116t-2.394,3.42t-6.042,1.634q-5.7,0-17.708-5.51t-15.086-6.118t-7.03-0.608q-15.732,0-24.776,10.184q-1.064,1.14-2.508,4.598t-1.444,6.65q0,4.408,3.116,6.84q3.952-5.776,9.348-9.196t10.944-3.42q6.84,0,6.84,5.32q0,6.384-7.486,13.87t-13.87,7.486q-4.104,0-4.104-3.496q0-1.216,0.646-2.128t1.558-0.684l-0.152,1.748q0,1.14,3.496,1.14q5.168,0,10.906-5.32t6.954-10.412q0.304-1.14,0.304-2.052q0-3.42-4.37-3.42t-9.044,3.344t-8.778,8.816q0.304,0.152,2.356,0.608l4.56,0.152q3.344,0.152,3.344,0.76q0,1.064-3.192,1.064l-0.456,0q-4.636,0-7.6-1.14q-5.852,8.968-5.852,16.492q0,9.728,7.524,9.728q8.512,0,17.708-11.856l14.896-22.192q8.36-12.16,14.516-12.464l2.888,0.684q1.368,0.76,1.368,2.28t-0.76,1.52l-2.432-0.38q-5.244,0-13.452,11.324l-15.124,21.128q-9.728,11.324-18.62,11.324q-6.08,0-8.398-3.268t-2.318-9.5z' fill='#B130FF' />
- </svg>
- </div>
- <div class='score-section'>
- <p class='score-text'>PB 0 PTS</p>
- <div class='score-badge'>
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
- <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" />
- </svg>
- <span class='score-badge-text'>#1</span>
- </div>
- </div>
- </div>
- <!-- Новый элемент для SPEED UP -->
- <div class='speed-up'>SPEED UP!</div>
- <div class='points-info' id='pointsDisplay'>PB 0 PTS</div>
- <div class='block-model'>
- <div class='item'>
- <p class='combo-text'>COMBO</p>
- <div class='object'>
- <div class='object-text' style='color: rgba(255, 214, 67, 0.87);'>0x</div>
- </div>
- </div>
- <div class='item'>
- <p class='combo-text'>SPEED</p>
- <div class='object'>
- <div class='object-text' style='color: rgba(106, 255, 103, 0.87);'>0x</div> <!-- Пример другого цвета -->
- </div>
- </div>
- <div class='item'>
- <p class='combo-text'>Proximity</p>
- <div class='object'>
- <div class='object-text' style='color: rgba(238, 237, 238, 0.87);'>0x</div> <!-- И еще один цвет -->
- </div>
- </div>
- </div>
- <p class='speed-warning'>KEEP SPEED ABOVE 95KM/H</p>
- </div>
- </div>
- </div>
- <script>
- // Функция для плавного обновления числа
- // Функция для плавного обновления числа с поддержкой условного форматирования текста
- // Универсальная функция анимации для обновления числовых значений
- function animateValueUpdate({ selector, oldValue, newValue, isPB = false, precision = 0, unit }) {
- const duration = 700;
- const start = performance.now();
- function updateNumber(timestamp) {
- const elapsedTime = timestamp - start;
- const progress = Math.min(elapsedTime / duration, 1);
- const currentNumber = oldValue + (newValue - oldValue) * progress;
- // Использование локали 'en-US' для формата с запятой как разделителем тысяч
- const formattedNumber = currentNumber.toLocaleString('en-US', { minimumFractionDigits: precision, maximumFractionDigits: precision });
- const displayValue = isPB ? `PB: ${formattedNumber} ${unit}` : `${formattedNumber}${unit}`;
- document.querySelector(selector).textContent = displayValue;
- if (progress < 1) {
- requestAnimationFrame(updateNumber);
- }
- }
- requestAnimationFrame(updateNumber);
- }
- // Обработка обновления различных значений
- function updateValue(type, newValue) {
- const selectorMap = {
- bestScore: '.score-text',
- currentScore: '#pointsDisplay',
- combo: '.item:nth-child(1) .object-text',
- speed: '.item:nth-child(2) .object-text',
- proximity: '.item:nth-child(3) .object-text'
- };
- const isPB = type === 'bestScore';
- const selector = selectorMap[type];
- const currentValueText = document.querySelector(selector)?.textContent.replace(/[^\d.-]/g, '') || "0";
- const currentValue = parseFloat(currentValueText);
- const precision = ['combo', 'speed', 'proximity'].includes(type) ? 1 : 0;
- const unit = ['combo', 'speed', 'proximity'].includes(type) ? "x" : "PTS"; // Удаление пробела для X
- animateValueUpdate({ selector, oldValue: currentValue, newValue: parseFloat(newValue), isPB, precision, unit });
- }
- // Очередь для хранения данных обновлений
- const updateQueue = [];
- let isAnimating = false; // Флаг, показывающий, идет ли в данный момент анимация
- function processNextUpdate() {
- if (updateQueue.length === 0 || isAnimating) {
- return; // Выходим, если анимация уже идёт или нет заданий в очереди
- }
- isAnimating = true; // Устанавливаем флаг анимации
- const data = updateQueue.shift(); // Извлекаем первый элемент очереди
- const speedUpElement = document.querySelector('.speed-up');
- if (!speedUpElement) {
- console.error("Элемент для обновления сообщения о ускорении не найден.");
- isAnimating = false;
- processNextUpdate(); // Пытаемся обработать следующий элемент в очереди
- return;
- }
- // Удаляем предыдущие классы анимации и добавляем новый
- speedUpElement.classList.remove('blink-2', 'shake-top');
- const animationClass = data.color.r === 1 && data.color.g === 0 && data.color.b === 0 ? 'shake-top' : 'blink-2';
- speedUpElement.classList.add(animationClass);
- // Изменяем цвет в середине анимации blink-2
- setTimeout(() => {
- speedUpElement.style.background = `rgba(${data.color.r * 255}, ${data.color.g * 255}, ${data.color.b * 255}, 0.2)`;
- speedUpElement.style.border = `0.49px solid rgba(${data.color.r * 255}, ${data.color.g * 255}, ${data.color.b * 255}, 0.68)`;
- speedUpElement.style.color = `rgba(${data.color.r * 255}, ${data.color.g * 255}, ${data.color.b * 255}, 1)`;
- }, 200); // 200ms для достижения 50% анимации blink-2
- // Обновляем текст в середине анимации
- setTimeout(() => {
- speedUpElement.textContent = data.message;
- }, 200); // Обновляем текст одновременно с изменением цвета
- // Завершаем анимацию и удаляем классы анимации с небольшой задержкой после полного цикла
- setTimeout(() => {
- speedUpElement.classList.remove('blink-2', 'shake-top');
- isAnimating = false;
- processNextUpdate(); // Обрабатываем следующее обновление в очереди
- }, 400); // Полная длительность анимации blink-2
- }
- function updateSpeedUpMessage(data) {
- updateQueue.push(data); // Добавляем данные в очередь
- if (!isAnimating) {
- processNextUpdate(); // Запускаем обработку очереди, если анимация не активна
- }
- }
- function updatePlace(newPlace) {
- console.log("updatePlace called with:", newPlace); // Отладочная информация
- // Поиск элемента, содержащего текст места
- const placeElement = document.querySelector('.score-badge-text');
- if (!placeElement) {
- console.error("Элемент для обновления места не найден.");
- return;
- }
- // Обновление текста элемента
- placeElement.textContent = `${newPlace}`;
- }
- function updateBadgeStyle() {
- const badge = document.querySelector('.score-badge');
- const badgeText = badge.querySelector('.score-badge-text');
- const svg = badge.querySelector('svg');
- // Определение стилей для разных позиций
- const styleConfigs = {
- 1: {
- backgroundColor: 'rgba(221, 3, 85, 1)',
- textColor: 'rgba(8, 6, 10, 1)',
- logoColor: 'rgba(8, 6, 10, 1)',
- fontSize: '16px'
- },
- 2: {
- backgroundColor: 'rgba(238, 237, 238, 0.87)',
- textColor: 'rgba(8, 6, 10, 1)',
- logoColor: 'rgba(8, 6, 10, 1)',
- fontSize: '16px'
- },
- 3: {
- backgroundColor: 'rgba(255, 129, 38, 1)',
- textColor: 'rgba(8, 6, 10, 1)',
- logoColor: 'rgba(8, 6, 10, 1)',
- fontSize: '16px'
- },
- default: {
- backgroundColor: 'rgba(255, 255, 255, 0.17)',
- textColor: 'rgba(238, 237, 238, 0.87)',
- logoColor: 'rgba(238, 237, 238, 0.52)',
- fontSize: ['16px', '14px', '12px'] // Используйте динамическое определение размера шрифта
- }
- };
- const position = parseInt(badgeText.textContent.replace(/[^\d]/g, '')) || 0;
- const config = styleConfigs[position] || styleConfigs.default;
- // Динамическое определение размера шрифта на основе позиции
- const fontSizeIndex = position < 100 ? 0 : position < 1000 ? 1 : 2;
- const finalFontSize = config.fontSize instanceof Array ? config.fontSize[fontSizeIndex] : config.fontSize;
- // Применение стилей
- badge.style.background = config.backgroundColor;
- badgeText.style.color = config.textColor;
- badgeText.style.fontSize = finalFontSize;
- svg.querySelectorAll('path').forEach(path => path.setAttribute('fill', config.logoColor));
- }
- // Функция для управления видимостью предупреждения о скорости
- function speedWarningDisplay(show) {
- const speedWarningElement = document.querySelector('.speed-warning');
- if (!speedWarningElement) return; // Выходим, если элемент не найден
- // Устанавливаем свойство display в зависимости от аргумента show
- speedWarningElement.style.display = show ? 'block' : 'none';
- }
- // Обработчик события, получающий данные от AC
- AC.onReceive('speedblock', arg => {
- // Проверяем значение arg, чтобы определить, показывать или скрывать предупреждение
- const shouldShow = arg === 'show'; // Пример условия, arg должен быть 'show' для отображения
- speedWarningDisplay(shouldShow);
- });
- function setupEventHandlers() {
- AC.onReceive('updateBestScore', newValue => updateValue('bestScore', newValue));
- AC.onReceive('updateScore', newValue => updateValue('currentScore', newValue));
- AC.onReceive('updateCombo', newValue => updateValue('combo', newValue));
- AC.onReceive('updateSpeed', newValue => updateValue('speed', newValue));
- AC.onReceive('updateProximity', newValue => updateValue('proximity', newValue));
- AC.onReceive('messages', updateSpeedUpMessage);
- AC.onReceive('speedblock', arg => speedWarningDisplay(arg === 'show'));
- AC.onReceive('updatePlace', newPlace => {
- updatePlace(newPlace);
- updateBadgeStyle(); // Вызов для обновления стилей после изменения места
- });
- AC.onReceive('webanim', arg => {
- if(arg === "startweb") {
- showContent();
- }
- });
- }
- function showContent() {
- const content = document.querySelector('.content');
- if(content) {
- content.style.visibility = 'visible';
- content.style.opacity = '1';
- }
- }
- document.addEventListener('DOMContentLoaded', setupEventHandlers);
- </script>
- </body>
- </html>
- ]]
- function checkAndLoadVideo()
- local rootFolder = ac.getFolder(ac.FolderID.Root) .. "\\cache\\remote_assets\\nohesivideo"
- local targetFile = rootFolder .. "\\2_90FPS_2.wmv"
- -- Check if the file exists
- if not io.fileExists(targetFile) then
- -- If the file doesn't exist, first download it
- local function onAssetsLoaded(err, folder)
- if err then
- print("Error loading assets: " .. err)
- return
- end
- -- Path to the file to move
- local sourceFile = folder .. "\\2_90FPS_2.wmv"
- -- Create the directory if it doesn't exist
- if not io.dirExists(rootFolder) then
- io.createDir(rootFolder)
- end
- -- Move the file
- if io.move(sourceFile, targetFile) then
- print("File successfully moved to " .. targetFile)
- loadVideoInPlayer(targetFile)
- else
- print("Failed to move the file.")
- end
- end
- -- Start downloading and unpacking the assets
- web.loadRemoteAssets("https://cdn.discordapp.com/attachments/880053607024717854/1209263927045652591/2_90FPS_2.zip?ex=65e649cb&is=65d3d4cb&hm=33f584a885ac8ea30f635cea6c28831a9ea8bf0e17e4799a51597b2bc1b81d00&", onAssetsLoaded)
- else
- -- If the file already exists, load it into MediaPlayer
- loadVideoInPlayer(targetFile)
- end
- end
- ---@param address string Hub address
- function NoHesiClient:initialize(address)
- self.address = address .. "/api/"
- end
- function NoHesiClient:postSettings(Uix, Uiy, Uis, fpsCheck, cpuoCheck, UIToggle, UIClear)
- 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)
- local success, err = pcall(function()
- ac.signBlob(payload, function (data)
- local urlEncodedData = urlEncode(ac.encodeBase64(data))
- 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
- web.post(url, function (err, response)
- ac.debug("err", err)
- end)
- end)
- end)
- if not success then
- ac.debug("Error occurred:", err)
- end
- end
- function NoHesiClient:getSettings(callback)
- local payload = ac.getUserSteamID()
- local success, err = pcall(function()
- ac.signBlob(payload, function (data)
- local urlEncodedData = urlEncode(ac.encodeBase64(data))
- 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
- web.get(url, function (err, response)
- ac.debug("err", err)
- callback(JSON.parse(response.body))
- end)
- end)
- end)
- if not success then
- ac.debug("Error occurred:", err)
- end
- end
- function NoHesiClient:postScore(score, combo, speed, time, distance, callback)
- 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())
- local success, err = pcall(function()
- ac.signBlob(payload, function (data)
- local urlEncodedData = urlEncode(ac.encodeBase64(data))
- 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
- web.post(url, function (err, response)
- ac.debug("err", err)
- callback(JSON.parse(response.body))
- end)
- end)
- end)
- if not success then
- ac.debug("Error occurred:", err)
- end
- end
- function NoHesiClient:getScore(callback)
- local payload = ac.getUserSteamID()
- local success, err = pcall(function()
- ac.signBlob(payload, function (data)
- local urlEncodedData = urlEncode(ac.encodeBase64(data))
- 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
- web.get(url, function (err, response)
- ac.debug("err", err)
- callback(JSON.parse(response.body))
- end)
- end)
- end)
- if not success then
- ac.debug("Error occurred:", err)
- end
- end
- function urlEncode(str)
- if str then
- str = string.gsub(str, "\n", "\r\n")
- str = string.gsub(str, "([^%w ])",
- function(c) return string.format("%%%02X", string.byte(c)) end)
- str = string.gsub(str, " ", "+")
- end
- return str
- end
- function script.prepare(dt)
- ac.log('speed' + ac.getCar(1).speedKmh)
- return ac.getCarState(1).speedKmh > 60
- end
- local nohesiclient = NoHesiClient(config.hubUrl)
- local requiredSpeed = 95
- local sim = ac.getSim()
- local maxcombo = 0
- local averageSpeed = 0
- local startdistance = 0
- local rundistance = 0
- local starttime = 0
- local runtime = 0
- local distancedriven = 0
- local timePassed = 0
- local speedMessageTimer = 0
- local mackMessageTimer = 0
- local totalScore = 0
- local comboMeter = 1
- local speedComboMeter = 1
- local proxiComboMeter = 1
- local comboColor = 0
- local dangerouslySlowTimer = 0
- local carsState = {}
- local wheelsWarningTimeout = 0
- local personalBest = 0
- local ownRank = 0
- local cheatcounter = 0
- local leaderboardByName = {}
- local MackMessages = { 'MAAAACK!!!!', 'MACKSAUCE!!', 'You Hesitated....', 'bRUH', 'No Shot...'}
- --new
- local CloseMessages = { 'IN THAT!!!!!', 'IN THERE.', 'D I V E', 'SKRRT!!!' }
- local NormalMessages = { 'Overtake', 'Smooth Move', 'Easy Breeze' }
- local FPS = sim.fps
- local CPUO = sim.cpuOccupancy
- local Uix, Uiy, Uis, fpsCheck, cpuoCheck, UIToggle, UIClear = nil, nil, nil, nil, nil, nil, nil
- local logoCheck = true
- --new
- local previousBestScore = nil
- local previousTotalScore = nil
- local previousComboMeter = nil
- local previousSpeedComboMeter = nil
- local previousProximity = nil
- local previousRank = nil
- local previousMessages = {} -- Для хранения предыдущих сообщений
- local previousSpeedWarningShown = nil -- Добавляем переменную состояния для предупреждения о скорости
- function OptionUI()
- local uiState = ac.getUI()
- ui.text('Settings list:')
- ui.childWindow('##checkyo', vec2(400, 450), ui.WindowFlags.MenuBar,function ()
- if ui.checkbox("Show FPS", fpsCheck) then
- fpsCheck = not fpsCheck
- end
- if ui.checkbox("Show CPU Occupancy", cpuoCheck) then
- cpuoCheck = not cpuoCheck
- end
- if ui.checkbox("Points UI", UIToggle) then
- UIToggle = not UIToggle
- end
- if ui.checkbox("Clear / Dark UI", UIClear) then
- UIClear = not UIClear
- end
- ui.text('Points position:')
- Uix = ui.slider('##slideyyo', Uix, -500, uiState.windowSize.x, 'x-axis: %0.0f')
- Uiy = ui.slider('##slidexyo', Uiy, -500, uiState.windowSize.y, 'y-axis: %0.0f')
- Uis = ui.slider('##slidesyo', Uis, 0, 1, 'Scale: %0.2f')
- if ui.button("☁️ Save Settings", vec2(120, 20)) then
- nohesiclient:postSettings(Uix, Uiy, Uis, fpsCheck, cpuoCheck, UIToggle, UIClear)
- end
- ui.sameLine(160, 0)
- if ui.button("☁️ Load Settings", vec2(120, 20)) then
- nohesiclient:getSettings(function (settings)
- ac.log(settings)
- if settings[1] == nil then
- Uix = uiState.windowSize.y * 0.005
- Uiy = uiState.windowSize.x * 0.0025
- Uis = uiState.windowSize.y * 0.0005
- fpsCheck = false
- cpuoCheck = false
- UIToggle = true
- UIClear = true
- else
- Uix = settings[1].uix
- Uiy = settings[1].uiy
- Uis = settings[1].uis
- fpsCheck = settings[1].fpscheck
- cpuoCheck = settings[1].cpuocheck
- UIToggle = settings[1].uitoggle
- UIClear = settings[1].uiclear
- end
- end)
- end
- if ui.button("Reset", vec2(50, 20)) then
- Uix = uiState.windowSize.y * 0.005
- Uiy = uiState.windowSize.x * 0.0025
- Uis = uiState.windowSize.y * 0.0005
- fpsCheck = false
- cpuoCheck = false
- UIToggle = true
- UIClear = true
- end
- end)
- end
- function OptionUIClosed()
- end
- nohesiclient:getSettings(function (settings)
- local uiState = ac.getUI()
- if settings[1] == nil then
- Uix = uiState.windowSize.y * 0.005
- Uiy = uiState.windowSize.x * 0.0025
- Uis = uiState.windowSize.y * 0.0005
- fpsCheck = false
- cpuoCheck = false
- UIToggle = true
- UIClear = true
- else
- Uix = settings[1].uix
- Uiy = settings[1].uiy
- Uis = settings[1].uis
- fpsCheck = settings[1].fpscheck
- cpuoCheck = settings[1].cpuocheck
- UIToggle = settings[1].uitoggle
- UIClear = settings[1].uiclear
- end
- end)
- nohesiclient:getScore(function (score)
- personalBest = score.score
- ownRank = score.rank
- end)
- local function updateTopScores()
- local newScores = {}
- for name, score in pairs(leaderboardByName) do
- table.insert(newScores, { Name = name, Score = score})
- end
- table.sort(newScores, function (a, b) return a.Score > b.Score end)
- topScores = newScores
- end
- local personalBestEvent = ac.OnlineEvent({
- ac.StructItem.key("overtakePb"),
- score = ac.StructItem.double()
- }, function (sender, message)
- leaderboardByName[ac.getDriverName(sender.index)] = message.score
- updateTopScores()
- end)
- local function broadcastPersonalBest()
- if totalScore > personalBest then
- personalBestEvent({ score = totalScore })
- end
- end
- local function savePersonalBest()
- if totalScore > personalBest then
- personalBest = totalScore
- nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score)
- personalBest = score.score
- ownRank = score.rank
- end)
- end
- end
- --updateLeaderboard()
- --setInterval(updateLeaderboard, 60)
- setInterval(broadcastPersonalBest, 1)
- if runtime > 0 then
- setInterval(savePersonalBest, 5)
- end
- local leaderboardSize = vec2(250, 80)
- local uiState = ac.getUI()
- function script.update(dt)
- if timePassed == 0 then
- addMessage('Made by No Hesi', 0)
- end
- local player = ac.getCarState(1)
- if player.engineLifeLeft < 1 then
- ac.console('Overtake score: ' .. totalScore)
- return
- end
- distancedriven = player.distanceDrivenSessionKm
- timePassed = timePassed + dt
- speedMessageTimer = speedMessageTimer + dt
- mackMessageTimer = mackMessageTimer + dt
- local comboFadingRate = 0.5 * math.lerp(1, 0.1, math.lerpInvSat(player.speedKmh, 80, 200)) + player.wheelsOutside
- comboMeter = math.max(1, comboMeter - dt * comboFadingRate)
- speedComboMeter = math.floor(math.lerp(1, 3, math.lerpInvSat(player.speedKmh, 150, 300)) * 10 + 0.5) / 10
- local sim = ac.getSim()
- while sim.carsCount > #carsState do
- carsState[#carsState + 1] = {}
- end
- if wheelsWarningTimeout > 0 then
- wheelsWarningTimeout = wheelsWarningTimeout - dt
- elseif player.wheelsOutside > 0 then
- if wheelsWarningTimeout == 0 then
- end
- addMessage('Car is Out Of Zone', -1)
- wheelsWarningTimeout = 60
- end
- if player.speedKmh < requiredSpeed then
- proxiComboMeter = 1
- if dangerouslySlowTimer > 3 then
- ac.console('Overtake score: ' .. totalScore)
- comboMeter = 1
- totalScore = 0
- else
- if dangerouslySlowTimer < 3 then
- if speedMessageTimer > 5 then
- --addMessage('a'..dangerouslySlowTimer, -1)
- speedMessageTimer = 0
- end
- end
- if dangerouslySlowTimer == 0 then
- addMessage('Speed up!', -1)
- end
- end
- dangerouslySlowTimer = dangerouslySlowTimer + dt
- comboMeter = 1
- if totalScore > personalBest and dangerouslySlowTimer > 3 then
- personalBest = totalScore
- -- ac.sendChatMessage('just scored a ' .. personalBest)
- nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score)
- personalBest = score.score
- ownRank = score.rank
- end)
- end
- return
- else
- dangerouslySlowTimer = 0
- end
- if player.collidedWith == 0 then
- if totalScore > personalBest then
- personalBest = totalScore
- -- ac.sendChatMessage('just scored a ' .. personalBest)
- nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score)
- personalBest = score.score
- ownRank = score.rank
- end)
- end
- cheatcounter = 0
- comboMeter = 1
- totalScore = 0
- if mackMessageTimer > 1 then
- addMessage(MackMessages[math.random(1, #MackMessages)], -1)
- mackMessageTimer = 0
- end
- end
- if totalScore > 0 then
- rundistance = distancedriven - startdistance
- runtime = timePassed - starttime
- averageSpeed = (rundistance / runtime)*3600
- else
- maxcombo = 0
- averageSpeed = 0
- runtime = 0
- starttime = 0
- rundistance = 0
- startdistance = 0
- proxiComboMeter = 1
- end
- local function get_closest_car_index()
- local k = 0
- local min = math.huge
- for i = 2, ac.getSim().carsCount do
- local car = ac.getCarState(i)
- local closestcarname = ac.getCarID(i-1)
- if not string.find(closestcarname, "traffic") then
- local dist = car.position:distance(player.position)
- if dist < min then
- k = i
- min = dist
- end
- end
- end
- return k
- end
- local closestcarindex = get_closest_car_index()
- local closestcar = ac.getCarState(closestcarindex)
- local closestcarname
- if closestcar then
- closestcarname = ac.getCarID(closestcarindex-1)
- if closestcar.position:closerToThan(player.position, 20) and math.dot(car.look, player.look) > 0.2 then
- local carToPlayer = player.position - closestcar.position
- local dotProduct = closestcar.look:dot(carToPlayer)
- local dotProduct2 = closestcar.side:dot(carToPlayer)
- local t = math.lerpInvSat(math.abs(dotProduct), 19, 6)
- local penaltyFactor = 1 + (t^2 * 4)
- local p = math.lerpInvSat(math.abs(dotProduct)+(math.abs(dotProduct2)*penaltyFactor), 19, 6)
- proxiComboMeter = math.round(math.lerp(1, 4, (-(p * p) + 2 * p)), 1)
- else
- proxiComboMeter = 1
- end
- else
- proxiComboMeter = 1 -- Assuming default value in case of error
- end
- for i = 2, ac.getSim().carsCount do
- local car = ac.getCarState(i)
- local state = carsState[i]
- -- ac.debug(car.collidedWith .. " COLLISION")
- if car.position:closerToThan(player.position, 4.5) then
- local drivingAlong = math.dot(car.look, player.look) > 0.2
- if not drivingAlong then
- state.drivingAlong = false
- if not state.nearMiss and car.position:closerToThan(player.position, 3) then
- state.nearMiss = true
- end
- end
- if not state.overtaken and not state.collided and state.drivingAlong then
- local posDir = (car.position - player.position):normalize()
- local posDot = math.dot(posDir, car.look)
- state.maxPosDot = math.max(state.maxPosDot, posDot)
- if posDot < -0.5 and state.maxPosDot > 0.5 then
- totalScore = totalScore + math.ceil(10 * comboMeter)
- if startdistance == 0 and starttime == 0 then
- startdistance = distancedriven
- starttime = timePassed
- end
- if cheatcounter > 10 then
- setInterval(function() ac.sendChatMessage('debug message from the anti-cheat') end, 1, "cheat")
- physics.engageGear(0, 0)
- physics.setCarNoInput(true)
- physics.teleportCarTo(0, 'PIT')
- totalScore = 0
- nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score)
- personalBest = score.score
- ownRank = score.rank
- end)
- setTimeout(function () ac.shutdownAssettoCorsa() end, 5)
- state.overtaken = true
- elseif car.position:closerToThan(player.position, 1.9) then
- cheatcounter = cheatcounter + 1
- state.overtaken = true
- elseif car.position:closerToThan(player.position, 3) then
- comboMeter = comboMeter + (2 + speedComboMeter + proxiComboMeter - 1)
- comboColor = comboColor + math.random(1, 90)
- addMessage(CloseMessages[math.random(#CloseMessages)].. ' ' .. (2 + speedComboMeter + proxiComboMeter - 1) .. 'x', 2)
- state.overtaken = true
- else
- comboMeter = comboMeter + (speedComboMeter + proxiComboMeter - 1)
- comboColor = comboColor + 90
- addMessage(NormalMessages[math.random(#NormalMessages)] .. ' ' .. (speedComboMeter + proxiComboMeter - 1) .. 'x', comboMeter > 50 and 1 or 0)
- state.overtaken = true
- end
- if comboMeter > maxcombo then
- maxcombo = comboMeter
- end
- end
- end
- else
- state.maxPosDot = -1
- state.overtaken = false
- state.collided = false
- state.drivingAlong = true
- state.nearMiss = false
- end
- end
- end
- local messages = {}
- local glitter = {}
- local glitterCount = 0
- function addMessage(text, mood)
- for i = math.min(#messages + 1, 1), 2, -1 do
- messages[i] = messages[i - 1]
- messages[i].targetPos = i
- end
- messages[1] = { text = text, age = 0, targetPos = 1, currentPos = 1, mood = mood }
- if mood == 1 then
- for i = 1, 60 do
- local dir = vec2(math.random() - 0.5, math.random() - 0.5)
- glitterCount = glitterCount + 1
- glitter[glitterCount] = {
- color = rgbm.new(hsv(math.random() * 360, 1, 1):rgb(), 1),
- pos = vec2(80, 140) + dir * vec2(40, 20),
- velocity = dir:normalize():scale(0.2 + math.random()),
- life = 0.5 + 0.5 * math.random()
- }
- end
- end
- end
- local function updateMessages(dt)
- comboColor = comboColor + dt * 10 * comboMeter
- if comboColor > 360 then comboColor = comboColor - 360 end
- for i = 1, #messages do
- local m = messages[i]
- m.age = m.age + dt
- m.currentPos = math.applyLag(m.currentPos, m.targetPos, 0.8, dt)
- end
- end
- local speedWarning = 0
- local fontpath = ''
- web.loadRemoteAssets('https://hub.nohesi.gg/ui/microgramma.zip', function (err, response)
- fontpath = response
- end)
- function loadVideoInPlayer(videoPath)
- player = ui.MediaPlayer()
- player:setSource(videoPath) -- Установите корректный путь к файлу
- :setAutoPlay(true) -- Автоматическое воспроизведение после загрузки
- :setLooping(true) -- Циклическое воспроизведение
- player:setUpdatePeriod(0.0) -- Установка периода обновления
- end
- checkAndLoadVideo()
- function add_suffix(position)
- local last_two_digits = position % 100
- if last_two_digits >= 11 and last_two_digits <= 13 then
- return "th"
- elseif position % 10 == 1 then
- return "st"
- elseif position % 10 == 2 then
- return "nd"
- elseif position % 10 == 3 then
- return "rd"
- else
- return "th"
- end
- end
- browser:navigate('data:text/html;base64,' .. ac.encodeBase64(htmlContent))
- local counter = 0
- -- Time for the image to fully appear in seconds
- local fadeInTime = 2
- -- Time for the image to fully fade out in seconds
- local fadeOutTime = 2
- -- Flag indicating if the animation is currently active
- local isAnimationActive = false -- Изначально анимация не активна
- -- Flag to check if the animation has been started
- local hasAnimationStarted = false
- -- Current time within the animation cycle
- local currentTime = 0
- -- Function to start the animation
- local function startAnimation()
- if not hasAnimationStarted and not ac.getSim().isInMainMenu then
- isAnimationActive = true
- hasAnimationStarted = true
- end
- end
- -- Animation function
- local function animate(dt)
- -- Start the animation based on the condition
- startAnimation()
- -- Stop the animation if it's not active
- if not isAnimationActive then return end
- -- Update the current animation time
- currentTime = currentTime + dt
- local totalTime = fadeInTime + fadeOutTime
- local alpha = 0
- -- Calculate the alpha value based on the current phase of the animation
- if currentTime <= fadeInTime then
- alpha = currentTime / fadeInTime
- elseif currentTime <= totalTime then
- alpha = 1 - (currentTime - fadeInTime) / fadeOutTime
- browser:sendAsync('webanim', "startweb")
- else
- isAnimationActive = false -- Deactivate the animation after completion
- currentTime = 0 -- Reset currentTime to allow the animation to be started again if needed
- end
- -- Set the alpha channel for color modulation
- local color = rgbm(1, 1, 1, alpha)
- -- Render the image with the animated alpha channel
- display.image({
- image = player,
- pos = vec2(0, 0),
- size = vec2(300, 300),
- color = color,
- uvStart = vec2(0, 0),
- uvEnd = vec2(1, 1)
- })
- end
- -- Schedule the animation to stop after the total duration
- setTimeout(function()
- isAnimationActive = false -- Deactivate the animation after completion
- end, fadeInTime + fadeOutTime)
- local function sendDataIfChanged()
- local speedWarningShown = ac.getCar(0).speedKmh <= 90 -- true, если скорость меньше или равна 90 км/ч
- local roundedComboMeter = math.floor(comboMeter * 10 + 0.5) / 10
- if personalBest ~= previousBestScore then
- browser:sendAsync('updateBestScore', personalBest)
- previousBestScore = personalBest
- end
- if totalScore ~= previousTotalScore then
- browser:sendAsync('updateScore', totalScore)
- previousTotalScore = totalScore
- end
- -- Сравниваем округленные значения до десятых
- if roundedComboMeter ~= previousComboMeter then
- browser:sendAsync('updateCombo', roundedComboMeter)
- previousComboMeter = roundedComboMeter
- end
- if speedComboMeter ~= previousSpeedComboMeter then
- browser:sendAsync('updateSpeed', speedComboMeter)
- previousSpeedComboMeter = speedComboMeter
- end
- if proxiComboMeter ~= previousProximity then
- browser:sendAsync('updateProximity', proxiComboMeter)
- previousProximity = proxiComboMeter
- end
- if ownRank ~= previousRank then
- browser:sendAsync('updatePlace', "#"..ownRank)
- previousRank = ownRank
- end
- -- Проверка и отправка состояния предупреждения о скорости
- if speedWarningShown ~= previousSpeedWarningShown then
- browser:sendAsync('speedblock', speedWarningShown and "show" or "notshow")
- previousSpeedWarningShown = speedWarningShown
- end
- for i, m in ipairs(messages) do
- local prevM = previousMessages[i] or {}
- -- Преобразование результатов функции rgbm в соответствующий объект
- local colorComponents = m.mood == 1 and {r=1, g=1, b=1, m=1}
- or m.mood == -1 and {r=1, g=0, b=0, m=1}
- or m.mood == 2 and {r=0.714, g=0.02, b=0.976, m=1}
- or {r=1, g=1, b=1, m=1} -- Упрощаем, всегда используем полную непрозрачность
- -- Проверяем, изменилось ли сообщение или его настроение
- if m.text ~= prevM.text or m.mood ~= prevM.mood then
- browser:sendAsync('messages', {message = m.text, color = colorComponents})
- end
- -- Обновляем предыдущее состояние сообщения
- previousMessages[i] = {text = m.text, mood = m.mood}
- end
- end
- function script.drawUI(dt)
- animate(ac.getScriptDeltaT())
- updateMessages(uiState.dt)
- ui.beginScale()
- ui.beginTransparentWindow('overtakeScore', vec2(Uix, Uiy), vec2(560, 300))
- -- Create an invisible item for spacing
- ui.dummy(vec2(520, 420))
- -- Get coordinates for rendering and draw the browser
- local p1, p2 = ui.itemRect()
- browser:draw(p1, p2, true)
- counter = counter + ac.getScriptDeltaT()
- if counter >= 0.1 then
- sendDataIfChanged()
- counter = 0
- end
- -- Handle mouse input for the browser
- -- nextupdate
- -- local function getMouseButtons()
- -- mouseButtons[1] = uis.isMouseLeftKeyDown
- -- mouseButtons[2] = uis.isMouseRightKeyDown
- -- mouseButtons[3] = uis.isMouseMiddleKeyDown
- -- return mouseButtons
- --end
- -- browser:mouseInput(ui.mouseLocalPos():sub(p1):div(lastSize), getMouseButtons(), uis.mouseWheel, false)
- ui.endTransparentWindow()
- if logoCheck then
- ui.drawImage('https://i.imgur.com/rHHbNc0.png', vec2(100 , uiState.windowSize.y), vec2(0, uiState.windowSize.y - 100))
- end
- ui.beginOutline()
- if fpsCheck then
- ui.setCursorY(0)
- ui.pushFont(ui.Font.Main)
- setInterval(function() FPS = sim.fps end, 0.1, "FPSCounter")
- ui.text(math.floor(FPS) .. " FPS")
- ui.endOutline(rgbm(0, 0, 0, 0.3))
- ui.sameLine(100, 0)
- end
- if cpuoCheck then
- ui.setCursorY(0)
- ui.pushFont(ui.Font.Main)
- setInterval(function() CPUO = sim.cpuOccupancy end, 0.1, "CPUOCounter")
- ui.text(math.floor(CPUO) .. "% CPU Occupancy")
- if fpsCheck then
- ui.sameLine(300, 0)
- else
- ui.sameLine(200, 0)
- end
- end
- ui.endOutline(rgbm(0, 0, 0, 0.3))
- end
- ui.registerOnlineExtra(ui.Icons.Burn, 'No Hesi UI Settings', nil, OptionUI, OptionUIClosed, ui.OnlineExtraFlags.Tool)
Add Comment
Please, Sign In to add comment