Compare commits

...

147 commits

Author SHA1 Message Date
Robert Müller c89509bc4b Add icon button to open console in bottom right of start menu
Add a button with the "terminal" icon in the bottom right of the start menu to open the local console to ensure that the local console is usable also when no physical keyboard (with F-keys) is available.
2024-09-07 13:12:30 +02:00
Dennis Felsing fc058fa432
Merge pull request #8906 from Robyt3/Client-Download-Gameserver-Fix
Fix map download fallback from game server
2024-09-06 22:11:02 +00:00
Dennis Felsing 3b22a3e02f
Merge pull request #8905 from furo321/hot-reload-super-crash
Fix crash with `hot_reload` while in `super`
2024-09-06 22:08:14 +00:00
Robert Müller ed1ef4e694 Fix map download fallback from game server
Do not reset the active map download's information before using the fallback map download.

Remove redundant calls of `ResetMapDownload` before disconnecting, as this already resets the map download.

Closes #8885. Regression from #8848.
2024-09-06 20:55:02 +02:00
furo 9103332e36 Fix crash with hot_reload while in super 2024-09-06 20:12:07 +02:00
Dennis Felsing 0948a53648
Merge pull request #8904 from furo321/fix-force-yes
Fix `random_unfinished_map` not working with `vote yes`
2024-09-06 17:34:21 +00:00
furo bbd34c9452 Fix random_unfinished_map not working with vote yes 2024-09-06 18:56:58 +02:00
Dennis Felsing e4282f100a
Merge pull request #8902 from KebsCS/pr-hotreload-lasttp
Add /lasttp to hot reload
2024-09-06 16:24:48 +00:00
KebsCS eb9e73f68b
Add /lasttp to hot reload 2024-09-06 16:31:46 +02:00
Dennis Felsing e0a95d14a6
Merge pull request #8898 from KebsCS/pr-country-filters
Change country and types filter behavior
2024-09-05 13:16:21 +00:00
KebsCS fa28ed77a6
Change country and types filter behavior 2024-09-05 14:24:11 +02:00
Dennis Felsing 5335813629
Merge pull request #8897 from KebsCS/pr-command-argument-validation
Fix /tpxy arg validation
2024-09-05 09:26:15 +00:00
KebsCS 796fa4275f
Fix tpxy arg validation 2024-09-05 11:04:03 +02:00
Dennis Felsing 5b0163d069
Merge pull request #8896 from dobrykafe/pr-auto-map-reload-setting
Add `ed_auto_map_reload` setting
2024-09-05 07:33:35 +00:00
dobrykafe c9e7e0f01b add ed_auto_map_reload setting 2024-09-05 00:23:03 +02:00
Dennis Felsing cf107a81a1
Merge pull request #8892 from ChillerDragon/pr_scoreboard_pause_game
Do not force scoreboard open when the game is paused
2024-09-04 07:15:29 +00:00
Robert Müller 4e2d7e2104
Merge pull request #8890 from dobrykafe/pr-player-name-07
Use player name function instead of config
2024-09-04 05:57:31 +00:00
ChillerDragon 74bb327799 Do not force scoreboard open when the game is paused
If the game is paused and a player joins a server (sv_tournament_mode 0)
The scoreboard will be forced open. Unless the client configured cl_scoreboard_on_death 0.

This can be quite annoying. Especially in the brand new 0.7 feature
where users can pause the game. Oy realized that this is a problem 12
year ago:

aec468a3c4 (diff-e0ff7a1d6079610adb64fc89fbfff23a381ed92f268d8fe188731a9e0c323b0aR389-R390)

For ddnet servers this would mostly affect tournaments where paused games are
used to give everyone enough time to download the map.

A open scoreboard also blocks broadcasts. So new users might miss the
admin announcements explaining why the game is paused.
2024-09-04 10:21:16 +08:00
dobrykafe 22d699fbc0 use player name function instead of config 2024-09-04 02:37:16 +02:00
Dennis Felsing 51c0b4dafc
Merge pull request #8888 from dobrykafe/pr-fix-updater
Add missing parentheses in updater code
2024-09-03 11:17:51 +00:00
dobrykafe 5f57ba9130 add missing parentheses in updater code 2024-09-03 12:44:02 +02:00
Dennis Felsing 69fa7ea18f
Merge pull request #8886 from furo321/use-str_copy
Use `str_copy` instead of `str_format` where possible
2024-09-03 07:08:54 +00:00
Dennis Felsing 41c40fb27f
Merge pull request #8883 from ChillerDragon/pr_actions_zoom
Add quick actions for zooming the view
2024-09-03 07:02:15 +00:00
furo 0664f12265 assert if str_format is used with no arguments 2024-09-03 08:49:55 +02:00
furo 8b5da71e89 Use str_copy instead of str_format where possible 2024-09-03 08:49:47 +02:00
ChillerDragon d0eebee44d Add quick actions for zooming the view 2024-09-03 09:58:36 +08:00
Dennis Felsing 1cbdb9d2f0
Merge pull request #8881 from rffontenelle/patch-8
Update brazilian_portuguese.txt
2024-09-02 21:37:22 +00:00
Dennis Felsing 8f79d4252c
Merge pull request #8882 from ewancg/master
make reconnect screen count down from 5-1 instead of 4-0
2024-09-02 21:37:07 +00:00
Ewan Green 7abad2b8a4 make reconnect screen count down from 5-1 instead of 4-0 2024-09-02 13:23:34 -06:00
Rafael Fontenelle c64b744ad3
Update brazilian_portuguese.txt 2024-09-02 16:02:39 -03:00
Dennis Felsing f599449f31
Merge pull request #8879 from furo321/conchain_browser
Add console chain for browser filters
2024-09-02 15:24:40 +00:00
Dennis Felsing 226d948acc Version 18.5 2024-09-02 17:02:21 +02:00
furo 58f14edb95 Add console chain for browser filters 2024-09-02 16:45:04 +02:00
Dennis Felsing a3fc627285
Merge pull request #8874 from ChillerDragon/pr_test_tooltip_style
Test tooltip description style
2024-09-02 06:17:10 +00:00
Dennis Felsing f79784bec7
Merge pull request #8873 from ChillerDragon/pr_actions_settings_and_history
Add quick actions Server settings and History
2024-09-02 06:16:31 +00:00
Dennis Felsing ccaddd2016
Merge pull request #8872 from ChillerDragon/pr_action_pipettte
Add quick action "Pipette"
2024-09-02 06:16:29 +00:00
Robert Müller 7a60f4a5e7
Merge pull request #8875 from ChillerDragon/pr_prompt_label_size
Grow prompt label if there is space and fix margins
2024-09-02 05:47:50 +00:00
ChillerDragon e9187cc352 Grow prompt label if there is space and fix margins
Closed #8867
2024-09-02 11:16:20 +08:00
ChillerDragon 2cf6622cd7 Test tooltip description style
See https://github.com/ddnet/ddnet/issues/8870
2024-09-02 10:32:55 +08:00
ChillerDragon 19a888734c Add quick actions Server settings and History 2024-09-02 09:23:21 +08:00
ChillerDragon 44fc871463 Add quick action "Pipette" 2024-09-02 09:13:03 +08:00
Dennis Felsing 157498799e
Merge pull request #8871 from BlaiZephyr/fix_inconsistency
fix inconsistent quickaction tooltips
2024-09-01 21:17:33 +00:00
BlaiZephyr a017190753 fix inconsistent quickaction labels 2024-09-01 20:09:06 +02:00
Dennis Felsing e17da385dd
Merge pull request #8869 from ChillerDragon/pr_fix_dead_order
Put dead players at the bottom of the scoreboard
2024-09-01 16:41:08 +00:00
Dennis Felsing bfc6a15df9
Merge pull request #8854 from Veydzher/patch-1
Update ukrainian.txt
2024-09-01 16:37:54 +00:00
ChillerDragon 2a26c1c5d6 Put dead players at the bottom of the scoreboard
Closed #8868
2024-09-01 19:17:03 +08:00
veydzh3r 624ac85e8c
Update ukrainian.txt 2024-09-01 10:29:39 +01:00
Dennis Felsing d9b031c887
Merge pull request #8864 from ChillerDragon/pr_gt_descs
Use same descriptions for game tile actions as the button
2024-09-01 09:21:09 +00:00
veydzh3r 403c4a40da
Update ukrainian.txt 2024-09-01 10:20:44 +01:00
Dennis Felsing 0e2960206a
Merge pull request #8865 from ChillerDragon/pr_action_toggle_grid
Add quick action "Toggle Grid"
2024-09-01 09:20:34 +00:00
veydzh3r a8616ef2ab
Merge branch 'ddnet:master' into patch-1 2024-09-01 11:57:42 +03:00
ChillerDragon 8b304cab23 Add quick action "Toggle Grid" 2024-09-01 16:23:04 +08:00
veydzh3r 9e88ca5846
Update ukrainian.txt 2024-09-01 09:23:03 +01:00
ChillerDragon 1b8e402a65 Use same descriptions for game tile actions as the button 2024-09-01 16:15:11 +08:00
Dennis Felsing 30acedaac5
Merge pull request #8820 from TsFreddie/resample
Resample to the device playback rate
2024-09-01 07:35:38 +00:00
Dennis Felsing bb3c76a290
Merge pull request #8834 from KebsCS/pr-command-argument-validation
Add validation for chat and console command arguments
2024-09-01 07:35:36 +00:00
Dennis Felsing 341e3f6ec7
Merge pull request #8842 from furo321/swedish-18.5
Update Swedish translations for 18.5
2024-09-01 07:33:52 +00:00
Dennis Felsing d4f72803be
Merge pull request #8860 from ChillerDragon/pr_action_gametiles
Add quick actions for game tiles
2024-09-01 07:33:46 +00:00
Dennis Felsing f2d5b83f84
Merge pull request #8863 from ChillerDragon/pr_sixup_enum
Use enum instead of magic number in skin color translation
2024-09-01 07:11:17 +00:00
Dennis Felsing fcf810cae1
Merge pull request #8862 from ChillerDragon/pr_consistent_nolint_fireweapon
Consistently apply the NOLINT comment for all sounds in FireWeapon
2024-09-01 07:11:03 +00:00
Dennis Felsing 50b0af1614
Merge pull request #8858 from ChillerDragon/pr_action_info
Add quick actions for Show Info Off/Dec/Hex
2024-09-01 07:05:57 +00:00
Dennis Felsing b5c11bc860
Merge pull request #8857 from ChillerDragon/pr_action_load_current_map
Add quick action "Load Current Map"
2024-09-01 07:05:55 +00:00
Dennis Felsing 7951136fab
Merge pull request #8861 from ChillerDragon/pr_prompt_fix_last_disabled
Fix editor prompt suggesting disabled last action
2024-09-01 07:05:30 +00:00
ChillerDragon 82ca4bc335 Use enum instead of magic number in skin color translation 2024-09-01 12:44:00 +08:00
ChillerDragon 5986ee03b9 Consistently apply the NOLINT comment for all sounds in FireWeapon
Most but not all sound creations in CCharacter::FireWeapon have this NOLINT comment.

Somehow clang does not get tripped on all of those. But sooner or later it might.
I currently got a new error in my downstream fork when tweaking
FireWeapon a bit. So I applied the NOLINT comment to ALL occurences in
FireWeapon not only the ones that clangd finds at the moment.
2024-09-01 11:39:17 +08:00
ChillerDragon e1cb617c42 Add quick actions for game tiles 2024-09-01 10:50:44 +08:00
ChillerDragon daad41fbd7 Fix editor prompt suggesting disabled last action
The editor prompt always has as a first entry the last action that ran.
But this entry should not show up if that action is currently disabled.
2024-09-01 10:42:55 +08:00
ChillerDragon ed49fef917 Add quick actions for Show Info Off/Dec/Hex 2024-09-01 08:53:04 +08:00
ChillerDragon e20250cd65 Add quick action "Load Current Map" 2024-09-01 08:15:58 +08:00
Dennis Felsing 36f0bcc509
Merge pull request #8618 from ChillerDragon/pr_editor_prompt
Add ctrl+p editor prompt (quick actions)
2024-08-31 21:42:48 +00:00
Dennis Felsing dd82cf83a2
Merge pull request #8853 from gerdoe-jr/master
Update russian.txt
2024-08-31 21:36:34 +00:00
Dennis Felsing 8ca93d451b
Merge pull request #8855 from ChillerDragon/pr_armor_progress
Only do armor progress hack in ddrace controller
2024-08-31 21:35:48 +00:00
Dennis Felsing 871f97a283
Merge pull request #8856 from ChillerDragon/pr_gun_damage
Call TakeDamage on projectile and laser hit (gun, shotgun and laser)
2024-08-31 21:35:16 +00:00
ChillerDragon 2c77aeef2b Add quick action to pick image for selected layer 2024-08-31 21:57:32 +08:00
ChillerDragon f053a29463 Add ctrl+p editor prompt (quick actions) 2024-08-31 21:57:31 +08:00
ChillerDragon f8a4e7dbab Call TakeDamage on projectile and laser hit (gun, shotgun and laser)
The grenade and hammer already triggered TakeDamage
now all weapons hitting a tee call the method again
2024-08-31 19:59:05 +08:00
ChillerDragon f44def1cdd Only do armor progress hack in ddrace controller
Only mess with the characters armor in ddrace controllers.
This allows other game modes to use the armor as actual armor.
2024-08-31 18:57:42 +08:00
veydzh3r bb45db8ad3
Update ukrainian.txt 2024-08-31 11:51:57 +01:00
Vlad 468900acfe typos in russian.txt 2024-08-31 13:44:09 +03:00
Dennis Felsing 6ab597a465
Merge pull request #8852 from JuraIBOZO/patch-8
Update russian.txt
2024-08-31 08:37:53 +00:00
Dennis Felsing a83248b9ec
Merge pull request #8851 from ChillerDragon/pr_chr_get_tuning
Simplify character tuning code
2024-08-31 08:24:42 +00:00
JuraIBOZO 383bef93b9 Update russian.txt 2024-08-31 10:19:36 +02:00
Robert Müller f0449befa5
Merge pull request #8841 from ChillerDragon/pr_unhardcode_07_gameflags
Share gameflags for 0.6/0.7 instead of hardcoding it
2024-08-31 07:38:22 +00:00
ChillerDragon c02a2ed1d6 Simplify character tuning code
entities/character.cpp now has the same api as prediction/entities/character.cpp
2024-08-31 10:41:39 +08:00
KebsCS d452bcda8f
Add validation for chat and console command arguments 2024-08-31 02:15:33 +02:00
Dennis Felsing 60688bf80e
Merge pull request #8850 from dobrykafe/pr-18.5-translations
Update Czech and Slovak translations for 18.5
2024-08-30 22:04:36 +00:00
dobrykafe 29a205a6d9 update slovak translations for 18.5 2024-08-30 22:18:45 +02:00
dobrykafe c10b466a40 update czech translations for 18.5 2024-08-30 22:18:35 +02:00
Dennis Felsing 914867d980
Merge pull request #8849 from furo321/fix-twlang-endline
Set correct end line in `twlang.py` for strings with context
2024-08-30 18:44:10 +00:00
furo d40cc86bfa Set correct end line in twlang.py for strings with context 2024-08-30 20:15:26 +02:00
Dennis Felsing f754054874
Merge pull request #8848 from Robyt3/Client-Map-Download-Reset-Cleanup
Fix map downloading overriding more recent map change messages
2024-08-30 16:20:48 +00:00
Robert Müller 05970178b2 Fix map downloading overriding more recent map change messages
Following sequence of events was possible:

1. HTTP map download for map X is started (e.g. client initially connects to server).
2. Map change message for map Y arrives while downloading (e.g. vote for another map has passed on the server).
3. Client already has map Y and loads it directly without starting or resetting the map download.
4. HTTP map download task for map X finishes.
5. Map X is loaded, overriding the map Y that should be loaded. This is one cause for #2804.

This could potentially also happen with the map download from the game-server, if the final map data chunk for a downloaded map is received directly after another map change message for a map which does not need to be downloaded, though this seems more unlikely.

This is fixed by resetting the current map download immediately after receiving a valid map change message instead of only when starting a new map download.

The relevant code to reset the map download is moved to `CClient::ResetMapDownload` to avoid duplicate code and ensure that the temporary map download file is always deleted and closed.
2024-08-30 17:54:38 +02:00
Dennis Felsing 3d5b533816
Merge pull request #8847 from EGYT5453/ua-18.5
Update Ukrainian l10n (18.5)
2024-08-30 09:16:10 +00:00
Andrii cdc5b9922a
Update Ukrainian l10n (18.5) 2024-08-30 10:41:55 +03:00
Dennis Felsing 528a38265e
Merge pull request #8846 from n0Ketchp/patch-21
Update spanish.txt
2024-08-30 06:52:17 +00:00
noKetchup 099ec44eac
Update spanish.txt 2024-08-30 00:29:21 -04:00
Dennis Felsing b641fdb8fd
Merge pull request #8845 from GokturkTalha/patch-11
Update Azerbaijanese translations for 18.5
2024-08-29 22:18:02 +00:00
Dennis Felsing e54fb30e72
Merge pull request #8844 from GokturkTalha/patch-10
Update Turkish translations for 18.5
2024-08-29 22:18:00 +00:00
Talha Aygün 325f908f09
Update Azerbaijanese translations for 18.5 2024-08-29 23:38:53 +03:00
Talha Aygün 870cff3da2
add author 2024-08-29 23:31:07 +03:00
Talha Aygün 4ea9ed9b2b
Update Turkish translations for 18.5 2024-08-29 23:29:27 +03:00
Jupeyy 98b0f50111
Merge pull request #8661 from DynamoFox/workaround-glew-on-wayland-partial-init
Workaround GLEW error code on init with Wayland
2024-08-29 18:21:08 +00:00
DynamoFox fe35322bb0 Workaround GLEW error code when calling glewInit() on Wayland
* Fixes OpenGL on Wayland when the GLX flavor of GLEW is linked
* Only if SDL2's video driver is "wayland" we also allow the
  GLEW_ERROR_NO_GLX_DISPLAY error code to be accepted as if glewInit()
  actually succeeded
* Hopefully this won't be needed anymore on future versions of GLEW when
  glewContextInit() is made part of the public API
2024-08-29 19:47:43 +02:00
Dennis Felsing 41cd82c2d3
Merge pull request #8653 from DynamoFox/force-sdl2-video-driver-selection-on-linux
Fix game launch on Wayland-only systems with SDL2.30+
2024-08-29 16:06:03 +00:00
Dennis Felsing 21375f0e1d
Merge pull request #8843 from Pioooooo/chinese-18.5
Update Chinese translations for 18.5
2024-08-29 13:42:23 +00:00
Pioooooo 55891afc50 Update Chinese translations for 18.5 2024-08-29 11:11:41 +00:00
furo 1d65fff3ff Update Swedish translations for 18.5 2024-08-29 12:46:11 +02:00
Robert Müller edcc3245f4
Merge pull request #8821 from def-/pr-transl-18.5
Update translations for 18.5, remove 0.7-only translations
2024-08-29 08:11:18 +00:00
Dennis Felsing 88739c5345 Update translations for 18.5, remove 0.7-only translations
Fix some minor stuff
2024-08-29 09:53:28 +02:00
Dennis Felsing da4bc09429
Merge pull request #8840 from Pioooooo/translation-fix
Update Chinese translations
2024-08-29 07:36:54 +00:00
ChillerDragon 43dee5d4c5 Share gameflags for 0.6/0.7 instead of hardcoding it
5cf0e5e997/datasrc/network.py (L8)
5cf0e5e997/datasrc/seven/network.py (L10)

0.6 game flags:

```python
GameFlags = ["TEAMS", "FLAGS"]
```

0.7 game flags:

```python
GameFlags = Flags("GAMEFLAG", ["TEAMS", "FLAGS", "SURVIVAL", "RACE"])
```

The 0.7 protocol extended the game flags without changing the old 0.6 flags.
So flag teams and flag flags are the same in both versions and 0.7 just
has additional flags.

The server is now sending the same m_GameFlags from the gamemode
controller to 0.6 and 0.7 connections. It clamps away the unsupported
flags for 0.6.

Before this commit the ddnet server would always send the hardcodet race
flag to 0.7 connections while ignoring what the gamemode set as a game
flag.
2024-08-29 15:15:48 +08:00
Pioooooo ba4f0368a4 Update Chinese translations 2024-08-29 04:34:17 +00:00
Dennis Felsing 5cf0e5e997
Merge pull request #8839 from dobrykafe/pr-create-07-skin-folder
Create 0.7 skin folder
2024-08-28 23:00:58 +00:00
dobrykafe 218ce1f53f create 0.7 skin folder 2024-08-29 00:36:06 +02:00
Dennis Felsing 91ae4ca120
Merge pull request #8838 from dobrykafe/pr-07-skins-directory
Add 0.7 skins directory button
2024-08-28 22:19:27 +00:00
dobrykafe 41fee0bb3f add 0.7 skins directory button 2024-08-28 23:57:12 +02:00
Dennis Felsing cc1dc405fe
Merge pull request #8837 from Robyt3/Android-Build-NDK-Version-Fix
Remove unnecessary `ndkVersion` from Gradle build
2024-08-28 17:12:22 +00:00
Robert Müller 1dd56f1a90 Remove unnecessary ndkVersion from Gradle build
Gradle can determine the NDK version automatically and this property is only required if multiple NDK versions are installed. The NDK version previously determined from the filename could not be parsed by Gradle anyway.
2024-08-28 18:12:05 +02:00
TsFreddie ecec1ff2a7 Resample to the device playback rate 2024-08-28 10:55:35 +08:00
Dennis Felsing 5a66dc05f4
Merge pull request #8828 from dobrykafe/pr-save-07-skins
Implement 0.7 skin save button
2024-08-27 21:50:23 +00:00
Dennis Felsing 7df1b24491
Merge pull request #8833 from furo321/vote-creator
Set `m_VoteCreator` when possible
2024-08-27 21:20:41 +00:00
dobrykafe 31ee6abe82 implement 0.7 skin save button 2024-08-27 23:18:43 +02:00
Dennis Felsing d78032e9a6
Merge pull request #8832 from Robyt3/Android-Shortcuts-Graphics-Backends
Add Android shortcuts for launching with Vulkan/OpenGL
2024-08-27 21:17:44 +00:00
Dennis Felsing 3d6820ab68
Merge pull request #8831 from furo321/fix-crash-hotreload
Fix `double free or corruption` crash with `hot_reload`
2024-08-27 21:16:58 +00:00
Dennis Felsing 8b7e5dee4d
Merge pull request #8830 from dobrykafe/pr-conchain-07-tee
Add console chain for 0.7 tee
2024-08-27 21:16:37 +00:00
Dennis Felsing 76c44cfb40
Merge pull request #8829 from furo321/use-getmapname
Use `GetMapName()` when getting the map name
2024-08-27 21:16:12 +00:00
furo f407adc61c Set m_VoteCreator when using force_vote 2024-08-27 21:44:00 +02:00
furo 9fbbf5cde7 Fix using random_unfinished_map from rcon 2024-08-27 21:43:22 +02:00
Robert Müller dc0d4b3b76 Add Android shortcuts for launching with Vulkan/OpenGL
Define shortcuts to launch the client with either Vulkan or OpenGL graphics backend, which can be accessed by long-pressing the app icon on the home screen. This feature is available for Android 7.1 and newer.
2024-08-27 21:04:43 +02:00
furo 9a7bc70f76 Fix double free or corruption crash with hot_reload 2024-08-27 20:44:55 +02:00
furo 0e016f9732 Use GetMapName() when getting the map name 2024-08-27 20:41:43 +02:00
dobrykafe cb57aa82bf add console chain for 0.7 tee 2024-08-27 20:01:27 +02:00
Robert Müller 14f1b36dd1
Merge pull request #8825 from KebsCS/pr-ghost-sorting
Add ghost list sorting
2024-08-27 16:55:22 +00:00
KebsCS 906f75c13a
Add ghost list sorting 2024-08-27 18:34:26 +02:00
Dennis Felsing 7c8f9b43ac
Merge pull request #8826 from bencie/announce-unfinishedmap-voter
Send result of `random_unfinished_map` to all clients
2024-08-27 15:25:12 +00:00
bencie c4ff37d9d9 Send result of random_unfinished_map to all clients 2024-08-27 16:37:15 +02:00
Dennis Felsing 3a2cecb8b4
Merge pull request #8823 from furo321/auto-reload-local-ips
Run editor auto-reload on any local address, and not just localhost
2024-08-27 11:29:14 +00:00
furo 358cd20183 Run editor auto-reload on any local address, and not just localhost 2024-08-27 13:05:52 +02:00
Dennis Felsing 9d920b3575
Merge pull request #8703 from sjrc6/pr_slider
Adjust slider handle size
2024-08-27 09:20:18 +00:00
Tater 5fb059ad92 Adjust slider handle size 2024-08-27 11:02:12 +02:00
Dennis Felsing 4b8dcc3315
Merge pull request #8811 from furo321/add-map-votes-directories
Add support for directories with `add_map_votes`
2024-08-27 08:59:25 +00:00
Dennis Felsing b30b493ab8
Merge pull request #8819 from Anime-pdf/master
speedup demo with mouse scroll only if menu is active
2024-08-27 07:15:38 +00:00
Dennis Felsing 29f3323735
Merge pull request #8813 from furo321/hot-reload-maps
Add `hot_reload` to reload map while preserving state
2024-08-27 07:03:05 +00:00
Paul 7c62b457b7 speedup demo with mouse scroll only if menu is active 2024-08-27 02:38:08 +03:00
furo 8b27a6e852 Add hot_reload to reload map while preserving state 2024-08-27 01:19:40 +02:00
furo 8f6d5d453e Add support for directories with add_map_votes 2024-08-25 23:29:34 +02:00
DynamoFox a013372be4 Fix game launch on Wayland-only systems with SDL2.30+ 2024-08-01 16:36:44 +02:00
114 changed files with 4120 additions and 1256 deletions

View file

@ -2462,6 +2462,7 @@ if(CLIENT)
editor_trackers.cpp editor_trackers.cpp
editor_trackers.h editor_trackers.h
editor_ui.h editor_ui.h
enums.h
explanations.cpp explanations.cpp
layer_selector.cpp layer_selector.cpp
layer_selector.h layer_selector.h
@ -2500,8 +2501,13 @@ if(CLIENT)
mapitems/sound.cpp mapitems/sound.cpp
mapitems/sound.h mapitems/sound.h
popups.cpp popups.cpp
prompt.cpp
prompt.h
proof_mode.cpp proof_mode.cpp
proof_mode.h proof_mode.h
quick_action.h
quick_actions.cpp
quick_actions.h
smooth_value.cpp smooth_value.cpp
smooth_value.h smooth_value.h
tileart.cpp tileart.cpp
@ -2924,6 +2930,7 @@ if(GTEST_FOUND OR DOWNLOAD_GTEST)
compression.cpp compression.cpp
csv.cpp csv.cpp
datafile.cpp datafile.cpp
editor.cpp
fs.cpp fs.cpp
git_revision.cpp git_revision.cpp
hash.cpp hash.cpp

View file

@ -275,9 +275,6 @@ Player
Player country: Player country:
== ﺐﻋﻼﻟﺍ ﺪﻠﺑ: == ﺐﻋﻼﻟﺍ ﺪﻠﺑ:
Player options
== ﺐﻋﻼﻟﺍ ﺕﺍﺩﺍﺪﻋﺍ
Players Players
== ﻦﻴﺒﻋﻻ == ﻦﻴﺒﻋﻻ
@ -773,9 +770,6 @@ Skin prefix
Show HUD Show HUD
== ﻞﻜﺸﻟﺍ ﺭﺎﻬﻇﺍ == ﻞﻜﺸﻟﺍ ﺭﺎﻬﻇﺍ
Reload
== ﻞﻴﻤﺤﺘﻟﺍ ﺓﺩﺎﻋﺍ
9+ new mentions 9+ new mentions
== 9+ ﺓﺪﻳﺪﺟ ﺕﺍﺭﺎﻌﺷﺍ == 9+ ﺓﺪﻳﺪﺟ ﺕﺍﺭﺎﻌﺷﺍ
@ -1026,9 +1020,6 @@ Debug mode enabled. Press Ctrl+Shift+D to disable debug mode.
Existing Player Existing Player
== ﺩﻮﺟﻮﻣ ﺐﻋﻼﻟﺍ == ﺩﻮﺟﻮﻣ ﺐﻋﻼﻟﺍ
Your nickname '%s' is already used (%d points). Do you still want to use it?
==
Checking for existing player with your name Checking for existing player with your name
== ﻚﻤﺳﺎﺑ ﺐﻋﻻ ﺩﻮﺟﻭ ﻦﻣ ﻖﻘﺤﺘﻟﺍ == ﻚﻤﺳﺎﺑ ﺐﻋﻻ ﺩﻮﺟﻭ ﻦﻣ ﻖﻘﺤﺘﻟﺍ
@ -1092,9 +1083,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1225,6 +1225,9 @@ Render complete
Are you sure that you want to restart? Are you sure that you want to restart?
== ==
Your nickname '%s' is already used (%d points). Do you still want to use it?
==
There's an unsaved map in the editor, you might want to save it. There's an unsaved map in the editor, you might want to save it.
== ==
@ -1478,6 +1481,12 @@ Unable to delete the demo '%s'
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1645,7 +1654,7 @@ Show local time always
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1819,6 +1828,39 @@ Unregister protocol and file extensions
DDNet %s is available: DDNet %s is available:
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Extras Extras
== ==

View file

@ -4,6 +4,7 @@
#modified by: #modified by:
# Gokturk 2024-06-09 12:00:50 # Gokturk 2024-06-09 12:00:50
# Gokturk 2024-07-17 02:24:00 # Gokturk 2024-07-17 02:24:00
# Gokturk 2024-08-29 23:39:00
##### /authors ##### ##### /authors #####
##### translated strings ##### ##### translated strings #####
@ -274,9 +275,6 @@ Play background music
Player Player
== Oyunçu == Oyunçu
Player options
== Oyunçu parametrlər
Players Players
== Oyunçular == Oyunçular
@ -652,9 +650,6 @@ Connecting dummy
Connect Dummy Connect Dummy
== Dummy daxil ol == Dummy daxil ol
Reload
== Yenilə
Deactivate Deactivate
== Qeyri-aktiv etmək == Qeyri-aktiv etmək
@ -1450,9 +1445,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== DDRace interfeysi == DDRace interfeysi
Show client IDs in scoreboard
== Xal lövhəsində klient ID-lərini göstərin
Show DDRace HUD Show DDRace HUD
== DDRace interfeysi göstər == DDRace interfeysi göstər
@ -1870,3 +1862,54 @@ Team %d (%d/%d)
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== https://wiki.ddnet.org/wiki/Mapping == https://wiki.ddnet.org/wiki/Mapping
Could not resolve connect address '%s'. See local console for details.
== '%s' Link ünvanını həll etmək mümkün olmadı. Ətraflı məlumat üçün yerli konsola baxın
Connect address error
== Link ünvanı xətası
Could not connect dummy
== Dummy qoşula bilmədi
Dummy is not allowed on this server
== Bu serverdə dummy icazə verilmir
Please wait…
== Zəhmət olmasa gözləyin…
Show client IDs (scoreboard, chat, spectator)
== Klient ID'sini göstərin (aparıcı lövhə, söhbət, izləyici)
Normal:
== Normal:
Team:
== Komanda:
Dummy settings
== Dummy parametrlər
Toggle to edit your dummy settings
== Dummy parametrlərini redaktə etmək üçün açın
Randomize
== Təsadüfi
Are you sure that you want to delete '%s'?
== '%s' Bunu silmək istədiyinizə əminsiniz?
Delete skin
== Skini silin
Basic
== Sadə
Custom
== Xüsusi
Unable to delete skin
== Skin silinə bilməz
Customize
== Fərdiləşdirmək

View file

@ -282,9 +282,6 @@ Player
Player country: Player country:
== Краіна гульца: == Краіна гульца:
Player options
== Опцыі гульца
Players Players
== Гульцы == Гульцы
@ -868,9 +865,6 @@ Loading ghost files
Time Time
== Час == Час
Reload
== Перазагрузіць
Deactivate Deactivate
== Выключыць == Выключыць
@ -1167,9 +1161,6 @@ Show votes window after voting
DDRace HUD DDRace HUD
== DDRace HUD == DDRace HUD
Show client IDs in scoreboard
== Паказваць ID кліента ў табло ачкоў
Show DDRace HUD Show DDRace HUD
== Паказваць DDRace HUD == Паказваць DDRace HUD
@ -1706,6 +1697,7 @@ Regular background color
Entities background color Entities background color
== Колер фону энтыты == Колер фону энтыты
[Graphics error]
An error during command recording occurred. Try to update your GPU drivers. An error during command recording occurred. Try to update your GPU drivers.
== Адбылася памылка падчас выканання каманды запісу. Паспрабуйце абнавіць драйверы відэакарты. == Адбылася памылка падчас выканання каманды запісу. Паспрабуйце абнавіць драйверы відэакарты.
@ -1874,3 +1866,54 @@ Team %d (%d/%d)
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== https://wiki.ddnet.org/wiki/Mapping == https://wiki.ddnet.org/wiki/Mapping
Could not resolve connect address '%s'. See local console for details.
==
Connect address error
==
Could not connect dummy
==
Dummy is not allowed on this server
==
Please wait…
==
Show client IDs (scoreboard, chat, spectator)
==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==

View file

@ -280,9 +280,6 @@ Player
Player country: Player country:
== Država == Država
Player options
== Postavke igrača
Players Players
== Igrači == Igrači
@ -651,9 +648,6 @@ Connecting dummy
Connect Dummy Connect Dummy
== Konektuj dummy-a == Konektuj dummy-a
Reload
== Reload
Deactivate Deactivate
== Deaktiviraj == Deaktiviraj
@ -951,9 +945,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1385,6 +1388,12 @@ Unable to delete the demo '%s'
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1591,7 +1600,7 @@ Show local time always
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1777,6 +1786,39 @@ Chat command (e.g. showall 1)
Unregister protocol and file extensions Unregister protocol and file extensions
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Emoticons Emoticons
== ==

