mirror of
https://github.com/ddnet/ddnet.git
synced 2024-09-19 01:02:20 +00:00
Compare commits
98 commits
e5c682f209
...
60ba9cc6d5
Author | SHA1 | Date | |
---|---|---|---|
60ba9cc6d5 | |||
8d431f8feb | |||
80e2de13da | |||
66fb5d5d7f | |||
d3f0c2a156 | |||
d5e81ca78d | |||
128ffd2313 | |||
af1b32d296 | |||
0ad1c08c22 | |||
9f278979e5 | |||
32e9240634 | |||
b5d662622c | |||
58ce5985d4 | |||
46acbdd6bf | |||
d536bceed6 | |||
337d5c7ab3 | |||
8e45d0a71a | |||
a865354320 | |||
3595b70170 | |||
11fd82077a | |||
3d30ce4bf2 | |||
9e0ba8a91f | |||
c89509bc4b | |||
306d3c7b58 | |||
9832288983 | |||
fc058fa432 | |||
3b22a3e02f | |||
ed1ef4e694 | |||
9103332e36 | |||
0948a53648 | |||
bbd34c9452 | |||
e4282f100a | |||
eb9e73f68b | |||
e0a95d14a6 | |||
fa28ed77a6 | |||
5335813629 | |||
796fa4275f | |||
5b0163d069 | |||
c9e7e0f01b | |||
cf107a81a1 | |||
4e2d7e2104 | |||
ChillerDragon | 74bb327799 | ||
22d699fbc0 | |||
51c0b4dafc | |||
5f57ba9130 | |||
69fa7ea18f | |||
41c40fb27f | |||
0664f12265 | |||
8b5da71e89 | |||
ChillerDragon | d0eebee44d | ||
1cbdb9d2f0 | |||
8f79d4252c | |||
7abad2b8a4 | |||
c64b744ad3 | |||
f599449f31 | |||
226d948acc | |||
58f14edb95 | |||
a3fc627285 | |||
f79784bec7 | |||
ccaddd2016 | |||
7a60f4a5e7 | |||
ChillerDragon | e9187cc352 | ||
ChillerDragon | 2cf6622cd7 | ||
ChillerDragon | 19a888734c | ||
ChillerDragon | 44fc871463 | ||
157498799e | |||
a017190753 | |||
e17da385dd | |||
bfc6a15df9 | |||
ChillerDragon | 2a26c1c5d6 | ||
624ac85e8c | |||
d9b031c887 | |||
403c4a40da | |||
0e2960206a | |||
a8616ef2ab | |||
ChillerDragon | 8b304cab23 | ||
9e88ca5846 | |||
ChillerDragon | 1b8e402a65 | ||
30acedaac5 | |||
bb3c76a290 | |||
341e3f6ec7 | |||
d4f72803be | |||
f2d5b83f84 | |||
fcf810cae1 | |||
50b0af1614 | |||
b5c11bc860 | |||
7951136fab | |||
ChillerDragon | 82ca4bc335 | ||
ChillerDragon | 5986ee03b9 | ||
ChillerDragon | e1cb617c42 | ||
ChillerDragon | daad41fbd7 | ||
ChillerDragon | ed49fef917 | ||
ChillerDragon | e20250cd65 | ||
bb45db8ad3 | |||
d452bcda8f | |||
1d65fff3ff | |||
ecec1ff2a7 | |||
df0b3567a6 |
|
@ -2462,6 +2462,7 @@ if(CLIENT)
|
|||
editor_trackers.cpp
|
||||
editor_trackers.h
|
||||
editor_ui.h
|
||||
enums.h
|
||||
explanations.cpp
|
||||
layer_selector.cpp
|
||||
layer_selector.h
|
||||
|
@ -2929,6 +2930,7 @@ if(GTEST_FOUND OR DOWNLOAD_GTEST)
|
|||
compression.cpp
|
||||
csv.cpp
|
||||
datafile.cpp
|
||||
editor.cpp
|
||||
fs.cpp
|
||||
git_revision.cpp
|
||||
hash.cpp
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
# Rafael Fontenelle 2024-04-30 14:52:00
|
||||
# Rafael Fontenelle 2024-06-11 22:43:00
|
||||
# Rafael Fontenelle 2024-07-17 12:04:00
|
||||
# Rafael Fontenelle 2024-09-02 16:02:00
|
||||
##### /authors #####
|
||||
|
||||
##### translated strings #####
|
||||
|
@ -1894,52 +1895,52 @@ https://wiki.ddnet.org/wiki/Mapping
|
|||
== https://wiki.ddnet.org/wiki/Mapping
|
||||
|
||||
Could not resolve connect address '%s'. See local console for details.
|
||||
==
|
||||
== Não foi possível resolver o endereço de conexão '%s'. Veja o console local para detalhes.
|
||||
|
||||
Connect address error
|
||||
==
|
||||
== Erro no endereço de conexão
|
||||
|
||||
Could not connect dummy
|
||||
==
|
||||
== Não foi possível conectar o dummy
|
||||
|
||||
Dummy is not allowed on this server
|
||||
==
|
||||
== Dummy não é permitido neste servidor
|
||||
|
||||
Please wait…
|
||||
==
|
||||
== Por favor, aguarde…
|
||||
|
||||
Show client IDs (scoreboard, chat, spectator)
|
||||
==
|
||||
== Mostrar IDs de clientes (placar, chat, observador)
|
||||
|
||||
Normal:
|
||||
==
|
||||
== Normal:
|
||||
|
||||
Team:
|
||||
==
|
||||
== Time:
|
||||
|
||||
Dummy settings
|
||||
==
|
||||
== Configurações do dummy
|
||||
|
||||
Toggle to edit your dummy settings
|
||||
==
|
||||
== Alterne para editar suas configurações do dummy
|
||||
|
||||
Randomize
|
||||
==
|
||||
== Aleatorizar
|
||||
|
||||
Are you sure that you want to delete '%s'?
|
||||
==
|
||||
== Tem certeza que deseja excluir '%s'?
|
||||
|
||||
Delete skin
|
||||
==
|
||||
== Excluir skin
|
||||
|
||||
Basic
|
||||
==
|
||||
== Básico
|
||||
|
||||
Custom
|
||||
==
|
||||
== Personalizado
|
||||
|
||||
Unable to delete skin
|
||||
==
|
||||
== Não foi possível excluir skin
|
||||
|
||||
Customize
|
||||
==
|
||||
== Personalizar
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# 3edcxzaq1 2020-06-25 00:00:00
|
||||
# cur.ie 2020-09-28 00:00:00
|
||||
# simpygirl 2022-02-20 00:00:00
|
||||
# furo 2024-07-17 00:00:00
|
||||
# furo 2024-08-29 00:00:00
|
||||
##### /authors #####
|
||||
|
||||
##### translated strings #####
|
||||
|
@ -1868,52 +1868,52 @@ https://wiki.ddnet.org/wiki/Mapping
|
|||
== https://wiki.ddnet.org/wiki/Mapping
|
||||
|
||||
Could not resolve connect address '%s'. See local console for details.
|
||||
==
|
||||
== Kunde inte förstå anslutnings adress '%s'. Se den lokala konsolen för detaljer.
|
||||
|
||||
Connect address error
|
||||
==
|
||||
== Anslutnings problem
|
||||
|
||||
Could not connect dummy
|
||||
==
|
||||
== Kunde inte ansluta dummy
|
||||
|
||||
Dummy is not allowed on this server
|
||||
==
|
||||
== Dummy är inte tillåten på denna server
|
||||
|
||||
Please wait…
|
||||
==
|
||||
== Vänligen vänta…
|
||||
|
||||
Show client IDs (scoreboard, chat, spectator)
|
||||
==
|
||||
== Visa klient IDen (poänglistan, chatt, åskadarmeny)
|
||||
|
||||
Normal:
|
||||
==
|
||||
== Normal:
|
||||
|
||||
Team:
|
||||
==
|
||||
== Lag:
|
||||
|
||||
Dummy settings
|
||||
==
|
||||
== Dummy inställningar
|
||||
|
||||
Toggle to edit your dummy settings
|
||||
==
|
||||
== Växla för att ändra dina dummy inställningar
|
||||
|
||||
Randomize
|
||||
==
|
||||
== Slumpa
|
||||
|
||||
Are you sure that you want to delete '%s'?
|
||||
==
|
||||
== Är du säker att du vill ta bort '%s'?
|
||||
|
||||
Delete skin
|
||||
==
|
||||
== Ta bort skin
|
||||
|
||||
Basic
|
||||
==
|
||||
== Enkel
|
||||
|
||||
Custom
|
||||
==
|
||||
== Anpassa
|
||||
|
||||
Unable to delete skin
|
||||
==
|
||||
== Kunde inte ta bort skin
|
||||
|
||||
Customize
|
||||
==
|
||||
== Ändra
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#modified by:
|
||||
# 404_not_found 2011-07-30 19:50:58
|
||||
# EGYT5453 (15.05.2024-04.06.2024)
|
||||
# veydzh3r (31.08.2024-01.09.2024)
|
||||
##### /authors #####
|
||||
|
||||
##### translated strings #####
|
||||
|
@ -32,10 +33,10 @@
|
|||
== ще %d…
|
||||
|
||||
%d player
|
||||
== Гравців: %d
|
||||
== Гравці: %d
|
||||
|
||||
%d players
|
||||
== Гравців: %d
|
||||
== Гравці: %d
|
||||
|
||||
[Demo player duration]
|
||||
%d sec.
|
||||
|
@ -76,10 +77,10 @@
|
|||
== Нових згадок: 9+
|
||||
|
||||
A demo with this name already exists
|
||||
== Демо з цією назвою вже існує
|
||||
== Демо з цією назвою уже існує
|
||||
|
||||
A folder with this name already exists
|
||||
== Тека з цією назвою вже існує
|
||||
== Тека з цією назвою уже існує
|
||||
|
||||
[Graphics error]
|
||||
A render command failed. Try to update your GPU drivers.
|
||||
|
@ -120,7 +121,7 @@ AFR
|
|||
== АФР
|
||||
|
||||
Aim bind
|
||||
== Прив'язка
|
||||
== Прив’язка
|
||||
|
||||
All
|
||||
== Усі
|
||||
|
@ -175,13 +176,13 @@ Are you sure that you want to delete the folder '%s'?
|
|||
== Ви дійсно хочете видалити теку '%s'?
|
||||
|
||||
Are you sure that you want to disconnect?
|
||||
== Ви дійсно хочете від'єднатися?
|
||||
== Ви дійсно хочете від’єднатися?
|
||||
|
||||
Are you sure that you want to disconnect and switch to a different server?
|
||||
== Ви дійсно хочете від'єднатися й приєднатися до іншого сервера?
|
||||
== Ви дійсно хочете від’єднатися й приєднатися до іншого сервера?
|
||||
|
||||
Are you sure that you want to disconnect your dummy?
|
||||
== Ви дійсно хочете від'єднати свого даммі?
|
||||
== Ви дійсно хочете від’єднати свого даммі?
|
||||
|
||||
Are you sure that you want to quit?
|
||||
== Ви дійсно хочете вийти?
|
||||
|
@ -193,7 +194,7 @@ Are you sure that you want to remove the player '%s' from your friends list?
|
|||
== Ви дійсно хочете прибрати гравця '%s' зі списку друзів?
|
||||
|
||||
Are you sure that you want to reset the controls to their defaults?
|
||||
== Ви дійсно хочете скинути налаштування керування до значень за замовчуванням?
|
||||
== Ви дійсно хочете скинути налаштування керувань до початкових значень?
|
||||
|
||||
Are you sure that you want to restart?
|
||||
== Ви дійсно хочете перезапустити?
|
||||
|
@ -211,7 +212,7 @@ AUS
|
|||
== АВС
|
||||
|
||||
Authed name color in scoreboard
|
||||
== Колір авторизованих у таблі
|
||||
== Колір авторизованих у таблиці
|
||||
|
||||
Auto
|
||||
== Авто
|
||||
|
@ -220,7 +221,7 @@ auto
|
|||
== автоматично
|
||||
|
||||
Automatically create statboard csv
|
||||
== Автоматично зберігати статистику у CSV-файл
|
||||
== Автоматично зберігати статистику у файл CSV
|
||||
|
||||
Automatically record demos
|
||||
== Автоматично записувати демо
|
||||
|
@ -235,7 +236,7 @@ Axis
|
|||
== Осі
|
||||
|
||||
Background
|
||||
== Фон
|
||||
== Тло
|
||||
|
||||
Background music volume
|
||||
== Гучність фонової музики
|
||||
|
@ -262,7 +263,7 @@ Call vote
|
|||
== Голосувати
|
||||
|
||||
Can't find a Tutorial server
|
||||
== Не вдалося знайти сервер-посібник
|
||||
== Не вдається знайти навчальний сервер
|
||||
|
||||
Cancel
|
||||
== Скасувати
|
||||
|
@ -295,13 +296,13 @@ Check now
|
|||
== Перевірити
|
||||
|
||||
Checking for existing player with your name
|
||||
== Перевіряємо Ваш нікнейм на доступність
|
||||
== Перевірка на наявність гравця з вашим ім’ям
|
||||
|
||||
CHN
|
||||
== КИТ
|
||||
|
||||
Choose default eyes when joining a server
|
||||
== Очі, які відображатимуться за замовчуванням
|
||||
== Типові очі під час приєднання до сервера
|
||||
|
||||
Clan
|
||||
== Клан
|
||||
|
@ -328,7 +329,7 @@ Close the demo player
|
|||
== Закрити програвач демо
|
||||
|
||||
Colors of the hook collision line, in case of a possible collision with:
|
||||
== Кольори лінії зіткнення гака, якщо він може зіштовхнутися з:
|
||||
== Кольори лінії зіткнення гака, в разі можливого зіткнення з:
|
||||
|
||||
Communities
|
||||
== Спільноти
|
||||
|
@ -337,28 +338,28 @@ Config directory
|
|||
== Тека налаштувань
|
||||
|
||||
Connect address error
|
||||
== Помилка адреси з'єднання
|
||||
== Помилка адреси з’єднання
|
||||
|
||||
Connect Dummy
|
||||
== Приєднати даммі
|
||||
== Під’єднати даммі
|
||||
|
||||
Connected
|
||||
== Приєднано
|
||||
== Під’єднано
|
||||
|
||||
Connecting dummy
|
||||
== Приєднуємо даммі
|
||||
== Під’єднання даммі
|
||||
|
||||
Connecting to
|
||||
== Приєднуємося до
|
||||
== Під’єднання до
|
||||
|
||||
Connection Problems…
|
||||
== Проблеми зі з'єднанням…
|
||||
== Проблеми зі з’єднанням…
|
||||
|
||||
Console
|
||||
== Консоль
|
||||
|
||||
Continue anyway?
|
||||
== Все одно продовжити?
|
||||
== Усе одно продовжити?
|
||||
|
||||
Controller
|
||||
== Контролер
|
||||
|
@ -376,24 +377,24 @@ Copy info
|
|||
== Скопіювати
|
||||
|
||||
Could not connect dummy
|
||||
== Не вдалося під'єднати даммі
|
||||
== Не вдалося під’єднати даммі
|
||||
|
||||
[Graphics error]
|
||||
Could not initialize the given graphics backend, reverting to the default backend now.
|
||||
== Не вдалося ініціалізувати заданий графічний рушій, повертаємося до рушія за замовчуванням.
|
||||
== Не вдалося ініціалізувати заданий графічний рушій, повернення до тпового рушія.
|
||||
|
||||
[Graphics error]
|
||||
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card.
|
||||
== Не вдалося ініціалізувати заданий графічний рушій, можливо тому що ви не встановили драйвери на вбудовану відеокарту.
|
||||
== Не вдалося ініціалізувати заданий графічний рушій, імовірно, ви не встановили драйвери на вбудовану відеокарту.
|
||||
|
||||
Could not resolve connect address '%s'. See local console for details.
|
||||
== Не вдалося визначити адресу з'єднання '%s' (див. локальну консоль).
|
||||
== Не вдалося визначити адресу з’єднання '%s'. Див. локальну консоль для подробиць.
|
||||
|
||||
Could not save downloaded map. Try manually deleting this file: %s
|
||||
== Не вдалося зберегти завантажену мапу. Спробуйте самостійно видалити цей файл: %s
|
||||
|
||||
Count players only
|
||||
== Рахувати тільки гравців
|
||||
== Рахувати лише гравців
|
||||
|
||||
Countries
|
||||
== Країни
|
||||
|
@ -417,7 +418,7 @@ Custom colors
|
|||
== Власні кольори
|
||||
|
||||
Customize
|
||||
== Налаштування
|
||||
== Налаштувати
|
||||
|
||||
Cut interval
|
||||
== Інтервал
|
||||
|
@ -438,7 +439,7 @@ DDNet %s is out!
|
|||
== Вийшов DDNet %s!
|
||||
|
||||
DDNet Client needs to be restarted to complete update!
|
||||
== Потрібно перезапустити клієнт DDNet, щоб завершити оновлення!
|
||||
== Потрібно перезапустити клієнт DDNet для завершення оновлення!
|
||||
|
||||
DDNet Client updated!
|
||||
== Клієнт DDNet оновлено!
|
||||
|
@ -447,22 +448,22 @@ DDRace HUD
|
|||
== HUD DDRace
|
||||
|
||||
DDraceNetwork is a cooperative online game where the goal is for you and your group of tees to reach the finish line of the map. As a newcomer you should start on Novice servers, which host the easiest maps. Consider the ping to choose a server close to you.
|
||||
== DDraceNetwork — кооперативна мережева гра, ціль якої — дістатися разом зі своєю групою тії до фінішної прямої. Новачкам варто почати із серверів "Для новачків" (Novice), на яких є найпростіші мапи. Зважайте на затримку, коли вибираєте сервер.
|
||||
== DDraceNetwork — кооперативна мережева гра, ціль якої — дістатися разом зі своєю групою тії до фінішної прямої. Новачкам варто почати із серверів «Для новачків» (Novice), на яких є найпростіші мапи. Зважайте на затримку, коли вибираєте сервер.
|
||||
|
||||
Deactivate
|
||||
== Деактивувати
|
||||
|
||||
Deactivate all
|
||||
== Деактивувати усіх
|
||||
== Деактивувати всіх
|
||||
|
||||
Deaths
|
||||
== С
|
||||
== Смерті
|
||||
|
||||
Debug mode enabled. Press Ctrl+Shift+D to disable debug mode.
|
||||
== Увімкнено режим налагодження. Натисніть Ctrl+Shift+D, щоб його вимкнути.
|
||||
|
||||
default
|
||||
== за замовчуванням
|
||||
== типово
|
||||
|
||||
Default length
|
||||
== Звичайна тривалість
|
||||
|
@ -495,16 +496,16 @@ Demos directory
|
|||
== Тека демо
|
||||
|
||||
Desktop fullscreen
|
||||
== Стільничний повноекранний
|
||||
== Робочий стіл на весь екран
|
||||
|
||||
Disconnect
|
||||
== Від'єднатися
|
||||
== Від’єднатися
|
||||
|
||||
Disconnect Dummy
|
||||
== Від'єднати даммі
|
||||
== Від’єднати даммі
|
||||
|
||||
Disconnected
|
||||
== Від'єднано
|
||||
== Від’єднано
|
||||
|
||||
Discord
|
||||
== Discord
|
||||
|
@ -522,10 +523,10 @@ Download skins
|
|||
== Завантажувати скіни
|
||||
|
||||
Downloading %s:
|
||||
== Завантажуємо %s:
|
||||
== Завантаження %s:
|
||||
|
||||
Downloading map
|
||||
== Завантажуємо мапу
|
||||
== Завантаження мапи
|
||||
|
||||
Draw!
|
||||
== Нічия!
|
||||
|
@ -543,7 +544,7 @@ Dummy settings
|
|||
== Налаштування даммі
|
||||
|
||||
Dynamic Camera
|
||||
== Рухома камера
|
||||
== Динамічна камера
|
||||
|
||||
Editor
|
||||
== Редактор
|
||||
|
@ -576,7 +577,7 @@ Enable regular chat sound
|
|||
== Звук звичайного повідомлення
|
||||
|
||||
Enable replays
|
||||
== Повтори
|
||||
== Увімкнути повтори
|
||||
|
||||
Enable server message sound
|
||||
== Звук повідомлення сервера
|
||||
|
@ -588,13 +589,13 @@ Enter Password
|
|||
== Введіть пароль
|
||||
|
||||
Enter Username
|
||||
== Введіть логін
|
||||
== Введіть ім’я користувача
|
||||
|
||||
Entities
|
||||
== Сутності
|
||||
|
||||
Entities background color
|
||||
== Колір фону сутностей
|
||||
== Колір тла сутностей
|
||||
|
||||
Error
|
||||
== Помилка
|
||||
|
@ -612,13 +613,13 @@ EUR
|
|||
== ЄВР
|
||||
|
||||
Example of usage
|
||||
== Наприклад
|
||||
== Приклад використання
|
||||
|
||||
Exclude
|
||||
== Виключити
|
||||
|
||||
Existing Player
|
||||
== Гравець вже існує
|
||||
== Гравець уже існує
|
||||
|
||||
Export cut as a separate demo
|
||||
== Експортувати фрагмент як окреме демо
|
||||
|
@ -681,7 +682,7 @@ Following
|
|||
|
||||
[Spectating]
|
||||
Following %s
|
||||
== Слідуємо за %s
|
||||
== Cпостерігання за %s
|
||||
|
||||
Force vote
|
||||
== Форсувати
|
||||
|
@ -711,7 +712,7 @@ FSAA samples
|
|||
== Вибірка FSAA
|
||||
|
||||
Fullscreen
|
||||
== Повноекранний
|
||||
== На весь екран
|
||||
|
||||
Game
|
||||
== Гра
|
||||
|
@ -738,13 +739,13 @@ Gameplay
|
|||
== Ігролад
|
||||
|
||||
General
|
||||
== Загальне
|
||||
== Загальні
|
||||
|
||||
Getting game info
|
||||
== Отримуємо інформацію про гру
|
||||
== Отримання інформації про гру
|
||||
|
||||
Getting server list from master server
|
||||
== Отримуємо список серверів з головного сервера
|
||||
== Отримання списку серверів з головного сервера
|
||||
|
||||
Ghost
|
||||
== Привид
|
||||
|
@ -756,7 +757,7 @@ Go back one marker
|
|||
== Перемотати до попередньої мітки
|
||||
|
||||
Go back one tick
|
||||
== Перемотати вперед на один тік
|
||||
== Перемотати вперед на один такт
|
||||
|
||||
Go back the specified duration
|
||||
== Перемотати назад
|
||||
|
@ -765,7 +766,7 @@ Go forward one marker
|
|||
== Перемотати до наступної мітки
|
||||
|
||||
Go forward one tick
|
||||
== Перемотати назад на один тік
|
||||
== Перемотати вперед на один такт
|
||||
|
||||
Go forward the specified duration
|
||||
== Перемотати вперед
|
||||
|
@ -831,31 +832,31 @@ HUD
|
|||
== HUD
|
||||
|
||||
Hue
|
||||
== Тон
|
||||
== Відтінок
|
||||
|
||||
Indicate map finish
|
||||
== Позначати пройдені мапи
|
||||
|
||||
Info Messages
|
||||
== Інфо-повідомлення
|
||||
== Інфо. повідомлення
|
||||
|
||||
Ingame controller mode
|
||||
== Режим контролера у грі
|
||||
|
||||
Ingame controller sens.
|
||||
== Чутл. у грі
|
||||
== Чутливість у грі
|
||||
|
||||
Ingame mouse sens.
|
||||
== Чутл. у грі
|
||||
== Чутливість у грі
|
||||
|
||||
Initializing assets
|
||||
== Ініціалізуємо текстури
|
||||
== Ініціалізація текстур
|
||||
|
||||
Initializing components
|
||||
== Ініціалізуємо компоненти
|
||||
== Ініціалізація компонентів
|
||||
|
||||
Initializing map logic
|
||||
== Ініціалізуємо логіку мапи
|
||||
== Ініціалізація логіки мапи
|
||||
|
||||
Internet
|
||||
== Інтернет
|
||||
|
@ -864,7 +865,7 @@ Invalid Demo
|
|||
== Недійсне демо
|
||||
|
||||
It's recommended that you check the settings to adjust them to your liking before joining a server.
|
||||
== Перед тим як приєднатися до сервера, рекомендуємо змінити налаштування до ваших уподобань.
|
||||
== Перед тим, як приєднатися до сервера, рекомендуємо змінити налаштування до ваших уподобань.
|
||||
|
||||
Join blue
|
||||
== До синіх
|
||||
|
@ -876,7 +877,7 @@ Join red
|
|||
== До червоних
|
||||
|
||||
Join Tutorial Server
|
||||
== Приєднатися до сервера-посібника
|
||||
== Приєднатися до навчального сервера
|
||||
|
||||
Jump
|
||||
== Стрибок
|
||||
|
@ -915,52 +916,52 @@ Loading…
|
|||
== Завантаження…
|
||||
|
||||
Loading assets
|
||||
== Завантажуємо текстури
|
||||
== Завантаження текстур
|
||||
|
||||
Loading commands…
|
||||
== Завантажуємо команди…
|
||||
== Завантаження команд…
|
||||
|
||||
Loading DDNet Client
|
||||
== Завантажуємо клієнт DDNet
|
||||
== Завантаження клієнта DDNet
|
||||
|
||||
Loading demo file from storage
|
||||
== Завантажуємо демо-файл зі сховища
|
||||
== Завантаження демо-файлу зі сховища
|
||||
|
||||
Loading demo files
|
||||
== Завантажуємо демо-файли
|
||||
== Завантаження демо-файлів
|
||||
|
||||
Loading ghost files
|
||||
== Завантажуємо файли привида
|
||||
== Завантаження файлів привида
|
||||
|
||||
Loading map file from storage
|
||||
== Завантажуємо файл мапи зі сховища
|
||||
== Завантаження файлу мапи зі сховища
|
||||
|
||||
Loading menu images
|
||||
== Завантажуємо зображення меню
|
||||
== Завантаження зображень меню
|
||||
|
||||
Loading menu themes
|
||||
== Завантажуємо теми меню
|
||||
== Завантаження тем меню
|
||||
|
||||
Loading race demo files
|
||||
== Завантажуємо демо-файли забігів
|
||||
== Завантаження демо-файлів забігів
|
||||
|
||||
Loading skin files
|
||||
== Завантажуємо файли скінів
|
||||
== Завантаження файлів скінів
|
||||
|
||||
Loading sound files
|
||||
== Завантажуємо звукові файли
|
||||
== Завантаження звукових файлів
|
||||
|
||||
Lock team
|
||||
== Замкнути команду
|
||||
|
||||
Locked
|
||||
== Заблоковано
|
||||
== Замкнено
|
||||
|
||||
Main menu
|
||||
== Головне меню
|
||||
|
||||
Manual
|
||||
== Вручну
|
||||
== Уручну
|
||||
|
||||
Map
|
||||
== Мапа
|
||||
|
@ -970,7 +971,7 @@ map not included
|
|||
== мапу не включено
|
||||
|
||||
Map sound volume
|
||||
== Гучність мапи
|
||||
== Гучність звуків мапи
|
||||
|
||||
Mark the beginning of a cut (right click to reset)
|
||||
== Позначити початок фрагмента (права кнопка миші, щоб скинути)
|
||||
|
@ -985,13 +986,13 @@ Match %d of %d
|
|||
== Збіг %d з %d
|
||||
|
||||
Max CSVs
|
||||
== Найбільше CSV-файлів
|
||||
== Макс. кількість файлів CSV
|
||||
|
||||
Max demos
|
||||
== Найбільше демо-файлів
|
||||
== Макс. кількість демо-файлів
|
||||
|
||||
Max Screenshots
|
||||
== Найбільше знімків екрана
|
||||
== Макс. кількість знімків екрана
|
||||
|
||||
may cause delay
|
||||
== може спричинити затримку
|
||||
|
@ -1015,7 +1016,7 @@ Move left
|
|||
== Вліво
|
||||
|
||||
Move player to spectators
|
||||
== Зробити гравця спостерігачем
|
||||
== Зробити гравця глядачем
|
||||
|
||||
Move right
|
||||
== Вправо
|
||||
|
@ -1030,7 +1031,7 @@ Multi-View
|
|||
== Мульти-камера
|
||||
|
||||
Mute when not active
|
||||
== Приглушувати, якщо вікно неактивне
|
||||
== Приглушувати звук поза грою
|
||||
|
||||
NA
|
||||
== ПНА
|
||||
|
@ -1051,7 +1052,7 @@ Netversion
|
|||
== Версія
|
||||
|
||||
New name:
|
||||
== Нова назва
|
||||
== Нова назва:
|
||||
|
||||
New random timeout code
|
||||
== Новий випадковий код тайм-ауту
|
||||
|
@ -1063,16 +1064,16 @@ Next weapon
|
|||
== Наст. зброя
|
||||
|
||||
Nickname
|
||||
== Нікнейм
|
||||
== Псевдонім
|
||||
|
||||
No
|
||||
== Ні
|
||||
|
||||
No answer from server yet.
|
||||
== Сервер ще не відповів.
|
||||
== Поки що немає відповіді від сервера.
|
||||
|
||||
No controller found. Plug in a controller.
|
||||
== Жодного контролера не знайдено. Підключіть контролер.
|
||||
== Жодного контролера не знайдено. Під’єднайте контролер.
|
||||
|
||||
No demo selected
|
||||
== Жодного демо не вибрано
|
||||
|
@ -1111,23 +1112,23 @@ Normal message
|
|||
== Звичайні повідомлення
|
||||
|
||||
NOT CONNECTED
|
||||
== НЕ ПРИЄДНАНО
|
||||
== НЕ ПІД’ЄДНАНО
|
||||
|
||||
Nothing hookable
|
||||
== нічим, за що можна зачепитися
|
||||
|
||||
[friends (server browser)]
|
||||
Offline (%d)
|
||||
== Офлайн (%d)
|
||||
== Не в мережі (%d)
|
||||
|
||||
Ok
|
||||
== Гаразд
|
||||
|
||||
Online clanmates (%d)
|
||||
== Онлайн клановці (%d)
|
||||
== Співклановці в мережі (%d)
|
||||
|
||||
Online players (%d)
|
||||
== Онлайн гравці (%d)
|
||||
== Гравці в мережі (%d)
|
||||
|
||||
Only save improvements
|
||||
== Зберігати лише покращення
|
||||
|
@ -1158,7 +1159,7 @@ Open the settings file
|
|||
|
||||
[Graphics error]
|
||||
Out of VRAM. Try removing custom assets (skins, entities, etc.), especially those with high resolution.
|
||||
== Недостатньо відеопам'яті. Спробуйте видалити власні текстури (скіни, сутності і т.п.), особливо ті, що мають високу роздільну здатність.
|
||||
== Недостатньо відеопам’яті. Спробуйте видалити власні текстури (скіни, сутності і т.д.), особливо ті, що мають високу роздільність.
|
||||
|
||||
Overlay entities
|
||||
== Накладати сутності
|
||||
|
@ -1167,7 +1168,7 @@ Parent Folder
|
|||
== Батьківська тека
|
||||
|
||||
Particles
|
||||
== Часточки
|
||||
== Частинки
|
||||
|
||||
Pause
|
||||
== Пауза
|
||||
|
@ -1228,7 +1229,7 @@ Position:
|
|||
== Позиція:
|
||||
|
||||
Preparing demo playback
|
||||
== Підготовлюємо відтворення демо
|
||||
== Підготовлення відтворення демо
|
||||
|
||||
Press a key…
|
||||
== Натисніть клавішу…
|
||||
|
@ -1243,7 +1244,7 @@ Quads are used for background decoration
|
|||
== Квади використовуються для декорацій
|
||||
|
||||
Quit
|
||||
== Вихід
|
||||
== Вийти
|
||||
|
||||
Quitting. Please wait…
|
||||
== Вихід. Будь ласка, зачекайте…
|
||||
|
@ -1252,7 +1253,7 @@ Race
|
|||
== Забіг
|
||||
|
||||
Randomize
|
||||
== Випадково
|
||||
== Навмання
|
||||
|
||||
Ratio
|
||||
== У/С
|
||||
|
@ -1261,7 +1262,7 @@ Reason:
|
|||
== Причина:
|
||||
|
||||
Reconnect in %d sec
|
||||
== Переприєднання через %dс
|
||||
== Повторне під’єднання за %dс
|
||||
|
||||
Record demo
|
||||
== Запис демо
|
||||
|
@ -1276,7 +1277,7 @@ Refresh Rate
|
|||
== Частота кадрів
|
||||
|
||||
Regular background color
|
||||
== Колір звичайного фону
|
||||
== Колір звичайного тла
|
||||
|
||||
[Ingame controller mode]
|
||||
Relative
|
||||
|
@ -1319,10 +1320,10 @@ Replay
|
|||
== Повтор
|
||||
|
||||
Replay feature is disabled!
|
||||
== Повтори відключено!
|
||||
== Повтори вимкнено!
|
||||
|
||||
Requesting to join the game
|
||||
== Запитуємо приєднання до гри
|
||||
== Запит на приєднання до гри
|
||||
|
||||
Reset
|
||||
== Скинути
|
||||
|
@ -1334,7 +1335,7 @@ Reset filter
|
|||
== Скинути фільтр
|
||||
|
||||
Reset to defaults
|
||||
== Скинути
|
||||
== Скинути до типових
|
||||
|
||||
Restart
|
||||
== Перезапустити
|
||||
|
@ -1361,7 +1362,7 @@ SA
|
|||
== ПДА
|
||||
|
||||
Same clan color in scoreboard
|
||||
== Колір співклановців у таблі
|
||||
== Колір співклановців у таблиці
|
||||
|
||||
Sat.
|
||||
== Насич.
|
||||
|
@ -1373,7 +1374,7 @@ Save ghost
|
|||
== Зберігати привида
|
||||
|
||||
Save power by lowering refresh rate (higher input latency)
|
||||
== Зберігати енергію зниженням частоти кадрів (вища затримка вводу)
|
||||
== Економити енергію шляхом зниження частоти кадрів (вища затримка введення)
|
||||
|
||||
Save the best demo of each race
|
||||
== Зберігати найкраще демо кожного забігу
|
||||
|
@ -1388,7 +1389,7 @@ Score limit
|
|||
== Гра до
|
||||
|
||||
Scoreboard
|
||||
== Табло
|
||||
== Таблиця
|
||||
|
||||
Screen
|
||||
== Екран
|
||||
|
@ -1400,10 +1401,10 @@ Search
|
|||
== Пошук
|
||||
|
||||
Searching
|
||||
== Шукаємо
|
||||
== Пошук
|
||||
|
||||
Sending initial client info
|
||||
== Надсилаємо початкові дані клієнта
|
||||
== Надсилання початкових даних клієнта
|
||||
|
||||
Server address:
|
||||
== Адреса сервера:
|
||||
|
@ -1424,7 +1425,7 @@ Server not full
|
|||
== Неповний сервер
|
||||
|
||||
Set all to Rifle
|
||||
== Встановити так, як у гвинтівки
|
||||
== Установити так, як у гвинтівки
|
||||
|
||||
Settings
|
||||
== Налаштування
|
||||
|
@ -1451,7 +1452,7 @@ Show clan above name plates
|
|||
== Показувати клан над ніками
|
||||
|
||||
Show client IDs (scoreboard, chat, spectator)
|
||||
== Показувати ID клієнта (табло, чат, спостерігачі)
|
||||
== Показувати ID клієнта (таблиця, чат, глядачі)
|
||||
|
||||
Show DDNet map finishes in server browser
|
||||
== Показувати пройдені мапи DDNet у браузері серверів
|
||||
|
@ -1463,7 +1464,7 @@ Show dummy actions
|
|||
== Показувати дії з даммі
|
||||
|
||||
Show entities
|
||||
== Сутності
|
||||
== Показ сутностей
|
||||
|
||||
Show finish messages
|
||||
== Показувати повідомлення про фініші
|
||||
|
@ -1472,7 +1473,7 @@ Show freeze bars
|
|||
== Показувати смугу заморозки
|
||||
|
||||
Show friend mark (♥) in name plates
|
||||
== Показувати позначку друга (♥) біля ніків
|
||||
== Показувати позначку друга (♥) біля псевдонімів
|
||||
|
||||
Show friends only
|
||||
== Показувати лише з друзями
|
||||
|
@ -1481,10 +1482,10 @@ Show ghost
|
|||
== Показувати привида
|
||||
|
||||
Show health, shields and ammo
|
||||
== Показувати здоров'я, щити й набої
|
||||
== Показувати здоров’я, захист і набої
|
||||
|
||||
Show hook strength icon indicator
|
||||
== Показувати графічний індикатор сили гака
|
||||
== Показувати іконку індикатора сили гака
|
||||
|
||||
Show hook strength number indicator
|
||||
== Показувати числовий індикатор сили гака
|
||||
|
@ -1508,10 +1509,10 @@ Show local time always
|
|||
== Завжди показувати місцевий час
|
||||
|
||||
Show name plates
|
||||
== Показувати ніки
|
||||
== Показувати псевдоніми
|
||||
|
||||
Show names in chat in team colors
|
||||
== Фарбувати ніки в чаті в кольори команд
|
||||
== Показувати імена в чаті в кольорах команди
|
||||
|
||||
Show only chat messages from friends
|
||||
== Показувати лише повідомлення від друзів
|
||||
|
@ -1550,7 +1551,7 @@ Show text entities
|
|||
== Текстові сутності
|
||||
|
||||
Show tiles layers from BG map
|
||||
== Показувати тайли з мапи фону
|
||||
== Показувати плитки з мапи фону
|
||||
|
||||
Show quads
|
||||
== Показувати квади
|
||||
|
@ -1580,13 +1581,13 @@ Slow down the demo
|
|||
== Сповільнити
|
||||
|
||||
Smooth Dynamic Camera
|
||||
== Гладка рухома камера
|
||||
== Гладка динамічна камера
|
||||
|
||||
Some map images could not be loaded. Check the local console for details.
|
||||
== Деякі зображення мапи не завантажилися. Деталі у локальній консолі.
|
||||
== Деякі зображення мапи не завантажилися. Див. локальну консоль для подробиць.
|
||||
|
||||
Some map sounds could not be loaded. Check the local console for details.
|
||||
== Деякі звуки мапи не завантажилися. Деталі у локальній консолі.
|
||||
== Деякі звуки мапи не завантажилися. Див. локальну консоль для подробиць.
|
||||
|
||||
Something hookable
|
||||
== чимось, за що можна зачепитися
|
||||
|
@ -1610,10 +1611,10 @@ Spectate previous
|
|||
== Попер. гравець
|
||||
|
||||
Spectator mode
|
||||
== Режим спостерігача
|
||||
== Режим глядача
|
||||
|
||||
Spectators
|
||||
== Спостерігачі
|
||||
== Глядачі
|
||||
|
||||
Speed
|
||||
== Швидкість
|
||||
|
@ -1665,7 +1666,7 @@ Switch weapon on pickup
|
|||
== Змінювати зброю при підхопленні
|
||||
|
||||
Switch weapon when out of ammo
|
||||
== Змінювати зброю коли закінчуються набої
|
||||
== Змінювати зброю при закінченні набоїв
|
||||
|
||||
System message
|
||||
== Повідомлення системи
|
||||
|
@ -1731,7 +1732,7 @@ Toggle ghost
|
|||
== Привид
|
||||
|
||||
Toggle keyboard shortcuts
|
||||
== Перемкнути скорочення
|
||||
== Перемкнути сполучення
|
||||
|
||||
Toggle to edit your dummy settings
|
||||
== Перемкніть, щоб змінити налаштування даммі
|
||||
|
@ -1746,7 +1747,7 @@ Try again
|
|||
== Спробувати ще раз
|
||||
|
||||
Trying to determine UDP connectivity…
|
||||
== Намагаємося визначити UDP-з'єднання…
|
||||
== Спроба визначити UDP-з’єднання…
|
||||
|
||||
Tutorial
|
||||
== Посібник
|
||||
|
@ -1767,10 +1768,10 @@ UI Color
|
|||
== Колір інтерфейсу
|
||||
|
||||
UI controller sens.
|
||||
== Чутл. у інтерфейсі
|
||||
== Чутл. в інтерфейсі
|
||||
|
||||
UI mouse sens.
|
||||
== Чутл. у інтерфейсі
|
||||
== Чутл. в інтерфейсі
|
||||
|
||||
Unable to delete skin
|
||||
== Не вдалося видалити скін
|
||||
|
@ -1798,25 +1799,25 @@ Unregister protocol and file extensions
|
|||
== Розреєструвати протокол і розширення файлів
|
||||
|
||||
Update failed! Check log…
|
||||
== Оновлення не вдалося! Перевірте журнал…
|
||||
== Помилка оновлення! Перевірте журнал…
|
||||
|
||||
Update now
|
||||
== Оновити
|
||||
|
||||
Updating…
|
||||
== Оновлюємо…
|
||||
== Оновлення…
|
||||
|
||||
Uploading map data to GPU
|
||||
== Вивантажуємо дані мапи до відеокарти
|
||||
== Вивантаження даних мапи до відеокарти
|
||||
|
||||
Use current map as background
|
||||
== Використовувати поточну мапу як фон
|
||||
== Використовувати поточну мапу як тло
|
||||
|
||||
Use high DPI
|
||||
== Високий DPI
|
||||
|
||||
Use k key to kill (restart), q to pause and watch other players. See settings for other key binds.
|
||||
== Натисніть "k", щоб умерти (почати спочатку), "q", щоб спостерігати за іншими гравцями. Інші скорочення дивіться у налаштуваннях.
|
||||
== Натисніть «k», щоб умерти (почати спочатку), «q», щоб спостерігати за іншими гравцями. Інші призначення клавіш дивіться у налаштуваннях.
|
||||
|
||||
Use old chat style
|
||||
== Старий стиль чату
|
||||
|
@ -1840,7 +1841,7 @@ Video name:
|
|||
== Назва відео:
|
||||
|
||||
Video was saved to '%s'
|
||||
== Відео було збережено до '%s'
|
||||
== Відео збережено до '%s'
|
||||
|
||||
Videos directory
|
||||
== Тека відео
|
||||
|
@ -1885,13 +1886,13 @@ Why are you slowmo replaying to read this?
|
|||
== Чому ви переглядаєте це у повторі?
|
||||
|
||||
Windowed
|
||||
== Віконний
|
||||
== У вікні
|
||||
|
||||
Windowed borderless
|
||||
== Віконний без рамок
|
||||
== Вікно без рамок
|
||||
|
||||
Windowed fullscreen
|
||||
== Віконний повноекранний
|
||||
== Вікно на весь екран
|
||||
|
||||
Yes
|
||||
== Так
|
||||
|
@ -1900,10 +1901,10 @@ Your movements are not taken into account when calculating the line colors
|
|||
== Ваші рухи не враховуються при розранку кольору лінії
|
||||
|
||||
You must restart the game for all settings to take effect.
|
||||
== Щоб налаштування набрали чинності, перезапустіть гру.
|
||||
== Щоб налаштування набули чинності, перезапустіть гру.
|
||||
|
||||
Your nickname '%s' is already used (%d points). Do you still want to use it?
|
||||
== Нікнейм '%s' вже зайнято (%d балів). Все ще хочете використовувати його?
|
||||
== Ваш псевдонім «%s» вже зайнято (%d балів). Усе ще хочете використовувати його?
|
||||
|
||||
Your skin
|
||||
== Ваш скін
|
||||
|
|
|
@ -10,7 +10,8 @@ ANDROID_NDK_VERSION="${ANDROID_NDK_VERSION:2}"
|
|||
# ANDROID_NDK_HOME must be exported for cargo-ndk
|
||||
export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/$ANDROID_NDK_VERSION"
|
||||
|
||||
ANDROID_API_LEVEL=34
|
||||
# ANDROID_API_LEVEL must specify the _minimum_ supported SDK version, otherwise this will cause linking errors at launch
|
||||
ANDROID_API_LEVEL=24
|
||||
ANDROID_SUB_BUILD_DIR=build_arch
|
||||
|
||||
COLOR_RED="\e[1;31m"
|
||||
|
|
|
@ -1,15 +1,40 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:installLocation="auto">
|
||||
|
||||
<!-- Vulkan 1.1.0 is used if supported -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.vulkan.version"
|
||||
android:required="false"
|
||||
android:version="0x00401000" />
|
||||
<!-- android:glEsVersion is not specified as OpenGL ES 1.0 is supported as fallback -->
|
||||
|
||||
<!-- Only playable in landscape mode -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.screen.landscape"
|
||||
android:required="true" />
|
||||
|
||||
<!-- Touchscreen support -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
|
||||
<!-- Game controller support -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.gamepad"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.usb.host"
|
||||
android:required="false" />
|
||||
|
||||
<!-- External mouse input events -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.type.pc"
|
||||
android:required="false" />
|
||||
|
||||
<!-- Teeworlds does broadcasts over local networks -->
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
|
||||
|
@ -25,17 +50,24 @@
|
|||
android:isGame="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
||||
android:hardwareAccelerated="true">
|
||||
<activity
|
||||
android:name="org.ddnet.client.NativeMain"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
|
||||
android:configChanges="layoutDirection|locale|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||
android:preferMinimalPostProcessing="true"
|
||||
android:screenOrientation="landscape"
|
||||
android:launchMode="singleInstance">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<!-- Let Android know that we can handle some USB devices and should receive this event -->
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="DDNet" />
|
||||
|
|
|
@ -21,7 +21,7 @@ android {
|
|||
defaultConfig {
|
||||
applicationId "org.ddnet.client"
|
||||
namespace("org.ddnet.client")
|
||||
minSdkVersion 19
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 34
|
||||
versionCode TW_VERSION_CODE
|
||||
versionName "TW_VERSION_NAME"
|
||||
|
|
|
@ -29,6 +29,8 @@ function compile_source_android() {
|
|||
-DCMAKE_SYSTEM_NAME=Android \
|
||||
-DCMAKE_SYSTEM_VERSION="$1" \
|
||||
-DCMAKE_ANDROID_ARCH_ABI="${3}" \
|
||||
-DCMAKE_C_FLAGS="$COMPILEFLAGS" -DCMAKE_CXX_FLAGS="$COMPILEFLAGS" -DCMAKE_CXX_FLAGS_RELEASE="$COMPILEFLAGS" -DCMAKE_C_FLAGS_RELEASE="$COMPILEFLAGS" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="$LINKFLAGS" -DCMAKE_SHARED_LINKER_FLAGS_RELEASE="$LINKFLAGS" \
|
||||
-B"$2" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DHIDAPI_SKIP_LIBUSB=TRUE \
|
||||
|
|
|
@ -46,7 +46,8 @@ fi
|
|||
mkdir -p "$1"
|
||||
cd "$1" || exit 1
|
||||
|
||||
_ANDROID_ABI_LEVEL=34
|
||||
# ANDROID_API_LEVEL must specify the _minimum_ supported SDK version, otherwise this will cause linking errors at launch
|
||||
ANDROID_API_LEVEL=24
|
||||
|
||||
function build_cmake_lib() {
|
||||
if [ ! -d "${1}" ]; then
|
||||
|
@ -59,7 +60,7 @@ function build_cmake_lib() {
|
|||
(
|
||||
cd "${1}" || exit 1
|
||||
cp "${CURDIR}"/scripts/compile_libs/cmake_lib_compile.sh cmake_lib_compile.sh
|
||||
./cmake_lib_compile.sh "$_ANDROID_ABI_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS"
|
||||
./cmake_lib_compile.sh "$ANDROID_API_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS"
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -74,7 +75,7 @@ cd compile_libs || exit 1
|
|||
(
|
||||
cd openssl || exit 1
|
||||
cp "${CURDIR}"/scripts/compile_libs/make_lib_openssl.sh make_lib_openssl.sh
|
||||
./make_lib_openssl.sh "$_ANDROID_ABI_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS"
|
||||
./make_lib_openssl.sh "$ANDROID_API_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS"
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -97,7 +98,7 @@ build_cmake_lib opus https://github.com/xiph/opus
|
|||
./autogen.sh
|
||||
fi
|
||||
cp "${CURDIR}"/scripts/compile_libs/make_lib_opusfile.sh make_lib_opusfile.sh
|
||||
./make_lib_opusfile.sh "$_ANDROID_ABI_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS"
|
||||
./make_lib_opusfile.sh "$ANDROID_API_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS"
|
||||
)
|
||||
|
||||
# SQLite, just download and built by hand
|
||||
|
@ -109,7 +110,7 @@ fi
|
|||
(
|
||||
cd sqlite3 || exit 1
|
||||
cp "${CURDIR}"/scripts/compile_libs/make_lib_sqlite3.sh make_lib_sqlite3.sh
|
||||
./make_lib_sqlite3.sh "$_ANDROID_ABI_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS"
|
||||
./make_lib_sqlite3.sh "$ANDROID_API_LEVEL" "$OS_NAME" "$COMPILEFLAGS" "$LINKFLAGS"
|
||||
)
|
||||
|
||||
cd ..
|
||||
|
|
|
@ -163,19 +163,19 @@
|
|||
#define CONF_ARCH_ARM 1
|
||||
#define CONF_ARCH_STRING "arm"
|
||||
#define CONF_ARCH_ENDIAN_BIG 1
|
||||
#endif
|
||||
|
||||
#if defined(__ARMEL__)
|
||||
#elif defined(__ARMEL__)
|
||||
#define CONF_ARCH_ARM 1
|
||||
#define CONF_ARCH_STRING "arm"
|
||||
#define CONF_ARCH_ENDIAN_LITTLE 1
|
||||
#endif
|
||||
|
||||
#if defined(__aarch64__) || defined(__arm64__) || defined(__ARM_ARCH)
|
||||
#elif defined(__aarch64__) || defined(__arm64__) || defined(__ARM_ARCH_ISA_A64)
|
||||
#define CONF_ARCH_ARM64 1
|
||||
#define CONF_ARCH_STRING "arm64"
|
||||
#if defined(__ARM_BIG_ENDIAN)
|
||||
#define CONF_ARCH_ENDIAN_BIG 1
|
||||
#else
|
||||
#define CONF_ARCH_ENDIAN_LITTLE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CONF_FAMILY_STRING
|
||||
#define CONF_FAMILY_STRING "unknown"
|
||||
|
|
|
@ -1236,6 +1236,7 @@ int str_format_int(char *buffer, size_t buffer_size, int value);
|
|||
template<typename... Args>
|
||||
int str_format_opt(char *buffer, int buffer_size, const char *format, Args... args)
|
||||
{
|
||||
static_assert(sizeof...(args) > 0, "Use str_copy instead of str_format without format arguments");
|
||||
return str_format(buffer, buffer_size, format, args...);
|
||||
}
|
||||
|
||||
|
|
|
@ -1508,8 +1508,9 @@ protected:
|
|||
vkInvalidateMappedMemoryRanges(m_VKDevice, 1, &MemRange);
|
||||
|
||||
size_t RealFullImageSize = maximum(ImageTotalSize, (size_t)(Height * m_GetPresentedImgDataHelperMappedLayoutPitch));
|
||||
if(vDstData.size() < RealFullImageSize)
|
||||
vDstData.resize(RealFullImageSize);
|
||||
size_t ExtraRowSize = Width * 4;
|
||||
if(vDstData.size() < RealFullImageSize + ExtraRowSize)
|
||||
vDstData.resize(RealFullImageSize + ExtraRowSize);
|
||||
|
||||
mem_copy(vDstData.data(), pResImageData, RealFullImageSize);
|
||||
|
||||
|
@ -1520,7 +1521,8 @@ protected:
|
|||
{
|
||||
size_t OffsetImagePacked = (Y * Width * 4);
|
||||
size_t OffsetImageUnpacked = (Y * m_GetPresentedImgDataHelperMappedLayoutPitch);
|
||||
mem_copy(vDstData.data() + OffsetImagePacked, vDstData.data() + OffsetImageUnpacked, Width * 4);
|
||||
mem_copy(vDstData.data() + RealFullImageSize, vDstData.data() + OffsetImageUnpacked, Width * 4);
|
||||
mem_copy(vDstData.data() + OffsetImagePacked, vDstData.data() + RealFullImageSize, Width * 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -362,7 +362,7 @@ void CClient::SendInput()
|
|||
// ugly workaround for dummy. we need to send input with dummy to prevent
|
||||
// prediction time resets. but if we do it too often, then it's
|
||||
// impossible to use grenade with frozen dummy that gets hammered...
|
||||
if(g_Config.m_ClDummyCopyMoves || m_aCurrentInput[i] % 2)
|
||||
if(g_Config.m_ClDummyCopyMoves || g_Config.m_ClDummyControl || m_aCurrentInput[i] % 2)
|
||||
Force = true;
|
||||
}
|
||||
}
|
||||
|
@ -668,10 +668,7 @@ void CClient::DisconnectWithReason(const char *pReason)
|
|||
m_CurrentServerCurrentPingTime = -1;
|
||||
m_CurrentServerNextPingTime = -1;
|
||||
|
||||
ResetMapDownload();
|
||||
m_aMapdownloadFilename[0] = '\0';
|
||||
m_aMapdownloadFilenameTemp[0] = '\0';
|
||||
m_aMapdownloadName[0] = '\0';
|
||||
ResetMapDownload(true);
|
||||
|
||||
// clear the current server info
|
||||
mem_zero(&m_CurrentServerInfo, sizeof(m_CurrentServerInfo));
|
||||
|
@ -1528,7 +1525,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
|
|||
DummyDisconnect(0);
|
||||
}
|
||||
|
||||
ResetMapDownload();
|
||||
ResetMapDownload(true);
|
||||
|
||||
SHA256_DIGEST *pMapSha256 = nullptr;
|
||||
const char *pMapUrl = nullptr;
|
||||
|
@ -2196,7 +2193,7 @@ int CClient::UnpackAndValidateSnapshot(CSnapshot *pFrom, CSnapshot *pTo)
|
|||
return Builder.Finish(pTo);
|
||||
}
|
||||
|
||||
void CClient::ResetMapDownload()
|
||||
void CClient::ResetMapDownload(bool ResetActive)
|
||||
{
|
||||
if(m_pMapdownloadTask)
|
||||
{
|
||||
|
@ -2215,19 +2212,24 @@ void CClient::ResetMapDownload()
|
|||
Storage()->RemoveFile(m_aMapdownloadFilenameTemp, IStorage::TYPE_SAVE);
|
||||
}
|
||||
|
||||
m_MapdownloadChunk = 0;
|
||||
m_MapdownloadSha256Present = false;
|
||||
m_MapdownloadSha256 = SHA256_ZEROED;
|
||||
m_MapdownloadCrc = 0;
|
||||
m_MapdownloadTotalsize = -1;
|
||||
m_MapdownloadAmount = 0;
|
||||
if(ResetActive)
|
||||
{
|
||||
m_MapdownloadChunk = 0;
|
||||
m_MapdownloadSha256Present = false;
|
||||
m_MapdownloadSha256 = SHA256_ZEROED;
|
||||
m_MapdownloadCrc = 0;
|
||||
m_MapdownloadTotalsize = -1;
|
||||
m_MapdownloadAmount = 0;
|
||||
m_aMapdownloadFilename[0] = '\0';
|
||||
m_aMapdownloadFilenameTemp[0] = '\0';
|
||||
m_aMapdownloadName[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void CClient::FinishMapDownload()
|
||||
{
|
||||
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", "download complete, loading map");
|
||||
|
||||
const int PrevMapdownloadTotalsize = m_MapdownloadTotalsize;
|
||||
SHA256_DIGEST *pSha256 = m_MapdownloadSha256Present ? &m_MapdownloadSha256 : nullptr;
|
||||
|
||||
bool FileSuccess = true;
|
||||
|
@ -2236,7 +2238,6 @@ void CClient::FinishMapDownload()
|
|||
FileSuccess &= Storage()->RenameFile(m_aMapdownloadFilenameTemp, m_aMapdownloadFilename, IStorage::TYPE_SAVE);
|
||||
if(!FileSuccess)
|
||||
{
|
||||
ResetMapDownload();
|
||||
char aError[128 + IO_MAX_PATH_LENGTH];
|
||||
str_format(aError, sizeof(aError), Localize("Could not save downloaded map. Try manually deleting this file: %s"), m_aMapdownloadFilename);
|
||||
DisconnectWithReason(aError);
|
||||
|
@ -2246,19 +2247,17 @@ void CClient::FinishMapDownload()
|
|||
const char *pError = LoadMap(m_aMapdownloadName, m_aMapdownloadFilename, pSha256, m_MapdownloadCrc);
|
||||
if(!pError)
|
||||
{
|
||||
ResetMapDownload();
|
||||
ResetMapDownload(true);
|
||||
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", "loading done");
|
||||
SendReady(CONN_MAIN);
|
||||
}
|
||||
else if(m_pMapdownloadTask) // fallback
|
||||
{
|
||||
ResetMapDownload();
|
||||
m_MapdownloadTotalsize = PrevMapdownloadTotalsize;
|
||||
ResetMapDownload(false);
|
||||
SendMapRequest();
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetMapDownload();
|
||||
DisconnectWithReason(pError);
|
||||
}
|
||||
}
|
||||
|
@ -2784,7 +2783,7 @@ void CClient::Update()
|
|||
else if(m_pMapdownloadTask->State() == EHttpState::ERROR || m_pMapdownloadTask->State() == EHttpState::ABORTED)
|
||||
{
|
||||
dbg_msg("webdl", "http failed, falling back to gameserver");
|
||||
ResetMapDownload();
|
||||
ResetMapDownload(false);
|
||||
SendMapRequest();
|
||||
}
|
||||
}
|
||||
|
@ -2930,6 +2929,24 @@ void CClient::Run()
|
|||
g_UuidManager.DebugDump();
|
||||
}
|
||||
|
||||
#ifndef CONF_WEBASM
|
||||
char aNetworkError[256];
|
||||
if(!InitNetworkClient(aNetworkError, sizeof(aNetworkError)))
|
||||
{
|
||||
log_error("client", "%s", aNetworkError);
|
||||
ShowMessageBox("Network Error", aNetworkError);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!m_Http.Init(std::chrono::seconds{1}))
|
||||
{
|
||||
const char *pErrorMessage = "Failed to initialize the HTTP client.";
|
||||
log_error("client", "%s", pErrorMessage);
|
||||
ShowMessageBox("HTTP Error", pErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// init graphics
|
||||
m_pGraphics = CreateEngineGraphicsThreaded();
|
||||
Kernel()->RegisterInterface(m_pGraphics); // IEngineGraphics
|
||||
|
@ -2953,24 +2970,6 @@ void CClient::Run()
|
|||
CVideo::Init();
|
||||
#endif
|
||||
|
||||
#ifndef CONF_WEBASM
|
||||
char aNetworkError[256];
|
||||
if(!InitNetworkClient(aNetworkError, sizeof(aNetworkError)))
|
||||
{
|
||||
log_error("client", "%s", aNetworkError);
|
||||
ShowMessageBox("Network Error", aNetworkError);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!m_Http.Init(std::chrono::seconds{1}))
|
||||
{
|
||||
const char *pErrorMessage = "Failed to initialize the HTTP client.";
|
||||
log_error("client", "%s", pErrorMessage);
|
||||
ShowMessageBox("HTTP Error", pErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// init text render
|
||||
m_pTextRender = Kernel()->RequestInterface<IEngineTextRender>();
|
||||
m_pTextRender->Init();
|
||||
|
@ -3317,7 +3316,7 @@ bool CClient::InitNetworkClient(char *pError, size_t ErrorSize)
|
|||
if(g_Config.m_Bindaddr[0])
|
||||
str_format(pError, ErrorSize, "Could not open the network client, try changing or unsetting the bindaddr '%s'.", g_Config.m_Bindaddr);
|
||||
else
|
||||
str_format(pError, ErrorSize, "Could not open the network client.");
|
||||
str_copy(pError, "Could not open the network client.", ErrorSize);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -4358,8 +4357,20 @@ void CClient::RegisterCommands()
|
|||
|
||||
// used for server browser update
|
||||
m_pConsole->Chain("br_filter_string", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_exclude_string", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_full", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_empty", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_spectators", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_friends", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_country", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_country_index", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_pw", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_gametype", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_gametype_strict", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_connecting_players", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_serveraddress", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_unfinished_map", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("br_filter_login", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("add_favorite", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("remove_favorite", ConchainServerBrowserUpdate, this);
|
||||
m_pConsole->Chain("end_favorite_group", ConchainServerBrowserUpdate, this);
|
||||
|
|
|
@ -360,7 +360,7 @@ public:
|
|||
|
||||
int UnpackAndValidateSnapshot(CSnapshot *pFrom, CSnapshot *pTo);
|
||||
|
||||
void ResetMapDownload();
|
||||
void ResetMapDownload(bool ResetActive);
|
||||
void FinishMapDownload();
|
||||
|
||||
void RequestDDNetInfo() override;
|
||||
|
|
|
@ -300,7 +300,7 @@ void CGraphics_Threaded::UnloadTexture(CTextureHandle *pIndex)
|
|||
FreeTextureIndex(pIndex);
|
||||
}
|
||||
|
||||
static bool ConvertToRGBA(uint8_t *pDest, const CImageInfo &SrcImage)
|
||||
bool ConvertToRGBA(uint8_t *pDest, const CImageInfo &SrcImage)
|
||||
{
|
||||
if(SrcImage.m_Format == CImageInfo::FORMAT_RGBA)
|
||||
{
|
||||
|
|
|
@ -27,9 +27,6 @@
|
|||
#include <engine/http.h>
|
||||
#include <engine/storage.h>
|
||||
|
||||
static constexpr const char *COMMUNITY_COUNTRY_NONE = "none";
|
||||
static constexpr const char *COMMUNITY_TYPE_NONE = "None";
|
||||
|
||||
class CSortWrap
|
||||
{
|
||||
typedef bool (CServerBrowser::*SortFunc)(int, int) const;
|
||||
|
|
|
@ -228,10 +228,8 @@ int CSound::Init()
|
|||
return -1;
|
||||
}
|
||||
|
||||
m_MixingRate = g_Config.m_SndRate;
|
||||
|
||||
SDL_AudioSpec Format, FormatOut;
|
||||
Format.freq = m_MixingRate;
|
||||
Format.freq = g_Config.m_SndRate;
|
||||
Format.format = AUDIO_S16;
|
||||
Format.channels = 2;
|
||||
Format.samples = g_Config.m_SndBufferSize;
|
||||
|
@ -239,7 +237,7 @@ int CSound::Init()
|
|||
Format.userdata = this;
|
||||
|
||||
// Open the audio device and start playing sound!
|
||||
m_Device = SDL_OpenAudioDevice(nullptr, 0, &Format, &FormatOut, 0);
|
||||
m_Device = SDL_OpenAudioDevice(nullptr, 0, &Format, &FormatOut, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
||||
if(m_Device == 0)
|
||||
{
|
||||
dbg_msg("sound", "unable to open audio: %s", SDL_GetError());
|
||||
|
@ -248,6 +246,7 @@ int CSound::Init()
|
|||
else
|
||||
dbg_msg("sound", "sound init successful using audio driver '%s'", SDL_GetCurrentAudioDriver());
|
||||
|
||||
m_MixingRate = FormatOut.freq;
|
||||
m_MaxFrames = FormatOut.samples * 2;
|
||||
#if defined(CONF_VIDEORECORDER)
|
||||
m_MaxFrames = maximum<uint32_t>(m_MaxFrames, 1024 * 2); // make the buffer bigger just in case
|
||||
|
|
|
@ -283,6 +283,7 @@ void CVideo::Pause(bool Pause)
|
|||
void CVideo::Stop()
|
||||
{
|
||||
dbg_assert(!m_Stopped, "Already stopped");
|
||||
m_Stopped = true;
|
||||
|
||||
m_pGraphics->WaitForIdle();
|
||||
|
||||
|
@ -341,8 +342,6 @@ void CVideo::Stop()
|
|||
pSound->PauseAudioDevice();
|
||||
delete ms_pCurrentVideo;
|
||||
pSound->UnpauseAudioDevice();
|
||||
|
||||
m_Stopped = true;
|
||||
}
|
||||
|
||||
void CVideo::NextVideoFrameThread()
|
||||
|
|
|
@ -53,7 +53,7 @@ public:
|
|||
virtual int GetInteger(unsigned Index) const = 0;
|
||||
virtual float GetFloat(unsigned Index) const = 0;
|
||||
virtual const char *GetString(unsigned Index) const = 0;
|
||||
virtual ColorHSLA GetColor(unsigned Index, bool Light) const = 0;
|
||||
virtual std::optional<ColorHSLA> GetColor(unsigned Index, bool Light) const = 0;
|
||||
|
||||
virtual void RemoveArgument(unsigned Index) = 0;
|
||||
|
||||
|
|
|
@ -124,6 +124,8 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
bool ConvertToRGBA(uint8_t *pDest, const CImageInfo &SrcImage);
|
||||
|
||||
/*
|
||||
Structure: CVideoMode
|
||||
*/
|
||||
|
|
|
@ -309,6 +309,9 @@ public:
|
|||
|
||||
static constexpr const char *COMMUNITY_DDNET = "ddnet";
|
||||
static constexpr const char *COMMUNITY_NONE = "none";
|
||||
|
||||
static constexpr const char *COMMUNITY_COUNTRY_NONE = "none";
|
||||
static constexpr const char *COMMUNITY_TYPE_NONE = "None";
|
||||
/**
|
||||
* Special community value for country/type filters that
|
||||
* affect all communities.
|
||||
|
|
|
@ -111,22 +111,29 @@ void SIntConfigVariable::ResetToOld()
|
|||
void SColorConfigVariable::CommandCallback(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
SColorConfigVariable *pData = static_cast<SColorConfigVariable *>(pUserData);
|
||||
|
||||
char aBuf[IConsole::CMDLINE_LENGTH + 64];
|
||||
if(pResult->NumArguments())
|
||||
{
|
||||
if(pData->CheckReadOnly())
|
||||
return;
|
||||
|
||||
const ColorHSLA Color = pResult->GetColor(0, pData->m_Light);
|
||||
const unsigned Value = Color.Pack(pData->m_Light ? 0.5f : 0.0f, pData->m_Alpha);
|
||||
const auto Color = pResult->GetColor(0, pData->m_Light);
|
||||
if(Color)
|
||||
{
|
||||
const unsigned Value = Color->Pack(pData->m_Light ? 0.5f : 0.0f, pData->m_Alpha);
|
||||
|
||||
*pData->m_pVariable = Value;
|
||||
if(pResult->m_ClientId != IConsole::CLIENT_ID_GAME)
|
||||
pData->m_OldValue = Value;
|
||||
*pData->m_pVariable = Value;
|
||||
if(pResult->m_ClientId != IConsole::CLIENT_ID_GAME)
|
||||
pData->m_OldValue = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
str_format(aBuf, sizeof(aBuf), "%s is not a valid color.", pResult->GetString(0));
|
||||
pData->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "config", aBuf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char aBuf[256];
|
||||
str_format(aBuf, sizeof(aBuf), "Value: %u", *pData->m_pVariable);
|
||||
pData->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "config", aBuf);
|
||||
|
||||
|
@ -493,8 +500,8 @@ void CConfigManager::Con_Toggle(IConsole::IResult *pResult, void *pUserData)
|
|||
{
|
||||
SColorConfigVariable *pColorVariable = static_cast<SColorConfigVariable *>(pVariable);
|
||||
const float Darkest = pColorVariable->m_Light ? 0.5f : 0.0f;
|
||||
const ColorHSLA Value = *pColorVariable->m_pVariable == pResult->GetColor(1, pColorVariable->m_Light).Pack(Darkest, pColorVariable->m_Alpha) ? pResult->GetColor(2, pColorVariable->m_Light) : pResult->GetColor(1, pColorVariable->m_Light);
|
||||
pColorVariable->SetValue(Value.Pack(Darkest, pColorVariable->m_Alpha));
|
||||
const std::optional<ColorHSLA> Value = *pColorVariable->m_pVariable == pResult->GetColor(1, pColorVariable->m_Light).value_or(ColorHSLA(0, 0, 0)).Pack(Darkest, pColorVariable->m_Alpha) ? pResult->GetColor(2, pColorVariable->m_Light) : pResult->GetColor(1, pColorVariable->m_Light);
|
||||
pColorVariable->SetValue(Value.value_or(ColorHSLA(0, 0, 0)).Pack(Darkest, pColorVariable->m_Alpha));
|
||||
}
|
||||
else if(pVariable->m_Type == SConfigVariable::VAR_STRING)
|
||||
{
|
||||
|
|
|
@ -73,6 +73,7 @@ MACRO_CONFIG_INT(ClShowfps, cl_showfps, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE,
|
|||
MACRO_CONFIG_INT(ClShowpred, cl_showpred, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show ingame prediction time in milliseconds")
|
||||
MACRO_CONFIG_INT(ClEyeWheel, cl_eye_wheel, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show eye wheel along together with emotes")
|
||||
MACRO_CONFIG_INT(ClEyeDuration, cl_eye_duration, 999999, 1, 999999, CFGFLAG_CLIENT | CFGFLAG_SAVE, "How long the eyes emotes last")
|
||||
MACRO_CONFIG_INT(ClFreezeStars, cl_freeze_stars, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show old star particles for frozen tees")
|
||||
|
||||
MACRO_CONFIG_INT(ClAirjumpindicator, cl_airjumpindicator, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show the air jump indicator")
|
||||
MACRO_CONFIG_INT(ClThreadsoundloading, cl_threadsoundloading, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Load sound files threaded")
|
||||
|
@ -105,6 +106,7 @@ MACRO_CONFIG_INT(EdZoomTarget, ed_zoom_target, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG
|
|||
MACRO_CONFIG_INT(EdShowkeys, ed_showkeys, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show pressed keys")
|
||||
MACRO_CONFIG_INT(EdAlignQuads, ed_align_quads, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Enable/disable quad alignment. When enabled, red lines appear to show how quad/points are aligned and snapped to other quads/points when moving them")
|
||||
MACRO_CONFIG_INT(EdShowQuadsRect, ed_show_quads_rect, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show the bounds of the selected quad. In case of multiple quads, it shows the bounds of the englobing rect. Can be helpful when aligning a group of quads")
|
||||
MACRO_CONFIG_INT(EdAutoMapReload, ed_auto_map_reload, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Run 'hot_reload' on the local server while rcon authed on map save")
|
||||
|
||||
MACRO_CONFIG_INT(ClShowWelcome, cl_show_welcome, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show welcome message indicating the first launch of the client")
|
||||
MACRO_CONFIG_INT(ClMotdTime, cl_motd_time, 10, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "How long to show the server message of the day")
|
||||
|
|
|
@ -40,22 +40,29 @@ float CConsole::CResult::GetFloat(unsigned Index) const
|
|||
return str_tofloat(m_apArgs[Index]);
|
||||
}
|
||||
|
||||
ColorHSLA CConsole::CResult::GetColor(unsigned Index, bool Light) const
|
||||
std::optional<ColorHSLA> CConsole::CResult::GetColor(unsigned Index, bool Light) const
|
||||
{
|
||||
if(Index >= m_NumArgs)
|
||||
return ColorHSLA(0, 0, 0);
|
||||
return std::nullopt;
|
||||
|
||||
const char *pStr = m_apArgs[Index];
|
||||
if(str_isallnum(pStr) || ((pStr[0] == '-' || pStr[0] == '+') && str_isallnum(pStr + 1))) // Teeworlds Color (Packed HSL)
|
||||
{
|
||||
const ColorHSLA Hsla = ColorHSLA(str_toulong_base(pStr, 10), true);
|
||||
unsigned long Value = str_toulong_base(pStr, 10);
|
||||
if(Value == std::numeric_limits<unsigned long>::max())
|
||||
return std::nullopt;
|
||||
const ColorHSLA Hsla = ColorHSLA(Value, true);
|
||||
if(Light)
|
||||
return Hsla.UnclampLighting();
|
||||
return Hsla;
|
||||
}
|
||||
else if(*pStr == '$') // Hex RGB/RGBA
|
||||
{
|
||||
return color_cast<ColorHSLA>(color_parse<ColorRGBA>(pStr + 1).value_or(ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)));
|
||||
auto ParsedColor = color_parse<ColorRGBA>(pStr + 1);
|
||||
if(ParsedColor)
|
||||
return color_cast<ColorHSLA>(ParsedColor.value());
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
else if(!str_comp_nocase(pStr, "red"))
|
||||
return ColorHSLA(0.0f / 6.0f, 1, .5f);
|
||||
|
@ -76,7 +83,7 @@ ColorHSLA CConsole::CResult::GetColor(unsigned Index, bool Light) const
|
|||
else if(!str_comp_nocase(pStr, "black"))
|
||||
return ColorHSLA(0, 0, 0);
|
||||
|
||||
return ColorHSLA(0, 0, 0);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const IConsole::CCommandInfo *CConsole::CCommand::NextCommandInfo(int AccessLevel, int FlagMask) const
|
||||
|
@ -129,12 +136,12 @@ int CConsole::ParseStart(CResult *pResult, const char *pString, int Length)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
|
||||
int CConsole::ParseArgs(CResult *pResult, const char *pFormat, bool IsColor)
|
||||
{
|
||||
char Command = *pFormat;
|
||||
char *pStr;
|
||||
int Optional = 0;
|
||||
int Error = 0;
|
||||
int Error = PARSEARGS_OK;
|
||||
|
||||
pResult->ResetVictim();
|
||||
|
||||
|
@ -155,7 +162,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
|
|||
{
|
||||
if(!Optional)
|
||||
{
|
||||
Error = 1;
|
||||
Error = PARSEARGS_MISSING_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -191,7 +198,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
|
|||
pStr++; // skip due to escape
|
||||
}
|
||||
else if(pStr[0] == 0)
|
||||
return 1; // return error
|
||||
return PARSEARGS_MISSING_VALUE; // return error
|
||||
|
||||
*pDst = *pStr;
|
||||
pDst++;
|
||||
|
@ -215,13 +222,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
|
|||
|
||||
if(Command == 'r') // rest of the string
|
||||
break;
|
||||
else if(Command == 'v') // validate victim
|
||||
pStr = str_skip_to_whitespace(pStr);
|
||||
else if(Command == 'i') // validate int
|
||||
pStr = str_skip_to_whitespace(pStr);
|
||||
else if(Command == 'f') // validate float
|
||||
pStr = str_skip_to_whitespace(pStr);
|
||||
else if(Command == 's') // validate string
|
||||
else if(Command == 'v' || Command == 'i' || Command == 'f' || Command == 's')
|
||||
pStr = str_skip_to_whitespace(pStr);
|
||||
|
||||
if(pStr[0] != 0) // check for end of string
|
||||
|
@ -230,6 +231,32 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
|
|||
pStr++;
|
||||
}
|
||||
|
||||
// validate args
|
||||
if(Command == 'i')
|
||||
{
|
||||
// don't validate colors here
|
||||
if(!IsColor)
|
||||
{
|
||||
int Value;
|
||||
if(!str_toint(pResult->GetString(pResult->NumArguments() - 1), &Value) ||
|
||||
Value == std::numeric_limits<int>::max() || Value == std::numeric_limits<int>::min())
|
||||
{
|
||||
Error = PARSEARGS_INVALID_INTEGER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(Command == 'f')
|
||||
{
|
||||
float Value;
|
||||
if(!str_tofloat(pResult->GetString(pResult->NumArguments() - 1), &Value) ||
|
||||
Value == std::numeric_limits<float>::max() || Value == std::numeric_limits<float>::min())
|
||||
{
|
||||
Error = PARSEARGS_INVALID_FLOAT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(pVictim)
|
||||
{
|
||||
pResult->SetVictim(pVictim);
|
||||
|
@ -314,7 +341,7 @@ void CConsole::Print(int Level, const char *pFrom, const char *pStr, ColorRGBA P
|
|||
{
|
||||
LEVEL LogLevel = IConsole::ToLogLevel(Level);
|
||||
// if console colors are not enabled or if the color is pure white, use default terminal color
|
||||
if(g_Config.m_ConsoleEnableColors && mem_comp(&PrintColor, &gs_ConsoleDefaultColor, sizeof(ColorRGBA)) != 0)
|
||||
if(g_Config.m_ConsoleEnableColors && PrintColor != gs_ConsoleDefaultColor)
|
||||
{
|
||||
log_log_color(LogLevel, ColorToLogColor(PrintColor), pFrom, "%s", pStr);
|
||||
}
|
||||
|
@ -487,10 +514,15 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientId, bo
|
|||
|
||||
if(Stroke || IsStrokeCommand)
|
||||
{
|
||||
if(ParseArgs(&Result, pCommand->m_pParams))
|
||||
if(int Error = ParseArgs(&Result, pCommand->m_pParams, pCommand->m_pfnCallback == &SColorConfigVariable::CommandCallback))
|
||||
{
|
||||
char aBuf[TEMPCMD_NAME_LENGTH + TEMPCMD_PARAMS_LENGTH + 32];
|
||||
str_format(aBuf, sizeof(aBuf), "Invalid arguments. Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
|
||||
char aBuf[CMDLINE_LENGTH + 64];
|
||||
if(Error == PARSEARGS_INVALID_INTEGER)
|
||||
str_format(aBuf, sizeof(aBuf), "%s is not a valid integer.", Result.GetString(Result.NumArguments() - 1));
|
||||
else if(Error == PARSEARGS_INVALID_FLOAT)
|
||||
str_format(aBuf, sizeof(aBuf), "%s is not a valid decimal number.", Result.GetString(Result.NumArguments() - 1));
|
||||
else
|
||||
str_format(aBuf, sizeof(aBuf), "Invalid arguments. Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
|
||||
Print(OUTPUT_LEVEL_STANDARD, "chatresp", aBuf);
|
||||
}
|
||||
else if(m_StoreCommands && pCommand->m_Flags & CFGFLAG_STORE)
|
||||
|
|
|
@ -115,7 +115,7 @@ class CConsole : public IConsole
|
|||
const char *GetString(unsigned Index) const override;
|
||||
int GetInteger(unsigned Index) const override;
|
||||
float GetFloat(unsigned Index) const override;
|
||||
ColorHSLA GetColor(unsigned Index, bool Light) const override;
|
||||
std::optional<ColorHSLA> GetColor(unsigned Index, bool Light) const override;
|
||||
|
||||
void RemoveArgument(unsigned Index) override
|
||||
{
|
||||
|
@ -144,7 +144,16 @@ class CConsole : public IConsole
|
|||
};
|
||||
|
||||
int ParseStart(CResult *pResult, const char *pString, int Length);
|
||||
int ParseArgs(CResult *pResult, const char *pFormat);
|
||||
|
||||
enum
|
||||
{
|
||||
PARSEARGS_OK = 0,
|
||||
PARSEARGS_MISSING_VALUE,
|
||||
PARSEARGS_INVALID_INTEGER,
|
||||
PARSEARGS_INVALID_FLOAT,
|
||||
};
|
||||
|
||||
int ParseArgs(CResult *pResult, const char *pFormat, bool IsColor = false);
|
||||
|
||||
/*
|
||||
this function will set pFormat to the next parameter (i,s,r,v,?) it contains and
|
||||
|
|
|
@ -70,18 +70,18 @@ void CEcon::Init(CConfig *pConfig, IConsole *pConsole, CNetBan *pNetBan)
|
|||
}
|
||||
|
||||
NETADDR BindAddr;
|
||||
if(g_Config.m_EcBindaddr[0] == '\0')
|
||||
if(g_Config.m_EcBindaddr[0] && net_host_lookup(g_Config.m_EcBindaddr, &BindAddr, NETTYPE_ALL) == 0)
|
||||
{
|
||||
mem_zero(&BindAddr, sizeof(BindAddr));
|
||||
// got bindaddr
|
||||
BindAddr.port = g_Config.m_EcPort;
|
||||
}
|
||||
else if(net_host_lookup(g_Config.m_EcBindaddr, &BindAddr, NETTYPE_ALL) != 0)
|
||||
else
|
||||
{
|
||||
char aBuf[256];
|
||||
str_format(aBuf, sizeof(aBuf), "The configured bindaddr '%s' cannot be resolved.", g_Config.m_Bindaddr);
|
||||
str_format(aBuf, sizeof(aBuf), "The configured bindaddr '%s' cannot be resolved.", g_Config.m_EcBindaddr);
|
||||
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "econ", aBuf);
|
||||
return;
|
||||
}
|
||||
BindAddr.type = NETTYPE_ALL;
|
||||
BindAddr.port = g_Config.m_EcPort;
|
||||
|
||||
if(m_NetConsole.Open(BindAddr, pNetBan))
|
||||
{
|
||||
|
|
|
@ -490,8 +490,7 @@ void CNetBan::ConBans(IConsole::IResult *pResult, void *pUser)
|
|||
|
||||
if(NumBans == 0)
|
||||
{
|
||||
str_format(aMsg, sizeof(aMsg), "The ban list is empty.");
|
||||
pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aMsg);
|
||||
pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "The ban list is empty.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -93,6 +93,7 @@ MAYBE_UNUSED static const char *FONT_ICON_EARTH_AMERICAS = "\xEF\x95\xBD";
|
|||
MAYBE_UNUSED static const char *FONT_ICON_NETWORK_WIRED = "\xEF\x9B\xBF";
|
||||
MAYBE_UNUSED static const char *FONT_ICON_LIST_UL = "\xEF\x83\x8A";
|
||||
MAYBE_UNUSED static const char *FONT_ICON_INFO = "\xEF\x84\xA9";
|
||||
MAYBE_UNUSED static const char *FONT_ICON_TERMINAL = "\xEF\x84\xA0";
|
||||
|
||||
MAYBE_UNUSED static const char *FONT_ICON_SLASH = "\xEF\x9C\x95";
|
||||
MAYBE_UNUSED static const char *FONT_ICON_PLAY = "\xEF\x81\x8B";
|
||||
|
|
|
@ -160,8 +160,6 @@ class CGameConsole : public CComponent
|
|||
static const ColorRGBA ms_SearchHighlightColor;
|
||||
static const ColorRGBA ms_SearchSelectedColor;
|
||||
|
||||
void Toggle(int Type);
|
||||
|
||||
static void PossibleCommandsRenderCallback(int Index, const char *pStr, void *pUser);
|
||||
static void ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConToggleRemoteConsole(IConsole::IResult *pResult, void *pUserData);
|
||||
|
@ -196,6 +194,7 @@ public:
|
|||
virtual bool OnInput(const IInput::CEvent &Event) override;
|
||||
void Prompt(char (&aPrompt)[32]);
|
||||
|
||||
void Toggle(int Type);
|
||||
bool IsClosed() { return m_ConsoleState == CONSOLE_CLOSED; }
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -1693,8 +1693,7 @@ void CHud::RenderRecord()
|
|||
if(m_ServerRecord > 0.0f)
|
||||
{
|
||||
char aBuf[64];
|
||||
str_format(aBuf, sizeof(aBuf), Localize("Server best:"));
|
||||
TextRender()->Text(5, 75, 6, aBuf, -1.0f);
|
||||
TextRender()->Text(5, 75, 6, Localize("Server best:"), -1.0f);
|
||||
char aTime[32];
|
||||
str_time_float(m_ServerRecord, TIME_HOURS_CENTISECS, aTime, sizeof(aTime));
|
||||
str_format(aBuf, sizeof(aBuf), "%s%s", m_ServerRecord > 3600 ? "" : " ", aTime);
|
||||
|
@ -1705,8 +1704,7 @@ void CHud::RenderRecord()
|
|||
if(PlayerRecord > 0.0f)
|
||||
{
|
||||
char aBuf[64];
|
||||
str_format(aBuf, sizeof(aBuf), Localize("Personal best:"));
|
||||
TextRender()->Text(5, 82, 6, aBuf, -1.0f);
|
||||
TextRender()->Text(5, 82, 6, Localize("Personal best:"), -1.0f);
|
||||
char aTime[32];
|
||||
str_time_float(PlayerRecord, TIME_HOURS_CENTISECS, aTime, sizeof(aTime));
|
||||
str_format(aBuf, sizeof(aBuf), "%s%s", PlayerRecord > 3600 ? "" : " ", aTime);
|
||||
|
|
|
@ -1208,7 +1208,7 @@ void CMenus::RenderPopupFullscreen(CUIRect Screen)
|
|||
pButtonText = Localize("Ok");
|
||||
if(Client()->ReconnectTime() > 0)
|
||||
{
|
||||
str_format(aBuf, sizeof(aBuf), Localize("Reconnect in %d sec"), (int)((Client()->ReconnectTime() - time_get()) / time_freq()));
|
||||
str_format(aBuf, sizeof(aBuf), Localize("Reconnect in %d sec"), (int)((Client()->ReconnectTime() - time_get()) / time_freq()) + 1);
|
||||
pTitle = Client()->ErrorString();
|
||||
pExtraText = aBuf;
|
||||
pButtonText = Localize("Abort");
|
||||
|
|
|
@ -869,11 +869,14 @@ void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
|
|||
{
|
||||
if(Click == 1)
|
||||
{
|
||||
// Left click: when all are active, only activate one
|
||||
// Left click: when all are active, only activate one and none
|
||||
for(int j = 0; j < MaxItems; ++j)
|
||||
{
|
||||
if(j != ItemIndex)
|
||||
Filter.Add(GetItemName(j));
|
||||
if(const char *pItemName = GetItemName(j);
|
||||
j != ItemIndex &&
|
||||
!((&Filter == &ServerBrowser()->CountriesFilter() && str_comp(pItemName, IServerBrowser::COMMUNITY_COUNTRY_NONE) == 0) ||
|
||||
(&Filter == &ServerBrowser()->TypesFilter() && str_comp(pItemName, IServerBrowser::COMMUNITY_TYPE_NONE) == 0)))
|
||||
Filter.Add(pItemName);
|
||||
}
|
||||
}
|
||||
else if(Click == 2)
|
||||
|
@ -890,7 +893,10 @@ void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
|
|||
bool AllFilteredExceptUs = true;
|
||||
for(int j = 0; j < MaxItems; ++j)
|
||||
{
|
||||
if(j != ItemIndex && !Filter.Filtered(GetItemName(j)))
|
||||
if(const char *pItemName = GetItemName(j);
|
||||
j != ItemIndex && !Filter.Filtered(pItemName) &&
|
||||
!((&Filter == &ServerBrowser()->CountriesFilter() && str_comp(pItemName, IServerBrowser::COMMUNITY_COUNTRY_NONE) == 0) ||
|
||||
(&Filter == &ServerBrowser()->TypesFilter() && str_comp(pItemName, IServerBrowser::COMMUNITY_TYPE_NONE) == 0)))
|
||||
{
|
||||
AllFilteredExceptUs = false;
|
||||
break;
|
||||
|
@ -898,7 +904,7 @@ void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
|
|||
}
|
||||
// When last one is removed, re-enable all currently selectable items.
|
||||
// Don't use Clear, to avoid enabling also currently unselectable items.
|
||||
if(AllFilteredExceptUs)
|
||||
if(AllFilteredExceptUs && Active)
|
||||
{
|
||||
for(int j = 0; j < MaxItems; ++j)
|
||||
{
|
||||
|
|
|
@ -1397,22 +1397,10 @@ void CMenus::RenderDemoBrowserButtons(CUIRect ButtonsView, bool WasListboxItemAc
|
|||
|
||||
// quick search
|
||||
{
|
||||
SetIconMode(true);
|
||||
CUIRect DemoSearch, SearchIcon;
|
||||
CUIRect DemoSearch;
|
||||
ButtonBarTop.VSplitLeft(ButtonBarBottom.h * 21.0f, &DemoSearch, &ButtonBarTop);
|
||||
ButtonBarTop.VSplitLeft(ButtonBarTop.h / 2.0f, nullptr, &ButtonBarTop);
|
||||
DemoSearch.VSplitLeft(TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS), &SearchIcon, &DemoSearch);
|
||||
DemoSearch.VSplitLeft(5.0f, nullptr, &DemoSearch);
|
||||
Ui()->DoLabel(&SearchIcon, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
|
||||
SetIconMode(false);
|
||||
m_DemoSearchInput.SetEmptyText(Localize("Search"));
|
||||
|
||||
if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())
|
||||
{
|
||||
Ui()->SetActiveItem(&m_DemoSearchInput);
|
||||
m_DemoSearchInput.SelectAll();
|
||||
}
|
||||
if(Ui()->DoClearableEditBox(&m_DemoSearchInput, &DemoSearch, 12.0f))
|
||||
if(Ui()->DoEditBox_Search(&m_DemoSearchInput, &DemoSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()))
|
||||
{
|
||||
RefreshFilteredDemos();
|
||||
DemolistOnUpdate(false);
|
||||
|
|
|
@ -683,26 +683,15 @@ void CMenus::RenderServerControl(CUIRect MainView)
|
|||
|
||||
// render quick search
|
||||
CUIRect QuickSearch;
|
||||
Bottom.VSplitLeft(5.0f, 0, &Bottom);
|
||||
Bottom.VSplitLeft(5.0f, nullptr, &Bottom);
|
||||
Bottom.VSplitLeft(250.0f, &QuickSearch, &Bottom);
|
||||
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
|
||||
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
|
||||
|
||||
Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
|
||||
float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f);
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
QuickSearch.VSplitLeft(SearchWidth, 0, &QuickSearch);
|
||||
QuickSearch.VSplitLeft(5.0f, 0, &QuickSearch);
|
||||
|
||||
if(m_ControlPageOpening || (Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed()))
|
||||
if(m_ControlPageOpening)
|
||||
{
|
||||
Ui()->SetActiveItem(&m_FilterInput);
|
||||
m_ControlPageOpening = false;
|
||||
Ui()->SetActiveItem(&m_FilterInput);
|
||||
m_FilterInput.SelectAll();
|
||||
}
|
||||
m_FilterInput.SetEmptyText(Localize("Search"));
|
||||
Ui()->DoClearableEditBox(&m_FilterInput, &QuickSearch, 14.0f);
|
||||
Ui()->DoEditBox_Search(&m_FilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed());
|
||||
|
||||
// call vote
|
||||
Bottom.VSplitRight(10.0f, &Bottom, 0);
|
||||
|
|
|
@ -257,7 +257,7 @@ void CMenus::SetNeedSendInfo()
|
|||
|
||||
void CMenus::RenderSettingsPlayer(CUIRect MainView)
|
||||
{
|
||||
CUIRect TabBar, PlayerTab, DummyTab, ChangeInfo, QuickSearch, QuickSearchClearButton;
|
||||
CUIRect TabBar, PlayerTab, DummyTab, ChangeInfo, QuickSearch;
|
||||
MainView.HSplitTop(20.0f, &TabBar, &MainView);
|
||||
TabBar.VSplitMid(&TabBar, &ChangeInfo, 20.f);
|
||||
TabBar.VSplitMid(&PlayerTab, &DummyTab);
|
||||
|
@ -340,7 +340,10 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView)
|
|||
}
|
||||
|
||||
MainView.HSplitTop(10.0f, nullptr, &MainView);
|
||||
MainView.HSplitBottom(25.0f, &MainView, &QuickSearch);
|
||||
MainView.HSplitBottom(20.0f, &MainView, &QuickSearch);
|
||||
MainView.HSplitBottom(5.0f, &MainView, nullptr);
|
||||
QuickSearch.VSplitLeft(220.0f, &QuickSearch, nullptr);
|
||||
|
||||
int OldSelected = -1;
|
||||
static CListBox s_ListBox;
|
||||
s_ListBox.DoStart(48.0f, vpFilteredFlags.size(), 10, 3, OldSelected, &MainView);
|
||||
|
@ -378,30 +381,7 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView)
|
|||
SetNeedSendInfo();
|
||||
}
|
||||
|
||||
// render quick search
|
||||
QuickSearch.VSplitLeft(240.0f, &QuickSearch, nullptr);
|
||||
QuickSearch.HSplitTop(5.0f, nullptr, &QuickSearch);
|
||||
|
||||
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
|
||||
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
|
||||
Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
|
||||
float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f);
|
||||
QuickSearch.VSplitLeft(SearchWidth - 1.5f, nullptr, &QuickSearch);
|
||||
QuickSearch.VSplitLeft(5.0f, nullptr, &QuickSearch);
|
||||
QuickSearch.VSplitLeft(QuickSearch.w - 10.0f, &QuickSearch, &QuickSearchClearButton);
|
||||
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())
|
||||
{
|
||||
Ui()->SetActiveItem(&s_FlagFilterInput);
|
||||
s_FlagFilterInput.SelectAll();
|
||||
}
|
||||
s_FlagFilterInput.SetEmptyText(Localize("Search"));
|
||||
Ui()->DoClearableEditBox(&s_FlagFilterInput, &QuickSearch, 14.0f);
|
||||
Ui()->DoEditBox_Search(&s_FlagFilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed());
|
||||
}
|
||||
|
||||
struct CUISkin
|
||||
|
@ -770,8 +750,8 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
|
|||
CUIRect QuickSearch, DatabaseButton, DirectoryButton, RefreshButton;
|
||||
MainView.HSplitBottom(20.0f, &MainView, &QuickSearch);
|
||||
MainView.HSplitBottom(5.0f, &MainView, nullptr);
|
||||
QuickSearch.VSplitLeft(240.0f, &QuickSearch, &DatabaseButton);
|
||||
QuickSearch.VSplitRight(10.0f, &QuickSearch, nullptr);
|
||||
QuickSearch.VSplitLeft(220.0f, &QuickSearch, &DatabaseButton);
|
||||
DatabaseButton.VSplitLeft(10.0f, nullptr, &DatabaseButton);
|
||||
DatabaseButton.VSplitLeft(150.0f, &DatabaseButton, &DirectoryButton);
|
||||
DirectoryButton.VSplitRight(175.0f, nullptr, &DirectoryButton);
|
||||
DirectoryButton.VSplitRight(25.0f, &DirectoryButton, &RefreshButton);
|
||||
|
@ -904,24 +884,10 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
|
|||
SetNeedSendInfo();
|
||||
}
|
||||
|
||||
// Quick search
|
||||
static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString));
|
||||
if(Ui()->DoEditBox_Search(&s_SkinFilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()))
|
||||
{
|
||||
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
|
||||
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
|
||||
Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
|
||||
float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f);
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
QuickSearch.VSplitLeft(SearchWidth + 5.0f, nullptr, &QuickSearch);
|
||||
static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString));
|
||||
if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())
|
||||
{
|
||||
Ui()->SetActiveItem(&s_SkinFilterInput);
|
||||
s_SkinFilterInput.SelectAll();
|
||||
}
|
||||
s_SkinFilterInput.SetEmptyText(Localize("Search"));
|
||||
if(Ui()->DoClearableEditBox(&s_SkinFilterInput, &QuickSearch, 14.0f))
|
||||
m_SkinListNeedsUpdate = true;
|
||||
m_SkinListNeedsUpdate = true;
|
||||
}
|
||||
|
||||
static CButtonContainer s_SkinDatabaseButton;
|
||||
|
@ -3310,15 +3276,15 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
|
|||
}
|
||||
}
|
||||
else if(State >= IUpdater::GETTING_MANIFEST && State < IUpdater::NEED_RESTART)
|
||||
str_format(aBuf, sizeof(aBuf), Localize("Updating…"));
|
||||
str_copy(aBuf, Localize("Updating…"));
|
||||
else if(State == IUpdater::NEED_RESTART)
|
||||
{
|
||||
str_format(aBuf, sizeof(aBuf), Localize("DDNet Client updated!"));
|
||||
str_copy(aBuf, Localize("DDNet Client updated!"));
|
||||
m_NeedRestartUpdate = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
str_format(aBuf, sizeof(aBuf), Localize("No updates available"));
|
||||
str_copy(aBuf, Localize("No updates available"));
|
||||
UpdaterRect.VSplitLeft(TextRender()->TextWidth(14.0f, aBuf, -1, -1.0f) + 10.0f, &UpdaterRect, &Button);
|
||||
Button.VSplitLeft(100.0f, &Button, nullptr);
|
||||
static CButtonContainer s_ButtonUpdate;
|
||||
|
|
|
@ -282,24 +282,10 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
|
|||
}
|
||||
}
|
||||
|
||||
// Quick search
|
||||
static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString));
|
||||
if(Ui()->DoEditBox_Search(&s_SkinFilterInput, &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()))
|
||||
{
|
||||
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
|
||||
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
|
||||
Ui()->DoLabel(&QuickSearch, FontIcons::FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
|
||||
float SearchWidth = TextRender()->TextWidth(14.0f, FontIcons::FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f);
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
QuickSearch.VSplitLeft(SearchWidth + 5.0f, nullptr, &QuickSearch);
|
||||
static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString));
|
||||
if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())
|
||||
{
|
||||
Ui()->SetActiveItem(&s_SkinFilterInput);
|
||||
s_SkinFilterInput.SelectAll();
|
||||
}
|
||||
s_SkinFilterInput.SetEmptyText(Localize("Search"));
|
||||
if(Ui()->DoClearableEditBox(&s_SkinFilterInput, &QuickSearch, 14.0f))
|
||||
m_SkinListNeedsUpdate = true;
|
||||
m_SkinListNeedsUpdate = true;
|
||||
}
|
||||
|
||||
static CButtonContainer s_DirectoryButton;
|
||||
|
|
|
@ -352,7 +352,7 @@ int InitSearchList(std::vector<const TName *> &vpSearchList, std::vector<TName>
|
|||
|
||||
void CMenus::RenderSettingsCustom(CUIRect MainView)
|
||||
{
|
||||
CUIRect TabBar, CustomList, QuickSearch, QuickSearchClearButton, DirectoryButton, ReloadButton;
|
||||
CUIRect TabBar, CustomList, QuickSearch, DirectoryButton, ReloadButton;
|
||||
|
||||
MainView.HSplitTop(20.0f, &TabBar, &MainView);
|
||||
const float TabWidth = TabBar.w / NUMBER_OF_ASSETS_TABS;
|
||||
|
@ -599,29 +599,13 @@ void CMenus::RenderSettingsCustom(CUIRect MainView)
|
|||
}
|
||||
}
|
||||
|
||||
// render quick search
|
||||
// Quick search
|
||||
MainView.HSplitBottom(ms_ButtonHeight, &MainView, &QuickSearch);
|
||||
QuickSearch.VSplitLeft(220.0f, &QuickSearch, &DirectoryButton);
|
||||
QuickSearch.HSplitTop(5.0f, nullptr, &QuickSearch);
|
||||
if(Ui()->DoEditBox_Search(&s_aFilterInputs[s_CurCustomTab], &QuickSearch, 14.0f, !Ui()->IsPopupOpen() && m_pClient->m_GameConsole.IsClosed()))
|
||||
{
|
||||
MainView.HSplitBottom(ms_ButtonHeight, &MainView, &QuickSearch);
|
||||
QuickSearch.VSplitLeft(240.0f, &QuickSearch, &DirectoryButton);
|
||||
QuickSearch.HSplitTop(5.0f, 0, &QuickSearch);
|
||||
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
|
||||
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
|
||||
|
||||
Ui()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 14.0f, TEXTALIGN_ML);
|
||||
float SearchWidth = TextRender()->TextWidth(14.0f, FONT_ICON_MAGNIFYING_GLASS, -1, -1.0f);
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
QuickSearch.VSplitLeft(SearchWidth, 0, &QuickSearch);
|
||||
QuickSearch.VSplitLeft(5.0f, 0, &QuickSearch);
|
||||
QuickSearch.VSplitLeft(QuickSearch.w - 10.0f, &QuickSearch, &QuickSearchClearButton);
|
||||
if(Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())
|
||||
{
|
||||
Ui()->SetActiveItem(&s_aFilterInputs[s_CurCustomTab]);
|
||||
s_aFilterInputs[s_CurCustomTab].SelectAll();
|
||||
}
|
||||
s_aFilterInputs[s_CurCustomTab].SetEmptyText(Localize("Search"));
|
||||
if(Ui()->DoClearableEditBox(&s_aFilterInputs[s_CurCustomTab], &QuickSearch, 14.0f))
|
||||
gs_aInitCustomList[s_CurCustomTab] = true;
|
||||
gs_aInitCustomList[s_CurCustomTab] = true;
|
||||
}
|
||||
|
||||
DirectoryButton.HSplitTop(5.0f, 0, &DirectoryButton);
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include "menus.h"
|
||||
|
||||
using namespace FontIcons;
|
||||
|
||||
void CMenus::RenderStartMenu(CUIRect MainView)
|
||||
{
|
||||
GameClient()->m_MenuBackground.ChangePosition(CMenuBackground::POS_START);
|
||||
|
@ -186,13 +188,27 @@ void CMenus::RenderStartMenu(CUIRect MainView)
|
|||
}
|
||||
|
||||
// render version
|
||||
CUIRect VersionUpdate, CurVersion;
|
||||
MainView.HSplitBottom(20.0f, nullptr, &VersionUpdate);
|
||||
VersionUpdate.VSplitRight(50.0f, &CurVersion, nullptr);
|
||||
VersionUpdate.VMargin(VMargin, &VersionUpdate);
|
||||
|
||||
CUIRect CurVersion, ConsoleButton;
|
||||
MainView.HSplitBottom(45.0f, nullptr, &CurVersion);
|
||||
CurVersion.VSplitRight(40.0f, &CurVersion, nullptr);
|
||||
CurVersion.HSplitTop(20.0f, &ConsoleButton, &CurVersion);
|
||||
CurVersion.HSplitTop(5.0f, nullptr, &CurVersion);
|
||||
ConsoleButton.VSplitRight(40.0f, nullptr, &ConsoleButton);
|
||||
Ui()->DoLabel(&CurVersion, GAME_RELEASE_VERSION, 14.0f, TEXTALIGN_MR);
|
||||
|
||||
static CButtonContainer s_ConsoleButton;
|
||||
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
|
||||
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
|
||||
if(DoButton_Menu(&s_ConsoleButton, FONT_ICON_TERMINAL, 0, &ConsoleButton, nullptr, IGraphics::CORNER_ALL, 5.0f, 0.0f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.1f)))
|
||||
{
|
||||
GameClient()->m_GameConsole.Toggle(CGameConsole::CONSOLETYPE_LOCAL);
|
||||
}
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
|
||||
CUIRect VersionUpdate;
|
||||
MainView.HSplitBottom(20.0f, nullptr, &VersionUpdate);
|
||||
VersionUpdate.VMargin(VMargin, &VersionUpdate);
|
||||
#if defined(CONF_AUTOUPDATE)
|
||||
CUIRect UpdateButton;
|
||||
VersionUpdate.VSplitRight(100.0f, &VersionUpdate, &UpdateButton);
|
||||
|
@ -240,12 +256,12 @@ void CMenus::RenderStartMenu(CUIRect MainView)
|
|||
}
|
||||
else if(State == IUpdater::FAIL)
|
||||
{
|
||||
str_format(aBuf, sizeof(aBuf), Localize("Update failed! Check log…"));
|
||||
str_copy(aBuf, Localize("Update failed! Check log…"));
|
||||
TextRender()->TextColor(1.0f, 0.4f, 0.4f, 1.0f);
|
||||
}
|
||||
else if(State == IUpdater::NEED_RESTART)
|
||||
{
|
||||
str_format(aBuf, sizeof(aBuf), Localize("DDNet Client updated!"));
|
||||
str_copy(aBuf, Localize("DDNet Client updated!"));
|
||||
TextRender()->TextColor(1.0f, 0.4f, 0.4f, 1.0f);
|
||||
}
|
||||
Ui()->DoLabel(&VersionUpdate, aBuf, 14.0f, TEXTALIGN_ML);
|
||||
|
|
|
@ -349,240 +349,247 @@ void CScoreboard::RenderScoreboard(CUIRect Scoreboard, int Team, int CountStart,
|
|||
char aBuf[64];
|
||||
int MaxTeamSize = m_pClient->Config()->m_SvMaxTeamSize;
|
||||
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
for(int RenderDead = 0; RenderDead < 2; RenderDead++)
|
||||
{
|
||||
// make sure that we render the correct team
|
||||
const CNetObj_PlayerInfo *pInfo = GameClient()->m_Snap.m_apInfoByDDTeamScore[i];
|
||||
if(!pInfo || pInfo->m_Team != Team)
|
||||
continue;
|
||||
|
||||
if(CountRendered++ < CountStart)
|
||||
continue;
|
||||
|
||||
int DDTeam = GameClient()->m_Teams.Team(pInfo->m_ClientId);
|
||||
int NextDDTeam = 0;
|
||||
bool RenderDead = Client()->m_TranslationContext.m_aClients[pInfo->m_ClientId].m_PlayerFlags7 & protocol7::PLAYERFLAG_DEAD;
|
||||
|
||||
ColorRGBA TextColor = TextRender()->DefaultTextColor();
|
||||
TextColor.a = RenderDead ? 0.5f : 1.0f;
|
||||
TextRender()->TextColor(TextColor);
|
||||
|
||||
for(int j = i + 1; j < MAX_CLIENTS; j++)
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
const CNetObj_PlayerInfo *pInfoNext = GameClient()->m_Snap.m_apInfoByDDTeamScore[j];
|
||||
if(!pInfoNext || pInfoNext->m_Team != Team)
|
||||
// make sure that we render the correct team
|
||||
const CNetObj_PlayerInfo *pInfo = GameClient()->m_Snap.m_apInfoByDDTeamScore[i];
|
||||
if(!pInfo || pInfo->m_Team != Team)
|
||||
continue;
|
||||
|
||||
NextDDTeam = GameClient()->m_Teams.Team(pInfoNext->m_ClientId);
|
||||
break;
|
||||
}
|
||||
if(CountRendered++ < CountStart)
|
||||
continue;
|
||||
|
||||
if(PrevDDTeam == -1)
|
||||
{
|
||||
for(int j = i - 1; j >= 0; j--)
|
||||
int DDTeam = GameClient()->m_Teams.Team(pInfo->m_ClientId);
|
||||
int NextDDTeam = 0;
|
||||
bool IsDead = Client()->m_TranslationContext.m_aClients[pInfo->m_ClientId].m_PlayerFlags7 & protocol7::PLAYERFLAG_DEAD;
|
||||
if(!RenderDead && IsDead)
|
||||
continue;
|
||||
if(RenderDead && !IsDead)
|
||||
continue;
|
||||
|
||||
ColorRGBA TextColor = TextRender()->DefaultTextColor();
|
||||
TextColor.a = RenderDead ? 0.5f : 1.0f;
|
||||
TextRender()->TextColor(TextColor);
|
||||
|
||||
for(int j = i + 1; j < MAX_CLIENTS; j++)
|
||||
{
|
||||
const CNetObj_PlayerInfo *pInfoPrev = GameClient()->m_Snap.m_apInfoByDDTeamScore[j];
|
||||
if(!pInfoPrev || pInfoPrev->m_Team != Team)
|
||||
const CNetObj_PlayerInfo *pInfoNext = GameClient()->m_Snap.m_apInfoByDDTeamScore[j];
|
||||
if(!pInfoNext || pInfoNext->m_Team != Team)
|
||||
continue;
|
||||
|
||||
PrevDDTeam = GameClient()->m_Teams.Team(pInfoPrev->m_ClientId);
|
||||
NextDDTeam = GameClient()->m_Teams.Team(pInfoNext->m_ClientId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CUIRect RowAndSpacing, Row;
|
||||
Scoreboard.HSplitTop(LineHeight + Spacing, &RowAndSpacing, &Scoreboard);
|
||||
RowAndSpacing.HSplitTop(LineHeight, &Row, nullptr);
|
||||
|
||||
// team background
|
||||
if(DDTeam != TEAM_FLOCK)
|
||||
{
|
||||
const ColorRGBA Color = GameClient()->GetDDTeamColor(DDTeam).WithAlpha(0.5f);
|
||||
int TeamRectCorners = 0;
|
||||
if(PrevDDTeam != DDTeam)
|
||||
if(PrevDDTeam == -1)
|
||||
{
|
||||
TeamRectCorners |= IGraphics::CORNER_T;
|
||||
State.m_TeamStartX = Row.x;
|
||||
State.m_TeamStartY = Row.y;
|
||||
}
|
||||
if(NextDDTeam != DDTeam)
|
||||
TeamRectCorners |= IGraphics::CORNER_B;
|
||||
RowAndSpacing.Draw(Color, TeamRectCorners, RoundRadius);
|
||||
|
||||
CurrentDDTeamSize++;
|
||||
|
||||
if(NextDDTeam != DDTeam)
|
||||
{
|
||||
const float TeamFontSize = FontSize / 1.5f;
|
||||
|
||||
if(NumPlayers > 8)
|
||||
for(int j = i - 1; j >= 0; j--)
|
||||
{
|
||||
if(DDTeam == TEAM_SUPER)
|
||||
str_copy(aBuf, Localize("Super"));
|
||||
else if(CurrentDDTeamSize <= 1)
|
||||
str_format(aBuf, sizeof(aBuf), "%d", DDTeam);
|
||||
const CNetObj_PlayerInfo *pInfoPrev = GameClient()->m_Snap.m_apInfoByDDTeamScore[j];
|
||||
if(!pInfoPrev || pInfoPrev->m_Team != Team)
|
||||
continue;
|
||||
|
||||
PrevDDTeam = GameClient()->m_Teams.Team(pInfoPrev->m_ClientId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CUIRect RowAndSpacing, Row;
|
||||
Scoreboard.HSplitTop(LineHeight + Spacing, &RowAndSpacing, &Scoreboard);
|
||||
RowAndSpacing.HSplitTop(LineHeight, &Row, nullptr);
|
||||
|
||||
// team background
|
||||
if(DDTeam != TEAM_FLOCK)
|
||||
{
|
||||
const ColorRGBA Color = GameClient()->GetDDTeamColor(DDTeam).WithAlpha(0.5f);
|
||||
int TeamRectCorners = 0;
|
||||
if(PrevDDTeam != DDTeam)
|
||||
{
|
||||
TeamRectCorners |= IGraphics::CORNER_T;
|
||||
State.m_TeamStartX = Row.x;
|
||||
State.m_TeamStartY = Row.y;
|
||||
}
|
||||
if(NextDDTeam != DDTeam)
|
||||
TeamRectCorners |= IGraphics::CORNER_B;
|
||||
RowAndSpacing.Draw(Color, TeamRectCorners, RoundRadius);
|
||||
|
||||
CurrentDDTeamSize++;
|
||||
|
||||
if(NextDDTeam != DDTeam)
|
||||
{
|
||||
const float TeamFontSize = FontSize / 1.5f;
|
||||
|
||||
if(NumPlayers > 8)
|
||||
{
|
||||
if(DDTeam == TEAM_SUPER)
|
||||
str_copy(aBuf, Localize("Super"));
|
||||
else if(CurrentDDTeamSize <= 1)
|
||||
str_format(aBuf, sizeof(aBuf), "%d", DDTeam);
|
||||
else
|
||||
str_format(aBuf, sizeof(aBuf), Localize("%d\n(%d/%d)", "Team and size"), DDTeam, CurrentDDTeamSize, MaxTeamSize);
|
||||
TextRender()->Text(State.m_TeamStartX, maximum(State.m_TeamStartY + Row.h / 2.0f - TeamFontSize, State.m_TeamStartY + 3.0f /* padding top */), TeamFontSize, aBuf);
|
||||
}
|
||||
else
|
||||
str_format(aBuf, sizeof(aBuf), Localize("%d\n(%d/%d)", "Team and size"), DDTeam, CurrentDDTeamSize, MaxTeamSize);
|
||||
TextRender()->Text(State.m_TeamStartX, maximum(State.m_TeamStartY + Row.h / 2.0f - TeamFontSize, State.m_TeamStartY + 3.0f /* padding top */), TeamFontSize, aBuf);
|
||||
{
|
||||
if(DDTeam == TEAM_SUPER)
|
||||
str_copy(aBuf, Localize("Super"));
|
||||
else if(CurrentDDTeamSize > 1)
|
||||
str_format(aBuf, sizeof(aBuf), Localize("Team %d (%d/%d)"), DDTeam, CurrentDDTeamSize, MaxTeamSize);
|
||||
else
|
||||
str_format(aBuf, sizeof(aBuf), Localize("Team %d"), DDTeam);
|
||||
TextRender()->Text(Row.x + Row.w / 2.0f - TextRender()->TextWidth(TeamFontSize, aBuf) / 2.0f + 10.0f, Row.y + Row.h, TeamFontSize, aBuf);
|
||||
}
|
||||
|
||||
CurrentDDTeamSize = 0;
|
||||
}
|
||||
}
|
||||
PrevDDTeam = DDTeam;
|
||||
|
||||
// background so it's easy to find the local player or the followed one in spectator mode
|
||||
if((!GameClient()->m_Snap.m_SpecInfo.m_Active && pInfo->m_Local) ||
|
||||
(GameClient()->m_Snap.m_SpecInfo.m_SpectatorId == SPEC_FREEVIEW && pInfo->m_Local) ||
|
||||
(GameClient()->m_Snap.m_SpecInfo.m_Active && pInfo->m_ClientId == GameClient()->m_Snap.m_SpecInfo.m_SpectatorId))
|
||||
{
|
||||
Row.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, RoundRadius);
|
||||
}
|
||||
|
||||
// score
|
||||
if(Race7)
|
||||
{
|
||||
if(pInfo->m_Score == -1)
|
||||
{
|
||||
aBuf[0] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
if(DDTeam == TEAM_SUPER)
|
||||
str_copy(aBuf, Localize("Super"));
|
||||
else if(CurrentDDTeamSize > 1)
|
||||
str_format(aBuf, sizeof(aBuf), Localize("Team %d (%d/%d)"), DDTeam, CurrentDDTeamSize, MaxTeamSize);
|
||||
else
|
||||
str_format(aBuf, sizeof(aBuf), Localize("Team %d"), DDTeam);
|
||||
TextRender()->Text(Row.x + Row.w / 2.0f - TextRender()->TextWidth(TeamFontSize, aBuf) / 2.0f + 10.0f, Row.y + Row.h, TeamFontSize, aBuf);
|
||||
// 0.7 uses milliseconds and ddnets str_time wants centiseconds
|
||||
// 0.7 servers can also send the amount of precision the client should use
|
||||
// we ignore that and always show 3 digit precision
|
||||
str_time((int64_t)absolute(pInfo->m_Score / 10), TIME_MINS_CENTISECS, aBuf, sizeof(aBuf));
|
||||
}
|
||||
|
||||
CurrentDDTeamSize = 0;
|
||||
}
|
||||
}
|
||||
PrevDDTeam = DDTeam;
|
||||
|
||||
// background so it's easy to find the local player or the followed one in spectator mode
|
||||
if((!GameClient()->m_Snap.m_SpecInfo.m_Active && pInfo->m_Local) ||
|
||||
(GameClient()->m_Snap.m_SpecInfo.m_SpectatorId == SPEC_FREEVIEW && pInfo->m_Local) ||
|
||||
(GameClient()->m_Snap.m_SpecInfo.m_Active && pInfo->m_ClientId == GameClient()->m_Snap.m_SpecInfo.m_SpectatorId))
|
||||
{
|
||||
Row.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, RoundRadius);
|
||||
}
|
||||
|
||||
// score
|
||||
if(Race7)
|
||||
{
|
||||
if(pInfo->m_Score == -1)
|
||||
else if(TimeScore)
|
||||
{
|
||||
aBuf[0] = '\0';
|
||||
if(pInfo->m_Score == -9999)
|
||||
{
|
||||
aBuf[0] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
str_time((int64_t)absolute(pInfo->m_Score) * 100, TIME_HOURS, aBuf, sizeof(aBuf));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 0.7 uses milliseconds and ddnets str_time wants centiseconds
|
||||
// 0.7 servers can also send the amount of precision the client should use
|
||||
// we ignore that and always show 3 digit precision
|
||||
str_time((int64_t)absolute(pInfo->m_Score / 10), TIME_MINS_CENTISECS, aBuf, sizeof(aBuf));
|
||||
str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Score, -999, 99999));
|
||||
}
|
||||
}
|
||||
else if(TimeScore)
|
||||
{
|
||||
if(pInfo->m_Score == -9999)
|
||||
TextRender()->Text(ScoreOffset + ScoreLength - TextRender()->TextWidth(FontSize, aBuf), Row.y + (Row.h - FontSize) / 2.0f, FontSize, aBuf);
|
||||
|
||||
// CTF flag
|
||||
if(pGameInfoObj && (pGameInfoObj->m_GameFlags & GAMEFLAG_FLAGS) &&
|
||||
pGameDataObj && (pGameDataObj->m_FlagCarrierRed == pInfo->m_ClientId || pGameDataObj->m_FlagCarrierBlue == pInfo->m_ClientId))
|
||||
{
|
||||
aBuf[0] = '\0';
|
||||
Graphics()->BlendNormal();
|
||||
Graphics()->TextureSet(pGameDataObj->m_FlagCarrierBlue == pInfo->m_ClientId ? GameClient()->m_GameSkin.m_SpriteFlagBlue : GameClient()->m_GameSkin.m_SpriteFlagRed);
|
||||
Graphics()->QuadsBegin();
|
||||
Graphics()->QuadsSetSubset(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
IGraphics::CQuadItem QuadItem(TeeOffset, Row.y - 5.0f - Spacing / 2.0f, Row.h / 2.0f, Row.h);
|
||||
Graphics()->QuadsDrawTL(&QuadItem, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
}
|
||||
|
||||
const CGameClient::CClientData &ClientData = GameClient()->m_aClients[pInfo->m_ClientId];
|
||||
|
||||
// skin
|
||||
if(RenderDead)
|
||||
{
|
||||
Graphics()->BlendNormal();
|
||||
Graphics()->TextureSet(client_data7::g_pData->m_aImages[client_data7::IMAGE_DEADTEE].m_Id);
|
||||
Graphics()->QuadsBegin();
|
||||
if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_TEAMS)
|
||||
{
|
||||
ColorRGBA Color = m_pClient->m_Skins7.GetTeamColor(true, 0, m_pClient->m_aClients[pInfo->m_ClientId].m_Team, protocol7::SKINPART_BODY);
|
||||
Graphics()->SetColor(Color.r, Color.g, Color.b, Color.a);
|
||||
}
|
||||
CTeeRenderInfo TeeInfo = m_pClient->m_aClients[pInfo->m_ClientId].m_RenderInfo;
|
||||
TeeInfo.m_Size *= TeeSizeMod;
|
||||
IGraphics::CQuadItem QuadItem(TeeOffset, Row.y, TeeInfo.m_Size, TeeInfo.m_Size);
|
||||
Graphics()->QuadsDrawTL(&QuadItem, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
}
|
||||
else
|
||||
{
|
||||
str_time((int64_t)absolute(pInfo->m_Score) * 100, TIME_HOURS, aBuf, sizeof(aBuf));
|
||||
CTeeRenderInfo TeeInfo = ClientData.m_RenderInfo;
|
||||
TeeInfo.m_Size *= TeeSizeMod;
|
||||
vec2 OffsetToMid;
|
||||
CRenderTools::GetRenderTeeOffsetToRenderedTee(CAnimState::GetIdle(), &TeeInfo, OffsetToMid);
|
||||
const vec2 TeeRenderPos = vec2(TeeOffset + TeeLength / 2, Row.y + Row.h / 2.0f + OffsetToMid.y);
|
||||
RenderTools()->RenderTee(CAnimState::GetIdle(), &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), TeeRenderPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Score, -999, 99999));
|
||||
}
|
||||
TextRender()->Text(ScoreOffset + ScoreLength - TextRender()->TextWidth(FontSize, aBuf), Row.y + (Row.h - FontSize) / 2.0f, FontSize, aBuf);
|
||||
|
||||
// CTF flag
|
||||
if(pGameInfoObj && (pGameInfoObj->m_GameFlags & GAMEFLAG_FLAGS) &&
|
||||
pGameDataObj && (pGameDataObj->m_FlagCarrierRed == pInfo->m_ClientId || pGameDataObj->m_FlagCarrierBlue == pInfo->m_ClientId))
|
||||
{
|
||||
Graphics()->BlendNormal();
|
||||
Graphics()->TextureSet(pGameDataObj->m_FlagCarrierBlue == pInfo->m_ClientId ? GameClient()->m_GameSkin.m_SpriteFlagBlue : GameClient()->m_GameSkin.m_SpriteFlagRed);
|
||||
Graphics()->QuadsBegin();
|
||||
Graphics()->QuadsSetSubset(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
IGraphics::CQuadItem QuadItem(TeeOffset, Row.y - 5.0f - Spacing / 2.0f, Row.h / 2.0f, Row.h);
|
||||
Graphics()->QuadsDrawTL(&QuadItem, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
}
|
||||
|
||||
const CGameClient::CClientData &ClientData = GameClient()->m_aClients[pInfo->m_ClientId];
|
||||
|
||||
// skin
|
||||
if(RenderDead)
|
||||
{
|
||||
Graphics()->BlendNormal();
|
||||
Graphics()->TextureSet(client_data7::g_pData->m_aImages[client_data7::IMAGE_DEADTEE].m_Id);
|
||||
Graphics()->QuadsBegin();
|
||||
if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_TEAMS)
|
||||
// name
|
||||
{
|
||||
ColorRGBA Color = m_pClient->m_Skins7.GetTeamColor(true, 0, m_pClient->m_aClients[pInfo->m_ClientId].m_Team, protocol7::SKINPART_BODY);
|
||||
Graphics()->SetColor(Color.r, Color.g, Color.b, Color.a);
|
||||
CTextCursor Cursor;
|
||||
TextRender()->SetCursor(&Cursor, NameOffset, Row.y + (Row.h - FontSize) / 2.0f, FontSize, TEXTFLAG_RENDER | TEXTFLAG_ELLIPSIS_AT_END);
|
||||
Cursor.m_LineWidth = NameLength;
|
||||
if(ClientData.m_AuthLevel)
|
||||
{
|
||||
TextRender()->TextColor(color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClAuthedPlayerColor)));
|
||||
}
|
||||
if(g_Config.m_ClShowIds)
|
||||
{
|
||||
char aClientId[16];
|
||||
GameClient()->FormatClientId(pInfo->m_ClientId, aClientId, EClientIdFormat::INDENT_AUTO);
|
||||
TextRender()->TextEx(&Cursor, aClientId);
|
||||
}
|
||||
TextRender()->TextEx(&Cursor, ClientData.m_aName);
|
||||
|
||||
// ready / watching
|
||||
if(Client()->IsSixup() && Client()->m_TranslationContext.m_aClients[pInfo->m_ClientId].m_PlayerFlags7 & protocol7::PLAYERFLAG_READY)
|
||||
{
|
||||
TextRender()->TextColor(0.1f, 1.0f, 0.1f, TextColor.a);
|
||||
TextRender()->TextEx(&Cursor, "✓");
|
||||
}
|
||||
}
|
||||
CTeeRenderInfo TeeInfo = m_pClient->m_aClients[pInfo->m_ClientId].m_RenderInfo;
|
||||
TeeInfo.m_Size *= TeeSizeMod;
|
||||
IGraphics::CQuadItem QuadItem(TeeOffset, Row.y, TeeInfo.m_Size, TeeInfo.m_Size);
|
||||
Graphics()->QuadsDrawTL(&QuadItem, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
}
|
||||
else
|
||||
{
|
||||
CTeeRenderInfo TeeInfo = ClientData.m_RenderInfo;
|
||||
TeeInfo.m_Size *= TeeSizeMod;
|
||||
vec2 OffsetToMid;
|
||||
CRenderTools::GetRenderTeeOffsetToRenderedTee(CAnimState::GetIdle(), &TeeInfo, OffsetToMid);
|
||||
const vec2 TeeRenderPos = vec2(TeeOffset + TeeLength / 2, Row.y + Row.h / 2.0f + OffsetToMid.y);
|
||||
RenderTools()->RenderTee(CAnimState::GetIdle(), &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), TeeRenderPos);
|
||||
}
|
||||
|
||||
// name
|
||||
{
|
||||
CTextCursor Cursor;
|
||||
TextRender()->SetCursor(&Cursor, NameOffset, Row.y + (Row.h - FontSize) / 2.0f, FontSize, TEXTFLAG_RENDER | TEXTFLAG_ELLIPSIS_AT_END);
|
||||
Cursor.m_LineWidth = NameLength;
|
||||
if(ClientData.m_AuthLevel)
|
||||
// clan
|
||||
{
|
||||
TextRender()->TextColor(color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClAuthedPlayerColor)));
|
||||
if(str_comp(ClientData.m_aClan, GameClient()->m_aClients[GameClient()->m_aLocalIds[g_Config.m_ClDummy]].m_aClan) == 0)
|
||||
{
|
||||
TextRender()->TextColor(color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClSameClanColor)));
|
||||
}
|
||||
else
|
||||
{
|
||||
TextRender()->TextColor(TextColor);
|
||||
}
|
||||
CTextCursor Cursor;
|
||||
TextRender()->SetCursor(&Cursor, ClanOffset + (ClanLength - minimum(TextRender()->TextWidth(FontSize, ClientData.m_aClan), ClanLength)) / 2.0f, Row.y + (Row.h - FontSize) / 2.0f, FontSize, TEXTFLAG_RENDER | TEXTFLAG_ELLIPSIS_AT_END);
|
||||
Cursor.m_LineWidth = ClanLength;
|
||||
TextRender()->TextEx(&Cursor, ClientData.m_aClan);
|
||||
}
|
||||
if(g_Config.m_ClShowIds)
|
||||
{
|
||||
char aClientId[16];
|
||||
GameClient()->FormatClientId(pInfo->m_ClientId, aClientId, EClientIdFormat::INDENT_AUTO);
|
||||
TextRender()->TextEx(&Cursor, aClientId);
|
||||
}
|
||||
TextRender()->TextEx(&Cursor, ClientData.m_aName);
|
||||
|
||||
// ready / watching
|
||||
if(Client()->IsSixup() && Client()->m_TranslationContext.m_aClients[pInfo->m_ClientId].m_PlayerFlags7 & protocol7::PLAYERFLAG_READY)
|
||||
{
|
||||
TextRender()->TextColor(0.1f, 1.0f, 0.1f, TextColor.a);
|
||||
TextRender()->TextEx(&Cursor, "✓");
|
||||
}
|
||||
}
|
||||
// country flag
|
||||
GameClient()->m_CountryFlags.Render(ClientData.m_Country, ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f),
|
||||
CountryOffset, Row.y + (Spacing + TeeSizeMod * 5.0f) / 2.0f, CountryLength, Row.h - Spacing - TeeSizeMod * 5.0f);
|
||||
|
||||
// clan
|
||||
{
|
||||
if(str_comp(ClientData.m_aClan, GameClient()->m_aClients[GameClient()->m_aLocalIds[g_Config.m_ClDummy]].m_aClan) == 0)
|
||||
// ping
|
||||
if(g_Config.m_ClEnablePingColor)
|
||||
{
|
||||
TextRender()->TextColor(color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClSameClanColor)));
|
||||
TextRender()->TextColor(color_cast<ColorRGBA>(ColorHSLA((300.0f - clamp(pInfo->m_Latency, 0, 300)) / 1000.0f, 1.0f, 0.5f)));
|
||||
}
|
||||
else
|
||||
{
|
||||
TextRender()->TextColor(TextColor);
|
||||
TextRender()->TextColor(TextRender()->DefaultTextColor());
|
||||
}
|
||||
CTextCursor Cursor;
|
||||
TextRender()->SetCursor(&Cursor, ClanOffset + (ClanLength - minimum(TextRender()->TextWidth(FontSize, ClientData.m_aClan), ClanLength)) / 2.0f, Row.y + (Row.h - FontSize) / 2.0f, FontSize, TEXTFLAG_RENDER | TEXTFLAG_ELLIPSIS_AT_END);
|
||||
Cursor.m_LineWidth = ClanLength;
|
||||
TextRender()->TextEx(&Cursor, ClientData.m_aClan);
|
||||
}
|
||||
|
||||
// country flag
|
||||
GameClient()->m_CountryFlags.Render(ClientData.m_Country, ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f),
|
||||
CountryOffset, Row.y + (Spacing + TeeSizeMod * 5.0f) / 2.0f, CountryLength, Row.h - Spacing - TeeSizeMod * 5.0f);
|
||||
|
||||
// ping
|
||||
if(g_Config.m_ClEnablePingColor)
|
||||
{
|
||||
TextRender()->TextColor(color_cast<ColorRGBA>(ColorHSLA((300.0f - clamp(pInfo->m_Latency, 0, 300)) / 1000.0f, 1.0f, 0.5f)));
|
||||
}
|
||||
else
|
||||
{
|
||||
str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Latency, 0, 999));
|
||||
TextRender()->Text(PingOffset + PingLength - TextRender()->TextWidth(FontSize, aBuf), Row.y + (Row.h - FontSize) / 2.0f, FontSize, aBuf);
|
||||
TextRender()->TextColor(TextRender()->DefaultTextColor());
|
||||
}
|
||||
str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Latency, 0, 999));
|
||||
TextRender()->Text(PingOffset + PingLength - TextRender()->TextWidth(FontSize, aBuf), Row.y + (Row.h - FontSize) / 2.0f, FontSize, aBuf);
|
||||
TextRender()->TextColor(TextRender()->DefaultTextColor());
|
||||
|
||||
if(CountRendered == CountEnd)
|
||||
break;
|
||||
if(CountRendered == CountEnd)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -790,15 +797,16 @@ bool CScoreboard::Active() const
|
|||
if(m_Active)
|
||||
return true;
|
||||
|
||||
const CNetObj_GameInfo *pGameInfoObj = GameClient()->m_Snap.m_pGameInfoObj;
|
||||
if(GameClient()->m_Snap.m_pLocalInfo && !GameClient()->m_Snap.m_SpecInfo.m_Active)
|
||||
{
|
||||
// we are not a spectator, check if we are dead
|
||||
if(!GameClient()->m_Snap.m_pLocalCharacter && g_Config.m_ClScoreboardOnDeath)
|
||||
// we are not a spectator, check if we are dead and the game isn't paused
|
||||
if(!GameClient()->m_Snap.m_pLocalCharacter && g_Config.m_ClScoreboardOnDeath &&
|
||||
!(pGameInfoObj && pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_PAUSED))
|
||||
return true;
|
||||
}
|
||||
|
||||
// if the game is over
|
||||
const CNetObj_GameInfo *pGameInfoObj = GameClient()->m_Snap.m_pGameInfoObj;
|
||||
if(pGameInfoObj && pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_GAMEOVER)
|
||||
return true;
|
||||
|
||||
|
|
|
@ -2071,6 +2071,32 @@ void CGameClient::OnNewSnapshot()
|
|||
m_Effects.AirJump(Pos, Alpha);
|
||||
}
|
||||
|
||||
if(g_Config.m_ClFreezeStars && !m_SuppressEvents)
|
||||
{
|
||||
for(auto &Character : m_Snap.m_aCharacters)
|
||||
{
|
||||
if(Character.m_Active && Character.m_HasExtendedData && Character.m_PrevExtendedData)
|
||||
{
|
||||
int FreezeTimeNow = Character.m_ExtendedData.m_FreezeEnd - Client()->GameTick(g_Config.m_ClDummy);
|
||||
int FreezeTimePrev = Character.m_PrevExtendedData->m_FreezeEnd - Client()->PrevGameTick(g_Config.m_ClDummy);
|
||||
vec2 Pos = vec2(Character.m_Cur.m_X, Character.m_Cur.m_Y);
|
||||
int StarsNow = (FreezeTimeNow + 1) / Client()->GameTickSpeed();
|
||||
int StarsPrev = (FreezeTimePrev + 1) / Client()->GameTickSpeed();
|
||||
if(StarsNow < StarsPrev || (StarsPrev == 0 && StarsNow > 0))
|
||||
{
|
||||
int Amount = StarsNow + 1;
|
||||
float Mid = 3 * pi / 2;
|
||||
float Min = Mid - pi / 3;
|
||||
float Max = Mid + pi / 3;
|
||||
for(int j = 0; j < Amount; j++)
|
||||
{
|
||||
float Angle = mix(Min, Max, (j + 1) / (float)(Amount + 2));
|
||||
m_Effects.DamageIndicator(Pos, direction(Angle));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(m_Snap.m_LocalClientId != m_PrevLocalId)
|
||||
m_PredictedDummyId = m_PrevLocalId;
|
||||
m_PrevLocalId = m_Snap.m_LocalClientId;
|
||||
|
@ -2500,7 +2526,7 @@ void CGameClient::SendSwitchTeam(int Team)
|
|||
void CGameClient::SendStartInfo7(bool Dummy) const
|
||||
{
|
||||
protocol7::CNetMsg_Cl_StartInfo Msg;
|
||||
Msg.m_pName = Dummy ? Client()->DummyName() : Config()->m_PlayerName;
|
||||
Msg.m_pName = Dummy ? Client()->DummyName() : Client()->PlayerName();
|
||||
Msg.m_pClan = Dummy ? Config()->m_ClDummyClan : Config()->m_PlayerClan;
|
||||
Msg.m_Country = Dummy ? Config()->m_ClDummyCountry : Config()->m_PlayerCountry;
|
||||
for(int p = 0; p < protocol7::NUM_SKINPARTS; p++)
|
||||
|
|
|
@ -57,7 +57,7 @@ void CUIElement::SUIElementRect::Reset()
|
|||
void CUIElement::SUIElementRect::Draw(const CUIRect *pRect, ColorRGBA Color, int Corners, float Rounding)
|
||||
{
|
||||
bool NeedsRecreate = false;
|
||||
if(m_UIRectQuadContainer == -1 || m_Width != pRect->w || m_Height != pRect->h || mem_comp(&m_QuadColor, &Color, sizeof(Color)) != 0)
|
||||
if(m_UIRectQuadContainer == -1 || m_Width != pRect->w || m_Height != pRect->h || m_QuadColor != Color)
|
||||
{
|
||||
m_pParent->Ui()->Graphics()->DeleteQuadContainer(m_UIRectQuadContainer);
|
||||
NeedsRecreate = true;
|
||||
|
@ -1004,6 +1004,25 @@ bool CUi::DoClearableEditBox(CLineInput *pLineInput, const CUIRect *pRect, float
|
|||
return ReturnValue;
|
||||
}
|
||||
|
||||
bool CUi::DoEditBox_Search(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, bool HotkeyEnabled)
|
||||
{
|
||||
CUIRect QuickSearch = *pRect;
|
||||
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
|
||||
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
|
||||
DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, FontSize, TEXTALIGN_ML);
|
||||
const float SearchWidth = TextRender()->TextWidth(FontSize, FONT_ICON_MAGNIFYING_GLASS);
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
QuickSearch.VSplitLeft(SearchWidth + 5.0f, nullptr, &QuickSearch);
|
||||
if(HotkeyEnabled && Input()->ModifierIsPressed() && Input()->KeyPress(KEY_F))
|
||||
{
|
||||
SetActiveItem(pLineInput);
|
||||
pLineInput->SelectAll();
|
||||
}
|
||||
pLineInput->SetEmptyText(Localize("Search"));
|
||||
return DoClearableEditBox(pLineInput, &QuickSearch, FontSize);
|
||||
}
|
||||
|
||||
int CUi::DoButton_Menu(CUIElement &UIElement, const CButtonContainer *pId, const std::function<const char *()> &GetTextLambda, const CUIRect *pRect, const SMenuButtonProperties &Props)
|
||||
{
|
||||
CUIRect Text = *pRect, DropDownIcon;
|
||||
|
|
|
@ -603,6 +603,24 @@ public:
|
|||
*/
|
||||
bool DoClearableEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, int Corners = IGraphics::CORNER_ALL, const std::vector<STextColorSplit> &vColorSplits = {});
|
||||
|
||||
/**
|
||||
* Creates an input field with a search icon and a clear [x] button attached to it.
|
||||
* The input will have default text "Search" and the hotkey Ctrl+F can be used to activate the input.
|
||||
*
|
||||
* @see DoEditBox
|
||||
*
|
||||
* @param pLineInput This pointer will be stored and written to on next user input.
|
||||
* So you can not pass in a pointer that goes out of scope such as a local variable.
|
||||
* Pass in either a member variable of the current class or a static variable.
|
||||
* For example ```static CLineInputBuffered<IO_MAX_PATH_LENGTH> s_MyInput;```
|
||||
* @param pRect the UI rect it will attach to
|
||||
* @param FontSize Size of the font (`10.0f`, `12.0f` and `14.0f` are commonly used here)
|
||||
* @param HotkeyEnabled Whether the hotkey to enable this editbox is currently enabled.
|
||||
*
|
||||
* @return true if the value of the input field changed since the last call.
|
||||
*/
|
||||
bool DoEditBox_Search(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, bool HotkeyEnabled);
|
||||
|
||||
int DoButton_Menu(CUIElement &UIElement, const CButtonContainer *pId, const std::function<const char *()> &GetTextLambda, const CUIRect *pRect, const SMenuButtonProperties &Props = {});
|
||||
// only used for popup menus
|
||||
int DoButton_PopupMenu(CButtonContainer *pButtonContainer, const char *pText, const CUIRect *pRect, float Size, int Align, float Padding = 0.0f, bool TransparentInactive = false, bool Enabled = true);
|
||||
|
|
|
@ -1102,10 +1102,10 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
|
|||
// grid button
|
||||
TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
|
||||
static int s_GridButton = 0;
|
||||
if(DoButton_FontIcon(&s_GridButton, FONT_ICON_BORDER_ALL, MapView()->MapGrid()->IsEnabled(), &Button, 0, "[ctrl+g] Toggle Grid", IGraphics::CORNER_L) ||
|
||||
if(DoButton_FontIcon(&s_GridButton, FONT_ICON_BORDER_ALL, m_QuickActionToggleGrid.Active(), &Button, 0, m_QuickActionToggleGrid.Description(), IGraphics::CORNER_L) ||
|
||||
(m_Dialog == DIALOG_NONE && CLineInput::GetActiveInput() == nullptr && Input()->KeyPress(KEY_G) && ModPressed && !ShiftPressed))
|
||||
{
|
||||
MapView()->MapGrid()->Toggle();
|
||||
m_QuickActionToggleGrid.Call();
|
||||
}
|
||||
|
||||
// grid settings button
|
||||
|
@ -1121,23 +1121,23 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
|
|||
// zoom group
|
||||
TB_Top.VSplitLeft(20.0f, &Button, &TB_Top);
|
||||
static int s_ZoomOutButton = 0;
|
||||
if(DoButton_FontIcon(&s_ZoomOutButton, FONT_ICON_MINUS, 0, &Button, 0, "[NumPad-] Zoom out", IGraphics::CORNER_L))
|
||||
if(DoButton_FontIcon(&s_ZoomOutButton, FONT_ICON_MINUS, 0, &Button, 0, m_QuickActionZoomOut.Description(), IGraphics::CORNER_L))
|
||||
{
|
||||
MapView()->Zoom()->ChangeValue(50.0f);
|
||||
m_QuickActionZoomOut.Call();
|
||||
}
|
||||
|
||||
TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
|
||||
static int s_ZoomNormalButton = 0;
|
||||
if(DoButton_FontIcon(&s_ZoomNormalButton, FONT_ICON_MAGNIFYING_GLASS, 0, &Button, 0, "[NumPad*] Zoom to normal and remove editor offset", IGraphics::CORNER_NONE))
|
||||
if(DoButton_FontIcon(&s_ZoomNormalButton, FONT_ICON_MAGNIFYING_GLASS, 0, &Button, 0, m_QuickActionResetZoom.Description(), IGraphics::CORNER_NONE))
|
||||
{
|
||||
MapView()->ResetZoom();
|
||||
m_QuickActionResetZoom.Call();
|
||||
}
|
||||
|
||||
TB_Top.VSplitLeft(20.0f, &Button, &TB_Top);
|
||||
static int s_ZoomInButton = 0;
|
||||
if(DoButton_FontIcon(&s_ZoomInButton, FONT_ICON_PLUS, 0, &Button, 0, "[NumPad+] Zoom in", IGraphics::CORNER_R))
|
||||
if(DoButton_FontIcon(&s_ZoomInButton, FONT_ICON_PLUS, 0, &Button, 0, m_QuickActionZoomIn.Description(), IGraphics::CORNER_R))
|
||||
{
|
||||
MapView()->Zoom()->ChangeValue(-50.0f);
|
||||
m_QuickActionZoomIn.Call();
|
||||
}
|
||||
|
||||
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
|
||||
|
@ -1228,10 +1228,10 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
|
|||
static char s_PipetteButton;
|
||||
ColorPalette.VSplitLeft(PipetteButtonWidth, &Button, &ColorPalette);
|
||||
ColorPalette.VSplitLeft(Spacing, nullptr, &ColorPalette);
|
||||
if(DoButton_FontIcon(&s_PipetteButton, FONT_ICON_EYE_DROPPER, m_ColorPipetteActive ? 1 : 0, &Button, 0, "[Ctrl+Shift+C] Color pipette. Pick a color from the screen by clicking on it.", IGraphics::CORNER_ALL) ||
|
||||
if(DoButton_FontIcon(&s_PipetteButton, FONT_ICON_EYE_DROPPER, m_QuickActionPipette.Active(), &Button, 0, m_QuickActionPipette.Description(), IGraphics::CORNER_ALL) ||
|
||||
(CLineInput::GetActiveInput() == nullptr && ModPressed && ShiftPressed && Input()->KeyPress(KEY_C)))
|
||||
{
|
||||
m_ColorPipetteActive = !m_ColorPipetteActive;
|
||||
m_QuickActionPipette.Call();
|
||||
}
|
||||
|
||||
// Palette color pickers
|
||||
|
@ -4384,7 +4384,7 @@ bool CEditor::ReplaceImage(const char *pFileName, int StorageType, bool CheckDup
|
|||
}
|
||||
}
|
||||
|
||||
CEditorImage ImgInfo(this);
|
||||
CImageInfo ImgInfo;
|
||||
if(!Graphics()->LoadPng(ImgInfo, pFileName, StorageType))
|
||||
{
|
||||
ShowFileDialogError("Failed to load image from file '%s'.", pFileName);
|
||||
|
@ -4394,21 +4394,33 @@ bool CEditor::ReplaceImage(const char *pFileName, int StorageType, bool CheckDup
|
|||
std::shared_ptr<CEditorImage> pImg = m_Map.m_vpImages[m_SelectedImage];
|
||||
Graphics()->UnloadTexture(&(pImg->m_Texture));
|
||||
pImg->Free();
|
||||
*pImg = ImgInfo;
|
||||
pImg->m_Width = ImgInfo.m_Width;
|
||||
pImg->m_Height = ImgInfo.m_Height;
|
||||
pImg->m_Format = ImgInfo.m_Format;
|
||||
pImg->m_pData = ImgInfo.m_pData;
|
||||
str_copy(pImg->m_aName, aBuf);
|
||||
pImg->m_External = IsVanillaImage(pImg->m_aName);
|
||||
|
||||
if(!pImg->m_External && g_Config.m_ClEditorDilate == 1 && pImg->m_Format == CImageInfo::FORMAT_RGBA)
|
||||
if(!pImg->m_External && pImg->m_Format != CImageInfo::FORMAT_RGBA)
|
||||
{
|
||||
DilateImage(ImgInfo.m_pData, ImgInfo.m_Width, ImgInfo.m_Height);
|
||||
uint8_t *pRgbaData = static_cast<uint8_t *>(malloc((size_t)pImg->m_Width * pImg->m_Height * CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA)));
|
||||
ConvertToRGBA(pRgbaData, *pImg);
|
||||
free(pImg->m_pData);
|
||||
pImg->m_pData = pRgbaData;
|
||||
pImg->m_Format = CImageInfo::FORMAT_RGBA;
|
||||
}
|
||||
|
||||
if(!pImg->m_External && g_Config.m_ClEditorDilate == 1)
|
||||
{
|
||||
DilateImage(pImg->m_pData, pImg->m_Width, pImg->m_Height);
|
||||
}
|
||||
|
||||
pImg->m_AutoMapper.Load(pImg->m_aName);
|
||||
int TextureLoadFlag = Graphics()->Uses2DTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE;
|
||||
if(ImgInfo.m_Width % 16 != 0 || ImgInfo.m_Height % 16 != 0)
|
||||
if(pImg->m_Width % 16 != 0 || pImg->m_Height % 16 != 0)
|
||||
TextureLoadFlag = 0;
|
||||
pImg->m_Texture = Graphics()->LoadTextureRaw(ImgInfo, TextureLoadFlag, pFileName);
|
||||
ImgInfo.m_pData = nullptr;
|
||||
pImg->m_Texture = Graphics()->LoadTextureRaw(*pImg, TextureLoadFlag, pFileName);
|
||||
|
||||
SortImages();
|
||||
for(size_t i = 0; i < m_Map.m_vpImages.size(); ++i)
|
||||
{
|
||||
|
@ -4447,7 +4459,7 @@ bool CEditor::AddImage(const char *pFileName, int StorageType, void *pUser)
|
|||
return false;
|
||||
}
|
||||
|
||||
CEditorImage ImgInfo(pEditor);
|
||||
CImageInfo ImgInfo;
|
||||
if(!pEditor->Graphics()->LoadPng(ImgInfo, pFileName, StorageType))
|
||||
{
|
||||
pEditor->ShowFileDialogError("Failed to load image from file '%s'.", pFileName);
|
||||
|
@ -4455,19 +4467,30 @@ bool CEditor::AddImage(const char *pFileName, int StorageType, void *pUser)
|
|||
}
|
||||
|
||||
std::shared_ptr<CEditorImage> pImg = std::make_shared<CEditorImage>(pEditor);
|
||||
*pImg = ImgInfo;
|
||||
pImg->m_Width = ImgInfo.m_Width;
|
||||
pImg->m_Height = ImgInfo.m_Height;
|
||||
pImg->m_Format = ImgInfo.m_Format;
|
||||
pImg->m_pData = ImgInfo.m_pData;
|
||||
pImg->m_External = IsVanillaImage(aBuf);
|
||||
|
||||
if(!pImg->m_External && g_Config.m_ClEditorDilate == 1 && pImg->m_Format == CImageInfo::FORMAT_RGBA)
|
||||
if(pImg->m_Format != CImageInfo::FORMAT_RGBA)
|
||||
{
|
||||
DilateImage(ImgInfo.m_pData, ImgInfo.m_Width, ImgInfo.m_Height);
|
||||
uint8_t *pRgbaData = static_cast<uint8_t *>(malloc((size_t)pImg->m_Width * pImg->m_Height * CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA)));
|
||||
ConvertToRGBA(pRgbaData, *pImg);
|
||||
free(pImg->m_pData);
|
||||
pImg->m_pData = pRgbaData;
|
||||
pImg->m_Format = CImageInfo::FORMAT_RGBA;
|
||||
}
|
||||
|
||||
if(!pImg->m_External && g_Config.m_ClEditorDilate == 1)
|
||||
{
|
||||
DilateImage(pImg->m_pData, pImg->m_Width, pImg->m_Height);
|
||||
}
|
||||
|
||||
int TextureLoadFlag = pEditor->Graphics()->Uses2DTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE;
|
||||
if(ImgInfo.m_Width % 16 != 0 || ImgInfo.m_Height % 16 != 0)
|
||||
if(pImg->m_Width % 16 != 0 || pImg->m_Height % 16 != 0)
|
||||
TextureLoadFlag = 0;
|
||||
pImg->m_Texture = pEditor->Graphics()->LoadTextureRaw(ImgInfo, TextureLoadFlag, pFileName);
|
||||
ImgInfo.m_pData = nullptr;
|
||||
pImg->m_Texture = pEditor->Graphics()->LoadTextureRaw(*pImg, TextureLoadFlag, pFileName);
|
||||
str_copy(pImg->m_aName, aBuf);
|
||||
pImg->m_AutoMapper.Load(pImg->m_aName);
|
||||
pEditor->m_Map.m_vpImages.push_back(pImg);
|
||||
|
@ -5739,30 +5762,25 @@ void CEditor::RenderModebar(CUIRect View)
|
|||
|
||||
void CEditor::RenderStatusbar(CUIRect View, CUIRect *pTooltipRect)
|
||||
{
|
||||
const bool ButtonsDisabled = m_ShowPicker;
|
||||
|
||||
CUIRect Button;
|
||||
View.VSplitRight(100.0f, &View, &Button);
|
||||
static int s_EnvelopeButton = 0;
|
||||
if(DoButton_Editor(&s_EnvelopeButton, m_QuickActionEnvelopes.Label(), m_QuickActionEnvelopes.Color(), &Button, 0, m_QuickActionEnvelopes.Description()) == 1)
|
||||
if(DoButton_Editor(&m_QuickActionEnvelopes, m_QuickActionEnvelopes.Label(), m_QuickActionEnvelopes.Color(), &Button, 0, m_QuickActionEnvelopes.Description()) == 1)
|
||||
{
|
||||
m_QuickActionEnvelopes.Call();
|
||||
}
|
||||
|
||||
View.VSplitRight(10.0f, &View, nullptr);
|
||||
View.VSplitRight(100.0f, &View, &Button);
|
||||
static int s_SettingsButton = 0;
|
||||
if(DoButton_Editor(&s_SettingsButton, "Server settings", ButtonsDisabled ? -1 : m_ActiveExtraEditor == EXTRAEDITOR_SERVER_SETTINGS, &Button, 0, "Toggles the server settings editor.") == 1)
|
||||
if(DoButton_Editor(&m_QuickActionServerSettings, m_QuickActionServerSettings.Label(), m_QuickActionServerSettings.Color(), &Button, 0, m_QuickActionServerSettings.Description()) == 1)
|
||||
{
|
||||
m_ActiveExtraEditor = m_ActiveExtraEditor == EXTRAEDITOR_SERVER_SETTINGS ? EXTRAEDITOR_NONE : EXTRAEDITOR_SERVER_SETTINGS;
|
||||
m_QuickActionServerSettings.Call();
|
||||
}
|
||||
|
||||
View.VSplitRight(10.0f, &View, nullptr);
|
||||
View.VSplitRight(100.0f, &View, &Button);
|
||||
static int s_HistoryButton = 0;
|
||||
if(DoButton_Editor(&s_HistoryButton, "History", ButtonsDisabled ? -1 : m_ActiveExtraEditor == EXTRAEDITOR_HISTORY, &Button, 0, "Toggles the editor history view.") == 1)
|
||||
if(DoButton_Editor(&m_QuickActionHistory, m_QuickActionHistory.Label(), m_QuickActionHistory.Color(), &Button, 0, m_QuickActionHistory.Description()) == 1)
|
||||
{
|
||||
m_ActiveExtraEditor = m_ActiveExtraEditor == EXTRAEDITOR_HISTORY ? EXTRAEDITOR_NONE : EXTRAEDITOR_HISTORY;
|
||||
m_QuickActionHistory.Call();
|
||||
}
|
||||
|
||||
View.VSplitRight(10.0f, pTooltipRect, nullptr);
|
||||
|
@ -7696,7 +7714,7 @@ void CEditor::RenderMenubar(CUIRect MenuBar)
|
|||
if(DoButton_Ex(&s_SettingsButton, "Settings", 0, &SettingsButton, 0, nullptr, IGraphics::CORNER_T, EditorFontSizes::MENU, TEXTALIGN_ML))
|
||||
{
|
||||
static SPopupMenuId s_PopupMenuEntitiesId;
|
||||
Ui()->DoPopupMenu(&s_PopupMenuEntitiesId, SettingsButton.x, SettingsButton.y + SettingsButton.h - 1.0f, 200.0f, 92.0f, this, PopupMenuSettings, PopupProperties);
|
||||
Ui()->DoPopupMenu(&s_PopupMenuEntitiesId, SettingsButton.x, SettingsButton.y + SettingsButton.h - 1.0f, 200.0f, 106.0f, this, PopupMenuSettings, PopupProperties);
|
||||
}
|
||||
|
||||
CUIRect ChangedIndicator, Info, Help, Close;
|
||||
|
@ -8597,7 +8615,7 @@ void CEditor::HandleWriterFinishJobs()
|
|||
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "editor/save", aBuf);
|
||||
|
||||
// send rcon.. if we can
|
||||
if(Client()->RconAuthed())
|
||||
if(Client()->RconAuthed() && g_Config.m_EdAutoMapReload)
|
||||
{
|
||||
CServerInfo CurrentServerInfo;
|
||||
Client()->GetServerInfo(&CurrentServerInfo);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <game/client/ui_listbox.h>
|
||||
#include <game/mapitems.h>
|
||||
|
||||
#include <game/editor/enums.h>
|
||||
#include <game/editor/mapitems/envelope.h>
|
||||
#include <game/editor/mapitems/layer.h>
|
||||
#include <game/editor/mapitems/layer_front.h>
|
||||
|
@ -323,6 +324,8 @@ public:
|
|||
const CMapView *MapView() const { return &m_MapView; }
|
||||
CLayerSelector *LayerSelector() { return &m_LayerSelector; }
|
||||
|
||||
void FillGameTiles(EGameTileOp FillTile) const;
|
||||
bool CanFillGameTiles() const;
|
||||
void AddGroup();
|
||||
void AddTileLayer();
|
||||
void LayerSelectImage();
|
||||
|
|
|
@ -320,7 +320,7 @@ void CEditorActionDeleteQuad::Redo()
|
|||
CEditorActionEditQuadPoint::CEditorActionEditQuadPoint(CEditor *pEditor, int GroupIndex, int LayerIndex, int QuadIndex, std::vector<CPoint> const &vPreviousPoints, std::vector<CPoint> const &vCurrentPoints) :
|
||||
CEditorActionLayerBase(pEditor, GroupIndex, LayerIndex), m_QuadIndex(QuadIndex), m_vPreviousPoints(vPreviousPoints), m_vCurrentPoints(vCurrentPoints)
|
||||
{
|
||||
str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit quad points");
|
||||
str_copy(m_aDisplayText, "Edit quad points");
|
||||
}
|
||||
|
||||
void CEditorActionEditQuadPoint::Undo()
|
||||
|
@ -628,7 +628,7 @@ CEditorActionGroup::CEditorActionGroup(CEditor *pEditor, int GroupIndex, bool De
|
|||
if(m_Delete)
|
||||
str_format(m_aDisplayText, sizeof(m_aDisplayText), "Delete group %d", m_GroupIndex);
|
||||
else
|
||||
str_format(m_aDisplayText, sizeof(m_aDisplayText), "New group");
|
||||
str_copy(m_aDisplayText, "New group", sizeof(m_aDisplayText));
|
||||
}
|
||||
|
||||
void CEditorActionGroup::Undo()
|
||||
|
@ -1198,7 +1198,7 @@ CEditorActionTileArt::CEditorActionTileArt(CEditor *pEditor, int PreviousImageCo
|
|||
IEditorAction(pEditor), m_PreviousImageCount(PreviousImageCount), m_vImageIndexMap(vImageIndexMap)
|
||||
{
|
||||
str_copy(m_aTileArtFile, pTileArtFile);
|
||||
str_format(m_aDisplayText, sizeof(m_aDisplayText), "Tile art");
|
||||
str_copy(m_aDisplayText, "Tile art");
|
||||
}
|
||||
|
||||
void CEditorActionTileArt::Undo()
|
||||
|
@ -1266,7 +1266,7 @@ CEditorCommandAction::CEditorCommandAction(CEditor *pEditor, EType Type, int *pS
|
|||
switch(m_Type)
|
||||
{
|
||||
case EType::ADD:
|
||||
str_format(m_aDisplayText, sizeof(m_aDisplayText), "Add command");
|
||||
str_copy(m_aDisplayText, "Add command");
|
||||
break;
|
||||
case EType::EDIT:
|
||||
str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit command %d", m_CommandIndex);
|
||||
|
|
36
src/game/editor/enums.h
Normal file
36
src/game/editor/enums.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef GAME_EDITOR_ENUMS_H
|
||||
#define GAME_EDITOR_ENUMS_H
|
||||
|
||||
constexpr const char *g_apGametileOpNames[] = {
|
||||
"Air",
|
||||
"Hookable",
|
||||
"Death",
|
||||
"Unhookable",
|
||||
"Hookthrough",
|
||||
"Freeze",
|
||||
"Unfreeze",
|
||||
"Deep Freeze",
|
||||
"Deep Unfreeze",
|
||||
"Blue Check-Tele",
|
||||
"Red Check-Tele",
|
||||
"Live Freeze",
|
||||
"Live Unfreeze",
|
||||
};
|
||||
enum class EGameTileOp
|
||||
{
|
||||
AIR,
|
||||
HOOKABLE,
|
||||
DEATH,
|
||||
UNHOOKABLE,
|
||||
HOOKTHROUGH,
|
||||
FREEZE,
|
||||
UNFREEZE,
|
||||
DEEP_FREEZE,
|
||||
DEEP_UNFREEZE,
|
||||
BLUE_CHECK_TELE,
|
||||
RED_CHECK_TELE,
|
||||
LIVE_FREEZE,
|
||||
LIVE_UNFREEZE,
|
||||
};
|
||||
|
||||
#endif
|
|
@ -6,6 +6,7 @@
|
|||
#include <engine/shared/map.h>
|
||||
#include <game/editor/editor.h>
|
||||
#include <game/editor/editor_actions.h>
|
||||
#include <game/editor/enums.h>
|
||||
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
|
@ -693,93 +694,126 @@ void CLayerTiles::ShowInfo()
|
|||
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
|
||||
}
|
||||
|
||||
CUi::EPopupMenuFunctionResult CLayerTiles::RenderProperties(CUIRect *pToolBox)
|
||||
void CLayerTiles::FillGameTiles(EGameTileOp Fill)
|
||||
{
|
||||
CUIRect Button;
|
||||
|
||||
const bool EntitiesLayer = IsEntitiesLayer();
|
||||
if(!CanFillGameTiles())
|
||||
return;
|
||||
|
||||
std::shared_ptr<CLayerGroup> pGroup = m_pEditor->m_Map.m_vpGroups[m_pEditor->m_SelectedGroup];
|
||||
|
||||
// Game tiles can only be constructed if the layer is relative to the game layer
|
||||
if(!EntitiesLayer && !(pGroup->m_OffsetX % 32) && !(pGroup->m_OffsetY % 32) && pGroup->m_ParallaxX == 100 && pGroup->m_ParallaxY == 100)
|
||||
int Result = (int)Fill;
|
||||
switch(Fill)
|
||||
{
|
||||
pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
|
||||
static int s_GameTilesButton = 0;
|
||||
if(m_pEditor->DoButton_Editor(&s_GameTilesButton, "Game tiles", 0, &Button, 0, "Constructs game tiles from this layer"))
|
||||
m_pEditor->PopupSelectGametileOpInvoke(m_pEditor->Ui()->MouseX(), m_pEditor->Ui()->MouseY());
|
||||
const int Selected = m_pEditor->PopupSelectGameTileOpResult();
|
||||
int Result = Selected;
|
||||
switch(Selected)
|
||||
case EGameTileOp::HOOKTHROUGH:
|
||||
Result = TILE_THROUGH_CUT;
|
||||
break;
|
||||
case EGameTileOp::FREEZE:
|
||||
Result = TILE_FREEZE;
|
||||
break;
|
||||
case EGameTileOp::UNFREEZE:
|
||||
Result = TILE_UNFREEZE;
|
||||
break;
|
||||
case EGameTileOp::DEEP_FREEZE:
|
||||
Result = TILE_DFREEZE;
|
||||
break;
|
||||
case EGameTileOp::DEEP_UNFREEZE:
|
||||
Result = TILE_DUNFREEZE;
|
||||
break;
|
||||
case EGameTileOp::BLUE_CHECK_TELE:
|
||||
Result = TILE_TELECHECKIN;
|
||||
break;
|
||||
case EGameTileOp::RED_CHECK_TELE:
|
||||
Result = TILE_TELECHECKINEVIL;
|
||||
break;
|
||||
case EGameTileOp::LIVE_FREEZE:
|
||||
Result = TILE_LFREEZE;
|
||||
break;
|
||||
case EGameTileOp::LIVE_UNFREEZE:
|
||||
Result = TILE_LUNFREEZE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(Result > -1)
|
||||
{
|
||||
const int OffsetX = -pGroup->m_OffsetX / 32;
|
||||
const int OffsetY = -pGroup->m_OffsetY / 32;
|
||||
|
||||
std::vector<std::shared_ptr<IEditorAction>> vpActions;
|
||||
std::shared_ptr<CLayerTiles> pGLayer = m_pEditor->m_Map.m_pGameLayer;
|
||||
int GameLayerIndex = std::find(m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin(), m_pEditor->m_Map.m_pGameGroup->m_vpLayers.end(), pGLayer) - m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin();
|
||||
|
||||
if(Result != TILE_TELECHECKIN && Result != TILE_TELECHECKINEVIL)
|
||||
{
|
||||
case 4:
|
||||
Result = TILE_THROUGH_CUT;
|
||||
break;
|
||||
case 5:
|
||||
Result = TILE_FREEZE;
|
||||
break;
|
||||
case 6:
|
||||
Result = TILE_UNFREEZE;
|
||||
break;
|
||||
case 7:
|
||||
Result = TILE_DFREEZE;
|
||||
break;
|
||||
case 8:
|
||||
Result = TILE_DUNFREEZE;
|
||||
break;
|
||||
case 9:
|
||||
Result = TILE_TELECHECKIN;
|
||||
break;
|
||||
case 10:
|
||||
Result = TILE_TELECHECKINEVIL;
|
||||
break;
|
||||
case 11:
|
||||
Result = TILE_LFREEZE;
|
||||
break;
|
||||
case 12:
|
||||
Result = TILE_LUNFREEZE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(Result > -1)
|
||||
{
|
||||
const int OffsetX = -pGroup->m_OffsetX / 32;
|
||||
const int OffsetY = -pGroup->m_OffsetY / 32;
|
||||
|
||||
static const char *s_apGametileOpNames[] = {
|
||||
"Air",
|
||||
"Hookable",
|
||||
"Death",
|
||||
"Unhookable",
|
||||
"Hookthrough",
|
||||
"Freeze",
|
||||
"Unfreeze",
|
||||
"Deep Freeze",
|
||||
"Deep Unfreeze",
|
||||
"Blue Check-Tele",
|
||||
"Red Check-Tele",
|
||||
"Live Freeze",
|
||||
"Live Unfreeze",
|
||||
};
|
||||
|
||||
std::vector<std::shared_ptr<IEditorAction>> vpActions;
|
||||
std::shared_ptr<CLayerTiles> pGLayer = m_pEditor->m_Map.m_pGameLayer;
|
||||
int GameLayerIndex = std::find(m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin(), m_pEditor->m_Map.m_pGameGroup->m_vpLayers.end(), pGLayer) - m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin();
|
||||
|
||||
if(Result != TILE_TELECHECKIN && Result != TILE_TELECHECKINEVIL)
|
||||
if(pGLayer->m_Width < m_Width + OffsetX || pGLayer->m_Height < m_Height + OffsetY)
|
||||
{
|
||||
if(pGLayer->m_Width < m_Width + OffsetX || pGLayer->m_Height < m_Height + OffsetY)
|
||||
std::map<int, std::shared_ptr<CLayer>> savedLayers;
|
||||
savedLayers[LAYERTYPE_TILES] = pGLayer->Duplicate();
|
||||
savedLayers[LAYERTYPE_GAME] = savedLayers[LAYERTYPE_TILES];
|
||||
|
||||
int PrevW = pGLayer->m_Width;
|
||||
int PrevH = pGLayer->m_Height;
|
||||
const int NewW = pGLayer->m_Width < m_Width + OffsetX ? m_Width + OffsetX : pGLayer->m_Width;
|
||||
const int NewH = pGLayer->m_Height < m_Height + OffsetY ? m_Height + OffsetY : pGLayer->m_Height;
|
||||
pGLayer->Resize(NewW, NewH);
|
||||
vpActions.push_back(std::make_shared<CEditorActionEditLayerTilesProp>(m_pEditor, m_pEditor->m_SelectedGroup, GameLayerIndex, ETilesProp::PROP_WIDTH, PrevW, NewW));
|
||||
const std::shared_ptr<CEditorActionEditLayerTilesProp> &Action1 = std::static_pointer_cast<CEditorActionEditLayerTilesProp>(vpActions[vpActions.size() - 1]);
|
||||
vpActions.push_back(std::make_shared<CEditorActionEditLayerTilesProp>(m_pEditor, m_pEditor->m_SelectedGroup, GameLayerIndex, ETilesProp::PROP_HEIGHT, PrevH, NewH));
|
||||
const std::shared_ptr<CEditorActionEditLayerTilesProp> &Action2 = std::static_pointer_cast<CEditorActionEditLayerTilesProp>(vpActions[vpActions.size() - 1]);
|
||||
|
||||
Action1->SetSavedLayers(savedLayers);
|
||||
Action2->SetSavedLayers(savedLayers);
|
||||
}
|
||||
|
||||
int Changes = 0;
|
||||
for(int y = OffsetY < 0 ? -OffsetY : 0; y < m_Height; y++)
|
||||
{
|
||||
for(int x = OffsetX < 0 ? -OffsetX : 0; x < m_Width; x++)
|
||||
{
|
||||
if(GetTile(x, y).m_Index)
|
||||
{
|
||||
const CTile ResultTile = {(unsigned char)Result};
|
||||
pGLayer->SetTile(x + OffsetX, y + OffsetY, ResultTile);
|
||||
Changes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vpActions.push_back(std::make_shared<CEditorBrushDrawAction>(m_pEditor, m_pEditor->m_SelectedGroup));
|
||||
char aDisplay[256];
|
||||
str_format(aDisplay, sizeof(aDisplay), "Construct '%s' game tiles (x%d)", g_apGametileOpNames[(int)Fill], Changes);
|
||||
m_pEditor->m_EditorHistory.RecordAction(std::make_shared<CEditorActionBulk>(m_pEditor, vpActions, aDisplay, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!m_pEditor->m_Map.m_pTeleLayer)
|
||||
{
|
||||
std::shared_ptr<CLayerTele> pLayer = std::make_shared<CLayerTele>(m_pEditor, m_Width, m_Height);
|
||||
m_pEditor->m_Map.MakeTeleLayer(pLayer);
|
||||
m_pEditor->m_Map.m_pGameGroup->AddLayer(pLayer);
|
||||
|
||||
vpActions.push_back(std::make_shared<CEditorActionAddLayer>(m_pEditor, m_pEditor->m_SelectedGroup, m_pEditor->m_Map.m_pGameGroup->m_vpLayers.size() - 1));
|
||||
|
||||
if(m_Width != pGLayer->m_Width || m_Height > pGLayer->m_Height)
|
||||
{
|
||||
std::map<int, std::shared_ptr<CLayer>> savedLayers;
|
||||
savedLayers[LAYERTYPE_TILES] = pGLayer->Duplicate();
|
||||
savedLayers[LAYERTYPE_GAME] = savedLayers[LAYERTYPE_TILES];
|
||||
|
||||
int NewW = pGLayer->m_Width;
|
||||
int NewH = pGLayer->m_Height;
|
||||
if(m_Width > pGLayer->m_Width)
|
||||
{
|
||||
NewW = m_Width;
|
||||
}
|
||||
if(m_Height > pGLayer->m_Height)
|
||||
{
|
||||
NewH = m_Height;
|
||||
}
|
||||
|
||||
int PrevW = pGLayer->m_Width;
|
||||
int PrevH = pGLayer->m_Height;
|
||||
const int NewW = pGLayer->m_Width < m_Width + OffsetX ? m_Width + OffsetX : pGLayer->m_Width;
|
||||
const int NewH = pGLayer->m_Height < m_Height + OffsetY ? m_Height + OffsetY : pGLayer->m_Height;
|
||||
pGLayer->Resize(NewW, NewH);
|
||||
pLayer->Resize(NewW, NewH);
|
||||
vpActions.push_back(std::make_shared<CEditorActionEditLayerTilesProp>(m_pEditor, m_pEditor->m_SelectedGroup, GameLayerIndex, ETilesProp::PROP_WIDTH, PrevW, NewW));
|
||||
const std::shared_ptr<CEditorActionEditLayerTilesProp> &Action1 = std::static_pointer_cast<CEditorActionEditLayerTilesProp>(vpActions[vpActions.size() - 1]);
|
||||
vpActions.push_back(std::make_shared<CEditorActionEditLayerTilesProp>(m_pEditor, m_pEditor->m_SelectedGroup, GameLayerIndex, ETilesProp::PROP_HEIGHT, PrevH, NewH));
|
||||
|
@ -788,124 +822,94 @@ CUi::EPopupMenuFunctionResult CLayerTiles::RenderProperties(CUIRect *pToolBox)
|
|||
Action1->SetSavedLayers(savedLayers);
|
||||
Action2->SetSavedLayers(savedLayers);
|
||||
}
|
||||
|
||||
int Changes = 0;
|
||||
for(int y = OffsetY < 0 ? -OffsetY : 0; y < m_Height; y++)
|
||||
{
|
||||
for(int x = OffsetX < 0 ? -OffsetX : 0; x < m_Width; x++)
|
||||
{
|
||||
if(GetTile(x, y).m_Index)
|
||||
{
|
||||
const CTile ResultTile = {(unsigned char)Result};
|
||||
pGLayer->SetTile(x + OffsetX, y + OffsetY, ResultTile);
|
||||
Changes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vpActions.push_back(std::make_shared<CEditorBrushDrawAction>(m_pEditor, m_pEditor->m_SelectedGroup));
|
||||
char aDisplay[256];
|
||||
str_format(aDisplay, sizeof(aDisplay), "Construct '%s' game tiles (x%d)", s_apGametileOpNames[Selected], Changes);
|
||||
m_pEditor->m_EditorHistory.RecordAction(std::make_shared<CEditorActionBulk>(m_pEditor, vpActions, aDisplay, true));
|
||||
}
|
||||
else
|
||||
|
||||
std::shared_ptr<CLayerTele> pTLayer = m_pEditor->m_Map.m_pTeleLayer;
|
||||
int TeleLayerIndex = std::find(m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin(), m_pEditor->m_Map.m_pGameGroup->m_vpLayers.end(), pTLayer) - m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin();
|
||||
|
||||
if(pTLayer->m_Width < m_Width + OffsetX || pTLayer->m_Height < m_Height + OffsetY)
|
||||
{
|
||||
if(!m_pEditor->m_Map.m_pTeleLayer)
|
||||
{
|
||||
std::shared_ptr<CLayerTele> pLayer = std::make_shared<CLayerTele>(m_pEditor, m_Width, m_Height);
|
||||
m_pEditor->m_Map.MakeTeleLayer(pLayer);
|
||||
m_pEditor->m_Map.m_pGameGroup->AddLayer(pLayer);
|
||||
std::map<int, std::shared_ptr<CLayer>> savedLayers;
|
||||
savedLayers[LAYERTYPE_TILES] = pTLayer->Duplicate();
|
||||
savedLayers[LAYERTYPE_TELE] = savedLayers[LAYERTYPE_TILES];
|
||||
|
||||
vpActions.push_back(std::make_shared<CEditorActionAddLayer>(m_pEditor, m_pEditor->m_SelectedGroup, m_pEditor->m_Map.m_pGameGroup->m_vpLayers.size() - 1));
|
||||
int PrevW = pTLayer->m_Width;
|
||||
int PrevH = pTLayer->m_Height;
|
||||
int NewW = pTLayer->m_Width < m_Width + OffsetX ? m_Width + OffsetX : pTLayer->m_Width;
|
||||
int NewH = pTLayer->m_Height < m_Height + OffsetY ? m_Height + OffsetY : pTLayer->m_Height;
|
||||
pTLayer->Resize(NewW, NewH);
|
||||
std::shared_ptr<CEditorActionEditLayerTilesProp> Action1, Action2;
|
||||
vpActions.push_back(Action1 = std::make_shared<CEditorActionEditLayerTilesProp>(m_pEditor, m_pEditor->m_SelectedGroup, TeleLayerIndex, ETilesProp::PROP_WIDTH, PrevW, NewW));
|
||||
vpActions.push_back(Action2 = std::make_shared<CEditorActionEditLayerTilesProp>(m_pEditor, m_pEditor->m_SelectedGroup, TeleLayerIndex, ETilesProp::PROP_HEIGHT, PrevH, NewH));
|
||||
|
||||
if(m_Width != pGLayer->m_Width || m_Height > pGLayer->m_Height)
|
||||
{
|
||||
std::map<int, std::shared_ptr<CLayer>> savedLayers;
|
||||
savedLayers[LAYERTYPE_TILES] = pGLayer->Duplicate();
|
||||
savedLayers[LAYERTYPE_GAME] = savedLayers[LAYERTYPE_TILES];
|
||||
|
||||
int NewW = pGLayer->m_Width;
|
||||
int NewH = pGLayer->m_Height;
|
||||
if(m_Width > pGLayer->m_Width)
|
||||
{
|
||||
NewW = m_Width;
|
||||
}
|
||||
if(m_Height > pGLayer->m_Height)
|
||||
{
|
||||
NewH = m_Height;
|
||||
}
|
||||
|
||||
int PrevW = pGLayer->m_Width;
|
||||
int PrevH = pGLayer->m_Height;
|
||||
pLayer->Resize(NewW, NewH);
|
||||
vpActions.push_back(std::make_shared<CEditorActionEditLayerTilesProp>(m_pEditor, m_pEditor->m_SelectedGroup, GameLayerIndex, ETilesProp::PROP_WIDTH, PrevW, NewW));
|
||||
const std::shared_ptr<CEditorActionEditLayerTilesProp> &Action1 = std::static_pointer_cast<CEditorActionEditLayerTilesProp>(vpActions[vpActions.size() - 1]);
|
||||
vpActions.push_back(std::make_shared<CEditorActionEditLayerTilesProp>(m_pEditor, m_pEditor->m_SelectedGroup, GameLayerIndex, ETilesProp::PROP_HEIGHT, PrevH, NewH));
|
||||
const std::shared_ptr<CEditorActionEditLayerTilesProp> &Action2 = std::static_pointer_cast<CEditorActionEditLayerTilesProp>(vpActions[vpActions.size() - 1]);
|
||||
|
||||
Action1->SetSavedLayers(savedLayers);
|
||||
Action2->SetSavedLayers(savedLayers);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<CLayerTele> pTLayer = m_pEditor->m_Map.m_pTeleLayer;
|
||||
int TeleLayerIndex = std::find(m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin(), m_pEditor->m_Map.m_pGameGroup->m_vpLayers.end(), pTLayer) - m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin();
|
||||
|
||||
if(pTLayer->m_Width < m_Width + OffsetX || pTLayer->m_Height < m_Height + OffsetY)
|
||||
{
|
||||
std::map<int, std::shared_ptr<CLayer>> savedLayers;
|
||||
savedLayers[LAYERTYPE_TILES] = pTLayer->Duplicate();
|
||||
savedLayers[LAYERTYPE_TELE] = savedLayers[LAYERTYPE_TILES];
|
||||
|
||||
int PrevW = pTLayer->m_Width;
|
||||
int PrevH = pTLayer->m_Height;
|
||||
int NewW = pTLayer->m_Width < m_Width + OffsetX ? m_Width + OffsetX : pTLayer->m_Width;
|
||||
int NewH = pTLayer->m_Height < m_Height + OffsetY ? m_Height + OffsetY : pTLayer->m_Height;
|
||||
pTLayer->Resize(NewW, NewH);
|
||||
std::shared_ptr<CEditorActionEditLayerTilesProp> Action1, Action2;
|
||||
vpActions.push_back(Action1 = std::make_shared<CEditorActionEditLayerTilesProp>(m_pEditor, m_pEditor->m_SelectedGroup, TeleLayerIndex, ETilesProp::PROP_WIDTH, PrevW, NewW));
|
||||
vpActions.push_back(Action2 = std::make_shared<CEditorActionEditLayerTilesProp>(m_pEditor, m_pEditor->m_SelectedGroup, TeleLayerIndex, ETilesProp::PROP_HEIGHT, PrevH, NewH));
|
||||
|
||||
Action1->SetSavedLayers(savedLayers);
|
||||
Action2->SetSavedLayers(savedLayers);
|
||||
}
|
||||
|
||||
int Changes = 0;
|
||||
for(int y = OffsetY < 0 ? -OffsetY : 0; y < m_Height; y++)
|
||||
{
|
||||
for(int x = OffsetX < 0 ? -OffsetX : 0; x < m_Width; x++)
|
||||
{
|
||||
if(GetTile(x, y).m_Index)
|
||||
{
|
||||
auto TileIndex = (y + OffsetY) * pTLayer->m_Width + x + OffsetX;
|
||||
Changes++;
|
||||
|
||||
STeleTileStateChange::SData Previous{
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Number,
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Type,
|
||||
pTLayer->m_pTiles[TileIndex].m_Index};
|
||||
|
||||
pTLayer->m_pTiles[TileIndex].m_Index = TILE_AIR + Result;
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Number = 1;
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Type = TILE_AIR + Result;
|
||||
|
||||
STeleTileStateChange::SData Current{
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Number,
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Type,
|
||||
pTLayer->m_pTiles[TileIndex].m_Index};
|
||||
|
||||
pTLayer->RecordStateChange(x, y, Previous, Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vpActions.push_back(std::make_shared<CEditorBrushDrawAction>(m_pEditor, m_pEditor->m_SelectedGroup));
|
||||
char aDisplay[256];
|
||||
str_format(aDisplay, sizeof(aDisplay), "Construct 'tele' game tiles (x%d)", Changes);
|
||||
m_pEditor->m_EditorHistory.RecordAction(std::make_shared<CEditorActionBulk>(m_pEditor, vpActions, aDisplay, true));
|
||||
Action1->SetSavedLayers(savedLayers);
|
||||
Action2->SetSavedLayers(savedLayers);
|
||||
}
|
||||
|
||||
int Changes = 0;
|
||||
for(int y = OffsetY < 0 ? -OffsetY : 0; y < m_Height; y++)
|
||||
{
|
||||
for(int x = OffsetX < 0 ? -OffsetX : 0; x < m_Width; x++)
|
||||
{
|
||||
if(GetTile(x, y).m_Index)
|
||||
{
|
||||
auto TileIndex = (y + OffsetY) * pTLayer->m_Width + x + OffsetX;
|
||||
Changes++;
|
||||
|
||||
STeleTileStateChange::SData Previous{
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Number,
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Type,
|
||||
pTLayer->m_pTiles[TileIndex].m_Index};
|
||||
|
||||
pTLayer->m_pTiles[TileIndex].m_Index = TILE_AIR + Result;
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Number = 1;
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Type = TILE_AIR + Result;
|
||||
|
||||
STeleTileStateChange::SData Current{
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Number,
|
||||
pTLayer->m_pTeleTile[TileIndex].m_Type,
|
||||
pTLayer->m_pTiles[TileIndex].m_Index};
|
||||
|
||||
pTLayer->RecordStateChange(x, y, Previous, Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vpActions.push_back(std::make_shared<CEditorBrushDrawAction>(m_pEditor, m_pEditor->m_SelectedGroup));
|
||||
char aDisplay[256];
|
||||
str_format(aDisplay, sizeof(aDisplay), "Construct 'tele' game tiles (x%d)", Changes);
|
||||
m_pEditor->m_EditorHistory.RecordAction(std::make_shared<CEditorActionBulk>(m_pEditor, vpActions, aDisplay, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CLayerTiles::CanFillGameTiles() const
|
||||
{
|
||||
const bool EntitiesLayer = IsEntitiesLayer();
|
||||
if(EntitiesLayer)
|
||||
return false;
|
||||
|
||||
std::shared_ptr<CLayerGroup> pGroup = m_pEditor->m_Map.m_vpGroups[m_pEditor->m_SelectedGroup];
|
||||
|
||||
// Game tiles can only be constructed if the layer is relative to the game layer
|
||||
return !(pGroup->m_OffsetX % 32) && !(pGroup->m_OffsetY % 32) && pGroup->m_ParallaxX == 100 && pGroup->m_ParallaxY == 100;
|
||||
}
|
||||
|
||||
CUi::EPopupMenuFunctionResult CLayerTiles::RenderProperties(CUIRect *pToolBox)
|
||||
{
|
||||
CUIRect Button;
|
||||
|
||||
const bool EntitiesLayer = IsEntitiesLayer();
|
||||
|
||||
if(CanFillGameTiles())
|
||||
{
|
||||
pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
|
||||
static int s_GameTilesButton = 0;
|
||||
if(m_pEditor->DoButton_Editor(&s_GameTilesButton, "Game tiles", 0, &Button, 0, "Constructs game tiles from this layer"))
|
||||
m_pEditor->PopupSelectGametileOpInvoke(m_pEditor->Ui()->MouseX(), m_pEditor->Ui()->MouseY());
|
||||
const int Selected = m_pEditor->PopupSelectGameTileOpResult();
|
||||
FillGameTiles((EGameTileOp)Selected);
|
||||
}
|
||||
|
||||
if(m_pEditor->m_Map.m_pGameLayer.get() != this)
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define GAME_EDITOR_MAPITEMS_LAYER_TILES_H
|
||||
|
||||
#include <game/editor/editor_trackers.h>
|
||||
#include <game/editor/enums.h>
|
||||
#include <map>
|
||||
|
||||
#include "layer.h"
|
||||
|
@ -122,6 +123,8 @@ public:
|
|||
void BrushSelecting(CUIRect Rect) override;
|
||||
int BrushGrab(std::shared_ptr<CLayerGroup> pBrush, CUIRect Rect) override;
|
||||
void FillSelection(bool Empty, std::shared_ptr<CLayer> pBrush, CUIRect Rect) override;
|
||||
void FillGameTiles(EGameTileOp Fill);
|
||||
bool CanFillGameTiles() const;
|
||||
void BrushDraw(std::shared_ptr<CLayer> pBrush, float wx, float wy) override;
|
||||
void BrushFlipX() override;
|
||||
void BrushFlipY() override;
|
||||
|
|
|
@ -509,6 +509,15 @@ bool CEditorMap::Load(const char *pFileName, int StorageType, const std::functio
|
|||
pImg->m_Height = ImgInfo.m_Height;
|
||||
pImg->m_Format = ImgInfo.m_Format;
|
||||
pImg->m_pData = ImgInfo.m_pData;
|
||||
if(pImg->m_Format != CImageInfo::FORMAT_RGBA)
|
||||
{
|
||||
uint8_t *pRgbaData = static_cast<uint8_t *>(malloc((size_t)pImg->m_Width * pImg->m_Height * CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA)));
|
||||
ConvertToRGBA(pRgbaData, *pImg);
|
||||
free(pImg->m_pData);
|
||||
pImg->m_pData = pRgbaData;
|
||||
pImg->m_Format = CImageInfo::FORMAT_RGBA;
|
||||
}
|
||||
|
||||
int TextureLoadFlag = m_pEditor->Graphics()->Uses2DTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE;
|
||||
if(ImgInfo.m_Width % 16 != 0 || ImgInfo.m_Height % 16 != 0)
|
||||
TextureLoadFlag = 0;
|
||||
|
|
|
@ -68,17 +68,9 @@ CUi::EPopupMenuFunctionResult CEditor::PopupMenuFile(void *pContext, CUIRect Vie
|
|||
|
||||
View.HSplitTop(2.0f, nullptr, &View);
|
||||
View.HSplitTop(12.0f, &Slot, &View);
|
||||
if(pEditor->DoButton_MenuItem(&s_OpenCurrentMapButton, "Load Current Map", 0, &Slot, 0, "Opens the current in game map for editing (ctrl+alt+l)"))
|
||||
if(pEditor->DoButton_MenuItem(&s_OpenCurrentMapButton, pEditor->m_QuickActionLoadCurrentMap.Label(), 0, &Slot, 0, pEditor->m_QuickActionLoadCurrentMap.Description()))
|
||||
{
|
||||
if(pEditor->HasUnsavedData())
|
||||
{
|
||||
pEditor->m_PopupEventType = POPEVENT_LOADCURRENT;
|
||||
pEditor->m_PopupEventActivated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pEditor->LoadCurrentMap();
|
||||
}
|
||||
pEditor->m_QuickActionLoadCurrentMap.Call();
|
||||
return CUi::POPUP_CLOSE_CURRENT;
|
||||
}
|
||||
|
||||
|
@ -314,20 +306,20 @@ CUi::EPopupMenuFunctionResult CEditor::PopupMenuSettings(void *pContext, CUIRect
|
|||
static int s_ButtonOff = 0;
|
||||
static int s_ButtonDec = 0;
|
||||
static int s_ButtonHex = 0;
|
||||
if(pEditor->DoButton_Ex(&s_ButtonOff, "Off", pEditor->m_ShowTileInfo == SHOW_TILE_OFF, &Off, 0, "Do not show tile information", IGraphics::CORNER_L))
|
||||
CQuickAction *pAction = &pEditor->m_QuickActionShowInfoOff;
|
||||
if(pEditor->DoButton_Ex(&s_ButtonOff, pAction->LabelShort(), pAction->Active(), &Off, 0, pAction->Description(), IGraphics::CORNER_L))
|
||||
{
|
||||
pEditor->m_ShowTileInfo = SHOW_TILE_OFF;
|
||||
pEditor->m_ShowEnvelopePreview = SHOWENV_NONE;
|
||||
pAction->Call();
|
||||
}
|
||||
if(pEditor->DoButton_Ex(&s_ButtonDec, "Dec", pEditor->m_ShowTileInfo == SHOW_TILE_DECIMAL, &Dec, 0, "[ctrl+i] Show tile information", IGraphics::CORNER_NONE))
|
||||
pAction = &pEditor->m_QuickActionShowInfoDec;
|
||||
if(pEditor->DoButton_Ex(&s_ButtonDec, pAction->LabelShort(), pAction->Active(), &Dec, 0, pAction->Description(), IGraphics::CORNER_NONE))
|
||||
{
|
||||
pEditor->m_ShowTileInfo = SHOW_TILE_DECIMAL;
|
||||
pEditor->m_ShowEnvelopePreview = SHOWENV_NONE;
|
||||
pAction->Call();
|
||||
}
|
||||
if(pEditor->DoButton_Ex(&s_ButtonHex, "Hex", pEditor->m_ShowTileInfo == SHOW_TILE_HEXADECIMAL, &Hex, 0, "[ctrl+shift+i] Show tile information in hexadecimal", IGraphics::CORNER_R))
|
||||
pAction = &pEditor->m_QuickActionShowInfoHex;
|
||||
if(pEditor->DoButton_Ex(&s_ButtonHex, pAction->LabelShort(), pAction->Active(), &Hex, 0, pAction->Description(), IGraphics::CORNER_R))
|
||||
{
|
||||
pEditor->m_ShowTileInfo = SHOW_TILE_HEXADECIMAL;
|
||||
pEditor->m_ShowEnvelopePreview = SHOWENV_NONE;
|
||||
pAction->Call();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,6 +371,30 @@ CUi::EPopupMenuFunctionResult CEditor::PopupMenuSettings(void *pContext, CUIRect
|
|||
}
|
||||
}
|
||||
|
||||
View.HSplitTop(2.0f, nullptr, &View);
|
||||
View.HSplitTop(12.0f, &Slot, &View);
|
||||
{
|
||||
Slot.VMargin(5.0f, &Slot);
|
||||
|
||||
CUIRect Label, Selector;
|
||||
Slot.VSplitMid(&Label, &Selector);
|
||||
CUIRect No, Yes;
|
||||
Selector.VSplitMid(&No, &Yes);
|
||||
|
||||
pEditor->Ui()->DoLabel(&Label, "Auto map reload", 10.0f, TEXTALIGN_ML);
|
||||
|
||||
static int s_ButtonNo = 0;
|
||||
static int s_ButtonYes = 0;
|
||||
if(pEditor->DoButton_Ex(&s_ButtonNo, "No", !g_Config.m_EdAutoMapReload, &No, 0, "Do not run 'hot_reload' on the local server while rcon authed on map save", IGraphics::CORNER_L))
|
||||
{
|
||||
g_Config.m_EdAutoMapReload = false;
|
||||
}
|
||||
if(pEditor->DoButton_Ex(&s_ButtonYes, "Yes", g_Config.m_EdAutoMapReload, &Yes, 0, "Run 'hot_reload' on the local server while rcon authed on map save", IGraphics::CORNER_R))
|
||||
{
|
||||
g_Config.m_EdAutoMapReload = true;
|
||||
}
|
||||
}
|
||||
|
||||
return CUi::POPUP_KEEP_OPEN;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ void CPrompt::OnRender(CUIRect _)
|
|||
{
|
||||
m_PromptSelectedIndex = 0;
|
||||
m_vpFilteredPromptList.clear();
|
||||
if(m_ResetFilterResults && m_pLastAction)
|
||||
if(m_ResetFilterResults && m_pLastAction && !m_pLastAction->Disabled())
|
||||
{
|
||||
m_vpFilteredPromptList.push_back(m_pLastAction);
|
||||
}
|
||||
|
@ -126,6 +126,8 @@ void CPrompt::OnRender(CUIRect _)
|
|||
s_ListBox.SetActive(!Ui()->IsPopupOpen());
|
||||
s_ListBox.DoStart(15.0f, m_vpFilteredPromptList.size(), 1, 5, m_PromptSelectedIndex, &Suggestions, false);
|
||||
|
||||
float LabelWidth = Overlay.w > 855.0f ? 200.0f : 100.0f;
|
||||
|
||||
for(size_t i = 0; i < m_vpFilteredPromptList.size(); i++)
|
||||
{
|
||||
const CListboxItem Item = s_ListBox.DoNextItem(m_vpFilteredPromptList[i], m_PromptSelectedIndex >= 0 && (size_t)m_PromptSelectedIndex == i);
|
||||
|
@ -133,9 +135,10 @@ void CPrompt::OnRender(CUIRect _)
|
|||
continue;
|
||||
|
||||
CUIRect LabelColumn, DescColumn;
|
||||
Item.m_Rect.VSplitLeft(5.0f, nullptr, &LabelColumn);
|
||||
LabelColumn.VSplitLeft(100.0f, &LabelColumn, &DescColumn);
|
||||
LabelColumn.VSplitRight(5.0f, &LabelColumn, nullptr);
|
||||
float Margin = 5.0f;
|
||||
Item.m_Rect.VSplitLeft(Margin, nullptr, &LabelColumn);
|
||||
LabelColumn.VSplitLeft(LabelWidth, &LabelColumn, &DescColumn);
|
||||
DescColumn.VSplitRight(Margin, &DescColumn, nullptr);
|
||||
|
||||
SLabelProperties Props;
|
||||
Props.m_MaxWidth = LabelColumn.w;
|
||||
|
@ -160,7 +163,7 @@ void CPrompt::OnRender(CUIRect _)
|
|||
{
|
||||
if(m_PromptSelectedIndex >= 0)
|
||||
{
|
||||
const CQuickAction *pBtn = m_vpFilteredPromptList[m_PromptSelectedIndex];
|
||||
CQuickAction *pBtn = m_vpFilteredPromptList[m_PromptSelectedIndex];
|
||||
SetInactive();
|
||||
pBtn->Call();
|
||||
m_pLastAction = pBtn;
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
class CPrompt : public CEditorComponent
|
||||
{
|
||||
bool m_ResetFilterResults = true;
|
||||
const CQuickAction *m_pLastAction = nullptr;
|
||||
CQuickAction *m_pLastAction = nullptr;
|
||||
int m_PromptSelectedIndex = -1;
|
||||
|
||||
std::vector<const CQuickAction *> m_vpFilteredPromptList;
|
||||
std::vector<CQuickAction *> m_vpFilteredPromptList;
|
||||
std::vector<CQuickAction *> m_vQuickActions;
|
||||
CLineInputBuffered<512> m_PromptInput;
|
||||
|
||||
|
|
|
@ -52,6 +52,17 @@ public:
|
|||
int Color() { return m_pfnColorCallback(); }
|
||||
|
||||
const char *Label() const { return m_pLabel; }
|
||||
|
||||
// skips to the part of the label after the first colon
|
||||
// useful for buttons that only show the state
|
||||
const char *LabelShort() const
|
||||
{
|
||||
const char *pShort = str_find(m_pLabel, ": ");
|
||||
if(!pShort)
|
||||
return m_pLabel;
|
||||
return pShort + 2;
|
||||
}
|
||||
|
||||
const char *Description() const { return m_pDescription; }
|
||||
};
|
||||
|
||||
|
|
|
@ -4,12 +4,28 @@
|
|||
|
||||
#include "editor_actions.h"
|
||||
|
||||
void CEditor::FillGameTiles(EGameTileOp FillTile) const
|
||||
{
|
||||
std::shared_ptr<CLayerTiles> pTileLayer = std::static_pointer_cast<CLayerTiles>(GetSelectedLayerType(0, LAYERTYPE_TILES));
|
||||
if(pTileLayer)
|
||||
pTileLayer->FillGameTiles(FillTile);
|
||||
}
|
||||
|
||||
bool CEditor::CanFillGameTiles() const
|
||||
{
|
||||
std::shared_ptr<CLayerTiles> pTileLayer = std::static_pointer_cast<CLayerTiles>(GetSelectedLayerType(0, LAYERTYPE_TILES));
|
||||
if(pTileLayer)
|
||||
return pTileLayer->CanFillGameTiles();
|
||||
return false;
|
||||
}
|
||||
|
||||
void CEditor::AddGroup()
|
||||
{
|
||||
m_Map.NewGroup();
|
||||
m_SelectedGroup = m_Map.m_vpGroups.size() - 1;
|
||||
m_EditorHistory.RecordAction(std::make_shared<CEditorActionGroup>(this, m_SelectedGroup, false));
|
||||
}
|
||||
|
||||
void CEditor::AddTileLayer()
|
||||
{
|
||||
std::shared_ptr<CLayer> pTileLayer = std::make_shared<CLayerTiles>(this, m_Map.m_pGameLayer->m_Width, m_Map.m_pGameLayer->m_Height);
|
||||
|
|
|
@ -8,9 +8,157 @@
|
|||
#define DEFAULT_BTN []() -> int { return -1; }
|
||||
|
||||
REGISTER_QUICK_ACTION(
|
||||
AddGroup, "Add group", [&]() { AddGroup(); }, ALWAYS_FALSE, ALWAYS_FALSE, DEFAULT_BTN, "Adds a new group")
|
||||
ToggleGrid,
|
||||
"Toggle Grid",
|
||||
[&]() { MapView()->MapGrid()->Toggle(); },
|
||||
ALWAYS_FALSE,
|
||||
[&]() -> bool { return MapView()->MapGrid()->IsEnabled(); },
|
||||
DEFAULT_BTN,
|
||||
"[Ctrl+G] Toggle Grid.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
Refocus, "Refocus", [&]() { MapView()->Focus(); }, ALWAYS_FALSE, ALWAYS_FALSE, DEFAULT_BTN, "[HOME] Restore map focus")
|
||||
GameTilesAir,
|
||||
"Game tiles: Air",
|
||||
[&]() { FillGameTiles(EGameTileOp::AIR); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesHookable,
|
||||
"Game tiles: Hookable",
|
||||
[&]() { FillGameTiles(EGameTileOp::HOOKABLE); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesDeath,
|
||||
"Game tiles: Death",
|
||||
[&]() { FillGameTiles(EGameTileOp::DEATH); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesUnhookable,
|
||||
"Game tiles: Unhookable",
|
||||
[&]() { FillGameTiles(EGameTileOp::UNHOOKABLE); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesHookthrough,
|
||||
"Game tiles: Hookthrough",
|
||||
[&]() { FillGameTiles(EGameTileOp::HOOKTHROUGH); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesFreeze,
|
||||
"Game tiles: Freeze",
|
||||
[&]() { FillGameTiles(EGameTileOp::FREEZE); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesUnfreeze,
|
||||
"Game tiles: Unfreeze",
|
||||
[&]() { FillGameTiles(EGameTileOp::UNFREEZE); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesDeepFreeze,
|
||||
"Game tiles: Deep Freeze",
|
||||
[&]() { FillGameTiles(EGameTileOp::DEEP_FREEZE); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesDeepUnfreeze,
|
||||
"Game tiles: Deep Unfreeze",
|
||||
[&]() { FillGameTiles(EGameTileOp::DEEP_UNFREEZE); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesBlueCheckTele,
|
||||
"Game tiles: Blue Check Tele",
|
||||
[&]() { FillGameTiles(EGameTileOp::BLUE_CHECK_TELE); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesRedCheckTele,
|
||||
"Game tiles: Red Check Tele",
|
||||
[&]() { FillGameTiles(EGameTileOp::RED_CHECK_TELE); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesLiveFreeze,
|
||||
"Game tiles: Live Freeze",
|
||||
[&]() { FillGameTiles(EGameTileOp::LIVE_FREEZE); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
GameTilesLiveUnfreeze,
|
||||
"Game tiles: Live Unfreeze",
|
||||
[&]() { FillGameTiles(EGameTileOp::LIVE_UNFREEZE); },
|
||||
[&]() -> bool { return !CanFillGameTiles(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Constructs game tiles from this layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
AddGroup,
|
||||
"Add group",
|
||||
[&]() { AddGroup(); },
|
||||
ALWAYS_FALSE,
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Adds a new group.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
ResetZoom,
|
||||
"Reset Zoom",
|
||||
[&]() { MapView()->ResetZoom(); },
|
||||
ALWAYS_FALSE,
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"[Numpad*] Zoom to normal and remove editor offset.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
ZoomOut,
|
||||
"Zoom Out",
|
||||
[&]() { MapView()->Zoom()->ChangeValue(50.0f); },
|
||||
ALWAYS_FALSE,
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"[Numpad-] Zoom out.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
ZoomIn,
|
||||
"Zoom In",
|
||||
[&]() { MapView()->Zoom()->ChangeValue(-50.0f); },
|
||||
ALWAYS_FALSE,
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"[Numpad+] Zoom in.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
Refocus,
|
||||
"Refocus",
|
||||
[&]() { MapView()->Focus(); },
|
||||
ALWAYS_FALSE,
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"[Home] Restore map focus.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
Proof,
|
||||
"Proof",
|
||||
|
@ -28,7 +176,25 @@ REGISTER_QUICK_ACTION(
|
|||
ALWAYS_FALSE,
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Saves the current map under a new name (ctrl+shift+s)")
|
||||
"[Ctrl+Shift+S] Saves the current map under a new name.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
LoadCurrentMap,
|
||||
"Load Current Map",
|
||||
[&]() {
|
||||
if(HasUnsavedData())
|
||||
{
|
||||
m_PopupEventType = POPEVENT_LOADCURRENT;
|
||||
m_PopupEventActivated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadCurrentMap();
|
||||
}
|
||||
},
|
||||
ALWAYS_FALSE,
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"[Ctrl+Alt+L] Opens the current in game map for editing.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
Envelopes,
|
||||
"Envelopes",
|
||||
|
@ -37,6 +203,22 @@ REGISTER_QUICK_ACTION(
|
|||
ALWAYS_FALSE,
|
||||
[&]() -> int { return m_ShowPicker ? -1 : m_ActiveExtraEditor == EXTRAEDITOR_ENVELOPES; },
|
||||
"Toggles the envelope editor.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
ServerSettings,
|
||||
"Server settings",
|
||||
[&]() { m_ActiveExtraEditor = m_ActiveExtraEditor == EXTRAEDITOR_SERVER_SETTINGS ? EXTRAEDITOR_NONE : EXTRAEDITOR_SERVER_SETTINGS; },
|
||||
ALWAYS_FALSE,
|
||||
ALWAYS_FALSE,
|
||||
[&]() -> int { return m_ShowPicker ? -1 : m_ActiveExtraEditor == EXTRAEDITOR_SERVER_SETTINGS; },
|
||||
"Toggles the server settings editor.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
History,
|
||||
"History",
|
||||
[&]() { m_ActiveExtraEditor = m_ActiveExtraEditor == EXTRAEDITOR_HISTORY ? EXTRAEDITOR_NONE : EXTRAEDITOR_HISTORY; },
|
||||
ALWAYS_FALSE,
|
||||
ALWAYS_FALSE,
|
||||
[&]() -> int { return m_ShowPicker ? -1 : m_ActiveExtraEditor == EXTRAEDITOR_HISTORY; },
|
||||
"Toggles the editor history view.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
AddImage,
|
||||
"Add Image",
|
||||
|
@ -44,7 +226,7 @@ REGISTER_QUICK_ACTION(
|
|||
ALWAYS_FALSE,
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Load a new image to use in the map")
|
||||
"Load a new image to use in the map.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
LayerPropAddImage,
|
||||
"Layer: Add Image",
|
||||
|
@ -52,7 +234,48 @@ REGISTER_QUICK_ACTION(
|
|||
[&]() -> bool { return !IsNonGameTileLayerSelected(); },
|
||||
ALWAYS_FALSE,
|
||||
DEFAULT_BTN,
|
||||
"Pick mapres image for currently selected layer")
|
||||
"Pick mapres image for currently selected layer.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
ShowInfoOff,
|
||||
"Show Info: Off",
|
||||
[&]() {
|
||||
m_ShowTileInfo = SHOW_TILE_OFF;
|
||||
m_ShowEnvelopePreview = SHOWENV_NONE;
|
||||
},
|
||||
ALWAYS_FALSE,
|
||||
[&]() -> bool { return m_ShowTileInfo == SHOW_TILE_OFF; },
|
||||
DEFAULT_BTN,
|
||||
"Do not show tile information.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
ShowInfoDec,
|
||||
"Show Info: Dec",
|
||||
[&]() {
|
||||
m_ShowTileInfo = SHOW_TILE_DECIMAL;
|
||||
m_ShowEnvelopePreview = SHOWENV_NONE;
|
||||
},
|
||||
ALWAYS_FALSE,
|
||||
[&]() -> bool { return m_ShowTileInfo == SHOW_TILE_DECIMAL; },
|
||||
DEFAULT_BTN,
|
||||
"[Ctrl+I] Show tile information.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
ShowInfoHex,
|
||||
"Show Info: Hex",
|
||||
[&]() {
|
||||
m_ShowTileInfo = SHOW_TILE_HEXADECIMAL;
|
||||
m_ShowEnvelopePreview = SHOWENV_NONE;
|
||||
},
|
||||
ALWAYS_FALSE,
|
||||
[&]() -> bool { return m_ShowTileInfo == SHOW_TILE_HEXADECIMAL; },
|
||||
DEFAULT_BTN,
|
||||
"[Ctrl+Shift+I] Show tile information in hexadecimal.")
|
||||
REGISTER_QUICK_ACTION(
|
||||
Pipette,
|
||||
"Pipette",
|
||||
[&]() { m_ColorPipetteActive = !m_ColorPipetteActive; },
|
||||
ALWAYS_FALSE,
|
||||
[&]() -> bool { return m_ColorPipetteActive; },
|
||||
DEFAULT_BTN,
|
||||
"[Ctrl+Shift+C] Color pipette. Pick a color from the screen by clicking on it.")
|
||||
|
||||
#undef ALWAYS_FALSE
|
||||
#undef DEFAULT_BTN
|
||||
|
|
|
@ -123,6 +123,13 @@ bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos)
|
|||
delete GameServer()->m_apSavedTees[m_pPlayer->GetCid()];
|
||||
GameServer()->m_apSavedTees[m_pPlayer->GetCid()] = nullptr;
|
||||
}
|
||||
|
||||
if(GameServer()->m_apSavedTeleTees[m_pPlayer->GetCid()])
|
||||
{
|
||||
m_pPlayer->m_LastTeleTee = *GameServer()->m_apSavedTeleTees[m_pPlayer->GetCid()];
|
||||
delete GameServer()->m_apSavedTeleTees[m_pPlayer->GetCid()];
|
||||
GameServer()->m_apSavedTeleTees[m_pPlayer->GetCid()] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -442,7 +449,7 @@ void CCharacter::FireWeapon()
|
|||
if(m_PainSoundTimer <= 0 && !(m_LatestPrevInput.m_Fire & 1))
|
||||
{
|
||||
m_PainSoundTimer = 1 * Server()->TickSpeed();
|
||||
GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_LONG, TeamMask());
|
||||
GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_LONG, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -459,7 +466,7 @@ void CCharacter::FireWeapon()
|
|||
{
|
||||
// reset objects Hit
|
||||
m_NumObjectsHit = 0;
|
||||
GameServer()->CreateSound(m_Pos, SOUND_HAMMER_FIRE, TeamMask());
|
||||
GameServer()->CreateSound(m_Pos, SOUND_HAMMER_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
|
||||
|
||||
Antibot()->OnHammerFire(m_pPlayer->GetCid());
|
||||
|
||||
|
@ -567,7 +574,7 @@ void CCharacter::FireWeapon()
|
|||
MouseTarget // MouseTarget
|
||||
);
|
||||
|
||||
GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE, TeamMask());
|
||||
GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -589,7 +596,7 @@ void CCharacter::FireWeapon()
|
|||
m_Core.m_Ninja.m_CurrentMoveTime = g_pData->m_Weapons.m_Ninja.m_Movetime * Server()->TickSpeed() / 1000;
|
||||
m_Core.m_Ninja.m_OldVelAmount = length(m_Core.m_Vel);
|
||||
|
||||
GameServer()->CreateSound(m_Pos, SOUND_NINJA_FIRE, TeamMask());
|
||||
GameServer()->CreateSound(m_Pos, SOUND_NINJA_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -108,6 +108,9 @@ void CGameContext::Construct(int Resetting)
|
|||
for(auto &pSavedTee : m_apSavedTees)
|
||||
pSavedTee = nullptr;
|
||||
|
||||
for(auto &pSavedTeleTee : m_apSavedTeleTees)
|
||||
pSavedTeleTee = nullptr;
|
||||
|
||||
for(auto &pSavedTeam : m_apSavedTeams)
|
||||
pSavedTeam = nullptr;
|
||||
|
||||
|
@ -131,6 +134,9 @@ void CGameContext::Destruct(int Resetting)
|
|||
for(auto &pSavedTee : m_apSavedTees)
|
||||
delete pSavedTee;
|
||||
|
||||
for(auto &pSavedTeleTee : m_apSavedTeleTees)
|
||||
delete pSavedTeleTee;
|
||||
|
||||
for(auto &pSavedTeam : m_apSavedTeams)
|
||||
delete pSavedTeam;
|
||||
|
||||
|
@ -773,7 +779,6 @@ void CGameContext::StartVote(const char *pDesc, const char *pCommand, const char
|
|||
{
|
||||
// reset votes
|
||||
m_VoteEnforce = VOTE_ENFORCE_UNKNOWN;
|
||||
m_VoteEnforcer = -1;
|
||||
for(auto &pPlayer : m_apPlayers)
|
||||
{
|
||||
if(pPlayer)
|
||||
|
@ -1204,7 +1209,7 @@ void CGameContext::OnTick()
|
|||
}
|
||||
else if(m_VoteEnforce == VOTE_ENFORCE_YES_ADMIN)
|
||||
{
|
||||
Console()->ExecuteLine(m_aVoteCommand, m_VoteEnforcer);
|
||||
Console()->ExecuteLine(m_aVoteCommand, m_VoteCreator);
|
||||
SendChat(-1, TEAM_ALL, "Vote passed enforced by authorized player", -1, FLAG_SIX);
|
||||
EndVote();
|
||||
}
|
||||
|
@ -1712,6 +1717,9 @@ void CGameContext::OnClientDrop(int ClientId, const char *pReason)
|
|||
delete m_apSavedTees[ClientId];
|
||||
m_apSavedTees[ClientId] = nullptr;
|
||||
|
||||
delete m_apSavedTeleTees[ClientId];
|
||||
m_apSavedTeleTees[ClientId] = nullptr;
|
||||
|
||||
m_aTeamMapping[ClientId] = -1;
|
||||
|
||||
m_VoteUpdate = true;
|
||||
|
@ -3208,12 +3216,19 @@ void CGameContext::ConHotReload(IConsole::IResult *pResult, void *pUserData)
|
|||
if(!pSelf->GetPlayerChar(i))
|
||||
continue;
|
||||
|
||||
CCharacter *pChar = pSelf->GetPlayerChar(i);
|
||||
|
||||
// Save the tee individually
|
||||
pSelf->m_apSavedTees[i] = new CSaveTee();
|
||||
pSelf->m_apSavedTees[i]->Save(pSelf->GetPlayerChar(i), false);
|
||||
pSelf->m_apSavedTees[i]->Save(pChar, false);
|
||||
|
||||
if(pSelf->m_apPlayers[i])
|
||||
pSelf->m_apSavedTeleTees[i] = new CSaveTee(pSelf->m_apPlayers[i]->m_LastTeleTee);
|
||||
|
||||
// Save the team state
|
||||
pSelf->m_aTeamMapping[i] = pSelf->GetDDRaceTeam(i);
|
||||
if(pSelf->m_aTeamMapping[i] == TEAM_SUPER)
|
||||
pSelf->m_aTeamMapping[i] = pChar->m_TeamBeforeSuper;
|
||||
|
||||
if(pSelf->m_apSavedTeams[pSelf->m_aTeamMapping[i]])
|
||||
continue;
|
||||
|
@ -3778,7 +3793,7 @@ void CGameContext::RegisterChatCommands()
|
|||
Console()->Register("rescuemode", "?r['auto'|'manual']", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConRescueMode, this, "Sets one of the two rescue modes (auto or manual). Prints current mode if no arguments provided");
|
||||
Console()->Register("tp", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConTeleTo, this, "Depending on the number of supplied arguments, teleport yourself to; (0.) where you are spectating or aiming; (1.) the specified player name");
|
||||
Console()->Register("teleport", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConTeleTo, this, "Depending on the number of supplied arguments, teleport yourself to; (0.) where you are spectating or aiming; (1.) the specified player name");
|
||||
Console()->Register("tpxy", "f[x] f[y]", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConTeleXY, this, "Teleport yourself to the specified coordinates. A tilde (~) can be used to denote your current position, e.g. '/tpxy ~1 ~' to teleport one tile to the right");
|
||||
Console()->Register("tpxy", "s[x] s[y]", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConTeleXY, this, "Teleport yourself to the specified coordinates. A tilde (~) can be used to denote your current position, e.g. '/tpxy ~1 ~' to teleport one tile to the right");
|
||||
Console()->Register("lasttp", "", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConLastTele, this, "Teleport yourself to the last location you teleported to");
|
||||
Console()->Register("tc", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConTeleCursor, this, "Teleport yourself to player or to where you are spectating/or looking if no player name is given");
|
||||
Console()->Register("telecursor", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConTeleCursor, this, "Teleport yourself to player or to where you are spectating/or looking if no player name is given");
|
||||
|
@ -4820,7 +4835,6 @@ void CGameContext::ForceVote(int EnforcerId, bool Success)
|
|||
return;
|
||||
|
||||
m_VoteEnforce = Success ? CGameContext::VOTE_ENFORCE_YES_ADMIN : CGameContext::VOTE_ENFORCE_NO_ADMIN;
|
||||
m_VoteEnforcer = EnforcerId;
|
||||
|
||||
char aBuf[256];
|
||||
const char *pOption = Success ? "yes" : "no";
|
||||
|
|
|
@ -183,6 +183,7 @@ public:
|
|||
bool m_aPlayerHasInput[MAX_CLIENTS];
|
||||
CSaveTeam *m_apSavedTeams[MAX_CLIENTS];
|
||||
CSaveTee *m_apSavedTees[MAX_CLIENTS];
|
||||
CSaveTee *m_apSavedTeleTees[MAX_CLIENTS];
|
||||
int m_aTeamMapping[MAX_CLIENTS];
|
||||
|
||||
// returns last input if available otherwise nulled PlayerInput object
|
||||
|
@ -575,7 +576,6 @@ public:
|
|||
VOTE_TYPE_SPECTATE,
|
||||
};
|
||||
int m_VoteVictim;
|
||||
int m_VoteEnforcer;
|
||||
|
||||
inline bool IsOptionVote() const { return m_VoteType == VOTE_TYPE_OPTION; }
|
||||
inline bool IsKickVote() const { return m_VoteType == VOTE_TYPE_KICK; }
|
||||
|
|
|
@ -78,16 +78,16 @@ void CTeeInfo::ToSixup()
|
|||
{
|
||||
int ColorBody = ColorHSLA(m_ColorBody).UnclampLighting().Pack(ms_DarkestLGT7);
|
||||
int ColorFeet = ColorHSLA(m_ColorFeet).UnclampLighting().Pack(ms_DarkestLGT7);
|
||||
m_aUseCustomColors[0] = true;
|
||||
m_aUseCustomColors[1] = true;
|
||||
m_aUseCustomColors[2] = true;
|
||||
m_aUseCustomColors[3] = true;
|
||||
m_aUseCustomColors[4] = true;
|
||||
m_aSkinPartColors[0] = ColorBody;
|
||||
m_aSkinPartColors[1] = 0x22FFFFFF;
|
||||
m_aSkinPartColors[2] = ColorBody;
|
||||
m_aSkinPartColors[3] = ColorBody;
|
||||
m_aSkinPartColors[4] = ColorFeet;
|
||||
m_aUseCustomColors[protocol7::SKINPART_BODY] = true;
|
||||
m_aUseCustomColors[protocol7::SKINPART_MARKING] = true;
|
||||
m_aUseCustomColors[protocol7::SKINPART_DECORATION] = true;
|
||||
m_aUseCustomColors[protocol7::SKINPART_HANDS] = true;
|
||||
m_aUseCustomColors[protocol7::SKINPART_FEET] = true;
|
||||
m_aSkinPartColors[protocol7::SKINPART_BODY] = ColorBody;
|
||||
m_aSkinPartColors[protocol7::SKINPART_MARKING] = 0x22FFFFFF;
|
||||
m_aSkinPartColors[protocol7::SKINPART_DECORATION] = ColorBody;
|
||||
m_aSkinPartColors[protocol7::SKINPART_HANDS] = ColorBody;
|
||||
m_aSkinPartColors[protocol7::SKINPART_FEET] = ColorFeet;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,6 +137,10 @@ void CTeeInfo::FromSixup()
|
|||
|
||||
str_copy(m_aSkinName, g_aStdSkins[BestSkin].m_aSkinName, sizeof(m_aSkinName));
|
||||
m_UseCustomColor = true;
|
||||
m_ColorBody = ColorHSLA(m_aUseCustomColors[0] ? m_aSkinPartColors[0] : 255).UnclampLighting(ms_DarkestLGT7).Pack(ColorHSLA::DARKEST_LGT);
|
||||
m_ColorFeet = ColorHSLA(m_aUseCustomColors[4] ? m_aSkinPartColors[4] : 255).UnclampLighting(ms_DarkestLGT7).Pack(ColorHSLA::DARKEST_LGT);
|
||||
m_ColorBody = ColorHSLA(m_aUseCustomColors[protocol7::SKINPART_BODY] ? m_aSkinPartColors[protocol7::SKINPART_BODY] : 255)
|
||||
.UnclampLighting(ms_DarkestLGT7)
|
||||
.Pack(ColorHSLA::DARKEST_LGT);
|
||||
m_ColorFeet = ColorHSLA(m_aUseCustomColors[protocol7::SKINPART_FEET] ? m_aSkinPartColors[protocol7::SKINPART_FEET] : 255)
|
||||
.UnclampLighting(ms_DarkestLGT7)
|
||||
.Pack(ColorHSLA::DARKEST_LGT);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#ifndef GAME_VERSION_H
|
||||
#define GAME_VERSION_H
|
||||
#ifndef GAME_RELEASE_VERSION
|
||||
#define GAME_RELEASE_VERSION "18.4"
|
||||
#define GAME_RELEASE_VERSION "18.5"
|
||||
#endif
|
||||
|
||||
// teeworlds
|
||||
|
@ -13,7 +13,7 @@
|
|||
#define GAME_NETVERSION7 "0.7 802f1be60a05665f"
|
||||
|
||||
// ddnet
|
||||
#define DDNET_VERSION_NUMBER 18040
|
||||
#define DDNET_VERSION_NUMBER 18050
|
||||
extern const char *GIT_SHORTREV_HASH;
|
||||
#define GAME_NAME "DDNet"
|
||||
#endif
|
||||
|
|
|
@ -109,8 +109,8 @@ void cxxbridge1$IConsole_IResult$GetString(const ::IConsole_IResult &self, ::std
|
|||
}
|
||||
|
||||
void cxxbridge1$IConsole_IResult$GetColor(const ::IConsole_IResult &self, ::std::uint32_t Index, bool Light, ::ColorHSLA *return$) noexcept {
|
||||
::ColorHSLA (::IConsole_IResult::*GetColor$)(::std::uint32_t, bool) const = &::IConsole_IResult::GetColor;
|
||||
new (return$) ::ColorHSLA((self.*GetColor$)(Index, Light));
|
||||
std::optional<::ColorHSLA> (::IConsole_IResult::*GetColor$)(::std::uint32_t, bool) const = &::IConsole_IResult::GetColor;
|
||||
new(return$)::ColorHSLA((self.*GetColor$)(Index, Light).value_or(::ColorHSLA(0, 0, 0)));
|
||||
}
|
||||
|
||||
::std::int32_t cxxbridge1$IConsole_IResult$NumArguments(const ::IConsole_IResult &self) noexcept {
|
||||
|
|
103
src/test/editor.cpp
Normal file
103
src/test/editor.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <base/system.h>
|
||||
|
||||
bool is_letter(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
|
||||
|
||||
bool IsValidEditorTooltip(const char *pTooltip, char *pErrorMsg, int ErrorMsgSize)
|
||||
{
|
||||
pErrorMsg[0] = '\0';
|
||||
char aHotkey[512];
|
||||
aHotkey[0] = '\0';
|
||||
|
||||
if(pTooltip[0] == '[')
|
||||
{
|
||||
str_copy(aHotkey, pTooltip + 1);
|
||||
const char *pHotkeyEnd = str_find(aHotkey, "]");
|
||||
if(!pHotkeyEnd)
|
||||
{
|
||||
str_copy(pErrorMsg, "tooltip missing closing square bracket", ErrorMsgSize);
|
||||
return false;
|
||||
}
|
||||
aHotkey[pHotkeyEnd - aHotkey] = '\0';
|
||||
|
||||
for(int i = 0; aHotkey[i]; i++)
|
||||
{
|
||||
bool ExpectLowerCase = true;
|
||||
if(i == 0)
|
||||
{
|
||||
ExpectLowerCase = false;
|
||||
}
|
||||
else if(!is_letter(aHotkey[i - 1]))
|
||||
{
|
||||
// the first character of a word should be uppercase
|
||||
ExpectLowerCase = false;
|
||||
}
|
||||
|
||||
bool IsLower = aHotkey[i] >= 'a' && aHotkey[i] <= 'z';
|
||||
bool IsUpper = aHotkey[i] >= 'A' && aHotkey[i] <= 'Z';
|
||||
|
||||
if(ExpectLowerCase && IsUpper)
|
||||
{
|
||||
str_format(pErrorMsg, ErrorMsgSize, "expected character '%c' at index %d to be lower case", aHotkey[i], i + 1);
|
||||
return false;
|
||||
}
|
||||
if(!ExpectLowerCase && IsLower)
|
||||
{
|
||||
str_format(pErrorMsg, ErrorMsgSize, "expected character '%c' at index %d to be upper case", aHotkey[i], i + 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *pParenthesis = str_find(pTooltip, "(");
|
||||
if(pParenthesis)
|
||||
{
|
||||
const char *pHotkey = str_find_nocase(pParenthesis, "ctrl");
|
||||
if(!pHotkey)
|
||||
pHotkey = str_find_nocase(pParenthesis, "shift");
|
||||
if(!pHotkey)
|
||||
pHotkey = str_find_nocase(pParenthesis, "home");
|
||||
|
||||
if(pHotkey)
|
||||
{
|
||||
int Offset = pHotkey - pTooltip;
|
||||
str_format(pErrorMsg, ErrorMsgSize, "found hotkey at offset %d. Hotkeys must be defined at the start.", Offset);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!str_endswith(pTooltip, "."))
|
||||
{
|
||||
str_copy(pErrorMsg, "tooltip has to end with a dot", ErrorMsgSize);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssertTooltip(const char *pTooltip)
|
||||
{
|
||||
char aError[512];
|
||||
bool IsValid = IsValidEditorTooltip(pTooltip, aError, sizeof(aError));
|
||||
if(!IsValid)
|
||||
{
|
||||
dbg_msg("test", "Invalid tooltip: %s", pTooltip);
|
||||
dbg_msg("test", "ERROR: %s", aError);
|
||||
}
|
||||
EXPECT_TRUE(IsValid);
|
||||
}
|
||||
|
||||
TEST(Editor, QuickActionNames)
|
||||
{
|
||||
char aError[512];
|
||||
EXPECT_TRUE(IsValidEditorTooltip("hello world.", aError, sizeof(aError)));
|
||||
EXPECT_TRUE(IsValidEditorTooltip("[Ctrl+H] hello world.", aError, sizeof(aError)));
|
||||
EXPECT_FALSE(IsValidEditorTooltip("[Ctrl+H hello world.", aError, sizeof(aError)));
|
||||
EXPECT_FALSE(IsValidEditorTooltip("[ctrl+h] hello world.", aError, sizeof(aError)));
|
||||
EXPECT_FALSE(IsValidEditorTooltip("hello world", aError, sizeof(aError)));
|
||||
EXPECT_FALSE(IsValidEditorTooltip("hello world (Ctrl+H).", aError, sizeof(aError)));
|
||||
|
||||
#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) AssertTooltip(description);
|
||||
#include <game/editor/quick_actions.h>
|
||||
#undef REGISTER_QUICK_ACTION
|
||||
}
|
Loading…
Reference in a new issue