SHOW:
|
|
- or go back to the newest paste.
1 | local config = ac.configValues({ | |
2 | hubUrl = "" | |
3 | }) | |
4 | - | |
4 | + | |
5 | ---@class NoHesiClient | |
6 | local NoHesiClient = class("NoHesiClient") | |
7 | - | |
7 | + | |
8 | - | |
8 | + | |
9 | local closestcar = 1 | |
10 | - | |
10 | + | |
11 | - | |
11 | + | |
12 | -- Import the WebBrowser library | |
13 | local WebBrowser = require('shared/web/browser') | |
14 | ui.setAsynchronousImagesLoading(true) | |
15 | - | |
15 | + | |
16 | -- Configure the WebBrowser | |
17 | WebBrowser.configure({ | |
18 | targetFPS = 240, | |
19 | }) | |
20 | - | |
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 | - | |
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 | - | |
33 | + | |
34 | - | |
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 | - | |
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 | - | |
50 | + | |
51 | -- Path to the file to move | |
52 | local sourceFile = folder .. "\\2_90FPS_2.wmv" | |
53 | print("Source file path: " .. sourceFile) | |
54 | - | |
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 | - | |
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 | - | |
80 | + | |
81 | - | |
81 | + | |
82 | - | |
82 | + | |
83 | - | |
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 | - | |
92 | + | |
93 | <style> | |
94 | - | |
94 | + | |
95 | - | |
95 | + | |
96 | - | |
96 | + | |
97 | .shake-top { | |
98 | animation: shake-top 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; | |
99 | } | |
100 | - | |
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 | - | |
122 | + | |
123 | - | |
123 | + | |
124 | - | |
124 | + | |
125 | - | |
125 | + | |
126 | - | |
126 | + | |
127 | - | |
127 | + | |
128 | .blink-2 { | |
129 | animation: blink-2 0.4s both; | |
130 | } | |
131 | - | |
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 | - | |
143 | + | |
144 | - | |
144 | + | |
145 | - | |
145 | + | |
146 | - | |
146 | + | |
147 | - | |
147 | + | |
148 | - | |
148 | + | |
149 | :root { | |
150 | --font-family: 'Roboto Flex', sans-serif; | |
151 | --second-family: 'Roboto', sans-serif; | |
152 | --main-color: #B130FF; /* Purple color */ | |
153 | } | |
154 | - | |
154 | + | |
155 | - | |
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 | - | |
163 | + | |
164 | } | |
165 | - | |
165 | + | |
166 | .dashboard { | |
167 | justify-content: flex-start; /* Это изменение выровняет все содержимое dashboard, включая logo, слева */ | |
168 | } | |
169 | - | |
169 | + | |
170 | .logo { | |
171 | justify-content: flex-start; | |
172 | width: 100%; | |
173 | } | |
174 | - | |
174 | + | |
175 | - | |
175 | + | |
176 | - | |
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 | - | |
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 | - | |
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 | - | |
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 | - | |
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 | - | |
226 | + | |
227 | - | |
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 | - | |
247 | + | |
248 | - | |
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 | - | |
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 | - | |
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 | - | |
272 | + | |
273 | - | |
273 | + | |
274 | .item { | |
275 | display: flex; | |
276 | flex-direction: column; /* Устанавливает направление элементов в колонку */ | |
277 | align-items: center; /* Центрирует элементы по горизонтали */ | |
278 | } | |
279 | - | |
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 | - | |
291 | + | |
292 | } | |
293 | - | |
293 | + | |
294 | - | |
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 | - | |
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 | - | |
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 | - | |
329 | + | |
330 | } | |
331 | - | |
331 | + | |
332 | - | |
332 | + | |
333 | - | |
333 | + | |
334 | .content { | |
335 | visibility: hidden; /* Содержимое скрыто, но прогружается */ | |
336 | opacity: 0; | |
337 | transition: visibility 0s, opacity 1s linear; /* Плавное появление */ | |
338 | transition-delay: 2s; /* Задержка перед началом анимации появления */ | |
339 | } | |
340 | - | |
340 | + | |
341 | .logo-text { | |
342 | position: absolute; | |
343 | - | |
343 | + | |
344 | opacity: 0; /* Изначально текст невидим */ | |
345 | margin-top: 30px | |
346 | - | |
346 | + | |
347 | } | |
348 | - | |
348 | + | |
349 | - | |
349 | + | |
350 | - | |
350 | + | |
351 | - | |
351 | + | |
352 | </style> | |
353 | </head> | |
354 | <body> | |
355 | - | |
355 | + | |
356 | - | |
356 | + | |
357 | - | |
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'> |
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' /> |
365 | + | <path d='M13.3 5.2l.1-.1 1.7.5h.8q1.3-.4 2.4-.4v.5q-.5.6-.5.9l.2.9-.2.2v.1l.3 1v1.4q-.5.7-.5 1.2v.2l.7 1.7-.7 1.6.6 1v.1l-.2 1.4v2.3l.2.4v.6l-.1.7.1.2v.1l-.2 1 .2.9-2.9.4-1.3-.2-.5.1-.6-.1-.8.2-.4-.1-.4.1q-.2-.3-.3-1.2 1.1 0 1.1-2v-.2l-.2-1.1.2-1.9V16l-.5-1.5v-.4q.2-.3.6-3.1l.2-.4-.2-.7.1-.9q-.3-2.4-1-2.4-.4 0-.6-.5 0-.6 2.6-.9zm6.1.1l1.1-.1 1.8 1.2q1.4 0 3 2.1.6 2.4 1.4 2.4.1 1.3.3 1.6v1l-.1 1 .1.5-.2.5.2.5q-1 4.7-2 4.7l.1.6q-1 .2-2.4 1.9-2.3.3-2.5.7-1 0-1-1.5l1.5-.8v-.3h.1l-.1-3.7v-1.9l.1-.9-.1-.1-.2-.4h-.1l.3-.7-.2-.8.2-.1h.1l-.1-1q.2-.3.3-1.8l-.3-.5.1-.5q-.2 0-.6-1l.1-.4-.1-.7h.1l-.1-.4h-.8zm10 20.6v1.6l-.4.1-.3-.2h-.4l-.4.1v-.1h-.5l-.6.1q-.2-.1-.6-.1l-.5.1q-.6 0-.9-.2l-.5.1h-.6l-.3-.1-.4.3-.8-.2-.5.1-.2-.1h-.2l-.1-.1q-.8 0-1 .3h-.5q0-.1-.7-.2l-.2.1-.6-.2-.5.2h-.4l-.2.1q-.8-.1-.8-.3l-1.1.2-.9-.1-.9.2-.7-.2h-1.1q-.1.1-.7.2l-.3-.1h-.1v-1.7l1.2.2q.3 0 .5-.2.4 0 .7.2l.8-.2 1.2.1h.1l.2.1.4-.1h.3l.3.1.9-.1.7.1h1l.7-.1h.2l1.9-.1H22.2q.2.2.4.3l.6-.2h.4q.3-.1.6-.1h.2l.3.1.4-.1.7.1.6-.2q1.1.2 2 .2H29.4zm0-23.9v1.5l-.7-.1-.9.2-.7-.1H25.9l-.5.2-.4-.1h-.1l-.4-.1-.1.1-.7-.2-.2.1-.6-.1-.6.1h-.2l-.4-.1v.1l-.5-.1-.2.1-.2-.1h-.6l-.4.2h-.1v-.1l-.9.2-.5-.2h-1l-.1-.1-.1.1h-1.2l-.2-.1-.2.2-.5-.2-.2.1-.1-.1-.3.2-.6-.1h-.4l-.7-.2-1.5.2h-.7V1.9q0 .1.3.1h.1v-.1l.3.2h.1q.1-.1.5-.1l.2-.3 1.5.4h.1l.6-.3h.6l.1.2 1.2-.1q.3.1.7.1.2 0 .5-.2l.3.2h.3l.6-.1.6.2q.7-.1.8-.3l.2.1q.5 0 .8-.3.3 0 .5.2.9.2.9.3l.4-.1v.1h.1l.3-.1h.6l1.6-.3 1.3.2.8-.1v.1l.2-.1h.7l.7.2h.4zm1.2 3.1l1.4.5h.8l2 .1v.1q0 1.5-1.3 1.5l-1.6 1.3q0 1.1-1.5 2l-.2.6q-.9-.2-.9-.4l.3-.5-.1-.8.2-.7-.1-.2v-.1l.4-.8v-.2q-.5-.7-.5-1.2v-.6q.3-.6 1.1-.6zm5.1 1.2l1.3-.8q.8.3 1.7.3l1.5-.3q2.1.1 2.1.5-.2 1.4-.6 1.4l.1.9q-1 2.7-1.7 3.3l-.4 1.6v.8q-1.5 1.1-1.5 2.4-1.2 1.8-1.9 4.6-.4 0-1 2.2v.6l-.6.1-3.4-.5h-1.1l-.5.1q-.6 0-.6-1.2l.2-.6h-.1q0-.3 1.2-1.2l1-2.3-.1-.7 2.7-5.3q0-1.3 1.3-3.3l-.1-.1v-.2q.2-.2.5-2.3zM41.5 18h.3l.5.4-.3 1.8.6 3.2-.2.2-1-.1-.5.4-1.1-.1-1.1.2q-2.7-.2-2.7-.5.4-1.4 1.2-1.4l1.8-.7q.3 0 .3-.6 1.1 0 1.5-2.3.1-.5.7-.5zm3-16.3v1.5l-.5.1-.8-.2-.4.1-.6-.1-.6.2-.2-.1-1.9.2-.1-.1-.4.1-.2-.1q-.1-.2-.4-.2-.5.1-.5.2l-.5-.1q-.2.2-.5.2h-.2l-.3-.1h-.5l-.6-.1-.6.2q-1.1-.2-2.1-.2l-.1.1v-.1l-.5.1-.3-.1q-.2 0-.2.2-.3 0-1.3-.3l-.8.2h-.5V1.7q0 .1.1.1.5-.1 1.1-.1h1.1l.8-.1.4.1H32.7l.4-.1.1.1h.5l.5-.1q.3.1.6.1l.5-.1q.6 0 1 .2l.4-.1h.7l.3.1.3-.2.9.1.4-.1.2.1H39.8l.1.1q.7 0 1-.3l.2.1.3-.1q0 .1.7.2l.1-.1.7.2.4-.2h.7q.3 0 .5.1zm0 25.8h-.7q-.8 0-.8-.2l-1.1.2-1-.1-.8.1-.7-.1h-1.1q-.1.1-.7.1h-.3l-.3.1-.2-.1-.3.1q0-.2-.7-.3l-.9.2H33l-.1-.1h-.2l-.3.1-.2-.1q0 .1-1.1.1-.1-.1-.6-.1 0 .1-.5.1 0-.1-.5-.2h-.6V26h.3l.7-.2.7.1h.8l.5.1 1.6-.2.1.1.2-.1.2.1.1-.1.4.1.6-.1.8.1.8-.2 1.7.2q.3 0 .5-.1.4 0 .7.1l.8-.1 1.2.1v-.1h.1l.2.1.4-.1h.3l.3.1.9-.1.7.2zm5.1-22.4q1.2.3 1.2.5V6q0 .9-1.5 1.3l-.5 1.4q.7 1.8 1.5 1.8.1.3 2.5 1 1.8 1.6 2.9 1.6 0 .2 1.2 1.2v.9q.3 0 .3.2l-.1.5q.5.6.5 2.1-.3 0-.5 2.2-1.2 1.3-2.3 3-2.7.8-3.7.8H51q-.2 0-.4-.7.2-1.2 1.1-1.2 1.2 0 1.3-1.1v-1.1q-.5-2.2-3-2.2-.1-.4-2-1 0-.3-1.9-1.1-.6-1.6-.9-1.6v-.6l-1-1.3.6-1.2-.4-1.2.4-.2v-.6q1.6-2.5 2.6-2.9l.1.1h.2q.3 0 1.9-1zm6.9 0h.1q.1.9.4.9l-.1.6.1.7-.4 1.3v.1l.5 1.3v.4q0 .6-.7 1.2h-.6v-1.2Q54.4 8.1 54 7.9q-2-1-2.3-1.3v-.3q0-.9.5-.9 2.3.3 2.7.7h.5q0-.8 1.1-1zM44.9 16.8h.4q.4.2.9 1.4v.1l-.1.6q.8.6 1.3 2.5v.1h.6l1.9 1.4v.6q0 .4-1.3.4-.1-.3-.8-.6l-.1.1h-.2q-.7-.3-.7-.5-.8 0-1.4 1H45l-.3-.6.1-.6-.3-.3v-.8l-.2-.7.2-1v-.8l-.1-.5q.2-1.8.5-1.8zM59.1 1.7v1.6h-1.5l-.6.1-.8-.1-.8.2-1.7-.3q-.3 0-.5.2-.4 0-.7-.2l-.8.2-1.2-.1h-.1l-.2-.1-.4.1h-.3l-.3-.1-.9.1-.7-.1h-1l-.7.1h-.2l-1.7.1V1.8l.1-.1.1.1q.8 0 1-.2h.5q0 .1.7.1h.2l.6.1.5-.1h.4l.2-.1q.8.1.8.2l1.1-.1h1l.8-.1.7.2h1.1q.1-.1.7-.2l.3.1.3-.1h.2l.3-.1q0 .3.7.3l.9-.2h.4l.5.1h.1v-.1l.6.1h.3zm0 24v1.6q-.8 0-1.7.2l-.6-.2-.7.1-.4-.1-.3.1h-.2q-.3 0-.5-.1h-.5l-.5-.2q-.3.1-.5.3h-.6l-1.9-.1h-.2l-.6-.1h-1l-.8.1-.9-.1-.3.1h-.3l-.4-.1-.1.1H46l-1.3.1-.7-.2v-1.5l.4-.1.9.1h.9l1.1.2q0-.2.8-.3l.2.1h.5l.4.2.7-.2h.1q.7 0 .7-.1h.5q.3.3 1 .3l.1-.2.2.1.3-.1.4.1.8-.2.4.3.3-.2.1.1.5-.1.5.2q.3-.2 1-.2l.4.1q.4 0 .6-.1h1.1zM60 5.1l1 .1.9-.1q1.2 0 1.3 1.6l-1.2.7v.3q0 1.1-.7 1.8 0 2.5-.9 2.5H60q-.4 0-.7-1.7l.1-.2V10l-.1-.7.1-1.7-.1-.2v-.1l.1-.4q-.3 0-.5-1.4 0-.4 1.1-.4zm11.2-1.7l-.1-.1q-1.1 0-1.1-.1h-.2l-.2-.1q0 .2-1.5.3l-.9-.2h-1.3l-.6.1-.6-.1h-.5l-.5-.2-.8.2-.5-.1-.7.2-.7-.1h-.8l-.5-.1-1.1.1V1.7l.1.1h.2l.3-.2.2.2q0-.2 1.1-.2.1.1.6.2 0-.2.5-.2 0 .1.5.2h.6l.4-.2 1.1.3.4-.1h.1l.3-.1.9.2.5-.2.1.1.8-.2.7.3 1.1-.3 1 .1.4-.1q0 .1.5.2l.1-.2q.6 0 1.1.2.6 0 .8-.3h.2l.9.2h.2l.6-.2q1.1.2 1.3.4h.2q.3 0 .3-.2h.1v.1h.2v1.5h-.3l-.4.1q-.7-.1-.8-.2h-.2l-1.1-.1q-.7 0-.9.2h-.1l-.2-.1-.3.1v-.1h-.2q-.3.1-1.3.2zM71 27.3h-.1l-.4.1q0-.1-.2-.1l-.4.1v-.1q-.5.1-.7.2-.5-.1-.7-.3h-2.1l-.4-.1-.9.2h-.2l-.4-.1h-.3q-.3 0-.9.2l-.2-.1-.7.1h-.2l-.9-.3q-.9.3-1.2.3 0-.2-.2-.2l-.3.1-.5-.1v.1l-.1-.1q-.2 0-.4.1v-1.6l.3.1.2-.1.1.1.4-.2.8.2 1-.1q.7 0 1.2.2.5-.4.7-.4h.2l1 .2h.7l.4-.1.9.1q.2 0 .8-.2.2 0 .7.3h.1l.2-.1v.1h.1q.2-.2.3-.2h.5q.1.1.3.1.8 0 .8-.1l.6.2.3-.1H71.5l.8.2h.2l.9.1.6-.1.3.1 1.6-.3h.3q.1.2.8.2v1.5l-.6-.2-1.1.4-1.5-.2q0 .2-.3.2h-.2q-.3 0-.5-.2l-1.1.2q-.6 0-.7-.3zm.4-21.8l1.1-.3h1.8q.7 1.2.7 2.6-.4.6-.4 1.7l.2.5-.5 2h-.6q-.3 0-.6-1.4l.3-.2q0-2.3-1.2-2.8 0-.8-.8-.8l-.1-.1v-.1l.2-.5zm-3.6-.3l.5.2.6-.2q.7 0 1.3.6l-.1 2.4.1 1.1-.3.6.4 2.2q-.3 0-.4.9l.2.4-.3.7v.1l.1.4-.2 2.6q.3.4.5 2.7l-.1.5.1.4-.3.2v.6q.2.4 1.4.8l.1.6v.2q-.3 0-.6.7h-.4l-.7-.2q-.6.3-1.6.3l-.8-.3v.2l-.1-.2H67l-1 .3q-.6 0-1.5-.6l-1.2.3q0-.3-.5-.3v-.3q.3 0 .3-.6.6 0 .9-1.6l.1-.1v-1.1q0-2.6.5-2.9v-.1q-.2-1.6-.7-1.9v-.1q.4-.4.4-.8V13l-.3-2v-.1q0-.9.8-1.9 0-1.8-.4-3.4h.1l2.3.1q0-.3 1-.5z' fill='#e319d8' stroke='#1428c4' stroke-miterlimit='#10' stroke-width="1.5"/> |
366 | - | <path d='M87.2604 18.2395V9.70853H89.856V18.2395H87.2604Z' fill='#B130FF' /> |
366 | + | |
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' /> |
367 | + | |
368 | - | <path d='M66.2493 18.2395V9.70853H74.8682V11.6596H68.6398V13.1186H74.3116V14.7826H68.6398V16.3704H74.9736V18.2395H66.2493Z' fill='#B130FF' /> |
368 | + | |
369 | - | <path d='M54.2848 18.2395V9.70853H56.8453V12.7612H61.6967V9.70853H64.2572V18.2395H61.6967V14.8998H56.8453V18.2395H54.2848Z' fill='#B130FF' /> |
369 | + | |
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' /> |
370 | + | |
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' /> |
371 | + | |
372 | <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
373 | <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" /> | |
374 | </svg> | |
375 | <span class='score-badge-text'>#1</span> | |
376 | </div> | |
377 | ||
378 | </div> | |
379 | </div> | |
380 | <!-- Новый элемент для SPEED UP --> | |
381 | <div class='speed-up'>SPEED UP!</div> | |
382 | - | |
382 | + | |
383 | <div class='block-model'> | |
384 | <div class='item'> | |
385 | <p class='combo-text'>COMBO</p> | |
386 | <div class='object'> | |
387 | <div class='object-text' style='color: rgba(255, 214, 67, 0.87);'>0x</div> | |
388 | </div> | |
389 | </div> | |
390 | <div class='item'> | |
391 | <p class='combo-text'>SPEED</p> | |
392 | <div class='object'> | |
393 | <div class='object-text' style='color: rgba(106, 255, 103, 0.87);'>0x</div> <!-- Пример другого цвета --> | |
394 | </div> | |
395 | </div> | |
396 | <div class='item'> | |
397 | <p class='combo-text'>Proximity</p> | |
398 | <div class='object'> | |
399 | <div class='object-text' style='color: rgba(238, 237, 238, 0.87);'>0x</div> <!-- И еще один цвет --> | |
400 | </div> | |
401 | ||
402 | ||
403 | </div> | |
404 | ||
405 | </div> | |
406 | - | |
406 | + | |
407 | </div> | |
408 | </div> | |
409 | - | |
409 | + | |
410 | ||
411 | <script> | |
412 | // Функция для плавного обновления числа | |
413 | // Функция для плавного обновления числа с поддержкой условного форматирования текста | |
414 | // Универсальная функция анимации для обновления числовых значений | |
415 | - | |
415 | + | |
416 | const duration = 700; | |
417 | const start = performance.now(); | |
418 | ||
419 | function updateNumber(timestamp) { | |
420 | const elapsedTime = timestamp - start; | |
421 | const progress = Math.min(elapsedTime / duration, 1); | |
422 | const currentNumber = oldValue + (newValue - oldValue) * progress; | |
423 | - | |
423 | + | |
424 | const formattedNumber = currentNumber.toLocaleString('en-US', { minimumFractionDigits: precision, maximumFractionDigits: precision }); | |
425 | const displayValue = isPB ? `PB: ${formattedNumber} ${unit}` : `${formattedNumber}${unit}`; | |
426 | ||
427 | document.querySelector(selector).textContent = displayValue; | |
428 | ||
429 | if (progress < 1) { | |
430 | requestAnimationFrame(updateNumber); | |
431 | - | |
431 | + | |
432 | } | |
433 | - | |
433 | + | |
434 | requestAnimationFrame(updateNumber); | |
435 | } | |
436 | ||
437 | // Обработка обновления различных значений | |
438 | - | |
438 | + | |
439 | const selectorMap = { | |
440 | bestScore: '.score-text', | |
441 | - | |
441 | + | |
442 | combo: '.item:nth-child(1) .object-text', | |
443 | speed: '.item:nth-child(2) .object-text', | |
444 | proximity: '.item:nth-child(3) .object-text' | |
445 | }; | |
446 | ||
447 | const isPB = type === 'bestScore'; | |
448 | const selector = selectorMap[type]; | |
449 | const currentValueText = document.querySelector(selector)?.textContent.replace(/[^\d.-]/g, '') || "0"; | |
450 | const currentValue = parseFloat(currentValueText); | |
451 | - | |
451 | + | |
452 | const unit = ['combo', 'speed', 'proximity'].includes(type) ? "x" : "PTS"; // Удаление пробела для X | |
453 | ||
454 | animateValueUpdate({ selector, oldValue: currentValue, newValue: parseFloat(newValue), isPB, precision, unit }); | |
455 | } | |
456 | ||
457 | ||
458 | - | |
458 | + | |
459 | const updateQueue = []; | |
460 | let isAnimating = false; // Флаг, показывающий, идет ли в данный момент анимация | |
461 | - | |
461 | + | |
462 | - | |
462 | + | |
463 | if (updateQueue.length === 0 || isAnimating) { | |
464 | return; // Выходим, если анимация уже идёт или нет заданий в очереди | |
465 | } | |
466 | - | |
466 | + | |
467 | isAnimating = true; // Устанавливаем флаг анимации | |
468 | const data = updateQueue.shift(); // Извлекаем первый элемент очереди | |
469 | ||
470 | const speedUpElement = document.querySelector('.speed-up'); | |
471 | - | |
471 | + | |
472 | console.error("Элемент для обновления сообщения о ускорении не найден."); | |
473 | isAnimating = false; | |
474 | - | |
474 | + | |
475 | return; | |
476 | } | |
477 | ||
478 | // Удаляем предыдущие классы анимации и добавляем новый | |
479 | speedUpElement.classList.remove('blink-2', 'shake-top'); | |
480 | const animationClass = data.color.r === 1 && data.color.g === 0 && data.color.b === 0 ? 'shake-top' : 'blink-2'; | |
481 | speedUpElement.classList.add(animationClass); | |
482 | - | |
482 | + | |
483 | // Изменяем цвет в середине анимации blink-2 | |
484 | setTimeout(() => { | |
485 | speedUpElement.style.background = `rgba(${data.color.r * 255}, ${data.color.g * 255}, ${data.color.b * 255}, 0.2)`; | |
486 | speedUpElement.style.border = `0.49px solid rgba(${data.color.r * 255}, ${data.color.g * 255}, ${data.color.b * 255}, 0.68)`; | |
487 | - | |
487 | + | |
488 | }, 200); // 200ms для достижения 50% анимации blink-2 | |
489 | ||
490 | // Обновляем текст в середине анимации | |
491 | setTimeout(() => { | |
492 | speedUpElement.textContent = data.message; | |
493 | }, 200); // Обновляем текст одновременно с изменением цвета | |
494 | - | |
494 | + | |
495 | // Завершаем анимацию и удаляем классы анимации с небольшой задержкой после полного цикла | |
496 | setTimeout(() => { | |
497 | speedUpElement.classList.remove('blink-2', 'shake-top'); | |
498 | isAnimating = false; | |
499 | - | |
499 | + | |
500 | }, 400); // Полная длительность анимации blink-2 | |
501 | } | |
502 | ||
503 | function updateSpeedUpMessage(data) { | |
504 | updateQueue.push(data); // Добавляем данные в очередь | |
505 | if (!isAnimating) { | |
506 | processNextUpdate(); // Запускаем обработку очереди, если анимация не активна | |
507 | - | |
507 | + | |
508 | } | |
509 | ||
510 | ||
511 | ||
512 | ||
513 | ||
514 | - | |
514 | + | |
515 | - | |
515 | + | |
516 | - | |
516 | + | |
517 | - | |
517 | + | |
518 | - | |
518 | + | |
519 | - | |
519 | + | |
520 | - | |
520 | + | |
521 | - | |
521 | + | |
522 | - | |
522 | + | |
523 | if (!placeElement) { | |
524 | console.error("Элемент для обновления места не найден."); | |
525 | - | |
525 | + | |
526 | } | |
527 | ||
528 | // Обновление текста элемента | |
529 | placeElement.textContent = `${newPlace}`; | |
530 | } | |
531 | ||
532 | - | |
532 | + | |
533 | const badge = document.querySelector('.score-badge'); | |
534 | const badgeText = badge.querySelector('.score-badge-text'); | |
535 | const svg = badge.querySelector('svg'); | |
536 | - | |
536 | + | |
537 | // Определение стилей для разных позиций | |
538 | const styleConfigs = { | |
539 | 1: { | |
540 | backgroundColor: 'rgba(221, 3, 85, 1)', | |
541 | - | |
541 | + | |
542 | logoColor: 'rgba(8, 6, 10, 1)', | |
543 | fontSize: '16px' | |
544 | }, | |
545 | 2: { | |
546 | backgroundColor: 'rgba(238, 237, 238, 0.87)', | |
547 | textColor: 'rgba(8, 6, 10, 1)', | |
548 | logoColor: 'rgba(8, 6, 10, 1)', | |
549 | fontSize: '16px' | |
550 | }, | |
551 | 3: { | |
552 | backgroundColor: 'rgba(255, 129, 38, 1)', | |
553 | textColor: 'rgba(8, 6, 10, 1)', | |
554 | logoColor: 'rgba(8, 6, 10, 1)', | |
555 | fontSize: '16px' | |
556 | }, | |
557 | default: { | |
558 | backgroundColor: 'rgba(255, 255, 255, 0.17)', | |
559 | textColor: 'rgba(238, 237, 238, 0.87)', | |
560 | logoColor: 'rgba(238, 237, 238, 0.52)', | |
561 | fontSize: ['16px', '14px', '12px'] // Используйте динамическое определение размера шрифта | |
562 | } | |
563 | }; | |
564 | ||
565 | const position = parseInt(badgeText.textContent.replace(/[^\d]/g, '')) || 0; | |
566 | const config = styleConfigs[position] || styleConfigs.default; | |
567 | ||
568 | // Динамическое определение размера шрифта на основе позиции | |
569 | - | |
569 | + | |
570 | const finalFontSize = config.fontSize instanceof Array ? config.fontSize[fontSizeIndex] : config.fontSize; | |
571 | ||
572 | - | |
572 | + | |
573 | badge.style.background = config.backgroundColor; | |
574 | badgeText.style.color = config.textColor; | |
575 | badgeText.style.fontSize = finalFontSize; | |
576 | - | |
576 | + | |
577 | } | |
578 | ||
579 | ||
580 | // Функция для управления видимостью предупреждения о скорости | |
581 | function speedWarningDisplay(show) { | |
582 | const speedWarningElement = document.querySelector('.speed-warning'); | |
583 | - | |
583 | + | |
584 | - | |
584 | + | |
585 | // Устанавливаем свойство display в зависимости от аргумента show | |
586 | speedWarningElement.style.display = show ? 'block' : 'none'; | |
587 | } | |
588 | ||
589 | - | |
589 | + | |
590 | ||
591 | // Обработчик события, получающий данные от AC | |
592 | AC.onReceive('speedblock', arg => { | |
593 | - | |
593 | + | |
594 | - | |
594 | + | |
595 | - | |
595 | + | |
596 | }); | |
597 | ||
598 | ||
599 | ||
600 | ||
601 | function setupEventHandlers() { | |
602 | - | |
602 | + | |
603 | - | |
603 | + | |
604 | - | |
604 | + | |
605 | - | |
605 | + | |
606 | AC.onReceive('updateProximity', newValue => updateValue('proximity', newValue)); | |
607 | AC.onReceive('messages', updateSpeedUpMessage); | |
608 | AC.onReceive('speedblock', arg => speedWarningDisplay(arg === 'show')); | |
609 | AC.onReceive('updatePlace', newPlace => { | |
610 | updatePlace(newPlace); | |
611 | updateBadgeStyle(); // Вызов для обновления стилей после изменения места | |
612 | }); | |
613 | ||
614 | AC.onReceive('webanim', arg => { | |
615 | if(arg === "startweb") { | |
616 | showContent(); | |
617 | } | |
618 | - | |
618 | + | |
619 | } | |
620 | ||
621 | function showContent() { | |
622 | const content = document.querySelector('.content'); | |
623 | if(content) { | |
624 | content.style.visibility = 'visible'; | |
625 | - | |
625 | + | |
626 | } | |
627 | } | |
628 | ||
629 | document.addEventListener('DOMContentLoaded', setupEventHandlers); | |
630 | ||
631 | ||
632 | </script> | |
633 | - | |
633 | + | |
634 | ||
635 | - | |
635 | + | |
636 | - | |
636 | + | |
637 | </body> | |
638 | - | |
638 | + | |
639 | - | |
639 | + | |
640 | - | |
640 | + | |
641 | - | |
641 | + | |
642 | ||
643 | function checkAndLoadVideo() | |
644 | local rootFolder = ac.getFolder(ac.FolderID.Root) .. "\\cache\\remote_assets\\nohesivideo" | |
645 | - | |
645 | + | |
646 | - | |
646 | + | |
647 | - | |
647 | + | |
648 | -- If the file doesn't exist, first download it | |
649 | local function onAssetsLoaded(err, folder) | |
650 | if err then | |
651 | print("Error loading assets: " .. err) | |
652 | return | |
653 | end | |
654 | ||
655 | -- Path to the file to move | |
656 | local sourceFile = folder .. "\\2_90FPS_2.wmv" | |
657 | ||
658 | -- Create the directory if it doesn't exist | |
659 | - | |
659 | + | |
660 | io.createDir(rootFolder) | |
661 | end | |
662 | - | |
662 | + | |
663 | -- Move the file | |
664 | if io.move(sourceFile, targetFile) then | |
665 | print("File successfully moved to " .. targetFile) | |
666 | loadVideoInPlayer(targetFile) | |
667 | - | |
667 | + | |
668 | print("Failed to move the file.") | |
669 | end | |
670 | end | |
671 | -- Start downloading and unpacking the assets | |
672 | web.loadRemoteAssets("https://cdn.discordapp.com/attachments/880053607024717854/1209263927045652591/2_90FPS_2.zip?ex=65e649cb&is=65d3d4cb&hm=33f584a885ac8ea30f635cea6c28831a9ea8bf0e17e4799a51597b2bc1b81d00&", onAssetsLoaded) | |
673 | else | |
674 | -- If the file already exists, load it into MediaPlayer | |
675 | loadVideoInPlayer(targetFile) | |
676 | end | |
677 | end | |
678 | ||
679 | ||
680 | ---@param address string Hub address | |
681 | function NoHesiClient:initialize(address) | |
682 | self.address = address .. "/api/" | |
683 | - | |
683 | + | |
684 | - | |
684 | + | |
685 | function NoHesiClient:postSettings(Uix, Uiy, Uis, fpsCheck, cpuoCheck, UIToggle, UIClear) | |
686 | 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) | |
687 | local success, err = pcall(function() | |
688 | ac.signBlob(payload, function (data) | |
689 | - | |
689 | + | |
690 | 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 | |
691 | web.post(url, function (err, response) | |
692 | ac.debug("err", err) | |
693 | end) | |
694 | end) | |
695 | end) | |
696 | if not success then | |
697 | ac.debug("Error occurred:", err) | |
698 | end | |
699 | end | |
700 | ||
701 | function NoHesiClient:getSettings(callback) | |
702 | local payload = ac.getUserSteamID() | |
703 | local success, err = pcall(function() | |
704 | ac.signBlob(payload, function (data) | |
705 | - | |
705 | + | |
706 | 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 | |
707 | web.get(url, function (err, response) | |
708 | ac.debug("err", err) | |
709 | callback(JSON.parse(response.body)) | |
710 | end) | |
711 | end) | |
712 | end) | |
713 | ||
714 | if not success then | |
715 | ac.debug("Error occurred:", err) | |
716 | end | |
717 | end | |
718 | - | |
718 | + | |
719 | function NoHesiClient:postScore(score, combo, speed, time, distance, callback) | |
720 | 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()) | |
721 | local success, err = pcall(function() | |
722 | ac.signBlob(payload, function (data) | |
723 | - | |
723 | + | |
724 | 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 | |
725 | web.post(url, function (err, response) | |
726 | ac.debug("err", err) | |
727 | callback(JSON.parse(response.body)) | |
728 | end) | |
729 | end) | |
730 | end) | |
731 | ||
732 | if not success then | |
733 | ac.debug("Error occurred:", err) | |
734 | end | |
735 | end | |
736 | - | |
736 | + | |
737 | function NoHesiClient:getScore(callback) | |
738 | local payload = ac.getUserSteamID() | |
739 | local success, err = pcall(function() | |
740 | ac.signBlob(payload, function (data) | |
741 | - | |
741 | + | |
742 | 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 | |
743 | web.get(url, function (err, response) | |
744 | ac.debug("err", err) | |
745 | callback(JSON.parse(response.body)) | |
746 | end) | |
747 | end) | |
748 | end) | |
749 | ||
750 | if not success then | |
751 | ac.debug("Error occurred:", err) | |
752 | end | |
753 | end | |
754 | - | |
754 | + | |
755 | function urlEncode(str) | |
756 | if str then | |
757 | str = string.gsub(str, "\n", "\r\n") | |
758 | str = string.gsub(str, "([^%w ])", | |
759 | - | |
759 | + | |
760 | str = string.gsub(str, " ", "+") | |
761 | end | |
762 | return str | |
763 | end | |
764 | ||
765 | function script.prepare(dt) | |
766 | ac.log('speed' + ac.getCar(1).speedKmh) | |
767 | return ac.getCarState(1).speedKmh > 60 | |
768 | end | |
769 | - | |
769 | + | |
770 | local nohesiclient = NoHesiClient(config.hubUrl) | |
771 | local requiredSpeed = 95 | |
772 | local sim = ac.getSim() | |
773 | ||
774 | - | |
774 | + | |
775 | local averageSpeed = 0 | |
776 | local startdistance = 0 | |
777 | local rundistance = 0 | |
778 | - | |
778 | + | |
779 | local runtime = 0 | |
780 | local distancedriven = 0 | |
781 | local timePassed = 0 | |
782 | local speedMessageTimer = 0 | |
783 | local mackMessageTimer = 0 | |
784 | local totalScore = 0 | |
785 | local comboMeter = 1 | |
786 | local speedComboMeter = 1 | |
787 | local proxiComboMeter = 1 | |
788 | local comboColor = 0 | |
789 | local dangerouslySlowTimer = 0 | |
790 | local carsState = {} | |
791 | local wheelsWarningTimeout = 0 | |
792 | local personalBest = 0 | |
793 | local ownRank = 0 | |
794 | local cheatcounter = 0 | |
795 | local leaderboardByName = {} | |
796 | local MackMessages = { 'MAAAACK!!!!', 'MACKSAUCE!!', 'You Hesitated....', 'bRUH', 'No Shot...'} | |
797 | --new | |
798 | local CloseMessages = { 'IN THAT!!!!!', 'IN THERE.', 'D I V E', 'SKRRT!!!' } | |
799 | local NormalMessages = { 'Overtake', 'Smooth Move', 'Easy Breeze' } | |
800 | local FPS = sim.fps | |
801 | local CPUO = sim.cpuOccupancy | |
802 | local Uix, Uiy, Uis, fpsCheck, cpuoCheck, UIToggle, UIClear = nil, nil, nil, nil, nil, nil, nil | |
803 | local logoCheck = true | |
804 | --new | |
805 | local previousBestScore = nil | |
806 | local previousTotalScore = nil | |
807 | local previousComboMeter = nil | |
808 | local previousSpeedComboMeter = nil | |
809 | local previousProximity = nil | |
810 | local previousRank = nil | |
811 | local previousMessages = {} -- Для хранения предыдущих сообщений | |
812 | local previousSpeedWarningShown = nil -- Добавляем переменную состояния для предупреждения о скорости | |
813 | ||
814 | ||
815 | function OptionUI() | |
816 | local uiState = ac.getUI() | |
817 | ui.text('Settings list:') | |
818 | - | |
818 | + | |
819 | - | |
819 | + | |
820 | fpsCheck = not fpsCheck | |
821 | end | |
822 | if ui.checkbox("Show CPU Occupancy", cpuoCheck) then | |
823 | cpuoCheck = not cpuoCheck | |
824 | end | |
825 | if ui.checkbox("Points UI", UIToggle) then | |
826 | UIToggle = not UIToggle | |
827 | end | |
828 | if ui.checkbox("Clear / Dark UI", UIClear) then | |
829 | UIClear = not UIClear | |
830 | end | |
831 | ||
832 | ui.text('Points position:') | |
833 | Uix = ui.slider('##slideyyo', Uix, -500, uiState.windowSize.x, 'x-axis: %0.0f') | |
834 | Uiy = ui.slider('##slidexyo', Uiy, -500, uiState.windowSize.y, 'y-axis: %0.0f') | |
835 | Uis = ui.slider('##slidesyo', Uis, 0, 1, 'Scale: %0.2f') | |
836 | - | |
836 | + | |
837 | nohesiclient:postSettings(Uix, Uiy, Uis, fpsCheck, cpuoCheck, UIToggle, UIClear) | |
838 | end | |
839 | ui.sameLine(160, 0) | |
840 | if ui.button("☁️ Load Settings", vec2(120, 20)) then | |
841 | nohesiclient:getSettings(function (settings) | |
842 | ac.log(settings) | |
843 | if settings[1] == nil then | |
844 | Uix = uiState.windowSize.y * 0.005 | |
845 | Uiy = uiState.windowSize.x * 0.0025 | |
846 | Uis = uiState.windowSize.y * 0.0005 | |
847 | fpsCheck = false | |
848 | cpuoCheck = false | |
849 | UIToggle = true | |
850 | UIClear = true | |
851 | else | |
852 | Uix = settings[1].uix | |
853 | Uiy = settings[1].uiy | |
854 | Uis = settings[1].uis | |
855 | fpsCheck = settings[1].fpscheck | |
856 | cpuoCheck = settings[1].cpuocheck | |
857 | UIToggle = settings[1].uitoggle | |
858 | UIClear = settings[1].uiclear | |
859 | end | |
860 | end) | |
861 | end | |
862 | if ui.button("Reset", vec2(50, 20)) then | |
863 | Uix = uiState.windowSize.y * 0.005 | |
864 | Uiy = uiState.windowSize.x * 0.0025 | |
865 | Uis = uiState.windowSize.y * 0.0005 | |
866 | fpsCheck = false | |
867 | cpuoCheck = false | |
868 | UIToggle = true | |
869 | UIClear = true | |
870 | end | |
871 | end) | |
872 | end | |
873 | ||
874 | function OptionUIClosed() | |
875 | ||
876 | end | |
877 | ||
878 | - | |
878 | + | |
879 | local uiState = ac.getUI() | |
880 | - | |
880 | + | |
881 | Uix = uiState.windowSize.y * 0.005 | |
882 | - | |
882 | + | |
883 | Uis = uiState.windowSize.y * 0.0005 | |
884 | fpsCheck = false | |
885 | cpuoCheck = false | |
886 | UIToggle = true | |
887 | UIClear = true | |
888 | else | |
889 | Uix = settings[1].uix | |
890 | Uiy = settings[1].uiy | |
891 | Uis = settings[1].uis | |
892 | fpsCheck = settings[1].fpscheck | |
893 | cpuoCheck = settings[1].cpuocheck | |
894 | UIToggle = settings[1].uitoggle | |
895 | UIClear = settings[1].uiclear | |
896 | end | |
897 | end) | |
898 | ||
899 | nohesiclient:getScore(function (score) | |
900 | personalBest = score.score | |
901 | ownRank = score.rank | |
902 | end) | |
903 | - | |
903 | + | |
904 | local function updateTopScores() | |
905 | local newScores = {} | |
906 | for name, score in pairs(leaderboardByName) do | |
907 | table.insert(newScores, { Name = name, Score = score}) | |
908 | - | |
908 | + | |
909 | table.sort(newScores, function (a, b) return a.Score > b.Score end) | |
910 | topScores = newScores | |
911 | end | |
912 | ||
913 | local personalBestEvent = ac.OnlineEvent({ | |
914 | ac.StructItem.key("overtakePb"), | |
915 | score = ac.StructItem.double() | |
916 | }, function (sender, message) | |
917 | - | |
917 | + | |
918 | updateTopScores() | |
919 | end) | |
920 | ||
921 | local function broadcastPersonalBest() | |
922 | if totalScore > personalBest then | |
923 | personalBestEvent({ score = totalScore }) | |
924 | end | |
925 | - | |
925 | + | |
926 | ||
927 | local function savePersonalBest() | |
928 | if totalScore > personalBest then | |
929 | personalBest = totalScore | |
930 | nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score) | |
931 | - | |
931 | + | |
932 | ownRank = score.rank | |
933 | end) | |
934 | end | |
935 | end | |
936 | ||
937 | --updateLeaderboard() | |
938 | --setInterval(updateLeaderboard, 60) | |
939 | setInterval(broadcastPersonalBest, 1) | |
940 | if runtime > 0 then | |
941 | - | |
941 | + | |
942 | end | |
943 | ||
944 | local leaderboardSize = vec2(250, 80) | |
945 | ||
946 | local uiState = ac.getUI() | |
947 | ||
948 | - | |
948 | + | |
949 | if timePassed == 0 then | |
950 | - | |
950 | + | |
951 | end | |
952 | - | |
952 | + | |
953 | local player = ac.getCarState(1) | |
954 | if player.engineLifeLeft < 1 then | |
955 | ac.console('Overtake score: ' .. totalScore) | |
956 | return | |
957 | - | |
957 | + | |
958 | ||
959 | distancedriven = player.distanceDrivenSessionKm | |
960 | timePassed = timePassed + dt | |
961 | speedMessageTimer = speedMessageTimer + dt | |
962 | mackMessageTimer = mackMessageTimer + dt | |
963 | - | |
963 | + | |
964 | ||
965 | local comboFadingRate = 0.5 * math.lerp(1, 0.1, math.lerpInvSat(player.speedKmh, 80, 200)) + player.wheelsOutside | |
966 | comboMeter = math.max(1, comboMeter - dt * comboFadingRate) | |
967 | speedComboMeter = math.floor(math.lerp(1, 3, math.lerpInvSat(player.speedKmh, 150, 300)) * 10 + 0.5) / 10 | |
968 | - | |
968 | + | |
969 | - | |
969 | + | |
970 | local sim = ac.getSim() | |
971 | while sim.carsCount > #carsState do | |
972 | carsState[#carsState + 1] = {} | |
973 | - | |
973 | + | |
974 | - | |
974 | + | |
975 | if wheelsWarningTimeout > 0 then | |
976 | wheelsWarningTimeout = wheelsWarningTimeout - dt | |
977 | elseif player.wheelsOutside > 0 then | |
978 | if wheelsWarningTimeout == 0 then | |
979 | - | |
979 | + | |
980 | addMessage('Car is Out Of Zone', -1) | |
981 | wheelsWarningTimeout = 60 | |
982 | end | |
983 | ||
984 | if player.speedKmh < requiredSpeed then | |
985 | proxiComboMeter = 1 | |
986 | if dangerouslySlowTimer > 3 then | |
987 | ac.console('Overtake score: ' .. totalScore) | |
988 | - | |
988 | + | |
989 | totalScore = 0 | |
990 | ||
991 | else | |
992 | if dangerouslySlowTimer < 3 then | |
993 | if speedMessageTimer > 5 then | |
994 | --addMessage('a'..dangerouslySlowTimer, -1) | |
995 | - | |
995 | + | |
996 | end | |
997 | end | |
998 | ||
999 | if dangerouslySlowTimer == 0 then | |
1000 | addMessage('Speed up!', -1) | |
1001 | end | |
1002 | ||
1003 | - | |
1003 | + | |
1004 | dangerouslySlowTimer = dangerouslySlowTimer + dt | |
1005 | comboMeter = 1 | |
1006 | if totalScore > personalBest and dangerouslySlowTimer > 3 then | |
1007 | - | |
1007 | + | |
1008 | -- ac.sendChatMessage('just scored a ' .. personalBest) | |
1009 | nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score) | |
1010 | personalBest = score.score | |
1011 | ownRank = score.rank | |
1012 | end) | |
1013 | - | -- ac.sendChatMessage('just scored a ' .. personalBest) |
1013 | + | |
1014 | ||
1015 | return | |
1016 | else | |
1017 | dangerouslySlowTimer = 0 | |
1018 | end | |
1019 | - | |
1019 | + | |
1020 | if player.collidedWith == 0 then | |
1021 | ||
1022 | if totalScore > personalBest then | |
1023 | personalBest = totalScore | |
1024 | - | |
1024 | + | -- ac.sendChatMessage('just scored a ' .. personalBest) |
1025 | nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score) | |
1026 | - | |
1026 | + | |
1027 | ownRank = score.rank | |
1028 | end) | |
1029 | - | -- ac.sendChatMessage('just scored a ' .. personalBest) |
1029 | + | |
1030 | cheatcounter = 0 | |
1031 | comboMeter = 1 | |
1032 | totalScore = 0 | |
1033 | if mackMessageTimer > 1 then | |
1034 | addMessage(MackMessages[math.random(1, #MackMessages)], -1) | |
1035 | mackMessageTimer = 0 | |
1036 | end | |
1037 | end | |
1038 | ||
1039 | if totalScore > 0 then | |
1040 | rundistance = distancedriven - startdistance | |
1041 | runtime = timePassed - starttime | |
1042 | averageSpeed = (rundistance / runtime)*3600 | |
1043 | - | |
1043 | + | |
1044 | maxcombo = 0 | |
1045 | averageSpeed = 0 | |
1046 | runtime = 0 | |
1047 | starttime = 0 | |
1048 | rundistance = 0 | |
1049 | startdistance = 0 | |
1050 | proxiComboMeter = 1 | |
1051 | end | |
1052 | ||
1053 | ||
1054 | ||
1055 | local function get_closest_car_index() | |
1056 | local k = 0 | |
1057 | - | |
1057 | + | |
1058 | - | |
1058 | + | |
1059 | - | |
1059 | + | |
1060 | local closestcarname = ac.getCarID(i-1) | |
1061 | if not string.find(closestcarname, "traffic") then | |
1062 | local dist = car.position:distance(player.position) | |
1063 | if dist < min then | |
1064 | k = i | |
1065 | min = dist | |
1066 | end | |
1067 | end | |
1068 | end | |
1069 | return k | |
1070 | end | |
1071 | ||
1072 | local closestcarindex = get_closest_car_index() | |
1073 | local closestcar = ac.getCarState(closestcarindex) | |
1074 | local closestcarname | |
1075 | ||
1076 | - | |
1076 | + | |
1077 | closestcarname = ac.getCarID(closestcarindex-1) | |
1078 | ||
1079 | if closestcar.position:closerToThan(player.position, 20) and math.dot(car.look, player.look) > 0.2 then | |
1080 | - | |
1080 | + | |
1081 | local dotProduct = closestcar.look:dot(carToPlayer) | |
1082 | local dotProduct2 = closestcar.side:dot(carToPlayer) | |
1083 | - | |
1083 | + | |
1084 | local t = math.lerpInvSat(math.abs(dotProduct), 19, 6) | |
1085 | local penaltyFactor = 1 + (t^2 * 4) | |
1086 | local p = math.lerpInvSat(math.abs(dotProduct)+(math.abs(dotProduct2)*penaltyFactor), 19, 6) | |
1087 | ||
1088 | - | |
1088 | + | |
1089 | else | |
1090 | proxiComboMeter = 1 | |
1091 | end | |
1092 | else | |
1093 | proxiComboMeter = 1 -- Assuming default value in case of error | |
1094 | end | |
1095 | ||
1096 | ||
1097 | ||
1098 | for i = 2, ac.getSim().carsCount do | |
1099 | local car = ac.getCarState(i) | |
1100 | - | |
1100 | + | |
1101 | - | |
1101 | + | |
1102 | - | |
1102 | + | |
1103 | local drivingAlong = math.dot(car.look, player.look) > 0.2 | |
1104 | if not drivingAlong then | |
1105 | state.drivingAlong = false | |
1106 | ||
1107 | if not state.nearMiss and car.position:closerToThan(player.position, 3) then | |
1108 | state.nearMiss = true | |
1109 | ||
1110 | ||
1111 | - | |
1111 | + | |
1112 | end | |
1113 | ||
1114 | - | |
1114 | + | |
1115 | - | |
1115 | + | |
1116 | local posDir = (car.position - player.position):normalize() | |
1117 | local posDot = math.dot(posDir, car.look) | |
1118 | - | |
1118 | + | |
1119 | - | |
1119 | + | |
1120 | totalScore = totalScore + math.ceil(10 * comboMeter) | |
1121 | if startdistance == 0 and starttime == 0 then | |
1122 | startdistance = distancedriven | |
1123 | starttime = timePassed | |
1124 | end | |
1125 | if cheatcounter > 10 then | |
1126 | setInterval(function() ac.sendChatMessage('debug message from the anti-cheat') end, 1, "cheat") | |
1127 | physics.engageGear(0, 0) | |
1128 | physics.setCarNoInput(true) | |
1129 | physics.teleportCarTo(0, 'PIT') | |
1130 | totalScore = 0 | |
1131 | nohesiclient:postScore(totalScore, maxcombo, averageSpeed, runtime, rundistance, function (score) | |
1132 | personalBest = score.score | |
1133 | ownRank = score.rank | |
1134 | end) | |
1135 | setTimeout(function () ac.shutdownAssettoCorsa() end, 5) | |
1136 | state.overtaken = true | |
1137 | elseif car.position:closerToThan(player.position, 1.9) then | |
1138 | cheatcounter = cheatcounter + 1 | |
1139 | state.overtaken = true | |
1140 | elseif car.position:closerToThan(player.position, 3) then | |
1141 | comboMeter = comboMeter + (2 + speedComboMeter + proxiComboMeter - 1) | |
1142 | comboColor = comboColor + math.random(1, 90) | |
1143 | addMessage(CloseMessages[math.random(#CloseMessages)].. ' ' .. (2 + speedComboMeter + proxiComboMeter - 1) .. 'x', 2) | |
1144 | state.overtaken = true | |
1145 | else | |
1146 | comboMeter = comboMeter + (speedComboMeter + proxiComboMeter - 1) | |
1147 | comboColor = comboColor + 90 | |
1148 | addMessage(NormalMessages[math.random(#NormalMessages)] .. ' ' .. (speedComboMeter + proxiComboMeter - 1) .. 'x', comboMeter > 50 and 1 or 0) | |
1149 | state.overtaken = true | |
1150 | end | |
1151 | if comboMeter > maxcombo then | |
1152 | maxcombo = comboMeter | |
1153 | end | |
1154 | end | |
1155 | end | |
1156 | ||
1157 | else | |
1158 | state.maxPosDot = -1 | |
1159 | state.overtaken = false | |
1160 | state.collided = false | |
1161 | - | |
1161 | + | |
1162 | state.nearMiss = false | |
1163 | end | |
1164 | end | |
1165 | end | |
1166 | ||
1167 | local messages = {} | |
1168 | local glitter = {} | |
1169 | local glitterCount = 0 | |
1170 | ||
1171 | - | |
1171 | + | |
1172 | for i = math.min(#messages + 1, 1), 2, -1 do | |
1173 | messages[i] = messages[i - 1] | |
1174 | messages[i].targetPos = i | |
1175 | - | |
1175 | + | |
1176 | messages[1] = { text = text, age = 0, targetPos = 1, currentPos = 1, mood = mood } | |
1177 | if mood == 1 then | |
1178 | for i = 1, 60 do | |
1179 | local dir = vec2(math.random() - 0.5, math.random() - 0.5) | |
1180 | glitterCount = glitterCount + 1 | |
1181 | glitter[glitterCount] = { | |
1182 | color = rgbm.new(hsv(math.random() * 360, 1, 1):rgb(), 1), | |
1183 | pos = vec2(80, 140) + dir * vec2(40, 20), | |
1184 | velocity = dir:normalize():scale(0.2 + math.random()), | |
1185 | life = 0.5 + 0.5 * math.random() | |
1186 | } | |
1187 | end | |
1188 | end | |
1189 | end | |
1190 | ||
1191 | local function updateMessages(dt) | |
1192 | comboColor = comboColor + dt * 10 * comboMeter | |
1193 | if comboColor > 360 then comboColor = comboColor - 360 end | |
1194 | for i = 1, #messages do | |
1195 | - | |
1195 | + | |
1196 | m.age = m.age + dt | |
1197 | m.currentPos = math.applyLag(m.currentPos, m.targetPos, 0.8, dt) | |
1198 | end | |
1199 | end | |
1200 | ||
1201 | local speedWarning = 0 | |
1202 | local fontpath = '' | |
1203 | ||
1204 | ||
1205 | - | |
1205 | + | |
1206 | fontpath = response | |
1207 | end) | |
1208 | - | |
1208 | + | |
1209 | - | |
1209 | + | |
1210 | ||
1211 | ||
1212 | function loadVideoInPlayer(videoPath) | |
1213 | - | |
1213 | + | |
1214 | - | |
1214 | + | |
1215 | - | |
1215 | + | |
1216 | - | |
1216 | + | |
1217 | :setLooping(true) -- Циклическое воспроизведение | |
1218 | player:setUpdatePeriod(0.0) -- Установка периода обновления | |
1219 | ||
1220 | end | |
1221 | ||
1222 | ||
1223 | checkAndLoadVideo() | |
1224 | ||
1225 | ||
1226 | function add_suffix(position) | |
1227 | - | |
1227 | + | |
1228 | if last_two_digits >= 11 and last_two_digits <= 13 then | |
1229 | - | |
1229 | + | |
1230 | - | |
1230 | + | |
1231 | return "st" | |
1232 | elseif position % 10 == 2 then | |
1233 | return "nd" | |
1234 | elseif position % 10 == 3 then | |
1235 | return "rd" | |
1236 | else | |
1237 | return "th" | |
1238 | end | |
1239 | end | |
1240 | ||
1241 | ||
1242 | ||
1243 | browser:navigate('data:text/html;base64,' .. ac.encodeBase64(htmlContent)) | |
1244 | local counter = 0 | |
1245 | - | |
1245 | + | |
1246 | - | |
1246 | + | |
1247 | - | |
1247 | + | |
1248 | ||
1249 | ||
1250 | - | |
1250 | + | |
1251 | - | |
1251 | + | |
1252 | - | |
1252 | + | |
1253 | - | |
1253 | + | |
1254 | - | |
1254 | + | |
1255 | local isAnimationActive = false -- Изначально анимация не активна | |
1256 | -- Flag to check if the animation has been started | |
1257 | local hasAnimationStarted = false | |
1258 | -- Current time within the animation cycle | |
1259 | local currentTime = 0 | |
1260 | ||
1261 | -- Function to start the animation | |
1262 | local function startAnimation() | |
1263 | if not hasAnimationStarted and not ac.getSim().isInMainMenu then | |
1264 | isAnimationActive = true | |
1265 | - | |
1265 | + | |
1266 | end | |
1267 | end | |
1268 | ||
1269 | -- Animation function | |
1270 | local function animate(dt) | |
1271 | -- Start the animation based on the condition | |
1272 | startAnimation() | |
1273 | - | |
1273 | + | |
1274 | ||
1275 | -- Stop the animation if it's not active | |
1276 | if not isAnimationActive then return end | |
1277 | ||
1278 | -- Update the current animation time | |
1279 | currentTime = currentTime + dt | |
1280 | local totalTime = fadeInTime + fadeOutTime | |
1281 | local alpha = 0 | |
1282 | ||
1283 | -- Calculate the alpha value based on the current phase of the animation | |
1284 | if currentTime <= fadeInTime then | |
1285 | alpha = currentTime / fadeInTime | |
1286 | elseif currentTime <= totalTime then | |
1287 | - | |
1287 | + | |
1288 | browser:sendAsync('webanim', "startweb") | |
1289 | else | |
1290 | isAnimationActive = false -- Deactivate the animation after completion | |
1291 | currentTime = 0 -- Reset currentTime to allow the animation to be started again if needed | |
1292 | ||
1293 | end | |
1294 | ||
1295 | -- Set the alpha channel for color modulation | |
1296 | local color = rgbm(1, 1, 1, alpha) | |
1297 | ||
1298 | -- Render the image with the animated alpha channel | |
1299 | - | |
1299 | + | |
1300 | image = player, | |
1301 | pos = vec2(0, 0), | |
1302 | - | |
1302 | + | |
1303 | color = color, | |
1304 | uvStart = vec2(0, 0), | |
1305 | uvEnd = vec2(1, 1) | |
1306 | }) | |
1307 | end | |
1308 | ||
1309 | -- Schedule the animation to stop after the total duration | |
1310 | setTimeout(function() | |
1311 | isAnimationActive = false -- Deactivate the animation after completion | |
1312 | end, fadeInTime + fadeOutTime) | |
1313 | - | |
1313 | + | |
1314 | ||
1315 | ||
1316 | ||
1317 | ||
1318 | - | |
1318 | + | |
1319 | - | |
1319 | + | |
1320 | - | |
1320 | + | |
1321 | - | |
1321 | + | |
1322 | - | |
1322 | + | |
1323 | ||
1324 | if personalBest ~= previousBestScore then | |
1325 | - | |
1325 | + | |
1326 | - | |
1326 | + | |
1327 | end | |
1328 | - | |
1328 | + | |
1329 | if totalScore ~= previousTotalScore then | |
1330 | browser:sendAsync('updateScore', totalScore) | |
1331 | previousTotalScore = totalScore | |
1332 | end | |
1333 | - | |
1333 | + | |
1334 | -- Сравниваем округленные значения до десятых | |
1335 | if roundedComboMeter ~= previousComboMeter then | |
1336 | browser:sendAsync('updateCombo', roundedComboMeter) | |
1337 | previousComboMeter = roundedComboMeter | |
1338 | - | |
1338 | + | |
1339 | ||
1340 | if speedComboMeter ~= previousSpeedComboMeter then | |
1341 | browser:sendAsync('updateSpeed', speedComboMeter) | |
1342 | previousSpeedComboMeter = speedComboMeter | |
1343 | end | |
1344 | - | |
1344 | + | |
1345 | if proxiComboMeter ~= previousProximity then | |
1346 | browser:sendAsync('updateProximity', proxiComboMeter) | |
1347 | previousProximity = proxiComboMeter | |
1348 | end | |
1349 | - | |
1349 | + | |
1350 | if ownRank ~= previousRank then | |
1351 | browser:sendAsync('updatePlace', "#"..ownRank) | |
1352 | previousRank = ownRank | |
1353 | ||
1354 | - | |
1354 | + | |
1355 | ||
1356 | -- Проверка и отправка состояния предупреждения о скорости | |
1357 | if speedWarningShown ~= previousSpeedWarningShown then | |
1358 | - | |
1358 | + | |
1359 | previousSpeedWarningShown = speedWarningShown | |
1360 | - | |
1360 | + | |
1361 | ||
1362 | ||
1363 | for i, m in ipairs(messages) do | |
1364 | local prevM = previousMessages[i] or {} | |
1365 | -- Преобразование результатов функции rgbm в соответствующий объект | |
1366 | - | |
1366 | + | |
1367 | - | |
1367 | + | |
1368 | or m.mood == 2 and {r=0.714, g=0.02, b=0.976, m=1} | |
1369 | or {r=1, g=1, b=1, m=1} -- Упрощаем, всегда используем полную непрозрачность | |
1370 | ||
1371 | -- Проверяем, изменилось ли сообщение или его настроение | |
1372 | if m.text ~= prevM.text or m.mood ~= prevM.mood then | |
1373 | browser:sendAsync('messages', {message = m.text, color = colorComponents}) | |
1374 | end | |
1375 | - | |
1375 | + | |
1376 | -- Обновляем предыдущее состояние сообщения | |
1377 | previousMessages[i] = {text = m.text, mood = m.mood} | |
1378 | end | |
1379 | ||
1380 | - | |
1380 | + | |
1381 | ||
1382 | ||
1383 | ||
1384 | - | |
1384 | + | |
1385 | function script.drawUI(dt) | |
1386 | - | |
1386 | + | |
1387 | - | |
1387 | + | |
1388 | - | |
1388 | + | |
1389 | - | |
1389 | + | |
1390 | updateMessages(uiState.dt) | |
1391 | - | |
1391 | + | |
1392 | - | |
1392 | + | ui.beginScale() |
1393 | ui.beginTransparentWindow('overtakeScore', vec2(Uix, Uiy), vec2(560, 300)) | |
1394 | - | |
1394 | + | |
1395 | ||
1396 | - | |
1396 | + | |
1397 | - | ui.beginScale() |
1397 | + | |
1398 | ui.dummy(vec2(520, 420)) | |
1399 | - | |
1399 | + | |
1400 | - | |
1400 | + | |
1401 | - | |
1401 | + | |
1402 | ||
1403 | counter = counter + ac.getScriptDeltaT() | |
1404 | ||
1405 | if counter >= 0.1 then | |
1406 | sendDataIfChanged() | |
1407 | - | |
1407 | + | |
1408 | ||
1409 | - | |
1409 | + | |
1410 | ||
1411 | -- Handle mouse input for the browser | |
1412 | -- nextupdate | |
1413 | - | |
1413 | + | |
1414 | -- mouseButtons[1] = uis.isMouseLeftKeyDown | |
1415 | - | |
1415 | + | |
1416 | -- mouseButtons[3] = uis.isMouseMiddleKeyDown | |
1417 | -- return mouseButtons | |
1418 | --end | |
1419 | ||
1420 | -- browser:mouseInput(ui.mouseLocalPos():sub(p1):div(lastSize), getMouseButtons(), uis.mouseWheel, false) | |
1421 | ||
1422 | ui.endTransparentWindow() | |
1423 | ||
1424 | - | |
1424 | + | |
1425 | if logoCheck then | |
1426 | - | |
1426 | + | |
1427 | end | |
1428 | - | |
1428 | + | |
1429 | - | |
1429 | + | |
1430 | if fpsCheck then | |
1431 | ui.setCursorY(0) | |
1432 | ui.pushFont(ui.Font.Main) | |
1433 | - | |
1433 | + | |
1434 | ui.text(math.floor(FPS) .. " FPS") | |
1435 | ui.endOutline(rgbm(0, 0, 0, 0.3)) | |
1436 | ui.sameLine(100, 0) | |
1437 | end | |
1438 | if cpuoCheck then | |
1439 | ui.setCursorY(0) | |
1440 | ui.pushFont(ui.Font.Main) | |
1441 | setInterval(function() CPUO = sim.cpuOccupancy end, 0.1, "CPUOCounter") | |
1442 | ui.text(math.floor(CPUO) .. "% CPU Occupancy") | |
1443 | if fpsCheck then | |
1444 | ui.sameLine(300, 0) | |
1445 | else | |
1446 | ui.sameLine(200, 0) | |
1447 | end | |
1448 | end | |
1449 | ui.endOutline(rgbm(0, 0, 0, 0.3)) | |
1450 | end | |
1451 | ui.registerOnlineExtra(ui.Icons.Burn, 'No Hesi UI Settings', nil, OptionUI, OptionUIClosed, ui.OnlineExtraFlags.Tool) |