View file

@ -34,6 +34,7 @@
# Rafael Fontenelle 2024-04-30 14:52:00 # Rafael Fontenelle 2024-04-30 14:52:00
# Rafael Fontenelle 2024-06-11 22:43:00 # Rafael Fontenelle 2024-06-11 22:43:00
# Rafael Fontenelle 2024-07-17 12:04:00 # Rafael Fontenelle 2024-07-17 12:04:00
# Rafael Fontenelle 2024-09-02 16:02:00
##### /authors ##### ##### /authors #####
##### translated strings ##### ##### translated strings #####
@ -311,9 +312,6 @@ Player
Player country: Player country:
== País do jogador: == País do jogador:
Player options
== Opções do jogador
Players Players
== Jogadores == Jogadores
@ -818,9 +816,6 @@ Show DDNet map finishes in server browser
transmits your player name to info.ddnet.org transmits your player name to info.ddnet.org
== transmite seu nome de jogador para info.ddnet.org == transmite seu nome de jogador para info.ddnet.org
Reload
== Recarregar
Time Time
== Tempo == Tempo
@ -1376,9 +1371,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== HUD do DDRace == HUD do DDRace
Show client IDs in scoreboard
== Mostrar IDs de cliente no placar
Show DDRace HUD Show DDRace HUD
== Mostrar HUD do DDRace == Mostrar HUD do DDRace
@ -1901,3 +1893,54 @@ Team %d (%d/%d)
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== 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

View file

@ -277,9 +277,6 @@ Player
Player country: Player country:
== Страна на играча: == Страна на играча:
Player options
== Настройки на Играча
Players Players
== Играчи == Играчи
@ -546,9 +543,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1094,6 +1100,12 @@ Are you sure that you want to disconnect?
Connect Dummy Connect Dummy
== ==
Dummy is not allowed on this server
==
Please wait…
==
Connecting dummy Connecting dummy
== ==
@ -1441,7 +1453,7 @@ Show votes window after voting
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1693,9 +1705,6 @@ Regular background color
Entities background color Entities background color
== ==
Reload
==
Use current map as background Use current map as background
== ==
@ -1732,6 +1741,39 @@ No updates available
Check now Check now
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Emoticons Emoticons
== ==

View file

@ -275,9 +275,6 @@ Player
Player country: Player country:
== País del jugador == País del jugador
Player options
== Opcions del jugador
Players Players
== Jugadors == Jugadors
@ -782,9 +779,6 @@ Show DDNet map finishes in server browser
transmits your player name to info.ddnet.org transmits your player name to info.ddnet.org
== retransmet el teu nom de jugar a info.ddnet.org == retransmet el teu nom de jugar a info.ddnet.org
Reload
== Recarga
Successfully saved the replay! Successfully saved the replay!
== S'ha guardat la repetició correctament! == S'ha guardat la repetició correctament!
@ -1197,9 +1191,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1541,6 +1544,12 @@ Unable to delete the demo '%s'
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1672,7 +1681,7 @@ Show local time always
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1828,6 +1837,39 @@ Entities background color
Unregister protocol and file extensions Unregister protocol and file extensions
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Extras Extras
== ==

View file

@ -277,9 +277,6 @@ Player
Player country: Player country:
== Çĕршыв: == Çĕршыв:
Player options
== Опцисем
Players Players
== Çынсем == Çынсем
@ -549,9 +546,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1097,6 +1103,12 @@ Are you sure that you want to disconnect?
Connect Dummy Connect Dummy
== ==
Dummy is not allowed on this server
==
Please wait…
==
Connecting dummy Connecting dummy
== ==
@ -1441,7 +1453,7 @@ Show votes window after voting
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1693,9 +1705,6 @@ Regular background color
Entities background color Entities background color
== ==
Reload
==
Use current map as background Use current map as background
== ==
@ -1732,6 +1741,39 @@ No updates available
Check now Check now
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Emoticons Emoticons
== ==

View file

@ -6,7 +6,7 @@
# Petr 2011-04-02 23:02:33 # Petr 2011-04-02 23:02:33
# Medik & Petr 2011-07-02 19:37:05 # Medik & Petr 2011-07-02 19:37:05
# TeeWorlds-org 2011-07-15 00:34:19 # TeeWorlds-org 2011-07-15 00:34:19
# dobrykafe 2024-06-09 00:00:00 # dobrykafe 2024-08-30 00:00:00
##### /authors ##### ##### /authors #####
##### translated strings ##### ##### translated strings #####
@ -281,9 +281,6 @@ Player
Player country: Player country:
== Země hráčů: == Země hráčů:
Player options
== Možnosti hráčů
Players Players
== Hráči == Hráči
@ -646,9 +643,6 @@ Connecting dummy
Connect Dummy Connect Dummy
== Připojit dummyho == Připojit dummyho
Reload
== Znovu načíst
Deactivate Deactivate
== Deaktivovat == Deaktivovat
@ -1634,9 +1628,6 @@ Show local time always
DDRace HUD DDRace HUD
== DDRace HUD == DDRace HUD
Show client IDs in scoreboard
== Zobrazit ID klientů ve výsledkové tabulce
Show DDRace HUD Show DDRace HUD
== Zobrazit DDRace HUD == Zobrazit DDRace HUD
@ -1853,26 +1844,77 @@ Tee
Show only chat messages from team members Show only chat messages from team members
== Zobrazit pouze zprávy od členů týmu == Zobrazit pouze zprávy od členů týmu
Could not resolve connect address '%s'. See local console for details.
== Nelze získat adresu k připojení '%s'. Podrobnosti najdete v místní konzoli.
Connect address error
== Chyba v adrese k připojení
Could not connect dummy
== Nepodařilo se připojit dummyho
[Spectating] [Spectating]
Following %s Following %s
== == Sledujete %s
Example of usage Example of usage
== == Příklad použití
Dummy is not allowed on this server
== Dummy není na tomto serveru povolen
Please wait…
== Čekejte prosím…
Show client IDs (scoreboard, chat, spectator)
== Zobrazit ID klientů (výsledková tabulka, chat, divák)
Normal:
== Normální:
Team:
== Tým:
Dummy settings
== Nastavení dummyho
Toggle to edit your dummy settings
== Přepnutím upravíte nastavení dummyho
Randomize
== Randomizovat
Are you sure that you want to delete '%s'?
== Jste si jisti, že chcete smazat '%s'?
Delete skin
== Odstranit skin
Basic
== Základní
Custom
== Vlastní
Unable to delete skin
== Nelze odstranit skin
Customize
== Přizpůsobit
Round %d/%d Round %d/%d
== == Kolo %d/%d
[Spectators] [Spectators]
%d others… %d others…
== == %d dalších…
[Team and size] [Team and size]
%d\n(%d/%d) %d\n(%d/%d)
== == %d\n(%d/%d)
Team %d (%d/%d) Team %d (%d/%d)
== == Tým %d (%d/%d)
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== == https://wiki.ddnet.org/wiki/Mapping

View file

@ -278,9 +278,6 @@ Player
Player country: Player country:
== Spillernes land: == Spillernes land:
Player options
== Spillermuligheder
Players Players
== Spillere == Spillere
@ -676,9 +673,6 @@ Kill
Pause Pause
== Pause == Pause
Reload
== Genindlæs
Deactivate Deactivate
== Deaktiver == Deaktiver
@ -1100,9 +1094,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1486,6 +1489,12 @@ Unable to delete the demo '%s'
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1653,7 +1662,7 @@ Show local time always
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1824,6 +1833,39 @@ Chat command (e.g. showall 1)
Unregister protocol and file extensions Unregister protocol and file extensions
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Extras Extras
== ==

View file

@ -290,9 +290,6 @@ Player
Player country: Player country:
== Speler land: == Speler land:
Player options
== Speler opties
Players Players
== Spelers == Spelers
@ -663,9 +660,6 @@ Connecting dummy
Connect Dummy Connect Dummy
== Verbind Dummy == Verbind Dummy
Reload
== Herladen
Deactivate Deactivate
== Deactiveren == Deactiveren
@ -1243,9 +1237,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1584,6 +1587,12 @@ Unable to delete the demo '%s'
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1709,7 +1718,7 @@ Show local time always
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1850,6 +1859,39 @@ Entities background color
Unregister protocol and file extensions Unregister protocol and file extensions
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Extras Extras
== ==

View file

@ -300,9 +300,6 @@ Kill
Pause Pause
== Paŭzi == Paŭzi
Player options
== Ludantaj agordoj
Player Player
== Ludanto == Ludanto
@ -565,9 +562,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1053,6 +1059,12 @@ Unable to delete the demo '%s'
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Stop record Stop record
== ==
@ -1481,7 +1493,7 @@ Show votes window after voting
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1724,9 +1736,6 @@ Regular background color
Entities background color Entities background color
== ==
Reload
==
Use current map as background Use current map as background
== ==
@ -1751,6 +1760,39 @@ DDNet Client updated!
No updates available No updates available
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Emoticons Emoticons
== ==

View file

@ -281,9 +281,6 @@ Player
Player country: Player country:
== Mängija maa: == Mängija maa:
Player options
== Mängija sätted
Players Players
== Mängijad == Mängijad
@ -710,9 +707,6 @@ Kill
Pause Pause
== Paus == Paus
Reload
== Lae uuesti
Deactivate Deactivate
== Deaktiveeri == Deaktiveeri
@ -1605,9 +1599,6 @@ Show local time always
DDRace HUD DDRace HUD
== DDRace HUD == DDRace HUD
Show client IDs in scoreboard
== Näita kliendi ID-eid tulemustabelis
Show DDRace HUD Show DDRace HUD
== Näita DDRace HUD-i == Näita DDRace HUD-i
@ -1872,5 +1863,56 @@ Round %d/%d
Team %d (%d/%d) Team %d (%d/%d)
== Tiim %d (%d/%d) == Tiim %d (%d/%d)
Could not resolve connect address '%s'. See local console for details.
==
Connect address error
==
Could not connect dummy
==
Dummy is not allowed on this server
==
Please wait…
==
Show client IDs (scoreboard, chat, spectator)
==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== ==

View file

@ -280,9 +280,6 @@ Player
Player country: Player country:
== Pelaajan maa: == Pelaajan maa:
Player options
== Pelaajavalinnat
Players Players
== Pelaajat == Pelaajat
@ -709,9 +706,6 @@ Kill
Pause Pause
== Pysäytä == Pysäytä
Reload
== Päivitä
Deactivate Deactivate
== Deaktivoi == Deaktivoi
@ -1634,9 +1628,6 @@ Show local time always
DDRace HUD DDRace HUD
== DDrace-HUD == DDrace-HUD
Show client IDs in scoreboard
== Näytä asiakas-ID:t pistetaulukossa
Show DDRace HUD Show DDRace HUD
== Näytä DDRace-HUD == Näytä DDRace-HUD
@ -1832,6 +1823,15 @@ Loading sound files
Moved ingame Moved ingame
== Liikui pelissä == Liikui pelissä
Could not resolve connect address '%s'. See local console for details.
==
Connect address error
==
Could not connect dummy
==
[Spectating] [Spectating]
Following %s Following %s
== ==
@ -1842,15 +1842,57 @@ Following %s
Example of usage Example of usage
== ==
Dummy is not allowed on this server
==
Please wait…
==
Converse Converse
== ==
Tee Tee
== ==
Show client IDs (scoreboard, chat, spectator)
==
Show only chat messages from team members Show only chat messages from team members
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Round %d/%d Round %d/%d
== ==

View file

@ -302,9 +302,6 @@ Player
Player country: Player country:
== Origine des Tees: == Origine des Tees:
Player options
== Options des joueurs
Players Players
== Joueurs == Joueurs
@ -610,9 +607,6 @@ AntiPing: predict other players
Are you sure that you want to disconnect your dummy? Are you sure that you want to disconnect your dummy?
== Êtes vous sûrs de vouloir déconnecter votre dummy ? == Êtes vous sûrs de vouloir déconnecter votre dummy ?
Reload
== Recharger
Server best: Server best:
== Meilleur score du serveur == Meilleur score du serveur
@ -1390,9 +1384,6 @@ Name Plate
Hook Collisions Hook Collisions
== Collisions du grappin == Collisions du grappin
Show client IDs in scoreboard
== Afficher les IDs des clients dans le tableau des scores
Hook collision line width Hook collision line width
== Largeur de la ligne de collision du grappin == Largeur de la ligne de collision du grappin
@ -1874,6 +1865,15 @@ Entities background color
Moved ingame Moved ingame
== Déplacé en jeu == Déplacé en jeu
Could not resolve connect address '%s'. See local console for details.
==
Connect address error
==
Could not connect dummy
==
[Spectating] [Spectating]
Following %s Following %s
== ==
@ -1881,6 +1881,48 @@ Following %s
Example of usage Example of usage
== ==
Dummy is not allowed on this server
==
Please wait…
==
Show client IDs (scoreboard, chat, spectator)
==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Round %d/%d Round %d/%d
== ==

View file

@ -280,9 +280,6 @@ Player
Player country: Player country:
== País do xogador == País do xogador
Player options
== Opcións de xogador
Players Players
== Xogadores == Xogadores
@ -652,9 +649,6 @@ Connecting dummy
Connect Dummy Connect Dummy
== Conectar Dummy == Conectar Dummy
Reload
== Recargar
Deactivate Deactivate
== Desactivar == Desactivar
@ -1358,9 +1352,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== HUD DDRace == HUD DDRace
Show client IDs in scoreboard
== Mostrar IDs de cliente na táboa de puntuación
Show DDRace HUD Show DDRace HUD
== Mostrar o HUD DDRace == Mostrar o HUD DDRace
@ -1571,6 +1562,15 @@ Relative
Absolute Absolute
== Absoluto == Absoluto
Could not resolve connect address '%s'. See local console for details.
==
Connect address error
==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1767,6 +1767,12 @@ Delete folder
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1797,6 +1803,9 @@ Info Messages
Show local time always Show local time always
== ==
Show client IDs (scoreboard, chat, spectator)
==
Always show chat Always show chat
== ==
@ -1851,6 +1860,39 @@ Regular background color
Entities background color Entities background color
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Round %d/%d Round %d/%d
== ==

View file

@ -299,9 +299,6 @@ Player
Player country: Player country:
== Spielerland: == Spielerland:
Player options
== Spieleroptionen
Players Players
== Spieler == Spieler
@ -365,9 +362,6 @@ Shotgun
Show chat Show chat
== Chat anzeigen == Chat anzeigen
Show client IDs in scoreboard
== Client-IDs in der Punktetafel anzeigen
Show DDRace HUD Show DDRace HUD
== DDRace-HUD anzeigen == DDRace-HUD anzeigen
@ -815,9 +809,6 @@ Skin prefix
Show HUD Show HUD
== HUD zeigen == HUD zeigen
Reload
== Neu laden
9+ new mentions 9+ new mentions
== 9+ Erwähnungen == 9+ Erwähnungen
@ -1887,5 +1878,56 @@ Round %d/%d
Team %d (%d/%d) Team %d (%d/%d)
== Team %d (%d/%d) == Team %d (%d/%d)
Could not resolve connect address '%s'. See local console for details.
== Konnte Verbindungsadresse '%s' nicht auflösen, siehe lokale Konsole für Details.
Connect address error
== Fehler in Verbindungsadresse
Could not connect dummy
== Konnte Dummy nicht verbinden
Dummy is not allowed on this server
== Dummy ist auf diesem Server nicht erlaubt
Please wait…
== Bitte warten…
Show client IDs (scoreboard, chat, spectator)
== Client-IDs anzeigen (Punktetafel, Chat, Beobachter)
Normal:
== Normal:
Team:
== Team:
Dummy settings
== Dummy-Einstellungen
Toggle to edit your dummy settings
== Zu Dummy-Einstellungen wechseln
Randomize
== Randomisieren
Are you sure that you want to delete '%s'?
== Bist du sicher dass du '%s' löschen willst?
Delete skin
== Skin löschen
Basic
== Einfach
Custom
== Individuell
Unable to delete skin
== Kann Skin nicht löschen
Customize
== Anpassen
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== ==

View file

@ -280,9 +280,6 @@ Player
Player country: Player country:
== Χώρα παίκτη: == Χώρα παίκτη:
Player options
== Επιλογές παίκτη
Players Players
== Παίκτες == Παίκτες
@ -555,9 +552,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1100,6 +1106,12 @@ Are you sure that you want to disconnect?
Connect Dummy Connect Dummy
== ==
Dummy is not allowed on this server
==
Please wait…
==
Connecting dummy Connecting dummy
== ==
@ -1444,7 +1456,7 @@ Show votes window after voting
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1693,9 +1705,6 @@ Regular background color
Entities background color Entities background color
== ==
Reload
==
Use current map as background Use current map as background
== ==
@ -1732,6 +1741,39 @@ No updates available
Check now Check now
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Emoticons Emoticons
== ==

View file

@ -270,9 +270,6 @@ Pistol
Player Player
== Játékos == Játékos
Player options
== Játékosok kezelése
Players Players
== Játékosok == Játékosok
@ -789,9 +786,6 @@ Show DDNet map finishes in server browser
transmits your player name to info.ddnet.org transmits your player name to info.ddnet.org
== továbbítja a játékosnevedet az info.ddnet.org oldalra == továbbítja a játékosnevedet az info.ddnet.org oldalra
Reload
== Újratöltés
Hammerfly dummy Hammerfly dummy
== Másolat kalapácsolás == Másolat kalapácsolás
@ -1060,9 +1054,6 @@ Auto
Replay Replay
== Visszajátszás == Visszajátszás
The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs.
==
Getting server list from master server Getting server list from master server
== Szerverlista lekérése a fő szerverekről == Szerverlista lekérése a fő szerverekről
@ -1338,9 +1329,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== DDRace HUD == DDRace HUD
Show client IDs in scoreboard
== Mutassa a kliensek ID-ét a pontszámtáblán
Show DDRace HUD Show DDRace HUD
== Mutassa a DDRace HUD-ot (Speciális HUD) == Mutassa a DDRace HUD-ot (Speciális HUD)
@ -1551,6 +1539,15 @@ Unregister protocol and file extensions
Open the directory to add custom assets Open the directory to add custom assets
== Megnyitni az egyedi Képek helyét == Megnyitni az egyedi Képek helyét
Could not resolve connect address '%s'. See local console for details.
==
Connect address error
==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1563,6 +1560,9 @@ Saving settings to '%s' failed
Error saving settings Error saving settings
== ==
The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs.
==
Loading demo file from storage Loading demo file from storage
== ==
@ -1772,6 +1772,12 @@ Delete folder
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1802,6 +1808,9 @@ Info Messages
Show local time always Show local time always
== ==
Show client IDs (scoreboard, chat, spectator)
==
Always show chat Always show chat
== ==
@ -1853,6 +1862,39 @@ Regular background color
Entities background color Entities background color
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Round %d/%d Round %d/%d
== ==

View file

@ -284,9 +284,6 @@ Player
Player country: Player country:
== Filtra per paese: == Filtra per paese:
Player options
== Opzioni giocatore
Players Players
== Giocatori == Giocatori
@ -709,9 +706,6 @@ Kill
Pause Pause
== Pausa == Pausa
Reload
== Ricarica
Deactivate Deactivate
== Disattiva == Disattiva
@ -1273,9 +1267,6 @@ Hook Collisions
Show health, shields and ammo Show health, shields and ammo
== Mostra vita, scudi e munizioni == Mostra vita, scudi e munizioni
Show client IDs in scoreboard
== Mostra gli ID clienti nella scoreboard
Show DDRace HUD Show DDRace HUD
== Mostra l'HUD DDRace == Mostra l'HUD DDRace
@ -1411,9 +1402,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1680,6 +1680,12 @@ Unable to delete the demo '%s'
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1783,6 +1789,9 @@ Show local time always
DDRace HUD DDRace HUD
== ==
Show client IDs (scoreboard, chat, spectator)
==
Always show chat Always show chat
== ==
@ -1873,6 +1882,39 @@ Regular background color
Entities background color Entities background color
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Extras Extras
== ==

View file

@ -274,9 +274,6 @@ Player
Player country: Player country:
== 所在地: == 所在地:
Player options
== プレイヤー設定
Players Players
== プレーヤー == プレーヤー
@ -684,9 +681,6 @@ Kill
Pause Pause
== 観察 == 観察
Reload
== 更新
Deactivate Deactivate
== 無効にする == 無効にする
@ -1135,9 +1129,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1512,6 +1515,12 @@ Unable to delete the demo '%s'
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1664,7 +1673,7 @@ Show local time always
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1829,6 +1838,39 @@ Chat command (e.g. showall 1)
Unregister protocol and file extensions Unregister protocol and file extensions
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Extras Extras
== ==

View file

@ -280,9 +280,6 @@ Pistol
Player Player
== 플레이어 == 플레이어
Player options
== 플레이어 옵션
Players Players
== 플레이어 == 플레이어
@ -534,9 +531,6 @@ Successfully saved the replay!
Replay feature is disabled! Replay feature is disabled!
== 리플레이 기능을 비활성화했습니다! == 리플레이 기능을 비활성화했습니다!
The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs.
==
Warning Warning
== 주의 == 주의
@ -702,9 +696,6 @@ Pause
Time Time
== 시간 == 시간
Reload
== 새로고침
Deactivate Deactivate
== 비활성화 == 비활성화
@ -1359,9 +1350,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== DDRace HUD == DDRace HUD
Show client IDs in scoreboard
== 점수판에 클라이언트 ID 표시
Show DDRace HUD Show DDRace HUD
== DDRace HUD 표시 == DDRace HUD 표시
@ -1563,6 +1551,15 @@ Copy info
Create a random skin Create a random skin
== 무작위 스킨 생성 == 무작위 스킨 생성
Could not resolve connect address '%s'. See local console for details.
==
Connect address error
==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1575,6 +1572,9 @@ Saving settings to '%s' failed
Error saving settings Error saving settings
== ==
The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs.
==
Loading demo file from storage Loading demo file from storage
== ==
@ -1784,6 +1784,12 @@ Delete folder
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1814,6 +1820,9 @@ Info Messages
Show local time always Show local time always
== ==
Show client IDs (scoreboard, chat, spectator)
==
Always show chat Always show chat
== ==
@ -1865,6 +1874,39 @@ Regular background color
Entities background color Entities background color
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Round %d/%d Round %d/%d
== ==

View file

@ -309,9 +309,6 @@ Player
Player country: Player country:
== Өлкөсү: == Өлкөсү:
Player options
== Оюнчу опциялары
Players Players
== Оюнчулар == Оюнчулар
@ -546,9 +543,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1091,6 +1097,12 @@ Are you sure that you want to disconnect?
Connect Dummy Connect Dummy
== ==
Dummy is not allowed on this server
==
Please wait…
==
Connecting dummy Connecting dummy
== ==
@ -1435,7 +1447,7 @@ Show votes window after voting
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1684,9 +1696,6 @@ Regular background color
Entities background color Entities background color
== ==
Reload
==
Use current map as background Use current map as background
== ==
@ -1723,6 +1732,39 @@ No updates available
Check now Check now
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Emoticons Emoticons
== ==

View file

@ -279,9 +279,6 @@ Player
Player country: Player country:
== Spillerland: == Spillerland:
Player options
== Spillerinstillinger
Players Players
== Spillere == Spillere
@ -653,9 +650,6 @@ Connecting dummy
Connect Dummy Connect Dummy
== Koble til dummy == Koble til dummy
Reload
== Last på nytt
Deactivate Deactivate
== Deaktiver == Deaktiver
@ -1101,9 +1095,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1487,6 +1490,12 @@ Unable to delete the demo '%s'
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1654,7 +1663,7 @@ Show local time always
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1825,6 +1834,39 @@ Chat command (e.g. showall 1)
Unregister protocol and file extensions Unregister protocol and file extensions
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Extras Extras
== ==

View file

@ -519,9 +519,6 @@ Kill
Pause Pause
== ﺚﮑﻣ ﺖﻟﺎﺣ == ﺚﮑﻣ ﺖﻟﺎﺣ
Player options
== ﻦﮑﯾﺯﺎﺑ ﺕﺎﻤﯿﻈﻨﺗ
Player Player
== ﻦﮑﯾﺯﺎﺑ == ﻦﮑﯾﺯﺎﺑ
@ -543,9 +540,6 @@ Vote command:
Time Time
== ﻥﺎﻣﺯ == ﻥﺎﻣﺯ
Reload
== ﻩﺭﺎﺑﻭﺩ ﯼﺮﯿﮔﺭﺎﺑ
Deactivate Deactivate
== ﻥﺩﺮﮐ ﻝﺎﻌﻓﺮﯿﻏ == ﻥﺩﺮﮐ ﻝﺎﻌﻓﺮﯿﻏ
@ -1225,9 +1219,6 @@ Show votes window after voting
DDRace HUD DDRace HUD
== DDRace HUD == DDRace HUD
Show client IDs in scoreboard
== ﺯﺎﯿﺘﻣﺍ ﯼﻮﻠﺑﺎﺗ ﺭﺩ ﺖﻨﯾﻼﮐ ID ﺶﯾﺎﻤﻧ
Show DDRace HUD Show DDRace HUD
== DDRace HUD ﺶﯾﺎﻤﻧ == DDRace HUD ﺶﯾﺎﻤﻧ
@ -1518,9 +1509,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1754,6 +1754,12 @@ Delete folder
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1787,6 +1793,9 @@ Info Messages
Show local time always Show local time always
== ==
Show client IDs (scoreboard, chat, spectator)
==
Always show chat Always show chat
== ==
@ -1841,6 +1850,39 @@ Entities background color
Unregister protocol and file extensions Unregister protocol and file extensions
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Round %d/%d Round %d/%d
== ==

View file

@ -282,9 +282,6 @@ Player
Player country: Player country:
== Narodowość: == Narodowość:
Player options
== Opcje gracza
Players Players
== Gracze == Gracze
@ -683,9 +680,6 @@ Enable replays
Automatically create statboard csv Automatically create statboard csv
== Automatycznie stwórz plik csv z tabelą wyników == Automatycznie stwórz plik csv z tabelą wyników
Reload
== Przeładuj
Replay feature is disabled! Replay feature is disabled!
== Funkcja odtwarzania jest wyłączona! == Funkcja odtwarzania jest wyłączona!
@ -1639,9 +1633,6 @@ Show health, shields and ammo
Show local time always Show local time always
== Pokazuj czas lokalny == Pokazuj czas lokalny
Show client IDs in scoreboard
== Pokazuj ID klienta w tabeli wyników
Show DDRace HUD Show DDRace HUD
== Pokazuj HUD DDRace == Pokazuj HUD DDRace
@ -1808,6 +1799,15 @@ Submitting the render commands failed. Try to update your GPU drivers.
Failed to swap framebuffers. Try to update your GPU drivers. Failed to swap framebuffers. Try to update your GPU drivers.
== ==
Could not resolve connect address '%s'. See local console for details.
==
Connect address error
==
Could not connect dummy
==
Match %d of %d Match %d of %d
== ==
@ -1840,6 +1840,12 @@ Folder Link
map not included map not included
== ==
Dummy is not allowed on this server
==
Please wait…
==
Aim bind Aim bind
== ==
@ -1855,9 +1861,45 @@ Tee
DDRace HUD DDRace HUD
== ==
Show client IDs (scoreboard, chat, spectator)
==
Show only chat messages from team members Show only chat messages from team members
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Round %d/%d Round %d/%d
== ==

View file

@ -290,9 +290,6 @@ Player
Player country: Player country:
== País do jogador == País do jogador
Player options
== Opções do jogador
Players Players
== Jogadores == Jogadores
@ -773,9 +770,6 @@ Connect Dummy
Time Time
== Tempo == Tempo
Reload
== Recarregar
Deactivate Deactivate
== Desativar == Desativar
@ -890,9 +884,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1330,6 +1333,12 @@ Unable to delete the demo '%s'
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Kill Kill
== ==
@ -1563,7 +1572,7 @@ Show local time always
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1767,6 +1776,39 @@ Chat command (e.g. showall 1)
Unregister protocol and file extensions Unregister protocol and file extensions
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Emoticons Emoticons
== ==

View file

@ -286,9 +286,6 @@ Player
Player country: Player country:
== Țara jucătorului: == Țara jucătorului:
Player options
== Opțiuni jucător
Players Players
== Jucători == Jucători
@ -561,9 +558,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1106,6 +1112,12 @@ Are you sure that you want to disconnect?
Connect Dummy Connect Dummy
== ==
Dummy is not allowed on this server
==
Please wait…
==
Connecting dummy Connecting dummy
== ==
@ -1450,7 +1462,7 @@ Show votes window after voting
DDRace HUD DDRace HUD
== ==
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== ==
Show DDRace HUD Show DDRace HUD
@ -1699,9 +1711,6 @@ Regular background color
Entities background color Entities background color
== ==
Reload
==
Use current map as background Use current map as background
== ==
@ -1738,6 +1747,39 @@ No updates available
Check now Check now
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Emoticons Emoticons
== ==

View file

@ -294,9 +294,6 @@ Player
Player country: Player country:
== Страна игрока: == Страна игрока:
Player options
== Опции игрока
Players Players
== Игроки == Игроки
@ -634,7 +631,7 @@ Loading DDNet Client
== Загрузка DDNet Client == Загрузка DDNet Client
Normal message Normal message
== Обычное с. == Обычное
Connecting dummy Connecting dummy
== Подключение дамми == Подключение дамми
@ -646,10 +643,10 @@ Save ghost
== Сохранять тень == Сохранять тень
DDNet Client updated! DDNet Client updated!
== DDNet Client обновлён! == DDNet клиент обновлён!
Highlighted message Highlighted message
== Выделенное с. == Выделенное
Demo Demo
== Демо == Демо
@ -727,7 +724,7 @@ Downloading %s:
== Скачивание %s: == Скачивание %s:
Update failed! Check log… Update failed! Check log…
== Ошибка. Проверьте логи == Не удалось обновиться! Подробности в логах
Restart Restart
== Рестарт == Рестарт
@ -750,9 +747,6 @@ Fetch Info
Connect Dummy Connect Dummy
== Подключить дамми == Подключить дамми
Reload
== Перезагрузить
Deactivate Deactivate
== Выключить == Выключить
@ -850,7 +844,7 @@ DDNet
== DDNet == DDNet
Friend message Friend message
== Дружеское с. == Дружеское
Save the best demo of each race Save the best demo of each race
== Сохранять лучшее демо каждой карты == Сохранять лучшее демо каждой карты
@ -922,10 +916,10 @@ Ratio
== Соотношение == Соотношение
Net Net
== Сеть == Сальдо
FPM FPM
== FPM == У/мин
Spree Spree
== Серия == Серия
@ -946,7 +940,7 @@ Grabs
== 9+ упоминаний == 9+ упоминаний
Client message Client message
== Клиентское с. == Клиентское
Warning Warning
== Предупреждение == Предупреждение
@ -979,7 +973,7 @@ Run server
== Запустить сервер == Запустить сервер
Server executable not found, can't run server Server executable not found, can't run server
== Файл сервера не найден, невозможно запустить == Файл сервера не найден, не удалось запустить сервер
Editor Editor
== Редактор == Редактор
@ -1362,9 +1356,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== DDRace HUD == DDRace HUD
Show client IDs in scoreboard
== Показывать ид. игроков
Show DDRace HUD Show DDRace HUD
== Показывать DDRace HUD == Показывать DDRace HUD
@ -1887,3 +1878,54 @@ Team %d (%d/%d)
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== https://wiki.ddnet.org/wiki/Mapping/ru == https://wiki.ddnet.org/wiki/Mapping/ru
Could not resolve connect address '%s'. See local console for details.
== Не удалось определить адрес подключения '%s'. Подробности в локальной консоли.
Connect address error
== Ошибка адреса подключения
Could not connect dummy
== Невозможно подключить дамми
Dummy is not allowed on this server
== Дамми не разрешен на этом сервере
Please wait…
== Пожалуйста, подождите…
Show client IDs (scoreboard, chat, spectator)
== Показывать ID клиента (табло, чат, наблюдатель)
Normal:
== Обычный:
Team:
== В команде:
Dummy settings
== Настройки дамми
Toggle to edit your dummy settings
== Нажмите, чтобы изменить настройки дамми
Randomize
== Случайный
Are you sure that you want to delete '%s'?
== Вы уверены, что хотите удалить '%s'?
Delete skin
== Удалить скин
Basic
== Пресеты
Custom
== Кастомизация
Unable to delete skin
== Невозможно удалить скин
Customize
== Кастомизировать

View file

@ -281,9 +281,6 @@ Player
Player country: Player country:
== Država igrača == Država igrača
Player options
== Podešavanja igrača
Players Players
== Igrači == Igrači
@ -685,9 +682,6 @@ Kill
Pause Pause
== Pauza == Pauza
Reload
== Osveži
Deactivate Deactivate
== Deaktiviraj == Deaktiviraj
@ -1264,9 +1258,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== DDRace Prikaz == DDRace Prikaz
Show client IDs in scoreboard
== Prikaži identitete na tabli rezultata
Show DDRace HUD Show DDRace HUD
== Prikaži DDRace Prikaz == Prikaži DDRace Prikaz
@ -1649,6 +1640,15 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1785,6 +1785,12 @@ Netversion
map not included map not included
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1809,6 +1815,9 @@ Info Messages
Show local time always Show local time always
== ==
Show client IDs (scoreboard, chat, spectator)
==
Always show chat Always show chat
== ==
@ -1860,6 +1869,39 @@ Regular background color
Entities background color Entities background color
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Round %d/%d Round %d/%d
== ==

View file

@ -277,9 +277,6 @@ Player
Player country: Player country:
== Држава играча == Држава играча
Player options
== Подешавања играча
Players Players
== Играчи == Играчи
@ -684,9 +681,6 @@ Kill
Pause Pause
== Пауза == Пауза
Reload
== Освежи
Deactivate Deactivate
== Деактивирај == Деактивирај
@ -1263,9 +1257,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== DDRace Приказ == DDRace Приказ
Show client IDs in scoreboard
== Прикажи идентитете на табли резултата
Show DDRace HUD Show DDRace HUD
== Прикажи DDRace Приказ == Прикажи DDRace Приказ
@ -1422,9 +1413,18 @@ Could not initialize the given graphics backend, reverting to the default backen
Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card. 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.
==
Connect address error
==
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== ==
Could not connect dummy
==
Error playing demo Error playing demo
== ==
@ -1703,6 +1703,12 @@ Unable to delete the demo '%s'
Unable to delete the folder '%s'. Make sure it's empty first. Unable to delete the folder '%s'. Make sure it's empty first.
== ==
Dummy is not allowed on this server
==
Please wait…
==
Loading… Loading…
== ==
@ -1766,6 +1772,9 @@ Info Messages
Show local time always Show local time always
== ==
Show client IDs (scoreboard, chat, spectator)
==
Always show chat Always show chat
== ==
@ -1850,6 +1859,39 @@ Entities background color
Unregister protocol and file extensions Unregister protocol and file extensions
== ==
Normal:
==
Team:
==
Dummy settings
==
Toggle to edit your dummy settings
==
Randomize
==
Are you sure that you want to delete '%s'?
==
Delete skin
==
Basic
==
Custom
==
Unable to delete skin
==
Customize
==
Open the directory to add custom assets Open the directory to add custom assets
== ==

View file

@ -41,6 +41,7 @@
# 2024-04-21 By # 2024-04-21 By
# 2024-06-10 By # 2024-06-10 By
# 2024-07-21 By # 2024-07-21 By
# 2024-08-29 Pioooooo
##### /authors ##### ##### /authors #####
##### translated strings ##### ##### translated strings #####
@ -342,9 +343,6 @@ HUD
Player country: Player country:
== 玩家国家 / 地区: == 玩家国家 / 地区:
Player options
== 玩家选项
Players Players
== 玩家 == 玩家
@ -831,9 +829,6 @@ Show DDNet map finishes in server browser
transmits your player name to info.ddnet.org transmits your player name to info.ddnet.org
== 将会发送你的玩家昵称到 info.ddnet.org == 将会发送你的玩家昵称到 info.ddnet.org
Reload
== 刷新
Enable replays Enable replays
== 启用短时回放 == 启用短时回放
@ -1389,9 +1384,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== DDRace HUD == DDRace HUD
Show client IDs in scoreboard
== 显示客户端 IDs (计分板中)
Show DDRace HUD Show DDRace HUD
== 显示 DDRace HUD == 显示 DDRace HUD
@ -1523,7 +1515,7 @@ Are you sure that you want to reset the controls to their defaults?
[Graphics error] [Graphics error]
Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again.
== 初始化失败。请尝试打开配置目录中的设置文件settings_ddnet.cfg并将“gfx_backend OpenGL”修改为“gfx_backend Vulkan”若没有前者则可直接输入后者再重试。 == 初始化失败。请尝试打开配置目录中的设置文件settings_ddnet.cfg并将 “gfx_backend” 设置为 “gfx_backend OpenGL” 或 “gfx_backend Vulkan” 再重试。
[Graphics error] [Graphics error]
Out of VRAM. Try removing custom assets (skins, entities, etc.), especially those with high resolution. Out of VRAM. Try removing custom assets (skins, entities, etc.), especially those with high resolution.
@ -1547,7 +1539,7 @@ Failed to swap framebuffers. Try to update your GPU drivers.
[Graphics error] [Graphics error]
Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again.
== 未知错误。请尝试打开配置目录中的设置文件settings_ddnet.cfg并将“gfx_backend OpenGL”修改为“gfx_backend Vulkan”若没有前者则可直接输入后者再重试。 == 未知错误。请尝试打开配置目录中的设置文件settings_ddnet.cfg并将 “gfx_backend” 设置为 “gfx_backend OpenGL” 或 “gfx_backend Vulkan” 再重试。
[Graphics error] [Graphics error]
Could not initialize the given graphics backend, reverting to the default backend now. Could not initialize the given graphics backend, reverting to the default backend now.
@ -1575,7 +1567,7 @@ No controller found. Plug in a controller.
== 未检测到任何控制器。请尝试重新连接控制器。 == 未检测到任何控制器。请尝试重新连接控制器。
Unregister protocol and file extensions Unregister protocol and file extensions
== 未注册的协议与扩充文件 == 清除协议与文件类型关联
Open the directory to add custom assets Open the directory to add custom assets
== 打开用以添加自定义资源的文件夹路径 == 打开用以添加自定义资源的文件夹路径
@ -1914,3 +1906,54 @@ Team %d (%d/%d)
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== https://wiki.ddnet.org/index.php?title=Mapping/zh&variant=zh-hans == https://wiki.ddnet.org/index.php?title=Mapping/zh&variant=zh-hans
Could not resolve connect address '%s'. See local console for details.
== 无法解析连接地址 '%s'。检查本地控制台以获取详情。
Connect address error
== 连接地址错误
Could not connect dummy
== 无法连接分身
Dummy is not allowed on this server
== 此服务器禁止使用分身
Please wait…
== 请稍等…
Show client IDs (scoreboard, chat, spectator)
== 显示客户端 ID计分板、聊天、观战者
Normal:
== 正常:
Team:
== 队伍:
Dummy settings
== 分身设置
Toggle to edit your dummy settings
== 切换以编辑分身设置
Randomize
== 随机
Are you sure that you want to delete '%s'?
== 你确定要删除'%s'吗?
Delete skin
== 删除皮肤
Basic
== 基本
Custom
== 自定义
Unable to delete skin
== 无法删除皮肤
Customize
== 自定义

View file

@ -3,7 +3,7 @@
# Limit and Petr # Limit and Petr
#modified by: #modified by:
# LimiT 2011-07-02 20:24:44 # LimiT 2011-07-02 20:24:44
# dobrykafe 2024-06-09 00:00:00 # dobrykafe 2024-08-30 00:00:00
##### /authors ##### ##### /authors #####
##### translated strings ##### ##### translated strings #####
@ -278,9 +278,6 @@ Player
Player country: Player country:
== Filter krajín: == Filter krajín:
Player options
== Nastavenia hráča
Players Players
== Hráči == Hráči
@ -1420,9 +1417,6 @@ Show votes window after voting
DDRace HUD DDRace HUD
== DDRace HUD == DDRace HUD
Show client IDs in scoreboard
== Zobraziť ID klientov vo výsledkovej tabuľke
Show DDRace HUD Show DDRace HUD
== Zobraziť DDRace HUD == Zobraziť DDRace HUD
@ -1648,9 +1642,6 @@ Regular background color
Entities background color Entities background color
== Farba pozadia entít == Farba pozadia entít
Reload
== Znovu načítať
Use current map as background Use current map as background
== Použiť aktuálnu mapu ako pozadie == Použiť aktuálnu mapu ako pozadie
@ -1850,26 +1841,77 @@ Tee
Show only chat messages from team members Show only chat messages from team members
== Zobraziť iba správy od členov tímu == Zobraziť iba správy od členov tímu
Could not resolve connect address '%s'. See local console for details.
== Nie je možné získať adresu na pripojenie '%s'. Podrobnosti nájdete v miestnej konzole.
Connect address error
== Chyba v adrese na pripojenie
Could not connect dummy
== Nepodarilo sa pripojiť dummyho
[Spectating] [Spectating]
Following %s Following %s
== == Sledujete %s
Example of usage Example of usage
== == Príklad použitia
Dummy is not allowed on this server
== Dummy nie je na tomto serveri povolený
Please wait…
== Čakajte prosím…
Show client IDs (scoreboard, chat, spectator)
== Zobraziť ID klientov (výsledková tabuľka, chat, divák)
Normal:
== Normálny:
Team:
== Tím:
Dummy settings
== Nastavenie dummyho
Toggle to edit your dummy settings
== Prepnutím upravíte nastavenie dummyho
Randomize
== Randomizovať
Are you sure that you want to delete '%s'?
== Ste si istí, že chcete zmazať '%s'?
Delete skin
== Odstrániť skin
Basic
== Základné
Custom
== Vlastné
Unable to delete skin
== Nedá sa odstrániť skin
Customize
== Prispôsobiť
Round %d/%d Round %d/%d
== == Kolo %d/%d
[Spectators] [Spectators]
%d others… %d others…
== == %d ďalších…
[Team and size] [Team and size]
%d\n(%d/%d) %d\n(%d/%d)
== == %d\n(%d/%d)
Team %d (%d/%d) Team %d (%d/%d)
== == Tím %d (%d/%d)
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== == https://wiki.ddnet.org/wiki/Mapping

View file

@ -299,9 +299,6 @@ Player
Player country: Player country:
== País del jugador == País del jugador
Player options
== Opciones de jugador
Players Players
== Jugadores == Jugadores
@ -626,7 +623,7 @@ Types
== Tipos == Tipos
DDNet %s is out! DDNet %s is out!
== ¡DDNet %s ya esta disponible! == ¡DDNet %s ya está disponible!
Downloading %s: Downloading %s:
== Descargando %s: == Descargando %s:
@ -673,9 +670,6 @@ Connecting dummy
Connect Dummy Connect Dummy
== Conectar Dummy == Conectar Dummy
Reload
== Recargar
Deactivate Deactivate
== Desactivar == Desactivar
@ -1390,9 +1384,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== HUD DDRace == HUD DDRace
Show client IDs in scoreboard
== Mostrar IDs de cliente en la tabla de puntuación
Show DDRace HUD Show DDRace HUD
== Mostrar el HUD DDRace == Mostrar el HUD DDRace
@ -1891,3 +1882,54 @@ Team %d (%d/%d)
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== https://wiki.ddnet.org/wiki/Mapping/es == https://wiki.ddnet.org/wiki/Mapping/es
Could not resolve connect address '%s'. See local console for details.
== No se pudo resolver la dirección de conexión '%s'. Ver la consola local para más detalles.
Connect address error
== Error de dirección de conexión
Could not connect dummy
== No se pudo conectar tu dummy
Dummy is not allowed on this server
== No se permiten dummys en este servidor
Please wait…
== Espera por favor…
Show client IDs (scoreboard, chat, spectator)
== Mostrar IDs de cliente (scoreboard, chat, espectador)
Normal:
== Normal:
Team:
== Equipo:
Dummy settings
== Configuración del dummy
Toggle to edit your dummy settings
== Actívalo para configurar tu dummy
Randomize
== Aleatorizar
Are you sure that you want to delete '%s'?
== ¿Estás seguro de que quieres eliminar '%s'?
Delete skin
== Eliminar skin
Basic
== Básico
Custom
== Personalizado
Unable to delete skin
== No se pudo borrar la skin
Customize
== Personalizar

View file

@ -7,7 +7,7 @@
# 3edcxzaq1 2020-06-25 00:00:00 # 3edcxzaq1 2020-06-25 00:00:00
# cur.ie 2020-09-28 00:00:00 # cur.ie 2020-09-28 00:00:00
# simpygirl 2022-02-20 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 ##### ##### /authors #####
##### translated strings ##### ##### translated strings #####
@ -282,9 +282,6 @@ Player
Player country: Player country:
== Land == Land
Player options
== Spelaralternativ
Players Players
== Spelare == Spelare
@ -900,9 +897,6 @@ Show HUD
Pause Pause
== Pausa == Pausa
Reload
== Ladda om
Spree Spree
== Spree == Spree
@ -1516,9 +1510,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== DDRace HUD == DDRace HUD
Show client IDs in scoreboard
== Visa klient ID i poänglista
Show DDRace HUD Show DDRace HUD
== Visa DDRace HUD == Visa DDRace HUD
@ -1875,3 +1866,54 @@ Team %d (%d/%d)
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== 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

View file

@ -30,6 +30,7 @@
# 2024-04-21 By # 2024-04-21 By
# 2024-06-10 By # 2024-06-10 By
# 2024-07-21 By # 2024-07-21 By
# 2024-08-29 Pioooooo
##### /authors ##### ##### /authors #####
##### translated strings ##### ##### translated strings #####
@ -331,9 +332,6 @@ HUD
Player country: Player country:
== 玩家國家/地區: == 玩家國家/地區:
Player options
== 玩家選項
Players Players
== 玩家 == 玩家
@ -820,9 +818,6 @@ Show DDNet map finishes in server browser
transmits your player name to info.ddnet.org transmits your player name to info.ddnet.org
== 將會發送你的玩家名稱到 info.ddnet.org == 將會發送你的玩家名稱到 info.ddnet.org
Reload
== 重新整理
Enable replays Enable replays
== 啟用短時回放 == 啟用短時回放
@ -1227,7 +1222,7 @@ Team %d
== 隊伍 %d == 隊伍 %d
Position: Position:
== 坐標 == 坐標
Speed: Speed:
== 速度 == 速度
@ -1378,9 +1373,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== DDRace HUD == DDRace HUD
Show client IDs in scoreboard
== 顯示客戶端 IDs (計分板中)
Show DDRace HUD Show DDRace HUD
== 顯示 DDRace HUD == 顯示 DDRace HUD
@ -1512,7 +1504,7 @@ Are you sure that you want to reset the controls to their defaults?
[Graphics error] [Graphics error]
Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again.
== 初始化失敗。請嘗試打開配置目錄中的設定檔案 (settings_ddnet.cfg) 并將“gfx_backend OpenGL”修改為“gfx_backend Vulkan” (若沒有前者則可直接輸入後者) 再重試。 == 初始化失敗。請嘗試打開配置目錄中的設定檔案 (settings_ddnet.cfg) 并將 “gfx_backend” 設定為 “gfx_backend OpenGL” 或 “gfx_backend Vulkan” 再重試。
[Graphics error] [Graphics error]
Out of VRAM. Try removing custom assets (skins, entities, etc.), especially those with high resolution. Out of VRAM. Try removing custom assets (skins, entities, etc.), especially those with high resolution.
@ -1536,7 +1528,7 @@ Failed to swap framebuffers. Try to update your GPU drivers.
[Graphics error] [Graphics error]
Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again. Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again.
== 未知錯誤。請嘗試打開配置目錄中的設定檔案 (settings_ddnet.cfg) 并將“gfx_backend OpenGL”修改為“gfx_backend Vulkan” (若沒有前者則可直接輸入後者) 再重試。 == 未知錯誤。請嘗試打開配置目錄中的設定檔案 (settings_ddnet.cfg) 并將 “gfx_backend” 設定為 “gfx_backend OpenGL” 或 “gfx_backend Vulkan” 再重試。
[Graphics error] [Graphics error]
Could not initialize the given graphics backend, reverting to the default backend now. Could not initialize the given graphics backend, reverting to the default backend now.
@ -1564,7 +1556,7 @@ No controller found. Plug in a controller.
== 未檢測到任何控制器。請嘗試重新連接控制器。 == 未檢測到任何控制器。請嘗試重新連接控制器。
Unregister protocol and file extensions Unregister protocol and file extensions
== 未注冊的協議與擴充檔案 == 清除連結與檔案類型關聯
Open the directory to add custom assets Open the directory to add custom assets
== 打開用以新增自定義材質的資料夾路徑 == 打開用以新增自定義材質的資料夾路徑
@ -1903,3 +1895,54 @@ Team %d (%d/%d)
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== https://wiki.ddnet.org/index.php?title=Mapping/zh&variant=zh-hant == https://wiki.ddnet.org/index.php?title=Mapping/zh&variant=zh-hant
Could not resolve connect address '%s'. See local console for details.
== 無法解析連線地址 '%s'。檢查本機控制台以取得詳情。
Connect address error
== 連線地址錯誤
Could not connect dummy
== 無法連線分身
Dummy is not allowed on this server
== 此服務器禁止使用分身
Please wait…
== 請稍等…
Show client IDs (scoreboard, chat, spectator)
== 顯示客戶端 ID計分板、聊天、旁觀者
Normal:
== 正常:
Team:
== 隊伍:
Dummy settings
== 分身設定
Toggle to edit your dummy settings
== 切換以編輯分身設定
Randomize
== 隨機
Are you sure that you want to delete '%s'?
== 你確定要刪除'%s'嗎?
Delete skin
== 刪除外觀
Basic
== 基本
Custom
== 自定義
Unable to delete skin
== 無法刪除外觀
Customize
== 自定義

View file

@ -16,6 +16,7 @@
# Gokturk 2024-04-24 03:01:50 # Gokturk 2024-04-24 03:01:50
# Gokturk 2024-06-09 12:00:50 # Gokturk 2024-06-09 12:00:50
# Gokturk 2024-07-17 02:24:00 # Gokturk 2024-07-17 02:24:00
# Gokturk 2024-08-29 23:31:00
##### /authors ##### ##### /authors #####
##### translated strings ##### ##### translated strings #####
@ -287,9 +288,6 @@ Play background music
Player Player
== Oyuncu == Oyuncu
Player options
== Oyuncu ayarları
Players Players
== Oyuncular == Oyuncular
@ -665,9 +663,6 @@ Connecting dummy
Connect Dummy Connect Dummy
== Dummy Katıl == Dummy Katıl
Reload
== Yenile
Deactivate Deactivate
== Devre dışı bırak == Devre dışı bırak
@ -1463,9 +1458,6 @@ Show health, shields and ammo
DDRace HUD DDRace HUD
== DDRace arayüzü == DDRace arayüzü
Show client IDs in scoreboard
== Skor tablosunda ki istemci ID'lerini göster
Show DDRace HUD Show DDRace HUD
== DDRace arayüzünü göster == DDRace arayüzünü göster
@ -1883,3 +1875,54 @@ Team %d (%d/%d)
https://wiki.ddnet.org/wiki/Mapping https://wiki.ddnet.org/wiki/Mapping
== https://wiki.ddnet.org/wiki/Mapping/tr == https://wiki.ddnet.org/wiki/Mapping/tr
Could not resolve connect address '%s'. See local console for details.
== '%s' Bağlantı adresi çözümlenemedi. Detaylar için yerel konsola bakın
Connect address error
== Bağlantı adresi hatası
Could not connect dummy
== Dummy bağlanamadı
Dummy is not allowed on this server
== Bu sunucuda dummy izin verilmiyor
Please wait…
== Lütfen bekleyin…
Show client IDs (scoreboard, chat, spectator)
== İstemci ID'sini göster (skor tablosu, sohbet, izleyici)
Normal:
== Normal:
Team:
== Takım:
Dummy settings
== Dummy ayarları
Toggle to edit your dummy settings
== Dummy ayarlarını düzenlemek için açın
Randomize
== Rastgele
Are you sure that you want to delete '%s'?
== '%s' bunu silmek istediğine emin misin?
Delete skin
== Skini sil
Basic
== Basit
Custom
== Özel
Unable to delete skin
== Skin silinemiyor
Customize
== Özelleştir

View file

@ -4,6 +4,7 @@
#modified by: #modified by:
# 404_not_found 2011-07-30 19:50:58 # 404_not_found 2011-07-30 19:50:58
# EGYT5453 (15.05.2024-04.06.2024) # EGYT5453 (15.05.2024-04.06.2024)
# veydzh3r (31.08.2024-01.09.2024)
##### /authors ##### ##### /authors #####
##### translated strings ##### ##### translated strings #####
@ -32,10 +33,10 @@
== ще %d… == ще %d…
%d player %d player
== Гравців: %d == Гравці: %d
%d players %d players
== Гравців: %d == Гравці: %d
[Demo player duration] [Demo player duration]
%d sec. %d sec.
@ -76,10 +77,10 @@
== Нових згадок: 9+ == Нових згадок: 9+
A demo with this name already exists A demo with this name already exists
== Демо з цією назвою вже існує == Демо з цією назвою уже існує
A folder with this name already exists A folder with this name already exists
== Тека з цією назвою вже існує == Тека з цією назвою уже існує
[Graphics error] [Graphics error]
A render command failed. Try to update your GPU drivers. A render command failed. Try to update your GPU drivers.
@ -120,7 +121,7 @@ AFR
== АФР == АФР
Aim bind Aim bind
== Прив'язка == Привязка
All All
== Усі == Усі
@ -165,6 +166,9 @@ AntiPing: predict weapons
Appearance Appearance
== Вигляд == Вигляд
Are you sure that you want to delete '%s'?
== Ви дійсно хочете видалити '%s'?
Are you sure that you want to delete the demo '%s'? Are you sure that you want to delete the demo '%s'?
== Ви дійсно хочете видалити демо '%s'? == Ви дійсно хочете видалити демо '%s'?
@ -172,13 +176,13 @@ Are you sure that you want to delete the folder '%s'?
== Ви дійсно хочете видалити теку '%s'? == Ви дійсно хочете видалити теку '%s'?
Are you sure that you want to disconnect? 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 and switch to a different server?
== Ви дійсно хочете від'єднатися й приєднатися до іншого сервера? == Ви дійсно хочете відєднатися й приєднатися до іншого сервера?
Are you sure that you want to disconnect your dummy? Are you sure that you want to disconnect your dummy?
== Ви дійсно хочете від'єднати свого даммі? == Ви дійсно хочете відєднати свого даммі?
Are you sure that you want to quit? Are you sure that you want to quit?
== Ви дійсно хочете вийти? == Ви дійсно хочете вийти?
@ -190,7 +194,7 @@ Are you sure that you want to remove the player '%s' from your friends list?
== Ви дійсно хочете прибрати гравця '%s' зі списку друзів? == Ви дійсно хочете прибрати гравця '%s' зі списку друзів?
Are you sure that you want to reset the controls to their defaults? Are you sure that you want to reset the controls to their defaults?
== Ви дійсно хочете скинути налаштування керування до значень за замовчуванням? == Ви дійсно хочете скинути налаштування керувань до початкових значень?
Are you sure that you want to restart? Are you sure that you want to restart?
== Ви дійсно хочете перезапустити? == Ви дійсно хочете перезапустити?
@ -208,7 +212,7 @@ AUS
== АВС == АВС
Authed name color in scoreboard Authed name color in scoreboard
== Колір авторизованих у таблі == Колір авторизованих у таблиці
Auto Auto
== Авто == Авто
@ -217,7 +221,7 @@ auto
== автоматично == автоматично
Automatically create statboard csv Automatically create statboard csv
== Автоматично зберігати статистику у CSV-файл == Автоматично зберігати статистику у файл CSV
Automatically record demos Automatically record demos
== Автоматично записувати демо == Автоматично записувати демо
@ -232,11 +236,14 @@ Axis
== Осі == Осі
Background Background
== Фон == Тло
Background music volume Background music volume
== Гучність фонової музики == Гучність фонової музики
Basic
== Базовий
Best Best
== НКом == НКом
@ -256,7 +263,7 @@ Call vote
== Голосувати == Голосувати
Can't find a Tutorial server Can't find a Tutorial server
== Не вдалося знайти сервер-посібник == Не вдається знайти навчальний сервер
Cancel Cancel
== Скасувати == Скасувати
@ -289,13 +296,13 @@ Check now
== Перевірити == Перевірити
Checking for existing player with your name Checking for existing player with your name
== Перевіряємо Ваш нікнейм на доступність == Перевірка на наявність гравця з вашим ім’ям
CHN CHN
== КИТ == КИТ
Choose default eyes when joining a server Choose default eyes when joining a server
== Очі, які відображатимуться за замовчуванням == Типові очі під час приєднання до сервера
Clan Clan
== Клан == Клан
@ -322,7 +329,7 @@ Close the demo player
== Закрити програвач демо == Закрити програвач демо
Colors of the hook collision line, in case of a possible collision with: Colors of the hook collision line, in case of a possible collision with:
== Кольори лінії зіткнення гака, якщо він може зіштовхнутися з: == Кольори лінії зіткнення гака, в разі можливого зіткнення з:
Communities Communities
== Спільноти == Спільноти
@ -330,26 +337,29 @@ Communities
Config directory Config directory
== Тека налаштувань == Тека налаштувань
Connect address error
== Помилка адреси з’єднання
Connect Dummy Connect Dummy
== Приєднати даммі == Під’єднати даммі
Connected Connected
== Приєднано == Під’єднано
Connecting dummy Connecting dummy
== Приєднуємо даммі == Під’єднання даммі
Connecting to Connecting to
== Приєднуємося до == Під’єднання до
Connection Problems… Connection Problems…
== Проблеми зі з'єднанням… == Проблеми зі зєднанням…
Console Console
== Консоль == Консоль
Continue anyway? Continue anyway?
== Все одно продовжити? == Усе одно продовжити?
Controller Controller
== Контролер == Контролер
@ -366,19 +376,25 @@ Converse
Copy info Copy info
== Скопіювати == Скопіювати
Could not connect dummy
== Не вдалося під’єднати даммі
[Graphics error] [Graphics error]
Could not initialize the given graphics backend, reverting to the default backend now. Could not initialize the given graphics backend, reverting to the default backend now.
== Не вдалося ініціалізувати заданий графічний рушій, повертаємося до рушія за замовчуванням. == Не вдалося ініціалізувати заданий графічний рушій, повернення до тпового рушія.
[Graphics error] [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 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'. Див. локальну консоль для подробиць.
Could not save downloaded map. Try manually deleting this file: %s Could not save downloaded map. Try manually deleting this file: %s
== Не вдалося зберегти завантажену мапу. Спробуйте самостійно видалити цей файл: %s == Не вдалося зберегти завантажену мапу. Спробуйте самостійно видалити цей файл: %s
Count players only Count players only
== Рахувати тільки гравців == Рахувати лише гравців
Countries Countries
== Країни == Країни
@ -395,9 +411,15 @@ Current
custom custom
== власний == власний
Custom
== Власний
Custom colors Custom colors
== Власні кольори == Власні кольори
Customize
== Налаштувати
Cut interval Cut interval
== Інтервал == Інтервал
@ -417,7 +439,7 @@ DDNet %s is out!
== Вийшов DDNet %s! == Вийшов DDNet %s!
DDNet Client needs to be restarted to complete update! DDNet Client needs to be restarted to complete update!
== Потрібно перезапустити клієнт DDNet, щоб завершити оновлення! == Потрібно перезапустити клієнт DDNet для завершення оновлення!
DDNet Client updated! DDNet Client updated!
== Клієнт DDNet оновлено! == Клієнт DDNet оновлено!
@ -426,22 +448,22 @@ DDRace HUD
== HUD DDRace == 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 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
== Деактивувати == Деактивувати
Deactivate all Deactivate all
== Деактивувати усіх == Деактивувати всіх
Deaths Deaths
== С == Смерті
Debug mode enabled. Press Ctrl+Shift+D to disable debug mode. Debug mode enabled. Press Ctrl+Shift+D to disable debug mode.
== Увімкнено режим налагодження. Натисніть Ctrl+Shift+D, щоб його вимкнути. == Увімкнено режим налагодження. Натисніть Ctrl+Shift+D, щоб його вимкнути.
default default
== за замовчуванням == типово
Default length Default length
== Звичайна тривалість == Звичайна тривалість
@ -458,6 +480,9 @@ Delete demo
Delete folder Delete folder
== Видалити теку == Видалити теку
Delete skin
== Видалити скін
Demo Demo
== Демо == Демо
@ -471,16 +496,16 @@ Demos directory
== Тека демо == Тека демо
Desktop fullscreen Desktop fullscreen
== Стільничний повноекранний == Робочий стіл на весь екран
Disconnect Disconnect
== Від'єднатися == Відєднатися
Disconnect Dummy Disconnect Dummy
== Від'єднати даммі == Відєднати даммі
Disconnected Disconnected
== Від'єднано == Відєднано
Discord Discord
== Discord == Discord
@ -498,10 +523,10 @@ Download skins
== Завантажувати скіни == Завантажувати скіни
Downloading %s: Downloading %s:
== Завантажуємо %s: == Завантаження %s:
Downloading map Downloading map
== Завантажуємо мапу == Завантаження мапи
Draw! Draw!
== Нічия! == Нічия!
@ -512,8 +537,14 @@ Dummy
Dummy copy Dummy copy
== Повторювати рухи == Повторювати рухи
Dummy is not allowed on this server
== Використання даммі заборонене на цьому сервері
Dummy settings
== Налаштування даммі
Dynamic Camera Dynamic Camera
== Рухома камера == Динамічна камера
Editor Editor
== Редактор == Редактор
@ -546,7 +577,7 @@ Enable regular chat sound
== Звук звичайного повідомлення == Звук звичайного повідомлення
Enable replays Enable replays
== Повтори == Увімкнути повтори
Enable server message sound Enable server message sound
== Звук повідомлення сервера == Звук повідомлення сервера
@ -558,13 +589,13 @@ Enter Password
== Введіть пароль == Введіть пароль
Enter Username Enter Username
== Введіть логін == Введіть ім’я користувача
Entities Entities
== Сутності == Сутності
Entities background color Entities background color
== Колір фону сутностей == Колір тла сутностей
Error Error
== Помилка == Помилка
@ -582,13 +613,13 @@ EUR
== ЄВР == ЄВР
Example of usage Example of usage
== Наприклад == Приклад використання
Exclude Exclude
== Виключити == Виключити
Existing Player Existing Player
== Гравець вже існує == Гравець уже існує
Export cut as a separate demo Export cut as a separate demo
== Експортувати фрагмент як окреме демо == Експортувати фрагмент як окреме демо
@ -651,7 +682,7 @@ Following
[Spectating] [Spectating]
Following %s Following %s
== Слідуємо за %s == Cпостерігання за %s
Force vote Force vote
== Форсувати == Форсувати
@ -681,7 +712,7 @@ FSAA samples
== Вибірка FSAA == Вибірка FSAA
Fullscreen Fullscreen
== Повноекранний == На весь екран
Game Game
== Гра == Гра
@ -708,13 +739,13 @@ Gameplay
== Ігролад == Ігролад
General General
== Загальне == Загальні
Getting game info Getting game info
== Отримуємо інформацію про гру == Отримання інформації про гру
Getting server list from master server Getting server list from master server
== Отримуємо список серверів з головного сервера == Отримання списку серверів з головного сервера
Ghost Ghost
== Привид == Привид
@ -726,7 +757,7 @@ Go back one marker
== Перемотати до попередньої мітки == Перемотати до попередньої мітки
Go back one tick Go back one tick
== Перемотати вперед на один тік == Перемотати вперед на один такт
Go back the specified duration Go back the specified duration
== Перемотати назад == Перемотати назад
@ -735,7 +766,7 @@ Go forward one marker
== Перемотати до наступної мітки == Перемотати до наступної мітки
Go forward one tick Go forward one tick
== Перемотати назад на один тік == Перемотати вперед на один такт
Go forward the specified duration Go forward the specified duration
== Перемотати вперед == Перемотати вперед
@ -801,31 +832,31 @@ HUD
== HUD == HUD
Hue Hue
== Тон == Відтінок
Indicate map finish Indicate map finish
== Позначати пройдені мапи == Позначати пройдені мапи
Info Messages Info Messages
== Інфо-повідомлення == Інфо. повідомлення
Ingame controller mode Ingame controller mode
== Режим контролера у грі == Режим контролера у грі
Ingame controller sens. Ingame controller sens.
== Чутл. у грі == Чутливість у грі
Ingame mouse sens. Ingame mouse sens.
== Чутл. у грі == Чутливість у грі
Initializing assets Initializing assets
== Ініціалізуємо текстури == Ініціалізація текстур
Initializing components Initializing components
== Ініціалізуємо компоненти == Ініціалізація компонентів
Initializing map logic Initializing map logic
== Ініціалізуємо логіку мапи == Ініціалізація логіки мапи
Internet Internet
== Інтернет == Інтернет
@ -834,7 +865,7 @@ Invalid Demo
== Недійсне демо == Недійсне демо
It's recommended that you check the settings to adjust them to your liking before joining a server. It's recommended that you check the settings to adjust them to your liking before joining a server.
== Перед тим як приєднатися до сервера, рекомендуємо змінити налаштування до ваших уподобань. == Перед тим, як приєднатися до сервера, рекомендуємо змінити налаштування до ваших уподобань.
Join blue Join blue
== До синіх == До синіх
@ -846,7 +877,7 @@ Join red
== До червоних == До червоних
Join Tutorial Server Join Tutorial Server
== Приєднатися до сервера-посібника == Приєднатися до навчального сервера
Jump Jump
== Стрибок == Стрибок
@ -885,52 +916,52 @@ Loading…
== Завантаження… == Завантаження…
Loading assets Loading assets
== Завантажуємо текстури == Завантаження текстур
Loading commands… Loading commands…
== Завантажуємо команди == Завантаження команд
Loading DDNet Client Loading DDNet Client
== Завантажуємо клієнт DDNet == Завантаження клієнта DDNet
Loading demo file from storage Loading demo file from storage
== Завантажуємо демо-файл зі сховища == Завантаження демо-файлу зі сховища
Loading demo files Loading demo files
== Завантажуємо демо-файли == Завантаження демо-файлів
Loading ghost files Loading ghost files
== Завантажуємо файли привида == Завантаження файлів привида
Loading map file from storage Loading map file from storage
== Завантажуємо файл мапи зі сховища == Завантаження файлу мапи зі сховища
Loading menu images Loading menu images
== Завантажуємо зображення меню == Завантаження зображень меню
Loading menu themes Loading menu themes
== Завантажуємо теми меню == Завантаження тем меню
Loading race demo files Loading race demo files
== Завантажуємо демо-файли забігів == Завантаження демо-файлів забігів
Loading skin files Loading skin files
== Завантажуємо файли скінів == Завантаження файлів скінів
Loading sound files Loading sound files
== Завантажуємо звукові файли == Завантаження звукових файлів
Lock team Lock team
== Замкнути команду == Замкнути команду
Locked Locked
== Заблоковано == Замкнено
Main menu Main menu
== Головне меню == Головне меню
Manual Manual
== Вручну == Уручну
Map Map
== Мапа == Мапа
@ -940,7 +971,7 @@ map not included
== мапу не включено == мапу не включено
Map sound volume Map sound volume
== Гучність мапи == Гучність звуків мапи
Mark the beginning of a cut (right click to reset) Mark the beginning of a cut (right click to reset)
== Позначити початок фрагмента (права кнопка миші, щоб скинути) == Позначити початок фрагмента (права кнопка миші, щоб скинути)
@ -955,13 +986,13 @@ Match %d of %d
== Збіг %d з %d == Збіг %d з %d
Max CSVs Max CSVs
== Найбільше CSV-файлів == Макс. кількість файлів CSV
Max demos Max demos
== Найбільше демо-файлів == Макс. кількість демо-файлів
Max Screenshots Max Screenshots
== Найбільше знімків екрана == Макс. кількість знімків екрана
may cause delay may cause delay
== може спричинити затримку == може спричинити затримку
@ -985,7 +1016,7 @@ Move left
== Вліво == Вліво
Move player to spectators Move player to spectators
== Зробити гравця спостерігачем == Зробити гравця глядачем
Move right Move right
== Вправо == Вправо
@ -1000,7 +1031,7 @@ Multi-View
== Мульти-камера == Мульти-камера
Mute when not active Mute when not active
== Приглушувати, якщо вікно неактивне == Приглушувати звук поза грою
NA NA
== ПНА == ПНА
@ -1021,7 +1052,7 @@ Netversion
== Версія == Версія
New name: New name:
== Нова назва == Нова назва:
New random timeout code New random timeout code
== Новий випадковий код тайм-ауту == Новий випадковий код тайм-ауту
@ -1033,16 +1064,16 @@ Next weapon
== Наст. зброя == Наст. зброя
Nickname Nickname
== Нікнейм == Псевдонім
No No
== Ні == Ні
No answer from server yet. No answer from server yet.
== Сервер ще не відповів. == Поки що немає відповіді від сервера.
No controller found. Plug in a controller. No controller found. Plug in a controller.
== Жодного контролера не знайдено. Підключіть контролер. == Жодного контролера не знайдено. Під’єднайте контролер.
No demo selected No demo selected
== Жодного демо не вибрано == Жодного демо не вибрано
@ -1071,6 +1102,9 @@ No updates available
None None
== Немає == Немає
Normal:
== Звичайний:
Normal Color Normal Color
== Колір звичайних повідомлень == Колір звичайних повідомлень
@ -1078,23 +1112,23 @@ Normal message
== Звичайні повідомлення == Звичайні повідомлення
NOT CONNECTED NOT CONNECTED
== НЕ ПРИЄДНАНО == НЕ ПІД’ЄДНАНО
Nothing hookable Nothing hookable
== нічим, за що можна зачепитися == нічим, за що можна зачепитися
[friends (server browser)] [friends (server browser)]
Offline (%d) Offline (%d)
== Офлайн (%d) == Не в мережі (%d)
Ok Ok
== Гаразд == Гаразд
Online clanmates (%d) Online clanmates (%d)
== Онлайн клановці (%d) == Співклановці в мережі (%d)
Online players (%d) Online players (%d)
== Онлайн гравці (%d) == Гравці в мережі (%d)
Only save improvements Only save improvements
== Зберігати лише покращення == Зберігати лише покращення
@ -1125,7 +1159,7 @@ Open the settings file
[Graphics error] [Graphics error]
Out of VRAM. Try removing custom assets (skins, entities, etc.), especially those with high resolution. Out of VRAM. Try removing custom assets (skins, entities, etc.), especially those with high resolution.
== Недостатньо відеопам'яті. Спробуйте видалити власні текстури (скіни, сутності і т.п.), особливо ті, що мають високу роздільну здатність. == Недостатньо відеопамяті. Спробуйте видалити власні текстури (скіни, сутності і т.д.), особливо ті, що мають високу роздільність.
Overlay entities Overlay entities
== Накладати сутності == Накладати сутності
@ -1134,7 +1168,7 @@ Parent Folder
== Батьківська тека == Батьківська тека
Particles Particles
== Часточки == Частинки
Pause Pause
== Пауза == Пауза
@ -1176,9 +1210,6 @@ Player country:
Player info change cooldown Player info change cooldown
== Затримка зміни інформації про гравця == Затримка зміни інформації про гравця
Player options
== Налаштування гравців
Players Players
== Гравці == Гравці
@ -1191,11 +1222,14 @@ Please enter your nickname below.
Please use a different filename Please use a different filename
== Будь ласка, назвіть файл по-іншому == Будь ласка, назвіть файл по-іншому
Please wait…
== Будь ласка, зачекайте…
Position: Position:
== Позиція: == Позиція:
Preparing demo playback Preparing demo playback
== Підготовлюємо відтворення демо == Підготовлення відтворення демо
Press a key… Press a key…
== Натисніть клавішу… == Натисніть клавішу…
@ -1210,7 +1244,7 @@ Quads are used for background decoration
== Квади використовуються для декорацій == Квади використовуються для декорацій
Quit Quit
== Вихід == Вийти
Quitting. Please wait… Quitting. Please wait…
== Вихід. Будь ласка, зачекайте… == Вихід. Будь ласка, зачекайте…
@ -1218,6 +1252,9 @@ Quitting. Please wait…
Race Race
== Забіг == Забіг
Randomize
== Навмання
Ratio Ratio
== У/С == У/С
@ -1225,7 +1262,7 @@ Reason:
== Причина: == Причина:
Reconnect in %d sec Reconnect in %d sec
== Переприєднання через %dс == Повторне під’єднання за %dс
Record demo Record demo
== Запис демо == Запис демо
@ -1240,15 +1277,12 @@ Refresh Rate
== Частота кадрів == Частота кадрів
Regular background color Regular background color
== Колір звичайного фону == Колір звичайного тла
[Ingame controller mode] [Ingame controller mode]
Relative Relative
== Відносний == Відносний
Reload
== Оновити
Remote console Remote console
== Віддалена консоль == Віддалена консоль
@ -1286,10 +1320,10 @@ Replay
== Повтор == Повтор
Replay feature is disabled! Replay feature is disabled!
== Повтори відключено! == Повтори вимкнено!
Requesting to join the game Requesting to join the game
== Запитуємо приєднання до гри == Запит на приєднання до гри
Reset Reset
== Скинути == Скинути
@ -1301,7 +1335,7 @@ Reset filter
== Скинути фільтр == Скинути фільтр
Reset to defaults Reset to defaults
== Скинути == Скинути до типових
Restart Restart
== Перезапустити == Перезапустити
@ -1328,7 +1362,7 @@ SA
== ПДА == ПДА
Same clan color in scoreboard Same clan color in scoreboard
== Колір співклановців у таблі == Колір співклановців у таблиці
Sat. Sat.
== Насич. == Насич.
@ -1340,7 +1374,7 @@ Save ghost
== Зберігати привида == Зберігати привида
Save power by lowering refresh rate (higher input latency) Save power by lowering refresh rate (higher input latency)
== Зберігати енергію зниженням частоти кадрів (вища затримка вводу) == Економити енергію шляхом зниження частоти кадрів (вища затримка введення)
Save the best demo of each race Save the best demo of each race
== Зберігати найкраще демо кожного забігу == Зберігати найкраще демо кожного забігу
@ -1355,7 +1389,7 @@ Score limit
== Гра до == Гра до
Scoreboard Scoreboard
== Табло == Таблиця
Screen Screen
== Екран == Екран
@ -1367,10 +1401,10 @@ Search
== Пошук == Пошук
Searching Searching
== Шукаємо == Пошук
Sending initial client info Sending initial client info
== Надсилаємо початкові дані клієнта == Надсилання початкових даних клієнта
Server address: Server address:
== Адреса сервера: == Адреса сервера:
@ -1391,7 +1425,7 @@ Server not full
== Неповний сервер == Неповний сервер
Set all to Rifle Set all to Rifle
== Встановити так, як у гвинтівки == Установити так, як у гвинтівки
Settings Settings
== Налаштування == Налаштування
@ -1417,8 +1451,8 @@ Show chat
Show clan above name plates Show clan above name plates
== Показувати клан над ніками == Показувати клан над ніками
Show client IDs in scoreboard Show client IDs (scoreboard, chat, spectator)
== Показувати ID клієнта в таблі == Показувати ID клієнта (таблиця, чат, глядачі)
Show DDNet map finishes in server browser Show DDNet map finishes in server browser
== Показувати пройдені мапи DDNet у браузері серверів == Показувати пройдені мапи DDNet у браузері серверів
@ -1430,7 +1464,7 @@ Show dummy actions
== Показувати дії з даммі == Показувати дії з даммі
Show entities Show entities
== Сутності == Показ сутностей
Show finish messages Show finish messages
== Показувати повідомлення про фініші == Показувати повідомлення про фініші
@ -1439,7 +1473,7 @@ Show freeze bars
== Показувати смугу заморозки == Показувати смугу заморозки
Show friend mark (♥) in name plates Show friend mark (♥) in name plates
== Показувати позначку друга (♥) біля ніків == Показувати позначку друга (♥) біля псевдонімів
Show friends only Show friends only
== Показувати лише з друзями == Показувати лише з друзями
@ -1448,10 +1482,10 @@ Show ghost
== Показувати привида == Показувати привида
Show health, shields and ammo Show health, shields and ammo
== Показувати здоров'я, щити й набої == Показувати здоров’я, захист і набої
Show hook strength icon indicator Show hook strength icon indicator
== Показувати графічний індикатор сили гака == Показувати іконку індикатора сили гака
Show hook strength number indicator Show hook strength number indicator
== Показувати числовий індикатор сили гака == Показувати числовий індикатор сили гака
@ -1475,10 +1509,10 @@ Show local time always
== Завжди показувати місцевий час == Завжди показувати місцевий час
Show name plates Show name plates
== Показувати ніки == Показувати псевдоніми
Show names in chat in team colors Show names in chat in team colors
== Фарбувати ніки в чаті в кольори команд == Показувати імена в чаті в кольорах команди
Show only chat messages from friends Show only chat messages from friends
== Показувати лише повідомлення від друзів == Показувати лише повідомлення від друзів
@ -1517,7 +1551,7 @@ Show text entities
== Текстові сутності == Текстові сутності
Show tiles layers from BG map Show tiles layers from BG map
== Показувати тайли з мапи фону == Показувати плитки з мапи фону
Show quads Show quads
== Показувати квади == Показувати квади
@ -1547,13 +1581,13 @@ Slow down the demo
== Сповільнити == Сповільнити
Smooth Dynamic Camera Smooth Dynamic Camera
== Гладка рухома камера == Гладка динамічна камера
Some map images could not be loaded. Check the local console for details. 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. Some map sounds could not be loaded. Check the local console for details.
== Деякі звуки мапи не завантажилися. Деталі у локальній консолі. == Деякі звуки мапи не завантажилися. Див. локальну консоль для подробиць.
Something hookable Something hookable
== чимось, за що можна зачепитися == чимось, за що можна зачепитися
@ -1577,10 +1611,10 @@ Spectate previous
== Попер. гравець == Попер. гравець
Spectator mode Spectator mode
== Режим спостерігача == Режим глядача
Spectators Spectators
== Спостерігачі == Глядачі
Speed Speed
== Швидкість == Швидкість
@ -1632,7 +1666,7 @@ Switch weapon on pickup
== Змінювати зброю при підхопленні == Змінювати зброю при підхопленні
Switch weapon when out of ammo Switch weapon when out of ammo
== Змінювати зброю коли закінчуються набої == Змінювати зброю при закінченні набоїв
System message System message
== Повідомлення системи == Повідомлення системи
@ -1640,6 +1674,9 @@ System message
Team Team
== Команда == Команда
Team:
== Командний:
Team %d Team %d
== Команда %d == Команда %d
@ -1695,7 +1732,10 @@ Toggle ghost
== Привид == Привид
Toggle keyboard shortcuts Toggle keyboard shortcuts
== Перемкнути скорочення == Перемкнути сполучення
Toggle to edit your dummy settings
== Перемкніть, щоб змінити налаштування даммі
transmits your player name to info.ddnet.org transmits your player name to info.ddnet.org
== передає ваш нікнейм до info.ddnet.org == передає ваш нікнейм до info.ddnet.org
@ -1707,7 +1747,7 @@ Try again
== Спробувати ще раз == Спробувати ще раз
Trying to determine UDP connectivity… Trying to determine UDP connectivity…
== Намагаємося визначити UDP-з'єднання… == Спроба визначити UDP-з’єднання…
Tutorial Tutorial
== Посібник == Посібник
@ -1728,10 +1768,13 @@ UI Color
== Колір інтерфейсу == Колір інтерфейсу
UI controller sens. UI controller sens.
== Чутл. у інтерфейсі == Чутл. в інтерфейсі
UI mouse sens. UI mouse sens.
== Чутл. у інтерфейсі == Чутл. в інтерфейсі
Unable to delete skin
== Не вдалося видалити скін
Unable to delete the demo '%s' Unable to delete the demo '%s'
== Не вдалося видалити демо '%s' == Не вдалося видалити демо '%s'
@ -1756,25 +1799,25 @@ Unregister protocol and file extensions
== Розреєструвати протокол і розширення файлів == Розреєструвати протокол і розширення файлів
Update failed! Check log… Update failed! Check log…
== Оновлення не вдалося! Перевірте журнал… == Помилка оновлення! Перевірте журнал…
Update now Update now
== Оновити == Оновити
Updating… Updating…
== Оновлюємо == Оновлення
Uploading map data to GPU Uploading map data to GPU
== Вивантажуємо дані мапи до відеокарти == Вивантаження даних мапи до відеокарти
Use current map as background Use current map as background
== Використовувати поточну мапу як фон == Використовувати поточну мапу як тло
Use high DPI Use high DPI
== Високий DPI == Високий DPI
Use k key to kill (restart), q to pause and watch other players. See settings for other key binds. 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 Use old chat style
== Старий стиль чату == Старий стиль чату
@ -1798,7 +1841,7 @@ Video name:
== Назва відео: == Назва відео:
Video was saved to '%s' Video was saved to '%s'
== Відео було збережено до '%s' == Відео збережено до '%s'
Videos directory Videos directory
== Тека відео == Тека відео
@ -1843,13 +1886,13 @@ Why are you slowmo replaying to read this?
== Чому ви переглядаєте це у повторі? == Чому ви переглядаєте це у повторі?
Windowed Windowed
== Віконний == У вікні
Windowed borderless Windowed borderless
== Віконний без рамок == Вікно без рамок
Windowed fullscreen Windowed fullscreen
== Віконний повноекранний == Вікно на весь екран
Yes Yes
== Так == Так
@ -1858,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. 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? Your nickname '%s' is already used (%d points). Do you still want to use it?
== Нікнейм '%s' вже зайнято (%d балів). Все ще хочете використовувати його? == Ваш псевдонім «%s» вже зайнято (%d балів). Усе ще хочете використовувати його?
Your skin Your skin
== Ваш скін == Ваш скін

View file

@ -7,8 +7,6 @@ export MAKEFLAGS
ANDROID_NDK_VERSION="$(cd "$ANDROID_HOME/ndk" && find . -maxdepth 1 | sort -n | tail -1)" ANDROID_NDK_VERSION="$(cd "$ANDROID_HOME/ndk" && find . -maxdepth 1 | sort -n | tail -1)"
ANDROID_NDK_VERSION="${ANDROID_NDK_VERSION:2}" ANDROID_NDK_VERSION="${ANDROID_NDK_VERSION:2}"
# ANDROID_NDK_VERSION must be exported for build.sh step
export ANDROID_NDK_VERSION
# ANDROID_NDK_HOME must be exported for cargo-ndk # ANDROID_NDK_HOME must be exported for cargo-ndk
export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/$ANDROID_NDK_VERSION" export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/$ANDROID_NDK_VERSION"
@ -197,6 +195,7 @@ cd "${BUILD_FOLDER}" || exit 1
mkdir -p src/main mkdir -p src/main
mkdir -p src/main/res/values mkdir -p src/main/res/values
mkdir -p src/main/res/xml
mkdir -p src/main/res/mipmap mkdir -p src/main/res/mipmap
function copy_dummy_files() { function copy_dummy_files() {
@ -213,6 +212,7 @@ copy_dummy_files scripts/android/files/proguard-rules.pro proguard-rules.pro
copy_dummy_files scripts/android/files/settings.gradle settings.gradle copy_dummy_files scripts/android/files/settings.gradle settings.gradle
copy_dummy_files scripts/android/files/AndroidManifest.xml src/main/AndroidManifest.xml copy_dummy_files scripts/android/files/AndroidManifest.xml src/main/AndroidManifest.xml
copy_dummy_files scripts/android/files/res/values/strings.xml src/main/res/values/strings.xml copy_dummy_files scripts/android/files/res/values/strings.xml src/main/res/values/strings.xml
copy_dummy_files scripts/android/files/res/xml/shortcuts.xml src/main/res/xml/shortcuts.xml
copy_dummy_files other/icons/DDNet_256x256x32.png src/main/res/mipmap/ic_launcher.png copy_dummy_files other/icons/DDNet_256x256x32.png src/main/res/mipmap/ic_launcher.png
copy_dummy_files other/icons/DDNet_256x256x32.png src/main/res/mipmap/ic_launcher_round.png copy_dummy_files other/icons/DDNet_256x256x32.png src/main/res/mipmap/ic_launcher_round.png

View file

@ -30,7 +30,8 @@
android:name="org.ddnet.client.NativeMain" android:name="org.ddnet.client.NativeMain"
android:exported="true" android:exported="true"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden" android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:screenOrientation="landscape"> android:screenOrientation="landscape"
android:launchMode="singleInstance">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
@ -38,6 +39,9 @@
<meta-data <meta-data
android:name="android.app.lib_name" android:name="android.app.lib_name"
android:value="DDNet" /> android:value="DDNet" />
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity> </activity>
</application> </application>
</manifest> </manifest>

View file

@ -18,7 +18,6 @@ java {
android { android {
compileSdkVersion 34 compileSdkVersion 34
ndkVersion "TW_NDK_VERSION"
defaultConfig { defaultConfig {
applicationId "org.ddnet.client" applicationId "org.ddnet.client"
namespace("org.ddnet.client") namespace("org.ddnet.client")

View file

@ -45,12 +45,13 @@ sed -i "s/TW_KEY_NAME/${TW_KEY_NAME_ESCAPED}/g" build.gradle
sed -i "s/TW_KEY_PW/${TW_KEY_PW_ESCAPED}/g" build.gradle sed -i "s/TW_KEY_PW/${TW_KEY_PW_ESCAPED}/g" build.gradle
sed -i "s/TW_KEY_ALIAS/${TW_KEY_ALIAS_ESCAPED}/g" build.gradle sed -i "s/TW_KEY_ALIAS/${TW_KEY_ALIAS_ESCAPED}/g" build.gradle
sed -i "s/TW_NDK_VERSION/${ANDROID_NDK_VERSION}/g" build.gradle
sed -i "s/TW_VERSION_CODE/${TW_VERSION_CODE}/g" build.gradle sed -i "s/TW_VERSION_CODE/${TW_VERSION_CODE}/g" build.gradle
sed -i "s/TW_VERSION_NAME/${TW_VERSION_NAME}/g" build.gradle sed -i "s/TW_VERSION_NAME/${TW_VERSION_NAME}/g" build.gradle
sed -i "s/DDNet/${APK_BASENAME}/g" src/main/res/values/strings.xml sed -i "s/DDNet/${APK_BASENAME}/g" src/main/res/values/strings.xml
sed -i "s/org.ddnet.client/${APK_PACKAGE_NAME}/g" src/main/res/xml/shortcuts.xml
sed -i "s/\"DDNet\"/\"${APK_BASENAME}\"/g" src/main/AndroidManifest.xml sed -i "s/\"DDNet\"/\"${APK_BASENAME}\"/g" src/main/AndroidManifest.xml
sed -i "s/org.ddnet.client/${APK_PACKAGE_NAME}/g" src/main/AndroidManifest.xml sed -i "s/org.ddnet.client/${APK_PACKAGE_NAME}/g" src/main/AndroidManifest.xml

View file

@ -10,6 +10,8 @@ public class NativeMain extends SDLActivity {
private static final int COMMAND_RESTART_APP = SDLActivity.COMMAND_USER + 1; private static final int COMMAND_RESTART_APP = SDLActivity.COMMAND_USER + 1;
private String[] launchArguments = new String[0];
@Override @Override
protected String[] getLibraries() { protected String[] getLibraries() {
return new String[] { return new String[] {
@ -20,17 +22,35 @@ public class NativeMain extends SDLActivity {
@Override @Override
public void onCreate(Bundle SavedInstanceState) { public void onCreate(Bundle SavedInstanceState) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
Intent intent = getIntent();
if(intent != null) {
String gfxBackend = intent.getStringExtra("gfx-backend");
if(gfxBackend != null) {
if(gfxBackend.equals("Vulkan")) {
launchArguments = new String[] {"gfx_backend Vulkan"};
} else if(gfxBackend.equals("OpenGL")) {
launchArguments = new String[] {"gfx_backend OpenGL"};
}
}
}
super.onCreate(SavedInstanceState); super.onCreate(SavedInstanceState);
} }
@Override
protected String[] getArguments() {
return launchArguments;
}
@Override @Override
protected boolean onUnhandledMessage(int command, Object param) { protected boolean onUnhandledMessage(int command, Object param) {
if(command == COMMAND_RESTART_APP) { if(command == COMMAND_RESTART_APP) {
restartApp(); restartApp();
return true; return true;
} }
return false; return false;
} }
private void restartApp() { private void restartApp() {
Intent restartIntent = Intent restartIntent =

View file

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">DDNet</string> <string name="app_name">DDNet</string>
<string name="shortcut_play_vulkan_short">Play (Vulkan)</string>
<string name="shortcut_play_opengl_short">Play (OpenGL)</string>
</resources> </resources>

View file

@ -0,0 +1,30 @@
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:shortcutId="play-vulkan"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:shortcutShortLabel="@string/shortcut_play_vulkan_short">
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="org.ddnet.client"
android:targetClass="org.ddnet.client.NativeMain">
<extra
android:name="gfx-backend"
android:value="Vulkan" />
</intent>
</shortcut>
<shortcut
android:shortcutId="play-opengl"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:shortcutShortLabel="@string/shortcut_play_opengl_short">
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="org.ddnet.client"
android:targetClass="org.ddnet.client.NativeMain">
<extra
android:name="gfx-backend"
android:value="OpenGL" />
</intent>
</shortcut>
</shortcuts>

View file

@ -37,7 +37,7 @@ def decode(fileobj, elements_per_key):
if current_key: if current_key:
if len(data[current_key]) != 1+elements_per_key: if len(data[current_key]) != 1+elements_per_key:
raise LanguageDecodeError("Wrong number of elements per key", fileobj.name, index) raise LanguageDecodeError("Wrong number of elements per key", fileobj.name, index)
data[current_key].append(index) data[current_key].append(index - 1 if current_context else index)
if line in data: if line in data:
raise LanguageDecodeError("Key defined multiple times: " + line, fileobj.name, index) raise LanguageDecodeError("Key defined multiple times: " + line, fileobj.name, index)
data[(line, current_context)] = [index - 1 if current_context else index] data[(line, current_context)] = [index - 1 if current_context else index]

View file

@ -1236,6 +1236,7 @@ int str_format_int(char *buffer, size_t buffer_size, int value);
template<typename... Args> template<typename... Args>
int str_format_opt(char *buffer, int buffer_size, const char *format, Args... 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...); return str_format(buffer, buffer_size, format, args...);
} }

View file

@ -443,7 +443,9 @@ static bool BackendInitGlew(EBackendType BackendType, int &GlewMajor, int &GlewM
#ifdef CONF_GLEW_HAS_CONTEXT_INIT #ifdef CONF_GLEW_HAS_CONTEXT_INIT
if(GLEW_OK != glewContextInit()) if(GLEW_OK != glewContextInit())
#else #else
if(GLEW_OK != glewInit()) GLenum InitResult = glewInit();
const char *pVideoDriver = SDL_GetCurrentVideoDriver();
if(GLEW_OK != InitResult && pVideoDriver && !str_comp(pVideoDriver, "wayland") && GLEW_ERROR_NO_GLX_DISPLAY != InitResult)
#endif #endif
return false; return false;
@ -1019,6 +1021,13 @@ int CGraphicsBackend_SDL_GL::Init(const char *pName, int *pScreen, int *pWidth,
SDL_GetVersion(&Linked); SDL_GetVersion(&Linked);
dbg_msg("sdl", "SDL version %d.%d.%d (compiled = %d.%d.%d)", Linked.major, Linked.minor, Linked.patch, dbg_msg("sdl", "SDL version %d.%d.%d (compiled = %d.%d.%d)", Linked.major, Linked.minor, Linked.patch,
Compiled.major, Compiled.minor, Compiled.patch); Compiled.major, Compiled.minor, Compiled.patch);
#if CONF_PLATFORM_LINUX && SDL_VERSION_ATLEAST(2, 0, 22)
// needed to workaround SDL from forcing exclusively X11 if linking against the GLX flavour of GLEW instead of the EGL one
// w/o this on Wayland systems (no XWayland support) SDL's Video subsystem will fail to load (starting from SDL2.30+)
if(Linked.major == 2 && Linked.minor >= 30)
SDL_SetHint(SDL_HINT_VIDEODRIVER, "x11,wayland");
#endif
} }
if(!SDL_WasInit(SDL_INIT_VIDEO)) if(!SDL_WasInit(SDL_INIT_VIDEO))

View file

@ -247,11 +247,7 @@ void CClient::SendReady(int Conn)
void CClient::SendMapRequest() void CClient::SendMapRequest()
{ {
if(m_MapdownloadFileTemp) dbg_assert(!m_MapdownloadFileTemp, "Map download already in progress");
{
io_close(m_MapdownloadFileTemp);
Storage()->RemoveFile(m_aMapdownloadFilenameTemp, IStorage::TYPE_SAVE);
}
m_MapdownloadFileTemp = Storage()->OpenFile(m_aMapdownloadFilenameTemp, IOFLAG_WRITE, IStorage::TYPE_SAVE); m_MapdownloadFileTemp = Storage()->OpenFile(m_aMapdownloadFilenameTemp, IOFLAG_WRITE, IStorage::TYPE_SAVE);
if(IsSixup()) if(IsSixup())
{ {
@ -656,6 +652,7 @@ void CClient::DisconnectWithReason(const char *pReason)
m_aRconAuthed[0] = 0; m_aRconAuthed[0] = 0;
mem_zero(m_aRconUsername, sizeof(m_aRconUsername)); mem_zero(m_aRconUsername, sizeof(m_aRconUsername));
mem_zero(m_aRconPassword, sizeof(m_aRconPassword)); mem_zero(m_aRconPassword, sizeof(m_aRconPassword));
m_MapDetailsPresent = false;
m_ServerSentCapabilities = false; m_ServerSentCapabilities = false;
m_UseTempRconCommands = 0; m_UseTempRconCommands = 0;
m_ExpectedRconCommands = -1; m_ExpectedRconCommands = -1;
@ -671,22 +668,7 @@ void CClient::DisconnectWithReason(const char *pReason)
m_CurrentServerCurrentPingTime = -1; m_CurrentServerCurrentPingTime = -1;
m_CurrentServerNextPingTime = -1; m_CurrentServerNextPingTime = -1;
// disable all downloads ResetMapDownload(true);
m_MapdownloadChunk = 0;
if(m_pMapdownloadTask)
m_pMapdownloadTask->Abort();
if(m_MapdownloadFileTemp)
{
io_close(m_MapdownloadFileTemp);
Storage()->RemoveFile(m_aMapdownloadFilenameTemp, IStorage::TYPE_SAVE);
}
m_MapdownloadFileTemp = 0;
m_MapdownloadSha256Present = false;
m_MapdownloadSha256 = SHA256_ZEROED;
m_MapdownloadCrc = 0;
m_MapdownloadTotalsize = -1;
m_MapdownloadAmount = 0;
m_MapDetailsPresent = false;
// clear the current server info // clear the current server info
mem_zero(&m_CurrentServerInfo, sizeof(m_CurrentServerInfo)); mem_zero(&m_CurrentServerInfo, sizeof(m_CurrentServerInfo));
@ -1543,6 +1525,8 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
DummyDisconnect(0); DummyDisconnect(0);
} }
ResetMapDownload(true);
SHA256_DIGEST *pMapSha256 = nullptr; SHA256_DIGEST *pMapSha256 = nullptr;
const char *pMapUrl = nullptr; const char *pMapUrl = nullptr;
if(MapDetailsWerePresent && str_comp(m_aMapDetailsName, pMap) == 0 && m_MapDetailsCrc == MapCrc) if(MapDetailsWerePresent && str_comp(m_aMapDetailsName, pMap) == 0 && m_MapDetailsCrc == MapCrc)
@ -1559,12 +1543,6 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
} }
else else
{ {
if(m_MapdownloadFileTemp)
{
io_close(m_MapdownloadFileTemp);
Storage()->RemoveFile(m_aMapdownloadFilenameTemp, IStorage::TYPE_SAVE);
}
// start map download // start map download
FormatMapDownloadFilename(pMap, pMapSha256, MapCrc, false, m_aMapdownloadFilename, sizeof(m_aMapdownloadFilename)); FormatMapDownloadFilename(pMap, pMapSha256, MapCrc, false, m_aMapdownloadFilename, sizeof(m_aMapdownloadFilename));
FormatMapDownloadFilename(pMap, pMapSha256, MapCrc, true, m_aMapdownloadFilenameTemp, sizeof(m_aMapdownloadFilenameTemp)); FormatMapDownloadFilename(pMap, pMapSha256, MapCrc, true, m_aMapdownloadFilenameTemp, sizeof(m_aMapdownloadFilenameTemp));
@ -1573,16 +1551,11 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
str_format(aBuf, sizeof(aBuf), "starting to download map to '%s'", m_aMapdownloadFilenameTemp); str_format(aBuf, sizeof(aBuf), "starting to download map to '%s'", m_aMapdownloadFilenameTemp);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", aBuf); m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", aBuf);
m_MapdownloadChunk = 0;
str_copy(m_aMapdownloadName, pMap); str_copy(m_aMapdownloadName, pMap);
m_MapdownloadSha256Present = (bool)pMapSha256; m_MapdownloadSha256Present = (bool)pMapSha256;
m_MapdownloadSha256 = pMapSha256 ? *pMapSha256 : SHA256_ZEROED; m_MapdownloadSha256 = pMapSha256 ? *pMapSha256 : SHA256_ZEROED;
m_MapdownloadCrc = MapCrc; m_MapdownloadCrc = MapCrc;
m_MapdownloadTotalsize = MapSize; m_MapdownloadTotalsize = MapSize;
m_MapdownloadAmount = 0;
ResetMapDownload();
if(pMapSha256) if(pMapSha256)
{ {
@ -2220,24 +2193,44 @@ int CClient::UnpackAndValidateSnapshot(CSnapshot *pFrom, CSnapshot *pTo)
return Builder.Finish(pTo); return Builder.Finish(pTo);
} }
void CClient::ResetMapDownload() void CClient::ResetMapDownload(bool ResetActive)
{ {
if(m_pMapdownloadTask) if(m_pMapdownloadTask)
{ {
m_pMapdownloadTask->Abort(); m_pMapdownloadTask->Abort();
m_pMapdownloadTask = NULL; m_pMapdownloadTask = nullptr;
}
if(m_MapdownloadFileTemp)
{
io_close(m_MapdownloadFileTemp);
m_MapdownloadFileTemp = 0;
}
if(Storage()->FileExists(m_aMapdownloadFilenameTemp, IStorage::TYPE_SAVE))
{
Storage()->RemoveFile(m_aMapdownloadFilenameTemp, IStorage::TYPE_SAVE);
}
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';
} }
m_MapdownloadFileTemp = 0;
m_MapdownloadAmount = 0;
} }
void CClient::FinishMapDownload() void CClient::FinishMapDownload()
{ {
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", "download complete, loading map"); m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", "download complete, loading map");
int Prev = m_MapdownloadTotalsize; SHA256_DIGEST *pSha256 = m_MapdownloadSha256Present ? &m_MapdownloadSha256 : nullptr;
m_MapdownloadTotalsize = -1;
SHA256_DIGEST *pSha256 = m_MapdownloadSha256Present ? &m_MapdownloadSha256 : 0;
bool FileSuccess = true; bool FileSuccess = true;
if(Storage()->FileExists(m_aMapdownloadFilename, IStorage::TYPE_SAVE)) if(Storage()->FileExists(m_aMapdownloadFilename, IStorage::TYPE_SAVE))
@ -2245,36 +2238,26 @@ void CClient::FinishMapDownload()
FileSuccess &= Storage()->RenameFile(m_aMapdownloadFilenameTemp, m_aMapdownloadFilename, IStorage::TYPE_SAVE); FileSuccess &= Storage()->RenameFile(m_aMapdownloadFilenameTemp, m_aMapdownloadFilename, IStorage::TYPE_SAVE);
if(!FileSuccess) if(!FileSuccess)
{ {
ResetMapDownload();
char aError[128 + IO_MAX_PATH_LENGTH]; 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); str_format(aError, sizeof(aError), Localize("Could not save downloaded map. Try manually deleting this file: %s"), m_aMapdownloadFilename);
DisconnectWithReason(aError); DisconnectWithReason(aError);
return; return;
} }
// load map
const char *pError = LoadMap(m_aMapdownloadName, m_aMapdownloadFilename, pSha256, m_MapdownloadCrc); const char *pError = LoadMap(m_aMapdownloadName, m_aMapdownloadFilename, pSha256, m_MapdownloadCrc);
if(!pError) if(!pError)
{ {
ResetMapDownload(); ResetMapDownload(true);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", "loading done"); m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", "loading done");
SendReady(CONN_MAIN); SendReady(CONN_MAIN);
} }
else if(m_pMapdownloadTask) // fallback else if(m_pMapdownloadTask) // fallback
{ {
ResetMapDownload(); ResetMapDownload(false);
m_MapdownloadTotalsize = Prev;
SendMapRequest(); SendMapRequest();
} }
else else
{ {
if(m_MapdownloadFileTemp)
{
io_close(m_MapdownloadFileTemp);
m_MapdownloadFileTemp = 0;
Storage()->RemoveFile(m_aMapdownloadFilenameTemp, IStorage::TYPE_SAVE);
}
ResetMapDownload();
DisconnectWithReason(pError); DisconnectWithReason(pError);
} }
} }
@ -2800,7 +2783,7 @@ void CClient::Update()
else if(m_pMapdownloadTask->State() == EHttpState::ERROR || m_pMapdownloadTask->State() == EHttpState::ABORTED) else if(m_pMapdownloadTask->State() == EHttpState::ERROR || m_pMapdownloadTask->State() == EHttpState::ABORTED)
{ {
dbg_msg("webdl", "http failed, falling back to gameserver"); dbg_msg("webdl", "http failed, falling back to gameserver");
ResetMapDownload(); ResetMapDownload(false);
SendMapRequest(); SendMapRequest();
} }
} }
@ -3333,7 +3316,7 @@ bool CClient::InitNetworkClient(char *pError, size_t ErrorSize)
if(g_Config.m_Bindaddr[0]) 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); str_format(pError, ErrorSize, "Could not open the network client, try changing or unsetting the bindaddr '%s'.", g_Config.m_Bindaddr);
else else
str_format(pError, ErrorSize, "Could not open the network client."); str_copy(pError, "Could not open the network client.", ErrorSize);
return false; return false;
} }
} }
@ -4374,8 +4357,20 @@ void CClient::RegisterCommands()
// used for server browser update // used for server browser update
m_pConsole->Chain("br_filter_string", ConchainServerBrowserUpdate, this); 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", 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_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("add_favorite", ConchainServerBrowserUpdate, this);
m_pConsole->Chain("remove_favorite", ConchainServerBrowserUpdate, this); m_pConsole->Chain("remove_favorite", ConchainServerBrowserUpdate, this);
m_pConsole->Chain("end_favorite_group", ConchainServerBrowserUpdate, this); m_pConsole->Chain("end_favorite_group", ConchainServerBrowserUpdate, this);

View file

@ -360,7 +360,7 @@ public:
int UnpackAndValidateSnapshot(CSnapshot *pFrom, CSnapshot *pTo); int UnpackAndValidateSnapshot(CSnapshot *pFrom, CSnapshot *pTo);
void ResetMapDownload(); void ResetMapDownload(bool ResetActive);
void FinishMapDownload(); void FinishMapDownload();
void RequestDDNetInfo() override; void RequestDDNetInfo() override;

View file

@ -27,9 +27,6 @@
#include <engine/http.h> #include <engine/http.h>
#include <engine/storage.h> #include <engine/storage.h>
static constexpr const char *COMMUNITY_COUNTRY_NONE = "none";
static constexpr const char *COMMUNITY_TYPE_NONE = "None";
class CSortWrap class CSortWrap
{ {
typedef bool (CServerBrowser::*SortFunc)(int, int) const; typedef bool (CServerBrowser::*SortFunc)(int, int) const;

View file

@ -228,10 +228,8 @@ int CSound::Init()
return -1; return -1;
} }
m_MixingRate = g_Config.m_SndRate;
SDL_AudioSpec Format, FormatOut; SDL_AudioSpec Format, FormatOut;
Format.freq = m_MixingRate; Format.freq = g_Config.m_SndRate;
Format.format = AUDIO_S16; Format.format = AUDIO_S16;
Format.channels = 2; Format.channels = 2;
Format.samples = g_Config.m_SndBufferSize; Format.samples = g_Config.m_SndBufferSize;
@ -239,7 +237,7 @@ int CSound::Init()
Format.userdata = this; Format.userdata = this;
// Open the audio device and start playing sound! // 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) if(m_Device == 0)
{ {
dbg_msg("sound", "unable to open audio: %s", SDL_GetError()); dbg_msg("sound", "unable to open audio: %s", SDL_GetError());
@ -248,6 +246,7 @@ int CSound::Init()
else else
dbg_msg("sound", "sound init successful using audio driver '%s'", SDL_GetCurrentAudioDriver()); dbg_msg("sound", "sound init successful using audio driver '%s'", SDL_GetCurrentAudioDriver());
m_MixingRate = FormatOut.freq;
m_MaxFrames = FormatOut.samples * 2; m_MaxFrames = FormatOut.samples * 2;
#if defined(CONF_VIDEORECORDER) #if defined(CONF_VIDEORECORDER)
m_MaxFrames = maximum<uint32_t>(m_MaxFrames, 1024 * 2); // make the buffer bigger just in case m_MaxFrames = maximum<uint32_t>(m_MaxFrames, 1024 * 2); // make the buffer bigger just in case

View file

@ -53,7 +53,7 @@ public:
virtual int GetInteger(unsigned Index) const = 0; virtual int GetInteger(unsigned Index) const = 0;
virtual float GetFloat(unsigned Index) const = 0; virtual float GetFloat(unsigned Index) const = 0;
virtual const char *GetString(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; virtual void RemoveArgument(unsigned Index) = 0;

View file

@ -254,6 +254,7 @@ public:
virtual void Ban(int ClientId, int Seconds, const char *pReason, bool VerbatimReason) = 0; virtual void Ban(int ClientId, int Seconds, const char *pReason, bool VerbatimReason) = 0;
virtual void RedirectClient(int ClientId, int Port, bool Verbose = false) = 0; virtual void RedirectClient(int ClientId, int Port, bool Verbose = false) = 0;
virtual void ChangeMap(const char *pMap) = 0; virtual void ChangeMap(const char *pMap) = 0;
virtual void ReloadMap() = 0;
virtual void DemoRecorder_HandleAutoStart() = 0; virtual void DemoRecorder_HandleAutoStart() = 0;

View file

@ -2341,7 +2341,7 @@ void CServer::UpdateRegisterServerInfo()
JsonWriter.WriteAttribute("map"); JsonWriter.WriteAttribute("map");
JsonWriter.BeginObject(); JsonWriter.BeginObject();
JsonWriter.WriteAttribute("name"); JsonWriter.WriteAttribute("name");
JsonWriter.WriteStrValue(m_aCurrentMap); JsonWriter.WriteStrValue(GetMapName());
JsonWriter.WriteAttribute("sha256"); JsonWriter.WriteAttribute("sha256");
JsonWriter.WriteStrValue(aMapSha256); JsonWriter.WriteStrValue(aMapSha256);
JsonWriter.WriteAttribute("size"); JsonWriter.WriteAttribute("size");
@ -2551,6 +2551,11 @@ void CServer::ChangeMap(const char *pMap)
m_MapReload = str_comp(Config()->m_SvMap, m_aCurrentMap) != 0; m_MapReload = str_comp(Config()->m_SvMap, m_aCurrentMap) != 0;
} }
void CServer::ReloadMap()
{
m_MapReload = true;
}
int CServer::LoadMap(const char *pMapName) int CServer::LoadMap(const char *pMapName)
{ {
m_MapReload = false; m_MapReload = false;

View file

@ -379,6 +379,7 @@ public:
void ChangeMap(const char *pMap) override; void ChangeMap(const char *pMap) override;
const char *GetMapName() const override; const char *GetMapName() const override;
void ReloadMap() override;
int LoadMap(const char *pMapName); int LoadMap(const char *pMapName);
void SaveDemo(int ClientId, float Time) override; void SaveDemo(int ClientId, float Time) override;

View file

@ -309,6 +309,9 @@ public:
static constexpr const char *COMMUNITY_DDNET = "ddnet"; static constexpr const char *COMMUNITY_DDNET = "ddnet";
static constexpr const char *COMMUNITY_NONE = "none"; 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 * Special community value for country/type filters that
* affect all communities. * affect all communities.

View file

@ -111,22 +111,29 @@ void SIntConfigVariable::ResetToOld()
void SColorConfigVariable::CommandCallback(IConsole::IResult *pResult, void *pUserData) void SColorConfigVariable::CommandCallback(IConsole::IResult *pResult, void *pUserData)
{ {
SColorConfigVariable *pData = static_cast<SColorConfigVariable *>(pUserData); SColorConfigVariable *pData = static_cast<SColorConfigVariable *>(pUserData);
char aBuf[IConsole::CMDLINE_LENGTH + 64];
if(pResult->NumArguments()) if(pResult->NumArguments())
{ {
if(pData->CheckReadOnly()) if(pData->CheckReadOnly())
return; return;
const ColorHSLA Color = pResult->GetColor(0, pData->m_Light); const auto Color = pResult->GetColor(0, pData->m_Light);
const unsigned Value = Color.Pack(pData->m_Light ? 0.5f : 0.0f, pData->m_Alpha); if(Color)
{
const unsigned Value = Color->Pack(pData->m_Light ? 0.5f : 0.0f, pData->m_Alpha);
*pData->m_pVariable = Value; *pData->m_pVariable = Value;
if(pResult->m_ClientId != IConsole::CLIENT_ID_GAME) if(pResult->m_ClientId != IConsole::CLIENT_ID_GAME)
pData->m_OldValue = Value; 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 else
{ {
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Value: %u", *pData->m_pVariable); str_format(aBuf, sizeof(aBuf), "Value: %u", *pData->m_pVariable);
pData->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "config", aBuf); 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); SColorConfigVariable *pColorVariable = static_cast<SColorConfigVariable *>(pVariable);
const float Darkest = pColorVariable->m_Light ? 0.5f : 0.0f; 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); 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.Pack(Darkest, pColorVariable->m_Alpha)); pColorVariable->SetValue(Value.value_or(ColorHSLA(0, 0, 0)).Pack(Darkest, pColorVariable->m_Alpha));
} }
else if(pVariable->m_Type == SConfigVariable::VAR_STRING) else if(pVariable->m_Type == SConfigVariable::VAR_STRING)
{ {

View file

@ -105,6 +105,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(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(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(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(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") MACRO_CONFIG_INT(ClMotdTime, cl_motd_time, 10, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "How long to show the server message of the day")
@ -343,6 +344,9 @@ MACRO_CONFIG_INT(BrDemoSort, br_demo_sort, 0, 0, 2, CFGFLAG_SAVE | CFGFLAG_CLIEN
MACRO_CONFIG_INT(BrDemoSortOrder, br_demo_sort_order, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Sorting order in demo browser") MACRO_CONFIG_INT(BrDemoSortOrder, br_demo_sort_order, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Sorting order in demo browser")
MACRO_CONFIG_INT(BrDemoFetchInfo, br_demo_fetch_info, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Whether to auto fetch demo infos on refresh") MACRO_CONFIG_INT(BrDemoFetchInfo, br_demo_fetch_info, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Whether to auto fetch demo infos on refresh")
MACRO_CONFIG_INT(GhSort, gh_sort, 1, 0, 2, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Sorting column in ghost list")
MACRO_CONFIG_INT(GhSortOrder, gh_sort_order, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Sorting order in ghost list")
MACRO_CONFIG_INT(SndBufferSize, snd_buffer_size, 512, 128, 32768, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Sound buffer size (may cause delay if large)") MACRO_CONFIG_INT(SndBufferSize, snd_buffer_size, 512, 128, 32768, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Sound buffer size (may cause delay if large)")
MACRO_CONFIG_INT(SndRate, snd_rate, 48000, 5512, 384000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Sound mixing rate") MACRO_CONFIG_INT(SndRate, snd_rate, 48000, 5512, 384000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Sound mixing rate")
MACRO_CONFIG_INT(SndEnable, snd_enable, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Sound enable") MACRO_CONFIG_INT(SndEnable, snd_enable, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Sound enable")

View file

@ -40,22 +40,29 @@ float CConsole::CResult::GetFloat(unsigned Index) const
return str_tofloat(m_apArgs[Index]); 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) if(Index >= m_NumArgs)
return ColorHSLA(0, 0, 0); return std::nullopt;
const char *pStr = m_apArgs[Index]; const char *pStr = m_apArgs[Index];
if(str_isallnum(pStr) || ((pStr[0] == '-' || pStr[0] == '+') && str_isallnum(pStr + 1))) // Teeworlds Color (Packed HSL) 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) if(Light)
return Hsla.UnclampLighting(); return Hsla.UnclampLighting();
return Hsla; return Hsla;
} }
else if(*pStr == '$') // Hex RGB/RGBA 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")) else if(!str_comp_nocase(pStr, "red"))
return ColorHSLA(0.0f / 6.0f, 1, .5f); 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")) else if(!str_comp_nocase(pStr, "black"))
return ColorHSLA(0, 0, 0); return ColorHSLA(0, 0, 0);
return ColorHSLA(0, 0, 0); return std::nullopt;
} }
const IConsole::CCommandInfo *CConsole::CCommand::NextCommandInfo(int AccessLevel, int FlagMask) const 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; return 0;
} }
int CConsole::ParseArgs(CResult *pResult, const char *pFormat) int CConsole::ParseArgs(CResult *pResult, const char *pFormat, bool IsColor)
{ {
char Command = *pFormat; char Command = *pFormat;
char *pStr; char *pStr;
int Optional = 0; int Optional = 0;
int Error = 0; int Error = PARSEARGS_OK;
pResult->ResetVictim(); pResult->ResetVictim();
@ -155,7 +162,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
{ {
if(!Optional) if(!Optional)
{ {
Error = 1; Error = PARSEARGS_MISSING_VALUE;
break; break;
} }
@ -191,7 +198,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
pStr++; // skip due to escape pStr++; // skip due to escape
} }
else if(pStr[0] == 0) else if(pStr[0] == 0)
return 1; // return error return PARSEARGS_MISSING_VALUE; // return error
*pDst = *pStr; *pDst = *pStr;
pDst++; pDst++;
@ -215,13 +222,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
if(Command == 'r') // rest of the string if(Command == 'r') // rest of the string
break; break;
else if(Command == 'v') // validate victim else if(Command == 'v' || Command == 'i' || Command == 'f' || Command == 's')
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
pStr = str_skip_to_whitespace(pStr); pStr = str_skip_to_whitespace(pStr);
if(pStr[0] != 0) // check for end of string if(pStr[0] != 0) // check for end of string
@ -230,6 +231,32 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
pStr++; 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) if(pVictim)
{ {
pResult->SetVictim(pVictim); pResult->SetVictim(pVictim);
@ -487,10 +514,15 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientId, bo
if(Stroke || IsStrokeCommand) 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]; char aBuf[CMDLINE_LENGTH + 64];
str_format(aBuf, sizeof(aBuf), "Invalid arguments. Usage: %s %s", pCommand->m_pName, pCommand->m_pParams); 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); Print(OUTPUT_LEVEL_STANDARD, "chatresp", aBuf);
} }
else if(m_StoreCommands && pCommand->m_Flags & CFGFLAG_STORE) else if(m_StoreCommands && pCommand->m_Flags & CFGFLAG_STORE)

View file

@ -115,7 +115,7 @@ class CConsole : public IConsole
const char *GetString(unsigned Index) const override; const char *GetString(unsigned Index) const override;
int GetInteger(unsigned Index) const override; int GetInteger(unsigned Index) const override;
float GetFloat(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 void RemoveArgument(unsigned Index) override
{ {
@ -144,7 +144,16 @@ class CConsole : public IConsole
}; };
int ParseStart(CResult *pResult, const char *pString, int Length); 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 this function will set pFormat to the next parameter (i,s,r,v,?) it contains and

View file

@ -490,8 +490,7 @@ void CNetBan::ConBans(IConsole::IResult *pResult, void *pUser)
if(NumBans == 0) if(NumBans == 0)
{ {
str_format(aMsg, sizeof(aMsg), "The ban list is empty."); pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", "The ban list is empty.");
pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "net_ban", aMsg);
return; return;
} }

View file

@ -4,6 +4,16 @@
#include "protocolglue.h" #include "protocolglue.h"
int GameFlags_ClampToSix(int Flags)
{
int Six = 0;
if(Flags & GAMEFLAG_TEAMS)
Six |= GAMEFLAG_TEAMS;
if(Flags & GAMEFLAG_FLAGS)
Six |= GAMEFLAG_FLAGS;
return Six;
}
int PlayerFlags_SevenToSix(int Flags) int PlayerFlags_SevenToSix(int Flags)
{ {
int Six = 0; int Six = 0;

View file

@ -1,6 +1,7 @@
#ifndef ENGINE_SHARED_PROTOCOLGLUE_H #ifndef ENGINE_SHARED_PROTOCOLGLUE_H
#define ENGINE_SHARED_PROTOCOLGLUE_H #define ENGINE_SHARED_PROTOCOLGLUE_H
int GameFlags_ClampToSix(int Flags);
int PlayerFlags_SevenToSix(int Flags); int PlayerFlags_SevenToSix(int Flags);
int PlayerFlags_SixToSeven(int Flags); int PlayerFlags_SixToSeven(int Flags);
void PickupType_SevenToSix(int Type7, int &Type6, int &SubType6); void PickupType_SevenToSix(int Type7, int &Type6, int &SubType6);

View file

@ -96,6 +96,7 @@ public:
CreateFolder("mapres", TYPE_SAVE); CreateFolder("mapres", TYPE_SAVE);
CreateFolder("downloadedmaps", TYPE_SAVE); CreateFolder("downloadedmaps", TYPE_SAVE);
CreateFolder("skins", TYPE_SAVE); CreateFolder("skins", TYPE_SAVE);
CreateFolder("skins7", TYPE_SAVE);
CreateFolder("downloadedskins", TYPE_SAVE); CreateFolder("downloadedskins", TYPE_SAVE);
CreateFolder("themes", TYPE_SAVE); CreateFolder("themes", TYPE_SAVE);
CreateFolder("communityicons", TYPE_SAVE); CreateFolder("communityicons", TYPE_SAVE);

View file

@ -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_NETWORK_WIRED = "\xEF\x9B\xBF";
MAYBE_UNUSED static const char *FONT_ICON_LIST_UL = "\xEF\x83\x8A"; 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_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_SLASH = "\xEF\x9C\x95";
MAYBE_UNUSED static const char *FONT_ICON_PLAY = "\xEF\x81\x8B"; MAYBE_UNUSED static const char *FONT_ICON_PLAY = "\xEF\x81\x8B";

View file

@ -160,8 +160,6 @@ class CGameConsole : public CComponent
static const ColorRGBA ms_SearchHighlightColor; static const ColorRGBA ms_SearchHighlightColor;
static const ColorRGBA ms_SearchSelectedColor; static const ColorRGBA ms_SearchSelectedColor;
void Toggle(int Type);
static void PossibleCommandsRenderCallback(int Index, const char *pStr, void *pUser); static void PossibleCommandsRenderCallback(int Index, const char *pStr, void *pUser);
static void ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData); static void ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData);
static void ConToggleRemoteConsole(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; virtual bool OnInput(const IInput::CEvent &Event) override;
void Prompt(char (&aPrompt)[32]); void Prompt(char (&aPrompt)[32]);
void Toggle(int Type);
bool IsClosed() { return m_ConsoleState == CONSOLE_CLOSED; } bool IsClosed() { return m_ConsoleState == CONSOLE_CLOSED; }
}; };
#endif #endif

View file

@ -1693,8 +1693,7 @@ void CHud::RenderRecord()
if(m_ServerRecord > 0.0f) if(m_ServerRecord > 0.0f)
{ {
char aBuf[64]; char aBuf[64];
str_format(aBuf, sizeof(aBuf), Localize("Server best:")); TextRender()->Text(5, 75, 6, Localize("Server best:"), -1.0f);
TextRender()->Text(5, 75, 6, aBuf, -1.0f);
char aTime[32]; char aTime[32];
str_time_float(m_ServerRecord, TIME_HOURS_CENTISECS, aTime, sizeof(aTime)); str_time_float(m_ServerRecord, TIME_HOURS_CENTISECS, aTime, sizeof(aTime));
str_format(aBuf, sizeof(aBuf), "%s%s", m_ServerRecord > 3600 ? "" : " ", aTime); str_format(aBuf, sizeof(aBuf), "%s%s", m_ServerRecord > 3600 ? "" : " ", aTime);
@ -1705,8 +1704,7 @@ void CHud::RenderRecord()
if(PlayerRecord > 0.0f) if(PlayerRecord > 0.0f)
{ {
char aBuf[64]; char aBuf[64];
str_format(aBuf, sizeof(aBuf), Localize("Personal best:")); TextRender()->Text(5, 82, 6, Localize("Personal best:"), -1.0f);
TextRender()->Text(5, 82, 6, aBuf, -1.0f);
char aTime[32]; char aTime[32];
str_time_float(PlayerRecord, TIME_HOURS_CENTISECS, aTime, sizeof(aTime)); str_time_float(PlayerRecord, TIME_HOURS_CENTISECS, aTime, sizeof(aTime));
str_format(aBuf, sizeof(aBuf), "%s%s", PlayerRecord > 3600 ? "" : " ", aTime); str_format(aBuf, sizeof(aBuf), "%s%s", PlayerRecord > 3600 ? "" : " ", aTime);

View file

@ -1208,7 +1208,7 @@ void CMenus::RenderPopupFullscreen(CUIRect Screen)
pButtonText = Localize("Ok"); pButtonText = Localize("Ok");
if(Client()->ReconnectTime() > 0) 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(); pTitle = Client()->ErrorString();
pExtraText = aBuf; pExtraText = aBuf;
pButtonText = Localize("Abort"); pButtonText = Localize("Abort");
@ -1282,6 +1282,11 @@ void CMenus::RenderPopupFullscreen(CUIRect Screen)
pButtonText = m_aMessageButton; pButtonText = m_aMessageButton;
TopAlign = true; TopAlign = true;
} }
else if(m_Popup == POPUP_SAVE_SKIN)
{
pTitle = Localize("Save skin");
pExtraText = Localize("Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced.");
}
CUIRect Box, Part; CUIRect Box, Part;
Box = Screen; Box = Screen;
@ -1756,6 +1761,52 @@ void CMenus::RenderPopupFullscreen(CUIRect Screen)
SetActive(false); SetActive(false);
} }
} }
else if(m_Popup == POPUP_SAVE_SKIN)
{
CUIRect Label, TextBox, Yes, No;
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VMargin(80.0f, &Part);
Part.VSplitMid(&No, &Yes);
Yes.VMargin(20.0f, &Yes);
No.VMargin(20.0f, &No);
static CButtonContainer s_ButtonNo;
if(DoButton_Menu(&s_ButtonNo, Localize("No"), 0, &No) || Ui()->ConsumeHotkey(CUi::HOTKEY_ESCAPE))
m_Popup = POPUP_NONE;
static CButtonContainer s_ButtonYes;
if(DoButton_Menu(&s_ButtonYes, Localize("Yes"), m_SkinNameInput.IsEmpty() ? 1 : 0, &Yes) || Ui()->ConsumeHotkey(CUi::HOTKEY_ENTER))
{
if(m_SkinNameInput.GetLength())
{
if(m_SkinNameInput.GetString()[0] != 'x' && m_SkinNameInput.GetString()[1] != '_')
{
if(m_pClient->m_Skins7.SaveSkinfile(m_SkinNameInput.GetString(), m_Dummy))
{
m_Popup = POPUP_NONE;
m_SkinListNeedsUpdate = true;
}
else
PopupMessage(Localize("Error"), Localize("Unable to save the skin"), Localize("Ok"), POPUP_SAVE_SKIN);
}
else
PopupMessage(Localize("Error"), Localize("Unable to save the skin with a reserved name"), Localize("Ok"), POPUP_SAVE_SKIN);
}
}
Box.HSplitBottom(60.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);
Part.VMargin(60.0f, &Label);
Label.VSplitLeft(100.0f, &Label, &TextBox);
TextBox.VSplitLeft(20.0f, nullptr, &TextBox);
Ui()->DoLabel(&Label, Localize("Name"), 18.0f, TEXTALIGN_ML);
Ui()->DoClearableEditBox(&m_SkinNameInput, &TextBox, 12.0f);
}
else else
{ {
Box.HSplitBottom(20.f, &Box, &Part); Box.HSplitBottom(20.f, &Box, &Part);

View file

@ -768,6 +768,14 @@ public:
bool HasFile() const { return m_aFilename[0]; } bool HasFile() const { return m_aFilename[0]; }
}; };
enum
{
GHOST_SORT_NONE = -1,
GHOST_SORT_NAME,
GHOST_SORT_TIME,
GHOST_SORT_DATE,
};
std::vector<CGhostItem> m_vGhosts; std::vector<CGhostItem> m_vGhosts;
std::chrono::nanoseconds m_GhostPopulateStartTime{0}; std::chrono::nanoseconds m_GhostPopulateStartTime{0};
@ -776,6 +784,7 @@ public:
CGhostItem *GetOwnGhost(); CGhostItem *GetOwnGhost();
void UpdateOwnGhost(CGhostItem Item); void UpdateOwnGhost(CGhostItem Item);
void DeleteGhostItem(int Index); void DeleteGhostItem(int Index);
void SortGhostlist();
bool CanDisplayWarning() const; bool CanDisplayWarning() const;
@ -802,6 +811,7 @@ public:
POPUP_QUIT, POPUP_QUIT,
POPUP_RESTART, POPUP_RESTART,
POPUP_WARNING, POPUP_WARNING,
POPUP_SAVE_SKIN,
// demo player states // demo player states
DEMOPLAYER_NONE = 0, DEMOPLAYER_NONE = 0,

View file

@ -869,11 +869,14 @@ void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
{ {
if(Click == 1) 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) for(int j = 0; j < MaxItems; ++j)
{ {
if(j != ItemIndex) if(const char *pItemName = GetItemName(j);
Filter.Add(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) else if(Click == 2)
@ -890,7 +893,10 @@ void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
bool AllFilteredExceptUs = true; bool AllFilteredExceptUs = true;
for(int j = 0; j < MaxItems; ++j) 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; AllFilteredExceptUs = false;
break; break;
@ -898,7 +904,7 @@ void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
} }
// When last one is removed, re-enable all currently selectable items. // When last one is removed, re-enable all currently selectable items.
// Don't use Clear, to avoid enabling also currently unselectable items. // Don't use Clear, to avoid enabling also currently unselectable items.
if(AllFilteredExceptUs) if(AllFilteredExceptUs && Active)
{ {
for(int j = 0; j < MaxItems; ++j) for(int j = 0; j < MaxItems; ++j)
{ {

View file

@ -175,12 +175,12 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
// increase/decrease speed // increase/decrease speed
if(!Input()->ModifierIsPressed() && !Input()->ShiftIsPressed() && !Input()->AltIsPressed()) if(!Input()->ModifierIsPressed() && !Input()->ShiftIsPressed() && !Input()->AltIsPressed())
{ {
if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP) || Input()->KeyPress(KEY_UP)) if(Input()->KeyPress(KEY_UP) || (m_MenuActive && Input()->KeyPress(KEY_MOUSE_WHEEL_UP)))
{ {
DemoPlayer()->AdjustSpeedIndex(+1); DemoPlayer()->AdjustSpeedIndex(+1);
UpdateLastSpeedChange(); UpdateLastSpeedChange();
} }
else if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN) || Input()->KeyPress(KEY_DOWN)) else if(Input()->KeyPress(KEY_DOWN) || (m_MenuActive && Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN)))
{ {
DemoPlayer()->AdjustSpeedIndex(-1); DemoPlayer()->AdjustSpeedIndex(-1);
UpdateLastSpeedChange(); UpdateLastSpeedChange();

View file

@ -76,7 +76,7 @@ void CMenus::RenderGame(CUIRect MainView)
if(!Client()->DummyAllowed()) if(!Client()->DummyAllowed())
{ {
DoButton_Menu(&s_DummyButton, Localize("Connect Dummy"), 1, &Button); DoButton_Menu(&s_DummyButton, Localize("Connect Dummy"), 1, &Button);
GameClient()->m_Tooltips.DoToolTip(&s_DummyButton, &Button, Localize("Dummy is not allowed on this server.")); GameClient()->m_Tooltips.DoToolTip(&s_DummyButton, &Button, Localize("Dummy is not allowed on this server"));
} }
else if(Client()->DummyConnectingDelayed()) else if(Client()->DummyConnectingDelayed())
{ {
@ -944,7 +944,7 @@ void CMenus::GhostlistPopulate()
m_vGhosts.clear(); m_vGhosts.clear();
m_GhostPopulateStartTime = time_get_nanoseconds(); m_GhostPopulateStartTime = time_get_nanoseconds();
Storage()->ListDirectoryInfo(IStorage::TYPE_ALL, m_pClient->m_Ghost.GetGhostDir(), GhostlistFetchCallback, this); Storage()->ListDirectoryInfo(IStorage::TYPE_ALL, m_pClient->m_Ghost.GetGhostDir(), GhostlistFetchCallback, this);
std::sort(m_vGhosts.begin(), m_vGhosts.end()); SortGhostlist();
CGhostItem *pOwnGhost = 0; CGhostItem *pOwnGhost = 0;
for(auto &Ghost : m_vGhosts) for(auto &Ghost : m_vGhosts)
@ -1000,6 +1000,7 @@ void CMenus::UpdateOwnGhost(CGhostItem Item)
Item.m_Date = std::time(0); Item.m_Date = std::time(0);
Item.m_Failed = false; Item.m_Failed = false;
m_vGhosts.insert(std::lower_bound(m_vGhosts.begin(), m_vGhosts.end(), Item), Item); m_vGhosts.insert(std::lower_bound(m_vGhosts.begin(), m_vGhosts.end(), Item), Item);
SortGhostlist();
} }
void CMenus::DeleteGhostItem(int Index) void CMenus::DeleteGhostItem(int Index)
@ -1009,6 +1010,22 @@ void CMenus::DeleteGhostItem(int Index)
m_vGhosts.erase(m_vGhosts.begin() + Index); m_vGhosts.erase(m_vGhosts.begin() + Index);
} }
void CMenus::SortGhostlist()
{
if(g_Config.m_GhSort == GHOST_SORT_NAME)
std::stable_sort(m_vGhosts.begin(), m_vGhosts.end(), [](const CGhostItem &Left, const CGhostItem &Right) {
return g_Config.m_GhSortOrder ? (str_comp(Left.m_aPlayer, Right.m_aPlayer) > 0) : (str_comp(Left.m_aPlayer, Right.m_aPlayer) < 0);
});
else if(g_Config.m_GhSort == GHOST_SORT_TIME)
std::stable_sort(m_vGhosts.begin(), m_vGhosts.end(), [](const CGhostItem &Left, const CGhostItem &Right) {
return g_Config.m_GhSortOrder ? (Left.m_Time > Right.m_Time) : (Left.m_Time < Right.m_Time);
});
else if(g_Config.m_GhSort == GHOST_SORT_DATE)
std::stable_sort(m_vGhosts.begin(), m_vGhosts.end(), [](const CGhostItem &Left, const CGhostItem &Right) {
return g_Config.m_GhSortOrder ? (Left.m_Date > Right.m_Date) : (Left.m_Date < Right.m_Date);
});
}
void CMenus::RenderGhost(CUIRect MainView) void CMenus::RenderGhost(CUIRect MainView)
{ {
// render background // render background
@ -1029,10 +1046,12 @@ void CMenus::RenderGhost(CUIRect MainView)
Headers.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_T, 5.0f); Headers.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_T, 5.0f);
Headers.VSplitRight(20.0f, &Headers, 0); Headers.VSplitRight(20.0f, &Headers, 0);
struct CColumn class CColumn
{ {
public:
const char *m_pCaption; const char *m_pCaption;
int m_Id; int m_Id;
int m_Sort;
float m_Width; float m_Width;
CUIRect m_Rect; CUIRect m_Rect;
}; };
@ -1046,11 +1065,11 @@ void CMenus::RenderGhost(CUIRect MainView)
}; };
static CColumn s_aCols[] = { static CColumn s_aCols[] = {
{"", -1, 2.0f, {0}}, {"", -1, GHOST_SORT_NONE, 2.0f, {0}},
{"", COL_ACTIVE, 30.0f, {0}}, {"", COL_ACTIVE, GHOST_SORT_NONE, 30.0f, {0}},
{Localizable("Name"), COL_NAME, 200.0f, {0}}, {Localizable("Name"), COL_NAME, GHOST_SORT_NAME, 200.0f, {0}},
{Localizable("Time"), COL_TIME, 90.0f, {0}}, {Localizable("Time"), COL_TIME, GHOST_SORT_TIME, 90.0f, {0}},
{Localizable("Date"), COL_DATE, 150.0f, {0}}, {Localizable("Date"), COL_DATE, GHOST_SORT_DATE, 150.0f, {0}},
}; };
int NumCols = std::size(s_aCols); int NumCols = std::size(s_aCols);
@ -1065,8 +1084,22 @@ void CMenus::RenderGhost(CUIRect MainView)
} }
// do headers // do headers
for(int i = 0; i < NumCols; i++) for(const auto &Col : s_aCols)
DoButton_GridHeader(&s_aCols[i].m_Id, Localize(s_aCols[i].m_pCaption), 0, &s_aCols[i].m_Rect); {
if(DoButton_GridHeader(&Col.m_Id, Localize(Col.m_pCaption), g_Config.m_GhSort == Col.m_Sort, &Col.m_Rect))
{
if(Col.m_Sort != GHOST_SORT_NONE)
{
if(g_Config.m_GhSort == Col.m_Sort)
g_Config.m_GhSortOrder ^= 1;
else
g_Config.m_GhSortOrder = 0;
g_Config.m_GhSort = Col.m_Sort;
SortGhostlist();
}
}
}
View.Draw(ColorRGBA(0, 0, 0, 0.15f), 0, 0); View.Draw(ColorRGBA(0, 0, 0, 0.15f), 0, 0);

View file

@ -3310,15 +3310,15 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
} }
} }
else if(State >= IUpdater::GETTING_MANIFEST && State < IUpdater::NEED_RESTART) 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) 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; m_NeedRestartUpdate = true;
} }
else 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); UpdaterRect.VSplitLeft(TextRender()->TextWidth(14.0f, aBuf, -1, -1.0f) + 10.0f, &UpdaterRect, &Button);
Button.VSplitLeft(100.0f, &Button, nullptr); Button.VSplitLeft(100.0f, &Button, nullptr);
static CButtonContainer s_ButtonUpdate; static CButtonContainer s_ButtonUpdate;

View file

@ -52,10 +52,12 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
MainView.HSplitBottom(40.0f, &MainView, &BottomView); MainView.HSplitBottom(40.0f, &MainView, &BottomView);
BottomView.HSplitTop(20.f, 0, &BottomView); BottomView.HSplitTop(20.f, 0, &BottomView);
CUIRect QuickSearch, Buttons; CUIRect QuickSearch, DirectoryButton, Buttons;
CUIRect ButtonLeft, ButtonMiddle, ButtonRight; CUIRect ButtonLeft, ButtonMiddle, ButtonRight;
BottomView.VSplitMid(&QuickSearch, &Buttons); BottomView.VSplitMid(&QuickSearch, &Buttons, 10.0f);
QuickSearch.VSplitLeft(240.0f, &QuickSearch, &DirectoryButton);
QuickSearch.VSplitRight(10.0f, &QuickSearch, nullptr);
const float ButtonSize = Buttons.w / 3; const float ButtonSize = Buttons.w / 3;
Buttons.VSplitLeft(ButtonSize, &ButtonLeft, &Buttons); Buttons.VSplitLeft(ButtonSize, &ButtonLeft, &Buttons);
@ -237,6 +239,14 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
// bottom buttons // bottom buttons
if(s_CustomSkinMenu) if(s_CustomSkinMenu)
{ {
static CButtonContainer s_CustomSkinSaveButton;
if(DoButton_Menu(&s_CustomSkinSaveButton, Localize("Save"), 0, &ButtonLeft))
{
m_Popup = POPUP_SAVE_SKIN;
m_SkinNameInput.SelectAll();
Ui()->SetActiveItem(&m_SkinNameInput);
}
static CButtonContainer s_RandomizeSkinButton; static CButtonContainer s_RandomizeSkinButton;
if(DoButton_Menu(&s_RandomizeSkinButton, Localize("Randomize"), 0, &ButtonMiddle)) if(DoButton_Menu(&s_RandomizeSkinButton, Localize("Randomize"), 0, &ButtonMiddle))
{ {
@ -251,7 +261,7 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
if(DoButton_Menu(&s_CustomSkinDeleteButton, Localize("Delete"), 0, &ButtonMiddle) || Ui()->ConsumeHotkey(CUi::HOTKEY_DELETE)) if(DoButton_Menu(&s_CustomSkinDeleteButton, Localize("Delete"), 0, &ButtonMiddle) || Ui()->ConsumeHotkey(CUi::HOTKEY_DELETE))
{ {
char aBuf[128]; char aBuf[128];
str_format(aBuf, sizeof(aBuf), Localize("Are you sure that you want to delete the skin '%s'?"), m_pSelectedSkin->m_aName); str_format(aBuf, sizeof(aBuf), Localize("Are you sure that you want to delete '%s'?"), m_pSelectedSkin->m_aName);
PopupConfirm(Localize("Delete skin"), aBuf, Localize("Yes"), Localize("No"), &CMenus::PopupConfirmDeleteSkin7); PopupConfirm(Localize("Delete skin"), aBuf, Localize("Yes"), Localize("No"), &CMenus::PopupConfirmDeleteSkin7);
} }
} }
@ -291,6 +301,16 @@ void CMenus::RenderSettingsTee7(CUIRect MainView)
if(Ui()->DoClearableEditBox(&s_SkinFilterInput, &QuickSearch, 14.0f)) if(Ui()->DoClearableEditBox(&s_SkinFilterInput, &QuickSearch, 14.0f))
m_SkinListNeedsUpdate = true; m_SkinListNeedsUpdate = true;
} }
static CButtonContainer s_DirectoryButton;
if(DoButton_Menu(&s_DirectoryButton, Localize("Skins directory"), 0, &DirectoryButton))
{
char aBuf[128 + IO_MAX_PATH_LENGTH];
Storage()->GetCompletePath(IStorage::TYPE_SAVE, "skins7", aBuf, sizeof(aBuf));
Storage()->CreateFolder("skins7", IStorage::TYPE_SAVE);
Client()->ViewFile(aBuf);
}
GameClient()->m_Tooltips.DoToolTip(&s_DirectoryButton, &DirectoryButton, Localize("Open the directory to add custom skins"));
} }
void CMenus::PopupConfirmDeleteSkin7() void CMenus::PopupConfirmDeleteSkin7()
@ -299,7 +319,7 @@ void CMenus::PopupConfirmDeleteSkin7()
if(!m_pClient->m_Skins7.RemoveSkin(m_pSelectedSkin)) if(!m_pClient->m_Skins7.RemoveSkin(m_pSelectedSkin))
{ {
PopupMessage(Localize("Error"), Localize("Unable to delete the skin"), Localize("Ok")); PopupMessage(Localize("Error"), Localize("Unable to delete skin"), Localize("Ok"));
return; return;
} }
m_pSelectedSkin = nullptr; m_pSelectedSkin = nullptr;

View file

@ -17,6 +17,8 @@
#include "menus.h" #include "menus.h"
using namespace FontIcons;
void CMenus::RenderStartMenu(CUIRect MainView) void CMenus::RenderStartMenu(CUIRect MainView)
{ {
GameClient()->m_MenuBackground.ChangePosition(CMenuBackground::POS_START); GameClient()->m_MenuBackground.ChangePosition(CMenuBackground::POS_START);
@ -186,13 +188,27 @@ void CMenus::RenderStartMenu(CUIRect MainView)
} }
// render version // render version
CUIRect VersionUpdate, CurVersion; CUIRect CurVersion, ConsoleButton;
MainView.HSplitBottom(20.0f, nullptr, &VersionUpdate); MainView.HSplitBottom(45.0f, nullptr, &CurVersion);
VersionUpdate.VSplitRight(50.0f, &CurVersion, nullptr); CurVersion.VSplitRight(40.0f, &CurVersion, nullptr);
VersionUpdate.VMargin(VMargin, &VersionUpdate); 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); 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) #if defined(CONF_AUTOUPDATE)
CUIRect UpdateButton; CUIRect UpdateButton;
VersionUpdate.VSplitRight(100.0f, &VersionUpdate, &UpdateButton); VersionUpdate.VSplitRight(100.0f, &VersionUpdate, &UpdateButton);
@ -240,12 +256,12 @@ void CMenus::RenderStartMenu(CUIRect MainView)
} }
else if(State == IUpdater::FAIL) 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); TextRender()->TextColor(1.0f, 0.4f, 0.4f, 1.0f);
} }
else if(State == IUpdater::NEED_RESTART) 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); TextRender()->TextColor(1.0f, 0.4f, 0.4f, 1.0f);
} }
Ui()->DoLabel(&VersionUpdate, aBuf, 14.0f, TEXTALIGN_ML); Ui()->DoLabel(&VersionUpdate, aBuf, 14.0f, TEXTALIGN_ML);

View file

@ -349,240 +349,247 @@ void CScoreboard::RenderScoreboard(CUIRect Scoreboard, int Team, int CountStart,
char aBuf[64]; char aBuf[64];
int MaxTeamSize = m_pClient->Config()->m_SvMaxTeamSize; 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 for(int i = 0; i < MAX_CLIENTS; i++)
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++)
{ {
const CNetObj_PlayerInfo *pInfoNext = GameClient()->m_Snap.m_apInfoByDDTeamScore[j]; // make sure that we render the correct team
if(!pInfoNext || pInfoNext->m_Team != Team) const CNetObj_PlayerInfo *pInfo = GameClient()->m_Snap.m_apInfoByDDTeamScore[i];
if(!pInfo || pInfo->m_Team != Team)
continue; continue;
NextDDTeam = GameClient()->m_Teams.Team(pInfoNext->m_ClientId); if(CountRendered++ < CountStart)
break; continue;
}
if(PrevDDTeam == -1) int DDTeam = GameClient()->m_Teams.Team(pInfo->m_ClientId);
{ int NextDDTeam = 0;
for(int j = i - 1; j >= 0; j--) 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]; const CNetObj_PlayerInfo *pInfoNext = GameClient()->m_Snap.m_apInfoByDDTeamScore[j];
if(!pInfoPrev || pInfoPrev->m_Team != Team) if(!pInfoNext || pInfoNext->m_Team != Team)
continue; continue;
PrevDDTeam = GameClient()->m_Teams.Team(pInfoPrev->m_ClientId); NextDDTeam = GameClient()->m_Teams.Team(pInfoNext->m_ClientId);
break; break;
} }
}
CUIRect RowAndSpacing, Row; if(PrevDDTeam == -1)
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; for(int j = i - 1; j >= 0; j--)
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) const CNetObj_PlayerInfo *pInfoPrev = GameClient()->m_Snap.m_apInfoByDDTeamScore[j];
str_copy(aBuf, Localize("Super")); if(!pInfoPrev || pInfoPrev->m_Team != Team)
else if(CurrentDDTeamSize <= 1) continue;
str_format(aBuf, sizeof(aBuf), "%d", DDTeam);
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 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 else
{ {
if(DDTeam == TEAM_SUPER) // 0.7 uses milliseconds and ddnets str_time wants centiseconds
str_copy(aBuf, Localize("Super")); // 0.7 servers can also send the amount of precision the client should use
else if(CurrentDDTeamSize > 1) // we ignore that and always show 3 digit precision
str_format(aBuf, sizeof(aBuf), Localize("Team %d (%d/%d)"), DDTeam, CurrentDDTeamSize, MaxTeamSize); str_time((int64_t)absolute(pInfo->m_Score / 10), TIME_MINS_CENTISECS, aBuf, sizeof(aBuf));
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;
} }
} else if(TimeScore)
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'; if(pInfo->m_Score == -9999)
{
aBuf[0] = '\0';
}
else
{
str_time((int64_t)absolute(pInfo->m_Score) * 100, TIME_HOURS, aBuf, sizeof(aBuf));
}
} }
else else
{ {
// 0.7 uses milliseconds and ddnets str_time wants centiseconds str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Score, -999, 99999));
// 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));
} }
} TextRender()->Text(ScoreOffset + ScoreLength - TextRender()->TextWidth(FontSize, aBuf), Row.y + (Row.h - FontSize) / 2.0f, FontSize, aBuf);
else if(TimeScore)
{ // CTF flag
if(pInfo->m_Score == -9999) 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 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 // name
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)
{ {
ColorRGBA Color = m_pClient->m_Skins7.GetTeamColor(true, 0, m_pClient->m_aClients[pInfo->m_ClientId].m_Team, protocol7::SKINPART_BODY); CTextCursor Cursor;
Graphics()->SetColor(Color.r, Color.g, Color.b, Color.a); 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 // clan
{
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(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 // country flag
if(Client()->IsSixup() && Client()->m_TranslationContext.m_aClients[pInfo->m_ClientId].m_PlayerFlags7 & protocol7::PLAYERFLAG_READY) 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);
TextRender()->TextColor(0.1f, 1.0f, 0.1f, TextColor.a);
TextRender()->TextEx(&Cursor, "");
}
}
// clan // ping
{ if(g_Config.m_ClEnablePingColor)
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))); TextRender()->TextColor(color_cast<ColorRGBA>(ColorHSLA((300.0f - clamp(pInfo->m_Latency, 0, 300)) / 1000.0f, 1.0f, 0.5f)));
} }
else else
{ {
TextRender()->TextColor(TextColor); TextRender()->TextColor(TextRender()->DefaultTextColor());
} }
CTextCursor Cursor; str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Latency, 0, 999));
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); TextRender()->Text(PingOffset + PingLength - TextRender()->TextWidth(FontSize, aBuf), Row.y + (Row.h - FontSize) / 2.0f, FontSize, aBuf);
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
{
TextRender()->TextColor(TextRender()->DefaultTextColor()); 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) if(CountRendered == CountEnd)
break; break;
}
} }
} }
@ -790,15 +797,16 @@ bool CScoreboard::Active() const
if(m_Active) if(m_Active)
return true; return true;
const CNetObj_GameInfo *pGameInfoObj = GameClient()->m_Snap.m_pGameInfoObj;
if(GameClient()->m_Snap.m_pLocalInfo && !GameClient()->m_Snap.m_SpecInfo.m_Active) if(GameClient()->m_Snap.m_pLocalInfo && !GameClient()->m_Snap.m_SpecInfo.m_Active)
{ {
// we are not a spectator, check if we are dead // 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) if(!GameClient()->m_Snap.m_pLocalCharacter && g_Config.m_ClScoreboardOnDeath &&
!(pGameInfoObj && pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_PAUSED))
return true; return true;
} }
// if the game is over // if the game is over
const CNetObj_GameInfo *pGameInfoObj = GameClient()->m_Snap.m_pGameInfoObj;
if(pGameInfoObj && pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_GAMEOVER) if(pGameInfoObj && pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_GAMEOVER)
return true; return true;

View file

@ -18,7 +18,7 @@
#include "menus.h" #include "menus.h"
#include "skins7.h" #include "skins7.h"
const char *const CSkins7::ms_apSkinPartNames[protocol7::NUM_SKINPARTS] = {"body", "marking", "decoration", "hands", "feet", "eyes"}; /* Localize("body","skins");Localize("marking","skins");Localize("decoration","skins");Localize("hands","skins");Localize("feet","skins");Localize("eyes","skins"); */ const char *const CSkins7::ms_apSkinPartNames[protocol7::NUM_SKINPARTS] = {"body", "marking", "decoration", "hands", "feet", "eyes"};
const char *const CSkins7::ms_apColorComponents[NUM_COLOR_COMPONENTS] = {"hue", "sat", "lgt", "alp"}; const char *const CSkins7::ms_apColorComponents[NUM_COLOR_COMPONENTS] = {"hue", "sat", "lgt", "alp"};
char *CSkins7::ms_apSkinVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS] = {{0}}; char *CSkins7::ms_apSkinVariables[NUM_DUMMIES][protocol7::NUM_SKINPARTS] = {{0}};
@ -525,7 +525,7 @@ bool CSkins7::ValidateSkinParts(char *apPartNames[protocol7::NUM_SKINPARTS], int
bool CSkins7::SaveSkinfile(const char *pSaveSkinName, int Dummy) bool CSkins7::SaveSkinfile(const char *pSaveSkinName, int Dummy)
{ {
char aBuf[IO_MAX_PATH_LENGTH]; char aBuf[IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), "skins/%s.json", pSaveSkinName); str_format(aBuf, sizeof(aBuf), SKINS_DIR "/%s.json", pSaveSkinName);
IOHANDLE File = Storage()->OpenFile(aBuf, IOFLAG_WRITE, IStorage::TYPE_SAVE); IOHANDLE File = Storage()->OpenFile(aBuf, IOFLAG_WRITE, IStorage::TYPE_SAVE);
if(!File) if(!File)
return false; return false;
@ -547,7 +547,7 @@ bool CSkins7::SaveSkinfile(const char *pSaveSkinName, int Dummy)
Writer.WriteAttribute("filename"); Writer.WriteAttribute("filename");
Writer.WriteStrValue(ms_apSkinVariables[Dummy][PartIndex]); Writer.WriteStrValue(ms_apSkinVariables[Dummy][PartIndex]);
const bool CustomColors = *ms_apUCCVariables[PartIndex]; const bool CustomColors = *ms_apUCCVariables[Dummy][PartIndex];
Writer.WriteAttribute("custom_colors"); Writer.WriteAttribute("custom_colors");
Writer.WriteBoolValue(CustomColors); Writer.WriteBoolValue(CustomColors);

View file

@ -191,6 +191,26 @@ void CGameClient::OnConsoleInit()
Console()->Chain("player_color_feet", ConchainSpecialInfoupdate, this); Console()->Chain("player_color_feet", ConchainSpecialInfoupdate, this);
Console()->Chain("player_skin", ConchainSpecialInfoupdate, this); Console()->Chain("player_skin", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_skin", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_skin_body", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_skin_marking", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_skin_decoration", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_skin_hands", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_skin_feet", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_skin_eyes", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_color_body", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_color_marking", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_color_decoration", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_color_hands", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_color_feet", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_color_eyes", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_use_custom_color_body", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_use_custom_color_marking", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_use_custom_color_decoration", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_use_custom_color_hands", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_use_custom_color_feet", ConchainSpecialInfoupdate, this);
Console()->Chain("player7_use_custom_color_eyes", ConchainSpecialInfoupdate, this);
Console()->Chain("dummy_name", ConchainSpecialDummyInfoupdate, this); Console()->Chain("dummy_name", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy_clan", ConchainSpecialDummyInfoupdate, this); Console()->Chain("dummy_clan", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy_country", ConchainSpecialDummyInfoupdate, this); Console()->Chain("dummy_country", ConchainSpecialDummyInfoupdate, this);
@ -199,6 +219,26 @@ void CGameClient::OnConsoleInit()
Console()->Chain("dummy_color_feet", ConchainSpecialDummyInfoupdate, this); Console()->Chain("dummy_color_feet", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy_skin", ConchainSpecialDummyInfoupdate, this); Console()->Chain("dummy_skin", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_skin", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_skin_body", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_skin_marking", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_skin_decoration", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_skin_hands", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_skin_feet", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_skin_eyes", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_color_body", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_color_marking", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_color_decoration", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_color_hands", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_color_feet", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_color_eyes", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_use_custom_color_body", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_use_custom_color_marking", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_use_custom_color_decoration", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_use_custom_color_hands", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_use_custom_color_feet", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy7_use_custom_color_eyes", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("cl_skin_download_url", ConchainRefreshSkins, this); Console()->Chain("cl_skin_download_url", ConchainRefreshSkins, this);
Console()->Chain("cl_skin_community_download_url", ConchainRefreshSkins, this); Console()->Chain("cl_skin_community_download_url", ConchainRefreshSkins, this);
Console()->Chain("cl_download_skins", ConchainRefreshSkins, this); Console()->Chain("cl_download_skins", ConchainRefreshSkins, this);
@ -2460,7 +2500,7 @@ void CGameClient::SendSwitchTeam(int Team)
void CGameClient::SendStartInfo7(bool Dummy) const void CGameClient::SendStartInfo7(bool Dummy) const
{ {
protocol7::CNetMsg_Cl_StartInfo Msg; 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_pClan = Dummy ? Config()->m_ClDummyClan : Config()->m_PlayerClan;
Msg.m_Country = Dummy ? Config()->m_ClDummyCountry : Config()->m_PlayerCountry; Msg.m_Country = Dummy ? Config()->m_ClDummyCountry : Config()->m_PlayerCountry;
for(int p = 0; p < protocol7::NUM_SKINPARTS; p++) for(int p = 0; p < protocol7::NUM_SKINPARTS; p++)

View file

@ -49,10 +49,10 @@ struct CGameMsg7
}; };
static CGameMsg7 gs_GameMsgList7[protocol7::NUM_GAMEMSGS] = { static CGameMsg7 gs_GameMsgList7[protocol7::NUM_GAMEMSGS] = {
{/*GAMEMSG_TEAM_SWAP*/ DO_CHAT, PARA_NONE, "Teams were swapped"}, // Localize("Teams were swapped") {/*GAMEMSG_TEAM_SWAP*/ DO_CHAT, PARA_NONE, "Teams were swapped"},
{/*GAMEMSG_SPEC_INVALIDID*/ DO_CHAT, PARA_NONE, "Invalid spectator id used"}, //! {/*GAMEMSG_SPEC_INVALIDID*/ DO_CHAT, PARA_NONE, "Invalid spectator id used"}, //!
{/*GAMEMSG_TEAM_SHUFFLE*/ DO_CHAT, PARA_NONE, "Teams were shuffled"}, // Localize("Teams were shuffled") {/*GAMEMSG_TEAM_SHUFFLE*/ DO_CHAT, PARA_NONE, "Teams were shuffled"},
{/*GAMEMSG_TEAM_BALANCE*/ DO_CHAT, PARA_NONE, "Teams have been balanced"}, // Localize("Teams have been balanced") {/*GAMEMSG_TEAM_BALANCE*/ DO_CHAT, PARA_NONE, "Teams have been balanced"},
{/*GAMEMSG_CTF_DROP*/ DO_SPECIAL, PARA_NONE, ""}, // special - play ctf drop sound {/*GAMEMSG_CTF_DROP*/ DO_SPECIAL, PARA_NONE, ""}, // special - play ctf drop sound
{/*GAMEMSG_CTF_RETURN*/ DO_SPECIAL, PARA_NONE, ""}, // special - play ctf return sound {/*GAMEMSG_CTF_RETURN*/ DO_SPECIAL, PARA_NONE, ""}, // special - play ctf return sound
@ -70,10 +70,10 @@ void CGameClient::DoTeamChangeMessage7(const char *pName, int ClientId, int Team
char aBuf[128]; char aBuf[128];
switch(GetStrTeam7(Team, m_pClient->m_TranslationContext.m_GameFlags & protocol7::GAMEFLAG_TEAMS)) switch(GetStrTeam7(Team, m_pClient->m_TranslationContext.m_GameFlags & protocol7::GAMEFLAG_TEAMS))
{ {
case STR_TEAM_GAME: str_format(aBuf, sizeof(aBuf), Localize("'%s' %sjoined the game"), pName, pPrefix); break; case STR_TEAM_GAME: str_format(aBuf, sizeof(aBuf), "'%s' %sjoined the game", pName, pPrefix); break;
case STR_TEAM_RED: str_format(aBuf, sizeof(aBuf), Localize("'%s' %sjoined the red team"), pName, pPrefix); break; case STR_TEAM_RED: str_format(aBuf, sizeof(aBuf), "'%s' %sjoined the red team", pName, pPrefix); break;
case STR_TEAM_BLUE: str_format(aBuf, sizeof(aBuf), Localize("'%s' %sjoined the blue team"), pName, pPrefix); break; case STR_TEAM_BLUE: str_format(aBuf, sizeof(aBuf), "'%s' %sjoined the blue team", pName, pPrefix); break;
case STR_TEAM_SPECTATORS: str_format(aBuf, sizeof(aBuf), Localize("'%s' %sjoined the spectators"), pName, pPrefix); break; case STR_TEAM_SPECTATORS: str_format(aBuf, sizeof(aBuf), "'%s' %sjoined the spectators", pName, pPrefix); break;
} }
m_Chat.AddLine(-1, 0, aBuf); m_Chat.AddLine(-1, 0, aBuf);
} }
@ -233,9 +233,9 @@ void *CGameClient::TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn)
protocol7::CNetMsg_Sv_ServerSettings *pMsg = (protocol7::CNetMsg_Sv_ServerSettings *)pRawMsg; protocol7::CNetMsg_Sv_ServerSettings *pMsg = (protocol7::CNetMsg_Sv_ServerSettings *)pRawMsg;
if(!m_pClient->m_TranslationContext.m_ServerSettings.m_TeamLock && pMsg->m_TeamLock) if(!m_pClient->m_TranslationContext.m_ServerSettings.m_TeamLock && pMsg->m_TeamLock)
m_Chat.AddLine(-1, 0, Localize("Teams were locked")); m_Chat.AddLine(-1, 0, "Teams were locked");
else if(m_pClient->m_TranslationContext.m_ServerSettings.m_TeamLock && !pMsg->m_TeamLock) else if(m_pClient->m_TranslationContext.m_ServerSettings.m_TeamLock && !pMsg->m_TeamLock)
m_Chat.AddLine(-1, 0, Localize("Teams were unlocked")); m_Chat.AddLine(-1, 0, "Teams were unlocked");
m_pClient->m_TranslationContext.m_ServerSettings.m_KickVote = pMsg->m_KickVote; m_pClient->m_TranslationContext.m_ServerSettings.m_KickVote = pMsg->m_KickVote;
m_pClient->m_TranslationContext.m_ServerSettings.m_KickMin = pMsg->m_KickMin; m_pClient->m_TranslationContext.m_ServerSettings.m_KickMin = pMsg->m_KickMin;
@ -387,18 +387,18 @@ void *CGameClient::TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn)
switch(pMsg7->m_Type) switch(pMsg7->m_Type)
{ {
case protocol7::VOTE_START_OP: case protocol7::VOTE_START_OP:
str_format(aBuf, sizeof(aBuf), Localize("'%s' called vote to change server option '%s' (%s)"), pName, pMsg7->m_pDescription, pMsg7->m_pReason); str_format(aBuf, sizeof(aBuf), "'%s' called vote to change server option '%s' (%s)", pName, pMsg7->m_pDescription, pMsg7->m_pReason);
m_Chat.AddLine(-1, 0, aBuf); m_Chat.AddLine(-1, 0, aBuf);
break; break;
case protocol7::VOTE_START_KICK: case protocol7::VOTE_START_KICK:
{ {
str_format(aBuf, sizeof(aBuf), Localize("'%s' called for vote to kick '%s' (%s)"), pName, pMsg7->m_pDescription, pMsg7->m_pReason); str_format(aBuf, sizeof(aBuf), "'%s' called for vote to kick '%s' (%s)", pName, pMsg7->m_pDescription, pMsg7->m_pReason);
m_Chat.AddLine(-1, 0, aBuf); m_Chat.AddLine(-1, 0, aBuf);
break; break;
} }
case protocol7::VOTE_START_SPEC: case protocol7::VOTE_START_SPEC:
{ {
str_format(aBuf, sizeof(aBuf), Localize("'%s' called for vote to move '%s' to spectators (%s)"), pName, pMsg7->m_pDescription, pMsg7->m_pReason); str_format(aBuf, sizeof(aBuf), "'%s' called for vote to move '%s' to spectators (%s)", pName, pMsg7->m_pDescription, pMsg7->m_pReason);
m_Chat.AddLine(-1, 0, aBuf); m_Chat.AddLine(-1, 0, aBuf);
} }
} }
@ -409,24 +409,24 @@ void *CGameClient::TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn)
switch(pMsg7->m_Type) switch(pMsg7->m_Type)
{ {
case protocol7::VOTE_START_OP: case protocol7::VOTE_START_OP:
str_format(aBuf, sizeof(aBuf), Localize("Admin forced server option '%s' (%s)"), pMsg7->m_pDescription, pMsg7->m_pReason); str_format(aBuf, sizeof(aBuf), "Admin forced server option '%s' (%s)", pMsg7->m_pDescription, pMsg7->m_pReason);
m_Chat.AddLine(-1, 0, aBuf); m_Chat.AddLine(-1, 0, aBuf);
break; break;
case protocol7::VOTE_START_SPEC: case protocol7::VOTE_START_SPEC:
str_format(aBuf, sizeof(aBuf), Localize("Admin moved '%s' to spectator (%s)"), pMsg7->m_pDescription, pMsg7->m_pReason); str_format(aBuf, sizeof(aBuf), "Admin moved '%s' to spectator (%s)", pMsg7->m_pDescription, pMsg7->m_pReason);
m_Chat.AddLine(-1, 0, aBuf); m_Chat.AddLine(-1, 0, aBuf);
break; break;
case protocol7::VOTE_END_ABORT: case protocol7::VOTE_END_ABORT:
m_Voting.OnReset(); m_Voting.OnReset();
m_Chat.AddLine(-1, 0, Localize("Vote aborted")); m_Chat.AddLine(-1, 0, "Vote aborted");
break; break;
case protocol7::VOTE_END_PASS: case protocol7::VOTE_END_PASS:
m_Voting.OnReset(); m_Voting.OnReset();
m_Chat.AddLine(-1, 0, pMsg7->m_ClientId == -1 ? Localize("Admin forced vote yes") : Localize("Vote passed")); m_Chat.AddLine(-1, 0, pMsg7->m_ClientId == -1 ? "Admin forced vote yes" : "Vote passed");
break; break;
case protocol7::VOTE_END_FAIL: case protocol7::VOTE_END_FAIL:
m_Voting.OnReset(); m_Voting.OnReset();
m_Chat.AddLine(-1, 0, pMsg7->m_ClientId == -1 ? Localize("Admin forced vote no") : Localize("Vote failed")); m_Chat.AddLine(-1, 0, pMsg7->m_ClientId == -1 ? "Admin forced vote no" : "Vote failed");
} }
} }
@ -630,10 +630,10 @@ void *CGameClient::TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn)
const char *pMsg = ""; const char *pMsg = "";
switch(GetStrTeam7(aParaI[0], TeamPlay)) switch(GetStrTeam7(aParaI[0], TeamPlay))
{ {
case STR_TEAM_GAME: pMsg = Localize("All players were moved to the game"); break; case STR_TEAM_GAME: pMsg = "All players were moved to the game"; break;
case STR_TEAM_RED: pMsg = Localize("All players were moved to the red team"); break; case STR_TEAM_RED: pMsg = "All players were moved to the red team"; break;
case STR_TEAM_BLUE: pMsg = Localize("All players were moved to the blue team"); break; case STR_TEAM_BLUE: pMsg = "All players were moved to the blue team"; break;
case STR_TEAM_SPECTATORS: pMsg = Localize("All players were moved to the spectators"); break; case STR_TEAM_SPECTATORS: pMsg = "All players were moved to the spectators"; break;
} }
m_Broadcast.DoBroadcast(pMsg); // client side broadcast m_Broadcast.DoBroadcast(pMsg); // client side broadcast
} }
@ -643,8 +643,8 @@ void *CGameClient::TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn)
const char *pMsg = ""; const char *pMsg = "";
switch(GetStrTeam7(aParaI[0], TeamPlay)) switch(GetStrTeam7(aParaI[0], TeamPlay))
{ {
case STR_TEAM_RED: pMsg = Localize("You were moved to the red team due to team balancing"); break; case STR_TEAM_RED: pMsg = "You were moved to the red team due to team balancing"; break;
case STR_TEAM_BLUE: pMsg = Localize("You were moved to the blue team due to team balancing"); break; case STR_TEAM_BLUE: pMsg = "You were moved to the blue team due to team balancing"; break;
} }
m_Broadcast.DoBroadcast(pMsg); // client side broadcast m_Broadcast.DoBroadcast(pMsg); // client side broadcast
} }
@ -655,7 +655,7 @@ void *CGameClient::TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn)
case protocol7::GAMEMSG_GAME_PAUSED: case protocol7::GAMEMSG_GAME_PAUSED:
{ {
int ClientId = clamp(aParaI[0], 0, MAX_CLIENTS - 1); int ClientId = clamp(aParaI[0], 0, MAX_CLIENTS - 1);
str_format(aBuf, sizeof(aBuf), Localize("'%s' initiated a pause"), m_aClients[ClientId].m_aName); str_format(aBuf, sizeof(aBuf), "'%s' initiated a pause", m_aClients[ClientId].m_aName);
SendChat(aBuf); SendChat(aBuf);
} }
break; break;
@ -669,22 +669,22 @@ void *CGameClient::TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn)
{ {
if(aParaI[0]) if(aParaI[0])
{ {
str_format(aBuf, sizeof(aBuf), Localize("The blue flag was captured by '%s' (%.2f seconds)"), m_aClients[ClientId].m_aName, Time); str_format(aBuf, sizeof(aBuf), "The blue flag was captured by '%s' (%.2f seconds)", m_aClients[ClientId].m_aName, Time);
} }
else else
{ {
str_format(aBuf, sizeof(aBuf), Localize("The red flag was captured by '%s' (%.2f seconds)"), m_aClients[ClientId].m_aName, Time); str_format(aBuf, sizeof(aBuf), "The red flag was captured by '%s' (%.2f seconds)", m_aClients[ClientId].m_aName, Time);
} }
} }
else else
{ {
if(aParaI[0]) if(aParaI[0])
{ {
str_format(aBuf, sizeof(aBuf), Localize("The blue flag was captured by '%s'"), m_aClients[ClientId].m_aName); str_format(aBuf, sizeof(aBuf), "The blue flag was captured by '%s'", m_aClients[ClientId].m_aName);
} }
else else
{ {
str_format(aBuf, sizeof(aBuf), Localize("The red flag was captured by '%s'"), m_aClients[ClientId].m_aName); str_format(aBuf, sizeof(aBuf), "The red flag was captured by '%s'", m_aClients[ClientId].m_aName);
} }
} }
SendChat(aBuf); SendChat(aBuf);
@ -696,7 +696,7 @@ void *CGameClient::TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn)
const char *pText = ""; const char *pText = "";
if(NumParaI == 0) if(NumParaI == 0)
{ {
pText = Localize(gs_GameMsgList7[GameMsgId].m_pText); pText = gs_GameMsgList7[GameMsgId].m_pText;
} }
// handle message // handle message

View file

@ -1332,9 +1332,18 @@ float CUi::DoScrollbarH(const void *pId, const CUIRect *pRect, float Current, co
Rail.VSplitLeft(pColorInner ? 8.0f : clamp(33.0f, Rail.h, Rail.w / 3.0f), &Handle, 0); Rail.VSplitLeft(pColorInner ? 8.0f : clamp(33.0f, Rail.h, Rail.w / 3.0f), &Handle, 0);
Handle.x += (Rail.w - Handle.w) * Current; Handle.x += (Rail.w - Handle.w) * Current;
CUIRect HandleArea = Handle;
if(!pColorInner)
{
HandleArea.h = pRect->h * 0.9f;
HandleArea.y = pRect->y + pRect->h * 0.05f;
HandleArea.w += 6.0f;
HandleArea.x -= 3.0f;
}
// logic // logic
const bool InsideRail = MouseHovered(&Rail); const bool InsideRail = MouseHovered(&Rail);
const bool InsideHandle = MouseHovered(&Handle); const bool InsideHandle = MouseHovered(&HandleArea);
bool Grabbed = false; // whether to apply the offset bool Grabbed = false; // whether to apply the offset
if(CheckActiveItem(pId)) if(CheckActiveItem(pId))
@ -1366,6 +1375,12 @@ float CUi::DoScrollbarH(const void *pId, const CUIRect *pRect, float Current, co
Grabbed = true; Grabbed = true;
} }
if(!pColorInner && (InsideHandle || Grabbed) && (CheckActiveItem(pId) || HotItem() == pId))
{
Handle.h += 3.0f;
Handle.y -= 1.5f;
}
if(InsideHandle && !MouseButton(0)) if(InsideHandle && !MouseButton(0))
{ {
SetHotItem(pId); SetHotItem(pId);
@ -1394,7 +1409,7 @@ float CUi::DoScrollbarH(const void *pId, const CUIRect *pRect, float Current, co
else else
{ {
Rail.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, Rail.h / 2.0f); Rail.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, Rail.h / 2.0f);
Handle.Draw(HandleColor, IGraphics::CORNER_ALL, Handle.h / 2.0f); Handle.Draw(HandleColor, IGraphics::CORNER_ALL, Rail.h / 2.0f);
} }
return ReturnValue; return ReturnValue;

View file

@ -1074,11 +1074,9 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
// proof button // proof button
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top); TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_ProofButton = 0; if(DoButton_Ex(&m_QuickActionProof, m_QuickActionProof.Label(), m_QuickActionProof.Active(), &Button, 0, m_QuickActionProof.Description(), IGraphics::CORNER_L))
if(DoButton_Ex(&s_ProofButton, "Proof", MapView()->ProofMode()->IsEnabled(), &Button, 0, "[ctrl+p] Toggles proof borders. These borders represent the area that a player can see with default zoom.", IGraphics::CORNER_L) ||
(m_Dialog == DIALOG_NONE && CLineInput::GetActiveInput() == nullptr && Input()->KeyPress(KEY_P) && ModPressed))
{ {
MapView()->ProofMode()->Toggle(); m_QuickActionProof.Call();
} }
TB_Top.VSplitLeft(14.0f, &Button, &TB_Top); TB_Top.VSplitLeft(14.0f, &Button, &TB_Top);
@ -1104,10 +1102,10 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
// grid button // grid button
TB_Top.VSplitLeft(25.0f, &Button, &TB_Top); TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
static int s_GridButton = 0; 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)) (m_Dialog == DIALOG_NONE && CLineInput::GetActiveInput() == nullptr && Input()->KeyPress(KEY_G) && ModPressed && !ShiftPressed))
{ {
MapView()->MapGrid()->Toggle(); m_QuickActionToggleGrid.Call();
} }
// grid settings button // grid settings button
@ -1123,23 +1121,23 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
// zoom group // zoom group
TB_Top.VSplitLeft(20.0f, &Button, &TB_Top); TB_Top.VSplitLeft(20.0f, &Button, &TB_Top);
static int s_ZoomOutButton = 0; 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); TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
static int s_ZoomNormalButton = 0; 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); TB_Top.VSplitLeft(20.0f, &Button, &TB_Top);
static int s_ZoomInButton = 0; 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); TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
@ -1230,10 +1228,10 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
static char s_PipetteButton; static char s_PipetteButton;
ColorPalette.VSplitLeft(PipetteButtonWidth, &Button, &ColorPalette); ColorPalette.VSplitLeft(PipetteButtonWidth, &Button, &ColorPalette);
ColorPalette.VSplitLeft(Spacing, nullptr, &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))) (CLineInput::GetActiveInput() == nullptr && ModPressed && ShiftPressed && Input()->KeyPress(KEY_C)))
{ {
m_ColorPipetteActive = !m_ColorPipetteActive; m_QuickActionPipette.Call();
} }
// Palette color pickers // Palette color pickers
@ -1254,10 +1252,9 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
// refocus button // refocus button
{ {
TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom); TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom);
static int s_RefocusButton = 0;
int FocusButtonChecked = MapView()->IsFocused() ? -1 : 1; int FocusButtonChecked = MapView()->IsFocused() ? -1 : 1;
if(DoButton_Editor(&s_RefocusButton, "Refocus", FocusButtonChecked, &Button, 0, "[HOME] Restore map focus") || (m_Dialog == DIALOG_NONE && CLineInput::GetActiveInput() == nullptr && Input()->KeyPress(KEY_HOME))) if(DoButton_Editor(&m_QuickActionRefocus, m_QuickActionRefocus.Label(), FocusButtonChecked, &Button, 0, m_QuickActionRefocus.Description()) || (m_Dialog == DIALOG_NONE && CLineInput::GetActiveInput() == nullptr && Input()->KeyPress(KEY_HOME)))
MapView()->Focus(); m_QuickActionRefocus.Call();
TB_Bottom.VSplitLeft(5.0f, nullptr, &TB_Bottom); TB_Bottom.VSplitLeft(5.0f, nullptr, &TB_Bottom);
} }
@ -4302,12 +4299,9 @@ void CEditor::RenderLayers(CUIRect LayersBox)
if(s_ScrollRegion.AddRect(AddGroupButton)) if(s_ScrollRegion.AddRect(AddGroupButton))
{ {
AddGroupButton.HSplitTop(RowHeight, &AddGroupButton, 0); AddGroupButton.HSplitTop(RowHeight, &AddGroupButton, 0);
static int s_AddGroupButton = 0; if(DoButton_Editor(&m_QuickActionAddGroup, m_QuickActionAddGroup.Label(), 0, &AddGroupButton, IGraphics::CORNER_R, m_QuickActionAddGroup.Description()))
if(DoButton_Editor(&s_AddGroupButton, "Add group", 0, &AddGroupButton, IGraphics::CORNER_R, "Adds a new group"))
{ {
m_Map.NewGroup(); m_QuickActionAddGroup.Call();
m_SelectedGroup = m_Map.m_vpGroups.size() - 1;
m_EditorHistory.RecordAction(std::make_shared<CEditorActionGroup>(this, m_SelectedGroup, false));
} }
} }
@ -4806,8 +4800,8 @@ void CEditor::RenderImagesList(CUIRect ToolBox)
{ {
AddImageButton.HSplitTop(5.0f, nullptr, &AddImageButton); AddImageButton.HSplitTop(5.0f, nullptr, &AddImageButton);
AddImageButton.HSplitTop(RowHeight, &AddImageButton, nullptr); AddImageButton.HSplitTop(RowHeight, &AddImageButton, nullptr);
if(DoButton_Editor(&s_AddImageButton, "Add", 0, &AddImageButton, 0, "Load a new image to use in the map")) if(DoButton_Editor(&s_AddImageButton, m_QuickActionAddImage.Label(), 0, &AddImageButton, 0, m_QuickActionAddImage.Description()))
InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", false, AddImage, this); m_QuickActionAddImage.Call();
} }
s_ScrollRegion.End(); s_ScrollRegion.End();
} }
@ -5745,30 +5739,25 @@ void CEditor::RenderModebar(CUIRect View)
void CEditor::RenderStatusbar(CUIRect View, CUIRect *pTooltipRect) void CEditor::RenderStatusbar(CUIRect View, CUIRect *pTooltipRect)
{ {
const bool ButtonsDisabled = m_ShowPicker;
CUIRect Button; CUIRect Button;
View.VSplitRight(100.0f, &View, &Button); View.VSplitRight(100.0f, &View, &Button);
static int s_EnvelopeButton = 0; if(DoButton_Editor(&m_QuickActionEnvelopes, m_QuickActionEnvelopes.Label(), m_QuickActionEnvelopes.Color(), &Button, 0, m_QuickActionEnvelopes.Description()) == 1)
if(DoButton_Editor(&s_EnvelopeButton, "Envelopes", ButtonsDisabled ? -1 : m_ActiveExtraEditor == EXTRAEDITOR_ENVELOPES, &Button, 0, "Toggles the envelope editor.") == 1)
{ {
m_ActiveExtraEditor = m_ActiveExtraEditor == EXTRAEDITOR_ENVELOPES ? EXTRAEDITOR_NONE : EXTRAEDITOR_ENVELOPES; m_QuickActionEnvelopes.Call();
} }
View.VSplitRight(10.0f, &View, nullptr); View.VSplitRight(10.0f, &View, nullptr);
View.VSplitRight(100.0f, &View, &Button); View.VSplitRight(100.0f, &View, &Button);
static int s_SettingsButton = 0; if(DoButton_Editor(&m_QuickActionServerSettings, m_QuickActionServerSettings.Label(), m_QuickActionServerSettings.Color(), &Button, 0, m_QuickActionServerSettings.Description()) == 1)
if(DoButton_Editor(&s_SettingsButton, "Server settings", ButtonsDisabled ? -1 : m_ActiveExtraEditor == EXTRAEDITOR_SERVER_SETTINGS, &Button, 0, "Toggles the server settings editor.") == 1)
{ {
m_ActiveExtraEditor = m_ActiveExtraEditor == EXTRAEDITOR_SERVER_SETTINGS ? EXTRAEDITOR_NONE : EXTRAEDITOR_SERVER_SETTINGS; m_QuickActionServerSettings.Call();
} }
View.VSplitRight(10.0f, &View, nullptr); View.VSplitRight(10.0f, &View, nullptr);
View.VSplitRight(100.0f, &View, &Button); View.VSplitRight(100.0f, &View, &Button);
static int s_HistoryButton = 0; if(DoButton_Editor(&m_QuickActionHistory, m_QuickActionHistory.Label(), m_QuickActionHistory.Color(), &Button, 0, m_QuickActionHistory.Description()) == 1)
if(DoButton_Editor(&s_HistoryButton, "History", ButtonsDisabled ? -1 : m_ActiveExtraEditor == EXTRAEDITOR_HISTORY, &Button, 0, "Toggles the editor history view.") == 1)
{ {
m_ActiveExtraEditor = m_ActiveExtraEditor == EXTRAEDITOR_HISTORY ? EXTRAEDITOR_NONE : EXTRAEDITOR_HISTORY; m_QuickActionHistory.Call();
} }
View.VSplitRight(10.0f, pTooltipRect, nullptr); View.VSplitRight(10.0f, pTooltipRect, nullptr);
@ -7702,7 +7691,7 @@ void CEditor::RenderMenubar(CUIRect MenuBar)
if(DoButton_Ex(&s_SettingsButton, "Settings", 0, &SettingsButton, 0, nullptr, IGraphics::CORNER_T, EditorFontSizes::MENU, TEXTALIGN_ML)) if(DoButton_Ex(&s_SettingsButton, "Settings", 0, &SettingsButton, 0, nullptr, IGraphics::CORNER_T, EditorFontSizes::MENU, TEXTALIGN_ML))
{ {
static SPopupMenuId s_PopupMenuEntitiesId; 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; CUIRect ChangedIndicator, Info, Help, Close;
@ -7919,7 +7908,7 @@ void CEditor::Render()
InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", true, CallbackSaveCopyMap, this); InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", true, CallbackSaveCopyMap, this);
// ctrl+shift+s to save as // ctrl+shift+s to save as
else if(Input()->KeyPress(KEY_S) && ModPressed && ShiftPressed) else if(Input()->KeyPress(KEY_S) && ModPressed && ShiftPressed)
InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", true, CallbackSaveMap, this); m_QuickActionSaveAs.Call();
// ctrl+s to save // ctrl+s to save
else if(Input()->KeyPress(KEY_S) && ModPressed) else if(Input()->KeyPress(KEY_S) && ModPressed)
{ {
@ -8362,6 +8351,7 @@ void CEditor::Init()
m_vComponents.emplace_back(m_MapView); m_vComponents.emplace_back(m_MapView);
m_vComponents.emplace_back(m_MapSettingsBackend); m_vComponents.emplace_back(m_MapSettingsBackend);
m_vComponents.emplace_back(m_LayerSelector); m_vComponents.emplace_back(m_LayerSelector);
m_vComponents.emplace_back(m_Prompt);
for(CEditorComponent &Component : m_vComponents) for(CEditorComponent &Component : m_vComponents)
Component.OnInit(this); Component.OnInit(this);
@ -8602,21 +8592,29 @@ void CEditor::HandleWriterFinishJobs()
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "editor/save", aBuf); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "editor/save", aBuf);
// send rcon.. if we can // send rcon.. if we can
if(Client()->RconAuthed()) if(Client()->RconAuthed() && g_Config.m_EdAutoMapReload)
{ {
CServerInfo CurrentServerInfo; CServerInfo CurrentServerInfo;
Client()->GetServerInfo(&CurrentServerInfo); Client()->GetServerInfo(&CurrentServerInfo);
NETADDR ServerAddr = Client()->ServerAddress();
const unsigned char aIpv4Localhost[16] = {127, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const unsigned char aIpv6Localhost[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
// and if we're on localhost NETADDR pAddr = Client()->ServerAddress();
if(!mem_comp(ServerAddr.ip, aIpv4Localhost, sizeof(aIpv4Localhost)) || !mem_comp(ServerAddr.ip, aIpv6Localhost, sizeof(aIpv6Localhost))) char aAddrStr[NETADDR_MAXSTRSIZE];
net_addr_str(&Client()->ServerAddress(), aAddrStr, sizeof(aAddrStr), true);
// and if we're on a local address
bool IsLocalAddress = false;
if(pAddr.ip[0] == 127 || pAddr.ip[0] == 10 || (pAddr.ip[0] == 192 && pAddr.ip[1] == 168) || (pAddr.ip[0] == 172 && (pAddr.ip[1] >= 16 && pAddr.ip[1] <= 31)))
IsLocalAddress = true;
if(str_startswith(aAddrStr, "[fe80:") || str_startswith(aAddrStr, "[::1"))
IsLocalAddress = true;
if(IsLocalAddress)
{ {
char aMapName[128]; char aMapName[128];
IStorage::StripPathAndExtension(pJob->GetRealFileName(), aMapName, sizeof(aMapName)); IStorage::StripPathAndExtension(pJob->GetRealFileName(), aMapName, sizeof(aMapName));
if(!str_comp(aMapName, CurrentServerInfo.m_aMap)) if(!str_comp(aMapName, CurrentServerInfo.m_aMap))
Client()->Rcon("reload"); Client()->Rcon("hot_reload");
} }
} }
} }

View file

@ -11,6 +11,7 @@
#include <game/client/ui_listbox.h> #include <game/client/ui_listbox.h>
#include <game/mapitems.h> #include <game/mapitems.h>
#include <game/editor/enums.h>
#include <game/editor/mapitems/envelope.h> #include <game/editor/mapitems/envelope.h>
#include <game/editor/mapitems/layer.h> #include <game/editor/mapitems/layer.h>
#include <game/editor/mapitems/layer_front.h> #include <game/editor/mapitems/layer_front.h>
@ -38,6 +39,8 @@
#include "layer_selector.h" #include "layer_selector.h"
#include "map_view.h" #include "map_view.h"
#include "smooth_value.h" #include "smooth_value.h"
#include <game/editor/prompt.h>
#include <game/editor/quick_action.h>
#include <deque> #include <deque>
#include <functional> #include <functional>
@ -60,7 +63,8 @@ enum
DIALOG_NONE = 0, DIALOG_NONE = 0,
DIALOG_FILE, DIALOG_FILE,
DIALOG_MAPSETTINGS_ERROR DIALOG_MAPSETTINGS_ERROR,
DIALOG_QUICK_PROMPT,
}; };
class CEditorImage; class CEditorImage;
@ -278,6 +282,7 @@ class CEditor : public IEditor
std::vector<std::reference_wrapper<CEditorComponent>> m_vComponents; std::vector<std::reference_wrapper<CEditorComponent>> m_vComponents;
CMapView m_MapView; CMapView m_MapView;
CLayerSelector m_LayerSelector; CLayerSelector m_LayerSelector;
CPrompt m_Prompt;
bool m_EditorWasUsedBefore = false; bool m_EditorWasUsedBefore = false;
@ -319,7 +324,20 @@ public:
const CMapView *MapView() const { return &m_MapView; } const CMapView *MapView() const { return &m_MapView; }
CLayerSelector *LayerSelector() { return &m_LayerSelector; } CLayerSelector *LayerSelector() { return &m_LayerSelector; }
void FillGameTiles(EGameTileOp FillTile) const;
bool CanFillGameTiles() const;
void AddGroup();
void AddTileLayer();
void LayerSelectImage();
bool IsNonGameTileLayerSelected() const;
#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) CQuickAction m_QuickAction##name;
#include <game/editor/quick_actions.h>
#undef REGISTER_QUICK_ACTION
CEditor() : CEditor() :
#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) m_QuickAction##name(text, description, callback, disabled, active, button_color),
#include <game/editor/quick_actions.h>
#undef REGISTER_QUICK_ACTION
m_ZoomEnvelopeX(1.0f, 0.1f, 600.0f), m_ZoomEnvelopeX(1.0f, 0.1f, 600.0f),
m_ZoomEnvelopeY(640.0f, 0.1f, 32000.0f), m_ZoomEnvelopeY(640.0f, 0.1f, 32000.0f),
m_MapSettingsCommandContext(m_MapSettingsBackend.NewContext(&m_SettingsCommandInput)) m_MapSettingsCommandContext(m_MapSettingsBackend.NewContext(&m_SettingsCommandInput))

View file

@ -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) : 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) 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() void CEditorActionEditQuadPoint::Undo()
@ -628,7 +628,7 @@ CEditorActionGroup::CEditorActionGroup(CEditor *pEditor, int GroupIndex, bool De
if(m_Delete) if(m_Delete)
str_format(m_aDisplayText, sizeof(m_aDisplayText), "Delete group %d", m_GroupIndex); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Delete group %d", m_GroupIndex);
else else
str_format(m_aDisplayText, sizeof(m_aDisplayText), "New group"); str_copy(m_aDisplayText, "New group", sizeof(m_aDisplayText));
} }
void CEditorActionGroup::Undo() void CEditorActionGroup::Undo()
@ -1198,7 +1198,7 @@ CEditorActionTileArt::CEditorActionTileArt(CEditor *pEditor, int PreviousImageCo
IEditorAction(pEditor), m_PreviousImageCount(PreviousImageCount), m_vImageIndexMap(vImageIndexMap) IEditorAction(pEditor), m_PreviousImageCount(PreviousImageCount), m_vImageIndexMap(vImageIndexMap)
{ {
str_copy(m_aTileArtFile, pTileArtFile); str_copy(m_aTileArtFile, pTileArtFile);
str_format(m_aDisplayText, sizeof(m_aDisplayText), "Tile art"); str_copy(m_aDisplayText, "Tile art");
} }
void CEditorActionTileArt::Undo() void CEditorActionTileArt::Undo()
@ -1266,7 +1266,7 @@ CEditorCommandAction::CEditorCommandAction(CEditor *pEditor, EType Type, int *pS
switch(m_Type) switch(m_Type)
{ {
case EType::ADD: case EType::ADD:
str_format(m_aDisplayText, sizeof(m_aDisplayText), "Add command"); str_copy(m_aDisplayText, "Add command");
break; break;
case EType::EDIT: case EType::EDIT:
str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit command %d", m_CommandIndex); str_format(m_aDisplayText, sizeof(m_aDisplayText), "Edit command %d", m_CommandIndex);

36
src/game/editor/enums.h Normal file
View 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

View file

@ -6,6 +6,7 @@
#include <engine/shared/map.h> #include <engine/shared/map.h>
#include <game/editor/editor.h> #include <game/editor/editor.h>
#include <game/editor/editor_actions.h> #include <game/editor/editor_actions.h>
#include <game/editor/enums.h>
#include <iterator> #include <iterator>
#include <numeric> #include <numeric>
@ -693,93 +694,126 @@ void CLayerTiles::ShowInfo()
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
} }
CUi::EPopupMenuFunctionResult CLayerTiles::RenderProperties(CUIRect *pToolBox) void CLayerTiles::FillGameTiles(EGameTileOp Fill)
{ {
CUIRect Button; if(!CanFillGameTiles())
return;
const bool EntitiesLayer = IsEntitiesLayer();
std::shared_ptr<CLayerGroup> pGroup = m_pEditor->m_Map.m_vpGroups[m_pEditor->m_SelectedGroup]; 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 int Result = (int)Fill;
if(!EntitiesLayer && !(pGroup->m_OffsetX % 32) && !(pGroup->m_OffsetY % 32) && pGroup->m_ParallaxX == 100 && pGroup->m_ParallaxY == 100) switch(Fill)
{ {
pToolBox->HSplitBottom(12.0f, pToolBox, &Button); case EGameTileOp::HOOKTHROUGH:
static int s_GameTilesButton = 0; Result = TILE_THROUGH_CUT;
if(m_pEditor->DoButton_Editor(&s_GameTilesButton, "Game tiles", 0, &Button, 0, "Constructs game tiles from this layer")) break;
m_pEditor->PopupSelectGametileOpInvoke(m_pEditor->Ui()->MouseX(), m_pEditor->Ui()->MouseY()); case EGameTileOp::FREEZE:
const int Selected = m_pEditor->PopupSelectGameTileOpResult(); Result = TILE_FREEZE;
int Result = Selected; break;
switch(Selected) 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: if(pGLayer->m_Width < m_Width + OffsetX || pGLayer->m_Height < m_Height + OffsetY)
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) 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; std::map<int, std::shared_ptr<CLayer>> savedLayers;
savedLayers[LAYERTYPE_TILES] = pGLayer->Duplicate(); savedLayers[LAYERTYPE_TILES] = pGLayer->Duplicate();
savedLayers[LAYERTYPE_GAME] = savedLayers[LAYERTYPE_TILES]; 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 PrevW = pGLayer->m_Width;
int PrevH = pGLayer->m_Height; int PrevH = pGLayer->m_Height;
const int NewW = pGLayer->m_Width < m_Width + OffsetX ? m_Width + OffsetX : pGLayer->m_Width; pLayer->Resize(NewW, NewH);
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)); 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]); 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)); 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); Action1->SetSavedLayers(savedLayers);
Action2->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::map<int, std::shared_ptr<CLayer>> savedLayers;
{ savedLayers[LAYERTYPE_TILES] = pTLayer->Duplicate();
std::shared_ptr<CLayerTele> pLayer = std::make_shared<CLayerTele>(m_pEditor, m_Width, m_Height); savedLayers[LAYERTYPE_TELE] = savedLayers[LAYERTYPE_TILES];
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)); 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) Action1->SetSavedLayers(savedLayers);
{ Action2->SetSavedLayers(savedLayers);
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));
} }
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) if(m_pEditor->m_Map.m_pGameLayer.get() != this)
{ {

View file

@ -2,6 +2,7 @@
#define GAME_EDITOR_MAPITEMS_LAYER_TILES_H #define GAME_EDITOR_MAPITEMS_LAYER_TILES_H
#include <game/editor/editor_trackers.h> #include <game/editor/editor_trackers.h>
#include <game/editor/enums.h>
#include <map> #include <map>
#include "layer.h" #include "layer.h"
@ -122,6 +123,8 @@ public:
void BrushSelecting(CUIRect Rect) override; void BrushSelecting(CUIRect Rect) override;
int BrushGrab(std::shared_ptr<CLayerGroup> pBrush, 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 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 BrushDraw(std::shared_ptr<CLayer> pBrush, float wx, float wy) override;
void BrushFlipX() override; void BrushFlipX() override;
void BrushFlipY() override; void BrushFlipY() override;

View file

@ -68,17 +68,9 @@ CUi::EPopupMenuFunctionResult CEditor::PopupMenuFile(void *pContext, CUIRect Vie
View.HSplitTop(2.0f, nullptr, &View); View.HSplitTop(2.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &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_QuickActionLoadCurrentMap.Call();
{
pEditor->m_PopupEventType = POPEVENT_LOADCURRENT;
pEditor->m_PopupEventActivated = true;
}
else
{
pEditor->LoadCurrentMap();
}
return CUi::POPUP_CLOSE_CURRENT; return CUi::POPUP_CLOSE_CURRENT;
} }
@ -107,9 +99,9 @@ CUi::EPopupMenuFunctionResult CEditor::PopupMenuFile(void *pContext, CUIRect Vie
View.HSplitTop(2.0f, nullptr, &View); View.HSplitTop(2.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View); View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_SaveAsButton, "Save As", 0, &Slot, 0, "Saves the current map under a new name (ctrl+shift+s)")) if(pEditor->DoButton_MenuItem(&s_SaveAsButton, pEditor->m_QuickActionSaveAs.Label(), 0, &Slot, 0, pEditor->m_QuickActionSaveAs.Description()))
{ {
pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", true, CEditor::CallbackSaveMap, pEditor); pEditor->m_QuickActionSaveAs.Call();
return CUi::POPUP_CLOSE_CURRENT; return CUi::POPUP_CLOSE_CURRENT;
} }
@ -314,20 +306,20 @@ CUi::EPopupMenuFunctionResult CEditor::PopupMenuSettings(void *pContext, CUIRect
static int s_ButtonOff = 0; static int s_ButtonOff = 0;
static int s_ButtonDec = 0; static int s_ButtonDec = 0;
static int s_ButtonHex = 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; pAction->Call();
pEditor->m_ShowEnvelopePreview = SHOWENV_NONE;
} }
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; pAction->Call();
pEditor->m_ShowEnvelopePreview = SHOWENV_NONE;
} }
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; pAction->Call();
pEditor->m_ShowEnvelopePreview = SHOWENV_NONE;
} }
} }
@ -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; return CUi::POPUP_KEEP_OPEN;
} }
@ -578,16 +594,9 @@ CUi::EPopupMenuFunctionResult CEditor::PopupGroup(void *pContext, CUIRect View,
// new tile layer // new tile layer
View.HSplitBottom(5.0f, &View, nullptr); View.HSplitBottom(5.0f, &View, nullptr);
View.HSplitBottom(12.0f, &View, &Button); View.HSplitBottom(12.0f, &View, &Button);
static int s_NewTileLayerButton = 0; if(pEditor->DoButton_Editor(&pEditor->m_QuickActionAddTileLayer, pEditor->m_QuickActionAddTileLayer.Label(), 0, &Button, 0, pEditor->m_QuickActionAddTileLayer.Description()))
if(pEditor->DoButton_Editor(&s_NewTileLayerButton, "Add tile layer", 0, &Button, 0, "Creates a new tile layer"))
{ {
std::shared_ptr<CLayer> pTileLayer = std::make_shared<CLayerTiles>(pEditor, pEditor->m_Map.m_pGameLayer->m_Width, pEditor->m_Map.m_pGameLayer->m_Height); pEditor->m_QuickActionAddTileLayer.Call();
pTileLayer->m_pEditor = pEditor;
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->AddLayer(pTileLayer);
int LayerIndex = pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_vpLayers.size() - 1;
pEditor->SelectLayer(LayerIndex);
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_Collapse = false;
pEditor->m_EditorHistory.RecordAction(std::make_shared<CEditorActionAddLayer>(pEditor, pEditor->m_SelectedGroup, LayerIndex));
return CUi::POPUP_CLOSE_CURRENT; return CUi::POPUP_CLOSE_CURRENT;
} }

172
src/game/editor/prompt.cpp Normal file
View file

@ -0,0 +1,172 @@
#include <engine/keys.h>
#include <game/client/ui_listbox.h>
#include <game/editor/quick_action.h>
#include "editor.h"
#include "prompt.h"
bool FuzzyMatch(const char *pHaystack, const char *pNeedle)
{
if(!pNeedle || !pNeedle[0])
return false;
char aBuf[2] = {0};
const char *pHit = pHaystack;
int NeedleLen = str_length(pNeedle);
for(int i = 0; i < NeedleLen; i++)
{
if(!pHit)
return false;
aBuf[0] = pNeedle[i];
pHit = str_find_nocase(pHit, aBuf);
if(pHit)
pHit++;
}
return pHit;
}
bool CPrompt::IsActive()
{
return CEditorComponent::IsActive() || Editor()->m_Dialog == DIALOG_QUICK_PROMPT;
}
void CPrompt::SetActive()
{
Editor()->m_Dialog = DIALOG_QUICK_PROMPT;
CEditorComponent::SetActive();
Ui()->SetActiveItem(&m_PromptInput);
}
void CPrompt::SetInactive()
{
m_ResetFilterResults = true;
m_PromptInput.Clear();
if(Editor()->m_Dialog == DIALOG_QUICK_PROMPT)
Editor()->m_Dialog = DIALOG_NONE;
CEditorComponent::SetInactive();
}
bool CPrompt::OnInput(const IInput::CEvent &Event)
{
if(Input()->ModifierIsPressed() && Input()->KeyIsPressed(KEY_P))
{
SetActive();
}
return false;
}
void CPrompt::OnInit(CEditor *pEditor)
{
CEditorComponent::OnInit(pEditor);
#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) m_vQuickActions.emplace_back(&Editor()->m_QuickAction##name);
#include <game/editor/quick_actions.h>
#undef REGISTER_QUICK_ACTION
}
void CPrompt::OnRender(CUIRect _)
{
if(!IsActive())
return;
if(Ui()->ConsumeHotkey(CUi::HOTKEY_ESCAPE))
{
SetInactive();
return;
}
static CListBox s_ListBox;
CUIRect Prompt, PromptBox;
CUIRect Suggestions;
Ui()->MapScreen();
CUIRect Overlay = *Ui()->Screen();
Overlay.Draw(ColorRGBA(0, 0, 0, 0.33f), IGraphics::CORNER_NONE, 0.0f);
CUIRect Background;
Overlay.VMargin(150.0f, &Background);
Background.HMargin(50.0f, &Background);
Background.Draw(ColorRGBA(0, 0, 0, 0.80f), IGraphics::CORNER_ALL, 5.0f);
Background.Margin(10.0f, &Prompt);
Prompt.VSplitMid(nullptr, &PromptBox);
Prompt.HSplitTop(16.0f, &PromptBox, &Suggestions);
PromptBox.Draw(ColorRGBA(0, 0, 0, 0.75f), IGraphics::CORNER_ALL, 2.0f);
Suggestions.y += 6.0f;
if(Ui()->DoClearableEditBox(&m_PromptInput, &PromptBox, 10.0f) || m_ResetFilterResults)
{
m_PromptSelectedIndex = 0;
m_vpFilteredPromptList.clear();
if(m_ResetFilterResults && m_pLastAction && !m_pLastAction->Disabled())
{
m_vpFilteredPromptList.push_back(m_pLastAction);
}
for(auto *pQuickAction : m_vQuickActions)
{
if(pQuickAction->Disabled())
continue;
if(m_PromptInput.IsEmpty() || FuzzyMatch(pQuickAction->Label(), m_PromptInput.GetString()))
{
bool Skip = false;
if(m_ResetFilterResults)
if(pQuickAction == m_pLastAction)
Skip = true;
if(!Skip)
m_vpFilteredPromptList.push_back(pQuickAction);
}
}
m_ResetFilterResults = false;
}
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);
if(!Item.m_Visible)
continue;
CUIRect LabelColumn, DescColumn;
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;
Props.m_EllipsisAtEnd = true;
Ui()->DoLabel(&LabelColumn, m_vpFilteredPromptList[i]->Label(), 10.0f, TEXTALIGN_ML, Props);
Props.m_MaxWidth = DescColumn.w;
ColorRGBA DescColor = TextRender()->DefaultTextColor();
DescColor.a = Item.m_Selected ? 1.0f : 0.8f;
TextRender()->TextColor(DescColor);
Ui()->DoLabel(&DescColumn, m_vpFilteredPromptList[i]->Description(), 10.0f, TEXTALIGN_MR, Props);
TextRender()->TextColor(TextRender()->DefaultTextColor());
}
const int NewSelected = s_ListBox.DoEnd();
if(m_PromptSelectedIndex != NewSelected)
{
m_PromptSelectedIndex = NewSelected;
}
if(s_ListBox.WasItemActivated())
{
if(m_PromptSelectedIndex >= 0)
{
CQuickAction *pBtn = m_vpFilteredPromptList[m_PromptSelectedIndex];
SetInactive();
pBtn->Call();
m_pLastAction = pBtn;
}
}
}

29
src/game/editor/prompt.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef GAME_EDITOR_PROMPT_H
#define GAME_EDITOR_PROMPT_H
#include <game/client/lineinput.h>
#include <game/client/ui_rect.h>
#include <game/editor/quick_action.h>
#include "component.h"
class CPrompt : public CEditorComponent
{
bool m_ResetFilterResults = true;
CQuickAction *m_pLastAction = nullptr;
int m_PromptSelectedIndex = -1;
std::vector<CQuickAction *> m_vpFilteredPromptList;
std::vector<CQuickAction *> m_vQuickActions;
CLineInputBuffered<512> m_PromptInput;
public:
void OnInit(CEditor *pEditor) override;
bool OnInput(const IInput::CEvent &Event) override;
void OnRender(CUIRect _) override;
bool IsActive();
void SetActive();
void SetInactive();
};
#endif

View file

@ -0,0 +1,69 @@
#ifndef GAME_EDITOR_QUICK_ACTION_H
#define GAME_EDITOR_QUICK_ACTION_H
#include <functional>
#include <utility>
typedef std::function<void()> FButtonClickCallback;
typedef std::function<bool()> FButtonDisabledCallback;
typedef std::function<bool()> FButtonActiveCallback;
typedef std::function<int()> FButtonColorCallback;
class CQuickAction
{
private:
const char *m_pLabel;
const char *m_pDescription;
FButtonClickCallback m_pfnCallback;
FButtonDisabledCallback m_pfnDisabledCallback;
FButtonActiveCallback m_pfnActiveCallback;
FButtonColorCallback m_pfnColorCallback;
public:
CQuickAction(
const char *pLabel,
const char *pDescription,
FButtonClickCallback pfnCallback,
FButtonDisabledCallback pfnDisabledCallback,
FButtonActiveCallback pfnActiveCallback,
FButtonColorCallback pfnColorCallback) :
m_pLabel(pLabel),
m_pDescription(pDescription),
m_pfnCallback(std::move(pfnCallback)),
m_pfnDisabledCallback(std::move(pfnDisabledCallback)),
m_pfnActiveCallback(std::move(pfnActiveCallback)),
m_pfnColorCallback(std::move(pfnColorCallback))
{
}
// code to run when the action is triggered
void Call() const { m_pfnCallback(); }
// bool that indicates if the action can be performed not or not
bool Disabled() { return m_pfnDisabledCallback(); }
// bool that indicates if the action is currently running
// only applies to actions that can be turned on or off like proof borders
bool Active() { return m_pfnActiveCallback(); }
// color "enum" that represents the state of the quick actions button
// used as Checked argument for DoButton_Editor()
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; }
};
#endif

View file

@ -0,0 +1,71 @@
#include <game/mapitems.h>
#include "editor.h"
#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);
pTileLayer->m_pEditor = this;
m_Map.m_vpGroups[m_SelectedGroup]->AddLayer(pTileLayer);
int LayerIndex = m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1;
SelectLayer(LayerIndex);
m_Map.m_vpGroups[m_SelectedGroup]->m_Collapse = false;
m_EditorHistory.RecordAction(std::make_shared<CEditorActionAddLayer>(this, m_SelectedGroup, LayerIndex));
}
bool CEditor::IsNonGameTileLayerSelected() const
{
std::shared_ptr<CLayer> pLayer = GetSelectedLayer(0);
if(!pLayer)
return false;
if(pLayer->m_Type != LAYERTYPE_TILES)
return false;
if(
pLayer == m_Map.m_pGameLayer ||
pLayer == m_Map.m_pFrontLayer ||
pLayer == m_Map.m_pSwitchLayer ||
pLayer == m_Map.m_pTeleLayer ||
pLayer == m_Map.m_pSpeedupLayer ||
pLayer == m_Map.m_pTuneLayer)
return false;
return true;
}
void CEditor::LayerSelectImage()
{
if(!IsNonGameTileLayerSelected())
return;
std::shared_ptr<CLayer> pLayer = GetSelectedLayer(0);
std::shared_ptr<CLayerTiles> pTiles = std::static_pointer_cast<CLayerTiles>(pLayer);
static SLayerPopupContext s_LayerPopupContext = {};
s_LayerPopupContext.m_pEditor = this;
Ui()->DoPopupMenu(&s_LayerPopupContext, Ui()->MouseX(), Ui()->MouseY(), 120, 270, &s_LayerPopupContext, PopupLayer);
PopupSelectImageInvoke(pTiles->m_Image, Ui()->MouseX(), Ui()->MouseY());
}

View file

@ -0,0 +1,281 @@
// This file can be included several times.
#ifndef REGISTER_QUICK_ACTION
#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description)
#endif
#define ALWAYS_FALSE []() -> bool { return false; }
#define DEFAULT_BTN []() -> int { return -1; }
REGISTER_QUICK_ACTION(
ToggleGrid,
"Toggle Grid",
[&]() { MapView()->MapGrid()->Toggle(); },
ALWAYS_FALSE,
[&]() -> bool { return MapView()->MapGrid()->IsEnabled(); },
DEFAULT_BTN,
"[Ctrl+G] Toggle Grid.")
REGISTER_QUICK_ACTION(
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",
[&]() { MapView()->ProofMode()->Toggle(); },
ALWAYS_FALSE,
[&]() -> bool { return MapView()->ProofMode()->IsEnabled(); },
DEFAULT_BTN,
"Toggles proof borders. These borders represent the area that a player can see with default zoom.")
REGISTER_QUICK_ACTION(
AddTileLayer, "Add tile layer", [&]() { AddTileLayer(); }, ALWAYS_FALSE, ALWAYS_FALSE, DEFAULT_BTN, "Creates a new tile layer.")
REGISTER_QUICK_ACTION(
SaveAs,
"Save As",
[&]() { InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save As", "maps", true, CEditor::CallbackSaveMap, this); },
ALWAYS_FALSE,
ALWAYS_FALSE,
DEFAULT_BTN,
"[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",
[&]() { m_ActiveExtraEditor = m_ActiveExtraEditor == EXTRAEDITOR_ENVELOPES ? EXTRAEDITOR_NONE : EXTRAEDITOR_ENVELOPES; },
ALWAYS_FALSE,
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",
[&]() { InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", false, AddImage, this); },
ALWAYS_FALSE,
ALWAYS_FALSE,
DEFAULT_BTN,
"Load a new image to use in the map.")
REGISTER_QUICK_ACTION(
LayerPropAddImage,
"Layer: Add Image",
[&]() { LayerSelectImage(); },
[&]() -> bool { return !IsNonGameTileLayerSelected(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"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

View file

@ -565,7 +565,7 @@ void CGameContext::ConMapInfo(IConsole::IResult *pResult, void *pUserData)
if(pResult->NumArguments() > 0) if(pResult->NumArguments() > 0)
pSelf->Score()->MapInfo(pResult->m_ClientId, pResult->GetString(0)); pSelf->Score()->MapInfo(pResult->m_ClientId, pResult->GetString(0));
else else
pSelf->Score()->MapInfo(pResult->m_ClientId, g_Config.m_SvMap); pSelf->Score()->MapInfo(pResult->m_ClientId, pSelf->Server()->GetMapName());
} }
void CGameContext::ConTimeout(IConsole::IResult *pResult, void *pUserData) void CGameContext::ConTimeout(IConsole::IResult *pResult, void *pUserData)

View file

@ -103,6 +103,35 @@ bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos)
TrySetRescue(RESCUEMODE_MANUAL); TrySetRescue(RESCUEMODE_MANUAL);
Server()->StartRecord(m_pPlayer->GetCid()); Server()->StartRecord(m_pPlayer->GetCid());
int Team = GameServer()->m_aTeamMapping[m_pPlayer->GetCid()];
if(Team != -1)
{
GameServer()->m_pController->Teams().SetForceCharacterTeam(m_pPlayer->GetCid(), Team);
GameServer()->m_aTeamMapping[m_pPlayer->GetCid()] = -1;
if(GameServer()->m_apSavedTeams[Team])
{
GameServer()->m_apSavedTeams[Team]->Load(GameServer(), Team, true, true);
delete GameServer()->m_apSavedTeams[Team];
GameServer()->m_apSavedTeams[Team] = nullptr;
}
if(GameServer()->m_apSavedTees[m_pPlayer->GetCid()])
{
GameServer()->m_apSavedTees[m_pPlayer->GetCid()]->Load(m_pPlayer->GetCharacter(), Team);
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; return true;
} }
@ -212,11 +241,7 @@ void CCharacter::HandleJetpack()
{ {
if(m_Core.m_Jetpack) if(m_Core.m_Jetpack)
{ {
float Strength; float Strength = GetTuning(m_TuneZone)->m_JetpackStrength;
if(!m_TuneZone)
Strength = Tuning()->m_JetpackStrength;
else
Strength = TuningList()[m_TuneZone].m_JetpackStrength;
TakeDamage(Direction * -1.0f * (Strength / 100.0f / 6.11f), 0, m_pPlayer->GetCid(), m_Core.m_ActiveWeapon); TakeDamage(Direction * -1.0f * (Strength / 100.0f / 6.11f), 0, m_pPlayer->GetCid(), m_Core.m_ActiveWeapon);
} }
} }
@ -242,7 +267,7 @@ void CCharacter::HandleNinja()
GameServer()->CreateDamageInd(m_Pos, 0, NinjaTime / Server()->TickSpeed(), TeamMask() & GameServer()->ClientsMaskExcludeClientVersionAndHigher(VERSION_DDNET_NEW_HUD)); GameServer()->CreateDamageInd(m_Pos, 0, NinjaTime / Server()->TickSpeed(), TeamMask() & GameServer()->ClientsMaskExcludeClientVersionAndHigher(VERSION_DDNET_NEW_HUD));
} }
m_Armor = clamp(10 - (NinjaTime / 15), 0, 10); GameServer()->m_pController->SetArmorProgress(this, NinjaTime);
// force ninja Weapon // force ninja Weapon
SetWeapon(WEAPON_NINJA); SetWeapon(WEAPON_NINJA);
@ -260,12 +285,9 @@ void CCharacter::HandleNinja()
// Set velocity // Set velocity
m_Core.m_Vel = m_Core.m_Ninja.m_ActivationDir * g_pData->m_Weapons.m_Ninja.m_Velocity; m_Core.m_Vel = m_Core.m_Ninja.m_ActivationDir * g_pData->m_Weapons.m_Ninja.m_Velocity;
vec2 OldPos = m_Pos; vec2 OldPos = m_Pos;
vec2 GroundElasticity; vec2 GroundElasticity = vec2(
GetTuning(m_TuneZone)->m_GroundElasticityX,
if(!m_TuneZone) GetTuning(m_TuneZone)->m_GroundElasticityY);
GroundElasticity = vec2(Tuning()->m_GroundElasticityX, Tuning()->m_GroundElasticityY);
else
GroundElasticity = vec2(TuningList()[m_TuneZone].m_GroundElasticityX, TuningList()[m_TuneZone].m_GroundElasticityY);
Collision()->MoveBox(&m_Core.m_Pos, &m_Core.m_Vel, vec2(GetProximityRadius(), GetProximityRadius()), GroundElasticity); Collision()->MoveBox(&m_Core.m_Pos, &m_Core.m_Vel, vec2(GetProximityRadius(), GetProximityRadius()), GroundElasticity);
@ -427,7 +449,7 @@ void CCharacter::FireWeapon()
if(m_PainSoundTimer <= 0 && !(m_LatestPrevInput.m_Fire & 1)) if(m_PainSoundTimer <= 0 && !(m_LatestPrevInput.m_Fire & 1))
{ {
m_PainSoundTimer = 1 * Server()->TickSpeed(); 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; return;
} }
@ -444,7 +466,7 @@ void CCharacter::FireWeapon()
{ {
// reset objects Hit // reset objects Hit
m_NumObjectsHit = 0; 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()); Antibot()->OnHammerFire(m_pPlayer->GetCid());
@ -476,11 +498,7 @@ void CCharacter::FireWeapon()
else else
Dir = vec2(0.f, -1.f); Dir = vec2(0.f, -1.f);
float Strength; float Strength = GetTuning(m_TuneZone)->m_HammerStrength;
if(!m_TuneZone)
Strength = Tuning()->m_HammerStrength;
else
Strength = TuningList()[m_TuneZone].m_HammerStrength;
vec2 Temp = pTarget->m_Core.m_Vel + normalize(Dir + vec2(0.f, -1.1f)) * 10.0f; vec2 Temp = pTarget->m_Core.m_Vel + normalize(Dir + vec2(0.f, -1.1f)) * 10.0f;
Temp = ClampVel(pTarget->m_MoveRestrictions, Temp); Temp = ClampVel(pTarget->m_MoveRestrictions, Temp);
@ -500,11 +518,7 @@ void CCharacter::FireWeapon()
// if we Hit anything, we have to wait for the reload // if we Hit anything, we have to wait for the reload
if(Hits) if(Hits)
{ {
float FireDelay; float FireDelay = GetTuning(m_TuneZone)->m_HammerHitFireDelay;
if(!m_TuneZone)
FireDelay = Tuning()->m_HammerHitFireDelay;
else
FireDelay = TuningList()[m_TuneZone].m_HammerHitFireDelay;
m_ReloadTimer = FireDelay * Server()->TickSpeed() / 1000; m_ReloadTimer = FireDelay * Server()->TickSpeed() / 1000;
} }
} }
@ -514,11 +528,7 @@ void CCharacter::FireWeapon()
{ {
if(!m_Core.m_Jetpack || !m_pPlayer->m_NinjaJetpack || m_Core.m_HasTelegunGun) if(!m_Core.m_Jetpack || !m_pPlayer->m_NinjaJetpack || m_Core.m_HasTelegunGun)
{ {
int Lifetime; int Lifetime = (int)(Server()->TickSpeed() * GetTuning(m_TuneZone)->m_GunLifetime);
if(!m_TuneZone)
Lifetime = (int)(Server()->TickSpeed() * Tuning()->m_GunLifetime);
else
Lifetime = (int)(Server()->TickSpeed() * TuningList()[m_TuneZone].m_GunLifetime);
new CProjectile( new CProjectile(
GameWorld(), GameWorld(),
@ -540,11 +550,7 @@ void CCharacter::FireWeapon()
case WEAPON_SHOTGUN: case WEAPON_SHOTGUN:
{ {
float LaserReach; float LaserReach = GetTuning(m_TuneZone)->m_LaserReach;
if(!m_TuneZone)
LaserReach = Tuning()->m_LaserReach;
else
LaserReach = TuningList()[m_TuneZone].m_LaserReach;
new CLaser(&GameServer()->m_World, m_Pos, Direction, LaserReach, m_pPlayer->GetCid(), WEAPON_SHOTGUN); new CLaser(&GameServer()->m_World, m_Pos, Direction, LaserReach, m_pPlayer->GetCid(), WEAPON_SHOTGUN);
GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc) GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
@ -553,11 +559,7 @@ void CCharacter::FireWeapon()
case WEAPON_GRENADE: case WEAPON_GRENADE:
{ {
int Lifetime; int Lifetime = (int)(Server()->TickSpeed() * GetTuning(m_TuneZone)->m_GrenadeLifetime);
if(!m_TuneZone)
Lifetime = (int)(Server()->TickSpeed() * Tuning()->m_GrenadeLifetime);
else
Lifetime = (int)(Server()->TickSpeed() * TuningList()[m_TuneZone].m_GrenadeLifetime);
new CProjectile( new CProjectile(
GameWorld(), GameWorld(),
@ -572,17 +574,13 @@ void CCharacter::FireWeapon()
MouseTarget // MouseTarget MouseTarget // MouseTarget
); );
GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE, TeamMask()); GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
} }
break; break;
case WEAPON_LASER: case WEAPON_LASER:
{ {
float LaserReach; float LaserReach = GetTuning(m_TuneZone)->m_LaserReach;
if(!m_TuneZone)
LaserReach = Tuning()->m_LaserReach;
else
LaserReach = TuningList()[m_TuneZone].m_LaserReach;
new CLaser(GameWorld(), m_Pos, Direction, LaserReach, m_pPlayer->GetCid(), WEAPON_LASER); new CLaser(GameWorld(), m_Pos, Direction, LaserReach, m_pPlayer->GetCid(), WEAPON_LASER);
GameServer()->CreateSound(m_Pos, SOUND_LASER_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc) GameServer()->CreateSound(m_Pos, SOUND_LASER_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
@ -598,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_CurrentMoveTime = g_pData->m_Weapons.m_Ninja.m_Movetime * Server()->TickSpeed() / 1000;
m_Core.m_Ninja.m_OldVelAmount = length(m_Core.m_Vel); 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; break;
} }
@ -608,10 +606,7 @@ void CCharacter::FireWeapon()
if(!m_ReloadTimer) if(!m_ReloadTimer)
{ {
float FireDelay; float FireDelay;
if(!m_TuneZone) GetTuning(m_TuneZone)->Get(38 + m_Core.m_ActiveWeapon, &FireDelay);
Tuning()->Get(38 + m_Core.m_ActiveWeapon, &FireDelay);
else
TuningList()[m_TuneZone].Get(38 + m_Core.m_ActiveWeapon, &FireDelay);
m_ReloadTimer = FireDelay * Server()->TickSpeed() / 1000; m_ReloadTimer = FireDelay * Server()->TickSpeed() / 1000;
} }
} }
@ -2067,7 +2062,7 @@ void CCharacter::ForceSetRescue(int RescueMode)
void CCharacter::DDRaceTick() void CCharacter::DDRaceTick()
{ {
mem_copy(&m_Input, &m_SavedInput, sizeof(m_Input)); mem_copy(&m_Input, &m_SavedInput, sizeof(m_Input));
m_Armor = clamp(10 - (m_FreezeTime / 15), 0, 10); GameServer()->m_pController->SetArmorProgress(this, m_FreezeTime);
if(m_Input.m_Direction != 0 || m_Input.m_Jump != 0) if(m_Input.m_Direction != 0 || m_Input.m_Jump != 0)
m_LastMove = Server()->Tick(); m_LastMove = Server()->Tick();

View file

@ -263,6 +263,7 @@ public:
bool IsSuper() const { return m_Core.m_Super; } bool IsSuper() const { return m_Core.m_Super; }
CSaveTee &GetLastRescueTeeRef(int Mode = RESCUEMODE_AUTO) { return m_RescueTee[Mode]; } CSaveTee &GetLastRescueTeeRef(int Mode = RESCUEMODE_AUTO) { return m_RescueTee[Mode]; }
CTuningParams *GetTuning(int Zone) { return Zone ? &TuningList()[Zone] : Tuning(); }
}; };
enum enum

View file

@ -94,6 +94,7 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
{ {
pHit->UnFreeze(); pHit->UnFreeze();
} }
pHit->TakeDamage(vec2(0, 0), 0, m_Owner, m_Type);
return true; return true;
} }

View file

@ -177,6 +177,8 @@ void CProjectile::Tick()
pChr->Freeze(); pChr->Freeze();
} }
} }
else if(pTargetChr)
pTargetChr->TakeDamage(vec2(0, 0), 0, m_Owner, m_Type);
if(pOwnerChar && !GameLayerClipped(ColPos) && if(pOwnerChar && !GameLayerClipped(ColPos) &&
((m_Type == WEAPON_GRENADE && pOwnerChar->HasTelegunGrenade()) || (m_Type == WEAPON_GUN && pOwnerChar->HasTelegunGun()))) ((m_Type == WEAPON_GRENADE && pOwnerChar->HasTelegunGrenade()) || (m_Type == WEAPON_GUN && pOwnerChar->HasTelegunGun())))

View file

@ -105,6 +105,17 @@ void CGameContext::Construct(int Resetting)
if(Resetting == NO_RESET) if(Resetting == NO_RESET)
{ {
for(auto &pSavedTee : m_apSavedTees)
pSavedTee = nullptr;
for(auto &pSavedTeleTee : m_apSavedTeleTees)
pSavedTeleTee = nullptr;
for(auto &pSavedTeam : m_apSavedTeams)
pSavedTeam = nullptr;
std::fill(std::begin(m_aTeamMapping), std::end(m_aTeamMapping), -1);
m_NonEmptySince = 0; m_NonEmptySince = 0;
m_pVoteOptionHeap = new CHeap(); m_pVoteOptionHeap = new CHeap();
} }
@ -119,7 +130,18 @@ void CGameContext::Destruct(int Resetting)
delete pPlayer; delete pPlayer;
if(Resetting == NO_RESET) if(Resetting == NO_RESET)
{
for(auto &pSavedTee : m_apSavedTees)
delete pSavedTee;
for(auto &pSavedTeleTee : m_apSavedTeleTees)
delete pSavedTeleTee;
for(auto &pSavedTeam : m_apSavedTeams)
delete pSavedTeam;
delete m_pVoteOptionHeap; delete m_pVoteOptionHeap;
}
if(m_pScore) if(m_pScore)
{ {
@ -757,7 +779,6 @@ void CGameContext::StartVote(const char *pDesc, const char *pCommand, const char
{ {
// reset votes // reset votes
m_VoteEnforce = VOTE_ENFORCE_UNKNOWN; m_VoteEnforce = VOTE_ENFORCE_UNKNOWN;
m_VoteEnforcer = -1;
for(auto &pPlayer : m_apPlayers) for(auto &pPlayer : m_apPlayers)
{ {
if(pPlayer) if(pPlayer)
@ -1188,7 +1209,7 @@ void CGameContext::OnTick()
} }
else if(m_VoteEnforce == VOTE_ENFORCE_YES_ADMIN) 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); SendChat(-1, TEAM_ALL, "Vote passed enforced by authorized player", -1, FLAG_SIX);
EndVote(); EndVote();
} }
@ -1261,7 +1282,7 @@ void CGameContext::OnTick()
if(m_SqlRandomMapResult->m_Success) if(m_SqlRandomMapResult->m_Success)
{ {
if(PlayerExists(m_SqlRandomMapResult->m_ClientId) && m_SqlRandomMapResult->m_aMessage[0] != '\0') if(PlayerExists(m_SqlRandomMapResult->m_ClientId) && m_SqlRandomMapResult->m_aMessage[0] != '\0')
SendChatTarget(m_SqlRandomMapResult->m_ClientId, m_SqlRandomMapResult->m_aMessage); SendChat(-1, TEAM_ALL, m_SqlRandomMapResult->m_aMessage);
if(m_SqlRandomMapResult->m_aMap[0] != '\0') if(m_SqlRandomMapResult->m_aMap[0] != '\0')
Server()->ChangeMap(m_SqlRandomMapResult->m_aMap); Server()->ChangeMap(m_SqlRandomMapResult->m_aMap);
else else
@ -1467,26 +1488,6 @@ void CGameContext::OnClientEnter(int ClientId)
} }
m_pController->OnPlayerConnect(m_apPlayers[ClientId]); m_pController->OnPlayerConnect(m_apPlayers[ClientId]);
if(Server()->IsSixup(ClientId))
{
{
protocol7::CNetMsg_Sv_GameInfo Msg;
Msg.m_GameFlags = protocol7::GAMEFLAG_RACE;
Msg.m_MatchCurrent = 1;
Msg.m_MatchNum = 0;
Msg.m_ScoreLimit = 0;
Msg.m_TimeLimit = 0;
Server()->SendPackMsg(&Msg, MSGFLAG_VITAL | MSGFLAG_NORECORD, ClientId);
}
// /team is essential
{
protocol7::CNetMsg_Sv_CommandInfoRemove Msg;
Msg.m_pName = "team";
Server()->SendPackMsg(&Msg, MSGFLAG_VITAL | MSGFLAG_NORECORD, ClientId);
}
}
{ {
CNetMsg_Sv_CommandInfoGroupStart Msg; CNetMsg_Sv_CommandInfoGroupStart Msg;
Server()->SendPackMsg(&Msg, MSGFLAG_VITAL | MSGFLAG_NORECORD, ClientId); Server()->SendPackMsg(&Msg, MSGFLAG_VITAL | MSGFLAG_NORECORD, ClientId);
@ -1710,6 +1711,17 @@ void CGameContext::OnClientDrop(int ClientId, const char *pReason)
delete m_apPlayers[ClientId]; delete m_apPlayers[ClientId];
m_apPlayers[ClientId] = 0; m_apPlayers[ClientId] = 0;
delete m_apSavedTeams[ClientId];
m_apSavedTeams[ClientId] = nullptr;
delete m_apSavedTees[ClientId];
m_apSavedTees[ClientId] = nullptr;
delete m_apSavedTeleTees[ClientId];
m_apSavedTeleTees[ClientId] = nullptr;
m_aTeamMapping[ClientId] = -1;
m_VoteUpdate = true; m_VoteUpdate = true;
// update spectator modes // update spectator modes
@ -3116,6 +3128,8 @@ void CGameContext::ConRandomUnfinishedMap(IConsole::IResult *pResult, void *pUse
CGameContext *pSelf = (CGameContext *)pUserData; CGameContext *pSelf = (CGameContext *)pUserData;
int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1; int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1;
if(pResult->m_ClientId != -1)
pSelf->m_VoteCreator = pResult->m_ClientId;
pSelf->m_pScore->RandomUnfinishedMap(pSelf->m_VoteCreator, Stars); pSelf->m_pScore->RandomUnfinishedMap(pSelf->m_VoteCreator, Stars);
} }
@ -3194,6 +3208,37 @@ void CGameContext::ConSetTeamAll(IConsole::IResult *pResult, void *pUserData)
pSelf->m_pController->DoTeamChange(pPlayer, Team, false); pSelf->m_pController->DoTeamChange(pPlayer, Team, false);
} }
void CGameContext::ConHotReload(IConsole::IResult *pResult, void *pUserData)
{
CGameContext *pSelf = (CGameContext *)pUserData;
for(int i = 0; i < MAX_CLIENTS; i++)
{
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(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;
pSelf->m_apSavedTeams[pSelf->m_aTeamMapping[i]] = new CSaveTeam();
pSelf->m_apSavedTeams[pSelf->m_aTeamMapping[i]]->Save(pSelf, pSelf->m_aTeamMapping[i], true, true);
}
pSelf->Server()->ReloadMap();
}
void CGameContext::ConAddVote(IConsole::IResult *pResult, void *pUserData) void CGameContext::ConAddVote(IConsole::IResult *pResult, void *pUserData)
{ {
CGameContext *pSelf = (CGameContext *)pUserData; CGameContext *pSelf = (CGameContext *)pUserData;
@ -3346,6 +3391,7 @@ void CGameContext::ConForceVote(IConsole::IResult *pResult, void *pUserData)
{ {
str_format(aBuf, sizeof(aBuf), "authorized player forced server option '%s' (%s)", pValue, pReason); str_format(aBuf, sizeof(aBuf), "authorized player forced server option '%s' (%s)", pValue, pReason);
pSelf->SendChatTarget(-1, aBuf, FLAG_SIX); pSelf->SendChatTarget(-1, aBuf, FLAG_SIX);
pSelf->m_VoteCreator = pResult->m_ClientId;
pSelf->Console()->ExecuteLine(pOption->m_aCommand); pSelf->Console()->ExecuteLine(pOption->m_aCommand);
break; break;
} }
@ -3420,8 +3466,18 @@ void CGameContext::ConClearVotes(IConsole::IResult *pResult, void *pUserData)
struct CMapNameItem struct CMapNameItem
{ {
char m_aName[IO_MAX_PATH_LENGTH - 4]; char m_aName[IO_MAX_PATH_LENGTH - 4];
bool m_IsDirectory;
bool operator<(const CMapNameItem &Other) const { return str_comp_nocase(m_aName, Other.m_aName) < 0; } static bool CompareFilenameAscending(const CMapNameItem Lhs, const CMapNameItem Rhs)
{
if(str_comp(Lhs.m_aName, "..") == 0)
return true;
if(str_comp(Rhs.m_aName, "..") == 0)
return false;
if(Lhs.m_IsDirectory != Rhs.m_IsDirectory)
return Lhs.m_IsDirectory;
return str_comp_filenames(Lhs.m_aName, Rhs.m_aName) < 0;
}
}; };
void CGameContext::ConAddMapVotes(IConsole::IResult *pResult, void *pUserData) void CGameContext::ConAddMapVotes(IConsole::IResult *pResult, void *pUserData)
@ -3429,19 +3485,48 @@ void CGameContext::ConAddMapVotes(IConsole::IResult *pResult, void *pUserData)
CGameContext *pSelf = (CGameContext *)pUserData; CGameContext *pSelf = (CGameContext *)pUserData;
std::vector<CMapNameItem> vMapList; std::vector<CMapNameItem> vMapList;
pSelf->Storage()->ListDirectory(IStorage::TYPE_ALL, "maps", MapScan, &vMapList); const char *pDirectory = pResult->GetString(0);
std::sort(vMapList.begin(), vMapList.end());
// Don't allow moving to parent directories
if(str_find_nocase(pDirectory, ".."))
return;
char aPath[IO_MAX_PATH_LENGTH] = "maps/";
str_append(aPath, pDirectory, sizeof(aPath));
pSelf->Storage()->ListDirectory(IStorage::TYPE_ALL, aPath, MapScan, &vMapList);
std::sort(vMapList.begin(), vMapList.end(), CMapNameItem::CompareFilenameAscending);
for(auto &Item : vMapList) for(auto &Item : vMapList)
{ {
char aDescription[64]; if(!str_comp(Item.m_aName, "..") && (!str_comp(aPath, "maps/")))
str_format(aDescription, sizeof(aDescription), "Map: %s", Item.m_aName); continue;
char aCommand[IO_MAX_PATH_LENGTH * 2 + 10]; char aDescription[VOTE_DESC_LENGTH];
char aMapEscaped[IO_MAX_PATH_LENGTH * 2]; str_format(aDescription, sizeof(aDescription), "%s: %s%s", Item.m_IsDirectory ? "Directory" : "Map", Item.m_aName, Item.m_IsDirectory ? "/" : "");
char *pDst = aMapEscaped;
str_escape(&pDst, Item.m_aName, aMapEscaped + sizeof(aMapEscaped)); char aCommand[VOTE_CMD_LENGTH];
str_format(aCommand, sizeof(aCommand), "change_map \"%s\"", aMapEscaped); char aOptionEscaped[IO_MAX_PATH_LENGTH * 2];
char *pDst = aOptionEscaped;
str_escape(&pDst, Item.m_aName, aOptionEscaped + sizeof(aOptionEscaped));
char aDirectory[IO_MAX_PATH_LENGTH] = "";
if(pResult->NumArguments())
str_copy(aDirectory, pDirectory);
if(!str_comp(Item.m_aName, ".."))
{
fs_parent_dir(aDirectory);
str_format(aCommand, sizeof(aCommand), "clear_votes; add_map_votes \"%s\"", aDirectory);
}
else if(Item.m_IsDirectory)
{
str_append(aDirectory, "/", sizeof(aDirectory));
str_append(aDirectory, aOptionEscaped, sizeof(aDirectory));
str_format(aCommand, sizeof(aCommand), "clear_votes; add_map_votes \"%s\"", aDirectory);
}
else
str_format(aCommand, sizeof(aCommand), "change_map \"%s/%s\"", pDirectory, aOptionEscaped);
pSelf->AddVote(aDescription, aCommand); pSelf->AddVote(aDescription, aCommand);
} }
@ -3451,11 +3536,15 @@ void CGameContext::ConAddMapVotes(IConsole::IResult *pResult, void *pUserData)
int CGameContext::MapScan(const char *pName, int IsDir, int DirType, void *pUserData) int CGameContext::MapScan(const char *pName, int IsDir, int DirType, void *pUserData)
{ {
if(IsDir || !str_endswith(pName, ".map")) if((!IsDir && !str_endswith(pName, ".map")) || !str_comp(pName, "."))
return 0; return 0;
CMapNameItem Item; CMapNameItem Item;
str_truncate(Item.m_aName, sizeof(Item.m_aName), pName, str_length(pName) - str_length(".map")); Item.m_IsDirectory = IsDir;
if(!IsDir)
str_truncate(Item.m_aName, sizeof(Item.m_aName), pName, str_length(pName) - str_length(".map"));
else
str_copy(Item.m_aName, pName);
static_cast<std::vector<CMapNameItem> *>(pUserData)->push_back(Item); static_cast<std::vector<CMapNameItem> *>(pUserData)->push_back(Item);
return 0; return 0;
@ -3552,12 +3641,13 @@ void CGameContext::OnConsoleInit()
Console()->Register("say", "r[message]", CFGFLAG_SERVER, ConSay, this, "Say in chat"); Console()->Register("say", "r[message]", CFGFLAG_SERVER, ConSay, this, "Say in chat");
Console()->Register("set_team", "i[id] i[team-id] ?i[delay in minutes]", CFGFLAG_SERVER, ConSetTeam, this, "Set team of player to team"); Console()->Register("set_team", "i[id] i[team-id] ?i[delay in minutes]", CFGFLAG_SERVER, ConSetTeam, this, "Set team of player to team");
Console()->Register("set_team_all", "i[team-id]", CFGFLAG_SERVER, ConSetTeamAll, this, "Set team of all players to team"); Console()->Register("set_team_all", "i[team-id]", CFGFLAG_SERVER, ConSetTeamAll, this, "Set team of all players to team");
Console()->Register("hot_reload", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConHotReload, this, "Reload the map while preserving the state of tees and teams");
Console()->Register("add_vote", "s[name] r[command]", CFGFLAG_SERVER, ConAddVote, this, "Add a voting option"); Console()->Register("add_vote", "s[name] r[command]", CFGFLAG_SERVER, ConAddVote, this, "Add a voting option");
Console()->Register("remove_vote", "r[name]", CFGFLAG_SERVER, ConRemoveVote, this, "remove a voting option"); Console()->Register("remove_vote", "r[name]", CFGFLAG_SERVER, ConRemoveVote, this, "remove a voting option");
Console()->Register("force_vote", "s[name] s[command] ?r[reason]", CFGFLAG_SERVER, ConForceVote, this, "Force a voting option"); Console()->Register("force_vote", "s[name] s[command] ?r[reason]", CFGFLAG_SERVER, ConForceVote, this, "Force a voting option");
Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, "Clears the voting options"); Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, "Clears the voting options");
Console()->Register("add_map_votes", "", CFGFLAG_SERVER, ConAddMapVotes, this, "Automatically adds voting options for all maps"); Console()->Register("add_map_votes", "?s[directory]", CFGFLAG_SERVER, ConAddMapVotes, this, "Automatically adds voting options for all maps");
Console()->Register("vote", "r['yes'|'no']", CFGFLAG_SERVER, ConVote, this, "Force a vote to yes/no"); Console()->Register("vote", "r['yes'|'no']", CFGFLAG_SERVER, ConVote, this, "Force a vote to yes/no");
Console()->Register("votes", "?i[page]", CFGFLAG_SERVER, ConVotes, this, "Show all votes (page 0 by default, 20 entries per page)"); Console()->Register("votes", "?i[page]", CFGFLAG_SERVER, ConVotes, this, "Show all votes (page 0 by default, 20 entries per page)");
Console()->Register("dump_antibot", "", CFGFLAG_SERVER, ConDumpAntibot, this, "Dumps the antibot status"); Console()->Register("dump_antibot", "", CFGFLAG_SERVER, ConDumpAntibot, this, "Dumps the antibot status");
@ -3703,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("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("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("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("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("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"); 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");
@ -4745,7 +4835,6 @@ void CGameContext::ForceVote(int EnforcerId, bool Success)
return; return;
m_VoteEnforce = Success ? CGameContext::VOTE_ENFORCE_YES_ADMIN : CGameContext::VOTE_ENFORCE_NO_ADMIN; m_VoteEnforce = Success ? CGameContext::VOTE_ENFORCE_YES_ADMIN : CGameContext::VOTE_ENFORCE_NO_ADMIN;
m_VoteEnforcer = EnforcerId;
char aBuf[256]; char aBuf[256];
const char *pOption = Success ? "yes" : "no"; const char *pOption = Success ? "yes" : "no";

Some files were not shown because too many files have changed in this diff Show more