diff --git a/.gitignore b/.gitignore index e56142b1e5..b4f040817c 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,4 @@ bam.exe .settings *.opensdf -DDRace* +DDNet* diff --git a/BUILDING_ANDROID b/BUILDING_ANDROID new file mode 100644 index 0000000000..a4fd5d90b6 --- /dev/null +++ b/BUILDING_ANDROID @@ -0,0 +1,26 @@ +This is how I build DDNet for Android: + +# Cloning the building repo with the SDL port for Android by Pelya +cd /media +git clone https://github.com/pelya/commandergenius.git + +# Get the most recent DDNet source +cd /media/commandergenius/project/jni/application/teeworlds +rm -rf src DDRace64.zip* +wget "https://github.com/def-/teeworlds/archive/DDRace64.zip" +unzip DDRace64.zip +mv teeworlds-DDRace64 src +mkdir src/src/game/generated +# Also the generated files don't get generated, copy them by hand +cp /media/ddrace/src/game/generated/* src/src/game/generated +rm -rf AndroidData +./AndroidPreBuild.sh + +# Actual compilation, needs a key to sign +cd /media/commandergenius +./changeAppSettings.sh -a +android update project -p project +./build.sh +jarsigner -verbose -keystore ~/.android/release.keystore -storepass MYSECRETPASS -sigalg MD5withRSA -digestalg SHA1 project/bin/MainActivity-release-unsigned.apk androidreleasekey +zipalign 4 project/bin/MainActivity-release-unsigned.apk project/bin/MainActivity-release.apk +scp project/bin/MainActivity-release.apk ddnet:/var/www/downloads/DDNet-$VERSION.apk diff --git a/README.md b/README.md new file mode 100644 index 0000000000..fec5d4d598 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +[DDraceNetwork](http://ddnet.tw) +================================ + +Our own flavor of DDRace, a Teeworlds mod. See the [website](http://ddnet.tw) for more information. diff --git a/announcement.txt b/announcement.txt index b361b965f6..26b2edecba 100644 --- a/announcement.txt +++ b/announcement.txt @@ -1,4 +1 @@ -# (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. -# This file has the text that is announced every once and a while on the server -Please Visit DDRace.info to report any bugs Thanks -# This File must always have a line break at the end of file +Please visit ddnet.tw to report any bugs. diff --git a/autoexec.cfg b/autoexec.cfg new file mode 100644 index 0000000000..ed4058ea1a --- /dev/null +++ b/autoexec.cfg @@ -0,0 +1,6 @@ +sv_name "Testserver with DDraceNetwork Features" +sv_port 8303 +sv_map "gravity" +sv_test_cmds 1 # set to 0 for DDraceNetwork game type +sv_rcon_password "rcon" +sv_register 1 diff --git a/bam.lua b/bam.lua index c250618aea..bfae4d63a4 100644 --- a/bam.lua +++ b/bam.lua @@ -8,6 +8,8 @@ Import("other/freetype/freetype.lua") config = NewConfig() config:Add(OptCCompiler("compiler")) config:Add(OptTestCompileC("stackprotector", "int main(){return 0;}", "-fstack-protector -fstack-protector-all")) +config:Add(OptTestCompileC("minmacosxsdk", "int main(){return 0;}", "-mmacosx-version-min=10.5 -isysroot /Developer/SDKs/MacOSX10.5.sdk")) +config:Add(OptTestCompileC("macosxppc", "int main(){return 0;}", "-arch ppc")) config:Add(OptLibrary("zlib", "zlib.h", false)) config:Add(SDL.OptFind("sdl", true)) config:Add(FreeType.OptFind("freetype", true)) @@ -144,6 +146,11 @@ function build(settings) --settings.objdir = Path("objs") settings.cc.Output = Intermediate_Output + --settings.cc.flags:Add("-m32") + --settings.link.flags:Add("-m32") + settings.link.flags:Add("-static-libgcc") + settings.link.flags:Add("-static-libstdc++") + cflags = os.getenv("CFLAGS") if cflags then settings.cc.flags:Add(cflags) @@ -155,8 +162,6 @@ function build(settings) if config.compiler.driver == "cl" then settings.cc.flags:Add("/wd4244") - settings.cc.flags:Add("/wd4291") - settings.cc.flags:Add("/EHsc") else settings.cc.flags:Add("-Wall") if family == "windows" then @@ -165,6 +170,10 @@ function build(settings) elseif platform == "macosx" then settings.cc.flags:Add("-mmacosx-version-min=10.5") settings.link.flags:Add("-mmacosx-version-min=10.5") + if config.minmacosxsdk.value == 1 then + settings.cc.flags:Add("-isysroot /Developer/SDKs/MacOSX10.5.sdk") + settings.link.flags:Add("-isysroot /Developer/SDKs/MacOSX10.5.sdk") + end elseif config.stackprotector.value == 1 then settings.cc.flags:Add("-fstack-protector", "-fstack-protector-all") settings.link.flags:Add("-fstack-protector", "-fstack-protector-all") @@ -179,8 +188,11 @@ function build(settings) if platform == "macosx" then settings.link.frameworks:Add("Carbon") settings.link.frameworks:Add("AppKit") + settings.link.libs:Add("dl") else settings.link.libs:Add("pthread") + settings.link.libs:Add("dl") + settings.link.libs:Add("rt") end if platform == "solaris" then @@ -221,6 +233,7 @@ function build(settings) if string.find(settings.config_name, "sql") then server_settings.link.libs:Add("mysqlcppconn-static") server_settings.link.libs:Add("mysqlclient") + server_settings.link.libs:Add("dl") end if platform == "macosx" then @@ -271,7 +284,7 @@ function build(settings) versionserver = Compile(settings, Collect("src/versionsrv/*.cpp")) masterserver = Compile(settings, Collect("src/mastersrv/*.cpp")) game_shared = Compile(settings, Collect("src/game/*.cpp"), nethash, network_source) - game_client = Compile(settings, CollectRecursive("src/game/client/*.cpp"), client_content_source) + game_client = Compile(client_settings, CollectRecursive("src/game/client/*.cpp"), client_content_source) game_server = Compile(settings, CollectRecursive("src/game/server/*.cpp"), server_content_source) game_editor = Compile(settings, Collect("src/game/editor/*.cpp")) @@ -292,11 +305,11 @@ function build(settings) end -- build client, server, version server and master server - client_exe = Link(client_settings, "DDRace", game_shared, game_client, + client_exe = Link(client_settings, "DDNet", game_shared, game_client, engine, client, game_editor, zlib, pnglite, wavpack, client_link_other, client_osxlaunch) - server_exe = Link(server_settings, "DDRace-Server", engine, server, + server_exe = Link(server_settings, "DDNet-Server", engine, server, game_shared, game_server, zlib, server_link_other) serverlaunch = {} @@ -454,47 +467,74 @@ if platform == "macosx" then release_sql_settings_x86_64.link.flags:Add("-arch x86_64") release_sql_settings_x86_64.cc.defines:Add("CONF_RELEASE", "CONF_SQL") - x86_d = build(debug_settings_x86_64) - sql_x86_d = build(debug_sql_settings_x86_64) - x86_r = build(release_settings_x86_64) - sql_x86_r = build(release_sql_settings_x86_64) + x86_64_d = build(debug_settings_x86_64) + sql_x86_64_d = build(debug_sql_settings_x86_64) + x86_64_r = build(release_settings_x86_64) + sql_x86_64_r = build(release_sql_settings_x86_64) end + DefaultTarget("game_debug_x86") - if arch == "ia32" then - PseudoTarget("release", ppc_r, x86_r) - PseudoTarget("debug", ppc_d, x86_d) - PseudoTarget("server_release", "server_release_x86", "server_release_ppc") - PseudoTarget("server_debug", "server_debug_x86", "server_debug_ppc") - PseudoTarget("client_release", "client_release_x86", "client_release_ppc") - PseudoTarget("client_debug", "client_debug_x86", "client_debug_ppc") - PseudoTarget("sql_release", sql_ppc_r, sql_x86_r) - PseudoTarget("sql_debug", sql_ppc_d, sql_x86_d) - PseudoTarget("server_sql_release", "server_sql_release_x86", "server_sql_release_ppc") - PseudoTarget("server_sql_debug", "server_sql_debug_x86", "server_sql_debug_ppc") - elseif arch == "amd64" then - PseudoTarget("release", ppc_r, x86_r, x86_64_r) - PseudoTarget("debug", ppc_d, x86_d, x86_64_d) - PseudoTarget("server_release", "server_release_x86", "server_release_x86_64", "server_release_ppc") - PseudoTarget("server_debug", "server_debug_x86", "server_release_x86_64", "server_debug_ppc") - PseudoTarget("client_release", "client_release_x86", "server_release_x86_64", "client_release_ppc") - PseudoTarget("client_debug", "client_debug_x86", "server_release_x86_64", "client_debug_ppc") - PseudoTarget("sql_release", sql_ppc_r, sql_x86_r, sql_x86_64_r) - PseudoTarget("sql_debug", sql_ppc_d, sql_x86_d, sql_x86_64_r) - PseudoTarget("server_sql_release", "server_sql_release_x86", "server_sql_release_x86_64", "server_sql_release_ppc") - PseudoTarget("server_sql_debug", "server_sql_debug_x86", "server_sql_debug_x86_64", "server_sql_debug_ppc") + + if config.macosxppc.value == 1 then + if arch == "ia32" then + PseudoTarget("release", ppc_r, x86_r) + PseudoTarget("debug", ppc_d, x86_d) + PseudoTarget("server_release", "server_release_ppc", "server_release_x86") + PseudoTarget("server_debug", "server_debug_ppc", "server_debug_x86") + PseudoTarget("client_release", "client_release_ppc", "client_release_x86") + PseudoTarget("client_debug", "client_debug_ppc", "client_debug_x86") + PseudoTarget("sql_release", sql_ppc_r, sql_x86_r) + PseudoTarget("sql_debug", sql_ppc_d, sql_x86_d) + PseudoTarget("server_sql_release", "server_sql_release_ppc", "server_sql_release_x86") + PseudoTarget("server_sql_debug", "server_sql_debug_ppc", "server_sql_debug_x86") + elseif arch == "amd64" then + PseudoTarget("release", ppc_r, x86_r, x86_64_r) + PseudoTarget("debug", ppc_d, x86_d, x86_64_d) + PseudoTarget("server_release", "server_release_ppc", "server_release_x86", "server_release_x86_64") + PseudoTarget("server_debug", "server_debug_ppc", "server_debug_x86", "server_debug_x86_64") + PseudoTarget("client_release", "client_release_ppc", "client_release_x86", "client_release_x86_64") + PseudoTarget("client_debug", "client_debug_ppc", "client_debug_x86", "client_debug_x86_64") + PseudoTarget("sql_release", sql_ppc_r, sql_x86_r, sql_x86_64_r) + PseudoTarget("sql_debug", sql_ppc_d, sql_x86_d, sql_x86_64_d) + PseudoTarget("server_sql_release", "server_sql_release_ppc", "server_sql_release_x86", "server_sql_release_x86_64") + PseudoTarget("server_sql_debug", "server_sql_debug_ppc", "server_sql_debug_x86", "server_sql_debug_x86_64") + else + PseudoTarget("release", ppc_r) + PseudoTarget("debug", ppc_d) + PseudoTarget("server_release", "server_release_ppc") + PseudoTarget("server_debug", "server_debug_ppc") + PseudoTarget("client_release", "client_release_ppc") + PseudoTarget("client_debug", "client_debug_ppc") + PseudoTarget("sql_release", sql_ppc_r) + PseudoTarget("sql_debug", sql_ppc_d) + PseudoTarget("server_sql_release", "server_sql_release_ppc") + PseudoTarget("server_sql_debug", "server_sql_debug_ppc") + end else - PseudoTarget("release", ppc_r) - PseudoTarget("debug", ppc_d) - PseudoTarget("server_release", "server_release_ppc") - PseudoTarget("server_debug", "server_debug_ppc") - PseudoTarget("client_release", "client_release_ppc") - PseudoTarget("client_debug", "client_debug_ppc") - PseudoTarget("sql_release", sql_ppc_r) - PseudoTarget("sql_debug", sql_ppc_d) - PseudoTarget("server_sql_release", "server_sql_release_ppc") - PseudoTarget("server_sql_debug", "server_sql_debug_ppc") + if arch == "ia32" then + PseudoTarget("release", x86_r) + PseudoTarget("debug", x86_d) + PseudoTarget("server_release", "server_release_x86") + PseudoTarget("server_debug", "server_debug_x86") + PseudoTarget("client_release", "client_release_x86") + PseudoTarget("client_debug", "client_debug_x86") + PseudoTarget("sql_release", sql_x86_r) + PseudoTarget("sql_debug", sql_x86_d) + PseudoTarget("server_sql_release", "server_sql_release_x86") + PseudoTarget("server_sql_debug", "server_sql_debug_x86") + elseif arch == "amd64" then + PseudoTarget("release", x86_r, x86_64_r) + PseudoTarget("debug", x86_d, x86_64_d) + PseudoTarget("server_release", "server_release_x86", "server_release_x86_64") + PseudoTarget("server_debug", "server_debug_x86", "server_debug_x86_64") + PseudoTarget("client_release", "client_release_x86", "client_release_x86_64") + PseudoTarget("client_debug", "client_debug_x86", "client_debug_x86_64") + PseudoTarget("sql_release", sql_x86_r, sql_x86_64_r) + PseudoTarget("sql_debug", sql_x86_d, sql_x86_64_d) + PseudoTarget("server_sql_release", "server_sql_release_x86", "server_sql_release_x86_64") + PseudoTarget("server_sql_debug", "server_sql_debug_x86", "server_sql_debug_x86_64") + end end - else build(debug_settings) build(debug_sql_settings) diff --git a/configure.lua b/configure.lua index 26825b06fd..2db70cc4d2 100644 --- a/configure.lua +++ b/configure.lua @@ -376,6 +376,8 @@ function OptCCompiler(name, default_driver, default_c, default_cxx, desc) SetDriversCL(settings) elseif option.driver == "gcc" then SetDriversGCC(settings) + elseif option.driver == "clang" then + SetDriversClang(settings) else error(option.driver.." is not a known c/c++ compile driver") end @@ -393,7 +395,7 @@ function OptCCompiler(name, default_driver, default_c, default_cxx, desc) local printhelp = function(option) local a = "" if option.desc then a = "for "..option.desc end - print("\t"..option.name.."=gcc|cl") + print("\t"..option.name.."=gcc|cl|clang") print("\t\twhat c/c++ compile driver to use"..a) print("\t"..option.name..".c=FILENAME") print("\t\twhat c compiler executable to use"..a) diff --git a/data/arrow.png b/data/arrow.png new file mode 100644 index 0000000000..ad7da36aa0 Binary files /dev/null and b/data/arrow.png differ diff --git a/data/editor/ddnet-tiles.rules b/data/editor/ddnet-tiles.rules new file mode 100644 index 0000000000..94d762bb81 --- /dev/null +++ b/data/editor/ddnet-tiles.rules @@ -0,0 +1,103 @@ +[DDNet] +Index 110 +BaseTile + +Index 94 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos 0 1 FULL +Pos 1 0 FULL +Pos 1 -1 EMPTY +Pos -1 1 EMPTY +Pos -1 -1 EMPTY + +Index 94 XFLIP +Pos 0 -1 EMPTY +Pos 1 0 EMPTY +Pos 0 1 FULL +Pos -1 0 FULL +Pos -1 -1 EMPTY +Pos 1 1 EMPTY +Pos 1 -1 EMPTY + +Index 94 YFLIP +Pos 0 1 EMPTY +Pos -1 0 EMPTY +Pos 0 -1 FULL +Pos 1 0 FULL +Pos 1 1 EMPTY +Pos -1 -1 EMPTY +Pos -1 1 EMPTY + +Index 94 XFLIP YFLIP +Pos 0 1 EMPTY +Pos 1 0 EMPTY +Pos 0 -1 FULL +Pos -1 0 FULL +Pos -1 1 EMPTY +Pos 1 -1 EMPTY +Pos 1 1 EMPTY + +Index 95 +Pos 0 0 EMPTY +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos 0 1 FULL +Pos 1 0 FULL +Pos 1 -1 FULL +Pos -1 1 FULL + +Index 95 XFLIP +Pos 0 0 EMPTY +Pos 0 -1 EMPTY +Pos 1 0 EMPTY +Pos 0 1 FULL +Pos -1 0 FULL +Pos -1 -1 FULL +Pos 1 1 FULL + +Index 95 XFLIP YFLIP +Pos 0 0 EMPTY +Pos 0 1 EMPTY +Pos 1 0 EMPTY +Pos 0 -1 FULL +Pos -1 0 FULL +Pos -1 1 FULL +Pos 1 -1 FULL + +Index 95 XFLIP YFLIP +Pos 0 0 EMPTY +Pos 0 1 EMPTY +Pos 1 0 EMPTY +Pos 0 -1 FULL +Pos -1 0 FULL +Pos -1 1 FULL +Pos 1 -1 FULL + +Index 108 +Pos 0 0 EMPTY +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos 0 1 FULL +Pos 1 0 FULL + +Index 108 XFLIP +Pos 0 0 EMPTY +Pos 0 -1 EMPTY +Pos 1 0 EMPTY +Pos 0 1 FULL +Pos -1 0 FULL + +Index 108 YFLIP +Pos 0 0 EMPTY +Pos 0 1 EMPTY +Pos -1 0 EMPTY +Pos 0 -1 FULL +Pos 1 0 FULL + +Index 108 XFLIP YFLIP +Pos 0 0 EMPTY +Pos 0 1 EMPTY +Pos 1 0 EMPTY +Pos 0 -1 FULL +Pos -1 0 FULL diff --git a/data/editor/ddnet-walls.rules b/data/editor/ddnet-walls.rules new file mode 100644 index 0000000000..770b70252e --- /dev/null +++ b/data/editor/ddnet-walls.rules @@ -0,0 +1,553 @@ +[Basic Walls] + +Index 16 +BaseTile + + +#1W2W3W4W +Index 17 +Pos 0 0 FULL +Pos 0 -1 EMPTY +Pos 0 1 EMPTY + +Pos -1 0 EMPTY +Pos 1 0 EMPTY + + +#1W2W3S4W +Index 18 +Pos 0 0 FULL + Pos 0 -1 EMPTY + +Pos -1 0 EMPTY +Pos 0 1 FULL +Pos 1 0 EMPTY + + +#1W2W3S4W +Index 18 YFLIP +Pos 0 0 FULL + Pos 0 -1 FULL +Pos -1 0 EMPTY +Pos 0 1 EMPTY +Pos 1 0 EMPTY + + +#1W2S3S4W +Index 19 +Pos 0 0 FULL + Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos 0 1 FULL +Pos 1 0 FULL + +Pos 1 1 FULL + +#1W2S3S4W +Index 19 YFLIP +Pos 0 0 FULL + Pos 0 -1 FULL +Pos -1 0 EMPTY +Pos 0 1 EMPTY +Pos 1 0 FULL +Pos 1 -1 FULL + +#1W2S3S4W +Index 19 XFLIP +Pos 0 0 FULL + Pos 0 -1 EMPTY +Pos 0 1 FULL +Pos -1 0 FULL +Pos 1 0 EMPTY + +Pos -1 1 FULL + +#1W2S3S4W +Index 19 XFLIP YFLIP +Pos 0 0 FULL + Pos -1 -1 FULL +Pos 0 -1 FULL +Pos -1 0 FULL +Pos 1 0 EMPTY +Pos 0 1 EMPTY + +#1W2S3W4S +Index 20 +Pos 0 0 FULL +Pos -1 0 FULL +Pos 1 0 FULL +Pos 0 -1 EMPTY +Pos 0 1 EMPTY + +#1W2S3S4S +Index 21 +Pos 0 0 FULL +Pos -1 1 FULL + +Pos 0 1 FULL +Pos 1 1 FULL + + + Pos 1 0 FULL +Pos -1 0 FULL +Pos 0 -1 EMPTY + + +#1W2S3S4S +Index 21 YFLIP +Pos 0 0 FULL +Pos -1 -1 FULL + +Pos 1 -1 FULL +Pos 0 -1 FULL + + Pos 1 0 FULL +Pos -1 0 FULL +Pos 0 1 EMPTY + +#1D2D3D4D +Index 32 +Pos 0 0 FULL +Pos -1 -1 EMPTY +Pos 0 -1 FULL +Pos 1 -1 EMPTY +Pos -1 0 FULL +Pos 1 0 FULL +Pos -1 1 EMPTY +Pos 0 1 FULL +Pos 1 1 EMPTY + +#1D2S3D4D +Index 33 +Pos 0 0 FULL +Pos -1 -1 EMPTY +Pos 0 -1 FULL +Pos 1 -1 EMPTY +Pos -1 0 FULL +Pos 1 0 FULL +Pos -1 1 EMPTY +Pos 0 1 FULL +Pos 1 1 FULL + +#1D2S3D4D +Index 33 XFLIP +Pos 0 0 FULL +Pos -1 -1 EMPTY +Pos 0 -1 FULL +Pos 1 -1 EMPTY +Pos -1 0 FULL +Pos 1 0 FULL +Pos -1 1 FULL +Pos 0 1 FULL +Pos 1 1 EMPTY + +#1D2S3D4D +Index 33 YFLIP +Pos 0 0 FULL +Pos -1 -1 EMPTY +Pos 0 -1 FULL +Pos 1 -1 FULL +Pos -1 0 FULL +Pos 1 0 FULL +Pos -1 1 EMPTY +Pos 0 1 FULL +Pos 1 1 EMPTY + +#1D2S3D4D +Index 33 XFLIP YFLIP +Pos 0 0 FULL +Pos -1 -1 FULL +Pos 0 -1 FULL +Pos 1 -1 EMPTY +Pos -1 0 FULL +Pos 1 0 FULL +Pos -1 1 EMPTY +Pos 0 1 FULL +Pos 1 1 EMPTY + +#1D2S3S4D +Index 34 +Pos -1 -1 EMPTY +Pos 0 -1 FULL +Pos 1 -1 EMPTY +Pos -1 0 FULL +Pos 0 0 FULL +Pos 1 0 FULL +Pos -1 1 FULL +Pos 0 1 FULL +Pos 1 1 FULL + +#1D2S3S4D +Index 34 YFLIP +Pos -1 -1 FULL +Pos 0 -1 FULL +Pos 1 -1 FULL +Pos -1 0 FULL +Pos 0 0 FULL +Pos 1 0 FULL +Pos -1 1 EMPTY +Pos 0 1 FULL +Pos 1 1 EMPTY + +#1S2S3S4D +Index 35 +Pos -1 -1 EMPTY +Pos 0 -1 FULL +Pos 1 -1 FULL +Pos -1 0 FULL +Pos 0 0 FULL +Pos 1 0 FULL +Pos -1 1 FULL +Pos 0 1 FULL +Pos 1 1 FULL + +#1S2S3S4D +Index 35 XFLIP +Pos -1 -1 FULL +Pos 0 -1 FULL +Pos 1 -1 EMPTY +Pos -1 0 FULL +Pos 0 0 FULL +Pos 1 0 FULL +Pos -1 1 FULL +Pos 0 1 FULL +Pos 1 1 FULL + +#1S2S3S4D +Index 35 YFLIP +Pos -1 -1 FULL +Pos 0 -1 FULL +Pos 1 -1 FULL +Pos -1 0 FULL +Pos 0 0 FULL +Pos 1 0 FULL +Pos -1 1 EMPTY +Pos 0 1 FULL +Pos 1 1 FULL + +#1S2S3S4D +Index 35 XFLIP YFLIP +Pos -1 -1 FULL +Pos 0 -1 FULL +Pos 1 -1 FULL +Pos -1 0 FULL +Pos 0 0 FULL +Pos 1 0 FULL +Pos -1 1 FULL +Pos 0 1 FULL +Pos 1 1 EMPTY + +#1S2D3S4D +Index 36 +Pos -1 -1 EMPTY +Pos 0 -1 FULL +Pos 1 -1 FULL +Pos -1 0 FULL +Pos 0 0 FULL +Pos 1 0 FULL +Pos -1 1 FULL +Pos 0 1 FULL +Pos 1 1 EMPTY + +#1S2D3S4D +Index 36 XFLIP +Pos -1 -1 FULL +Pos 0 -1 FULL +Pos 1 -1 EMPTY +Pos -1 0 FULL +Pos 0 0 FULL +Pos 1 0 FULL +Pos -1 1 EMPTY +Pos 0 1 FULL +Pos 1 1 FULL + +#1S2S3D4D +Index 37 +Pos -1 -1 EMPTY +Pos 0 -1 FULL +Pos 1 -1 FULL +Pos -1 0 FULL +Pos 0 0 FULL +Pos 1 0 FULL +Pos -1 1 EMPTY +Pos 0 1 FULL +Pos 1 1 FULL + +#1S2S3D4D +Index 37 XFLIP +Pos -1 -1 FULL +Pos 0 -1 FULL +Pos 1 -1 EMPTY +Pos -1 0 FULL +Pos 0 0 FULL +Pos 1 0 FULL +Pos -1 1 FULL +Pos 0 1 FULL +Pos 1 1 EMPTY + +#1W2D3S4W +Index 48 +Pos 0 0 FULL +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos 0 1 FULL +Pos 1 1 EMPTY +Pos 1 0 FULL + +#1W2D3S4W +Index 48 XFLIP +Pos 0 0 FULL +Pos 0 -1 EMPTY +Pos 1 0 EMPTY +Pos -1 1 EMPTY +Pos 0 1 FULL +Pos -1 0 FULL + +#1W2D3S4W +Index 48 YFLIP +Pos 0 0 FULL +Pos -1 0 EMPTY +Pos 0 1 EMPTY +Pos 1 -1 EMPTY +Pos 0 -1 FULL +Pos 1 0 FULL + +#1W2D3S4W +Index 48 XFLIP YFLIP +Pos 0 0 FULL +Pos 1 0 EMPTY +Pos 0 1 EMPTY +Pos -1 -1 EMPTY +Pos 0 -1 FULL +Pos -1 0 FULL + +#1W2D3D4S +Index 49 +Pos -1 1 EMPTY + +Pos 0 1 FULL +Pos 1 1 EMPTY + + +Pos 0 0 FULL + Pos 1 0 FULL +Pos -1 0 FULL +Pos 0 -1 EMPTY + + + +#1W2D3D4S +Index 49 YFLIP +Pos -1 -1 EMPTY + +Pos 1 -1 EMPTY +Pos 0 -1 FULL + +Pos 0 0 FULL + Pos 1 0 FULL +Pos -1 0 FULL +Pos 0 1 EMPTY + +#1W2S3D4S +Index 50 +Pos -1 1 EMPTY + +Pos 0 1 FULL +Pos 1 1 FULL + + +Pos 0 0 FULL + Pos 1 0 FULL +Pos -1 0 FULL +Pos 0 -1 EMPTY + +#1W2S3D4S +Index 50 XFLIP +Pos -1 1 FULL + +Pos 0 1 FULL +Pos 1 1 EMPTY + + +Pos 0 0 FULL + Pos 1 0 FULL +Pos -1 0 FULL +Pos 0 -1 EMPTY + + + +#1W2S3D4S +Index 50 YFLIP +Pos -1 -1 EMPTY + +Pos 1 -1 FULL +Pos 0 -1 FULL + +Pos 0 0 FULL + Pos 1 0 FULL +Pos -1 0 FULL +Pos 0 1 EMPTY + + +#1W2S3D4S +Index 50 XFLIP YFLIP +Pos -1 -1 FULL + +Pos 1 -1 EMPTY +Pos 0 -1 FULL + +Pos 0 0 FULL + Pos 1 0 FULL +Pos -1 0 FULL +Pos 0 1 EMPTY + +#1S2W3S4W +Index 51 +Pos 0 0 FULL +Pos -1 0 EMPTY +Pos 1 0 EMPTY +Pos 0 -1 FULL +Pos 0 1 FULL + +#1W2S3W4W +Index 52 + Pos 0 -1 EMPTY + +Pos -1 0 EMPTY +Pos 0 0 FULL +Pos 1 0 FULL + + + Pos 0 1 EMPTY + + +#1W2S3W4W +Index 52 XFLIP + Pos 0 -1 EMPTY + +Pos -1 0 FULL +Pos 0 0 FULL +Pos 1 0 EMPTY + + + Pos 0 1 EMPTY + + +#1S2S3S4W +Index 53 + Pos 0 -1 FULL + +Pos -1 0 EMPTY +Pos 1 0 FULL + + +Pos 0 0 FULL + Pos 0 1 FULL +Pos 1 -1 FULL +Pos 1 1 FULL + + +#1S2S3S4W +Index 53 XFLIP + Pos 0 -1 FULL + +Pos -1 -1 FULL +Pos -1 0 FULL + +Pos -1 1 FULL + Pos 0 0 FULL + +Pos 1 0 EMPTY + + + Pos 0 1 FULL + + +#1D2D3S4W +Index 64 + Pos 0 -1 FULL + +Pos -1 0 EMPTY +Pos 1 0 FULL + + +Pos 0 0 FULL + Pos 0 1 FULL +Pos 1 -1 EMPTY +Pos 1 1 EMPTY + + +#1D2D3S4W +Index 64 XFLIP + Pos 0 -1 FULL + +Pos -1 -1 EMPTY +Pos -1 0 FULL + +Pos -1 1 EMPTY + Pos 0 0 FULL + +Pos 1 0 EMPTY + + + Pos 0 1 FULL + + +#1S2W3D4S +Index 65 + Pos 0 -1 FULL + +Pos -1 -1 FULL +Pos -1 0 FULL + +Pos -1 1 EMPTY + Pos 0 0 FULL + +Pos 1 0 EMPTY + + + Pos 0 1 FULL + + +#1S2W3D4S +Index 65 XFLIP + Pos 0 -1 FULL + +Pos -1 0 EMPTY +Pos 1 0 FULL + + +Pos 0 0 FULL + Pos 0 1 FULL +Pos 1 -1 FULL +Pos 1 1 EMPTY + + +#1S2W3D4S +Index 65 YFLIP + Pos 0 -1 FULL + +Pos -1 -1 EMPTY +Pos -1 0 FULL + +Pos -1 1 FULL + Pos 0 0 FULL + +Pos 1 0 EMPTY + + + Pos 0 1 FULL + + +#1S2W3D4S +Index 65 XFLIP YFLIP + Pos 0 -1 FULL + +Pos -1 0 EMPTY +Pos 1 0 FULL + + +Pos 0 0 FULL + Pos 0 1 FULL +Pos 1 -1 EMPTY +Pos 1 1 FULL diff --git a/data/editor/entities.png b/data/editor/entities.png index 2e21391871..8b05a16dbb 100644 Binary files a/data/editor/entities.png and b/data/editor/entities.png differ diff --git a/data/editor/entities_clear.png b/data/editor/entities_clear.png index f092ec83ee..34b69bbbbe 100644 Binary files a/data/editor/entities_clear.png and b/data/editor/entities_clear.png differ diff --git a/data/editor/fadeout.rules b/data/editor/fadeout.rules new file mode 100644 index 0000000000..e84838ac1e --- /dev/null +++ b/data/editor/fadeout.rules @@ -0,0 +1,93 @@ +[Fadeout] +Index 6 +BaseTile + +Index 6 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +Index 7 +Pos 0 -1 FULL +Pos -1 0 EMPTY +Pos 0 1 FULL +Pos 1 0 FULL +Pos 1 -1 FULL +Pos 1 1 FULL + +Index 7 XFLIP +Pos 0 -1 FULL +Pos -1 0 FULL +Pos 0 1 FULL +Pos 1 0 EMPTY +Pos -1 -1 FULL +Pos -1 1 FULL + +Index 8 +Pos 0 -1 EMPTY +Pos -1 0 FULL +Pos 0 1 FULL +Pos 1 0 FULL +Pos -1 1 FULL +Pos 1 1 FULL + +Index 8 YFLIP +Pos 0 -1 FULL +Pos -1 0 FULL +Pos 0 1 EMPTY +Pos 1 0 FULL +Pos -1 -1 FULL +Pos 1 -1 FULL + +Index 9 +Pos 0 -1 FULL +Pos -1 0 FULL +Pos 0 1 FULL +Pos 1 0 FULL +Pos 1 1 EMPTY + +Index 9 YFLIP +Pos 0 -1 FULL +Pos -1 0 FULL +Pos 0 1 FULL +Pos 1 0 FULL +Pos 1 -1 EMPTY + +Index 9 XFLIP +Pos 0 -1 FULL +Pos -1 0 FULL +Pos 0 1 FULL +Pos 1 0 FULL +Pos -1 1 EMPTY + +Index 9 XFLIP YFLIP +Pos 0 -1 FULL +Pos -1 0 FULL +Pos 0 1 FULL +Pos 1 0 FULL +Pos -1 -1 EMPTY + +Index 10 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos 0 1 FULL +Pos 1 0 FULL + +Index 10 XFLIP +Pos 0 -1 EMPTY +Pos -1 0 FULL +Pos 0 1 FULL +Pos 1 0 EMPTY + +Index 10 YFLIP +Pos 0 -1 FULL +Pos -1 0 EMPTY +Pos 0 1 EMPTY +Pos 1 0 FULL + +Index 10 XFLIP YFLIP +Pos 0 -1 FULL +Pos -1 0 FULL +Pos 0 1 EMPTY +Pos 1 0 EMPTY diff --git a/data/editor/front.png b/data/editor/front.png index f4a625bb1c..3ba4a16fec 100644 Binary files a/data/editor/front.png and b/data/editor/front.png differ diff --git a/data/editor/grass_main.rules b/data/editor/grass_main.rules index b909eb0e84..c032a22df7 100644 --- a/data/editor/grass_main.rules +++ b/data/editor/grass_main.rules @@ -223,3 +223,81 @@ Index 28 Pos -1 -1 EMPTY Pos -1 0 FULL Pos 0 -1 FULL + +[Freeze] +Index 110 +BaseTile + +Index 110 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +Index 108 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +Index 109 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +Index 124 +Pos -1 0 EMPTY +Pos 0 1 EMPTY + +Index 125 +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +[Unfreeze] +Index 78 +BaseTile + +Index 78 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +Index 76 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +Index 77 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +Index 92 +Pos -1 0 EMPTY +Pos 0 1 EMPTY + +Index 93 +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +[Tele] +Index 78 +BaseTile + +Index 142 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +Index 140 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +Index 141 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +Index 156 +Pos -1 0 EMPTY +Pos 0 1 EMPTY + +Index 157 +Pos 0 1 EMPTY +Pos 1 0 EMPTY diff --git a/data/editor/round-tiles.rules b/data/editor/round-tiles.rules new file mode 100644 index 0000000000..f405173bf8 --- /dev/null +++ b/data/editor/round-tiles.rules @@ -0,0 +1,87 @@ +[DDNet] +Index 1 +BaseTile + +Index 2 +Pos 0 -1 EMPTY +Pos 0 1 FULL +Pos 1 0 EMPTY +Pos 1 -1 EMPTY + +Index 2 XFLIP +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos 1 0 FULL +Pos -1 -1 EMPTY + +Index 2 YFLIP +Pos -1 0 FULL +Pos 0 1 EMPTY +Pos 1 0 EMPTY +Pos 1 1 EMPTY + +Index 2 XFLIP YFLIP +Pos 0 -1 FULL +Pos -1 0 EMPTY +Pos 0 1 EMPTY +Pos -1 1 EMPTY + +Index 33 +Pos 0 0 EMPTY +Pos 0 -1 FULL +Pos -1 0 FULL +Pos 1 -1 FULL +Pos -1 1 FULL + +Index 33 XFLIP +Pos 0 0 EMPTY +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 -1 FULL +Pos 1 1 FULL + +Index 33 YFLIP +Pos 0 0 EMPTY +Pos 0 1 FULL +Pos -1 0 FULL +Pos 1 1 FULL +Pos -1 -1 FULL + +Index 33 XFLIP YFLIP +Pos 0 0 EMPTY +Pos 0 1 FULL +Pos 1 0 FULL +Pos -1 1 FULL +Pos 1 -1 FULL + +Index 18 +Pos 0 -1 FULL +Pos -1 0 FULL +Pos -1 -1 FULL +Pos 0 1 EMPTY +Pos 1 0 EMPTY +Pos 1 1 EMPTY + +Index 18 XFLIP +Pos 0 -1 FULL +Pos 1 0 FULL +Pos 1 -1 FULL +Pos 0 1 EMPTY +Pos -1 0 EMPTY +Pos -1 1 EMPTY + +Index 18 YFLIP +Pos 0 1 FULL +Pos -1 0 FULL +Pos -1 1 FULL +Pos 0 -1 EMPTY +Pos 1 0 EMPTY +Pos 1 -1 EMPTY + +Index 18 XFLIP YFLIP +Pos 0 1 FULL +Pos 1 0 FULL +Pos 1 1 FULL +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos -1 -1 EMPTY diff --git a/data/editor/switch.png b/data/editor/switch.png index 327fb86689..74c0a930f7 100644 Binary files a/data/editor/switch.png and b/data/editor/switch.png differ diff --git a/data/editor/tele.png b/data/editor/tele.png index bc278fedea..eba388d78d 100644 Binary files a/data/editor/tele.png and b/data/editor/tele.png differ diff --git a/data/editor/tune.png b/data/editor/tune.png new file mode 100644 index 0000000000..2a643bbdd5 Binary files /dev/null and b/data/editor/tune.png differ diff --git a/data/languages/belarusian.txt b/data/languages/belarusian.txt new file mode 100644 index 0000000000..a2a19fb0ae --- /dev/null +++ b/data/languages/belarusian.txt @@ -0,0 +1,1018 @@ +##### authors ##### +#originally created by: +# arionwt1997 +#modified by: +# +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% загружана + +%ds left +== засталося %d сек. + +%i minute left +== Засталася %i хвіліна! + +%i minutes left +== Засталося %i хвілін! + +%i second left +== Засталася %i секунда! + +%i seconds left +== Засталося %i секунд! + +%s wins! +== %s перамог! + +-Page %d- +== -Старонка %d- + +Abort +== Адмена + +Add +== Дадаць + +Add Friend +== Дадаць сябра + +Address +== Адрас + +All +== Усё + +Always show name plates +== Заўсёды паказваць нікі гульцоў + +Are you sure that you want to delete the demo? +== Вы ўпэўнены, што жадаеце выдаліць дэма? + +Are you sure that you want to quit? +== Вы сапраўды жадаеце выйсці? + +Are you sure that you want to remove the player from your friends list? +== Вы ўпэўнены, што жадаеце выдаліць гульца з сяброў? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Бо гэта ваш першы запуск гульні, калі ласка, увядзіце свой нік у поле ніжэй. Таксама рекоммендуется праверыць налады гульні і памяняць некаторыя з іх перад тым, як пачаць гуляць. + +Automatically record demos +== Аўтаматычна запісваць дэма + +Automatically take game over screenshot +== Рабіць здымак вынікаў гульні + +Blue team +== Сінія + +Blue team wins! +== Сінія перамаглі! + +Body +== Цела + +Call vote +== Галасаваць + +Change settings +== Змяніць налады + +Chat +== Чат + +Clan +== Клан + +Client +== Кліент + +Close +== Выйсці + +Compatible version +== Сумяшчальная версія + +Connect +== Падлучыцца + +Connecting to +== Падлучэнне да + +Connection Problems... +== Праблемы з сувяззю... + +Console +== Кансоль + +Controls +== Кіраванне + +Count players only +== Лічыць толькі гульцоў + +Country +== Сцяг вашай краіны + +Current +== Бягучы + +Custom colors +== Свае колеры + +Delete +== Выдаліць + +Delete demo +== Выдаліць дэма + +Demo details +== Дэталі дэма + +Demofile: %s +== Дэма: %s + +Demos +== Дэма + +Disconnect +== Адключыць + +Disconnected +== Адключана + +Downloading map +== Спампоўка карты + +Draw! +== Нічыя! + +Dynamic Camera +== Дынамічная камера + +Emoticon +== Эмоцыі + +Enter +== Уваход + +Error +== Памылка + +Error loading demo +== памылка пры загрузцы дэма + +Favorite +== Абраны + +Favorites +== Абраныя + +Feet +== Ногі + +Filter +== Фільтр + +Fire +== Стрэл + +Folder +== Тэчка + +Force vote +== Фарсіраваць + +Free-View +== Вольны агляд + +Friends +== Сябры + +Fullscreen +== Поўнаэкранны рэжым + +Game +== Гульня + +Game info +== Інфа пра гульню + +Game over +== Гульня скончана + +Game type +== Тып гульні + +Game types: +== Тып гульні: + +General +== Асноўныя + +Graphics +== Графіка + +Grenade +== Гранатамёт + +Hammer +== Молат + +Has people playing +== Не пусты сервер + +High Detail +== Высокая дэталізацыя + +Hook +== Крук + +Invalid Demo +== Недапушчальнае дэма + +Join blue +== За сініх + +Join red +== За чырвоных + +Jump +== Скачок + +Kick player +== Забаніць гульца + +Language +== Мова + +Loading +== Загрузка + +MOTD +== MOTD + +Map +== Карта + +Maximum ping: +== Макс. пінг: + +Mouse sens. +== Адчув. мышы + +Move left +== Крок налева + +Move player to spectators +== Зрабіць назіральнікам + +Move right +== Крок направа + +Movement +== Перасоўванне + +Mute when not active +== Глушыць гукі, калі гульня неактыўная + +Name +== Імя + +Next weapon +== След. зброя + +Nickname +== Нік + +No +== Не + +No password +== Без пароля + +No servers found +== Сервера не знойдзены + +No servers match your filter criteria +== Няма сервераў, падыходных пад ваш фільтр + +Ok +== ОК + +Open +== Адкрыць + +Parent Folder +== Бацькоўскі каталог + +Password +== Пароль + +Password incorrect +== Пароль + +Ping +== Пінг + +Pistol +== Пісталет + +Play +== Прагляд + +Play background music +== Гуляць фонавую музыку + +Player +== Гулец + +Player country: +== Краіна: + +Player options +== Опцыі гульца + +Players +== Гульцы + +Please balance teams! +== Збалансуйце каманды! + +Prev. weapon +== Прад. зброя + +Quality Textures +== Якасныя тэкстуры + +Quit +== Выйсце + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Чыннік: + +Red team +== Чырвоныя + +Red team wins! +== Чырвоныя перамаглі! + +Refresh +== Абнавіць + +Refreshing master servers +== Абнаўленне спісу майстар-сервераў + +Remote console +== Кансоль сервера + +Remove +== Выдаліць + +Remove friend +== Выдаліць сябра + +Rename +== Пераназв. + +Rename demo +== Пераназваць дэма + +Reset filter +== Скінуць фільтры + +Sample rate +== Чашчыня + +Score +== Ачкі + +Score board +== Табло + +Score limit +== Ліміт ачкоў + +Scoreboard +== Табло + +Screenshot +== Здымак + +Server address: +== Адрас сервера + +Server details +== Дэталі сервера + +Server filter +== Фільтр сервераў + +Server info +== Інфармацыя + +Server not full +== Сервер не запоўнены + +Settings +== Налады + +Shotgun +== Драбавік + +Show chat +== Паказаць чат + +Show friends only +== Толькі з сябрамі + +Show ingame HUD +== Паказваць нутрагульнявы HUD + +Show name plates +== Паказваць нікі гульцоў + +Show only supported +== Паказваць толькі падтрымоўваныя дазволы экрана + +Skins +== Скіны + +Sound +== Гук + +Sound error +== Гукавая памылка + +Spectate +== Назіраць + +Spectate next +== Назіраць наст. + +Spectate previous +== Назіраць папяр. + +Spectator mode +== Назіральнік + +Spectators +== Назіральнікі + +Standard gametype +== Стандартны тып гульні + +Standard map +== Стандартная карта + +Stop record +== Стоп + +Strict gametype filter +== Строгі фільтр рэжым. + +Sudden Death +== Хуткая смерць + +Switch weapon on pickup +== Перамыкаць зброю пры падборы + +Team +== Каманда + +Team chat +== Камандны чат + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Выйшла Teeworlds %s! Спампоўвайце на www.teeworlds.com! + +Texture Compression +== Сціск тэкстур + +The audio device couldn't be initialised. +== Аўдыё прылада не можа быць ініцыялізавана + +The server is running a non-standard tuning on a pure game type. +== Сервер запушчаны з нестандартнымі наладамі на стандартным тыпе гульні. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Ёсць незахаваная карта ў рэдактары, Вы можаце захаваць яе перад тым, як выйсці. + +Time limit +== Ліміт часу + +Time limit: %d min +== Ліміт часу: %d + +Try again +== ОК + +Type +== Тып + +Unable to delete the demo +== Немагчыма выдаліць дэма + +Unable to rename the demo +== Немагчыма пераназваць дэма + +Use sounds +== Выкарыстоўваць гукі + +Use team colors for name plates +== Камандныя колеры для нікаў гульцоў + +V-Sync +== Вертыкальная сінхранізацыя + +Version +== Версія + +Vote command: +== Камманда галасавання: + +Vote description: +== Апісанне галасавання: + +Vote no +== Супраць + +Vote yes +== За + +Voting +== Галасаванне + +Warmup +== Размінка + +Weapon +== Зброя + +Welcome to Teeworlds +== Сардэчна запрашаем у Teeworlds! + +Yes +== Так + +You must restart the game for all settings to take effect. +== Перазапусціце гульню для ўжывання змен. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Новае імя + +Sat. +== Кантраст + +Miscellaneous +== Дадаткова + +Internet +== Інтэрнэт + +Max demos +== Максімальная колькасць дэма + +News +== Навіны + +Join game +== Гуляць + +FSAA samples +== Сэмплаў FSAA + +%d of %d servers, %d players +== %d з %d сервераў, %d гульцоў + +Sound volume +== Гучнасць + +Created: +== Створаны: + +Max Screenshots +== Максімальная колькасць здымкаў + +Length: +== Даўжыня + +Skin name +== + +Rifle +== Бласцер + +Patterns +== + +Netversion: +== Версія: + +Map: +== Карта: + +%d Bytes +== %d байт + +Coloration +== + +Info +== Інфа + +Hue +== Адценне + +Record demo +== Запісаць дэма + +Your skin +== Ваш скін + +Size: +== Памер: + +Reset to defaults +== Скінуць налады + +Quit anyway? +== Выйсці? + +Display Modes +== Дазвол экрана + +Version: +== Версія: + +Round +== Раўнд + +Lht. +== Яркасць + +no limit +== Без ліміту + +Quick search: +== Хуткі пошук: + +UI Color +== Колер інтэрфейсу + +Host address +== Адрас сервера + +Crc: +== Crc: + +Alpha +== Празрыст. + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Бягучая версія: %s + +LAN +== LAN + +Name plates size +== Памер + +Type: +== Тып: + diff --git a/data/languages/bosnian.txt b/data/languages/bosnian.txt new file mode 100644 index 0000000000..2e58e88927 --- /dev/null +++ b/data/languages/bosnian.txt @@ -0,0 +1,1020 @@ +##### authors ##### +#originally created by: +# *** +#modified by: +# *** 2011-01-05 18:48:00 +# *** 2011-04-12 17:53:42 +# *** 2011-07-04 23:31:22 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== Učitano %d%% + +%ds left +== Još %ds + +%i minute left +== Preostalo: %i min. + +%i minutes left +== Preostalo: %i min. + +%i second left +== Preostalo: %i sek. + +%i seconds left +== Preostalo: %i sek. + +%s wins! +== %s je pobijedio! + +-Page %d- +== -Strana %d- + +Abort +== Prekini + +Add +== Dodaj + +Add Friend +== Dodaj prijatelja + +Address +== Adresa + +All +== Svi + +Always show name plates +== Uvijek prikaži imena igrača + +Are you sure that you want to delete the demo? +== Jeste li sigurni da želite obrisati demo-snimak? + +Are you sure that you want to quit? +== Jeste li sigurni da želite izići? + +Are you sure that you want to remove the player from your friends list? +== Da li ste sigurni da želite ukloniti igrača iz vaše liste prijatelja? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Pošto prvi put pokrećete igru, molimo da ispod unesete Vaš nadimak (nick). Preporučujemo da provjerite postavke i podesite ih prema Vašem ukusu prije nego se konektujete na server. + +Automatically record demos +== Automatski demo-snimci + +Automatically take game over screenshot +== Automatski screenshot po završetku igre + +Blue team +== Plavi tim + +Blue team wins! +== Plavi tim je pobijedio! + +Body +== Tijelo + +Call vote +== Glasanje + +Change settings +== Izmijeni postavke + +Chat +== Chat + +Clan +== Klan + +Client +== Klijent + +Close +== Zatvori + +Compatible version +== Kompatibilna verzija + +Connect +== Konektuj + +Connecting to +== Konektujem na + +Connection Problems... +== Problemi sa konekcijom... + +Console +== Konzola + +Controls +== Kontrole + +Count players only +== Izbroj samo igrače + +Country +== Država + +Current +== Current + +Custom colors +== Vlastite boje + +Delete +== Obriši + +Delete demo +== Brisanje demo-snimka + +Demo details +== Podaci o demo-snimku + +Demofile: %s +== Demo-snimak: %s + +Demos +== Demo + +Disconnect +== Diskonektuj + +Disconnected +== Diskonektovan + +Downloading map +== Preuzimam mapu + +Draw! +== Neriješeno! + +Dynamic Camera +== Dinamička kamera + +Emoticon +== Emoticon + +Enter +== Započni + +Error +== Greška + +Error loading demo +== Došlo je do greške pri učitavanju demo-snimka + +Favorite +== Omiljeni + +Favorites +== Omiljeni + +Feet +== Stopala + +Filter +== Filter + +Fire +== Pucanje + +Folder +== Direktorij + +Force vote +== Obavezno glasanje + +Free-View +== Slobodan pogled + +Friends +== Prijatelji + +Fullscreen +== Čitav ekran + +Game +== Igra + +Game info +== O igri + +Game over +== Igra je završena + +Game type +== Tip igre + +Game types: +== Tipovi igre: + +General +== Opće + +Graphics +== Grafika + +Grenade +== Granate + +Hammer +== Čekić + +Has people playing +== Server nije prazan + +High Detail +== Visoki detalji + +Hook +== Lanac + +Invalid Demo +== Neispravan demo-snimak + +Join blue +== U plavi tim + +Join red +== U crveni tim + +Jump +== Skok + +Kick player +== Izbaci igrača iz igre + +Language +== Jezik + +Loading +== Učitavam + +MOTD +== Poruka dana + +Map +== Mapa + +Maximum ping: +== Maksimalan ping: + +Mouse sens. +== Osjetljivost miša + +Move left +== Nalijevo + +Move player to spectators +== Izbaci igrača u posmatrače + +Move right +== Nadesno + +Movement +== Kretanje + +Mute when not active +== Bez zvuka prilikom neaktivnosti + +Name +== Ime + +Next weapon +== Sljedeće oružje + +Nickname +== Nadimak + +No +== Ne + +No password +== Bez lozinke + +No servers found +== Nije pronađen nijedan server + +No servers match your filter criteria +== Nijedan server ne odgovara zadatom kriteriju + +Ok +== OK + +Open +== Otvori + +Parent Folder +== Prethodni direktorij + +Password +== Lozinka + +Password incorrect +== Pogrešna lozinka + +Ping +== Ping + +Pistol +== Pištolj + +Play +== Pogledaj + +Play background music +== Pozadinska muzika + +Player +== Igrač + +Player country: +== Država + +Player options +== Postavke igrača + +Players +== Igrači + +Please balance teams! +== Molim uravnotežite timove! + +Prev. weapon +== Prethodno oružje + +Quality Textures +== Visokokvalitetne teksture + +Quit +== Izlaz + +REC %3d:%02d +== %3d:%02d + +Reason: +== Razlog: + +Red team +== Crveni tim + +Red team wins! +== Crveni tim je pobijedio! + +Refresh +== Osvježi + +Refreshing master servers +== Osvježavam glavne servere + +Remote console +== Udaljena konzola + +Remove +== Ukloni + +Remove friend +== Ukloni prijatelja + +Rename +== Preimenuj + +Rename demo +== Preimenuj demo-snimak + +Reset filter +== Poništi filter + +Sample rate +== Frekvencija + +Score +== Rezultat + +Score board +== Bodovi + +Score limit +== Max. bodova + +Scoreboard +== Bodovi + +Screenshot +== Screenshot + +Server address: +== Adresa servera: + +Server details +== Podaci o serveru + +Server filter +== Filter servera + +Server info +== O Serveru + +Server not full +== Server nije pun + +Settings +== Postavke + +Shotgun +== Pumparica + +Show chat +== Prikaži chat + +Show friends only +== Prikaži prijatelje + +Show ingame HUD +== Prikaži HUD + +Show name plates +== Prikaži imena igrača + +Show only supported +== Prikaži samo podržane + +Skins +== Izgled + +Sound +== Zvuk + +Sound error +== Problem sa zvukom + +Spectate +== Posmatraj + +Spectate next +== Posmatraj narednog + +Spectate previous +== Posmatraj prethodnog + +Spectator mode +== Posmatrački mod + +Spectators +== Posmatrači + +Standard gametype +== Standardni tip igre + +Standard map +== Standardna mapa + +Stop record +== Prekini snimanje + +Strict gametype filter +== Striktan filter tipa igre + +Sudden Death +== Iznenadna smrt + +Switch weapon on pickup +== Aktiviraj novo oružje prilikom uzimanja + +Team +== Tim + +Team chat +== Timski chat + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworld %s je objavljen! Preuzmi ga na www.teeworlds.com! + +Texture Compression +== Kompresija tekstura + +The audio device couldn't be initialised. +== Audio-uređaj nije moguće pokrenuti. + +The server is running a non-standard tuning on a pure game type. +== Server sadrži nestandardne postavke. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Mapa u editoru nije memorisana, možda je želite memorisati prije nego izađete iz igre. + +Time limit +== Max. vremena + +Time limit: %d min +== Max. vremena: %d min. + +Try again +== Pokušaj ponovo + +Type +== Tip + +Unable to delete the demo +== Demo-snimak nije moguće obrisati + +Unable to rename the demo +== Demo-snimak nije moguće preimenovati + +Use sounds +== Aktiviraj zvuk + +Use team colors for name plates +== Koristi timsku boju u prikazu imena + +V-Sync +== V-Sync + +Version +== Verzija + +Vote command: +== Komanda za glasanje: + +Vote description: +== Opis glasanja: + +Vote no +== Ne + +Vote yes +== Da + +Voting +== Glasanje + +Warmup +== Zagrijavanje + +Weapon +== Oružje + +Welcome to Teeworlds +== Dobrodošli u Teeworlds + +Yes +== Da + +You must restart the game for all settings to take effect. +== Morate ponovo pokrenuti igru da bi sve postavke bile primijenjene. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Novo ime: + +Sat. +== Zasić. + +Miscellaneous +== Razno + +Internet +== Internet + +Max demos +== Maximalan broj demo-snimaka + +News +== Novosti + +Join game +== Uključi se u igru + +FSAA samples +== FSAA samples + +%d of %d servers, %d players +== %d od %d server(a), %d igrač(a) + +Sound volume +== Jačina zvuka + +Created: +== Kreirano: + +Max Screenshots +== Maksimalan broj screenshot-a + +Length: +== Dužina: + +Skin name +== + +Rifle +== Laser + +Patterns +== + +Netversion: +== Net-verzija: + +Map: +== Mapa: + +%d Bytes +== %d bytes + +Coloration +== + +Info +== Info + +Hue +== Nijans. + +Record demo +== Snimi demo + +Your skin +== Vaš izgled + +Size: +== Veličina: + +Reset to defaults +== Vrati na početne postavke + +Quit anyway? +== Izlaz? + +Display Modes +== Rezolucija i način prikaza + +Version: +== Verzija: + +Round +== Runda + +Lht. +== Svjetl. + +no limit +== bez ograničenja + +Quick search: +== Brza pretraga: + +UI Color +== Boja menija + +Host address +== Adresa servera + +Crc: +== Crc: + +Alpha +== Provid. + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Trenutna verzija: %s + +LAN +== LAN + +Name plates size +== Veličina podloge za ime + +Type: +== Tip: + diff --git a/data/languages/brazilian_portuguese.txt b/data/languages/brazilian_portuguese.txt new file mode 100644 index 0000000000..1d51929a21 --- /dev/null +++ b/data/languages/brazilian_portuguese.txt @@ -0,0 +1,1026 @@ +##### authors ##### +#originally created by: +# slinack +#modified by: +# yemDX 2010-05-29 17:22:42 +# yemDX 2010-05-30 02:51:34 +# slinack 2010-06-02 20:57:46 +# slinack 2011-05-02 18:24:12 +# Isadora 2011-05-09 17:26:02 +# slinack 2011-07-03 00:23:26 +# Ryomou Hentai Girl 2011-07-11 11:33:42 +# Isadora 2011-07-20 21:47:11 +# HeroiAmarelo 2012-08-01 15:50:18 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% carregado + +%ds left +== Faltam %ds + +%i minute left +== Falta %i minuto + +%i minutes left +== Faltam %i minutos + +%i second left +== Falta %i segundo + +%i seconds left +== Faltam %i segundos + +%s wins! +== %s vence! + +-Page %d- +== -Página %d- + +Abort +== Cancelar + +Add +== Adicionar + +Add Friend +== Adicionar amigo + +Address +== Endereço + +All +== Todos + +Always show name plates +== Sempre mostrar apelidos + +Are you sure that you want to delete the demo? +== Tem certeza que deseja deletar o demo? + +Are you sure that you want to quit? +== Você tem certeza que deseja sair? + +Are you sure that you want to remove the player from your friends list? +== Tem certeza que deseja remover o jogador de sua lista de amigos? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Como esta é a primeira vez que você abre o jogo, por favor, coloque seu apelido abaixo. É recomendado que você verifique as configurações e então ajuste-as para suas preferências antes de entrar em um servidor. + +Automatically record demos +== Gravar demos automaticamente + +Automatically take game over screenshot +== Capturar tela final de jogo automaticamente + +Blue team +== Time azul + +Blue team wins! +== Vitória do time azul! + +Body +== Corpo + +Call vote +== Votar + +Change settings +== Mudar configurações + +Chat +== Chat + +Clan +== Clã + +Client +== Cliente + +Close +== Fechar + +Compatible version +== Versão compatível + +Connect +== Conectar + +Connecting to +== Conectando a + +Connection Problems... +== Problemas de conexão... + +Console +== Console + +Controls +== Controles + +Count players only +== Contar apenas jogadores + +Country +== País + +Current +== Atual + +Custom colors +== Cores personalizadas + +Delete +== Deletar + +Delete demo +== Deletar demo + +Demo details +== Detalhes do demo + +Demofile: %s +== Demo: %s + +Demos +== Demos + +Disconnect +== Desconectar + +Disconnected +== Desconectado + +Downloading map +== Baixando mapa + +Draw! +== Empate! + +Dynamic Camera +== Câmera dinâmica + +Emoticon +== Emoticon + +Enter +== Entrar + +Error +== Erro + +Error loading demo +== Erro ao carregar demo + +Favorite +== Favorito + +Favorites +== Favoritos + +Feet +== Pés + +Filter +== Filtro + +Fire +== Atirar + +Folder +== Pasta + +Force vote +== Forçar votação + +Free-View +== Visualização livre + +Friends +== Amigos + +Fullscreen +== Tela cheia + +Game +== Jogo + +Game info +== Jogo + +Game over +== Fim do jogo + +Game paused +== Game Pausado + +Game type +== Tipo de jogo + +Game types: +== Tipos de jogo: + +General +== Geral + +Graphics +== Gráficos + +Grenade +== Granada + +Hammer +== Martelo + +Has people playing +== Há pessoas jogando + +High Detail +== Mostrar detalhes + +Hook +== Gancho + +Invalid Demo +== Demo inválido + +Join blue +== Azul + +Join red +== Vermelho + +Jump +== Pular + +Kick player +== Expulsar jogador + +Language +== Idioma + +Loading +== Carregando + +MOTD +== MOTD + +Map +== Mapa + +Maximum ping: +== Ping máximo: + +Mouse sens. +== Sens. do mouse + +Move left +== Esquerda + +Move player to spectators +== Mover jogador para espectadores + +Move right +== Direita + +Movement +== Movimento + +Mute when not active +== Silenciar quando inativo + +Name +== Nome + +Next weapon +== Próxima arma + +Nickname +== Apelido + +No +== Não + +No password +== Sem senha + +No servers found +== Nenhum servidor encontrado + +No servers match your filter criteria +== Nenhum servidor corresponde aos critérios do filtro + +Ok +== Ok + +Open +== Abrir + +Parent Folder +== Diretório pai + +Password +== Senha + +Password incorrect +== Senha incorreta + +Ping +== Ping + +Pistol +== Pistola + +Play +== Assistir + +Play background music +== Tocar música de fundo + +Player +== Jogador + +Player country: +== País do jogador: + +Player options +== Opções do jogador + +Players +== Jogadores + +Please balance teams! +== Favor balancear os times! + +Prev. weapon +== Arma anterior + +Quality Textures +== Texturas de qualidade + +Quit +== Sair + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Motivo: + +Red team +== Time vermelho + +Red team wins! +== Vitória do time vermelho! + +Refresh +== Atualizar + +Refreshing master servers +== Atualizando servidores mestres + +Remote console +== Console remoto + +Remove +== Deletar + +Remove friend +== Deletar amigo + +Rename +== Renomear + +Rename demo +== Renomear demo + +Reset filter +== Resetar filtro + +Respawn +== Respawn + +Sample rate +== Frequência do som + +Score +== Pontos + +Score board +== Placar + +Score limit +== Pontuação máx. + +Scoreboard +== Placar + +Screenshot +== Capturar tela + +Server address: +== Endereço: + +Server details +== Detalhes do servidor + +Server filter +== Filtro de servidores + +Server info +== Servidor + +Server not full +== Servidor não lotado + +Settings +== Configurações + +Shotgun +== Espingarda + +Show chat +== Mostrar chat + +Show friends only +== Mostrar apenas amigos + +Show ingame HUD +== Mostrar HUD do jogo + +Show name plates +== Mostrar apelidos + +Show only chat messages from friends +== Mostrar mensagens de amigos, apenas + +Show only supported +== Mostrar apenas modos suportados + +Skins +== Skins + +Sound +== Som + +Sound error +== Erro de som + +Spectate +== Observar + +Spectate next +== Observar próximo + +Spectate previous +== Observar anterior + +Spectator mode +== Modo espectador + +Spectators +== Espectadores + +Standard gametype +== Tipo de jogo padrão + +Standard map +== Mapa padrão + +Stop record +== Parar a gravação + +Strict gametype filter +== Filtrar tipo de jogo exato + +Sudden Death +== Morte Súbita + +Switch weapon on pickup +== Equipar arma ao pegá-la + +Team +== Time + +Team chat +== Chat do time + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s foi lançado! Baixe-o em www.teeworlds.com! + +Texture Compression +== Compressão de textura + +The audio device couldn't be initialised. +== O dispositivo de áudio não pôde ser inicializado. + +The server is running a non-standard tuning on a pure game type. +== O servidor está rodando com modificações em um tipo de jogo oficial. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Existe um mapa não salvo no editor, você pode querer salvá-lo antes de sair do jogo. + +Time limit +== Limite de tempo + +Time limit: %d min +== Limite de tempo: %d minutos + +Try again +== Tente de novo + +Type +== Tipo + +Unable to delete the demo +== Não foi possível deletar o demo + +Unable to rename the demo +== Não foi possível renomear o demo + +Use sounds +== Usar sons + +Use team colors for name plates +== Usar cores dos times em apelidos + +V-Sync +== V-Sync + +Version +== Versão + +Vote command: +== Comando da votação: + +Vote description: +== Descrição da votação: + +Vote no +== Votar Não + +Vote yes +== Votar Sim + +Voting +== Votação + +Warmup +== Aquecimento + +Weapon +== Arma + +Welcome to Teeworlds +== Bem-vindo ao Teeworlds! + +Yes +== Sim + +You must restart the game for all settings to take effect. +== Você deve reiniciar o jogo para que todas as alterações tenham efeito. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Novo nome: + +Sat. +== Saturação + +Miscellaneous +== Diversos + +Internet +== Internet + +Max demos +== Máximo de demos + +News +== Novidades + +Join game +== Entrar no jogo + +FSAA samples +== Amostras FSAA + +%d of %d servers, %d players +== %d de %d servidores, %d jogadores + +Sound volume +== Volume do som + +Created: +== Criado: + +Max Screenshots +== Máximo de capturas de tela + +Length: +== Duração: + +Skin name +== + +Rifle +== Laser + +Patterns +== + +Netversion: +== Netversion + +Map: +== Mapa: + +%d Bytes +== %d Bytes + +Coloration +== + +Info +== Informações + +Hue +== Matiz + +Record demo +== Gravar demo + +Your skin +== Sua skin + +Size: +== Tamanho: + +Reset to defaults +== Resetar configurações + +Quit anyway? +== Sair mesmo assim? + +Display Modes +== Modos de exibição + +Version: +== Versão + +Round +== Rodada + +Lht. +== Luminos. + +no limit +== sem limite + +Quick search: +== Pesquisa rápida: + +UI Color +== Cor do menu + +Host address +== Endereço do servidor + +Crc: +== Crc: + +Alpha +== Alpha + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Versão atual: %s + +LAN +== LAN + +Name plates size +== Tamanho dos apelidos + +Type: +== Tipo: + diff --git a/data/languages/bulgarian.txt b/data/languages/bulgarian.txt new file mode 100644 index 0000000000..3677cccbab --- /dev/null +++ b/data/languages/bulgarian.txt @@ -0,0 +1,1018 @@ +##### authors ##### +#originally created by: +# Hip-Hop_Blond +#modified by: +# Hip-Hop_Blond 2011-07-15 01:19:13 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% заредено + +%ds left +== остават %d сек. + +%i minute left +== %i минута остава + +%i minutes left +== %i минути остават + +%i second left +== %i секунда остава + +%i seconds left +== %i секунди остават + +%s wins! +== %s печели! + +-Page %d- +== -Страница %d- + +Abort +== Прекрати + +Add +== Добави + +Add Friend +== Добави Приятел + +Address +== Адрес + +All +== Всички + +Always show name plates +== Винаги показвай лентите с имената + +Are you sure that you want to delete the demo? +== Сигуен ли си че искаш да изтриеш това демо? + +Are you sure that you want to quit? +== Сигурен ли си че искаш да напуснеш? + +Are you sure that you want to remove the player from your friends list? +== Сигурен ли си че искаш да премахнеш този играч от листа с приятели? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Тъй като стартираш играта за първи път, моля въведи своя ник отдолу. Препоръчително е да провериш и нагласиш настройките по ваш свой избор преди да започнеш игра. + +Automatically record demos +== Записвай демота автоматично + +Automatically take game over screenshot +== Автоматична снимка в края на играта + +Blue team +== Отбор Сини + +Blue team wins! +== Отбор Сини печели! + +Body +== Тяло + +Call vote +== Гласувай + +Change settings +== Промени настройките + +Chat +== Чат + +Clan +== Клан + +Client +== Клиент + +Close +== Затвори + +Compatible version +== Съвместима версия + +Connect +== Впиши + +Connecting to +== Свързвам се с + +Connection Problems... +== Проблеми с Връзката... + +Console +== Конзола + +Controls +== Контроли + +Count players only +== Брой на играчите + +Country +== Държава + +Current +== Текущ + +Custom colors +== Произволни цветове + +Delete +== Изтрий + +Delete demo +== Изтрий демото + +Demo details +== Детайли за демото + +Demofile: %s +== Демофайл: %s + +Demos +== Демота + +Disconnect +== Отпиши + +Disconnected +== Отписан + +Downloading map +== Свалям карта + +Draw! +== Равни! + +Dynamic Camera +== Динамична Камера + +Emoticon +== Емотикони + +Enter +== Влез + +Error +== Грешка + +Error loading demo +== Грешка при зареждане на демо файл + +Favorite +== Любим + +Favorites +== Любими + +Feet +== Крака + +Filter +== Филтър + +Fire +== Стрелба + +Folder +== Папка + +Force vote +== Форсирай вот + +Free-View +== Свободен Изглед + +Friends +== Приятели + +Fullscreen +== Цял Екран + +Game +== Игра + +Game info +== Игра + +Game over +== Играта свърши + +Game type +== Тип игра + +Game types: +== Тип иги: + +General +== Основни + +Graphics +== Графика + +Grenade +== Граната + +Hammer +== Чук + +Has people playing +== Не показвай празни + +High Detail +== Висока Детайлност + +Hook +== Верига + +Invalid Demo +== Невалидно Демо + +Join blue +== Влез син + +Join red +== Влез червен + +Jump +== Скок + +Kick player +== Кикни играч + +Language +== Език + +Loading +== Зареждам + +MOTD +== MOTD + +Map +== Карта + +Maximum ping: +== Макс. пинг: + +Mouse sens. +== Мишка + +Move left +== На ляво + +Move player to spectators +== Премести играча в наблюдатели + +Move right +== На дясно + +Movement +== Движение + +Mute when not active +== Изключвай звука когато играта е неактивна + +Name +== Име + +Next weapon +== След. оръжие + +Nickname +== Ник + +No +== Не + +No password +== Без парола + +No servers found +== Няма намерени сървъри + +No servers match your filter criteria +== Няма сървъри съответстващи твоите настройки + +Ok +== Добре + +Open +== Отвори + +Parent Folder +== Предишна Папка + +Password +== Парола + +Password incorrect +== Грешна Парола + +Ping +== Пинг + +Pistol +== Пистолет + +Play +== Възпроизведи + +Play background music +== Пусни музика за фон + +Player +== Играч + +Player country: +== Страна на играча: + +Player options +== Настройки на Играча + +Players +== Играчи + +Please balance teams! +== Моля уравновесете отборите! + +Prev. weapon +== Пред. оръжие + +Quality Textures +== Качествени Текстури + +Quit +== Изход + +REC %3d:%02d +== Записани %3d:%02d + +Reason: +== Причина: + +Red team +== Отбор Червени + +Red team wins! +== Отбор Червени печели! + +Refresh +== Обнови + +Refreshing master servers +== Обновновявам мастър-сървърите + +Remote console +== Сървър Конзола + +Remove +== Премахни + +Remove friend +== Премахни приятел + +Rename +== Преименувай + +Rename demo +== Преименувай демо + +Reset filter +== Рестартирай филтрите + +Sample rate +== Честота + +Score +== Точки + +Score board +== Резултат + +Score limit +== Лимит на точките + +Scoreboard +== Резултат + +Screenshot +== Снимка + +Server address: +== Адрес на сървъра: + +Server details +== Детайли за Сървъра + +Server filter +== Филтър на сървъра + +Server info +== Инфо + +Server not full +== Не показвай пълните + +Settings +== Настройки + +Shotgun +== Пушка + +Show chat +== Показвай чата + +Show friends only +== Покажи приятели + +Show ingame HUD +== Показвай детайли в играта + +Show name plates +== Показвай лентите с имената + +Show only supported +== Показвай само съвместимите + +Skins +== Модели + +Sound +== Звук + +Sound error +== Грешка в звука + +Spectate +== Наблюдавай + +Spectate next +== Наблюдавай след. + +Spectator mode +== Наблюдател + +Spectators +== Наблюдатели + +Standard gametype +== Стандартен тип игри + +Standard map +== Стандартна карта + +Stop record +== Спри записа + +Strict gametype filter +== Стриктен геймтайп филтър + +Sudden Death +== Внезапна Смърт + +Switch weapon on pickup +== Сменяй оръжието при взимане + +Team +== Отбор + +Team chat +== Отборен чат + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Излезна Teeworlds %s! Свали новата версия от www.teeworlds.com! + +Texture Compression +== Компресиране на Текстурите + +The audio device couldn't be initialised. +== Аудио устройството не може да бъде стартирано. + +The server is running a non-standard tuning on a pure game type. +== Този сървър използва нестандартен тунинг на стандартен тип ига. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Има незапазена карта в редактора, ако напуснете картата няма да бъде запазена. + +Time limit +== Лимит на Времето + +Time limit: %d min +== Лимит на Времето: %d мин + +Try again +== Опитай пак + +Type +== Тип + +Unable to delete the demo +== Изтриването неуспешно + +Unable to rename the demo +== Преименуването неуспешно + +Use sounds +== Използвай Звук + +Use team colors for name plates +== Използвай цветове в лентите + +V-Sync +== Вертикална Синхронизация + +Version +== Версия + +Vote command: +== Вот команда: + +Vote description: +== Вот дефиниция: + +Vote no +== Против + +Vote yes +== За + +Voting +== Гласуване + +Warmup +== Разгрявка + +Weapon +== Оръжия + +Welcome to Teeworlds +== Добре дошли в Teeworlds! + +Yes +== Да + +You must restart the game for all settings to take effect. +== Трябва да рестартираш играта за да поемат ефект новите настройки. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectate previous +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Ново име: + +Sat. +== Контраст + +Miscellaneous +== Допълнителни + +Internet +== Интернет + +Max demos +== Максимум Демота + +News +== Новини + +Join game +== Влез + +FSAA samples +== FSAA семпли + +%d of %d servers, %d players +== %d/%d сървъри, %d играчи + +Sound volume +== Сила на звука + +Created: +== Създаден: + +Max Screenshots +== Максимум Снимки + +Length: +== Дължина: + +Skin name +== + +Rifle +== Лазер + +Patterns +== + +Netversion: +== Версия: + +Map: +== Карта: + +%d Bytes +== %d Байтове + +Coloration +== + +Info +== Инфо + +Hue +== Оттенък + +Record demo +== Запиши демо + +Your skin +== Твоят модел + +Size: +== Размер + +Reset to defaults +== Рестартирай настройките + +Quit anyway? +== Излезни все пак? + +Display Modes +== Режими на Показване + +Version: +== Версия: + +Round +== Рунд + +Lht. +== Яркост + +no limit +== Без лимит + +Quick search: +== Бързо Търсене: + +UI Color +== Цвят на Интерфейса + +Host address +== Адрес на сървъра + +Crc: +== Crc: + +Alpha +== Прозрачност + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Текуща версия: %s + +LAN +== ЛАН + +Name plates size +== Размер на лентата с имената + +Type: +== Тип: + diff --git a/data/languages/chuvash.txt b/data/languages/chuvash.txt new file mode 100644 index 0000000000..13ce5f3bee --- /dev/null +++ b/data/languages/chuvash.txt @@ -0,0 +1,1009 @@ +##### authors ##### +#originally created by: +# Watz +#modified by: +# +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% тиенĕ + +%ds left +== %d сек. + +%i minute left +== %i мин. + +%i minutes left +== %i мин. + +%i second left +== %i сек. + +%i seconds left +== %i сек. + +%s wins! +== %s çĕнтерчĕ! + +-Page %d- +== -Страница %d- + +Abort +== Каялла + +Add +== Хуш + +Add Friend +== Юлташа хуш + +Address +== Адрес + +All +== Пурте + +Always show name plates +== Вылякансен ятсене кăтартма + +Are you sure that you want to delete the demo? +== Эсир демо парахасшăн-и? + +Are you sure that you want to quit? +== Эсир тухасшăн-и? + +Are you sure that you want to remove the player from your friends list? +== Юшташу списокра тĕплесшĕн-и? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Пĕрремĕш вăйă умĕнче, хăвăн ята аяларах çыр. Ĕнерленӳсене тереслер, улăштар. + +Automatically record demos +== Демосене автоматикăла çыр + +Automatically take game over screenshot +== Вăйăн пайта çут-ӳкерчĕк ту + +Blue team +== Кăвак ушкăн + +Blue team wins! +== Кăвак ушкăн çĕнтерчĕ! + +Body +== Ӳт + +Call vote +== Сасăла + +Change settings +== Йĕркелӳсене урăхлатма + +Chat +== Калаçу + +Clan +== Кланĕ + +Client +== Клиент + +Close +== Хупма + +Compatible version +== Пĕрле ĕçлекен верси + +Connect +== Çых + +Connecting to +== Çыхăну + +Connection Problems... +== Çыхăну проблемасем + +Console +== Консоль + +Controls +== Пăхăнтару + +Count players only +== Çынсене анчах шутлама + +Country +== Çĕрĕн ялавĕ + +Current +== Хальхи + +Custom colors +== Усă кураканăн тĕсĕсем + +Delete +== Шăлса тасатма + +Delete demo +== Демо шăлса тасатма + +Demo details +== Демо тĕплĕсем + +Demofile: %s +== Демо: %s + +Demos +== Демосем + +Disconnect +== Çыхăну чарма + +Disconnected +== Çыхăну чарăнчĕ + +Downloading map +== Карта илӳ + +Draw! +== Никам та çĕнтермерĕ! + +Dynamic Camera +== Куçакан камера + +Emoticon +== Кăмăл-туйăмсем + +Enter +== Кĕрӳ + +Error +== Йăнăш + +Error loading demo +== Демо илĕвĕн йăнăш + +Favorite +== Суйланни + +Favorites +== Суйланнисем + +Feet +== Урасем + +Filter +== Фильтр + +Fire +== Пĕрӳ + +Folder +== Папка + +Force vote +== Вăйлăт + +Free-View +== Ирĕклĕ обзор + +Friends +== Юлташсем + +Fullscreen +== Тулли экран + +Game +== Вăйă + +Game info +== Вăйă пĕлтерӳ + +Game over +== Вăйа вĕçленчĕ + +Game type +== Вăйăн тĕс + +Game types: +== Вăйăн тĕсĕсем + +General +== Тĕп + +Graphics +== Ӳкерӳ + +Grenade +== Гранатомёт + +Hammer +== Мăлатук + +Has people playing +== Вылякан пур + +High Detail +== Лайăх ӳкерӳ + +Hook +== Çекĕл + +Invalid Demo +== Тĕрĕс мар демо + +Join blue +== Кăваксемпе! + +Join red +== Хĕрлĕсемпе! + +Jump +== Сик + +Kick player +== Вылякана бан + +Language +== Чĕлхе + +Loading +== Кĕт, тархасшăн + +MOTD +== MOTD + +Map +== Карта + +Maximum ping: +== Чи пысăк пинг: + +Mouse sens. +== Шăшин туйăмлăх + +Move left +== Сулахаялла + +Move player to spectators +== Пăхакансем çине куçарма + +Move right +== Сылтăмалла + +Movement +== Куçăм + +Mute when not active +== Тивмен чух сасăна шăплантар + +Name +== Ят + +Next weapon +== Тепĕр хĕç-пăшал + +Nickname +== Ят + +No +== Çук + +No password +== Вăрттăн сăмахсăр + +No servers found +== Серверсем çук + +No servers match your filter criteria +== Фильтр майлă серверсем çук + +Ok +== Çапла + +Open +== Уçма + +Parent Folder +== Аслă каталог + +Password +== Вăрттăн сăмах + +Password incorrect +== Тĕрĕс мар вăрттăн сăмах + +Ping +== Пинг + +Pistol +== Пистолет + +Play +== Пăхма + +Play background music +== Кĕвви + +Player +== Вылякан + +Player country: +== Çĕршыв: + +Player options +== Опцисем + +Players +== Çынсем + +Please balance teams! +== Вылякан ушкăнĕсене шайлаштар! + +Prev. weapon +== Малтанхи хĕç-пăшал + +Quality Textures +== Лайăх текстурасем + +Quit +== Туху + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Сăлтав: + +Red team +== Хĕрлĕ ушкăн + +Red team wins! +== Хĕрлĕ ушкăн çĕнтерчĕ! + +Refresh +== Каллех вула + +Refreshing master servers +== Мастер-серверсен каллех вулу + +Remote console +== Сервер консолĕ + +Remove +== Тасат + +Remove friend +== Юлташа тасат + +Rename +== Çĕнĕ ят + +Rename demo +== Демо çĕнĕ ят + +Reset filter +== Фильтрсене тасат + +Sample rate +== Тăтăшлăх + +Score +== Паллă + +Score board +== Паллăсен хăмми + +Score limit +== Паллă лимичĕ + +Scoreboard +== Паллăсен хăмми + +Screenshot +== Экран ӳкерӳ + +Server address: +== Сервер адресĕ + +Server details +== Сервер тĕплĕсем + +Server filter +== Серверсен фильтр + +Server info +== Пĕлтерӳ + +Server not full +== Сервер тулли мар + +Settings +== Йĕркелӳсем + +Shotgun +== Йĕтре пăшал + +Show chat +== Калаçӳ кăтартма + +Show friends only +== Юлташсемпе анчах + +Show ingame HUD +== Вăйă çинчи HUD кăтартма + +Show name plates +== Вылякансен ят кăтартма + +Show only supported +== Ĕçлекен экран режимĕсем анчах + +Skins +== Сăнсем + +Sound +== Сасă + +Sound error +== Сасă йăнăш + +Spectate +== Пăхма + +Spectate next +== Тепĕрне пăхма + +Spectate previous +== Малтанхине пăхма + +Spectator mode +== Пăхакан + +Spectators +== Пăхакансем + +Standard gametype +== Стандартлă вăйă тĕсĕ + +Standard map +== Стандартлă карта + +Stop record +== Чарма + +Strict gametype filter +== Хытă вăйă тĕс фильтрĕ + +Sudden Death +== Хăвăрт вилӳ + +Switch weapon on pickup +== Хĕç-пăшал илнĕ чух улăштарма + +Team +== Ушкăн + +Team chat +== Ушкăнри калаçу + +Texture Compression +== Текстур хĕсӳ + +The audio device couldn't be initialised. +== Аудио ярма пултараймасть + +The server is running a non-standard tuning on a pure game type. +== Серверта стардартлă мар опцисемпе таса вăйă тĕсĕрĕ + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Редакторта упраман карта пур. Туху умĕн ăна упрама пултарать + +Time limit +== Вăхăт лимичĕ + +Time limit: %d min +== Вăхăт лимичĕ: %d мин + +Try again +== Тепĕр хут + +Type +== Тĕс + +Unable to delete the demo +== Демо тĕплеме пултараймасть + +Unable to rename the demo +== Демо ячĕ улăштарма пулмасть + +Use sounds +== Сасă уса курма + +Use team colors for name plates +== Ушкăн тĕсĕсем ятсем валли + +V-Sync +== Вертикаллĕ синхронизаци + +Version +== Версие + +Vote command: +== Суйлав хушу: + +Vote description: +== Суйлав пирки: + +Vote failed +== Суйлу ватнă + +Vote no +== Хирĕç + +Vote yes +== Çинчен + +Voting +== Суйлав + +Warmup +== Карăну + +Weapon +== Хĕç-пăшал + +Welcome to Teeworlds +== Ырă сунса кĕтетпĕр Teeworlds-ра! + +Yes +== Çапла + +You must restart the game for all settings to take effect. +== Режим улăштарма валли вăйă сӳнтер тата каллер яр + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Teeworlds %s is out! Download it at www.teeworlds.com! +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Çĕн ят + +Max demos +== Чи пысăк демо хисепĕ + +Internet +== Интернет + +Round +== Раунд + +Quit anyway? +== Туху? + +News +== Хыпарсем + +Join game +== Выляма + +Crc: +== Crc: + +FSAA samples +== FSAA якату + +Sat. +== Контраст + +LAN +== LAN + +Sound volume +== Сасă янравлăш + +Created: +== Хайланă: + +%d Bytes +== %d байт + +Record demo +== Демо çыр + +%d of %d servers, %d players +== %d серв. %d тупнă, %d çын + +Miscellaneous +== Хушнисем + +Netversion: +== Версия: + +Info +== Пĕлтерӳ + +UI Color +== Интерфейс тĕсĕ + +Max Screenshots +== Ӳкерчĕксен чи пысăк хисепĕ + +Size: +== Пыçăкăш: + +Hue +== Тĕс сĕмĕ + +Your skin +== Санăн сăн + +Reset to defaults +== Йĕркелӳ тасат + +Rifle +== Бластер + +Display Modes +== Экран режимсем + +Version: +== Версие: + +Map: +== Карта: + +Lht. +== Çутăллăх + +Quick search: +== Хăвăрт шырав: + +Teeworlds %s is out! Download it at [url=http://www.teeworlds.com]www.teeworlds.com[/url]! +== Тухрĕ Teeworlds %s! Кунта: [url=http://www.teeworlds.com]www.teeworlds.com[/url]! + +Host address +== Сервер адресĕ + +Alpha +== Тăрă + +Length: +== Вăрăмăш + +no limit +== Лимитсăр + +Current version: %s +== Хальхи версие: %s + +Name plates size +== Пысăкăш + +Type: +== Тĕс: + diff --git a/data/languages/czech.txt b/data/languages/czech.txt new file mode 100644 index 0000000000..9a6313c632 --- /dev/null +++ b/data/languages/czech.txt @@ -0,0 +1,1021 @@ +##### authors ##### +#originally created by: +# +#modified by: +# khubajsn 2010-05-31 16:54:03 +# Petr 2011-04-02 23:02:33 +# Medik & Petr 2011-07-02 19:37:05 +# TeeWorlds-org 2011-07-15 00:34:19 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% načteno + +%ds left +== zbývá %ds + +%i minute left +== Zbývá minut: %i + +%i minutes left +== Zbývá minut: %i + +%i second left +== Zbývá sekund: %i + +%i seconds left +== Zbývá sekund: %i + +%s wins! +== Vítězí %s! + +-Page %d- +== -Strana %d- + +Abort +== Přerušit + +Add +== Přidat + +Add Friend +== Přidat do přátel + +Address +== Adresa + +All +== Všem + +Always show name plates +== Vždy zobrazovat + +Are you sure that you want to delete the demo? +== Opravdu chcete smazat tento záznam? + +Are you sure that you want to quit? +== Opravdu chcete ukončit Teeworlds? + +Are you sure that you want to remove the player from your friends list? +== Chcete odebrat tohoto hráče ze seznamu přátel? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Jelikož je toto Vaše první spuštění hry, zadejte prosím svou přezdívku. Než začnete hrát, doporučujeme si upravit nastavení tak, aby Vám vyhovovalo. + +Automatically record demos +== Automaticky nahrávat záznamy + +Automatically take game over screenshot +== Na konci kola vyfotit obrazovku + +Blue team +== Modří + +Blue team wins! +== Modří vyhráli! + +Body +== Tělo + +Call vote +== Hlasovat + +Change settings +== Změna nastavení + +Chat +== Chat + +Clan +== Herní klan + +Client +== Klient + +Close +== Zavřít + +Compatible version +== Kompatibilní verze + +Connect +== Připojit + +Connecting to +== Připojuji na + +Connection Problems... +== Chyba spojení... + +Console +== Konzola + +Controls +== Ovládání + +Count players only +== Započítat hrající hráče + +Country +== Národnost + +Current +== Aktuálně + +Custom colors +== Vlastní barvy + +Delete +== Smazat + +Delete demo +== Smazat záznam + +Demo details +== Detaily záznamu: + +Demofile: %s +== Soubor záznamu: %s + +Demos +== Záznamy + +Disconnect +== Odpojit + +Disconnected +== Odpojeno + +Downloading map +== Stahování mapy + +Draw! +== Remíza! + +Dynamic Camera +== Pohyblivá kamera + +Emoticon +== Emotikony + +Enter +== Vstoupit + +Error +== Chyba + +Error loading demo +== Chyba načítání záznamu + +Favorite +== Oblíbený + +Favorites +== Oblíbené + +Feet +== Nohy + +Filter +== Filtr + +Fire +== Střelba + +Folder +== Složka + +Force vote +== Zvolit + +Free-View +== Volný pohled + +Friends +== Přátelé + +Fullscreen +== Celá obrazovka + +Game +== Hra + +Game info +== Hra + +Game over +== Konec hry + +Game type +== Herní mód + +Game types: +== Herní mód: + +General +== Hlavní + +Graphics +== Video + +Grenade +== Granátomet + +Hammer +== Kladivo + +Has people playing +== Hráči na serveru + +High Detail +== Vysoké detaily + +Hook +== Hák + +Invalid Demo +== Neplatný záznam + +Join blue +== Modří + +Join red +== Červení + +Jump +== Skok + +Kick player +== Vyhodit hráče + +Language +== Jazyk + +Loading +== Načítání + +MOTD +== MOTD + +Map +== Mapa + +Maximum ping: +== Maximální ping: + +Mouse sens. +== Citlivost myši + +Move left +== Pohyb doleva + +Move player to spectators +== Poslat hráče pozorovat + +Move right +== Pohyb doprava + +Movement +== Pohyb + +Mute when not active +== Při nečinnosti ztlumit zvuk + +Name +== Jméno + +Next weapon +== Další zbraň + +Nickname +== Přezdívka + +No +== Ne + +No password +== Bez hesla + +No servers found +== Žádný server nenalezen + +No servers match your filter criteria +== Žádný server neodpovídá požadavkům filtru + +Ok +== OK + +Open +== Otevřít + +Parent Folder +== Nadřazená složka + +Password +== Heslo + +Password incorrect +== Špatné heslo + +Ping +== Ping + +Pistol +== Pistolka + +Play +== Přehrát + +Play background music +== Přehrávat hudbu na pozadí + +Player +== Hráč + +Player country: +== Národnost: + +Player options +== Nastavení hráčů + +Players +== Hráči + +Please balance teams! +== Prosím vyrovnejte týmy! + +Prev. weapon +== Předchozí zbraň + +Quality Textures +== Kvalitní textury + +Quit +== Konec + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Důvod: + +Red team +== Červení + +Red team wins! +== Červení vyhráli! + +Refresh +== Obnovit + +Refreshing master servers +== Aktualizování hlavních serverů + +Remote console +== Vzdálená konzola + +Remove +== Odstranit + +Remove friend +== Odebrat přítele + +Rename +== Přejmenovat + +Rename demo +== Přejmenovat záznam + +Reset filter +== Vymazat filtr + +Sample rate +== Vzorkování: + +Score +== Skóre + +Score board +== Tabulka výsledků + +Score limit +== Skóre limit + +Scoreboard +== Tabulka výsledků + +Screenshot +== Vyfotit obrazovku + +Server address: +== Adresa serveru: + +Server details +== Detaily serveru + +Server filter +== Filtr serverů + +Server info +== Informace + +Server not full +== Možnost připojit se + +Settings +== Nastavení + +Shotgun +== Brokovnice + +Show chat +== Zobrazit chat + +Show friends only +== Zobrazit přátele + +Show ingame HUD +== Zobrazit HUD + +Show name plates +== Zobrazovat jména hráčů + +Show only supported +== Zobrazit pouze podporované + +Skins +== Vzhledy + +Sound +== Zvuk + +Sound error +== Chyba zvuku + +Spectate +== Pozorovat + +Spectate next +== Pozorovat dalšího + +Spectate previous +== Pozorovat minulého + +Spectator mode +== Nastavit pozorování + +Spectators +== Pozorovatelé + +Standard gametype +== Základní herní módy + +Standard map +== Základní mapy + +Stop record +== Ukončit záznam + +Strict gametype filter +== Pouze tento herní mód + +Sudden Death +== Náhlá smrt + +Switch weapon on pickup +== Přehazovat zbraň na nově sebranou + +Team +== Týmu + +Team chat +== Týmový chat + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Stáhněte si nové Teeworlds %s na www.teeworlds.com! + +Texture Compression +== Komprimované textury + +The audio device couldn't be initialised. +== Zvukové zařízení nelze inicializovat. + +The server is running a non-standard tuning on a pure game type. +== Server používá nestandartní nastavení (tuning) pro základní módy. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== V editoru map je neuložená mapa, možná ji chcete před ukončením hry uložit. + +Time limit +== Časový limit + +Time limit: %d min +== Časový limit: %d min + +Try again +== Zkusit znovu + +Type +== Mód + +Unable to delete the demo +== Záznam se nepodařilo smazat + +Unable to rename the demo +== Záznam nelze přejmenovat + +Use sounds +== Povolit zvuk + +Use team colors for name plates +== Obarvit jména hráčů podle týmu + +V-Sync +== Vertikální synchronizace + +Version +== Verze + +Vote command: +== Příkaz: + +Vote description: +== Popisek hlasování: + +Vote no +== NE + +Vote yes +== ANO + +Voting +== Hlasování + +Warmup +== Zahřívací kolo + +Weapon +== Zbraně + +Welcome to Teeworlds +== Vítejte v Teeworlds + +Yes +== Ano + +You must restart the game for all settings to take effect. +== Změna nastavení se projeví až po restartu hry. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Nové jméno: + +Sat. +== Barva + +Miscellaneous +== Ostatní + +Internet +== Internet + +Max demos +== Maximální počet záznamů + +News +== Novinky + +Join game +== Do hry + +FSAA samples +== FSAA vzorkování + +%d of %d servers, %d players +== Serverů: %d z %d, Hráčů: %d + +Sound volume +== Hlasitost + +Created: +== Vytvořeno: + +Max Screenshots +== Maximální počet vyfocených obrazovek + +Length: +== Délka: + +Skin name +== + +Rifle +== Laser + +Patterns +== + +Netversion: +== Netversion: + +Map: +== Mapa: + +%d Bytes +== %d Bajtů + +Coloration +== + +Info +== Info + +Hue +== Tón + +Record demo +== Zaznamenat + +Your skin +== Zvolený vzhled + +Size: +== Velikost: + +Reset to defaults +== Obnovit výchozí + +Quit anyway? +== Opravdu odejít? + +Display Modes +== Režimy zobrazení + +Version: +== Verze: + +Round +== Kolo + +Lht. +== Jas + +no limit +== neomezeno + +Quick search: +== Vyhledávání: + +UI Color +== Barvy uživatelského rozhraní + +Host address +== Adresa serveru + +Crc: +== Crc: + +Alpha +== Alfa + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Aktuální verze: %s + +LAN +== LAN + +Name plates size +== Velikost jmen + +Type: +== Typ: + diff --git a/data/languages/danish.txt b/data/languages/danish.txt new file mode 100644 index 0000000000..d243c24079 --- /dev/null +++ b/data/languages/danish.txt @@ -0,0 +1,1018 @@ +##### authors ##### +#originally created by: +# SnOrKilll +#modified by: +# matiasmunk 2011-08-06 22:49:11 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% indlæst + +%ds left +== %d sekunder tilbage + +%i minute left +== %i minut tilbage + +%i minutes left +== %i minutter tilbage + +%i second left +== %i sekund tilbage + +%i seconds left +== %i sekunder tilbage + +%s wins! +== %s vinder! + +-Page %d- +== -Side %d- + +Abort +== Afbryd + +Add +== Tilføj + +Add Friend +== Tilføj ven + +Address +== Adresse + +All +== Alle + +Always show name plates +== Vis altid navneskilte + +Are you sure that you want to delete the demo? +== Er du sikker på at du vil slette denne demo? + +Are you sure that you want to quit? +== Er du sikker på at du vil afslutte? + +Are you sure that you want to remove the player from your friends list? +== Er du sikker på at du vil fjerne denne spiller fra din venneliste? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Siden dette er første gang du starter spillet, så vær så venlig at skrive dit navn nedenunder. Det anbefales at du kigger på indstillingerne så du kan tilpasse dem inden du kobler dig på en server. + +Automatically record demos +== Optag automatisk demoer + +Automatically take game over screenshot +== Tag automatisk et game over screenshot + +Blue team +== Blå hold + +Blue team wins! +== Blåt hold vinder! + +Body +== Krop + +Call vote +== Lav afstemning + +Change settings +== Ændre indstillinger + +Chat +== Chat + +Clan +== Klan + +Client +== Klient + +Close +== Luk + +Compatible version +== Kompatibel version + +Connect +== Tilslut + +Connecting to +== Forbinder til + +Connection Problems... +== Forbindelsesproblemer... + +Console +== Konsol + +Controls +== Taster + +Count players only +== Tæl kun spillere + +Country +== Land + +Current +== Nuværende + +Custom colors +== Brugerdefinerede farver + +Delete +== Slet + +Delete demo +== Slet demo + +Demo details +== Demoinformation + +Demofile: %s +== Demo fil: %s + +Demos +== Demoer + +Disconnect +== Frakoble + +Disconnected +== Frakoblet + +Downloading map +== Downloader bane + +Draw! +== Uafgjort! + +Dynamic Camera +== Dynamisk kamera + +Emoticon +== Humørikoner + +Enter +== Fortsæt + +Error +== Fejl + +Error loading demo +== Kunne ikke ikke indlæse demoen + +Favorite +== Favorit + +Favorites +== Favoritter + +Feet +== Fødder + +Filter +== Filter + +Fire +== Skyd + +Folder +== Mappe + +Force vote +== Tving afstemning + +Free-View +== Free-View + +Friends +== Venner + +Fullscreen +== Fuldskærm + +Game +== Spil + +Game info +== Spil info + +Game over +== Game over + +Game type +== Spiltype + +Game types: +== Spiltyper: + +General +== Generelt + +Graphics +== Grafik + +Grenade +== Granatkaster + +Hammer +== Hammer + +Has people playing +== Har folk som spiller + +High Detail +== Extra detaljer + +Hook +== Krog + +Invalid Demo +== Ugyldig demo + +Join blue +== Spil for blå + +Join red +== Spil for rød + +Jump +== Hop + +Kick player +== Udsmid spiller + +Language +== Sprog + +Loading +== Indlæser + +MOTD +== MOTD + +Map +== Kort + +Maximum ping: +== Højeste ping: + +Mouse sens. +== Musfølsomhed + +Move left +== Gå til venstre + +Move player to spectators +== Flyt spilleren til tilskuerne + +Move right +== Gå til højre + +Movement +== Bevægelse + +Mute when not active +== Ingen lyd når du ikke er aktiv + +Name +== Navn + +Next weapon +== Næste våben + +Nickname +== Navn + +No +== Nej + +No password +== Ingen adgangskode + +No servers found +== Ingen servere fundet + +No servers match your filter criteria +== Ingen servere matcher din filterkriterier + +Ok +== Ok + +Open +== Åben + +Parent Folder +== Overmappe + +Password +== Adgangskode + +Password incorrect +== Forkert adgangskode + +Ping +== Ping + +Pistol +== Pistol + +Play +== Spil + +Play background music +== Spil baggrunds musik + +Player +== Spiller + +Player country: +== Spillernes land: + +Player options +== Spillermuligheder + +Players +== Spillere + +Please balance teams! +== Balancer venligst holdet! + +Prev. weapon +== Foregående våben + +Quality Textures +== Kvalitetsteksturer + +Quit +== Afslut + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Grund: + +Red team +== Rødt hold + +Red team wins! +== Rødt hold vinder! + +Refresh +== Opdater + +Refreshing master servers +== Opdaterer masterservere + +Remote console +== Serverkonsol + +Remove +== Fjern + +Remove friend +== Fjern ven + +Rename +== Omdøb navn + +Rename demo +== Omdøb navn på demoen + +Reset filter +== Nulstil filter + +Sample rate +== Sample frekvens + +Score +== Score + +Score board +== Resultatliste + +Score limit +== Score grænse + +Scoreboard +== Resultatliste + +Screenshot +== Skærmbillede + +Server address: +== Serveradresse + +Server details +== Serverdetaljer + +Server filter +== Serverfilter + +Server info +== Serverinfo + +Server not full +== Ikke fuld server + +Settings +== Indstillinger + +Shotgun +== Haglgevær + +Show chat +== Vis chat + +Show friends only +== Vis venner + +Show ingame HUD +== Vis HUD i spillet + +Show name plates +== Vis navneskilte + +Show only supported +== Vis kun understøttede + +Skins +== Udseende + +Sound +== Lyd + +Sound error +== Lyd fejl + +Spectate +== Kig på + +Spectate next +== Kig på næste + +Spectate previous +== Kig på forrige + +Spectator mode +== Tilskuer mode + +Spectators +== Tilskuer + +Standard gametype +== Standard spilletype + +Standard map +== Standard map + +Stop record +== Stop optagelse + +Strict gametype filter +== Streng spiltype filter + +Sudden Death +== Sudden Death + +Switch weapon on pickup +== Byt våben ved anskafning + +Team +== Hold + +Team chat +== Holdchat + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworld %s er ude nu! Download den på www.teeworlds.com! + +Texture Compression +== Teksturkompression + +The audio device couldn't be initialised. +== Lydenheden kunne ikke startes + +The server is running a non-standard tuning on a pure game type. +== Denne server kører på en ikke-standard tuning på en ren spilletype. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Der er en ikke-gemt bane i editoren, vil du gemme den før du afslutter? + +Time limit +== Tidsbegrænsning + +Time limit: %d min +== Tidsbegrænsning: %d min + +Try again +== Forsøg igen + +Type +== Type + +Unable to delete the demo +== Kunne ikke slette demoen + +Unable to rename the demo +== Kunne ikke omdøbe demoen + +Use sounds +== Anvend lydeffekter + +Use team colors for name plates +== Anvend holdfarver til navneskilte + +V-Sync +== V-Sync + +Version +== Version + +Vote command: +== Afstemningsordre: + +Vote description: +== Afstemningsbeskrivelse: + +Vote no +== Stem nej + +Vote yes +== Stem ja + +Voting +== Afstemning + +Warmup +== Opvarmning + +Weapon +== Våben + +Welcome to Teeworlds +== Velkommen til Teeworlds + +Yes +== Ja + +You must restart the game for all settings to take effect. +== Du skal genstarte spillet før alle indstillingerne virker. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Nyt navn: + +Sat. +== Mætning + +Miscellaneous +== Diverse + +Internet +== Internet + +Max demos +== Max antal demoer + +News +== Nyheder + +Join game +== Start spil + +FSAA samples +== FSAA samples + +%d of %d servers, %d players +== %d af %d servere, %d spillere + +Sound volume +== Lydstyrke + +Created: +== Lavet: + +Max Screenshots +== Max antal screenshots + +Length: +== Længde + +Skin name +== + +Rifle +== Laser + +Patterns +== + +Netversion: +== Netversion: + +Map: +== Bane + +%d Bytes +== %d Bytes + +Coloration +== + +Info +== Info + +Hue +== Farve + +Record demo +== Optag demo + +Your skin +== Dit udseende + +Size: +== Størrelse: + +Reset to defaults +== Sæt til standard + +Quit anyway? +== Afslut alligevel? + +Display Modes +== Skærmopløsning + +Version: +== Version: + +Round +== Runde + +Lht. +== Lys + +no limit +== Ingen grænse + +Quick search: +== Hurtig søg: + +UI Color +== Brugergrænseflade farve + +Host address +== Værtsadresse + +Crc: +== Crc: + +Alpha +== Alpha + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Nuværende version: %s + +LAN +== LAN + +Name plates size +== Størrelse på navneskilte + +Type: +== Type: + diff --git a/data/languages/dutch.txt b/data/languages/dutch.txt new file mode 100644 index 0000000000..4f44e6ba90 --- /dev/null +++ b/data/languages/dutch.txt @@ -0,0 +1,1030 @@ +##### authors ##### +#originally created by: +# +#modified by: +# vierkant 2010-06-03 19:06:34 +# vierkant 2010-06-04 13:20:25 +# vierkant 2010-06-09 14:40:12 +# Sijmen Schoon 2010-11-21 16:08:46 +# vierkant 2010-11-21 18:19:48 +# vierkant 2010-12-16 19:44:49 +# vierkant 2011-01-03 12:22:05 +# vierkant 2011-01-29 18:40:28 +# vierkant 2011-04-02 22:19:30 +# Vijfhoek 2011-05-02 18:09:31 +# Syntax Error 2011-07-02 20:09:24 +# Yared Hufkens 2012-02-03 19:57:59 +# Yared Hufkens 2012-12-08 10:51:30 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% is geladen + +%ds left +== %ds over + +%i minute left +== nog %i minuut + +%i minutes left +== nog %i minuten + +%i second left +== nog %i seconde + +%i seconds left +== nog %i seconden + +%s wins! +== %s wint! + +-Page %d- +== -Pagina %d- + +Abort +== Afbreken + +Add +== Toevoegen + +Add Friend +== Voeg vriend toe + +Address +== Adres + +All +== Alle + +Always show name plates +== Altijd naamplaten laten zien + +Are you sure that you want to delete the demo? +== Weet je zeker dat je de demo wil verwijderen? + +Are you sure that you want to quit? +== Weet je zeker dat je wil stoppen? + +Are you sure that you want to remove the player from your friends list? +== Weet je zeker dat je deze speler uit je vriendenlijst wilt verwijderen? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Omdat dit de eerste keer is dat je het spel opstart, moet je een bijnaam kiezen. Doe dat hieronder. Het is aanbevolen om de instellingen te controleren voordat je een spel start. + +Automatically record demos +== Automatisch demo's opnemen + +Automatically take game over screenshot +== Automatisch schermafbeeldingen nemen + +Blue team +== Blauwe team + +Blue team wins! +== Blauwe team wint! + +Body +== Lichaam + +Call vote +== Stem + +Change settings +== Verander instellingen + +Chat +== Chat + +Clan +== Clan + +Client +== Client + +Close +== Sluiten + +Compatible version +== Samenwerkende versie + +Connect +== Verbinden + +Connecting to +== Verbinden met + +Connection Problems... +== Verbindingsproblemen... + +Console +== Console + +Controls +== Besturing + +Count players only +== Tel alleen spelers + +Country +== Land + +Current +== Huidig + +Custom colors +== Aangepaste kleuren + +Delete +== Verwijder + +Delete demo +== Verwijder demo + +Demo details +== Demo details + +Demofile: %s +== Demobestand: %s + +Demos +== Demo's + +Disconnect +== Stoppen + +Disconnected +== Verbinding verbroken + +Downloading map +== Kaart downloaden + +Draw! +== Gelijkspel! + +Dynamic Camera +== Dynamische camera + +Emoticon +== Emoticon + +Enter +== Starten + +Error +== Fout + +Error loading demo +== Fout bij laden demo + +Favorite +== Favoriet + +Favorites +== Favorieten + +Feet +== Voeten + +Filter +== Filter + +Fire +== Schieten + +Folder +== Map + +Force vote +== Forceer stem + +Free-View +== Vrij bewegen + +Friends +== Vrienden + +Fullscreen +== Volledig scherm + +Game +== Spel + +Game info +== Spelinfo + +Game over +== Spel afgelopen + +Game type +== Speltype + +Game types: +== Speltypes: + +General +== Algemeen + +Graphics +== Beeld + +Grenade +== Granaat + +Hammer +== Hamer + +Has people playing +== Mensen in server + +High Detail +== Veel details + +Hook +== Haak + +Invalid Demo +== Ongeldige demo + +Join blue +== Ga bij blauw + +Join red +== Ga bij rood + +Jump +== Springen + +Kick player +== Kick speler + +Language +== Taal + +Loading +== Laden + +MOTD +== MOTD + +Map +== Kaart + +Maximum ping: +== Hoogste ping: + +Mouse sens. +== Muis resolutie + +Move left +== Naar links + +Move player to spectators +== Verplaats speler naar toeschouwers + +Move right +== Naar rechts + +Movement +== Bewegen + +Mute when not active +== Dempen wanneer inactief + +Name +== Naam + +Next weapon +== Volgend wapen + +Nickname +== Bijnaam + +No +== Nee + +No password +== Geen wachtwoord + +No servers found +== Geen servers gevonden + +No servers match your filter criteria +== Geen servers gevonden op zoekopdracht + +Ok +== Oké + +Open +== Open + +Parent Folder +== Bovenliggende map + +Password +== Wachtwoord + +Password incorrect +== Wachtwoord verkeerd + +Ping +== Ping + +Pistol +== Pistool + +Play +== Spelen + +Play background music +== Speel achtergrondmuziek + +Player +== Speler + +Player country: +== Speler land: + +Player options +== Speler opties + +Players +== Spelers + +Please balance teams! +== Balanceer teams! + +Prev. weapon +== Vorig wapen + +Quality Textures +== Structuurkwaliteit + +Quit +== Afsluiten + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Reden: + +Red team +== Rode team + +Red team wins! +== Rode team wint! + +Refresh +== Vernieuwen + +Refreshing master servers +== Masterservers vernieuwen + +Remote console +== Remote console + +Remove +== Verwijderen + +Remove friend +== Verwijder vriend + +Rename +== Hernoem + +Rename demo +== Hernoem demo + +Reset filter +== Herstel filter + +Sample rate +== Voorbeeldssnelheid + +Score +== Score + +Score board +== Score bord + +Score limit +== Score limiet + +Scoreboard +== Scorebord + +Screenshot +== Schermafbeelding + +Server address: +== Serveradres: + +Server details +== Serverdetails + +Server filter +== Serverfilter + +Server info +== Serverinfo + +Server not full +== Server niet vol + +Settings +== Instellingen + +Shotgun +== Geweer + +Show chat +== Laat chat zien + +Show friends only +== Laat vrienden zien + +Show ingame HUD +== Laat ingame HUD zien + +Show name plates +== Laat naamplaat zien + +Show only supported +== Laat alleen compatibele servers zien + +Skins +== Skins + +Sound +== Geluid + +Sound error +== Geluidsfout + +Spectate +== Toekijken + +Spectate next +== Volg volgende speler + +Spectate previous +== Volg vorige speler + +Spectator mode +== Toeschouwer modus + +Spectators +== Toeschouwers + +Standard gametype +== Standaard speltype + +Standard map +== Standaard kaart + +Stop record +== Stop met opnemen + +Strict gametype filter +== Strikt speltype filter + +Sudden Death +== Sudden Death + +Switch weapon on pickup +== Verander wapen bij het oppakken + +Team +== Team + +Team chat +== Team chat + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s is uit! Download het op www.teeworlds.com! + +Texture Compression +== Structuurscompressie + +The audio device couldn't be initialised. +== Het geluid kon niet worden geladen. + +The server is running a non-standard tuning on a pure game type. +== The server draait geen standaard spel type. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Er is een onopgeslagen kaart in de editor, misschien wil je deze opslaan voordat je stopt. + +Time limit +== Tijdslimiet + +Time limit: %d min +== Tijdslimiet: %d minuten + +Try again +== Probeer opnieuw + +Type +== Type + +Unable to delete the demo +== Demo kon niet worden verwijderd + +Unable to rename the demo +== Niet mogelijk om de demo te hernoemen + +Use sounds +== Gebruik geluid + +Use team colors for name plates +== Gebruik teamkleuren voor naamplaten + +V-Sync +== V-Sync + +Version +== Versie + +Vote command: +== Stem commando: + +Vote description: +== Stem omschrijving: + +Vote no +== Stem nee + +Vote yes +== Stem ja + +Voting +== Stemmen + +Warmup +== Opwarmen + +Weapon +== Wapen + +Welcome to Teeworlds +== Welkom bij Teeworlds + +Yes +== Ja + +You must restart the game for all settings to take effect. +== Je moet het spel herstarten voordat de veranderingen effect hebben. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Nieuwe naam: + +Sat. +== Verz. + +Miscellaneous +== Diverse + +Internet +== Internet + +Max demos +== Maximaal aantal demo's + +News +== Nieuws + +Join game +== Binnenkomen + +FSAA samples +== FSAA samples + +%d of %d servers, %d players +== %d van %d Servers, %d spelers + +Sound volume +== Volume + +Created: +== Gemaakt: + +Max Screenshots +== Maximaal aantal schermafbeeldingen + +Length: +== Lengte: + +Skin name +== + +Rifle +== Laser + +Patterns +== + +Netversion: +== Netversie: + +Map: +== Kaart: + +%d Bytes +== %d Bytes + +Coloration +== + +Info +== Info + +Hue +== Tint + +Record demo +== Neem demo op + +Your skin +== Jouw skin + +Size: +== Grootte: + +Reset to defaults +== Standaardinstellingen + +Quit anyway? +== Toch afsluiten? + +Display Modes +== Laat modes zien + +Version: +== Versie: + +Round +== Ronde + +Lht. +== Licht + +no limit +== ongelimiteerd + +Quick search: +== Snel zoeken: + +UI Color +== UI kleur + +Host address +== Server adres + +Crc: +== Crc: + +Alpha +== Alpha + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Huidige versie: %s + +LAN +== LAN + +Name plates size +== Grootte naamplaat + +Type: +== Type: + diff --git a/data/languages/finnish.txt b/data/languages/finnish.txt new file mode 100644 index 0000000000..f05476d356 --- /dev/null +++ b/data/languages/finnish.txt @@ -0,0 +1,1019 @@ +##### authors ##### +#originally created by: +# ziltoide +#modified by: +# ziltoide 2011-04-06 23:56:54 +# ziltoide 2011-07-05 18:31:04 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% ladattu + +%ds left +== %ds jäljellä + +%i minute left +== %i minuutti jäljellä + +%i minutes left +== %i minuuttia jäljellä + +%i second left +== %i sekunti jäljellä + +%i seconds left +== %i sekuntia jäljellä + +%s wins! +== %s voitti! + +-Page %d- +== -Sivu %d- + +Abort +== Keskeytä + +Add +== Lisää + +Add Friend +== Lisää ystävä + +Address +== Osoite + +All +== Kaikki + +Always show name plates +== Näytä nimikyltit aina + +Are you sure that you want to delete the demo? +== Oletko varma, että haluat poistaa demon? + +Are you sure that you want to quit? +== Oletko varma, että haluat lopettaa? + +Are you sure that you want to remove the player from your friends list? +== Oletko varma, että haluat poistaa pelaajan ystävälistaltasi? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Tämä on ensimmäinen kerta kun käynnistät pelin, ole hyvä ja kirjoita nimimerkkisi alle. On suositeltua, että tarkistat asetukset ja muutat niitä mielesi mukaan ennen kuin liityt palvelimille. + +Automatically record demos +== Nauhoita demot automaattisesti + +Automatically take game over screenshot +== Ota pelin jälkeinen kuvankaappaus autom. + +Blue team +== Sininen joukkue + +Blue team wins! +== Sininen joukkue voitti! + +Body +== Vartalo + +Call vote +== Aloita äänestys + +Change settings +== Muuta asetuksia + +Chat +== Chatti + +Clan +== Klaani + +Client +== Asiakasohjelma + +Close +== Sulje + +Compatible version +== Yhteensopiva versio + +Connect +== Yhdistä + +Connecting to +== Yhdistetään + +Connection Problems... +== Yhteysongelmia... + +Console +== Konsoli + +Controls +== Kontrollit + +Count players only +== Laske vain pelaajat + +Country +== Maa + +Current +== Nykyinen + +Custom colors +== Omat värit + +Delete +== Poista + +Delete demo +== Poista demo + +Demo details +== Demon tiedot + +Demofile: %s +== Demotiedosto: %s + +Demos +== Demot + +Disconnect +== Katk. yhteys + +Disconnected +== Yhteys katkaistu + +Downloading map +== Ladataan kenttää + +Draw! +== Tasapeli! + +Dynamic Camera +== Dynaaminen kamera + +Emoticon +== Hymiö + +Enter +== Jatka + +Error +== Virhe + +Error loading demo +== Virhe ladattaessa demoa + +Favorite +== Suosikki + +Favorites +== Suosikit + +Feet +== Jalat + +Filter +== Suotimet + +Fire +== Ammu + +Folder +== Kansio + +Force vote +== Päätä äänestys + +Free-View +== Vapaa näkymä + +Friends +== Ystävät + +Fullscreen +== Koko näyttö + +Game +== Peli + +Game info +== Pelin tiedot + +Game over +== Peli loppui + +Game type +== Pelityyppi + +Game types: +== Pelityypit: + +General +== Yleinen + +Graphics +== Grafiikat + +Grenade +== Kranaatti + +Hammer +== Nuija + +Has people playing +== Ihmisiä pelaamassa + +High Detail +== Korkea yksityiskohtataso + +Hook +== Koukku + +Invalid Demo +== Epäkelpo demo + +Join blue +== Liity sinisiin + +Join red +== Liity punaisiin + +Jump +== Hyppää + +Kick player +== Potkaise pelaaja + +Language +== Kieli + +Loading +== Ladataan + +MOTD +== Palvelimen viesti + +Map +== Kenttä + +Maximum ping: +== Maksimiping: + +Mouse sens. +== Hiiren herkkyys + +Move left +== Liiku vasemmalle + +Move player to spectators +== Siirrä pelaaja katsojaksi + +Move right +== Liiku oikealle + +Movement +== Liike + +Mute when not active +== Mykistä, kun ei aktiivinen + +Name +== Nimi + +Next weapon +== Seuraava ase + +Nickname +== Nimimerkki + +No +== Ei + +No password +== Ei salasanaa + +No servers found +== Palvelimia ei löytynyt + +No servers match your filter criteria +== Yksikään palvelin ei täyttänyt suodinkriteerejäsi + +Ok +== Ok + +Open +== Avaa + +Parent Folder +== Yläkansio + +Password +== Salasana + +Password incorrect +== Väärä salasana + +Ping +== Ping + +Pistol +== Pistooli + +Play +== Toista + +Play background music +== Soita taustamusiikki + +Player +== Pelaaja + +Player country: +== Pelaajan maa: + +Player options +== Pelaajavalinnat + +Players +== Pelaajat + +Please balance teams! +== Ole hyvä ja tasoita joukkueita! + +Prev. weapon +== Edellinen ase + +Quality Textures +== Hyvälaatuiset tekstuurit + +Quit +== Lopeta + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Syy: + +Red team +== Punainen joukkue + +Red team wins! +== Punainen joukkue voitti! + +Refresh +== Päivitä + +Refreshing master servers +== Päivitetään isäntäpalvelimia + +Remote console +== Etäkonsoli + +Remove +== Poista + +Remove friend +== Poista ystävä + +Rename +== Nimeä + +Rename demo +== Uudelleennimeä demo + +Reset filter +== Nollaa suotimet + +Sample rate +== Näytteenottotaajuus + +Score +== Pisteet + +Score board +== Tulostaulu + +Score limit +== Pisteraja + +Scoreboard +== Tuloslista + +Screenshot +== Kuvankaappaus + +Server address: +== Palvelimen osoite: + +Server details +== Palvelimen yksityiskohdat + +Server filter +== Palvelinsuotimet + +Server info +== Palvelintiedot + +Server not full +== Palvelin ei täysi + +Settings +== Asetukset + +Shotgun +== Haulikko + +Show chat +== Näytä chatti + +Show friends only +== Näytä vain ystävät + +Show ingame HUD +== Näytä peli-HUD + +Show name plates +== Näytä nimikyltit + +Show only supported +== Näytä vain tuetut + +Skins +== Ulkonäkö + +Sound +== Äänet + +Sound error +== Äänivirhe + +Spectate +== Katso + +Spectate next +== Katso seuraavaa + +Spectate previous +== Katso edellistä + +Spectator mode +== Katsojatila + +Spectators +== Katsojat + +Standard gametype +== Standardipelityyppi + +Standard map +== Standardikenttä + +Stop record +== Lopeta nauhoit. + +Strict gametype filter +== Tarkka pelityyppisuodin + +Sudden Death +== Äkkikuolema + +Switch weapon on pickup +== Vaihda asetta nostaessasi uuden + +Team +== Joukkue + +Team chat +== Joukkuechatti + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s on julkaistu! Lataa se osoitteesta www.teeworlds.com! + +Texture Compression +== Tekstuuripakkaus + +The audio device couldn't be initialised. +== Äänilaitteen alustaminen ei onnistunut. + +The server is running a non-standard tuning on a pure game type. +== Tämä palvelin ajaa ei-standardeja virityksiä standardipelityypillä. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Editorissa on tallentamaton kenttä, saatat haluta tallentaa sen ennen kuin lopetat pelin. + +Time limit +== Aikaraja + +Time limit: %d min +== Aikaraja: %d min + +Try again +== Yritä uudelleen + +Type +== Tyyppi + +Unable to delete the demo +== Demon poistaminen ei onnistunut + +Unable to rename the demo +== Demon nimeäminen ei onnistunut + +Use sounds +== Käytä ääniefektejä + +Use team colors for name plates +== Käytä joukkueiden värejä nimikylteissä + +V-Sync +== V-Sync + +Version +== Versio + +Vote command: +== Äänestyskäsky: + +Vote description: +== Äänestyksen kuvaus: + +Vote no +== Äänestä ei + +Vote yes +== Äänestä kyllä + +Voting +== Äänestys + +Warmup +== Lämmittely + +Weapon +== Ase + +Welcome to Teeworlds +== Tervetuloa Teeworldsiin + +Yes +== Kyllä + +You must restart the game for all settings to take effect. +== Sinun täytyy uudelleenkäynnistää peli, jotta kaikki asetukset tulisivat voimaan. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Uusi nimi: + +Sat. +== Kyllästys + +Miscellaneous +== Sekalaista + +Internet +== Internet + +Max demos +== Demomaksimi + +News +== Uutiset + +Join game +== Liity peliin + +FSAA samples +== FSAA näytteet + +%d of %d servers, %d players +== %d/%d palvelinta, %d pelaajaa + +Sound volume +== Äänenvoimakkuus + +Created: +== Luotu: + +Max Screenshots +== Kuvankaappausmaksimi + +Length: +== Pituus: + +Skin name +== + +Rifle +== Kivääri + +Patterns +== + +Netversion: +== Yhteysversio: + +Map: +== Kenttä: + +%d Bytes +== %d tavua + +Coloration +== + +Info +== Info + +Hue +== Värisävy + +Record demo +== Nauhoita demo + +Your skin +== Sinun ulkonäkösi + +Size: +== Koko: + +Reset to defaults +== Palaa oletusasetuksiin + +Quit anyway? +== Lopeta siitä huolimatta? + +Display Modes +== Näyttötilat + +Version: +== Versio: + +Round +== Kierros + +Lht. +== Valoisuus + +no limit +== ei rajaa + +Quick search: +== Pikahaku: + +UI Color +== Käyttöliittymäväri + +Host address +== Palvelimen osoite + +Crc: +== Crc: + +Alpha +== Alfa + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Nykyinen versio: %s + +LAN +== LAN + +Name plates size +== Nimikylttien koko + +Type: +== Tyyppi: + diff --git a/data/languages/french.txt b/data/languages/french.txt new file mode 100644 index 0000000000..bf26a8ce77 --- /dev/null +++ b/data/languages/french.txt @@ -0,0 +1,1029 @@ +##### authors ##### +#originally created by: +# +#modified by: +# lordskelethom 2010-05-30 14:01:11 +# Ubu 2010-05-30 18:41:43 +# Choupom 2010-10-11 00:32:54 +# Choupom 2010-11-21 14:07:30 +# Choupom 2011-01-05 22:13:44 +# clecle226 2011-02-09 12:28:39 +# clecle226 2011-02-12 23:43:59 +# Choupom 2011-02-13 12:24:15 +# Choupom 2011-04-02 19:53:54 +# Choupom 2011-04-03 23:00:57 +# Ubu 2011-04-04 21:09:05 +# Choupom 2011-07-02 19:23:49 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% chargé + +%ds left +== %ds restantes + +%i minute left +== %i minute restante + +%i minutes left +== %i minutes restantes + +%i second left +== %i seconde restante + +%i seconds left +== %i secondes restantes + +%s wins! +== %s gagne ! + +-Page %d- +== -Page %d- + +Abort +== Annuler + +Add +== Ajouter + +Add Friend +== Ajouter aux amis + +Address +== Adresse + +All +== Tout le monde + +Always show name plates +== Toujours afficher les pseudonymes + +Are you sure that you want to delete the demo? +== Êtes-vous sûr de vouloir supprimer cette démo ? + +Are you sure that you want to quit? +== Êtes-vous sûr de vouloir quitter ? + +Are you sure that you want to remove the player from your friends list? +== Êtes-vous sûr de vouloir enlever le joueur de votre liste d'amis ? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Comme c'est la première fois que vous lancez le jeu, veuillez entrer votre pseudonyme ci-dessous. Vous devriez vérifier les réglages et les ajuster avant de rejoindre un serveur. + +Automatically record demos +== Enregistrer automatiquement une démo + +Automatically take game over screenshot +== Sauvegarder les résultats à la fin de la partie + +Blue team +== Équipe bleue + +Blue team wins! +== L'équipe bleue gagne ! + +Body +== Corps + +Call vote +== Voter + +Change settings +== Changer les options + +Chat +== Chat + +Clan +== Clan + +Client +== Client + +Close +== Fermer + +Compatible version +== Version compatible + +Connect +== Connecter + +Connecting to +== Connexion à + +Connection Problems... +== Problèmes de connexion... + +Console +== Console + +Controls +== Contrôles + +Count players only +== Seulement les joueurs + +Country +== Pays + +Current +== Actuellement + +Custom colors +== Couleurs personnalisées + +Delete +== Supprimer + +Delete demo +== Supprimer la démo + +Demo details +== Détails de la démo + +Demofile: %s +== Démo : %s + +Demos +== Démos + +Disconnect +== Partir + +Disconnected +== Déconnecté + +Downloading map +== Téléchargement de la carte + +Draw! +== Égalité ! + +Dynamic Camera +== Caméra dynamique + +Emoticon +== Émoticônes + +Enter +== Démarrer + +Error +== Erreur + +Error loading demo +== Erreur pendant le chargement de la démo + +Favorite +== Favori + +Favorites +== Favoris + +Feet +== Pieds + +Filter +== Filtre + +Fire +== Tirer + +Folder +== Dossier + +Force vote +== Forcer le vote + +Free-View +== Vue libre + +Friends +== Amis + +Fullscreen +== Plein écran + +Game +== Jeu + +Game info +== Info. jeu + +Game over +== Fin de la partie + +Game type +== Type de jeu + +Game types: +== Types de jeu : + +General +== Général + +Graphics +== Affichage + +Grenade +== Lance-grenade + +Hammer +== Maillet + +Has people playing +== Au moins un joueur + +High Detail +== Tous les détails + +Hook +== Grappin + +Invalid Demo +== Démo invalide + +Join blue +== Rej. Bleus + +Join red +== Rej. Rouges + +Jump +== Sauter + +Kick player +== Éjecter un joueur + +Language +== Langue + +Loading +== Chargement + +MOTD +== MOTD + +Map +== Carte + +Maximum ping: +== Ping maximum : + +Mouse sens. +== Sensib. souris + +Move left +== Aller à gauche + +Move player to spectators +== Déplacer le joueur dans les spectateurs + +Move right +== Aller à droite + +Movement +== Mouvement + +Mute when not active +== Rendre muet en cas d'inactivité + +Name +== Nom + +Next weapon +== Arme suivante + +Nickname +== Pseudonyme + +No +== Non + +No password +== Pas de mot de passe + +No servers found +== Aucun serveur trouvé + +No servers match your filter criteria +== Aucun serveur ne correspond à vos critères + +Ok +== Ok + +Open +== Ouvrir + +Parent Folder +== Dossier parent + +Password +== Mot de passe + +Password incorrect +== Mot de passe incorrect + +Ping +== Ping + +Pistol +== Pistolet + +Play +== Jouer + +Play background music +== Jouer la musique de fond + +Player +== Joueur + +Player country: +== Nation joueurs : + +Player options +== Joueurs + +Players +== Joueurs + +Please balance teams! +== Équilibrez les équipes ! + +Prev. weapon +== Arme précédente + +Quality Textures +== Textures haute qualité + +Quit +== Quitter + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Raison : + +Red team +== Équipe rouge + +Red team wins! +== L'équipe rouge gagne ! + +Refresh +== Rafraîchir + +Refreshing master servers +== Rafraîchissement des serveurs maîtres + +Remote console +== Console serveur + +Remove +== Enlever + +Remove friend +== Enlever des amis + +Rename +== Renommer + +Rename demo +== Renommer la démo + +Reset filter +== Filtres par défaut + +Sample rate +== Fréquence + +Score +== Score + +Score board +== Scores + +Score limit +== Score limite + +Scoreboard +== Scores + +Screenshot +== Capture d'écran + +Server address: +== Addresse : + +Server details +== Détails du serveur + +Server filter +== Filtres + +Server info +== Info. serveur + +Server not full +== Pas de serveurs pleins + +Settings +== Options + +Shotgun +== Fusil à pompe + +Show chat +== Montrer le chat + +Show friends only +== Montrer les amis + +Show ingame HUD +== Afficher l'interface du jeu + +Show name plates +== Montrer les pseudonymes + +Show only supported +== Ne montrer que les résolutions supportées + +Skins +== Skins + +Sound +== Son + +Sound error +== Erreur de son + +Spectate +== Regarder + +Spectate next +== Suivre suivant + +Spectate previous +== Suivre précédent + +Spectator mode +== Menu spectateur + +Spectators +== Spectateurs + +Standard gametype +== Types de jeu standards + +Standard map +== Cartes standards + +Stop record +== Arrêter + +Strict gametype filter +== Types de jeu exacts + +Sudden Death +== Mort Subite + +Switch weapon on pickup +== Sélectionner l'arme ramassée + +Team +== Équipe + +Team chat +== Chat équipe + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s est sorti ! Téléchargez-le sur www.teeworlds.com ! + +Texture Compression +== Compression des textures + +The audio device couldn't be initialised. +== Le périphérique audio n'a pas pu être initialisé. + +The server is running a non-standard tuning on a pure game type. +== Le serveur utilise des paramètres non standards sur un type de jeu standard. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Il y a une carte non-enregistrée dans l'éditeur, vous voulez peut-être l'enregistrer avant de quitter le jeu. + +Time limit +== Temps limite + +Time limit: %d min +== Temps limite : %d min + +Try again +== Réessayer + +Type +== Type + +Unable to delete the demo +== Impossible de supprimer la démo + +Unable to rename the demo +== Impossible de renommer la démo + +Use sounds +== Jouer les sons + +Use team colors for name plates +== Utiliser la couleur des équipes + +V-Sync +== V-Sync + +Version +== Version + +Vote command: +== Commande : + +Vote description: +== Description : + +Vote no +== Voter non + +Vote yes +== Voter oui + +Voting +== Vote + +Warmup +== Échauffement + +Weapon +== Arme + +Welcome to Teeworlds +== Bienvenue dans Teeworlds + +Yes +== Oui + +You must restart the game for all settings to take effect. +== Les changements prendront effet au prochain redémarrage. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Nouveau nom : + +Sat. +== Saturation + +Miscellaneous +== Divers + +Internet +== Internet + +Max demos +== Nombre maximum de démos + +News +== Nouvelles + +Join game +== Rejoindre + +FSAA samples +== Échantillons FSAA + +%d of %d servers, %d players +== %d/%d serveurs, %d joueurs + +Sound volume +== Volume du son + +Created: +== Créé le : + +Max Screenshots +== Nombre maximum de captures d'écran + +Length: +== Longueur : + +Skin name +== + +Rifle +== Laser + +Patterns +== + +Netversion: +== Netversion : + +Map: +== Carte : + +%d Bytes +== %d octets + +Coloration +== + +Info +== Info. + +Hue +== Teinte + +Record demo +== Enregistrer + +Your skin +== Votre skin + +Size: +== Taille : + +Reset to defaults +== Réinitialiser + +Quit anyway? +== Quitter quand même ? + +Display Modes +== Résolutions + +Version: +== Version : + +Round +== Round + +Lht. +== Luminosité + +no limit +== pas de limite + +Quick search: +== Recherche rapide : + +UI Color +== Couleur du menu + +Host address +== Adresse hôte + +Crc: +== Crc : + +Alpha +== Alpha + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Version actuelle : %s + +LAN +== LAN + +Name plates size +== Taille + +Type: +== Type : + diff --git a/data/languages/german.txt b/data/languages/german.txt new file mode 100644 index 0000000000..074691a5d9 --- /dev/null +++ b/data/languages/german.txt @@ -0,0 +1,1036 @@ +##### authors ##### +#originally created by: +# Dominik Geyer +#modified by: +# Fujnky 2010-06-03 11:30:05 +# Fujnky 2010-06-03 17:41:45 +# Fujnky 2010-06-05 23:36:52 +# Fujnky 2010-06-07 16:17:40 +# Fujnky 2010-06-11 09:50:47 +# Sworddragon 2010-11-21 14:25:00 +# Fujnky 2011-01-02 19:49:22 +# heinrich5991 2011-01-23 17:53:42 +# Sworddragon 2011-02-09 12:54:50 +# heinrich5991 2011-04-03 23:46:51 +# ghost91 2011-04-04 20:47:01 +# andi103 2011-05-02 19:12:27 +# andi103 2011-05-03 23:25:20 +# heinrich5991 2011-07-02 09:10:21 +# Yared Hufkens 2012-02-03 19:57:59 +# andi103 2012-07-14 11:31:11 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% geladen + +%ds left +== Noch %ds + +%i minute left +== Noch %i Minute + +%i minutes left +== Noch %i Minuten + +%i second left +== Noch %i Sekunde + +%i seconds left +== Noch %i Sekunden + +%s wins! +== %s gewinnt! + +-Page %d- +== -Seite %d- + +Abort +== Abbrechen + +Add +== Hinzufügen + +Add Friend +== Freund hinzufügen + +Address +== Adresse + +All +== Alle + +Always show name plates +== Spielernamen immer anzeigen + +Are you sure that you want to delete the demo? +== Möchtest du die Aufnahme wirklich löschen? + +Are you sure that you want to quit? +== Bist du sicher, dass du beenden möchtest? + +Are you sure that you want to remove the player from your friends list? +== Bist du sicher, dass du diesen Spieler aus deiner Freundesliste entfernen möchtest? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Da dies der erste Start des Spiels ist, gib bitte unten deinen Namen ein. Es wird empfohlen die Einstellungen zu überprüfen, um sie deinen Vorstellungen anzupassen, bevor du einem Server beitrittst. + +Automatically record demos +== Automatisch Aufnahmen erstellen + +Automatically take game over screenshot +== Autom. ein Bild vom Spielergebnis erstellen + +Blue team +== Blaues Team + +Blue team wins! +== Blaues Team gewinnt! + +Body +== Körper + +Call vote +== Abstimmen + +Change settings +== Einstellungen ändern + +Chat +== Chat + +Clan +== Clan + +Client +== Client + +Close +== Schließ. + +Compatible version +== Kompatible Version + +Connect +== Verbinden + +Connecting to +== Verbinden mit + +Connection Problems... +== Verbindungsprobleme... + +Console +== Konsole + +Controls +== Steuerung + +Count players only +== Nur Spieler zählen + +Country +== Land + +Current +== Aktuell + +Custom colors +== Eigene Farben + +Delete +== Löschen + +Delete demo +== Aufnahme löschen + +Demo details +== Aufnahmendetails: + +Demofile: %s +== Demodatei: %s + +Demos +== Aufnahm. + +Disconnect +== Trennen + +Disconnected +== Verbindung getrennt + +Downloading map +== Karte herunterladen + +Draw! +== Unentschieden! + +Dynamic Camera +== Dynamische Kamera + +Emoticon +== Emoticon + +Enter +== Starten + +Error +== Fehlgeschlagen + +Error loading demo +== Fehler beim Laden der Aufnahme, die Datei ist ungültig. + +Favorite +== Favorit + +Favorites +== Favoriten + +Feet +== Füße + +Filter +== Filter + +Fire +== Feuern + +Folder +== Ordner + +Force vote +== Erzwingen + +Free-View +== Freie Ansicht + +Friends +== Freunde + +Fullscreen +== Vollbild + +Game +== Spiel + +Game info +== Spielinfo + +Game over +== Spiel vorbei + +Game paused +== Spiel pausiert + +Game type +== Spieltyp + +Game types: +== Spieltypen: + +General +== Allgemein + +Graphics +== Grafik + +Grenade +== Granate + +Hammer +== Hammer + +Has people playing +== Server nicht leer + +High Detail +== Hohe Details + +Hook +== Haken + +Invalid Demo +== Ungültige Aufnahme + +Join blue +== zu Blau + +Join red +== zu Rot + +Jump +== Springen + +Kick player +== Spieler kicken + +Language +== Sprache + +Loading +== Laden + +MOTD +== Nachricht des Tages + +Map +== Karte + +Maximum ping: +== Maximaler Ping: + +Mouse sens. +== Maus-Empfindl. + +Move left +== Nach links + +Move player to spectators +== Spieler zu Zuschauer machen + +Move right +== Nach rechts + +Movement +== Bewegungen + +Mute when not active +== Stumm, wenn inaktiv + +Name +== Name + +Next weapon +== Nächste Waffe + +Nickname +== Spitzname + +No +== Nein + +No password +== Kein Passwort + +No servers found +== Keine Server gefunden + +No servers match your filter criteria +== Kein Server entspricht den Kriterien + +Ok +== OK + +Open +== Öffnen + +Parent Folder +== Übergeordneter Ordner + +Password +== Passwort + +Password incorrect +== Passwort falsch + +Ping +== Ping + +Pistol +== Pistole + +Play +== Abspielen + +Play background music +== Hintergrundmusik abspielen + +Player +== Spieler + +Player country: +== Spielerland: + +Player options +== Spieleroptionen + +Players +== Spieler + +Please balance teams! +== Bitte Teams ausgleichen! + +Prev. weapon +== Vorherige Waffe + +Quality Textures +== Hochauflösende Texturen + +Quit +== Beenden + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Grund: + +Red team +== Rotes team + +Red team wins! +== Rotes Team gewinnt! + +Refresh +== Erneuern + +Refreshing master servers +== Aktualisiere Masterserver + +Remote console +== Remotekonsole + +Remove +== Löschen + +Remove friend +== Freund entfernen + +Rename +== Umbenennen + +Rename demo +== Aufnahme umbenennen + +Reset filter +== Standardfilter + +Respawn +== Wiederbelebung + +Sample rate +== Abtastrate + +Score +== Score + +Score board +== Punktetafel + +Score limit +== Punktelimit + +Scoreboard +== Punktetafel + +Screenshot +== Bildschirmfoto + +Server address: +== Serveradresse: + +Server details +== Serverdetails + +Server filter +== Filter + +Server info +== Serverinfo + +Server not full +== Server nicht voll + +Settings +== Optionen + +Shotgun +== Schrotflinte + +Show chat +== Chat anzeigen + +Show friends only +== Nur Freunde zeigen + +Show ingame HUD +== Spielanzeigen + +Show name plates +== Zeige Namen + +Show only chat messages from friends +== Nur Nachrichten von Freunden zeigen + +Show only supported +== Zeige nur unterstützte + +Skins +== Skins + +Sound +== Ton + +Sound error +== Audiofehler + +Spectate +== Zuschauen + +Spectate next +== Nächstem zusehen + +Spectate previous +== Vorherigem zusehen + +Spectator mode +== Zuschaueroptionen + +Spectators +== Zuschauer + +Standard gametype +== Standard-Spieltyp + +Standard map +== Standardkarte + +Stop record +== Aufnahme be. + +Strict gametype filter +== Genauer Spieltyp + +Sudden Death +== Sudden Death + +Switch weapon on pickup +== Waffe beim Aufnehmen wechseln + +Team +== Team + +Team chat +== Teamchat + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworld %s ist da! Lade es unter www.teeworlds.com runter! + +Texture Compression +== Texturkompression + +The audio device couldn't be initialised. +== Das Audiosystem konnte nicht gestartet werden. + +The server is running a non-standard tuning on a pure game type. +== Der Server läuft nicht mit Standardeinstellungen. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Im Editor ist noch eine ungespeicherte Karte. Vielleicht möchtest du sie speichern. + +Time limit +== Zeitlimit + +Time limit: %d min +== Zeitlimit: %d min + +Try again +== Nochmal versuchen + +Type +== Typ + +Unable to delete the demo +== Aufnahme konnte nicht gelöscht werden + +Unable to rename the demo +== Aufnahme konnte nicht umbenannt werden + +Use sounds +== Aktiviere Ton + +Use team colors for name plates +== Nutze Teamfarben für Spielernamen + +V-Sync +== V-Sync + +Version +== Version + +Vote command: +== Kommando: + +Vote description: +== Beschreibung: + +Vote no +== Nein + +Vote yes +== Ja + +Voting +== Abstimmung + +Warmup +== Aufwärmen + +Weapon +== Waffe + +Welcome to Teeworlds +== Willkommen bei Teeworlds + +Yes +== Ja + +You must restart the game for all settings to take effect. +== Du musst das Spiel neustarten um die Änderungen zu übernehmen. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Neuer Name: + +Sat. +== Sätt. + +Miscellaneous +== Verschiedenes + +Internet +== Internet + +Max demos +== Maximale Anzahl an Aufnahmen + +News +== News + +Join game +== Beitreten + +FSAA samples +== Vollbild Anti-Aliasing + +%d of %d servers, %d players +== %d von %d Servern, %d Spieler + +Sound volume +== Lautstärke + +Created: +== Erstellt: + +Max Screenshots +== Maximale Anzahl an Bildschirmfotos + +Length: +== Länge + +Skin name +== + +Rifle +== Laser + +Patterns +== + +Netversion: +== Netversion: + +Map: +== Karte: + +%d Bytes +== %d Bytes + +Coloration +== + +Info +== Info + +Hue +== Farb. + +Record demo +== Aufnahme sta. + +Your skin +== Dein Skin + +Size: +== Größe: + + +== ## translated strings ##### + +Reset to defaults +== Standardeinstellung + +Quit anyway? +== Trotzdem beenden? + +Display Modes +== Auflösungen + +Version: +== Version: + +Round +== Runde + +Lht. +== Hell. + +no limit +== Keine Begrenzung + +Quick search: +== Schnellsuche: + +UI Color +== Menüfarbe + +Host address +== Serveradresse + +Crc: +== Prüfsumme: + +Alpha +== Alpha + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Aktuelle Version: %s + +LAN +== LAN + +Name plates size +== Größe der Spielernamen + +Type: +== Typ: + diff --git a/data/languages/greek.txt b/data/languages/greek.txt new file mode 100644 index 0000000000..da10337598 --- /dev/null +++ b/data/languages/greek.txt @@ -0,0 +1,1018 @@ +##### authors ##### +#originally created by: +# Evropi +#modified by: +# +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% φορτώθηκε + +%ds left +== %ds ακόμα + +%i minute left +== απομένει %i λεπτό + +%i minutes left +== απομένουν %i λεπτά + +%i second left +== απομένει %i δευτερόλεπτο + +%i seconds left +== απομένουν %i δευτερόλεπτα + +%s wins! +== Ο/Η %s νίκησε! + +-Page %d- +== -Σελίδα %d- + +Abort +== Ματαίωση + +Add +== Προσθήκη + +Add Friend +== Προσθήκη Φίλου + +Address +== Διεύθυνση + +All +== Όλοι + +Always show name plates +== Πάντα εμφάνιζε τα ψευδώνυμα + +Are you sure that you want to delete the demo? +== Θέλετε σίγουρα να διαγράψετε αυτήν την επίδειξη; + +Are you sure that you want to quit? +== Θέλετε σίγουρα να παραιτηθείτε; + +Are you sure that you want to remove the player from your friends list? +== Θέλετε σίγουρα να αφερέσετε αυτόν τον παίκτη από την λίστα των φίλων σας; + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Επειδή τρέχετε το παιχνίδι για πρώτη φορά, παρακαλώ βάλτε το ψευδώνυμο σας από κάτω. Συνίσταται να προσαρμόσετε τις ρυθμίσεις ανάλογα με τις προτιμήσεις σας πριν παίξετε. + +Automatically record demos +== Αυτόματη καταγραφή επιδείξεων + +Automatically take game over screenshot +== Αυτόμ. λήψη στιγμιότυπου στο τέλος παιχν. + +Blue team +== Μπλε ομάδα + +Blue team wins! +== Η μπλε ομάδα νίκησε! + +Body +== Σώμα + +Call vote +== Ψηφοφορία + +Change settings +== Αλλαγή ρυθμίσεων + +Chat +== Συνομιλία + +Clan +== Φυλή + +Client +== Πελάτης + +Close +== Κλείσιμο + +Compatible version +== Συμβατή έκδοση + +Connect +== Σύνδεση + +Connecting to +== Σύνδεση με + +Connection Problems... +== Προβλήματα Σύνδεσης... + +Console +== Κονσόλα + +Controls +== Χειρισμός + +Count players only +== Μέτρηση παικτών μόνο + +Country +== Χώρα + +Current +== Τρέχουσα + +Custom colors +== Προσαρμοσμένα χρώματα + +Delete +== Διαγραφή + +Delete demo +== Διαγραφή επίδειξης + +Demo details +== Λεπτομέρειες επίδειξης + +Demofile: %s +== Αρχείο επίδειξης: %s + +Demos +== Επιδείξεις + +Disconnect +== Αποσύνδεση + +Disconnected +== Αποσυνδέθηκε + +Downloading map +== Λήψη χάρτη + +Draw! +== Ισοπαλία! + +Dynamic Camera +== Δυναμική Κάμερα + +Emoticon +== Emoticon + +Enter +== Είσοδος + +Error +== Σφάλμα + +Error loading demo +== Σφάλμα φόρτωσης επίδειξης + +Favorite +== Αγαπημένος + +Favorites +== Αγαπημένοι + +Feet +== Πόδια + +Filter +== Φίλτρο + +Fire +== Πυρ + +Folder +== Φάκελος + +Force vote +== Αναγκαστική ψήφος + +Free-View +== Ελεύθερη Όψη + +Friends +== Φίλοι + +Fullscreen +== Πλήρης οθόνη + +Game +== Παιχνίδι + +Game info +== Πληροφορίες παιχνιδιού + +Game over +== Τέλος παιχνιδιού + +Game paused +== Παιχνίδι εν παύση + +Game type +== Τύπος παιχνιδιού + +Game types: +== Τύποι παιχνιδιού: + +General +== Γενικά + +Graphics +== Γραφικά + +Grenade +== Εκτοξευτής + +Hammer +== Σφυρί + +Has people playing +== Έχει παίκτες μέσα + +High Detail +== Υψηλή Λεπτομέρεια + +Hook +== Γάντζος + +Invalid Demo +== Άκυρη Επίδειξη + +Join blue +== Μπλε + +Join red +== Κόκκινοι + +Jump +== Άλμα + +Kick player +== Αποβολή παίκτη + +Language +== Γλώσσα + +Loading +== Φόρτωση + +MOTD +== Μήνυμα της ημέρας + +Map +== Χάρτης + +Maximum ping: +== Μέγιστο ping: + +Mouse sens. +== Ταχύτητα δείκτη + +Move left +== Κίνηση αριστερά + +Move player to spectators +== Μετακίνηση του παίκτη στους θεατές + +Move right +== Κίνηση δεξιά + +Movement +== Κίνηση + +Mute when not active +== Σίγαση εν αδρανεία + +Name +== Όνομα + +Next weapon +== Επόμενο όπλο + +Nickname +== Ψευδώνυμο + +No +== Όχι + +No password +== Χωρίς κωδικό + +No servers found +== Δεν βρέθηκαν διακομιστές + +No servers match your filter criteria +== Κανένας διακομιστής με αυτά τα κριτήρια + +Ok +== Εντάξει + +Open +== Άνοιγμα + +Parent Folder +== Επάνω + +Password +== Κωδικός + +Password incorrect +== Λανθασμένος κωδικός + +Ping +== Ping + +Pistol +== Πιστόλι + +Play +== Παίξτε + +Play background music +== Αναπαραγωγή μουσικής υπόκρουσης + +Player +== Παίκτης + +Player country: +== Χώρα παίκτη: + +Player options +== Επιλογές παίκτη + +Players +== Παίκτες + +Please balance teams! +== Παρακαλούμε εξισορροπήστε τις ομάδες! + +Prev. weapon +== Προηγούμενο όπλο + +Quality Textures +== Ποιότητα Υφής + +Quit +== Έξοδος + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Αιτία: + +Red team +== Κόκκινη ομάδα + +Red team wins! +== Η κόκκινη ομάδα νίκησε! + +Refresh +== Ανανέωση + +Refreshing master servers +== Ανανέωση κύριων διακομιστών + +Remote console +== Απομακρ. κονσόλα + +Remove +== Αφαίρεση + +Remove friend +== Αφαίρεση φίλου + +Rename +== Μετονομασία + +Rename demo +== Μετονομασία επίδειξης + +Reset filter +== Αφαίρεση φίλτρου + +Respawn +== Αναγέννηση + +Sample rate +== Δειγματοληψία + +Score +== Βαθμ. + +Score board +== Πίνακας βαθμ/γίας + +Score limit +== Όριο βαθμολογίας + +Scoreboard +== Πίνακας βαθμ/γίας + +Screenshot +== Στιγμιότυπο οθόνης + +Server address: +== Διεύθ/ση διακομιστή: + +Server details +== Λεπτομέριες διακομιστή + +Server filter +== Φίλτρο διακομιστών + +Server info +== Πληροφορίες + +Server not full +== Μη πλήρης διακομιστής + +Settings +== Ρυθμίσεις + +Shotgun +== Καραμπίνα + +Show chat +== Προβ. συνομιλίας + +Show friends only +== Εμφάνιση φίλων μόνο + +Show ingame HUD +== Εμφάνιση μενού εντός παιχνιδιού + +Show name plates +== Προβολή ψευδωνύμων + +Show only chat messages from friends +== Προβολή μηνυμάτων μόνο από φίλους + +Show only supported +== Προβολή υποστηριζόμενων μόνο + +Skins +== Στολές + +Sound +== Ήχος + +Sound error +== Σφάλμα ήχου + +Spectate +== Θέαση + +Spectate next +== Επόμενος + +Spectate previous +== Προηγούμενος + +Spectator mode +== Λειτουργία θέασης + +Spectators +== Θεατές + +Standard gametype +== Κανονικός τύπος παιχν. + +Standard map +== Κανονικός χάρτης + +Stop record +== Παύση καταγραφής + +Strict gametype filter +== Αυστηρό φίλτρο παιχν. + +Sudden Death +== Ξαφνικός Θάνατος + +Switch weapon on pickup +== Αλλαγή όπλου ανά μάζεμα + +Team +== Ομάδα + +Team chat +== Ομαδική συνομιλία + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Το Teeworlds %s κυκλοφόρησε! Κατεβάστε το από το www.teeworlds.com! + +Texture Compression +== Συμπίεση Υφής + +The audio device couldn't be initialised. +== Η συσκευή ήχου δεν μπορούσε να ενεργοποιηθεί. + +The server is running a non-standard tuning on a pure game type. +== Ο διακομιστής εκτελεί μη-κανονικές ρυθμίσεις σε κανονικό τύπο παιχνιδιού. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Υπαρχεί μη αποθηκευμένος χάρτης στον επεξεργαστή. Να γίνει αποθήκευση πριν την έξοδο; + +Time limit +== Όριο χρόνου + +Time limit: %d min +== Όριο χρόνου: %d λ + +Try again +== Προσπαθήστε ξανά + +Type +== Τύπος + +Unable to delete the demo +== Απέτυχε η διαγραφή της επίδειξης + +Unable to rename the demo +== Απέτυχε η μετονομασία της επίδειξης + +Use sounds +== Χρήση ήχων + +Use team colors for name plates +== Χρήση χρώμ. ομάδας στα ψευδώνυμα + +V-Sync +== Καθοδικός συγχρονισμός + +Version +== Έκδοση + +Vote command: +== Εντολή ψήφου: + +Vote description: +== Περιγραφή ψήφου: + +Vote no +== Ψηφίστε όχι + +Vote yes +== Ψηφίστε ναι + +Voting +== Ψηφοφορία + +Warmup +== Ζέσταμα + +Weapon +== Όπλο + +Welcome to Teeworlds +== Καλωσορίσατε στο Teeworlds + +Yes +== Ναι + +You must restart the game for all settings to take effect. +== Πρέπει να επανεκκινήσετε το παιχνίδι για να εφαρμοστούν όλες οι αλλαγές. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Νέο όνομα: + +Sat. +== Κορεσμός + +Miscellaneous +== Διάφορα + +Internet +== Διαδίκτυο + +Max demos +== Μέγιστος αριθμός επιδείξεων + +News +== Νέα + +Join game +== Συμμετοχή + +FSAA samples +== Δείγματα FSAA + +%d of %d servers, %d players +== %d από %d διακ/στές, %d παίκτες + +Sound volume +== Ένταση ήχου + +Created: +== Δημιουργήθηκε: + +Max Screenshots +== Μέγιστος αριθμός στιγμιότυπων + +Length: +== Διάρκεια: + +Skin name +== + +Rifle +== Λέιζερ + +Patterns +== + +Netversion: +== Έκδοση: + +Map: +== Χάρτης: + +%d Bytes +== %d Bytes + +Coloration +== + +Info +== Πληροφορίες + +Hue +== Απόχρωση + +Record demo +== Εγγ. επίδειξης + +Your skin +== Η στολή σας + +Size: +== Μέγεθος: + +Reset to defaults +== Επαναφορά προεπιλογών + +Quit anyway? +== Έξοδος οπωσδήποτε; + +Display Modes +== Ανάλυσεις Οθόνης + +Version: +== Έκδοση: + +Round +== Γύρος + +Lht. +== Φωτεινότ. + +no limit +== χωρίς όριο + +Quick search: +== Γρήγορη αναζήτηση: + +UI Color +== Χρώμα Μενού + +Host address +== Διεύθυνση διακομιστή + +Crc: +== Crc: + +Alpha +== Άλφα + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Τρέχουσα έκδοση: %s + +LAN +== Τοπικά + +Name plates size +== Μήκος ψευδωνύμου + +Type: +== Τύπος: + diff --git a/data/languages/hungarian.txt b/data/languages/hungarian.txt new file mode 100644 index 0000000000..87a5dcb869 --- /dev/null +++ b/data/languages/hungarian.txt @@ -0,0 +1,1018 @@ +##### authors ##### +#originally created by: +# Feca +#modified by: +# +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% betöltve + +%ds left +== %ds vissza + +%i minute left +== %i perc vissza + +%i minutes left +== %i perc vissza + +%i second left +== %i másodperc vissza + +%i seconds left +== %i másodperc vissza + +%s wins! +== %s nyert! + +-Page %d- +== -oldal %d- + +Abort +== Mégse + +Add +== Hozzáad + +Add Friend +== Hozzáad barátot + +Address +== Cím + +All +== Mindenki + +Always show name plates +== Mindig mutassa a névtáblát + +Are you sure that you want to delete the demo? +== Biztos hogy le akarod törölni a demót? + +Are you sure that you want to quit? +== Biztos hogy ki akarsz lépni? + +Are you sure that you want to remove the player from your friends list? +== Biztos vagy benne hogy ki akarod törölni a játékost a barátok listájáról? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Mivel ez az első alkalom hogy elindítottad ezt a játékot, kérlekk add meg a neved! + +Automatically record demos +== Magától rögzítse a demókat + +Automatically take game over screenshot +== Magától készítsen a játék végén képet + +Blue team +== Kék csapat + +Blue team wins! +== A kék csapat nyert! + +Body +== Test + +Call vote +== Szavazás + +Change settings +== Beállítások átállítása + +Chat +== Chat + +Clan +== Klán + +Client +== Kliens + +Close +== Bezárás + +Compatible version +== Kompatibilis Verzió + +Connect +== Csatlakozás + +Connecting to +== Csatlakozás a + +Connection Problems... +== Csatlakozási problémák... + +Console +== Konzol + +Controls +== Irányítás + +Count players only +== Csak számontartott játékosok + +Country +== Ország + +Current +== Aktuális + +Custom colors +== Egyéni színek + +Delete +== Törlés + +Delete demo +== Demó törlése + +Demo details +== Információk a demóról + +Demos +== Demók + +Disconnect +== Szerver elhagyása + +Disconnected +== Kilépett + +Downloading map +== Pálya letöltése + +Draw! +== Rajzolj! + +Dynamic Camera +== Dinamikus kamera + +Emoticon +== Hangulatjel + +Enter +== Belépés + +Error +== Hiba + +Error loading demo +== Hiba a demó betöltésében + +Favorite +== Kedvenc + +Favorites +== Kedvencek + +Feet +== Láb + +Filter +== Szűrő + +Fire +== Tűz + +Folder +== Mappa + +Force vote +== Különleges szavazás + +Free-View +== Szabad-nézet + +Friends +== Barátok + +Fullscreen +== Teljesképernyő + +Game +== Játék + +Game info +== Játék infó + +Game over +== Játék vége + +Game type +== Játék fajtája + +Game types: +== Játék fajtái: + +General +== Általános + +Graphics +== Grafika + +Grenade +== Gránát + +Hammer +== Kalapács + +Has people playing +== Játékos játszik + +High Detail +== Jó részletek + +Hook +== Horog + +Invalid Demo +== Érvénytelen demó + +Join blue +== Kékhez lépés + +Join red +== Piroshoz lépés + +Jump +== Ugrás + +Kick player +== Játékos kirúgása + +Language +== Nyelv + +Loading +== Betöltés + +MOTD +== Napi üzenet + +Map +== Pálya + +Maximum ping: +== Maximum Ping: + +Mouse sens. +== Egér érzékenysége + +Move left +== Balra lépés + +Move player to spectators +== Megfigyelő lett + +Move right +== Jobbra lépés + +Movement +== Mozgás + +Mute when not active +== Letiltás ha nem aktív + +Name +== Név + +Next weapon +== Következő fegyver + +Nickname +== Becenév + +No +== Nem + +No password +== Jelszó nélküli + +No servers found +== Nem talált szervereket + +No servers match your filter criteria +== Nincs szerver a szűrőfeltételeidhez + +Ok +== Oké + +Open +== Megnyit + +Parent Folder +== Szülői mappa + +Password +== Jelszó + +Password incorrect +== Helytelen jelszó + +Ping +== Ping + +Pistol +== Pisztoly + +Play +== Játék + +Player +== Játékos + +Player options +== Játékos beállításai + +Players +== Játékosok + +Please balance teams! +== Kérlek egyenlítsd ki a csapatokat! + +Prev. weapon +== Előző fegyver + +Quality Textures +== Minőségi kidolgozás + +Quit +== Kilépés + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Indok: + +Red team +== Piros csapat + +Red team wins! +== A piros csapat nyert! + +Refresh +== Újratöltés + +Refreshing master servers +== A master szerverek frissítése + +Remote console +== Távoli konzol + +Remove +== Eltávolítás + +Remove friend +== Barát eltávolítása + +Rename +== Átnevezés + +Rename demo +== Demó átnevezése + +Reset filter +== Szűrő visszaállítása + +Sample rate +== Mintavételi frekvencia + +Score +== Pontszám + +Score board +== Pontszám tábla + +Score limit +== Ponthatár + +Scoreboard +== Pontszámtábla + +Screenshot +== Pillanatkép + +Server address: +== Szerver címe: + +Server details +== Szerver részletei + +Server filter +== Szerver szűrő + +Server info +== Szerver infó + +Server not full +== Szerver nincs tele + +Settings +== Beállítások + +Shotgun +== Sörétes puska + +Show chat +== Chat mutatása + +Show friends only +== Barátok mutatása + +Show ingame HUD +== Játék közbeni HUD mutatása + +Show name plates +== Név táblák mutatása + +Show only supported +== Csak támogatott mutatása + +Skins +== Skinek + +Sound +== Hang + +Sound error +== Hang hiba + +Spectate +== Megfigyelés + +Spectator mode +== Néző mód + +Spectators +== Megfigyelők + +Standard gametype +== Általános játékfajta + +Standard map +== Általános pálya + +Stop record +== Felvétel megállítása + +Sudden Death +== Gyors halál + +Switch weapon on pickup +== Fegyverváltás felvételnél + +Team +== Csapat + +Team chat +== Csapat chat + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s kint van! Töltsd le www.teeworlds.com oldalon! + +Texture Compression +== Textúra tömörítés + +The audio device couldn't be initialised. +== A hangeszköz nem kezdeményezhető. + +The server is running a non-standard tuning on a pure game type. +== A szerver egy nem szabványos hangolást futtat a tiszta játék típuson. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Van egy mentett térkép a szerkesztőben, talán akarod menteni, mielőtt kilépsz a játékból. + +Time limit +== Időhatár + +Time limit: %d min +== Időhatár: %d perc + +Try again +== Próbáld újra + +Type +== Típus + +Unable to delete the demo +== Nem sikerült törölni a demó + +Unable to rename the demo +== Nem sikerült átnevezni a demó + +Use sounds +== Hangok használata + +Use team colors for name plates +== Csapat szín használata név tábláknál + +V-Sync +== V-Sync + +Version +== Verzió + +Vote command: +== Szavazás parancsa: + +Vote description: +== Szavazás leírása: + +Vote no +== Nem + +Vote yes +== Igen + +Voting +== Szavazás + +Warmup +== Kezdés + +Weapon +== Fegyver + +Welcome to Teeworlds +== Üdvözöljük a Teeworlds-ben + +Yes +== Igen + +You must restart the game for all settings to take effect. +== Újra kell indítani a játékot, a beállítások érvénybe lépéséhez. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Demofile: %s +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Play background music +== + +Player country: +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectate next +== + +Spectate previous +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Strict gametype filter +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Új Név: + +Sat. +== Sat. + +Internet +== Internet + +Max demos +== Maximum Demó + +News +== Hírek + +Join game +== Csatlakozás a játékhoz + +FSAA samples +== FSAA mintáku + +%d of %d servers, %d players +== %d Szerver, %d Játékos + +Sound volume +== Hangerő + +Created: +== Készítette: + +Max Screenshots +== Maximum Fotó + +Length: +== Hossza: + +Skin name +== + +Rifle +== Lézer + +Patterns +== + +Netversion: +== Netes verzió: + +Map: +== Pálya: + +%d Bytes +== %d Bit + +Coloration +== + +Info +== Infó + +Miscellaneous +== Vegyes + +Record demo +== Demó felvétele + +Your skin +== Te skined + +Size: +== Méret: + +Reset to defaults +== Visszaállítás az alapértelmezettre + +Quit anyway? +== Mindenképpen kilép? + +Display Modes +== Kijelző módok + +Version: +== Verzió: + +Round +== Menet + +Lht. +== Lht. + +no limit +== Nincs korlát + +Quick search: +== Gyors keresés: + +UI Color +== UI Szín + +Host address +== Szerver címe + +Hue +== Színárnyalat + +Crc: +== Crc: + +Alpha +== Alpha + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Aktuális verzió: %s + +LAN +== Helyi + +Name plates size +== Névtáblák mérete + +Type: +== Típus: + diff --git a/data/languages/index.txt b/data/languages/index.txt new file mode 100644 index 0000000000..751c6bfa57 --- /dev/null +++ b/data/languages/index.txt @@ -0,0 +1,97 @@ +##### language indices ##### + +belarusian +== Беларуская +== 112 + +bosnian +== Bosanski +== 70 + +brazilian_portuguese +== Português brasileiro +== 76 + +bulgarian +== Български +== 100 + +czech +== Česky +== 203 + +danish +== Dansk +== 208 + +dutch +== Nederlands +== 528 + +finnish +== Suomi +== 246 + +french +== Français +== 250 + +german +== Deutsch +== 276 + +hungarian +== Magyar +== 348 + +italian +== Italiano +== 380 + +kyrgyz +== Кыргызча +== 417 + +norwegian +== Norsk +== 578 + +polish +== Polski +== 616 + +portuguese +== Português +== 620 + +romanian +== Română +== 642 + +russian +== Русский +== 643 + +serbian +== Srpski +== 688 + +slovak +== Slovensky +== 703 + +spanish +== Español +== 724 + +swedish +== Svenska +== 752 + +turkish +== Türkçe +== 792 + +ukrainian +== Українська +== 804 diff --git a/data/languages/italian.txt b/data/languages/italian.txt new file mode 100644 index 0000000000..30768bdc82 --- /dev/null +++ b/data/languages/italian.txt @@ -0,0 +1,1025 @@ +##### authors ##### +#originally created by: +# +#modified by: +# Lanta 2010-05-30 16:26:04 +# Lanta 2010-05-31 22:05:45 +# Carmine 2011-01-23 17:54:06 +# Otacon 2011-05-02 18:21:08 +# Apmox 2011-07-15 01:09:59 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% caricato + +%ds left +== %ds sec. + +%i minute left +== %i minuto rimanente + +%i minutes left +== %i minuti rimanenti + +%i second left +== %i secondo rimanente + +%i seconds left +== %i secondi rimanenti + +%s wins! +== %s ha vinto! + +-Page %d- +== -Pagina %d- + +Abort +== Annulla + +Add +== Aggiungi + +Add Friend +== Aggiungi amico + +Address +== Indirizzo + +All +== Tutti + +Always show name plates +== Mostra sempre i nomi + +Are you sure that you want to delete the demo? +== Sicuro di voler eliminare la demo? + +Are you sure that you want to quit? +== Sicuro di voler chiudere il gioco? + +Are you sure that you want to remove the player from your friends list? +== Sicuro di voler rimuovere il giocatore dalla lista di amici? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Essendo la prima volta che avvii il gioco, ti preghiamo di inserire il tuo nickname qui sotto. Puoi cambiare le impostazioni in base alle tue preferenze prima di entrare in gioco. + +Automatically record demos +== Registra automaticamente demo + +Automatically take game over screenshot +== Cattura schermata alla fine di ogni partita + +Blue team +== Squadra blu + +Blue team wins! +== La squadra blu ha vinto! + +Body +== Corpo + +Call vote +== Vota + +Change settings +== Cambia opzioni + +Chat +== Chat + +Clan +== Clan + +Client +== Client + +Close +== Chiudi + +Compatible version +== Versione compatibile + +Connect +== Connetti + +Connecting to +== Connessione a + +Connection Problems... +== Problemi di connessione... + +Console +== Console + +Controls +== Comandi + +Count players only +== Conta solo giocatori + +Country +== Paese + +Current +== Attuale + +Custom colors +== Colori personalizzati + +Delete +== Elimina + +Delete demo +== Elimina demo + +Demo details +== Dettagli demo + +Demofile: %s +== Demo: %s + +Demos +== Demo + +Disconnect +== Disconnetti + +Disconnected +== Disconnesso + +Downloading map +== Scaricamento mappa in corso + +Draw! +== Pareggio! + +Dynamic Camera +== Camera dinamica + +Emoticon +== Emoticon + +Enter +== Entra + +Error +== Errore + +Error loading demo +== Errore caricamento demo + +Favorite +== Preferito + +Favorites +== Preferiti + +Feet +== Piedi + +Filter +== Filtro + +Fire +== Fuoco + +Folder +== Cartella + +Force vote +== Forza voto + +Free-View +== Visione libera + +Friends +== Amici + +Fullscreen +== Schermo intero + +Game +== Partita + +Game info +== Info partita + +Game over +== Partita finita + +Game type +== Tipo + +Game types: +== Tipo di gioco: + +General +== Generale + +Graphics +== Aspetto + +Grenade +== Lanciagranate + +Hammer +== Martello + +Has people playing +== Con giocatori + +High Detail +== Alta qualità + +Hook +== Rampino + +Invalid Demo +== Demo non valida + +Join blue +== Vai ai blu + +Join red +== Vai ai rossi + +Jump +== Salta + +Kick player +== Caccia giocatore + +Language +== Lingua + +Loading +== Caricamento + +MOTD +== MOTD + +Map +== Mappa + +Maximum ping: +== Ping massimo: + +Mouse sens. +== Sensibilità + +Move left +== Sinistra + +Move player to spectators +== Fai osservare il giocatore + +Move right +== Destra + +Movement +== Movimento + +Mute when not active +== Silenzioso quanto inattivo + +Name +== Nome + +Next weapon +== Arma seguente + +Nickname +== Nickname + +No +== No + +No password +== Senza password + +No servers found +== Nessun server trovato + +No servers match your filter criteria +== Nessun server corrisponde ai tuoi criteri di ricerca + +Ok +== Ok + +Open +== Apri + +Parent Folder +== Cartella superiore + +Password +== Password + +Password incorrect +== Password errata + +Ping +== Ping + +Pistol +== Pistola + +Play +== Riproduci + +Play background music +== Riproduci musica di sottofondo + +Player +== Giocatore + +Player country: +== Filtra per paese: + +Player options +== Opzioni giocatore + +Players +== Giocatori + +Please balance teams! +== Equilibra le squadre! + +Prev. weapon +== Arma precedente + +Quality Textures +== Texture di qualità + +Quit +== Chiudi + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Motivo: + +Red team +== Squadra rossa + +Red team wins! +== La squadra rossa ha vinto! + +Refresh +== Aggiorna + +Refreshing master servers +== Aggiornamento server principali + +Remote console +== Console remota + +Remove +== Elimina + +Remove friend +== Elimina amico + +Rename +== Rinomina + +Rename demo +== Rinomina demo + +Reset filter +== Azzera filtri + +Sample rate +== Frequenza + +Score +== Punti + +Score board +== Punteggio + +Score limit +== Limite punti + +Scoreboard +== Punteggi + +Screenshot +== Cattura schermata + +Server address: +== Indirizzo server: + +Server details +== Dettagli server + +Server filter +== Filtro server + +Server info +== Info server + +Server not full +== Server non pieno + +Settings +== Opzioni + +Shotgun +== Fucile + +Show chat +== Mostra chat + +Show friends only +== Mostra solo amici + +Show ingame HUD +== Mostra HUD in gioco + +Show name plates +== Mostra nomi + +Show only supported +== Mostra solo supportati + +Skins +== Skin + +Sound +== Suono + +Sound error +== Errore suono + +Spectate +== Osserva + +Spectate next +== Osserva succ. + +Spectate previous +== Osserva prec. + +Spectator mode +== Menu osservazione + +Spectators +== Spettatori + +Standard gametype +== Tipo di gioco normale + +Standard map +== Mappa normale + +Stop record +== Ferma reg. + +Strict gametype filter +== Tipo di gioco preciso + +Sudden Death +== Tempo supplementare + +Switch weapon on pickup +== Cambia arma ad ogni raccolta + +Team +== Squadra + +Team chat +== Chat di squadra + +Teeworlds %s is out! Download it at www.teeworlds.com! +== È uscito Teeworld %s! Scaricalo su www.teeworlds.com! + +Texture Compression +== Compressione texture + +The audio device couldn't be initialised. +== Il dispositivo audio non può essere inizializzato. + +The server is running a non-standard tuning on a pure game type. +== Il server presenta impostazioni non standard in un tipo di gioco normale. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== C'è una mappa non salvata nell'editor, probabilmente vuoi salvarla prima di uscire. + +Time limit +== Limite di tempo + +Time limit: %d min +== Limite di tempo: %d min + +Try again +== Ritenta + +Type +== Tipo + +Unable to delete the demo +== Impossibile eliminare la demo + +Unable to rename the demo +== Impossibile rinominare la demo + +Use sounds +== Attiva suoni + +Use team colors for name plates +== Usa i colori della squadra nei nomi + +V-Sync +== Sincronizzazione verticale + +Version +== Versione + +Vote command: +== Comando di voto: + +Vote description: +== Descrizione voto: + +Vote no +== Vota no + +Vote yes +== Vota sì + +Voting +== Votazione + +Warmup +== Riscaldamento + +Weapon +== Arma + +Welcome to Teeworlds +== Benvenuto su Teeworlds + +Yes +== Sì + +You must restart the game for all settings to take effect. +== Devi riavviare il gioco per rendere effettive le modifiche. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Nuovo nome: + +Sat. +== Saturazione + +Miscellaneous +== Altro + +Internet +== Internet + +Max demos +== Numero massimo di demo + +News +== Novità + +Join game +== Entra + +FSAA samples +== Campioni FSAA + +%d of %d servers, %d players +== %d di %d server, %d giocatori + +Sound volume +== Volume suono + +Created: +== Creata: + +Max Screenshots +== Numero massimo di catture + +Length: +== Durata: + +Skin name +== + +Rifle +== Laser + +Patterns +== + +Netversion: +== Versione net + +Map: +== Mappa: + +%d Bytes +== %d byte + +Coloration +== + +Info +== Info + +Hue +== Tinta + +Record demo +== Registra demo + +Your skin +== La tua skin + +Size: +== Dimensione: + + +== ## translated strings ##### + +Reset to defaults +== Reimposta + +Quit anyway? +== Vuoi chiudere comunque? + +Display Modes +== Risoluzioni schermo + +Version: +== Versione: + +Round +== Turno + +Lht. +== Luminosità + +no limit +== senza limiti + +Quick search: +== Ricerca rapida: + +UI Color +== Color interfaccia + +Host address +== Indirizzo host + +Crc: +== Crc: + +Alpha +== Alpha + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Versione attuale: %s + +LAN +== LAN + +Name plates size +== Dimensione nomi + +Type: +== Tipo: + diff --git a/data/languages/japanese.txt b/data/languages/japanese.txt new file mode 100644 index 0000000000..9122d3da6c --- /dev/null +++ b/data/languages/japanese.txt @@ -0,0 +1,1006 @@ +##### authors ##### +#originally created by: +# orange +#modified by: +# +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% 読み込み中 + +%ds left +== 残り %ds + +%i minute left +== 残り %i 分 + +%i minutes left +== 残り %i 分 + +%i second left +== 残り %i 秒 + +%i seconds left +== 残り %i 秒 + +%s wins! +== %s の勝ち! + +-Page %d- +== -%d ページ- + +Abort +== 中止 + +Add +== 追加 + +Add Friend +== 友達追加 + +Address +== アドレス + +All +== 全部 + +Always show name plates +== 常に名前を表示 + +Are you sure that you want to delete the demo? +== デモを削除してもよろしいですか? + +Are you sure that you want to quit? +== 終了してもよろしいですか? + +Are you sure that you want to remove the player from your friends list? +== このプレイヤーを友達リストから削除してもよろしいですか? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== あなたのハンドルネームを入力してください。(半角英数のみ) + +Automatically record demos +== 自動的にデモを保存する + +Automatically take game over screenshot +== 自動的にスクリーンショットを保存する + +Blue team +== 青チーム + +Blue team wins! +== 青チームの勝ち! + +Body +== 体 + +Call vote +== 投票を開始する + +Change settings +== 設定を変える + +Chat +== チャット + +Clan +== クラン + +Client +== クライアント + +Close +== 閉じる + +Compatible version +== 互換性のあるバージョン + +Connect +== 接続 + +Connecting to +== 接続先 + +Connection Problems... +== 接続中に問題が発生 + +Console +== コンソール + +Controls +== コントロール + +Count players only +== 実際のプレイヤー数を表示 + +Country +== 国 + +Current +== 現行 + +Custom colors +== 色変更 + +Delete +== 削除 + +Delete demo +== デモを削除 + +Demo details +== デモの詳細 + +Demofile: %s +== デモファイル: %s + +Demos +== デモ + +Disconnect +== 切断 + +Disconnected +== 切断されました + +Downloading map +== マップをダウンロード中 + +Draw! +== 引き分け! + +Dynamic Camera +== ダイナミックカメラ + +Emoticon +== エモティコン + +Enter +== Enter + +Error +== エラー + +Error loading demo +== デモの読み込みに失敗しました + +Favorite +== お気に入り + +Favorites +== お気に入り + +Feet +== 足 + +Filter +== フィルタ + +Fire +== 射撃 + +Folder +== フォルダ + +Force vote +== 強制投票 + +Free-View +== 自由視点 + +Friends +== 友達 + +Fullscreen +== フルスクリーン + +Game +== ゲーム + +Game info +== ゲーム情報 + +Game over +== ゲームオーバー + +Game type +== ゲームタイプ + +Game types: +== ゲームタイプ: + +General +== 一般 + +Graphics +== グラフィック + +Grenade +== グレネード + +Hammer +== ハンマー + +Has people playing +== プレイヤーがいるサーバー + +High Detail +== High Detail表示 + +Hook +== フック + +Invalid Demo +== 無効なデモ + +Join blue +== 青チームに参加 + +Join red +== 赤チームに参加 + +Jump +== ジャンプ + +Kick player +== プレイヤーをキック + +Language +== 言語 + +Loading +== 読み込み中 + +MOTD +== 説明 + +Map +== マップ + +Maximum ping: +== Pingの最大値: + +Mouse sens. +== マウス感度 + +Move left +== 左移動 + +Move player to spectators +== プレイヤーを観戦席に移動 + +Move right +== 右移動 + +Movement +== 動き + +Mute when not active +== 非アクティブ時にミュート + +Name +== 名前 + +Next weapon +== 次の武器 + +Nickname +== ハンドルネーム + +No +== いいえ + +No password +== パスワードがないサーバー + +No servers found +== サーバーが見つかりません + +No servers match your filter criteria +== フィルタに適合するサーバーが見つかりません + +Ok +== Ok + +Open +== 開く + +Parent Folder +== 親フォルダ + +Password +== パスワード + +Password incorrect +== パスワードが正しくありません + +Ping +== Ping + +Pistol +== ピストル + +Play +== プレイ + +Play background music +== BGMを再生する + +Player +== プレイヤー + +Player country: +== 国籍: + +Player options +== プレイヤー設定 + +Players +== プレイヤー + +Please balance teams! +== チーム人数が偏っています! + +Prev. weapon +== 前の武器 + +Quality Textures +== テクスチャの品質 + +Quit +== 終了 + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== 理由: + +Red team +== 赤チーム + +Red team wins! +== 赤チームの勝ち! + +Refresh +== 更新 + +Refreshing master servers +== サーバーリスト更新 + +Remote console +== リモコン + +Remove +== 削除 + +Remove friend +== 友達を削除 + +Rename +== 名前変更 + +Rename demo +== デモの名前変更 + +Reset filter +== フィルタをリセット + +Sample rate +== サンプルレート + +Score +== スコア + +Score board +== スコアボード + +Score limit +== スコア上限 + +Scoreboard +== スコアボード + +Screenshot +== スクリーンショット + +Server address: +== IPアドレス: + +Server details +== サーバーの詳細 + +Server filter +== サーバーフィルタ + +Server info +== サーバー情報 + +Server not full +== 空きがあるサーバー + +Settings +== 設定 + +Shotgun +== ショットガン + +Show chat +== チャットを見る + +Show friends only +== 友達がいるサーバー + +Show ingame HUD +== ゲーム内でHUDを表示 + +Show name plates +== 名前を表示 + +Show only supported +== 対応している項目のみを表示 + +Skins +== スキン + +Sound +== サウンド + +Sound error +== サウンドエラー + +Spectate +== 観戦 + +Spectate next +== 次の人を見る + +Spectate previous +== 前の人を見る + +Spectator mode +== 観戦モード + +Spectators +== 観戦者 + +Standard gametype +== 標準のゲームタイプ + +Standard map +== 標準のマップ + +Stop record +== 録画停止 + +Strict gametype filter +== ゲームタイプ完全一致検索 + +Sudden Death +== サドンデス + +Switch weapon on pickup +== 拾った武器に持ちかえる + +Team +== チーム + +Team chat +== チームチャット + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s が公開されました!ダウンロードはこちら!www.teeworlds.com + +Texture Compression +== テクスチャ圧縮 + +The audio device couldn't be initialised. +== オーディオデバイスの初期化に失敗しました。 + +The server is running a non-standard tuning on a pure game type. +== サーバーがMODを導入しているようです。 + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== 保存されていないデータがありますが、よろしいですか。 + +Time limit +== 残り時間 + +Time limit: %d min +== 残り %d 分 + +Try again +== もう一度 + +Type +== タイプ + +Unable to delete the demo +== デモを削除できません。 + +Unable to rename the demo +== デモの名前を変更できません。 + +Use sounds +== 音を鳴らす + +Use team colors for name plates +== 名前表示にチームカラーを使う + +V-Sync +== V-Sync + +Version +== バージョン + +Vote command: +== 投票コマンド: + +Vote description: +== 投票の説明: + +Vote no +== 反対 + +Vote yes +== 賛成 + +Voting +== 投票 + +Warmup +== 準備中 + +Weapon +== 武器 + +Welcome to Teeworlds +== Teeworldsの世界へようこそ! + +Yes +== はい + +You must restart the game for all settings to take effect. +== 設定を反映するためにはゲームの再起動が必要です。 + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== 新しい名前: + +Max demos +== デモの最大数 + +Internet +== オンライン + +Round +== ラウンド + +Quit anyway? +== 終了しますか? + +News +== ニュース + +Join game +== ゲームに参加 + +Crc: +== Crc: + +FSAA samples +== FSAAサンプル数 + +Sat. +== 彩度 + +LAN +== LAN + +Sound volume +== 音量 + +Created: +== 作成: + +%d Bytes +== %d バイト + +Record demo +== デモを録画 + +%d of %d servers, %d players +== %d of %d サーバー, %d プレイヤー + +Miscellaneous +== その他 + +Netversion: +== Netversion: + +Info +== 情報 + +UI Color +== UIカラー + +Max Screenshots +== スクリーンショットの最大数 + +Size: +== サイズ: + +Hue +== 色相 + +Your skin +== あなたのスキン + +Reset to defaults +== 初期設定に戻す + +Rifle +== ライフル + +Display Modes +== 画面モード + +Version: +== バージョン: + +Map: +== マップ: + +Lht. +== 明るさ + +Quick search: +== ワード検索: + +Host address +== IPアドレス:ポート + +Alpha +== 透明度 + +Length: +== 長さ: + +no limit +== 無制限 + +Current version: %s +== クライアントのバージョン: %s + +Name plates size +== 名前表示の大きさ + +Type: +== タイプ: + diff --git a/data/languages/korean.txt b/data/languages/korean.txt new file mode 100644 index 0000000000..e7156578d3 --- /dev/null +++ b/data/languages/korean.txt @@ -0,0 +1,1006 @@ +##### authors ##### +#originally created by: +# ksgcln72 +#modified by: +# +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% 로딩... + +%ds left +== %d 초 후 취소 + +%i minute left +== %i 분 남음 + +%i minutes left +== %i 분 남음 + +%i second left +== %i 초 남음 + +%i seconds left +== %i 초 남음 + +%s wins! +== %s (이)가 이김! + +-Page %d- +== -Page %d- + +Abort +== 중단 + +Add +== 추가 + +Add Friend +== 친구 추가 + +Address +== 주소 + +All +== 모두 + +Always show name plates +== 항상 이름표시 + +Are you sure that you want to delete the demo? +== 데모를 삭제하시겠습니까? + +Are you sure that you want to quit? +== 종료하시겠습니까? + +Are you sure that you want to remove the player from your friends list? +== 당신의 친구목록에서 플레이어를 제거하시겠습니까? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== 다음 빈칸에 게임에서 사용할 닉네임을 입력해 주세요. + +Automatically record demos +== 자동으로 데모 기록 + +Automatically take game over screenshot +== 자동으로 게임 종료 스크린샷 저장 + +Blue team +== 블루팀 + +Blue team wins! +== 블루팀 승리! + +Body +== 몸 + +Call vote +== 투표 + +Change settings +== 세팅 바꿈 + +Chat +== 채팅 + +Clan +== 클랜 + +Client +== 클라이언트 + +Close +== 닫기 + +Compatible version +== 버전 호환 + +Connect +== 연결 + +Connecting to +== 연결 중 + +Connection Problems... +== 연결에 문제가 있음... + +Console +== 콘솔 + +Controls +== 조작 + +Count players only +== Count players only + +Country +== 국가 + +Current +== 현재 + +Custom colors +== 사용자 정의 색상 + +Delete +== 삭제 + +Delete demo +== 데모 삭제 + +Demo details +== 데모 세부정보 + +Demos +== 데모 + +Disconnect +== 연결끊기 + +Disconnected +== 연결 끊김 + +Downloading map +== 맵 다운로드 중 + +Draw! +== 무승부! + +Dynamic Camera +== 다이내믹 카메라 + +Emoticon +== 이모티콘 + +Enter +== 입력 + +Error +== 에러 + +Error loading demo +== 데모 로딩 에러 + +Favorite +== 즐겨찾기 + +Favorites +== 즐겨찾기 + +Feet +== 발 + +Filter +== 필터 + +Fire +== 발사 + +Folder +== 폴더 + +Force vote +== 강제 투표 + +Free-View +== 자유 시점 + +Friends +== 친구 + +Fullscreen +== 전체화면 + +Game +== 게임 + +Game info +== 게임 정보 + +Game over +== 게임 오버 + +Game type +== 게임 종류 + +Game types: +== 게임 종류: + +General +== 일반 + +Graphics +== 그래픽 + +Grenade +== 유탄 발사기 + +Hammer +== 해머 + +Has people playing +== 적어도 한 플레이어 + +High Detail +== 자세한 내용 + +Hook +== 갈고리 + +Invalid Demo +== 잘못된 데모 + +Join blue +== 블루팀에 참가 + +Join red +== 레드팀에 참가 + +Jump +== 점프 + +Kick player +== 플레이어 추방 + +Language +== 언어 + +Loading +== 로딩 + +MOTD +== MOTD + +Map +== 맵 + +Maximum ping: +== 최대 핑 + +Mouse sens. +== 미우스 감도 + +Move left +== 왼쪽으로 이동 + +Move player to spectators +== 관전자로 플레이어 이동 + +Move right +== 오른쪽으로 이동 + +Movement +== 이동 + +Mute when not active +== 음소거 때 비활성 + +Name +== 이름 + +Next weapon +== 다음 무기 + +Nickname +== 닉네임 + +No +== 아니오 + +No password +== 비밀번호 없음 + +No servers found +== 서버를 찾을 수 없음 + +No servers match your filter criteria +== 필터에 일치하는 서버가 없음 + +Ok +== 확인 + +Open +== 열기 + +Parent Folder +== 상위 폴더 + +Password +== 비밀번호 + +Password incorrect +== 비밀번호 오류 + +Ping +== 핑 + +Pistol +== 권총 + +Play +== 플레이 + +Player +== 플레이어 + +Player options +== 플레이어 옵션 + +Players +== 유저수 + +Please balance teams! +== 팀 밸런스! + +Prev. weapon +== 이전 무기 + +Quality Textures +== 텍스처 품질 + +Quit +== 종료 + +REC %3d:%02d +== 녹화 %3d:%02d + +Reason: +== 이유: + +Red team +== 레드팀 + +Red team wins! +== 레드팀 승리! + +Refresh +== 새로고침 + +Refreshing master servers +== 마스터 서버를 새로 고치는 중 + +Remote console +== 원격 콘솔 + +Remove +== 삭제 + +Remove friend +== 친구 삭제 + +Rename +== 이름 변경 + +Rename demo +== 데모 이름 변경 + +Reset filter +== 필터 초기화 + +Sample rate +== 샘플링 속도 + +Score +== 점수 + +Score board +== 점수판 + +Score limit +== 점수 제한 + +Scoreboard +== 점수판 + +Screenshot +== 스크린샷 + +Server address: +== 서버 주소: + +Server details +== 서버 세부정보 + +Server filter +== 서버 필터 + +Server info +== 서버 정보 + +Server not full +== 빈 방 + +Settings +== 세팅 + +Shotgun +== 샷건 + +Show chat +== 채팅 보기 + +Show friends only +== 친구가 있는 방 + +Show ingame HUD +== 게임 인터페이스 표시 + +Show name plates +== 이름 표시 + +Show only supported +== 지원하는 해상도 표시 + +Skins +== 스킨 + +Sound +== 소리 + +Sound error +== 소리 오류 + +Spectate +== 관전 + +Spectator mode +== 관전 모드 + +Spectators +== 관전자 + +Standard gametype +== 기본 게임 종류 + +Standard map +== 기본 맵 + +Stop record +== 기록 중지 + +Strict gametype filter +== 엄격한 모드 필터 + +Sudden Death +== 서든 데스 + +Switch weapon on pickup +== 획득한 무기로 교체 + +Team +== 팀 + +Team chat +== 팀 채팅 + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s is out! Download it at www.teeworlds.com! + +Texture Compression +== 텍스처 압축 + +The audio device couldn't be initialised. +== 오디오 장치를 초기화할 수 없음 + +The server is running a non-standard tuning on a pure game type. +== 이 서버는 게임의 표준 형식에 비표준 설정을 사용합니다. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== 맵이 저장되지 않았습니다. 종료하기 전에 저장할 수 있습니다. + +Time limit +== 시간 제한 + +Time limit: %d min +== 시간 제한: %d min + +Try again +== 재시도 + +Type +== 종류 + +Unable to delete the demo +== 데모를 삭제할 수 없음 + +Unable to rename the demo +== 데모의 이름을 바꿀 수 없음 + +Use sounds +== 소리 사용 + +Use team colors for name plates +== 이름에 팀 컬러 사용 + +V-Sync +== V-Sync + +Version +== 버전 + +Vote command: +== 투표 명령: + +Vote description: +== 투표 설명: + +Vote no +== 아니오 + +Vote yes +== 예 + +Voting +== 투표 중 + +Warmup +== 준비 + +Weapon +== 무기 + +Welcome to Teeworlds +== Teeworlds 에 오신 것을 환영합니다. + +Yes +== 예 + +You must restart the game for all settings to take effect. +== 설정을 적용하기 위해선 게임을 다시 시작해야 합니다. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Demofile: %s +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Play background music +== + +Player country: +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectate next +== + +Spectate previous +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== 새 이름: + +Max demos +== 최대 데모 + +Internet +== 인터넷 + +Round +== 라운드 + +Quit anyway? +== 종료하시겠습니까? + +News +== 뉴스 + +Join game +== 게임 참가 + +Crc: +== Crc: + +FSAA samples +== FSAA 샘플 + +Sat. +== 채도 + +LAN +== 랜 + +Sound volume +== 소리 크기 + +Created: +== Created: + +%d Bytes +== %d Bytes + +Record demo +== 데모 녹화 + +%d of %d servers, %d players +== %d/%d 서버, %d 플레이어 + +Miscellaneous +== 여러가지 + +Netversion: +== 넷 버전: + +Info +== 정보 + +UI Color +== 메뉴 색깔 + +Your skin +== 당신의 스킨 + +Max Screenshots +== 최대 스크린샷 + +Size: +== 사이즈: + +Hue +== 색조 + +Reset to defaults +== 초기화 + +Rifle +== 레이저 + +Display Modes +== 디스플레이 모드 + +Version: +== 버전: + +Map: +== 맵: + +Lht. +== 명도 + +Quick search: +== 빠른 검색: + +Host address +== 호스트 주소 + +Alpha +== 알파 + +Length: +== 길이: + +no limit +== 제한 없음 + +Current version: %s +== 현재 버전: %s + +Name plates size +== 크기 + +Type: +== 종류: + diff --git a/data/languages/kyrgyz.txt b/data/languages/kyrgyz.txt new file mode 100644 index 0000000000..c65e376be2 --- /dev/null +++ b/data/languages/kyrgyz.txt @@ -0,0 +1,686 @@ +##### translated strings ##### + +%d Bytes +== %d байт + +%d of %d servers, %d players +== %d / %d сервер, %d оюнчу + +%d%% loaded +== %d%% жүктөлдү + +%ds left +== %d сек. калды + +%i minute left +== %i минута калды! + +%i minutes left +== %i минута калды! + +%i second left +== %i секунда калды! + +%i seconds left +== %i секунда калды! + +%s wins! +== %s утту! + +-Page %d- +== -Барак %d- + +Abort +== Жокко чыгаруу + +Add +== Кошуу + +Add Friend +== Досту кошуу + +Address +== Дареги + +All +== Баары + +Alpha +== Тунук. + +Always show name plates +== Ат көрнөкчөлөрдү дайыма көрсөтүү + +Are you sure that you want to delete the demo? +== Сиз чын эле демонун өчүрүлүшүн каалайсызбы? + +Are you sure that you want to quit? +== Сиз чын эле оюндан чыгууну каалайсызбы? + +Are you sure that you want to remove the player from your friends list? +== Сиз чын эле бул оюнчуну достор тизмеңизден өчүргүңүз келеби? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Бул оюндун биринчи жүргүзүлүшү болгону үчүн, төмөн жакка такма атыңызды киргизиңиз. Серверге туташуу алдында ырастоолорду текшериңиз. + +Automatically record demos +== Демону автоматтуу түрдө жаздыруу + +Automatically take game over screenshot +== Оюн натыйжаларын сүрөткө тартуу + +Blue team +== Көктөр + +Blue team wins! +== Көктөр утту! + +Body +== Дене + +Call vote +== Добуш берүү + +Change settings +== Ырастоолорду өзгөртүү + +Chat +== Маек + +Clan +== Кланы + +Client +== Клиент + +Close +== Чыгуу + +Compatible version +== Батышуучу версия + +Connect +== Туташуу + +Connecting to +== Туташтырылууда + +Connection Problems... +== Байланыш көйгөйлөрү... + +Console +== Консоль + +Controls +== Башкаруу + +Count players only +== Оюнчуларды гана саноо + +Country +== Өлкө + +Crc: +== Crc: + +Created: +== Жаратылганы: + +Current +== Кезектегиси + +Current version: %s +== Кезектеги версиясы: %s + +Custom colors +== Өз түстөрүңүз + +Delete +== Өчүрүү + +Delete demo +== Демону өчүрүү + +Demo details +== Демо деталдары + +Demofile: %s +== Демофайлы: %s + +Demos +== Демолор + +Disconnect +== Өчүрүү + +Disconnected +== Өчүрүлдү + +Display Modes +== Көрсөтүү режимдери + +Downloading map +== Карта жүктөөлүүдө + +Draw! +== Тең! + +Dynamic Camera +== Динамикалык камера + +Emoticon +== Эмоциялар + +Enter +== Кирүү + +Error +== Ката + +Error loading demo +== Демону жүктөө учурундагы ката + +FSAA samples +== FSAA сэмплдери + +Favorite +== Тандалма + +Favorites +== Тандалмалар + +Feet +== Бут + +Filter +== Фильтр + +Fire +== Атуу + +Folder +== Папка + +Force vote +== Тездетүү + +Free-View +== Эркин сереп + +Friends +== Достор + +Fullscreen +== Толук экран + +Game +== Оюн + +Game info +== Оюн жөнүндө + +Game over +== Оюн бүттү + +Game type +== Оюн түрү + +Game types: +== Оюн түрү: + +General +== Негизги + +Graphics +== Графика + +Grenade +== Гранатомёт + +Hammer +== Барскан + +Has people playing +== Бош эмес сервер + +High Detail +== Жогорку деталдаштыруу + +Hook +== Илмек + +Host address +== Сервер дареги + +Hue +== Түсү + +Info +== Маалымат + +Internet +== Интернет + +Invalid Demo +== Жарабаган демо + +Join blue +== Көктөргө + +Join game +== Ойноо + +Join red +== Кызылдарга + +Jump +== Секирүү + +Kick player +== Оюнчуну чыгаруу + +LAN +== LAN + +Language +== Тил + +Length: +== Узундугу: + +Lht. +== Ач. түс. + +Loading +== Жүктөлүүдө + +MOTD +== Күндүн билдирүүсү + +Map +== Картасы + +Map: +== Картасы: + +Max Screenshots +== Сүрөттөрдүн жогорку чеги + +Max demos +== Демолордун жогорку чеги + +Maximum ping: +== Пингдин ж. чеги: + +Miscellaneous +== Кошумча + +Mouse sens. +== Чычкан сезгич. + +Move left +== Солго басуу + +Move player to spectators +== Оюнчуну байкоочуларга ташуу + +Move right +== Оңго басуу + +Movement +== Аракет + +Mute when not active +== Активдүү эмес кезде үндү өчүрүү + +Name +== Аты + +Name plates size +== Ат көрнөкчөлөрдүн өлчөмү + +Netversion: +== Версиясы: + +New name: +== Жаңы аты: + +News +== Жаңылыктар + +Next weapon +== Кийин. курал + +Nickname +== Такма атыңыз + +No +== Жок + +No password +== Сырсөзсүз + +No servers found +== Серверлер табылган жок + +No servers match your filter criteria +== Сиздин фильтриңизге жарай турган серверлер жок + +Ok +== ОК + +Open +== Ачуу + +Parent Folder +== Ата-энелик каталог + +Password +== Сырсөзү + +Password incorrect +== Сырсөз + +Ping +== Пинги + +Pistol +== Тапанча + +Play +== Көрүү + +Play background music +== Фон музыкасын ойнотуу + +Player +== Оюнчу + +Player country: +== Өлкөсү: + +Player options +== Оюнчу опциялары + +Players +== Оюнчулар + +Please balance teams! +== Команадаларды баланстаңыз! + +Prev. weapon +== Мурун. курал + +Quality Textures +== Сапаттуу текстуралар + +Quick search: +== Тез издөө: + +Quit +== Чыгуу + +Quit anyway? +== Чыгуу? + +REC %3d:%02d +== ЖАЗДЫРУУ %3d:%02d + +Reason: +== Себеби: + +Record demo +== Демо жаздыруу + +Red team +== Кызылдар + +Red team wins! +== Кызылдар утту! + +Refresh +== Жаңылоо + +Refreshing master servers +== Мастер-серверлер тизмесин жаңылоо + +Remote console +== Алыскы консоль + +Remove +== Өчүрүү + +Remove friend +== Досту өчүрүү + +Rename +== Атын өзгөртүү + +Rename demo +== Демо атын өзгөртүү + +Reset filter +== Фильтрлерди түшүрүү + +Reset to defaults +== Жарыяланбаска түшүрүү + +Rifle +== Бластер + +Round +== Раунду + +Sample rate +== Жыштыгы + +Sat. +== Канык. + +Score +== Упайы + +Score board +== Табло + +Score limit +== Упай чеги + +Scoreboard +== Табло + +Screenshot +== Сүрөт + +Server address: +== Сервер дареги: + +Server details +== Сервер деталдары + +Server filter +== Сервер фильтри + +Server info +== Маалымат + +Server not full +== Сервер толук эмес + +Settings +== Ырастоолор + +Shotgun +== Мылтык + +Show chat +== Маекти көрсөтүү + +Show friends only +== Достор менен гана + +Show ingame HUD +== Оюн ичиндеги HUD'ни көрсөтүү + +Show name plates +== Оюнчулардын аттарын көрсөтүү + +Show only supported +== Колдолгонду гана көрсөтүү + +Size: +== Өлчөмү: + +Skins +== Терилер + +Sound +== Үн + +Sound error +== Үн катасы + +Sound volume +== Үн көлөмү + +Spectate +== Байкоо + +Spectate next +== Кийин. байкоо + +Spectate previous +== Мурун. байкоо + +Spectator mode +== Байкоочу + +Spectators +== Байкоочулар + +Standard gametype +== Стандарттуу оюн түрү + +Standard map +== Стандарттуу карта + +Stop record +== Токтотуу + +Strict gametype filter +== Оюн түрүнүн так фильтри + +Sudden Death +== Тез өлүм + +Switch weapon on pickup +== Көтөрүлгөн куралга которуу + +Team +== Команда + +Team chat +== Команда маеги + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s чыкты! www.teeworlds.com сайтынан жүктөп алыңыз! + +Texture Compression +== Текстура кысылышы + +The audio device couldn't be initialised. +== Аудио түзмөгүн инициализациялап алууга мүмкүн эмес. + +The server is running a non-standard tuning on a pure game type. +== Бул сервер стандартту эмес ырастоолор менен таза оюн түрүндө иштеп жатат. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Редактордо сакталбаган карта бар, аны оюндан чыгаар алдында сактасаңыз болот. + +Time limit +== Убакыт чеги + +Time limit: %d min +== Убакыт чеги: %d мин. + +Try again +== ОК + +Type +== Түрү + +Type: +== Түрү: + +UI Color +== Интерфейс түсү + +Unable to delete the demo +== Демону өчүрүү мүмкүн эмес + +Unable to rename the demo +== Демо атын өзгөртүү мүмкүн эмес + +Use sounds +== Үндөрдү колдонуу + +Use team colors for name plates +== Аттар үчүн команданын түсүн колдонуу + +V-Sync +== Тик синхрондоштуруусу + +Version +== Версиясы + +Version: +== Версиясы: + +Vote command: +== Добуш коммандасы: + +Vote description: +== Добуш баяндамасы: + +Vote no +== Каршы + +Vote yes +== Макул + +Voting +== Добуш берүү + +Warmup +== Даярдануу + +Weapon +== Курал + +Welcome to Teeworlds +== Teeworlds'ко кош келиңиз! + +Yes +== Ооба + +You must restart the game for all settings to take effect. +== Өзгөртүүлөрдү колдонуу үчүн оюнду кайта жүргүзүңүз. + +Your skin +== Териңиз + +no limit +== чексиз + +Game paused +== Оюн бир азга токтотулду + +Respawn +== Кайра жаралуу + +Show only chat messages from friends +== Достордун гана маек билдирүүлөрүн көрсөтүү + +##### needs translation ##### + +##### old translations ##### diff --git a/data/languages/license.txt b/data/languages/license.txt new file mode 100644 index 0000000000..cf468677f1 --- /dev/null +++ b/data/languages/license.txt @@ -0,0 +1,2 @@ +All content is released under CC-BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0/). +Information about the authors can be found within the according file. diff --git a/data/languages/norwegian.txt b/data/languages/norwegian.txt new file mode 100644 index 0000000000..b92ad51309 --- /dev/null +++ b/data/languages/norwegian.txt @@ -0,0 +1,1018 @@ +##### authors ##### +#originally created by: +# MertenNor +#modified by: +# MertenNor 2011-07-02 08:46:16 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% lastet + +%ds left +== %d sekunder igjen + +%i minute left +== %i minutt igjen + +%i minutes left +== %i minutter igjen + +%i second left +== %i sekund igjen + +%i seconds left +== %i sekunder igjen + +%s wins! +== %s Vinner! + +-Page %d- +== -Side %d- + +Abort +== Avbryt + +Add +== Legg til + +Add Friend +== Legg til venn + +Address +== Adresse + +All +== Alle + +Always show name plates +== Vis alltid navnskilt + +Are you sure that you want to delete the demo? +== Er du sikker på at du vil slette denne demoen? + +Are you sure that you want to quit? +== Er du sikker på at du vil avslutte? + +Are you sure that you want to remove the player from your friends list? +== Er du sikker på at du vil fjerne denne spilleren fra vennelisten? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Siden dette er første gang du starter spillet, må du skrive inn et kallenavn nedenfor. Det anbefales at du sjekker innstillingene for å justere dem slik du ønsker før du kobler til en server. + +Automatically record demos +== Spill inn demoer automatisk + +Automatically take game over screenshot +== Ta skjermbilder når runden er over + +Blue team +== Blått lag + +Blue team wins! +== Blått lag vant! + +Body +== Kropp + +Call vote +== Stem + +Change settings +== Endre innstillinger + +Chat +== Chat + +Clan +== Klan + +Client +== Klient + +Close +== Lukk + +Compatible version +== Kompitabel versjon + +Connect +== Koble til + +Connecting to +== Kobler til + +Connection Problems... +== Tilkoblingsproblemer... + +Console +== Konsoll + +Controls +== Kontroller + +Count players only +== Tell bare spillere + +Country +== Land + +Current +== Nåværende + +Custom colors +== Egendefinerte farger + +Delete +== Slett + +Delete demo +== Slett demo + +Demo details +== Demo detaljer + +Demofile: %s +== Demo fil: %s + +Demos +== Demoer + +Disconnect +== Koble fra + +Disconnected +== Frakoblet + +Downloading map +== Laster ned banen + +Draw! +== Uavgjort! + +Dynamic Camera +== Dynamisk kamera + +Emoticon +== Uttrykksikoner + +Enter +== Enter + +Error +== Feil + +Error loading demo +== Kunne ikke laste in Demo`en + +Favorite +== Favoritt + +Favorites +== Favoritter + +Feet +== Føtter + +Filter +== Filter + +Fire +== Skyt + +Folder +== Mappe + +Force vote +== Tvungen valg + +Free-View +== fri-visning + +Friends +== Venner + +Fullscreen +== Fullskjerm + +Game +== Spill + +Game info +== Spill info + +Game over +== Spill avsluttet + +Game type +== Spilltype + +Game types: +== Spilletyper: + +General +== Generelt + +Graphics +== Grafikk + +Grenade +== Granat + +Hammer +== Hammer + +Has people playing +== Har folk som spiller + +High Detail +== Ekstra detaljer + +Hook +== Gripe tang + +Invalid Demo +== Ugyldig Demo + +Join blue +== Bli med blå + +Join red +== Bli med rød + +Jump +== Hopp + +Kick player +== Kast ut spiller + +Language +== Språk + +Loading +== Laster inn + +MOTD +== Dagens melding + +Map +== Bane + +Maximum ping: +== Maks ping: + +Mouse sens. +== Mushastighet + +Move left +== Gå til venstre + +Move player to spectators +== Gjør spiller til tilskuer + +Move right +== Gå til høyre + +Movement +== Beveglighet + +Mute when not active +== Slå av lyd når spillet ikke er aktivt + +Name +== Navn + +Next weapon +== Neste Våpen + +Nickname +== Kallenavn + +No +== Nei + +No password +== ikke passord + +No servers found +== Ingen servere funnet + +No servers match your filter criteria +== Ingen servere tilsvarer dine filter kriterier + +Ok +== Ok + +Open +== Åpne + +Parent Folder +== Forrige mappe + +Password +== Passord + +Password incorrect +== Feil passord + +Ping +== Ping + +Pistol +== Pistol + +Play +== Spill + +Play background music +== Spill bakrunds musikk + +Player +== Spiller + +Player country: +== spiller landet + +Player options +== Spillerinstillinger + +Players +== Spillere + +Please balance teams! +== Balanser lagene! + +Prev. weapon +== Forrige våpen + +Quality Textures +== Teksturkvalitet + +Quit +== Avslutt + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Grunn: + +Red team +== Rødt lag + +Red team wins! +== Rødt lag vant! + +Refresh +== Oppdater + +Refreshing master servers +== Opdaterer master servere + +Remote console +== Server konsoll + +Remove +== Ta bort + +Remove friend +== Ta bort venn + +Rename +== Gi nytt navn + +Rename demo +== Gi nytt navn på Demo + +Reset filter +== Tilbakestill filteret + +Sample rate +== Enkel frekvens + +Score +== Poeng + +Score board +== Poengliste + +Score limit +== Poenggrense + +Scoreboard +== Poengliste + +Screenshot +== Skjermbilde + +Server address: +== Serveradresse + +Server details +== Serverdetaljer + +Server filter +== Serverfilter + +Server info +== Serverinfo + +Server not full +== Serveren er ikke full + +Settings +== Instillinger + +Shotgun +== Hagle + +Show chat +== Vis samtale + +Show friends only +== Vis venner + +Show ingame HUD +== Vis HUD i spillet + +Show name plates +== Vis navnskilt + +Show only supported +== Vis kompatible + +Skins +== Utseender + +Sound +== Lyd + +Sound error +== Lydfeil + +Spectate +== Se på + +Spectate next +== Se på neste + +Spectate previous +== Se på forige + +Spectator mode +== Tilhenger modus + +Spectators +== Tilskuere: + +Standard gametype +== Standard spilletyper + +Standard map +== Standard bane + +Stop record +== Stopp inspilling + +Strict gametype filter +== spilltype-filter + +Sudden Death +== Brå død + +Switch weapon on pickup +== Bytt våpen ved innsamling + +Team +== Lag + +Team chat +== Lagsamtale + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s er ute! Last ned på www.teeworlds.com! + +Texture Compression +== Teksturkompressjon + +The audio device couldn't be initialised. +== Lydenheten kunne ikke initialiseres. + +The server is running a non-standard tuning on a pure game type. +== Denne serveren kjører ikke standard-innstillinger på standard spilltype. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Det er en ulagret bane i baneredigereren, du vil kanskje lagre den før du avslutter. + +Time limit +== Tidsbegrensning + +Time limit: %d min +== Tidsgrense: %d min + +Try again +== Prøv igjen + +Type +== Type + +Unable to delete the demo +== Kunne ikke slette demoen + +Unable to rename the demo +== Kunne ikke endre navn på demoen + +Use sounds +== Lyd på + +Use team colors for name plates +== Bruk lagfarger for navnskilt + +V-Sync +== V-Sync + +Version +== Versjon + +Vote command: +== Stemmingskommando: + +Vote description: +== Stemmebeskrivelse: + +Vote no +== Stem Nei + +Vote yes +== Stem Ja + +Voting +== Stemming + +Warmup +== Oppvarming + +Weapon +== Våpen + +Welcome to Teeworlds +== Velkommen til Teeworlds + +Yes +== Ja + +You must restart the game for all settings to take effect. +== Du må starte spillet på nytt før alle endringene tar effekt. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Nytt navn: + +Sat. +== Metning + +Miscellaneous +== Diverse + +Internet +== Internett + +Max demos +== Maks antall demoer + +News +== Nyheter + +Join game +== Bli med i spillet + +FSAA samples +== FSAA prøver + +%d of %d servers, %d players +== %d av %d servere, %d spillere + +Sound volume +== Lydvolum + +Created: +== Opprettet: + +Max Screenshots +== Maks antall skjermbilder + +Length: +== Lengde + +Skin name +== + +Rifle +== Laser + +Patterns +== + +Netversion: +== Net versjon: + +Map: +== Bane: + +%d Bytes +== %d Bytes + +Coloration +== + +Info +== Info + +Hue +== Farge + +Record demo +== Ta opp demo + +Your skin +== Ditt utseende + +Size: +== Størrelse: + +Reset to defaults +== Tilbakestill standardinstillinger + +Quit anyway? +== Avslutt uansett? + +Display Modes +== Skjerm moduser + +Version: +== Versjon: + +Round +== Runde + +Lht. +== Lysstyrke + +no limit +== Ingen begrensninger + +Quick search: +== Raskt Søk: + +UI Color +== Menyfarge + +Host address +== Serveradresse + +Crc: +== Crc: + +Alpha +== Synlighet + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Nåværende versjon: %s + +LAN +== LAN + +Name plates size +== Størrelse på navnskilt + +Type: +== Type: + diff --git a/data/languages/polish.txt b/data/languages/polish.txt new file mode 100644 index 0000000000..649aabcc71 --- /dev/null +++ b/data/languages/polish.txt @@ -0,0 +1,1024 @@ +##### authors ##### +#originally created by: +# Łukasz D +#modified by: +# Martin Pola 2011-04-02 22:18:00 +# Tsin 2011-04-06 23:15:58 +# Shymwo 2011-07-15 01:27:45 +# Shymwo 2011-07-21 18:54:53 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== Załadowano %d%% + +%ds left +== Pozostało %ds + +%i minute left +== Pozostało minut: %i + +%i minutes left +== Pozostało minut: %i + +%i second left +== Pozostało sekund: %i + +%i seconds left +== Pozostało sekund: %i + +%s wins! +== Wygrał %d! + +-Page %d- +== -Strona %d- + +Abort +== Anuluj + +Add +== Dodaj + +Add Friend +== Dodaj znajomego + +Address +== Adres + +All +== Wszyscy + +Always show name plates +== Zawsze pokazuj nicki graczy + +Are you sure that you want to delete the demo? +== Czy na pewno chcesz usunąć to demo? + +Are you sure that you want to quit? +== Czy na pewno chcesz opuścić grę? + +Are you sure that you want to remove the player from your friends list? +== Czy na pewno chcesz usunąć tego gracza z listy znajomych? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== To pierwsze uruchomienie gry, podaj swój nick poniżej. Zalecane jest też sprawdzenie ustawień i dopasowanie ich do swoich upodobań przed dołączeniem do gry. + +Automatically record demos +== Automatycznie rejestruj dema + +Automatically take game over screenshot +== Automatycznie zrób zrzut ekranu końca gry + +Blue team +== Niebiescy + +Blue team wins! +== Niebiescy wygrali! + +Body +== Ciało + +Call vote +== Głosowanie + +Change settings +== Zmień ustawienia + +Chat +== Chat + +Clan +== Klan + +Client +== Klient + +Close +== Zamknij + +Compatible version +== Kompatybilna wersja + +Connect +== Połącz + +Connecting to +== Łączenie z + +Connection Problems... +== Problemy z połączeniem... + +Console +== Konsola + +Controls +== Sterowanie + +Count players only +== Licz tylko graczy + +Country +== Kraj + +Current +== Aktualnie + +Custom colors +== Dostosuj kolory + +Delete +== Usuń + +Delete demo +== Usuń demo + +Demo details +== Szczegóły dema + +Demofile: %s +== Plik dema: %s + +Demos +== Dema + +Disconnect +== Rozłącz + +Disconnected +== Rozłączono + +Downloading map +== Pobieranie mapy + +Draw! +== Remis! + +Dynamic Camera +== Dynamiczna kamera + +Emoticon +== Emotikona + +Enter +== Wejdź + +Error +== Błąd + +Error loading demo +== Błąd przy ładowaniu dema + +Favorite +== Ulubiony + +Favorites +== Ulubione + +Feet +== Stopy + +Filter +== Filtr + +Fire +== Strzał + +Folder +== Katalog + +Force vote +== Wymuś głosowanie + +Free-View +== Wolna kamera + +Friends +== Znajomi + +Fullscreen +== Pełny ekran + +Game +== Gra + +Game info +== Info o grze + +Game over +== Koniec gry + +Game type +== Typ gry + +Game types: +== Typy gier: + +General +== Ogólne + +Graphics +== Grafika + +Grenade +== Granatnik + +Hammer +== Młot + +Has people playing +== Nie pokazuj pustych + +High Detail +== Wysoka jakość + +Hook +== Hak + +Invalid Demo +== Nieprawidłowe demo + +Join blue +== Do niebieskich + +Join red +== Do czerwonych + +Jump +== Skok + +Kick player +== Wyrzuć gracza + +Language +== Język + +Loading +== Ładowanie + +MOTD +== Wiadomość dnia + +Map +== Mapa + +Maximum ping: +== Maksymalny ping: + +Mouse sens. +== Czułość myszy + +Move left +== W lewo + +Move player to spectators +== Przesuń gracza do obserwatorów + +Move right +== W prawo + +Movement +== Ruch + +Mute when not active +== Wycisz, kiedy gra nieaktywna + +Name +== Nazwa + +Next weapon +== Następna broń + +Nickname +== Nick + +No +== Nie + +No password +== Bez hasła + +No servers found +== Nie znaleziono serwerów + +No servers match your filter criteria +== Nie znaleziono serwerów spełniających twoje kryteria + +Ok +== Ok + +Open +== Otwórz + +Parent Folder +== Nadrzędny katalog + +Password +== Hasło + +Password incorrect +== Błędne hasło + +Ping +== Ping + +Pistol +== Pistolet + +Play +== Graj + +Play background music +== Odtwarzaj muzykę w tle + +Player +== Gracz + +Player country: +== Narodowość: + +Player options +== Opcje gracza + +Players +== Gracze + +Please balance teams! +== Proszę wyrównać szanse drużyn! + +Prev. weapon +== Poprzednia broń + +Quality Textures +== Szczegółowe tekstury + +Quit +== Wyjście + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Powód: + +Red team +== Czerwoni + +Red team wins! +== Czerwoni wygrali! + +Refresh +== Odśwież + +Refreshing master servers +== Odświeżanie głównych serwerów + +Remote console +== Zdalna konsola + +Remove +== Usuń + +Remove friend +== Usuń znajomego + +Rename +== Zmień nazwę + +Rename demo +== Zmień nazwę dema + +Reset filter +== Domyślne filtry + +Sample rate +== Częstotliwość próbkowania + +Score +== Wynik + +Score board +== Tablica wyników + +Score limit +== Limit punktów + +Scoreboard +== Tablica wyników + +Screenshot +== Screenshot + +Server address: +== Adres serwera: + +Server details +== Szczegóły serwera + +Server filter +== Filtr serwerów + +Server info +== Info serwera + +Server not full +== Nie pokazuj pełnych + +Settings +== Opcje + +Shotgun +== Shotgun + +Show chat +== Pokaż chat + +Show friends only +== Pokaż tylko znajomych + +Show ingame HUD +== Pokaż wewnętrzny HUD + +Show name plates +== Pokaż nicki + +Show only supported +== Pokaż tylko wspierane + +Skins +== Motywy + +Sound +== Dźwięk + +Sound error +== Błąd dźwięku + +Spectate +== Obserwuj + +Spectate next +== Obserwuj następnego + +Spectate previous +== Obserwuj poprzedniego + +Spectator mode +== Tryb obserwatora + +Spectators +== Obserwatorzy + +Standard gametype +== Standardowy typ gry + +Standard map +== Standardowa mapa + +Stop record +== Zakończ REC + +Strict gametype filter +== Szczegółowy filtr typu gry + +Sudden Death +== Nagła śmierć + +Switch weapon on pickup +== Zmień broń po podniesieniu + +Team +== Drużyna + +Team chat +== Chat drużynowy + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Nowa wersja Teeworlds %s jest dostępna! Do ściągnięcia z www.teeworlds.com! + +Texture Compression +== Kompresja tekstur + +The audio device couldn't be initialised. +== Urządzenie dźwiękowe nie mogło zostać zainicjowane + +The server is running a non-standard tuning on a pure game type. +== Ten serwer korzysta z niestandardowych ustawień. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== W edytorze jest niezapisana mapa! Zapisz ją, jeśli nie chcesz + +Time limit +== Limit czasu + +Time limit: %d min +== Limit czasu: %d min + +Try again +== Ponów próbę + +Type +== Typ + +Unable to delete the demo +== Nie można usunąć dema + +Unable to rename the demo +== Nie można zmienić nazwy dema + +Use sounds +== Włącz dźwięki + +Use team colors for name plates +== Użyj koloru drużyn dla wyświetlania nicków + +V-Sync +== Synchronizacja pionowa (V-Sync) + +Version +== Wersja + +Vote command: +== Polecenie głosowania: + +Vote description: +== Opis głosowania: + +Vote no +== Nie + +Vote yes +== Tak + +Voting +== Głosowanie + +Warmup +== Rozgrzewka + +Weapon +== Bronie + +Welcome to Teeworlds +== Witaj w Teeworlds + +Yes +== Tak + +You must restart the game for all settings to take effect. +== Uruchom ponownie grę, aby użyć nowych ustawieńn + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Nowa nazwa: + +Sat. +== Nasycenie + +Miscellaneous +== Różne + +Internet +== Internet + +Max demos +== Maksymalnie dem + +News +== Wiadomości + +Join game +== Dołącz + +FSAA samples +== Próbkowanie FSAA (Antyaliasing) + +%d of %d servers, %d players +== %d z %d serwerów, %d graczy + +Sound volume +== Głośność + +utracić swojej pracy! +== + +Created: +== Utworzono: + +Max Screenshots +== Maksymalnie screenshotów + +Length: +== Długość: + +Skin name +== + +Rifle +== Laser + +Patterns +== + +Netversion: +== Wersja sieciowa: + +Map: +== Mapa: + +%d Bytes +== %d Bajtów + +Coloration +== + +Info +== Info + +Hue +== Kolor + +Record demo +== Nagraj demo + +Your skin +== Twój wygląd + +Size: +== Rozmiar: + +Reset to defaults +== Przywróć domyślne + +Quit anyway? +== Wyjść mimo wszystko? + +Display Modes +== Tryby wyświetlania + +Version: +== Wersja: + +Round +== Runda + +Lht. +== Jasność + +no limit +== bez limitu + +Quick search: +== Szybkie wyszukiwanie: + +UI Color +== Kolor menu + +Host address +== Adres serwera + +Crc: +== Crc: + +Alpha +== Alfa + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Aktualna wersja: %s + +LAN +== LAN + +Name plates size +== Wielkość wyświetlanych nicków + +Type: +== Typ: + diff --git a/data/languages/portuguese.txt b/data/languages/portuguese.txt new file mode 100644 index 0000000000..972304a20f --- /dev/null +++ b/data/languages/portuguese.txt @@ -0,0 +1,1020 @@ +##### authors ##### +#originally created by: +# HeroiAmarelo +#modified by: +# HeroiAmarelo 2011-07-15 00:35:23 +# HeroiAmarelo 2012-08-01 15:47:40 +# HeroiAmarelo 2012-10-18 23:56:32 +##### /authors ##### + +##### translated strings ##### + +%d player left +== %d jogador saiu + +%d player not ready +== %d jogador não preparado + +%d players left +== %d jogadores sairam + +%d players not ready +== %d jogadores não preparados + +%d servers, %d players +== %d servidores, %d jogadores + +%d%% loaded +== %d%% A Carregar + +%ds left +== faltam %ds + +%i minute left +== falta %i minuto + +%i minutes left +== faltam %i minutos + +%i second left +== falta %i segundo + +%i seconds left +== faltam %i segundos + +%s wins! +== %s ganhou! + +'%s' called for vote to kick '%s' (%s) +== '%s' Criou um kick-vote para kickar: '%s' (%s) + +'%s' called for vote to move '%s' to spectators (%s) +== '%s' Criou um spec-vote para mover o jogador '%s' para spec (%s) + +'%s' called vote to change server option '%s' (%s) +== '%s' Criou um voto para mudar a opção do servidor '%s' (%s) + +'%s' entered and joined the %s +== '%s' Entou e juntou-se ao %s + +'%s' has left the game +== '%s' saiu do jogo + +'%s' has left the game (%s) +== '%s' saiu do jogo (%s) + +'%s' joined the %s +== '%s' entrou no %s + +-Page %d- +== -Página %d- + +Abort +== Cancelar + +Add +== Adicionar + +Add Friend +== Adicionar Amigo + +Address +== Endereço + +Admin forced server option '%s' (%s) +== O Admin forçou a server-option '%s' (%s) + +Admin forced vote no +== O Admin forçou o voto: Sim + +Admin forced vote yes +== O Admin forçou o voto: Não + +Admin moved '%s' to spectator (%s) +== O Admin moveu o jogador '%s' para os specs. (%s) + +All +== Todos + +Alp: +== Alp; + +Always show name plates +== Mostrar nicks + +Anti Aliasing +== Anti Aliasing + +Are you sure that you want to delete the demo? +== Tens a certeza que queres apagar a demo? + +Are you sure that you want to quit? +== Queres mesmo sair? + +Are you sure that you want to remove the player from your friends list? +== Queres mesmo apaga o jogador da tua lista de amigos? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Olá! Pelos vistos é a primeira vez que inicias o jogo, por isso escolhe um Nick Name para ti! Aproveita e vê as configurações antes de entrar num servidor! + +Automatically record demos +== Gravar demos Automáticamente + +Automatically take game over screenshot +== Tirar screenshots no final do turno autom. + +Back +== Voltar + +Basic +== Básico + +Blue team +== Equipa azul + +Blue team wins! +== A Equipa azul ganhou! + +Bodies +== Corpos + +Body +== Corpo + +Borderless window +== Janela S/ Bordas + +Call vote +== Votação + +Change settings +== Mudar configurações + +Chat +== Conversa + +Clan +== Clã + +Client +== Client + +Close +== Fechar + +Compatible version +== Versão compatível + +Connect +== Ligar + +Connecting to +== A ligar ao endereço + +Connection Problems... +== Problemas na ligação! + +Console +== Console + +Controls +== Controlos + +Count players only +== Contar apenas jogadores + +Country +== País + +Crc +== Crc + +Created +== Criado + +Current +== Atualmente + +Custom +== Personalizado + +Custom colors +== Cores personalizadas + +Customize +== Personalizável + +Decoration +== Decoração + +Delete +== Eliminar + +Delete demo +== Eliminar demo + +Demo +== Demo + +Demo details +== Detalhes da demo + +Demofile: %s +== Demo: %s + +Demos +== Demos + +Detail +== Detalhes + +Disconnect +== Desconectar + +Disconnected +== Desconectado + +Downloading map +== A sacar o mapa... + +Draw! +== Empate! + +Dynamic Camera +== Câmera dinâmica + +Editor +== Editor + +Emoticon +== Emotes + +Enter +== Entrar + +Error +== Erro + +Error loading demo +== Erro a carregar a demo + +Eyes +== Olhos + +Favorite +== Favorito + +Favorites +== Favoritos + +Feet +== Pés + +Filter +== Filtro + +Fire +== Disparar + +Folder +== Pasta + +Force vote +== Forçar + +Format +== Formato + +Free-View +== Vista Livre + +Friends +== Amigos + +Fullscreen +== Fullscreen (Ecrã inteiro) + +Game +== Jogo + +Game info +== Info sobre o jogo + +Game over +== Fim de Jogo + +Game paused +== Jogo em Pausa + +Game starts in +== O jogo começa em + +Game type +== Tipo de jogo + +Game types: +== Tipos de jogo: + +General +== Geral + +Graphics +== Gráficos + +Grenade +== Granada + +Hammer +== Martelo + +Hands +== Mãos + +Has people playing +== Há gente a jogar + +High Detail +== Mais detalhes (HD) + +Hook +== Gancho + +Hue: +== Hue: + +Invalid Demo +== Demo inválida + +Join blue +== Juntar - Azuis + +Join red +== J. Vermelhos + +Jump +== Saltar + +Kick player +== Expulsar jogador + +Kick voting requires %d players on the server +== São necessários %d jogadores para fazer um kick vote neste server + +Language +== Língua + +Laser +== Laser + +Length +== Comprimento + +Letterbox +== Letterbox + +Lgt: +== Lgt: + +Loading +== A carregar dados + +Local server +== Servidor local + +MOTD +== MOTD + +Map +== Mapa + +Match +== Combate + +Max +== Maximo + +Maximum ping: +== Ping máximo: + +Misc +== Misc + +Mouse sens. +== Sens. do rato + +Move left +== Esquerda + +Move player to spectators +== Juntar jogador aos spectators + +Move right +== Direita + +Movement +== Movimento + +Mute when not active +== Fazer mute em estado AFK. + +Name +== Nome + +Netversion +== Netversion + +Next weapon +== Arma Seguinte + +Nickname +== Nick + +No +== Não + +No password +== Sem password + +No servers found +== Nenhum servidor encontrado. + +No servers match your filter criteria +== Não há servidores que correspondam às definições de procura. + +Normal: +== Normal: + +Ok +== Aceitar + +Only %d active players are allowed +== Apenas são permitidos %d jogadores ativos + +Open +== Abrir + +Parent Folder +== Pasta superior + +Password +== Password + +Password incorrect +== Password errada! + +Personal +== Pessoal + +Ping +== Ping + +Pistol +== Pistola + +Play +== Ver + +Play background music +== Tocar a música de fundo + +Player +== Jogador + +Player country: +== País do jogador + +Player options +== Opções do jogador + +Players +== Jogadores + +Please balance teams! +== Equilibrem as equipas! + +Prev. weapon +== Arma anterior + +Quality Textures +== Texturas de Qualidade + +Quit +== Sair + +REC %3d:%02d +== REC %3d:%02d + +Ready +== Preparado + +Reason: +== Motivo: + +Recorded +== A gravar + +Red team +== Equipa verm. + +Red team wins! +== Ganhou a equipa vermelha! + +Refresh +== Atualizar + +Refreshing master servers +== A Atualizar servidores + +Remote console +== Remote console + +Remove +== Eliminar + +Remove friend +== Apagar amigo + +Rename +== Renomear + +Rename demo +== Renomear demo + +Reset +== Reset + +Reset filter +== Reiniciar Fitro + +Resolutions +== Resoluções + +Respawn +== Respawn + +Round over +== Fim da ronda + +Round over! +== Fim da ronda! + +Sample rate +== Frequencia de rate + +Sat: +== Sat: + +Save +== Gravar + +Save skin +== Gravar skin + +Score +== Pontos + +Score board +== Tabela de Pontos + +Score limit +== Pontuação Máx. + +Scoreboard +== Pontuação + +Screen +== Screen + +Screenshot +== Screenshot + +Server address: +== Endereço do servidor: + +Server details +== Detalhes do servidor + +Server does not allow voting to kick players +== O servidor não permite kick-votes + +Server does not allow voting to move players to spectators +== O servidor não permite spec-votes + +Server filter +== Filtro de servidores + +Server info +== Info de servidor + +Server not full +== Servidores não cheios + +Servers +== Servidores + +Settings +== Config. + +Shotgun +== Shotgun + +Show chat +== Mostrar chat + +Show friends only +== Mostrar apenas amigos + +Show ingame HUD +== Mostrar HUD do jogo + +Show name plates +== Mostrar nick's + +Show only chat messages from friends +== Mostrar apenas mensagens chat dos amigos + +Show only supported +== Mostrar apenas suportado + +Size +== Tamanho + +Skin +== Skin + +Skins +== Skins + +Sound +== Som + +Sound error +== Erro no som! + +Spectate +== Spectate + +Spectate next +== Observar próximo + +Spectate previous +== Observar anterior + +Spectator mode +== Modo spectator + +Spectators +== Spectators + +Spectators aren't allowed to start a vote. +== Os specs. não podem começar votes. + +Standard Gametype +== Tipo de jogo original + +Standard gametype +== Tipo de jogo normal + +Standard map +== Mapa normal + +Stop record +== Parar de gravar + +Strict gametype filter +== Tipo de jogo especifico + +Sudden Death +== Morte súbita + +Switch weapon on pickup +== Mudar de arma ao agarrar + +Tattoos +== Tatuagens + +Team +== Equipa + +Team chat +== Chat de equipa + +Team: +== Equipa: + +Teams are locked +== As equipas estão bloqueadas + +Teams are locked. Time to wait before changing team: %02d:%02d +== As equipas estão bloqueadas. Tempo de espera para poder mudar de equipa: %02d:%02d + +Teams were locked +== As equipas estavam bloqueadas + +Teams were unlocked +== As equipas estavam desbloqueadas + +Tee +== Tee + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s já saiu! Download em www.teeworlds.com! + +Texture +== Textura + +Texture Compression +== Compressão de Textura + +The audio device couldn't be initialised. +== O dispositivo de som não pode ser iniciado. + +The server is running a non-standard tuning on a pure game type. +== O servidor está a usar um tipo de jogo não oficial com o nome de um oficial. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== O mapa que editaste não foi gravado. Queres gravar antes de sair? + +Time limit +== Tempo máx. + +Time limit: %d min +== Limite de tempo: %d min + +Try again +== Tenta outra vez + +Type +== Tipo + +Unable to delete the demo +== Não podes eliminar a demo + +Unable to rename the demo +== Impossivel renomear a demo + +Use sounds +== Ligar efeitos sonoros + +Use team colors for name plates +== Usar cores dos nicks com a cor da equipa + +V-Sync +== V-Sync + +Version +== Versão + +Volume +== Volume + +Vote aborted +== Vote cancelado + +Vote command: +== Comando: + +Vote description: +== Descrição de votação: + +Vote failed +== Vote falhado + +Vote no +== Votar não + +Vote passed +== Vote aceite + +Vote yes +== Votar sim + +Voting +== A votar + +Wait for current vote to end before calling a new one. +== Espera que este vote acabe para poderes começar um novo + +Wait for next round +== Espera pela próxima ronda + +Warmup +== Preparação + +Weapon +== Arma + +Welcome to Teeworlds +== Bem-vindo ao Teeworlds! + +Wide +== Largura + +Yes +== Sim + +You must restart the game for all settings to take effect. +== Para que as configurações sejam efectuadas deves Reiniciar o jogo. + +You must wait %d seconds before making another vote +== Tens de esperar %d seg. antes de fazer um novo vote. + +blue +== azul + +blue team +== equipa azul + +game +== jogo + +red +== vermelho + +red team +== equipa vermelha + +spectators +== specs + +wait for more players +== Espera por mais jogadores + +##### needs translation ##### + +%.3f KiB +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Color +== + +Global +== + +Host address: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Local +== + +Nickname is empty. +== + +Record +== + +Search: +== + +Spectating +== + +Tattoo +== + +##### old translations ##### + +Internet +== Internet + +Max demos +== Demos maximas + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== Tens a serteza que queres guardar a skin? Se já existir uma como o meu nome, ela será substituida. + +News +== Notícias + +Sat. +== Sat. + +Join game +== Entrar no jogo + +Patterns +== Patterns + +New name: +== Novo nome: + +%d of %d servers, %d players +== %d de %d servidores, %d jogadores + +Sound volume +== Volume + +Created: +== Criado: + +Max Screenshots +== Máx. de Capturas de Tela + +Record demo +== Gravar uma demo + +Length: +== Longitude: + +Skin name +== Nome da skin + +Rifle +== Laser + +Netversion: +== Netversion + +Map: +== Mapa: + +FSAA samples +== Amostras FSAA + +%d Bytes +== %d Bytes + +Coloration +== Coloração + +Info +== Info. + +Miscellaneous +== Diversos + +Lht. +== Luz + +Your skin +== A tua skin + +Size: +== Tamanho: + +Reset to defaults +== Pôr como defeito + +Quit anyway? +== Sair na mesma? + +Display Modes +== Modos de exibição + +Version: +== Versão: + +Round +== Ronda + +no limit +== sem limite + +Quick search: +== Busca rápida: + +UI Color +== Cor do menu + +Host address +== Endereço do Host + +Hue +== Matiz + +Crc: +== Crc: + +Alpha +== Alpha + +Current version: %s +== Versão atual : %s + +LAN +== LAN + +Name plates size +== Caracteres maximos nos nicks + +Type: +== Tipo: + diff --git a/data/languages/readme.txt b/data/languages/readme.txt new file mode 100644 index 0000000000..6469b49239 --- /dev/null +++ b/data/languages/readme.txt @@ -0,0 +1,10 @@ +Copyright (c) 2012 Magnus Auvinen + + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + + +Please visit http://www.teeworlds.com for up-to-date information about +the game, including new versions, custom maps and much more. diff --git a/data/languages/romanian.txt b/data/languages/romanian.txt new file mode 100644 index 0000000000..dad94ac646 --- /dev/null +++ b/data/languages/romanian.txt @@ -0,0 +1,1024 @@ +##### authors ##### +#originally created by: +# kneekoo +#modified by: +# kneekoo 2011-01-18 18:26:02 +# kneekoo 2011-02-09 12:23:47 +# kneekoo 2011-04-03 23:00:38 +# kneekoo 2011-05-25 18:36:32 +# kneekoo 2011-07-01 18:40:49 +# kneekoo 2011-07-05 23:34:34 +# kneekoo 2012-05-01 02:01:47 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% încărcat + +%ds left +== %ds a ieșit + +%i minute left +== %i minute rămas + +%i minutes left +== %i minutes rămase + +%i second left +== %i secundă rămasă + +%i seconds left +== %i secunde rămase + +%s wins! +== %s câștigă! + +-Page %d- +== -Pagina %d- + +Abort +== Anulează + +Add +== Adaugă + +Add Friend +== Adaugă prieten + +Address +== Adresa + +All +== Toți + +Always show name plates +== Afișează întotdeauna pseudonimele + +Are you sure that you want to delete the demo? +== Sigur vrei să ștergi demo-ul? + +Are you sure that you want to quit? +== Sigur vrei să ieși? + +Are you sure that you want to remove the player from your friends list? +== Sigur vrei să ștergi jucătorul din lista ta de prieteni? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Aceasta fiind prima lansare a jocului, te rog introdu-ți pseudonimul mai jos. E recomandat să verifici setările și să le ajustezi cum vrei înainte să intri pe un server. + +Automatically record demos +== Înregistrează automat demo-uri + +Automatically take game over screenshot +== Fă automat o captură de ecran la final + +Blue team +== Echipa albastră + +Blue team wins! +== Echipa albastră a câștigat! + +Body +== Corp + +Call vote +== Votează + +Change settings +== Modifică setările + +Chat +== Chat + +Clan +== Clanul + +Client +== Clientul + +Close +== Închide + +Compatible version +== Versiune compatibilă + +Connect +== Conectează + +Connecting to +== Conectare la + +Connection Problems... +== Probleme la conexiune... + +Console +== Consolă + +Controls +== Controale + +Count players only +== Numără doar jucătorii + +Country +== Țara + +Current +== Curent + +Custom colors +== Culori personalizate + +Delete +== Șterge + +Delete demo +== Șterge demonstrația + +Demo details +== Detalii demo + +Demofile: %s +== Fișier demo: %s + +Demos +== Demo + +Disconnect +== Deconectare + +Disconnected +== Deconectat + +Downloading map +== Se descarcă harta + +Draw! +== Egalitate! + +Dynamic Camera +== Cameră dinamică + +Emoticon +== Figurine + +Enter +== Intră + +Error +== Eroare + +Error loading demo +== Eroare la încărcarea demo-ului + +Favorite +== Favorit + +Favorites +== Favorite + +Feet +== Picioare + +Filter +== Filtre + +Fire +== Foc + +Folder +== Dosar + +Force vote +== Forțează votul + +Free-View +== Vizualizare liberă + +Friends +== Prieteni + +Fullscreen +== Ecrat complet + +Game +== Joc + +Game info +== Informații joc + +Game over +== Final de joc + +Game paused +== Joc în pauză + +Game type +== Tip de joc + +Game types: +== Tipuri de joc: + +General +== General + +Graphics +== Grafică + +Grenade +== Grenade + +Hammer +== Ciocan + +Has people playing +== Are jucători activi + +High Detail +== Detalii înalte + +Hook +== Cârlig + +Invalid Demo +== Demo nevalid + +Join blue +== La albaștri + +Join red +== La roșii + +Jump +== Salt + +Kick player +== Dă afară jucător + +Language +== Limba + +Loading +== Se încarcă + +MOTD +== Mesajul zilei + +Map +== Hartă + +Maximum ping: +== Ping maxim: + +Mouse sens. +== Sensib. maus + +Move left +== Mută la stânga + +Move player to spectators +== Mută jucătorul la spectatori + +Move right +== Mută la dreapta + +Movement +== Mișcare + +Mute when not active +== Opreşte sunetul la inactivate + +Name +== Nume + +Next weapon +== Arma următoare + +Nickname +== Pseudonim + +No +== Nu + +No password +== Fără parolă + +No servers found +== Nici un server găsit + +No servers match your filter criteria +== Nici un server nu corespunde criteriilor + +Ok +== Bine + +Open +== Deschide + +Parent Folder +== Dosarul părinte + +Password +== Parolă + +Password incorrect +== Parolă incorectă + +Ping +== Ping + +Pistol +== Pistol + +Play +== Redă + +Play background music +== Redă muzică în fundal + +Player +== Jucător + +Player country: +== Țara jucătorului: + +Player options +== Opțiuni jucător + +Players +== Jucători + +Please balance teams! +== Echilibrați echipele! + +Prev. weapon +== Arma precedentă + +Quality Textures +== Texturi de înaltă calitate + +Quit +== Ieșire + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Motiv: + +Red team +== Echipa roșie + +Red team wins! +== Echipa roșie a câștigat! + +Refresh +== Reîncarcă + +Refreshing master servers +== Reîncarcă serverele principale + +Remote console +== Consolă server + +Remove +== Șterge + +Remove friend +== Șterge prietenul + +Rename +== Redenumește + +Rename demo +== Redenumește demo-ul + +Reset filter +== Filtru implicit + +Respawn +== Renaşte + +Sample rate +== Frecvența + +Score +== Scor + +Score board +== Scoruri + +Score limit +== Limita de scor + +Scoreboard +== Scoruri + +Screenshot +== Captură de ecran + +Server address: +== Adresă server: + +Server details +== Detalii server + +Server filter +== Filtru servere: + +Server info +== Info. server + +Server not full +== Are locuri libere + +Settings +== Setări + +Shotgun +== Pușcă + +Show chat +== Afișare chat + +Show friends only +== Arată prietenii + +Show ingame HUD +== Arată scorul în joc + +Show name plates +== Arată pseudonimele + +Show only chat messages from friends +== Arată doar chatul prietenilor + +Show only supported +== Arată doar modurile suportate + +Skins +== Costume + +Sound +== Sunet + +Sound error +== Eroare de sunet + +Spectate +== Spectator + +Spectate next +== Vezi următorul + +Spectate previous +== Vezi anteriorul + +Spectator mode +== Mod spectator + +Spectators +== Spectatori + +Standard gametype +== Jocuri standard + +Standard map +== Hărți standard + +Stop record +== Stop înreg. + +Strict gametype filter +== Filtru strict tip joc + +Sudden Death +== Moarte subită + +Switch weapon on pickup +== Schimbă arma la găsire + +Team +== Echipa + +Team chat +== Chat cu echipa + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s este lansat! Descarcă-l de la www.teeworlds.com! + +Texture Compression +== Compresie texturi + +The audio device couldn't be initialised. +== Dispozitivul audio nu a putut fi inițializat. + +The server is running a non-standard tuning on a pure game type. +== Serverul folosește parametri ne-standard într-un joc standard. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Există o hartă nesalvată în editor. Probabil vrei să o salvezi înainte să ieși din joc. + +Time limit +== Timp limită + +Time limit: %d min +== Timp limită : %d min + +Try again +== Reîncearcă + +Type +== Tip + +Unable to delete the demo +== Nu pot șterge demo-ul + +Unable to rename the demo +== Nu pot redenumi demo-ul + +Use sounds +== Folosește sunetul + +Use team colors for name plates +== Folosește culorile echipelor pe etichetele de nume + +V-Sync +== Sincronizare verticală (V-Sync) + +Version +== Versiune + +Vote command: +== Comandă votare: + +Vote description: +== Descriere votare: + +Vote no +== Votează nu + +Vote yes +== Votează da + +Voting +== Votare + +Warmup +== Încălzire + +Weapon +== Arme + +Welcome to Teeworlds +== Bun venit în Teeworlds + +Yes +== Da + +You must restart the game for all settings to take effect. +== Trebuie să repornești jocul pentru aplicarea setărilor. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Nume nou: + +Sat. +== Saturație + +Miscellaneous +== Diverse + +Internet +== Internet + +Max demos +== Număr maxim de demo-uri + +News +== Știri + +Join game +== Intră în joc + +FSAA samples +== Eșantioane FSAA + +%d of %d servers, %d players +== %d/%d servere, %d jucători + +Sound volume +== Volum sunet + +Created: +== Creată la data: + +Max Screenshots +== Număr maxim de capturi de ecran + +Length: +== Durată: + +Skin name +== + +Rifle +== Carabină + +Patterns +== + +Netversion: +== Cod rețea: + +Map: +== Harta: + +%d Bytes +== %d octeți + +Coloration +== + +Info +== Informații + +Hue +== Tentă + +Record demo +== Înreg. demo + +Your skin +== Costumul tău + +Size: +== Dimensiunea: + +Reset to defaults +== Setări implicite + +Quit anyway? +== Ieși oricum? + +Display Modes +== Moduri de afișare + +Version: +== Versiune: + +Round +== Runda + +Lht. +== Luminozitate + +no limit +== fără limită + +Quick search: +== Căutare rapidă: + +UI Color +== Culoare meniu + +Host address +== Adresa gazdei + +Crc: +== Suma de control: + +Alpha +== Alfa + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Versiunea actuală: %s + +LAN +== Rețea + +Name plates size +== Dimensiune nume placă + +Type: +== Tip: + diff --git a/data/languages/russian.txt b/data/languages/russian.txt new file mode 100644 index 0000000000..35f39fba1f --- /dev/null +++ b/data/languages/russian.txt @@ -0,0 +1,1023 @@ +##### authors ##### +#originally created by: +# kaddyd +#modified by: +# carzil 2011-04-05 19:19:06 +# RaZeR[RT] 2011-04-12 17:57:02 +# BotanEgg 2011-05-02 19:07:35 +# Bananbl4 2011-07-07 02:03:04 +# Arion WT 2011-07-07 09:39:05 +# Arion WT 2011-08-07 12:16:28 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% загружено + +%ds left +== осталось %d сек. + +%i minute left +== Осталась %i минута! + +%i minutes left +== Осталось %i минут! + +%i second left +== Осталась %i секунда! + +%i seconds left +== Осталось %i секунд! + +%s wins! +== %s победил! + +-Page %d- +== -Страница %d- + +Abort +== Отмена + +Add +== Добавить + +Add Friend +== Добавить друга + +Address +== Адрес + +All +== Все + +Always show name plates +== Всегда показывать имена игроков + +Are you sure that you want to delete the demo? +== Вы уверены, что хотите удалить демо? + +Are you sure that you want to quit? +== Вы действительно желаете выйти? + +Are you sure that you want to remove the player from your friends list? +== Вы уверены, что хотите удалить игрока из друзей? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Пожалуйста, введите своё имя. Также рекоммендуется проверить настройки игры и поменять некоторые из них перед тем, как начать играть. + +Automatically record demos +== Автоматически записывать демо + +Automatically take game over screenshot +== Делать снимок результатов игры + +Blue team +== Синие + +Blue team wins! +== Синие победили! + +Body +== Тело + +Call vote +== Голосовать + +Change settings +== Изменить настройки + +Chat +== Чат + +Clan +== Клан + +Client +== Клиент + +Close +== Выход + +Compatible version +== Совместимая версия + +Connect +== Подключиться + +Connecting to +== Подключение к + +Connection Problems... +== Проблемы со связью... + +Console +== Консоль + +Controls +== Управление + +Count players only +== Считать только игроков + +Country +== Флаг вашей страны + +Current +== Текущий + +Custom colors +== Свои цвета + +Delete +== Удалить + +Delete demo +== Удалить демо + +Demo details +== Детали демо + +Demofile: %s +== Демо: %s + +Demos +== Демо + +Disconnect +== Отключить + +Disconnected +== Отключено + +Downloading map +== Скачивание карты + +Draw! +== Ничья! + +Dynamic Camera +== Динамическая камера + +Emoticon +== Эмоции + +Enter +== Вход + +Error +== Ошибка + +Error loading demo +== ошибка при загрузке демо + +Favorite +== Избранный + +Favorites +== Избранные + +Feet +== Ноги + +Filter +== Фильтр + +Fire +== Выстрел + +Folder +== Папка + +Force vote +== Форсировать + +Free-View +== Свободный обзор + +Friends +== Друзья + +Fullscreen +== Полноэкранный режим + +Game +== Игра + +Game info +== Инфо об игре + +Game over +== Игра окончена + +Game type +== Тип игры + +Game types: +== Тип игры: + +General +== Основные + +Graphics +== Графика + +Grenade +== Гранатомёт + +Hammer +== Молот + +Has people playing +== Не пустой сервер + +High Detail +== Высокая детализация + +Hook +== Крюк + +Invalid Demo +== Недопустимое демо + +Join blue +== За синих + +Join red +== За красных + +Jump +== Прыжок + +Kick player +== Забанить игрока + +Language +== Язык + +Loading +== Загрузка + +MOTD +== MOTD + +Map +== Карта + +Maximum ping: +== Макс. пинг: + +Mouse sens. +== Чувств. мыши + +Move left +== Шаг влево + +Move player to spectators +== Сделать наблюдателем + +Move right +== Шаг вправо + +Movement +== Перемещение + +Mute when not active +== Глушить звуки, когда игра неактивна + +Name +== Имя + +Next weapon +== След. оружие + +Nickname +== Имя + +No +== Нет + +No password +== Без пароля + +No servers found +== Сервера не найдены + +No servers match your filter criteria +== Нет серверов, подходящих под ваш фильтр + +Ok +== ОК + +Open +== Открыть + +Parent Folder +== Родительский каталог + +Password +== Пароль + +Password incorrect +== Пароль + +Ping +== Пинг + +Pistol +== Пистолет + +Play +== Просмотр + +Play background music +== Играть фоновую музыку + +Player +== Игрок + +Player country: +== Страна: + +Player options +== Опции игрока + +Players +== Игроки + +Please balance teams! +== Сбалансируйте команды! + +Prev. weapon +== Пред. оружие + +Quality Textures +== Качественные текстуры + +Quit +== Выход + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Причина: + +Red team +== Красные + +Red team wins! +== Красные победили! + +Refresh +== Обновить + +Refreshing master servers +== Обновление списка мастер-серверов + +Remote console +== Консоль сервера + +Remove +== Удалить + +Remove friend +== Удалить друга + +Rename +== Переименов. + +Rename demo +== Переименовать демо + +Reset filter +== Сбросить фильтры + +Sample rate +== Частота + +Score +== Очки + +Score board +== Табло + +Score limit +== Лимит очков + +Scoreboard +== Табло + +Screenshot +== Снимок + +Server address: +== Адрес сервера + +Server details +== Детали сервера + +Server filter +== Фильтр серверов + +Server info +== Информация + +Server not full +== Сервер не заполнен + +Settings +== Настройки + +Shotgun +== Дробовик + +Show chat +== Показать чат + +Show friends only +== Только с друзьями + +Show ingame HUD +== Показывать внутриигровой HUD + +Show name plates +== Показывать имена игроков + +Show only supported +== Показывать только поддерживаемые разрешения экрана + +Skins +== Скины + +Sound +== Звук + +Sound error +== Звуковая ошибка + +Spectate +== Наблюдать + +Spectate next +== Наблюдать след. + +Spectate previous +== Наблюдать пред. + +Spectator mode +== Наблюдатель + +Spectators +== Наблюдатели + +Standard gametype +== Стандартный тип игры + +Standard map +== Стандартная карта + +Stop record +== Стоп + +Strict gametype filter +== Строгий фильтр р-мов + +Sudden Death +== Быстрая смерть + +Switch weapon on pickup +== Переключать оружие при подборе + +Team +== Команда + +Team chat +== Командный чат + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Вышла Teeworlds %s! Скачивайте на www.teeworlds.com! + +Texture Compression +== Сжатие текстур + +The audio device couldn't be initialised. +== Аудио устройство не может быть инициализировано + +The server is running a non-standard tuning on a pure game type. +== Сервер запущен с нестандартными настройками на стандартном типе игры. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Есть несохранённая карта в редакторе, Вы можете сохранить её перед тем, как выйти. + +Time limit +== Лимит времени + +Time limit: %d min +== Лимит времени: %d + +Try again +== ОК + +Type +== Тип + +Unable to delete the demo +== Невозможно удалить демо + +Unable to rename the demo +== Невозможно переименовать демо + +Use sounds +== Использовать звуки + +Use team colors for name plates +== Командные цвета для имен игроков + +V-Sync +== Вертикальная синхронизация + +Version +== Версия + +Vote command: +== Комманда голосования: + +Vote description: +== Описание голосования: + +Vote no +== Против + +Vote yes +== За + +Voting +== Голосование + +Warmup +== Разминка + +Weapon +== Оружие + +Welcome to Teeworlds +== Добро пожаловать в Teeworlds! + +Yes +== Да + +You must restart the game for all settings to take effect. +== Перезапустите игру для применения изменений. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Новое имя + +Sat. +== Контраст + +Miscellaneous +== Дополнительно + +Internet +== Интернет + +Max demos +== Максимальное количество демо + +News +== Новости + +Join game +== Играть + +FSAA samples +== Сэмплов FSAA + +%d of %d servers, %d players +== %d из %d серверов, %d игроков + +Sound volume +== Громкость звука + +Created: +== Создан: + +Max Screenshots +== Максимальное количество снимков + +Length: +== Длина + +Skin name +== + +Rifle +== Бластер + +Patterns +== + +Netversion: +== Версия: + +Map: +== Карта: + +%d Bytes +== %d байт + +Coloration +== + +Info +== Инфо + +Hue +== Оттенок + +Record demo +== Записать демо + +Your skin +== Ваш скин + +Size: +== Размер: + +Reset to defaults +== Сбросить настройки + +Quit anyway? +== Выйти? + +Display Modes +== Разрешение экрана + +Version: +== Версия: + +Round +== Раунд + +Lht. +== Яркость + +no limit +== Без лимита + +Quick search: +== Быстрый поиск: + +UI Color +== Цвет интерфейса + +Host address +== Адрес сервера + +Crc: +== Crc: + +Alpha +== Прозрачн. + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Текущая версия: %s + +LAN +== LAN + +Name plates size +== Размер + +Type: +== Тип: + diff --git a/data/languages/serbian.txt b/data/languages/serbian.txt new file mode 100644 index 0000000000..07131694f1 --- /dev/null +++ b/data/languages/serbian.txt @@ -0,0 +1,1020 @@ +##### authors ##### +#originally created by: +# DNR +#modified by: +# DNR 2011-07-15 00:36:32 +# EliteTee 2011-11-30 01:55:30 +# DNR 2011-11-30 01:57:52 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% učitano + +%ds left +== Još %ds + +%i minute left +== %i minut preostao + +%i minutes left +== Preostalo: %i min. + +%i second left +== Preostalo: %i min. + +%i seconds left +== Preostalo: %i sek. + +%s wins! +== %s je pobedio! + +-Page %d- +== -Strana %d- + +Abort +== Prekini + +Add +== Dodaj + +Add Friend +== Dodaj prijatelja + +Address +== Adresa + +All +== Svi + +Always show name plates +== Uvek prikaži imena igrača + +Are you sure that you want to delete the demo? +== Da li sigurni da želite da obrišete demo-snimak? + +Are you sure that you want to quit? +== Jeste li sigurni da želite da izađete? + +Are you sure that you want to remove the player from your friends list? +== Da li ste sigurni da želite da obrišete igrača iz liste prijatelja? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Pošto prvi put pokrećete igru, molimo da ispod unesete Vaš nadimak (nick). Preporučujemo da proverite podešavanja i podesite ih prema Vašem ukusu pre nego što se konektujete na server. + +Automatically record demos +== Automatski snimi demo + +Automatically take game over screenshot +== Automatski snimi screenshot + +Blue team +== Plavi tim + +Blue team wins! +== Plavi tim je pobedio! + +Body +== Telo + +Call vote +== Glasanje + +Change settings +== Izmeni podešavanja + +Chat +== Chat + +Clan +== Klan + +Client +== Klijent + +Close +== Zatvori + +Compatible version +== Kompatibilna verzija + +Connect +== Konektuj + +Connecting to +== Konektujem se na + +Connection Problems... +== Problemi sa konekcijom... + +Console +== Konzola + +Controls +== Kontrole + +Count players only +== Broj samo igrače + +Country +== Država + +Current +== Trenutno + +Custom colors +== Vlastite boje + +Delete +== Izbriši + +Delete demo +== Obriši demo-snimak + +Demo details +== Detalji demo-a + +Demofile: %s +== Demo-snimak: %s + +Demos +== Demo-i + +Disconnect +== Diskonektuj + +Disconnected +== Diskonektovan + +Downloading map +== Preuzimam mapu + +Draw! +== Nerešeno! + +Dynamic Camera +== Dinamična kamera + +Emoticon +== Emoticon + +Enter +== Započni + +Error +== Greška + +Error loading demo +== Greška prilikom učitavanja demo-snimka + +Favorite +== Omiljen + +Favorites +== Omiljeni + +Feet +== Stopala + +Filter +== Filter + +Fire +== Pucaj + +Folder +== Folder + +Force vote +== Obavezno glasanje + +Free-View +== Slobodan pregled + +Friends +== Prijatelji + +Fullscreen +== Čitav ekran + +Game +== Igra + +Game info +== O igri + +Game over +== Igra je završena + +Game type +== Tip igre + +Game types: +== Tipovi igre: + +General +== Opšte + +Graphics +== Grafika + +Grenade +== Granate + +Hammer +== Čekić + +Has people playing +== Server nije prazan + +High Detail +== Visoki detalji + +Hook +== Kuka + +Invalid Demo +== Neispravan demo-snimak + +Join blue +== U plavi tim + +Join red +== U crveni tim + +Jump +== Skok + +Kick player +== Izbaci igrača iz igre + +Language +== Jezik + +Loading +== Učitavam + +MOTD +== Vest dana + +Map +== Mapa + +Maximum ping: +== Maksimalan ping: + +Mouse sens. +== Osetljivost miša + +Move left +== Nalevo + +Move player to spectators +== Prebaci igrača u posmatrače + +Move right +== Nadesno + +Movement +== Kretanje + +Mute when not active +== Bez zvuka prilikom neaktivnosti + +Name +== Ime + +Next weapon +== Sledeće oružje + +Nickname +== Nadimak + +No +== Ne + +No password +== Bez lozinke + +No servers found +== Nije pronađen nijedan server + +No servers match your filter criteria +== Nijedan server ne odgovara zadatom kriteriju + +Ok +== Ok + +Open +== Otvori + +Parent Folder +== Prethodni folder + +Password +== Lozinka + +Password incorrect +== Pogrešna lozinka + +Ping +== Ping + +Pistol +== Pištolj + +Play +== Pokreni + +Play background music +== Pozadinska muzika + +Player +== Igrač + +Player country: +== Država + +Player options +== Podešavanja igrača + +Players +== Igrači + +Please balance teams! +== Molim uravnotežite timove! + +Prev. weapon +== Prethodno oružje + +Quality Textures +== Visokokvalitetne teksture + +Quit +== Izlaz + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Razlog: + +Red team +== Crveni tim + +Red team wins! +== Crveni tim je pobedio! + +Refresh +== Osveži + +Refreshing master servers +== Osvežavam master servere + +Remote console +== Udaljena konzola + +Remove +== Ukloni + +Remove friend +== Ukloni prijatelja + +Rename +== Preimenuj + +Rename demo +== Preimenuj demo + +Reset filter +== Poništi filter + +Sample rate +== Frekvencija + +Score +== Rezultat + +Score board +== Bodovi + +Score limit +== Max. bodova + +Scoreboard +== Bodovi + +Screenshot +== Screenshot + +Server address: +== Adresa servera: + +Server details +== Podaci o serveru + +Server filter +== Filter servera + +Server info +== O Serveru + +Server not full +== Server nije pun + +Settings +== Podešavanja + +Shotgun +== Sačmarica + +Show chat +== Prikaži chat + +Show friends only +== Prikaži samo prijatelje + +Show ingame HUD +== Prikaži HUD + +Show name plates +== Prikaži imena igrača + +Show only supported +== Prikaži samo podržane + +Skins +== Izgled + +Sound +== Zvuk + +Sound error +== Problem sa zvukom + +Spectate +== Posmatraj + +Spectate next +== Posmatraj sledećeg + +Spectate previous +== Posmatraj prošlog + +Spectator mode +== Posmatrački mod + +Spectators +== Posmatrači + +Standard gametype +== Standardni tip igre + +Standard map +== Standardna mapa + +Stop record +== Prekini snimanje + +Strict gametype filter +== Striktan filter tipa igre + +Sudden Death +== Iznenadna smrt + +Switch weapon on pickup +== Aktiviraj novo oružje prilikom uzimanja + +Team +== Tim + +Team chat +== Timski chat + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworld %s je objavljen! Preuzmi ga na www.teeworlds.com! + +Texture Compression +== Kompresija tekstura + +The audio device couldn't be initialised. +== Audio uređaj nije moguće pokrenuti. + +The server is running a non-standard tuning on a pure game type. +== Server sadrži nestandardna podešavanja. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Mapa u editoru nije zapamćena, možda želite da je zapamtite pre izlaska iz igre. + +Time limit +== Max. vremena + +Time limit: %d min +== Vremensko ograničenje: %d min. + +Try again +== Pokušaj ponovo + +Type +== Tip + +Unable to delete the demo +== Demo nije moguće obrisati + +Unable to rename the demo +== Demo nije moguće preimenovati + +Use sounds +== Koristi zvuk + +Use team colors for name plates +== Koristi timsku boju u prikazu imena + +V-Sync +== V-Sync + +Version +== Verzija + +Vote command: +== Komanda za glasanje: + +Vote description: +== Opis glasanja: + +Vote no +== Protiv + +Vote yes +== Za + +Voting +== Glasanje + +Warmup +== Zagrejavanje + +Weapon +== Oružje + +Welcome to Teeworlds +== Dobrodošli u Teeworlds + +Yes +== Da + +You must restart the game for all settings to take effect. +== Morate ponovo pokrenuti igru da bi sva podešavanja bila primenjena. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Novo ime: + +Sat. +== Zasić. + +Miscellaneous +== Razno + +Internet +== Internet + +Max demos +== Maksimalan broj demo-a + +News +== Novosti + +Join game +== Uključi se u igru + +FSAA samples +== FSAA samples + +%d of %d servers, %d players +== %d od %d server(a), %d igrač(a) + +Sound volume +== Jačina zvuka + +Created: +== Kreirano: + +Max Screenshots +== Maksimalan broj screenshot-ova + +Length: +== Dužina: + +Skin name +== + +Rifle +== Puška + +Patterns +== + +Netversion: +== Net verzija: + +Map: +== Mapa: + +%d Bytes +== %d Bajtova + +Coloration +== + +Info +== Info + +Hue +== Nijansa + +Record demo +== Snimi demo + +Your skin +== Vaš izgled + +Size: +== Veličina: + +Reset to defaults +== Resetuj podešavanja + +Quit anyway? +== Izlaz? + +Display Modes +== Rezolucija i način prikaza + +Version: +== Verzija: + +Round +== Runda + +Lht. +== Svetl. + +no limit +== bez ograničenja + +Quick search: +== Brza pretraga: + +UI Color +== Boja menija + +Host address +== Adresa servera + +Crc: +== Crc: + +Alpha +== Provid. + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Trenutna verzija: %s + +LAN +== LAN + +Name plates size +== Veličina pločice za ime + +Type: +== Tip: + diff --git a/data/languages/simplified_chinese.txt b/data/languages/simplified_chinese.txt new file mode 100644 index 0000000000..2d113ebc2f --- /dev/null +++ b/data/languages/simplified_chinese.txt @@ -0,0 +1,1018 @@ +##### authors ##### +#originally created by: +# MK124 +#modified by: +# +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== 已加载 %d%% + +%ds left +== %ds 离开了 + +%i minute left +== 还剩下 %i 分钟 + +%i minutes left +== 还剩下 %i 分钟 + +%i second left +== 还剩下 %i 秒钟 + +%i seconds left +== 还剩下 %i 秒钟 + +%s wins! +== %s 胜利! + +-Page %d- +== -第 %d 页- + +Abort +== 取消 + +Add +== 添加 + +Add Friend +== 添加好友 + +Address +== 地址 + +All +== 全部 + +Always show name plates +== 总是显示名字板 + +Are you sure that you want to delete the demo? +== 你确定要删除这个录像吗? + +Are you sure that you want to quit? +== 你确定要退出吗? + +Are you sure that you want to remove the player from your friends list? +== 你确定要把这个玩家从好友列表中删除吗? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== 这是你第一次执行游戏, 所以请在下面输入你的昵称. 建议你在进入一个服务器之前, 先确认一下设置是否符合你的习惯. + +Automatically record demos +== 自动记录录像 + +Automatically take game over screenshot +== 自动截取游戏结束画面 + +Blue team +== 蓝队 + +Blue team wins! +== 蓝队胜利! + +Body +== 身体 + +Call vote +== 发起投票 + +Change settings +== 改变设置 + +Chat +== 聊天 + +Clan +== 团队 + +Client +== 客户端 + +Close +== 关闭 + +Compatible version +== 兼容的版本 + +Connect +== 连接 + +Connecting to +== 正在连接到 + +Connection Problems... +== 连接出现问题... + +Console +== 控制台 + +Controls +== 控制 + +Count players only +== 只统计玩家数 + +Country +== 国家/地区 + +Current +== 当前 + +Custom colors +== 自定义颜色 + +Delete +== 删除 + +Delete demo +== 删除录像 + +Demo details +== 录像详细信息 + +Demofile: %s +== 录像: %s + +Demos +== 录像 + +Disconnect +== 断开连接 + +Disconnected +== 连接已断开 + +Downloading map +== 正在下载地图 + +Draw! +== 平局! + +Dynamic Camera +== 动态镜头 + +Emoticon +== 表情 + +Enter +== 进入 + +Error +== 错误 + +Error loading demo +== 读取录像错误 + +Favorite +== 收藏 + +Favorites +== 收藏夹 + +Feet +== 脚 + +Filter +== 过滤器 + +Fire +== 开火 + +Folder +== 文件夹 + +Force vote +== 强制投票 + +Free-View +== 自由视角 + +Friends +== 好友 + +Fullscreen +== 全屏幕 + +Game +== 游戏 + +Game info +== 游戏信息 + +Game over +== 游戏结束 + +Game paused +== 游戏已暂停 + +Game type +== 游戏类型 + +Game types: +== 游戏类型: + +General +== 常规 + +Graphics +== 图像 + +Grenade +== 手榴弹 + +Hammer +== 锤子 + +Has people playing +== 有人在玩 + +High Detail +== 高细节 + +Hook +== 钩子 + +Invalid Demo +== 无效的录像 + +Join blue +== 加入蓝队 + +Join red +== 加入红队 + +Jump +== 跳跃 + +Kick player +== 踢除玩家 + +Language +== 语言 + +Loading +== 载入中 + +MOTD +== 每日通告 + +Map +== 地图 + +Maximum ping: +== 最高延迟: + +Mouse sens. +== 鼠标灵敏度 + +Move left +== 向左移动 + +Move player to spectators +== 移动玩家为观众 + +Move right +== 向右移动 + +Movement +== 移动 + +Mute when not active +== 不活动的时候静音 + +Name +== 名字 + +Next weapon +== 下一个武器 + +Nickname +== 昵称 + +No +== 否 + +No password +== 无密码 + +No servers found +== 找不到任何服务器 + +No servers match your filter criteria +== 没有任何服务器匹配你的过滤器 + +Ok +== 确定 + +Open +== 打开 + +Parent Folder +== 上级目录 + +Password +== 密码 + +Password incorrect +== 密码错误 + +Ping +== 延迟 + +Pistol +== 手枪 + +Play +== 播放 + +Play background music +== 播放背景音乐 + +Player +== 玩家 + +Player country: +== 玩家的国家/地区: + +Player options +== 玩家选项 + +Players +== 玩家数 + +Please balance teams! +== 请平衡团队人数! + +Prev. weapon +== 上一个武器 + +Quality Textures +== 高品质纹理 + +Quit +== 退出 + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== 理由: + +Red team +== 红队 + +Red team wins! +== 红队胜利! + +Refresh +== 刷新 + +Refreshing master servers +== 刷新主服务器中 + +Remote console +== 远程控制台 + +Remove +== 移除 + +Remove friend +== 删除好友 + +Rename +== 重命名 + +Rename demo +== 重命名录像 + +Reset filter +== 重置过滤器 + +Respawn +== 重生 + +Sample rate +== 采样率 + +Score +== 分数 + +Score board +== 得分榜 + +Score limit +== 分数限制 + +Scoreboard +== 得分榜 + +Screenshot +== 截图 + +Server address: +== 服务器地址: + +Server details +== 服务器详细信息 + +Server filter +== 服务器过滤器 + +Server info +== 服务器信息 + +Server not full +== 服务器未满 + +Settings +== 设置 + +Shotgun +== 霰弹枪 + +Show chat +== 显示聊天 + +Show friends only +== 只显示好友 + +Show ingame HUD +== 显示游戏内 HUD + +Show name plates +== 显示名字板 + +Show only chat messages from friends +== 只显示好友消息 + +Show only supported +== 只显示支持的 + +Skins +== 皮肤 + +Sound +== 声音 + +Sound error +== 声音出现错误 + +Spectate +== 旁观 + +Spectate next +== 观看后一个 + +Spectate previous +== 观看前一个 + +Spectator mode +== 旁观者模式 + +Spectators +== 旁观者 + +Standard gametype +== 标准游戏类型 + +Standard map +== 标准地图 + +Stop record +== 停止录制 + +Strict gametype filter +== 严格过滤游戏类型 + +Sudden Death +== 出乎意料的死亡 + +Switch weapon on pickup +== 拾取武器时自动装备 + +Team +== 团队 + +Team chat +== 团队聊天 + +Teeworlds %s is out! Download it at www.teeworlds.com! +== 新版 Teeworlds %s 已经发布! 请到 www.teeworlds.com 下载最新版! + +Texture Compression +== 纹理压缩 + +The audio device couldn't be initialised. +== 无法初始化音频设备。 + +The server is running a non-standard tuning on a pure game type. +== 此服务器运行着经过调整的非标准游戏类型. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== 编辑器中的地图尚未保存, 或许你应该在退出游戏之前保存一下. + +Time limit +== 时间限制 + +Time limit: %d min +== 时间限制: %d 分钟 + +Try again +== 重试 + +Type +== 类型 + +Unable to delete the demo +== 无法删除这个录像 + +Unable to rename the demo +== 无法重命名这个录像 + +Use sounds +== 开启声音 + +Use team colors for name plates +== 为名字板使用团队颜色 + +V-Sync +== 垂直同步 + +Version +== 版本 + +Vote command: +== 投票命令: + +Vote description: +== 投票描述: + +Vote no +== 否决 + +Vote yes +== 赞成 + +Voting +== 投票 + +Warmup +== 热身 + +Weapon +== 武器 + +Welcome to Teeworlds +== 欢迎来到 Teeworlds! + +Yes +== 是 + +You must restart the game for all settings to take effect. +== 你必须重启游戏才能让所有设置起效. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== 新名字: + +Sat. +== 饱和度 + +Miscellaneous +== 杂项 + +Internet +== 因特网 + +Max demos +== 最大录像数 + +News +== 新闻 + +Join game +== 加入游戏 + +FSAA samples +== 全屏抗锯齿(FSAA)采样倍数 + +%d of %d servers, %d players +== %d / %d 个服务器, %d 个玩家 + +Sound volume +== 音量 + +Created: +== 已创建: + +Max Screenshots +== 最大截图数 + +Length: +== 长度: + +Skin name +== + +Rifle +== 激光枪 + +Patterns +== + +Netversion: +== 网络版本 + +Map: +== 地图: + +%d Bytes +== %d 字节 + +Coloration +== + +Info +== 信息 + +Hue +== 色调 + +Record demo +== 录制录像 + +Your skin +== 你的皮肤 + +Size: +== 尺寸: + +Reset to defaults +== 恢复默认设置 + +Quit anyway? +== 无论如何都要退出吗? + +Display Modes +== 显示模式 + +Version: +== 版本: + +Round +== 回合 + +Lht. +== 亮度 + +no limit +== 无限制 + +Quick search: +== 快速搜索: + +UI Color +== 界面颜色 + +Host address +== 主机地址 + +Crc: +== Crc校验码: + +Alpha +== 透明度 + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== 当前版本: %s + +LAN +== 局域网 + +Name plates size +== 名字板尺寸 + +Type: +== 类型: + diff --git a/data/languages/slovak.txt b/data/languages/slovak.txt new file mode 100644 index 0000000000..7fc91b11b5 --- /dev/null +++ b/data/languages/slovak.txt @@ -0,0 +1,1018 @@ +##### authors ##### +#originally created by: +# Limit and Petr +#modified by: +# LimiT 2011-07-02 20:24:44 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% načítaných + +%ds left +== Zostáva %ds + +%i minute left +== Zostávajúce minúty: %i + +%i minutes left +== Zostávajúce minúty: %i + +%i second left +== Zostávajúce sekundy: %i + +%i seconds left +== Zostávajúce sekundy: %i + +%s wins! +== %s vyhráva! + +-Page %d- +== -Strana %d- + +Abort +== Zrušiť + +Add +== Pridať + +Add Friend +== Pridať Priateľa + +Address +== Adresa + +All +== Všetkým + +Always show name plates +== Vždy zobrazovať menovky + +Are you sure that you want to delete the demo? +== Určite chcete vymazať tento záznam? + +Are you sure that you want to quit? +== Ukončiť hru? + +Are you sure that you want to remove the player from your friends list? +== Ste si istí, že chcete tohto hráča odstrániť zo zoznamu priateľov? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Vitajte v hre TeeWorlds. Predtým, ako sa pripojíte na herný server, odporúčame nastaviť si hru podľa svojich požiadavkov. Napíšte do políčka nižšie meno pre Vášho tee a pokračujte kliknutím na tlačítko. + +Automatically record demos +== Automaticky nahrávať záznamy + +Automatically take game over screenshot +== Automaticky vyfotiť koniec kola + +Blue team +== Modrý tým + +Blue team wins! +== Modrý tým vyhráva! + +Body +== Telo + +Call vote +== Hlasovať + +Change settings +== Zmeniť nastavenia + +Chat +== Chat + +Clan +== Klan + +Client +== Klient + +Close +== Zavrieť + +Compatible version +== Kompatibilná verzia + +Connect +== Pripojiť + +Connecting to +== Pripojujem sa k + +Connection Problems... +== Problémy s pripojením... + +Console +== Konzola + +Controls +== Ovládanie + +Count players only +== Nepočítať divákov + +Country +== Krajina + +Current +== Aktuálne + +Custom colors +== Vlastné farby + +Delete +== Vymazať + +Delete demo +== Vymazať záznam + +Demo details +== Detaily nahrávky + +Demofile: %s +== Nahrávka: %s + +Demos +== Záznamy + +Disconnect +== Odpojiť + +Disconnected +== Prerušené spojenie + +Downloading map +== Sťahujem mapu + +Draw! +== Remíza! + +Dynamic Camera +== Dynamická kamera + +Emoticon +== Smajlíci + +Enter +== Vstúpiť + +Error +== Chyba + +Error loading demo +== Problém s otvorením záznamu + +Favorite +== Obľúbený + +Favorites +== Obľúbené + +Feet +== Nohy + +Filter +== Filter + +Fire +== Streľba + +Folder +== Zložka + +Force vote +== Zvoliť + +Free-View +== Voľná Kamera + +Friends +== Priatelia + +Fullscreen +== Celá obrazovka + +Game +== Hra + +Game info +== Info o hre + +Game over +== Koniec hry + +Game type +== Herný typ + +Game types: +== Herné typy: + +General +== Hlavné + +Graphics +== Grafika + +Grenade +== Granátomet + +Hammer +== Kladivo + +Has people playing +== Nie je prázdny + +High Detail +== Detaily + +Hook +== Hook/Lano + +Invalid Demo +== Neplatný Záznam + +Join blue +== K modrým + +Join red +== K červeným + +Jump +== Skok + +Kick player +== Vyhodiť hráča + +Language +== Jazyk + +Loading +== Nahrávam + +MOTD +== MOTD + +Map +== Mapa + +Maximum ping: +== Maximálny ping: + +Mouse sens. +== Citlivosť myši + +Move left +== Pohyb vľavo + +Move player to spectators +== Poslať hráča pozorovať + +Move right +== Pohyb vpravo + +Movement +== Pohyb + +Mute when not active +== Stlmiť pri neaktivite + +Name +== Meno + +Next weapon +== Ďalšia zbraň + +Nickname +== Prezývka + +No +== Nie + +No password +== Bez hesla + +No servers found +== Nenájdené žiadne servery + +No servers match your filter criteria +== Žiadny server nezodpovedá zadaným kritériám + +Ok +== OK + +Open +== Otvoriť + +Parent Folder +== Nadradený Priečinok + +Password +== Heslo + +Password incorrect +== Nesprávne heslo + +Ping +== Ping + +Pistol +== Pištoľ + +Play +== Prehrať + +Play background music +== Prehrať hudbu na pozadí + +Player +== Hráč + +Player country: +== Filter krajín: + +Player options +== Nastavenia hráča + +Players +== Hráči + +Please balance teams! +== Prosím vyrovnajte týmy! + +Prev. weapon +== Predošlá zbraň + +Quality Textures +== Kvalitné textúry + +Quit +== Ukončiť + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Dôvod: + +Red team +== Červený tým + +Red team wins! +== Červený tým vyhráva! + +Refresh +== Obnoviť + +Refreshing master servers +== Obnovujem master servery + +Remote console +== Vzdialená konzola + +Remove +== Odstrániť + +Remove friend +== Vymazať priateľa + +Rename +== Premenovať + +Rename demo +== Premenovať nahrávku + +Reset filter +== Obnoviť filter + +Sample rate +== Sample rate + +Score +== Skóre + +Score board +== Tabuľka výsledkov + +Score limit +== Limit skóre + +Scoreboard +== Tabuľka výsledkov + +Screenshot +== Screenshot + +Server address: +== Adresa servera: + +Server details +== Detaily servera + +Server filter +== Filter serverov + +Server info +== Informácie + +Server not full +== Nie je plný + +Settings +== Nastavenia + +Shotgun +== Brokovnica + +Show chat +== Ukázať chat + +Show friends only +== Ukázať priateľov + +Show ingame HUD +== Ukázať HUD + +Show name plates +== Zobrazovať menovky + +Show only supported +== Zobraziť len podporované + +Skins +== Skiny + +Sound +== Zvuk + +Sound error +== Zvuková chyba + +Spectate +== Pozorovať + +Spectate next +== Pozorovať ďalšieho + +Spectate previous +== Pozorovať predch. + +Spectator mode +== Mód diváka + +Spectators +== Diváci + +Standard gametype +== Štandardný herný typ + +Standard map +== Štandardná mapa + +Stop record +== Nenahrávať + +Strict gametype filter +== Striktný filter módov + +Sudden Death +== Rýchla Smrť + +Switch weapon on pickup +== Nastavovať zdvíhanú zbraň ako aktuálnu + +Team +== Týmu + +Team chat +== Týmový chat + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Vyšla nová verzia hry: %s! Stiahnite si ju na www.teeworlds.com! + +Texture Compression +== Kompresia textúr + +The audio device couldn't be initialised. +== Zvukové zariadenie nemohlo byť inicializované. + +The server is running a non-standard tuning on a pure game type. +== Server používa neštandardné nastavenia na základnom hernom móde. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== V editore máte neuloženú mapu, možno si ju chcete pred skončením hry uložiť. + +Time limit +== Časový limit + +Time limit: %d min +== Časový limit: %d min + +Try again +== Skúsiť znovu + +Type +== Typ + +Unable to delete the demo +== Nemôžem vymazať záznam + +Unable to rename the demo +== Nahrávka sa nedá premenovať + +Use sounds +== Povoliť zvuky + +Use team colors for name plates +== Zafarbiť menovky podľa farieb týmov + +V-Sync +== V-Sync + +Version +== Verzia + +Vote command: +== Príkaz hlasu: + +Vote description: +== Popis hlasu: + +Vote no +== Nie + +Vote yes +== Áno + +Voting +== Hlasovanie + +Warmup +== Rozohrávka + +Weapon +== Zbrane + +Welcome to Teeworlds +== Vitajte v hre Teeworlds! + +Yes +== Áno + +You must restart the game for all settings to take effect. +== Zmeny sa prejavia po reštartovaní hry. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Nové meno: + +Sat. +== Sat. + +Miscellaneous +== Ostatné + +Internet +== Internet + +Max demos +== Maximum nahrávok + +News +== Novinky + +Join game +== Hrať + +FSAA samples +== Anti-aliasing + +%d of %d servers, %d players +== %d z %d serverov, %d hráčov online + +Sound volume +== Hlasitosť + +Created: +== Vytvorené: + +Max Screenshots +== Maximum obrázkov + +Length: +== Dĺžka: + +Skin name +== + +Rifle +== Laser + +Patterns +== + +Netversion: +== Netversion: + +Map: +== Mapa: + +%d Bytes +== %d Bajtov + +Coloration +== + +Info +== Info + +Hue +== Hue + +Record demo +== Nahrávať + +Your skin +== Váš skin + +Size: +== Veľkosť: + +Reset to defaults +== Vrátiť na pôvodné + +Quit anyway? +== Aj tak ukončiť? + +Display Modes +== Možnosti zobrazenia + +Version: +== Verzia: + +Round +== Kolo + +Lht. +== Lht. + +no limit +== bez limitu + +Quick search: +== Hľadanie: + +UI Color +== Farba prostredia + +Host address +== Hostiteľ + +Crc: +== Crc: + +Alpha +== Alpha + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Aktuálna verzia: %s + +LAN +== LAN + +Name plates size +== Veľkosť menoviek + +Type: +== Typ: + diff --git a/data/languages/spanish.txt b/data/languages/spanish.txt new file mode 100644 index 0000000000..b7ef477ca2 --- /dev/null +++ b/data/languages/spanish.txt @@ -0,0 +1,1022 @@ +##### authors ##### +#originally created by: +# ReGnuM +#modified by: +# Ninja Style 2011-04-06 23:22:06 +# Alexandre 2011-07-11 11:30:00 +# GaBOr 2011-07-15 01:23:39 +# HeroiAmarelo 2012-08-01 15:52:45 +# Mortcheck 2012-11-03 22:58:43 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% cargado + +%ds left +== %ds restantes + +%i minute left +== %i minuto restante + +%i minutes left +== %i minutos restantes + +%i second left +== %i segundo restante + +%i seconds left +== %i segundos restantes + +%s wins! +== ¡%s gana! + +'%s' entered and joined the %s +== '%s' entró y se unió el %s + +'%s' has left the game +== '%s' ha salido del juego + +'%s' has left the game (%s) +== '%s' ha salido del juego (%s) + +-Page %d- +== -Página %d- + +Abort +== Cancelar + +Add +== Añadir + +Add Friend +== Añadir amigo + +Address +== Dirección + +All +== Todos + +Always show name plates +== Mostrar siempre los apodos + +Are you sure that you want to delete the demo? +== ¿Estás seguro de que quieres eliminar la demo? + +Are you sure that you want to quit? +== ¿Estás seguro de que quieres salir? + +Are you sure that you want to remove the player from your friends list? +== ¿Estás seguro de que quieres eliminar a este jugador de la lista de amigos? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Como es la primera vez que abres el juego, por favor, introduce tu apodo. Es recomendable que verifiques tu configuración y ajustes las preferencias antes de unirte a un servidor. + +Automatically record demos +== Grabar demos automáticamente + +Automatically take game over screenshot +== Captura de pantalla al final de la partida + +Blue team +== Equipo azul + +Blue team wins! +== ¡El equipo azul gana! + +Body +== Cuerpo + +Call vote +== Votar + +Change settings +== Cambiar configuración + +Chat +== Conversación + +Clan +== Clan + +Client +== Cliente + +Close +== Cerrar + +Compatible version +== Versión compatible + +Connect +== Conectar + +Connecting to +== Conectando con + +Connection Problems... +== Problemas de conexión... + +Console +== Consola + +Controls +== Controles + +Count players only +== Solo contar jugadores + +Country +== País + +Current +== Actual + +Custom colors +== Colores personalizados + +Delete +== Borrar + +Delete demo +== Borrar demo + +Demo details +== Detalles de la demo + +Demofile: %s +== Archivo: %s + +Demos +== Demos + +Disconnect +== Desconectar + +Disconnected +== Desconectado + +Downloading map +== Descargando mapa + +Draw! +== ¡Empate! + +Dynamic Camera +== Cámara dinámica + +Emoticon +== Emoticon + +Enter +== Entrar + +Error +== Error + +Error loading demo +== Error al cargar la demo + +Favorite +== Favorito + +Favorites +== Favoritos + +Feet +== Pies + +Filter +== Filtro + +Fire +== Disparar + +Folder +== Carpeta + +Force vote +== Forzar + +Free-View +== Vista libre + +Friends +== Amigos + +Fullscreen +== Pantalla completa + +Game +== Juego + +Game info +== Información del juego + +Game over +== Fin de la partida + +Game paused +== Juego en Pausa + +Game type +== Modo + +Game types: +== Tipos de juego: + +General +== General + +Graphics +== Gráficos + +Grenade +== Granada + +Hammer +== Martillo + +Has people playing +== Hay gente jugando + +High Detail +== Mas detalles (HD) + +Hook +== Gancho + +Invalid Demo +== Demo inválida + +Join blue +== Unirse a azul + +Join red +== Unirse a rojo + +Jump +== Saltar + +Kick player +== Expulsar jugador + +Language +== Idioma + +Loading +== Cargando + +MOTD +== MOTD + +Map +== Mapa + +Maximum ping: +== Ping máximo: + +Mouse sens. +== Sensibilidad ratón + +Move left +== Mover a la izquierda + +Move player to spectators +== Mover jugador a espectadores + +Move right +== Mover a la derecha + +Movement +== Movimiento + +Mute when not active +== Silenciar si no está activo + +Name +== Nombre + +Next weapon +== Arma siguiente + +Nickname +== Apodo + +No +== No + +No password +== Sin contraseña + +No servers found +== Ningún servidor encontrado + +No servers match your filter criteria +== Ningún servidor corresponde a los criterios de filtrado + +Ok +== Aceptar + +Open +== Abrir + +Parent Folder +== Directorio padre + +Password +== Contraseña + +Password incorrect +== Contraseña incorreta + +Ping +== Ping + +Pistol +== Pistola + +Play +== Reproducir + +Play background music +== Reproducir música de fondo + +Player +== Jugador + +Player country: +== País del jugador + +Player options +== Opciones de jugador + +Players +== Jugadores + +Please balance teams! +== Por favor, ¡equilibrad los equipos! + +Prev. weapon +== Arma anterior + +Quality Textures +== Texturas de calidad + +Quit +== Salir + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Motivo: + +Red team +== Equipo rojo + +Red team wins! +== ¡El equipo rojo gana! + +Refresh +== Actualizar + +Refreshing master servers +== Actualizando servidores maestros + +Remote console +== Consola remota + +Remove +== Eliminar + +Remove friend +== Eliminar amigo + +Rename +== Renombrar + +Rename demo +== Renombrar demo + +Reset filter +== Resetear filtro + +Respawn +== Respawn + +Sample rate +== Frecuencia de muestreo + +Score +== Puntos + +Score board +== Puntuación + +Score limit +== Límite puntos + +Scoreboard +== Puntuación + +Screenshot +== Captura de pantalla + +Server address: +== IP del servidor: + +Server details +== Detalles del servidor + +Server filter +== Filtro del servidor + +Server info +== Servidor + +Server not full +== Servidor sin llenar + +Settings +== Configuración + +Shotgun +== Escopeta + +Show chat +== Mostrar chat + +Show friends only +== Solo mostrar amigos + +Show ingame HUD +== Mostrar HUD durante el juego + +Show name plates +== Mostrar apodos + +Show only chat messages from friends +== Recibir mensages solo de amigos + +Show only supported +== Mostrar únicamente modos soportados + +Skins +== Skins + +Sound +== Sonido + +Sound error +== Error de sonido + +Spectate +== Asistir + +Spectate next +== Observar siguiente + +Spectate previous +== Observar anterior + +Spectator mode +== Modo espectador + +Spectators +== Espectadores + +Standard gametype +== Tipo de juego estándar + +Standard map +== Mapa estándar + +Stop record +== Detener grabación + +Strict gametype filter +== Tipo de juego del filtro + +Sudden Death +== Muerte súbita + +Switch weapon on pickup +== Cambiar al arma recogida + +Team +== Equipo + +Team chat +== En equipo + +Teeworlds %s is out! Download it at www.teeworlds.com! +== ¡Teeworlds %s ha salido! ¡Descárgalo desde www.teeworlds.com! + +Texture Compression +== Compresión de texturas + +The audio device couldn't be initialised. +== El dispositivo de audio no puede ser inicializado. + +The server is running a non-standard tuning on a pure game type. +== El servidor está ejecutando una configuración no estándar en un tipo de juego puro. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Tienes un mapa sin guardar en el editor, quizá quieras guardarlo antes de salir. + +Time limit +== Tiempo límite + +Time limit: %d min +== Tiempo límite: %d minutos + +Try again +== Intentar de nuevo + +Type +== Tipo + +Unable to delete the demo +== No se pudo eliminar la demo + +Unable to rename the demo +== No se pudo renombrar la demo + +Use sounds +== Usar sonidos + +Use team colors for name plates +== Usar el color de equipo en los apodos + +V-Sync +== V-Sync + +Version +== Versión + +Vote command: +== Comando de votación: + +Vote description: +== Descripción de la votación: + +Vote no +== Votar no + +Vote yes +== Votar sí + +Voting +== Votación + +Warmup +== Calentamiento + +Weapon +== Arma + +Welcome to Teeworlds +== ¡Bienvenido/a a Teeworlds! + +Yes +== Sí + +You must restart the game for all settings to take effect. +== Debes reiniciar el juego para que los cambios tengan efecto. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Nuevo nombre: + +Sat. +== Saturación + +Miscellaneous +== Miscelánea + +Internet +== Internet + +Max demos +== Número máximo de demos + +News +== Noticias + +Join game +== Unirse + +FSAA samples +== Muestras FSAA + +%d of %d servers, %d players +== %d de %d servidores, %d jugadores + +Sound volume +== Volumen de sonido + +Created: +== Creado: + +Max Screenshots +== Número máximo de capturas + +Length: +== Longitud: + +Skin name +== + +Rifle +== Láser + +Patterns +== + +Netversion: +== Versión Net + +Map: +== Mapa: + +%d Bytes +== %d bytes + +Coloration +== + +Info +== Información + +Hue +== Matiz + +Record demo +== Grabar demo + +Your skin +== Tu skin + +Size: +== Tamaño: + +Reset to defaults +== Resetar por defecto + +Quit anyway? +== ¿Salir de todos modos? + +Display Modes +== Modos de video + +Version: +== Versión: + +Round +== Ronda + +Lht. +== Luminosidad + +no limit +== sin límite + +Quick search: +== Búsqueda rápida: + +UI Color +== Color de menú + +Host address +== Dirección del host + +Crc: +== CRC: + +Alpha +== Alpha + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Versión actual: %s + +LAN +== LAN + +Name plates size +== Tamaño de la fuente de los apodos + +Type: +== Tipo: + diff --git a/data/languages/swedish.txt b/data/languages/swedish.txt new file mode 100644 index 0000000000..4cbc3d93de --- /dev/null +++ b/data/languages/swedish.txt @@ -0,0 +1,1019 @@ +##### authors ##### +#originally created by: +# matricks +#modified by: +# Martin Pola 2011-04-02 11:17:09 +# Kottizen 2011-07-02 00:34:54 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% laddat + +%ds left +== %d sekunder kvar + +%i minute left +== %i minut kvar + +%i minutes left +== %i minuter kvar + +%i second left +== %i sekund kvar + +%i seconds left +== %i sekunder kvar + +%s wins! +== %s vinner! + +-Page %d- +== -Sida %d- + +Abort +== Avbryt + +Add +== Lägg till + +Add Friend +== Lägg till kompis + +Address +== Adress + +All +== Alla + +Always show name plates +== Visa alltid namnskyltar + +Are you sure that you want to delete the demo? +== Är du säker på att du vill ta bort detta demo? + +Are you sure that you want to quit? +== Är du säker på att du vill avsluta? + +Are you sure that you want to remove the player from your friends list? +== Är du säker på att du vill ta bort denna spelaren från din kompislista? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Detta är första gången du startar spelet, var vänligen skriv in ditt smeknamn här nedanför. Det är rekommenderat att du kontrollera inställningarna och justerar dom till din preferens innan du börjar spela. + +Automatically record demos +== Spela in demon automatiskt + +Automatically take game over screenshot +== Ta skärmdumpar vid matchslut + +Blue team +== Blå laget + +Blue team wins! +== Blå laget vann! + +Body +== Kropp + +Call vote +== Röstning + +Change settings +== Ändra inställningar + +Chat +== Chatt + +Clan +== Klan + +Client +== Klient + +Close +== Stäng + +Compatible version +== Kompatibel version + +Connect +== Anslut + +Connecting to +== Ansluter till + +Connection Problems... +== Anslutningsproblem... + +Console +== Konsol + +Controls +== Kontroller + +Count players only +== Räkna endast spelare + +Country +== Land + +Current +== Nuvarande + +Custom colors +== Egna färger + +Delete +== Ta bort + +Delete demo +== Ta bort demo + +Demo details +== Demoinformation + +Demofile: %s +== Demofil: %s + +Demos +== Demon + +Disconnect +== Avsluta + +Disconnected +== Frånkopplad + +Downloading map +== Laddar ner karta + +Draw! +== Oavgjort! + +Dynamic Camera +== Dynamisk kamera + +Emoticon +== Känsloikon + +Enter +== Fortsätt + +Error +== Fel + +Error loading demo +== Kunde inte ladda demot + +Favorite +== Favorit + +Favorites +== Favoriter + +Feet +== Fötter + +Filter +== Filter + +Fire +== Skjuta + +Folder +== Mapp + +Force vote +== Tvinga omröstning + +Free-View +== Friläge + +Friends +== Kompisar + +Fullscreen +== Fullskärm + +Game +== Spel + +Game info +== Spelinfo + +Game over +== Slutspelat + +Game type +== Speltyp + +Game types: +== Speltyper: + +General +== Generellt + +Graphics +== Grafik + +Grenade +== Granater + +Hammer +== Hammare + +Has people playing +== Inte tom + +High Detail +== Extra detaljer + +Hook +== Haken + +Invalid Demo +== Ogiltigt deo + +Join blue +== Spela i blått + +Join red +== Spela i rött + +Jump +== Hoppa + +Kick player +== Kicka spelare + +Language +== Språk + +Loading +== Laddar + +MOTD +== Meddelande + +Map +== Bana + +Maximum ping: +== Högsta ping: + +Mouse sens. +== Muskänslighet + +Move left +== Gå vänster + +Move player to spectators +== Flytta till åskådarna + +Move right +== Gå höger + +Movement +== Förflyttning + +Mute when not active +== Stäng av ljudet när spelet inte är aktivt + +Name +== Namn + +Next weapon +== Nästa vapen + +Nickname +== Smeknamn + +No +== Nej + +No password +== Inget lösenord + +No servers found +== Inga servrar hittade + +No servers match your filter criteria +== Inga servrar matchar dina filterkriterer + +Ok +== Ok + +Open +== Öppna + +Parent Folder +== Uppliggande mapp + +Password +== Lösenord + +Password incorrect +== Felaktigt lösenord + +Ping +== Ping + +Pistol +== Pistol + +Play +== Spela + +Play background music +== Aktivera bakgrundsmusik + +Player +== Spelare + +Player country: +== Land + +Player options +== Spelaralternativ + +Players +== Spelare + +Please balance teams! +== Balansera lagen! + +Prev. weapon +== Föregående vapen + +Quality Textures +== Kvalitetstexturer + +Quit +== Avsluta + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Anledning: + +Red team +== Röda laget + +Red team wins! +== Röda laget vann! + +Refresh +== Uppdatera + +Refreshing master servers +== Uppdaterar huvudservrar + +Remote console +== Serverkonsol + +Remove +== Ta bort + +Remove friend +== Ta bort kompis + +Rename +== Byt namn + +Rename demo +== Byt namn på demo + +Reset filter +== Återställ filter + +Sample rate +== Samplingsfrekvens + +Score +== Poäng + +Score board +== Poängtavla + +Score limit +== Poängmål + +Scoreboard +== Poänglista + +Screenshot +== Skärmdump + +Server address: +== Serveradress + +Server details +== Serverdetaljer + +Server filter +== Serverfilter + +Server info +== Serverinfo + +Server not full +== Lediga platser + +Settings +== Inställningar + +Shotgun +== Hagelgevär + +Show chat +== Visa chatten + +Show friends only +== Visa kompisar + +Show ingame HUD +== Visa HUD i spel + +Show name plates +== Visa namnskyltar + +Show only supported +== Visa endast upplösningar som stöds + +Skins +== Utseende + +Sound +== Ljud + +Sound error +== Ljudfel + +Spectate +== Åskåda + +Spectate next +== Se på nästa + +Spectate previous +== Se på föregående + +Spectator mode +== Åskådarläge + +Spectators +== Åskådare + +Standard gametype +== Standard speltyp + +Standard map +== Standardkarta + +Stop record +== Sluta spela in + +Strict gametype filter +== Strikt speltypsfilter + +Sudden Death +== Plötslig död + +Switch weapon on pickup +== Byt vapen vid anskaffning + +Team +== Lag + +Team chat +== Lagchatt + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s är ute nu! Ladda ner det från www.teeworlds.com. + +Texture Compression +== Texturkompression + +The audio device couldn't be initialised. +== Ljudet kunde inte startas + +The server is running a non-standard tuning on a pure game type. +== Denna server kör inte standardinställningar på en reserverad speltyp. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Det finns en osparad bana i redigeraden, du kanske vill spara den innan du avslutar. + +Time limit +== Tidsbegränsning + +Time limit: %d min +== Tidsgräns: %d min + +Try again +== Försök igen + +Type +== Typ + +Unable to delete the demo +== Kunde inte ta bort demot + +Unable to rename the demo +== Kunde inte byta namn på demot + +Use sounds +== Aktivera ljudeffekter + +Use team colors for name plates +== Använd lagfärger i namnskyltar + +V-Sync +== V-Synk + +Version +== Version + +Vote command: +== Omröstningskommando: + +Vote description: +== Beskrivning: + +Vote no +== Rösta nej + +Vote yes +== Rösta ja + +Voting +== Röstning + +Warmup +== Uppvärmning + +Weapon +== Vapen + +Welcome to Teeworlds +== Välkommen till Teeworlds + +Yes +== Ja + +You must restart the game for all settings to take effect. +== Du måste starta om spelet för att ändringarna skall verkställas. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Nytt namn: + +Sat. +== Mättnad + +Miscellaneous +== Övrigt + +Internet +== Internet + +Max demos +== Max antal demon + +News +== Nyheter + +Join game +== Spela + +FSAA samples +== FSAA-samplingar + +%d of %d servers, %d players +== %d av %d servrar, %d spelare + +Sound volume +== Volym + +Created: +== Skapad: + +Max Screenshots +== Max antal skärmdumpar + +Length: +== Längd + +Skin name +== + +Rifle +== Gevär + +Patterns +== + +Netversion: +== Nätversion: + +Map: +== Bana + +%d Bytes +== %d Bytes + +Coloration +== + +Info +== Info + +Hue +== Nyans + +Record demo +== Spela in demo + +Your skin +== Ditt skin + +Size: +== Storlek: + +Reset to defaults +== Återställ till standard + +Quit anyway? +== Avsluta i alla fall? + +Display Modes +== Skärmlägen + +Version: +== Version: + +Round +== Runda + +Lht. +== Ljusstyrka + +no limit +== Ingen gräns + +Quick search: +== Snabbsök: + +UI Color +== Gränssnittfärg + +Host address +== Serveraddress + +Crc: +== Crc: + +Alpha +== Alpha + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Nuvarande version: %s + +LAN +== LAN + +Name plates size +== Storlek på namnskyltar + +Type: +== Typ: + diff --git a/data/languages/turkish.txt b/data/languages/turkish.txt new file mode 100644 index 0000000000..5412bb5e7c --- /dev/null +++ b/data/languages/turkish.txt @@ -0,0 +1,1024 @@ +##### authors ##### +#originally created by: +# Learath2 +#modified by: +# Learath2 2011-06-27 18:54:08 +# Learath2 2011-08-21 00:12:50 +# Learath2 2011-09-07 15:59:20 +# Learath2 2012-01-01 22:54:29 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% yüklendi + +%ds left +== %ds kaldı + +%i minute left +== %i dakika kaldı + +%i minutes left +== %i dakika kaldı + +%i second left +== %i saniye kaldı + +%i seconds left +== %i saniye kaldı + +%s wins! +== %s kazandı! + +-Page %d- +== -Sayfa %d- + +Abort +== İptal + +Add +== Ekle + +Add Friend +== Arkadaş Ekle + +Address +== Adres + +All +== Hepsi + +Always show name plates +== Oyuncu isimlerini her zaman göster + +Are you sure that you want to delete the demo? +== Demo dosyasını silmek istediğinize emin misiniz? + +Are you sure that you want to quit? +== Çıkmak istediğinize emin misiniz? + +Are you sure that you want to remove the player from your friends list? +== Bu oyuncuyu arkadaş listenizden kaldırmak istediğinize emin misiniz? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Oyunu ilk açışınız olduğundan dolayı takma adınızı giriniz. Bir oyuna katılmadan önce ayarları incelemeniz ve isteklerinize göre özelleştirmeniz önerilir. + +Automatically record demos +== Demoları otomatik olarak kaydet + +Automatically take game over screenshot +== Otomatik olarak oyun bitişinde ekran görüntüsü al + +Blue team +== Mavi takım + +Blue team wins! +== Mavi takım kazandı! + +Body +== Gövde + +Call vote +== Oylama başlat + +Change settings +== Ayarları değiştir + +Chat +== Sohbet + +Clan +== Clan + +Client +== İstemci + +Close +== Kapat + +Compatible version +== Uyumlu versiyon + +Connect +== Bağlan + +Connecting to +== Bağlanılıyor + +Connection Problems... +== Bağlantı Hataları... + +Console +== Konsol + +Controls +== Kontroller + +Count players only +== Sadece oyuncuları say + +Country +== Ülke + +Current +== Şimdiki + +Custom colors +== Özel renkler + +Delete +== Sil + +Delete demo +== Demoyu sil + +Demo details +== Demo ayrıntıları + +Demofile: %s +== Demo dosyası + +Demos +== Demolar + +Disconnect +== Bağlantıyı kes + +Disconnected +== Bağlantı kesildi + +Downloading map +== Harita yükleniyor + +Draw! +== Berabere! + +Dynamic Camera +== Dinamik Kamera + +Emoticon +== Yüz ifadesi + +Enter +== Gir + +Error +== Hata + +Error loading demo +== Demo yüklenirken hata oluştu + +Favorite +== Favori + +Favorites +== Favoriler + +Feet +== Ayak + +Filter +== Filtre + +Fire +== Ateş + +Folder +== Dosya + +Force vote +== Oylamayı zorla + +Free-View +== Serbest Görüntü + +Friends +== Arkadaşlar + +Fullscreen +== Tam ekran + +Game +== Oyun + +Game info +== Oyun bilgisi + +Game over +== Oyun sonu + +Game type +== Oyun türü + +Game types: +== Oyun türleri: + +General +== Genel + +Graphics +== Grafik + +Grenade +== El bombası + +Hammer +== Çekiç + +Has people playing +== Insan bulunduran + +High Detail +== High Detail + +Hook +== Kanca + +Invalid Demo +== Geçersiz Demo + +Join blue +== Maviye katıl + +Join red +== Kırmızıya katıl + +Jump +== Zıpla + +Kick player +== Oyuncuyu At + +Language +== Dil + +Loading +== Yükleniyor + +MOTD +== MOTD + +Map +== Harita + +Maximum ping: +== Maximum ping + +Mouse sens. +== Mouse duyarlılığı + +Move left +== Sola git + +Move player to spectators +== Seyircilere katıl + +Move right +== Sağa git + +Movement +== Hareket + +Mute when not active +== Aktif değilken sessizleştir + +Name +== İsim + +Next weapon +== Sonraki silah + +Nickname +== Takma ad + +No +== Hayır + +No password +== Şifresiz + +No servers found +== Sunucu bulunamadı + +No servers match your filter criteria +== Filtre kriterlerinize uygun sunucu bulunamadı + +Ok +== Tamam + +Open +== Aç + +Parent Folder +== Üst dizin + +Password +== Şifre + +Password incorrect +== Hatalı şifre + +Ping +== Ping + +Pistol +== Tabanca + +Play +== Oynat + +Play background music +== Arkaplan müziğini çal + +Player +== Oyuncu + +Player options +== Oyuncu ayarları + +Players +== Oyuncular + +Please balance teams! +== Takımları dengeleyin! + +Prev. weapon +== Önceki silah + +Quality Textures +== Kaliteli dolgular + +Quit +== Çıkış + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Neden : + +Red team +== Kırmızı takım + +Red team wins! +== Kırmızı takım kazandı! + +Refresh +== Yenile + +Refreshing master servers +== Ana sunucu yenileniyor + +Remote console +== Uzak Konsol + +Remove +== Çıkar + +Remove friend +== Arkadaşı sil + +Rename +== Yeniden isimlendir + +Rename demo +== Demonun adını değiştir + +Reset filter +== Filitreleri varsayılanlara getir + +Sample rate +== Sample rate + +Score +== Skor + +Score board +== Skor tahtası + +Score limit +== Skor limiti + +Scoreboard +== Skor tahtası + +Screenshot +== Ekran alıntısı + +Server address: +== Sunucu adresi : + +Server details +== Sunucucu detayları + +Server filter +== Filtreler + +Server info +== Sunucu bilgisi + +Server not full +== Sunucu dolu değil + +Settings +== Ayarlar + +Shotgun +== Tüfek + +Show chat +== Sohbeti göster + +Show friends only +== Sadece arkadaşları göster + +Show ingame HUD +== Oyun içi HUD ı göster + +Show name plates +== Oyuncu isimlerini göster + +Show only supported +== Desteklenmeyen çözünürlükleri gizle + +Skins +== Skins + +Sound +== Ses + +Sound error +== Ses hatası + +Spectate +== İzle + +Spectate next +== Sıradakini izle + +Spectate previous +== Öncekini izle + +Spectator mode +== İzleyici modu + +Spectators +== İzleyiciler + +Standard gametype +== Standart oyun türleri + +Standard map +== Standart haritalar + +Stop record +== Kaydı durdur + +Strict gametype filter +== Sıkı oyun türü filtresi + +Sudden Death +== Ani ölüm + +Switch weapon on pickup +== Silah alındığıda yeni silaha geç + +Team +== Takım + +Team chat +== Takım sohbeti + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Teeworlds %s çıktı! www.teeworlds.com dan indirin! + +Texture Compression +== Dolgu sıkıştırması + +The audio device couldn't be initialised. +== Ses donanımı başlatılamadı. + +The server is running a non-standard tuning on a pure game type. +== Bu sunucu standart olmayan bir ayarı saf bir oyun türünde kullanıyor. + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== Editörde kaydedilmemiş bir harita var, kapatmadan önce kaydetmek isteyebilirsiniz. + +Time limit +== Süre limiti + +Time limit: %d min +== Süre limiti: %d min + +Try again +== Tekrar dene + +Type +== Tür + +Unable to delete the demo +== Demo silinemiyor + +Unable to rename the demo +== Demonun ismi değiştirilemiyor + +Use sounds +== Sesleri kullan + +Use team colors for name plates +== İsim etiketlerinde takım renklerini kullan + +V-Sync +== V-Sync + +Version +== Sürüm + +Vote command: +== Komut: + +Vote description: +== Açıklama : + +Vote no +== Olumsuz oy ver + +Vote yes +== Olumlu oy ver + +Voting +== Oylama + +Warmup +== Isınma + +Weapon +== Silah + +Welcome to Teeworlds +== Teeworlds'e hoşgeldiniz + +Yes +== Evet + +You must restart the game for all settings to take effect. +== Bütün ayarların aktif olması için oyunu yeniden başlatmalısınız. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Color +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Demo +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Personal +== + +Player country: +== + +Ready +== + +Record +== + +Recorded +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Servers +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Spectating +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +Volume +== + +Vote aborted +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== Yeni isim : + +Sat. +== Doygunluk + +Miscellaneous +== Çeşitli + +Internet +== Internet + +Max demos +== Maksimum Demo Sayısı + +News +== Haberler + +Join game +== Oyuna katıl + +FSAA samples +== FSAA örnekleri + +%d of %d servers, %d players +== %d/%d sunucu, %d oyuncu + +Sound volume +== Ses yüksekliği + +Created: +== Yaratıldı: + +Max Screenshots +== Maksimum Ekran Alıntısı Sayısı + +Length: +== Uzunluk: + +Skin name +== + +Rifle +== Lazer + +Patterns +== + +Netversion: +== Netversion : + +Map: +== Harita: + +%d Bytes +== %d Bayt + +Coloration +== + +Info +== Bilgi + +Hue +== Renk + +Record demo +== Demo kaydet + +Your skin +== Sizin görünüşünüz + +Size: +== Boyut: + + +== ## translated strings ##### + +Reset to defaults +== Varsayılanlara getir + +Quit anyway? +== Çıkmak istediğinize emin misiniz? + +Display Modes +== Çözünürlük + +Version: +== Versyon : + +Round +== Raund + +Lht. +== Parlaklık + +no limit +== limit yok + +Quick search: +== Hızlı Arama : + +UI Color +== Menü rengi + +Host address +== Sunucu adresi + +Crc: +== Crc: + +Alpha +== Şeffaflık + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Current version: %s +== Mevcut versiyon: %s + +LAN +== LAN + +Name plates size +== Boyut + +Type: +== Tür: + diff --git a/data/languages/ukrainian.txt b/data/languages/ukrainian.txt new file mode 100644 index 0000000000..cfc3d8cddf --- /dev/null +++ b/data/languages/ukrainian.txt @@ -0,0 +1,1018 @@ +##### authors ##### +#originally created by: +# .ua and Ivan +#modified by: +# 404_not_found 2011-07-30 19:50:58 +##### /authors ##### + +##### translated strings ##### + +%d%% loaded +== %d%% завантажено + +%ds left +== залишилось %d сек. + +Abort +== Відміна + +Address +== Адреса + +All +== Всі + +Always show name plates +== Завжди показувати ніки гравців + +Are you sure that you want to delete the demo? +== Ви впевнені, що хочете видалити демо? + +Are you sure that you want to quit? +== Ви дійсно бажаєте вийти? + +As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server. +== Так як це ваш перший запуск гри, будь ласка, введіть свій нік у полі нижче. Також рекомендуємо перевірити налаштування гри і змінити деякі з них, перед тим, як почати грати. + +Blue team +== Сині + +Blue team wins! +== Сині перемогли! + +Body +== Тіло + +Call vote +== Голосувати + +Chat +== Чат + +Close +== Зачинити + +Compatible version +== Сумісна версія + +Connect +== Під’єднатись + +Connecting to +== Підключення до + +Connection Problems... +== Проблеми зі з'єднанням... + +Console +== Консоль + +Controls +== Управління + +Current +== Поточний + +Custom colors +== Особистий колір + +Delete +== Видалити + +Demos +== Демо + +Disconnect +== Від'єднатись + +Disconnected +== Від'єднанно + +Downloading map +== Завантаження карти + +Draw! +== Нічия! + +Dynamic Camera +== Динамічна камера + +Emoticon +== Емоція + +Enter +== Вхід + +Error +== Помилка + +Error loading demo +== Помилка при завантаженні демо + +Favorite +== Обраний + +Favorites +== Обрані + +Feet +== Ноги + +Filter +== Фільтр + +Fire +== Постріл + +Folder +== Папка + +Force vote +== Форсувати + +Fullscreen +== Повноекранний режим + +Game +== Гра + +Game info +== Інфо по грі + +Game over +== Гра закінчена + +Game type +== Тип гри + +Game types: +== Типи гри: + +General +== Основні + +Graphics +== Графіка + +Grenade +== Ракетниця + +Hammer +== Молоток + +Has people playing +== Сервер не порожній + +High Detail +== Висока деталізація + +Hook +== Гак + +Invalid Demo +== Невірне демо + +Join blue +== За синіх + +Join red +== За червоних + +Jump +== Стрибок + +Language +== Мова + +Loading +== Завантаження + +MOTD +== MOTD + +Map +== Карта + +Maximum ping: +== Макс. пінг: + +Mouse sens. +== Чутлив. миші + +Move left +== Вліво + +Move right +== Вправо + +Movement +== Переміщення + +Mute when not active +== Глушити звуки, коли гра неактивна + +Name +== Ім'я + +Next weapon +== Наступ. зброя + +Nickname +== Нік + +No +== Ні + +No password +== Без пароля + +No servers found +== Сервера не знайдені + +No servers match your filter criteria +== Не знайдено ні одного сервара, який би відповідав вашим критеріям + +Ok +== Ок + +Open +== Відкрити + +Password +== Пароль + +Password incorrect +== пароль введено невірно + +Ping +== Пінг + +Pistol +== Пістолет + +Play +== Перегляд + +Player +== Гравець + +Players +== Гравці + +Please balance teams! +== Збалансуйте команди! + +Prev. weapon +== Попер. зброя + +Quality Textures +== Якісні текстури + +Quit +== Вихід + +Reason: +== Причина: + +Red team +== Червоні + +Red team wins! +== Червоні перемогли! + +Refresh +== Оновити + +Refreshing master servers +== Оновлення списку майстер-сервера + +Remote console +== Консоль сервера + +Reset filter +== Скинути фільтри + +Sample rate +== Частота + +Score +== Очки + +Score board +== Табло + +Score limit +== Ліміт очок + +Scoreboard +== Хід гри + +Screenshot +== Скріншот + +Server details +== Деталі сервера + +Server info +== Інформація + +Server not full +== Сервер не заповнений + +Settings +== Налаштування + +Shotgun +== Дробовик + +Show chat +== Показати чат + +Show name plates +== Показувати ніки гравців + +Show only supported +== Тільки підтримувані режими + +Skins +== Скіни + +Sound +== Звук + +Spectate +== Спостерігати + +Spectators +== Спостерігачі + +Standard gametype +== Стандартний тип гри + +Standard map +== Стандартна карта + +Stop record +== Зупинити запис + +Sudden Death +== Швидка смерть + +Switch weapon on pickup +== Перемикати зброю при підборі + +Team +== Команда + +Team chat +== Командний чат + +Teeworlds %s is out! Download it at www.teeworlds.com! +== Вийшла Teeworlds %s! Завантажуйте на www.teeworlds.com! + +Texture Compression +== Стиснення текстур + +The server is running a non-standard tuning on a pure game type. +== Сервер запущено з нестандартними налаштуваннями на стандартному типі гри. + +Time limit +== Ліміт часу + +Time limit: %d min +== Ліміт часу: %d хв + +Try again +== Спробувати знову + +Type +== Тип + +Use sounds +== Використовувати звуки + +V-Sync +== Вертикальна синхронізація + +Version +== Версія + +Vote no +== Проти + +Vote yes +== За + +Voting +== Голосування + +Warmup +== Розминка + +Weapon +== зброя + +Welcome to Teeworlds +== Ласкаво просимо в Teeworlds! + +Yes +== Так + +You must restart the game for all settings to take effect. +== Перезапустіть гру для застосування змін. + +##### needs translation ##### + +%.3f KiB +== + +%d player left +== + +%d player not ready +== + +%d players left +== + +%d players not ready +== + +%d servers, %d players +== + +%i minute left +== + +%i minutes left +== + +%i second left +== + +%i seconds left +== + +%s wins! +== + +'%s' called for vote to kick '%s' (%s) +== + +'%s' called for vote to move '%s' to spectators (%s) +== + +'%s' called vote to change server option '%s' (%s) +== + +'%s' entered and joined the %s +== + +'%s' has left the game +== + +'%s' has left the game (%s) +== + +'%s' joined the %s +== + +-Page %d- +== + +Add +== + +Add Friend +== + +Admin forced server option '%s' (%s) +== + +Admin forced vote no +== + +Admin forced vote yes +== + +Admin moved '%s' to spectator (%s) +== + +Alp: +== + +Anti Aliasing +== + +Are you sure that you want to remove the player from your friends list? +== + +Are you sure you want to rename the demo? +== + +Are you sure you want to save your skin? If a skin with this name already exists, it will be replaced. +== + +Automatically record demos +== + +Automatically take game over screenshot +== + +Back +== + +Basic +== + +Bodies +== + +Borderless window +== + +Change settings +== + +Clan +== + +Client +== + +Color +== + +Count players only +== + +Country +== + +Crc +== + +Created +== + +Custom +== + +Customize +== + +Decoration +== + +Delete demo +== + +Demo +== + +Demo details +== + +Demofile: %s +== + +Detail +== + +Editor +== + +Eyes +== + +Format +== + +Free-View +== + +Friends +== + +Game paused +== + +Game starts in +== + +Global +== + +Hands +== + +Host address: +== + +Hue: +== + +Join +== + +Joined +== + +Joined blue +== + +Joined red +== + +Kick player +== + +Kick voting requires %d players on the server +== + +Laser +== + +Length +== + +Letterbox +== + +Lgt: +== + +Local +== + +Local server +== + +Match +== + +Max +== + +Misc +== + +Move player to spectators +== + +Netversion +== + +Nickname is empty. +== + +Normal: +== + +Only %d active players are allowed +== + +Parent Folder +== + +Personal +== + +Play background music +== + +Player country: +== + +Player options +== + +REC %3d:%02d +== + +Ready +== + +Record +== + +Recorded +== + +Remove +== + +Remove friend +== + +Rename +== + +Rename demo +== + +Reset +== + +Resolutions +== + +Respawn +== + +Round over +== + +Round over! +== + +Sat: +== + +Save +== + +Save skin +== + +Screen +== + +Search: +== + +Server address: +== + +Server does not allow voting to kick players +== + +Server does not allow voting to move players to spectators +== + +Server filter +== + +Servers +== + +Show friends only +== + +Show ingame HUD +== + +Show only chat messages from friends +== + +Size +== + +Skin +== + +Sound error +== + +Spectate next +== + +Spectate previous +== + +Spectating +== + +Spectator mode +== + +Spectators aren't allowed to start a vote. +== + +Standard Gametype +== + +Strict gametype filter +== + +Tattoo +== + +Tattoos +== + +Team: +== + +Teams are locked +== + +Teams are locked. Time to wait before changing team: %02d:%02d +== + +Teams were locked +== + +Teams were unlocked +== + +Tee +== + +Texture +== + +The audio device couldn't be initialised. +== + +There's an unsaved map in the editor, you might want to save it before you quit the game. +== + +Unable to delete the demo +== + +Unable to rename the demo +== + +Use team colors for name plates +== + +Volume +== + +Vote aborted +== + +Vote command: +== + +Vote description: +== + +Vote failed +== + +Vote passed +== + +Wait for current vote to end before calling a new one. +== + +Wait for next round +== + +Wide +== + +You must wait %d seconds before making another vote +== + +blue +== + +blue team +== + +game +== + +red +== + +red team +== + +spectators +== + +wait for more players +== + +##### old translations ##### + +New name: +== + +Sat. +== Контраст. + +Max demos +== + +Internet +== Інтернет + +News +== Новини + +Are you sure you want to save your skin ? If a skin with this name already exists, it will be replaced. +== + +Rifle +== Лазер + +Join game +== Приєднатись до гри + +%d of %d servers, %d players +== %d/%d серверів, %d гравців + +Sound volume +== Гучність звуку + +Created: +== + +Max Screenshots +== + +Length: +== + +Skin name +== + +Coloration +== + +Patterns +== + +Netversion: +== + +Map: +== + +FSAA samples +== Семплів FSAA + +Your skin +== Ваш скін + +%d Bytes +== + +Info +== Інфо + +Version: +== + +Hue +== Відтінок + +Miscellaneous +== Додатково + +Reset to defaults +== Скинути налаштування + +Quit anyway? +== + +Display Modes +== Режими дисплея + +Round +== Раунд + +Lht. +== Яскравість + +no limit +== + +Quick search: +== Швидкий пошук: + +Size: +== + +UI Color +== колір інтерфейсу + +Host address +== Адреса сервера + +Crc: +== + +Alpha +== Прозор.. + +Current version: %s +== Поточна версія: %s + +LAN +== LAN + +Record demo +== Запис демо + +Name plates size +== + +Type: +== + diff --git a/data/mapres/bg_ddr.png b/data/mapres/bg_ddr.png deleted file mode 100644 index f343cda97e..0000000000 Binary files a/data/mapres/bg_ddr.png and /dev/null differ diff --git a/data/mapres/ddnet-start.png b/data/mapres/ddnet-start.png new file mode 100644 index 0000000000..7985e6465f Binary files /dev/null and b/data/mapres/ddnet-start.png differ diff --git a/data/mapres/ddnet-tiles.png b/data/mapres/ddnet-tiles.png new file mode 100644 index 0000000000..f52d177374 Binary files /dev/null and b/data/mapres/ddnet-tiles.png differ diff --git a/data/mapres/ddnet-walls.png b/data/mapres/ddnet-walls.png new file mode 100644 index 0000000000..df6ab4aea2 Binary files /dev/null and b/data/mapres/ddnet-walls.png differ diff --git a/data/mapres/entities.png b/data/mapres/entities.png index 988f5140af..bb218daa30 100644 Binary files a/data/mapres/entities.png and b/data/mapres/entities.png differ diff --git a/data/mapres/fadeout.png b/data/mapres/fadeout.png new file mode 100644 index 0000000000..1d15f974b1 Binary files /dev/null and b/data/mapres/fadeout.png differ diff --git a/data/mapres/generic_lamps.png b/data/mapres/generic_lamps.png new file mode 100644 index 0000000000..8c514e6185 Binary files /dev/null and b/data/mapres/generic_lamps.png differ diff --git a/data/mapres/generic_unhookable_0.7.png b/data/mapres/generic_unhookable_0.7.png new file mode 100644 index 0000000000..8b65aa9fc2 Binary files /dev/null and b/data/mapres/generic_unhookable_0.7.png differ diff --git a/data/mapres/grass_main.png b/data/mapres/grass_main.png index 2dde589606..5161884ab4 100644 Binary files a/data/mapres/grass_main.png and b/data/mapres/grass_main.png differ diff --git a/data/mapres/grass_main_0.7.png b/data/mapres/grass_main_0.7.png new file mode 100644 index 0000000000..544a6941b3 Binary files /dev/null and b/data/mapres/grass_main_0.7.png differ diff --git a/data/mapres/light.png b/data/mapres/light.png new file mode 100644 index 0000000000..c54254b054 Binary files /dev/null and b/data/mapres/light.png differ diff --git a/data/mapres/moon_0.7.png b/data/mapres/moon_0.7.png new file mode 100644 index 0000000000..1bc2e843e3 Binary files /dev/null and b/data/mapres/moon_0.7.png differ diff --git a/data/mapres/round-tiles.png b/data/mapres/round-tiles.png new file mode 100644 index 0000000000..8e8c35e724 Binary files /dev/null and b/data/mapres/round-tiles.png differ diff --git a/data/maps/Kobra.map b/data/maps/Kobra.map new file mode 100644 index 0000000000..21367b6b1f Binary files /dev/null and b/data/maps/Kobra.map differ diff --git a/data/maps/gravity.cfg b/data/maps/gravity.cfg new file mode 100644 index 0000000000..8f29abb9fd --- /dev/null +++ b/data/maps/gravity.cfg @@ -0,0 +1,11 @@ +tune_zone_leave 0 "Leaving beloved standards behind" + +tune_zone 1 hook_drag_accel -3 +tune_zone 1 gun_speed 100 +tune_zone 1 shotgun_strength 50 + +tune_zone_enter 2 "Space!\nMultiple lines!" +tune_zone 2 gravity 0 +tune_zone 2 air_friction 1 +tune_zone 2 ground_friction 1 +tune_zone_leave 2 "back2earth" diff --git a/data/maps/gravity.map b/data/maps/gravity.map new file mode 100644 index 0000000000..dfcca4abe3 Binary files /dev/null and b/data/maps/gravity.map differ diff --git a/data/skins/00!XeNoN!.png b/data/skins/00!XeNoN!.png new file mode 100644 index 0000000000..3535557f9d Binary files /dev/null and b/data/skins/00!XeNoN!.png differ diff --git a/data/skins/0011hvad.png b/data/skins/0011hvad.png new file mode 100644 index 0000000000..0ea626e268 Binary files /dev/null and b/data/skins/0011hvad.png differ diff --git a/data/skins/00260teecammo.png b/data/skins/00260teecammo.png new file mode 100644 index 0000000000..7c63778568 Binary files /dev/null and b/data/skins/00260teecammo.png differ diff --git a/data/skins/00Ahri.png b/data/skins/00Ahri.png new file mode 100644 index 0000000000..b41a0f33f2 Binary files /dev/null and b/data/skins/00Ahri.png differ diff --git a/data/skins/00Amol.png b/data/skins/00Amol.png new file mode 100644 index 0000000000..528b3e1d4f Binary files /dev/null and b/data/skins/00Amol.png differ diff --git a/data/skins/00Amsel.png b/data/skins/00Amsel.png new file mode 100644 index 0000000000..44218cc357 Binary files /dev/null and b/data/skins/00Amsel.png differ diff --git a/data/skins/00AngelAhri.png b/data/skins/00AngelAhri.png new file mode 100644 index 0000000000..c1d6dbe5cc Binary files /dev/null and b/data/skins/00AngelAhri.png differ diff --git a/data/skins/00Ante.png b/data/skins/00Ante.png new file mode 100644 index 0000000000..cab5721003 Binary files /dev/null and b/data/skins/00Ante.png differ diff --git a/data/skins/00Aoe.png b/data/skins/00Aoe.png new file mode 100644 index 0000000000..f6819fddfe Binary files /dev/null and b/data/skins/00Aoe.png differ diff --git a/data/skins/00Aoe4leg.png b/data/skins/00Aoe4leg.png new file mode 100644 index 0000000000..b80cea6c32 Binary files /dev/null and b/data/skins/00Aoe4leg.png differ diff --git a/data/skins/00AoeSkateboard.png b/data/skins/00AoeSkateboard.png new file mode 100644 index 0000000000..ffc70ee3e1 Binary files /dev/null and b/data/skins/00AoeSkateboard.png differ diff --git a/data/skins/00AppendixX.png b/data/skins/00AppendixX.png new file mode 100644 index 0000000000..c466e3227e Binary files /dev/null and b/data/skins/00AppendixX.png differ diff --git a/data/skins/00Artkis.png b/data/skins/00Artkis.png new file mode 100644 index 0000000000..2eb80d0d00 Binary files /dev/null and b/data/skins/00Artkis.png differ diff --git a/data/skins/00Aura.png b/data/skins/00Aura.png new file mode 100644 index 0000000000..77d82e58f2 Binary files /dev/null and b/data/skins/00Aura.png differ diff --git a/data/skins/00Beast.png b/data/skins/00Beast.png new file mode 100644 index 0000000000..8b0858f733 Binary files /dev/null and b/data/skins/00Beast.png differ diff --git a/data/skins/00Bibi.png b/data/skins/00Bibi.png new file mode 100644 index 0000000000..45cd6c85de Binary files /dev/null and b/data/skins/00Bibi.png differ diff --git a/data/skins/00Broke.png b/data/skins/00Broke.png new file mode 100644 index 0000000000..d5590fa4d4 Binary files /dev/null and b/data/skins/00Broke.png differ diff --git a/data/skins/00Cena.png b/data/skins/00Cena.png new file mode 100644 index 0000000000..ee24e5cf88 Binary files /dev/null and b/data/skins/00Cena.png differ diff --git a/data/skins/00Chicken.png b/data/skins/00Chicken.png new file mode 100644 index 0000000000..d7edd77d9a Binary files /dev/null and b/data/skins/00Chicken.png differ diff --git a/data/skins/00Crouser.png b/data/skins/00Crouser.png new file mode 100644 index 0000000000..341de04f83 Binary files /dev/null and b/data/skins/00Crouser.png differ diff --git a/data/skins/00Cruz.png b/data/skins/00Cruz.png new file mode 100644 index 0000000000..59c550345f Binary files /dev/null and b/data/skins/00Cruz.png differ diff --git a/data/skins/00DoNe.png b/data/skins/00DoNe.png new file mode 100644 index 0000000000..41b52de07c Binary files /dev/null and b/data/skins/00DoNe.png differ diff --git a/data/skins/00Elite.png b/data/skins/00Elite.png new file mode 100644 index 0000000000..49de996357 Binary files /dev/null and b/data/skins/00Elite.png differ diff --git a/data/skins/00Eviltee.png b/data/skins/00Eviltee.png new file mode 100644 index 0000000000..3249627b24 Binary files /dev/null and b/data/skins/00Eviltee.png differ diff --git a/data/skins/00FerzaK.png b/data/skins/00FerzaK.png new file mode 100644 index 0000000000..f1c9cd5112 Binary files /dev/null and b/data/skins/00FerzaK.png differ diff --git a/data/skins/00Fumm.png b/data/skins/00Fumm.png new file mode 100644 index 0000000000..b3829054ef Binary files /dev/null and b/data/skins/00Fumm.png differ diff --git a/data/skins/00Giese.png b/data/skins/00Giese.png new file mode 100644 index 0000000000..06459e676a Binary files /dev/null and b/data/skins/00Giese.png differ diff --git a/data/skins/00Hesse.png b/data/skins/00Hesse.png new file mode 100644 index 0000000000..70d38d0040 Binary files /dev/null and b/data/skins/00Hesse.png differ diff --git a/data/skins/00Infi.png b/data/skins/00Infi.png new file mode 100644 index 0000000000..c597a6176e Binary files /dev/null and b/data/skins/00Infi.png differ diff --git a/data/skins/00Jambi.png b/data/skins/00Jambi.png new file mode 100644 index 0000000000..710112ff21 Binary files /dev/null and b/data/skins/00Jambi.png differ diff --git a/data/skins/00Joggin.png b/data/skins/00Joggin.png new file mode 100644 index 0000000000..5972975d28 Binary files /dev/null and b/data/skins/00Joggin.png differ diff --git a/data/skins/00Kayumi.png b/data/skins/00Kayumi.png new file mode 100644 index 0000000000..d19c44c7bc Binary files /dev/null and b/data/skins/00Kayumi.png differ diff --git a/data/skins/00Kladdy.png b/data/skins/00Kladdy.png new file mode 100644 index 0000000000..c4769d5e83 Binary files /dev/null and b/data/skins/00Kladdy.png differ diff --git a/data/skins/00LanuX.png b/data/skins/00LanuX.png new file mode 100644 index 0000000000..0bb1c86a5e Binary files /dev/null and b/data/skins/00LanuX.png differ diff --git a/data/skins/00Lexin.png b/data/skins/00Lexin.png new file mode 100644 index 0000000000..7e7678aefe Binary files /dev/null and b/data/skins/00Lexin.png differ diff --git a/data/skins/00Mad.png b/data/skins/00Mad.png new file mode 100644 index 0000000000..b4e391f45a Binary files /dev/null and b/data/skins/00Mad.png differ diff --git a/data/skins/00Micro.png b/data/skins/00Micro.png new file mode 100644 index 0000000000..9aa0417699 Binary files /dev/null and b/data/skins/00Micro.png differ diff --git a/data/skins/00Mys.png b/data/skins/00Mys.png new file mode 100644 index 0000000000..8cb8800a0b Binary files /dev/null and b/data/skins/00Mys.png differ diff --git a/data/skins/00Nealson.png b/data/skins/00Nealson.png new file mode 100644 index 0000000000..1d2dc261a0 Binary files /dev/null and b/data/skins/00Nealson.png differ diff --git a/data/skins/00Necrid.png b/data/skins/00Necrid.png new file mode 100644 index 0000000000..faccd2964f Binary files /dev/null and b/data/skins/00Necrid.png differ diff --git a/data/skins/00NeoN.png b/data/skins/00NeoN.png new file mode 100644 index 0000000000..84af3cd805 Binary files /dev/null and b/data/skins/00NeoN.png differ diff --git a/data/skins/00No0B.png b/data/skins/00No0B.png new file mode 100644 index 0000000000..0ec6761801 Binary files /dev/null and b/data/skins/00No0B.png differ diff --git a/data/skins/00Omar.png b/data/skins/00Omar.png new file mode 100644 index 0000000000..e97a29d194 Binary files /dev/null and b/data/skins/00Omar.png differ diff --git a/data/skins/00Phaturia.png b/data/skins/00Phaturia.png new file mode 100644 index 0000000000..8c3e7b0241 Binary files /dev/null and b/data/skins/00Phaturia.png differ diff --git a/data/skins/00Pit.png b/data/skins/00Pit.png new file mode 100644 index 0000000000..4f2ac25aa9 Binary files /dev/null and b/data/skins/00Pit.png differ diff --git a/data/skins/00Pwner.png b/data/skins/00Pwner.png new file mode 100644 index 0000000000..186d29616e Binary files /dev/null and b/data/skins/00Pwner.png differ diff --git a/data/skins/00Ricardo.png b/data/skins/00Ricardo.png new file mode 100644 index 0000000000..bbda2a055e Binary files /dev/null and b/data/skins/00Ricardo.png differ diff --git a/data/skins/00Rico.png b/data/skins/00Rico.png new file mode 100644 index 0000000000..26bf3808d3 Binary files /dev/null and b/data/skins/00Rico.png differ diff --git a/data/skins/00Russs.png b/data/skins/00Russs.png new file mode 100644 index 0000000000..dbf630b155 Binary files /dev/null and b/data/skins/00Russs.png differ diff --git a/data/skins/00SBL.png b/data/skins/00SBL.png new file mode 100644 index 0000000000..93a0f1f563 Binary files /dev/null and b/data/skins/00SBL.png differ diff --git a/data/skins/00Shorefire.png b/data/skins/00Shorefire.png new file mode 100644 index 0000000000..006fe76c4c Binary files /dev/null and b/data/skins/00Shorefire.png differ diff --git a/data/skins/00Skeith.png b/data/skins/00Skeith.png new file mode 100644 index 0000000000..332d2790f4 Binary files /dev/null and b/data/skins/00Skeith.png differ diff --git a/data/skins/00Soap.png b/data/skins/00Soap.png new file mode 100644 index 0000000000..664da5517c Binary files /dev/null and b/data/skins/00Soap.png differ diff --git a/data/skins/00Spy090.png b/data/skins/00Spy090.png new file mode 100644 index 0000000000..e99f83ce39 Binary files /dev/null and b/data/skins/00Spy090.png differ diff --git a/data/skins/00Sui.png b/data/skins/00Sui.png new file mode 100644 index 0000000000..60a73d38df Binary files /dev/null and b/data/skins/00Sui.png differ diff --git a/data/skins/00Unknown.png b/data/skins/00Unknown.png new file mode 100644 index 0000000000..cb5569f43a Binary files /dev/null and b/data/skins/00Unknown.png differ diff --git a/data/skins/00Would.png b/data/skins/00Would.png new file mode 100644 index 0000000000..d298e2ea84 Binary files /dev/null and b/data/skins/00Would.png differ diff --git a/data/skins/00Yamado.png b/data/skins/00Yamado.png new file mode 100644 index 0000000000..bd72cfb5a5 Binary files /dev/null and b/data/skins/00Yamado.png differ diff --git a/data/skins/00Zed.png b/data/skins/00Zed.png new file mode 100644 index 0000000000..951668a331 Binary files /dev/null and b/data/skins/00Zed.png differ diff --git a/data/skins/00Zerodin.png b/data/skins/00Zerodin.png new file mode 100644 index 0000000000..e1c58d7c7a Binary files /dev/null and b/data/skins/00Zerodin.png differ diff --git a/data/skins/00Zgokee.png b/data/skins/00Zgokee.png new file mode 100644 index 0000000000..6f56fd38b0 Binary files /dev/null and b/data/skins/00Zgokee.png differ diff --git a/data/skins/00aMu.png b/data/skins/00aMu.png new file mode 100644 index 0000000000..76ac6a5b22 Binary files /dev/null and b/data/skins/00aMu.png differ diff --git a/data/skins/00iParano.png b/data/skins/00iParano.png new file mode 100644 index 0000000000..f519d7116a Binary files /dev/null and b/data/skins/00iParano.png differ diff --git a/data/skins/00kintaro.png b/data/skins/00kintaro.png new file mode 100644 index 0000000000..501463b75c Binary files /dev/null and b/data/skins/00kintaro.png differ diff --git a/data/skins/00lufu1.png b/data/skins/00lufu1.png new file mode 100644 index 0000000000..e74bbdcc7f Binary files /dev/null and b/data/skins/00lufu1.png differ diff --git a/data/skins/00mr3xX.png b/data/skins/00mr3xX.png new file mode 100644 index 0000000000..6577b7f66b Binary files /dev/null and b/data/skins/00mr3xX.png differ diff --git a/data/skins/00secreT.png b/data/skins/00secreT.png new file mode 100644 index 0000000000..97b182524d Binary files /dev/null and b/data/skins/00secreT.png differ diff --git a/data/skins/00skeptar.png b/data/skins/00skeptar.png new file mode 100644 index 0000000000..1f2d095f86 Binary files /dev/null and b/data/skins/00skeptar.png differ diff --git a/data/skins/00teeson.png b/data/skins/00teeson.png new file mode 100644 index 0000000000..4a4d4a97b6 Binary files /dev/null and b/data/skins/00teeson.png differ diff --git a/data/skins/00whis.png b/data/skins/00whis.png new file mode 100644 index 0000000000..8a7e6348e6 Binary files /dev/null and b/data/skins/00whis.png differ diff --git a/data/skins/01Inferno.png b/data/skins/01Inferno.png new file mode 100644 index 0000000000..504ce2c593 Binary files /dev/null and b/data/skins/01Inferno.png differ diff --git a/data/skins/Apish Coke.png b/data/skins/Apish Coke.png new file mode 100644 index 0000000000..3b2a905f64 Binary files /dev/null and b/data/skins/Apish Coke.png differ diff --git a/data/skins/Black Phantom heinrich5991.png b/data/skins/Black Phantom heinrich5991.png new file mode 100644 index 0000000000..40b871de3c Binary files /dev/null and b/data/skins/Black Phantom heinrich5991.png differ diff --git a/data/skins/Bloody FreaKy.png b/data/skins/Bloody FreaKy.png new file mode 100644 index 0000000000..de41cf7dd3 Binary files /dev/null and b/data/skins/Bloody FreaKy.png differ diff --git a/data/skins/DuMoH.png b/data/skins/DuMoH.png new file mode 100644 index 0000000000..df61b38cc6 Binary files /dev/null and b/data/skins/DuMoH.png differ diff --git a/data/skins/Hidden Assassin.png b/data/skins/Hidden Assassin.png new file mode 100644 index 0000000000..16dcfaf23e Binary files /dev/null and b/data/skins/Hidden Assassin.png differ diff --git a/data/skins/Shadow.png b/data/skins/Shadow.png new file mode 100644 index 0000000000..844f23c2dc Binary files /dev/null and b/data/skins/Shadow.png differ diff --git a/data/skins/Terrorist.png b/data/skins/Terrorist.png new file mode 100644 index 0000000000..f8419a65e8 Binary files /dev/null and b/data/skins/Terrorist.png differ diff --git a/data/skins/apple.png b/data/skins/apple.png deleted file mode 100644 index 55eb43f9d0..0000000000 Binary files a/data/skins/apple.png and /dev/null differ diff --git a/data/skins/aqua.png b/data/skins/aqua.png new file mode 100644 index 0000000000..04e20f26b4 Binary files /dev/null and b/data/skins/aqua.png differ diff --git a/data/skins/atlas_by_whis.png b/data/skins/atlas_by_whis.png new file mode 100644 index 0000000000..5130c6672d Binary files /dev/null and b/data/skins/atlas_by_whis.png differ diff --git a/data/skins/bauer.png b/data/skins/bauer.png new file mode 100644 index 0000000000..702d381afb Binary files /dev/null and b/data/skins/bauer.png differ diff --git a/data/skins/bomb.png b/data/skins/bomb.png new file mode 100644 index 0000000000..49d1a54729 Binary files /dev/null and b/data/skins/bomb.png differ diff --git a/data/skins/bunny.png b/data/skins/bunny.png new file mode 100644 index 0000000000..cdaf90a4c5 Binary files /dev/null and b/data/skins/bunny.png differ diff --git a/data/skins/caesar.png b/data/skins/caesar.png new file mode 100644 index 0000000000..caae7b067b Binary files /dev/null and b/data/skins/caesar.png differ diff --git a/data/skins/chera.png b/data/skins/chera.png new file mode 100644 index 0000000000..b7b53e9280 Binary files /dev/null and b/data/skins/chera.png differ diff --git a/data/skins/chinese_by_whis.png b/data/skins/chinese_by_whis.png new file mode 100644 index 0000000000..5c64176f61 Binary files /dev/null and b/data/skins/chinese_by_whis.png differ diff --git a/data/skins/demonlimekitty.png b/data/skins/demonlimekitty.png new file mode 100644 index 0000000000..83e507caf5 Binary files /dev/null and b/data/skins/demonlimekitty.png differ diff --git a/data/skins/dragon.png b/data/skins/dragon.png new file mode 100644 index 0000000000..88d12e3e74 Binary files /dev/null and b/data/skins/dragon.png differ diff --git a/data/skins/draw.png b/data/skins/draw.png new file mode 100644 index 0000000000..89308871c2 Binary files /dev/null and b/data/skins/draw.png differ diff --git a/data/skins/emo.png b/data/skins/emo.png new file mode 100644 index 0000000000..fc75fe2445 Binary files /dev/null and b/data/skins/emo.png differ diff --git a/data/skins/fuzzy_coala.png b/data/skins/fuzzy_coala.png new file mode 100644 index 0000000000..aa65dd2e3d Binary files /dev/null and b/data/skins/fuzzy_coala.png differ diff --git a/data/skins/hedgehog.png b/data/skins/hedgehog.png new file mode 100644 index 0000000000..00e629c5ee Binary files /dev/null and b/data/skins/hedgehog.png differ diff --git a/data/skins/kirby.png b/data/skins/kirby.png new file mode 100644 index 0000000000..a1323efed3 Binary files /dev/null and b/data/skins/kirby.png differ diff --git a/data/skins/lightbulb.png b/data/skins/lightbulb.png new file mode 100644 index 0000000000..0201b519de Binary files /dev/null and b/data/skins/lightbulb.png differ diff --git a/data/skins/masterchief.png b/data/skins/masterchief.png new file mode 100644 index 0000000000..c05a036b91 Binary files /dev/null and b/data/skins/masterchief.png differ diff --git a/data/skins/mike.png b/data/skins/mike.png new file mode 100644 index 0000000000..eca7c368e9 Binary files /dev/null and b/data/skins/mike.png differ diff --git a/data/skins/nanas.png b/data/skins/nanas.png new file mode 100644 index 0000000000..e665d0d997 Binary files /dev/null and b/data/skins/nanas.png differ diff --git a/data/skins/napoleon.png b/data/skins/napoleon.png new file mode 100644 index 0000000000..8201790612 Binary files /dev/null and b/data/skins/napoleon.png differ diff --git a/data/skins/nosey.png b/data/skins/nosey.png new file mode 100644 index 0000000000..d12fc8caf1 Binary files /dev/null and b/data/skins/nosey.png differ diff --git a/data/skins/oldman.png b/data/skins/oldman.png new file mode 100644 index 0000000000..d8e0f23d43 Binary files /dev/null and b/data/skins/oldman.png differ diff --git a/data/skins/pbody_by_whis.png b/data/skins/pbody_by_whis.png new file mode 100644 index 0000000000..76722a14a3 Binary files /dev/null and b/data/skins/pbody_by_whis.png differ diff --git a/data/skins/pepsi_tee.png b/data/skins/pepsi_tee.png deleted file mode 100644 index 909ab127c4..0000000000 Binary files a/data/skins/pepsi_tee.png and /dev/null differ diff --git a/data/skins/pepsican.png b/data/skins/pepsican.png deleted file mode 100644 index 2912f9213f..0000000000 Binary files a/data/skins/pepsican.png and /dev/null differ diff --git a/data/skins/red_bird.png b/data/skins/red_bird.png new file mode 100644 index 0000000000..9dbceb6e2f Binary files /dev/null and b/data/skins/red_bird.png differ diff --git a/data/skins/roman.png b/data/skins/roman.png new file mode 100644 index 0000000000..e523ba6d70 Binary files /dev/null and b/data/skins/roman.png differ diff --git a/data/skins/savage.png b/data/skins/savage.png new file mode 100644 index 0000000000..b111b46904 Binary files /dev/null and b/data/skins/savage.png differ diff --git a/data/skins/tails.png b/data/skins/tails.png new file mode 100644 index 0000000000..a72f93385e Binary files /dev/null and b/data/skins/tails.png differ diff --git a/data/skins/tank.png b/data/skins/tank.png new file mode 100644 index 0000000000..6b000ffb33 Binary files /dev/null and b/data/skins/tank.png differ diff --git a/data/skins/tauren.png b/data/skins/tauren.png new file mode 100644 index 0000000000..aa79a12d16 Binary files /dev/null and b/data/skins/tauren.png differ diff --git a/data/skins/teledipsy.png b/data/skins/teledipsy.png new file mode 100644 index 0000000000..860cfb0d0c Binary files /dev/null and b/data/skins/teledipsy.png differ diff --git a/data/skins/telelaalaa.png b/data/skins/telelaalaa.png new file mode 100644 index 0000000000..0777f599bf Binary files /dev/null and b/data/skins/telelaalaa.png differ diff --git a/data/skins/telepo.png b/data/skins/telepo.png new file mode 100644 index 0000000000..591da21fc1 Binary files /dev/null and b/data/skins/telepo.png differ diff --git a/data/skins/teletinkywinky.png b/data/skins/teletinkywinky.png new file mode 100644 index 0000000000..aad997fb3f Binary files /dev/null and b/data/skins/teletinkywinky.png differ diff --git a/data/skins/troll.png b/data/skins/troll.png new file mode 100644 index 0000000000..6553866bbb Binary files /dev/null and b/data/skins/troll.png differ diff --git a/data/skins/ts2_contest_skin.png b/data/skins/ts2_contest_skin.png new file mode 100644 index 0000000000..48925e418d Binary files /dev/null and b/data/skins/ts2_contest_skin.png differ diff --git a/datasrc/content.py b/datasrc/content.py index dcdcc106f4..0eb5b7c1de 100644 --- a/datasrc/content.py +++ b/datasrc/content.py @@ -233,6 +233,7 @@ def FileList(format, num): image_fileicons = Image("fileicons", "file_icons.png") image_guibuttons = Image("guibuttons", "gui_buttons.png") image_guiicons = Image("guiicons", "gui_icons.png") +image_arrow = Image("arrow", "arrow.png") container.images.Add(image_null) container.images.Add(image_game) @@ -248,6 +249,7 @@ def FileList(format, num): container.images.Add(image_fileicons) container.images.Add(image_guibuttons) container.images.Add(image_guiicons) +container.images.Add(image_arrow) container.pickups.Add(Pickup("health")) container.pickups.Add(Pickup("armor")) diff --git a/datasrc/datatypes.py b/datasrc/datatypes.py index 5441e3783c..e2bc5794e1 100644 --- a/datasrc/datatypes.py +++ b/datasrc/datatypes.py @@ -294,6 +294,14 @@ def emit_unpack(self): def emit_pack(self): return ["pPacker->AddString(%s, -1);" % self.name] +class NetStringHalfStrict(NetVariable): + def emit_declaration(self): + return ["const char *%s;"%self.name] + def emit_unpack(self): + return ["pMsg->%s = pUnpacker->GetString(CUnpacker::SANITIZE_CC);" % self.name] + def emit_pack(self): + return ["pPacker->AddString(%s, -1);" % self.name] + class NetStringStrict(NetVariable): def emit_declaration(self): return ["const char *%s;"%self.name] diff --git a/datasrc/network.py b/datasrc/network.py index fa06fc290f..f6ea4e9281 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -1,7 +1,7 @@ from datatypes import * Emotes = ["NORMAL", "PAIN", "HAPPY", "SURPRISE", "ANGRY", "BLINK"] -PlayerFlags = ["PLAYING", "IN_MENU", "CHATTING", "SCOREBOARD"] +PlayerFlags = ["PLAYING", "IN_MENU", "CHATTING", "SCOREBOARD", "AIM"] GameFlags = ["TEAMS", "FLAGS"] GameStateFlags = ["GAMEOVER", "SUDDENDEATH", "PAUSED"] @@ -229,9 +229,9 @@ ]), NetMessage("Sv_Chat", [ - NetIntRange("m_Team", 'TEAM_SPECTATORS', 'TEAM_BLUE'), + NetIntRange("m_Team", -2, 3), NetIntRange("m_ClientID", -1, 'MAX_CLIENTS-1'), - NetString("m_pMessage"), + NetStringHalfStrict("m_pMessage"), ]), NetMessage("Sv_KillMsg", [ @@ -294,7 +294,7 @@ ### Client messages NetMessage("Cl_Say", [ NetBool("m_Team"), - NetString("m_pMessage"), + NetStringHalfStrict("m_pMessage"), ]), NetMessage("Cl_SetTeam", [ @@ -340,45 +340,29 @@ NetStringStrict("m_Value"), NetStringStrict("m_Reason"), ]), - - NetMessage("Cl_IsDDRace", []), + + NetMessage("Cl_IsDDNet", []), NetMessage("Sv_DDRaceTime", [ NetIntAny("m_Time"), NetIntAny("m_Check"), NetIntRange("m_Finish", 0, 1), ]), - + NetMessage("Sv_Record", [ NetIntAny("m_ServerTimeBest"), NetIntAny("m_PlayerTimeBest"), ]), - + NetMessage("Sv_PlayerTime", [ NetIntAny("m_Time"), NetIntRange("m_ClientID", 0, 'MAX_CLIENTS-1'), ]), - - NetMessage("Cl_TeamsState", [ - NetIntAny("m_Tee0"), - NetIntAny("m_Tee1"), - NetIntAny("m_Tee2"), - NetIntAny("m_Tee3"), - NetIntAny("m_Tee4"), - NetIntAny("m_Tee5"), - NetIntAny("m_Tee6"), - NetIntAny("m_Tee7"), - NetIntAny("m_Tee8"), - NetIntAny("m_Tee9"), - NetIntAny("m_Tee10"), - NetIntAny("m_Tee11"), - NetIntAny("m_Tee12"), - NetIntAny("m_Tee13"), - NetIntAny("m_Tee14"), - NetIntAny("m_Tee15"), - ]), + + NetMessage("Sv_TeamsState", []), NetMessage("Cl_ShowOthers", [ NetBool("m_Show"), - ]), + ]) +# Can't add any NetMessages here! ] diff --git a/docs/conf/Data/ClassHierarchy.nd b/docs/conf/Data/ClassHierarchy.nd index c1a71232a3..5dbf421324 100644 Binary files a/docs/conf/Data/ClassHierarchy.nd and b/docs/conf/Data/ClassHierarchy.nd differ diff --git a/docs/conf/Data/ConfigFileInfo.nd b/docs/conf/Data/ConfigFileInfo.nd index c32f2955eb..74be4db23e 100644 Binary files a/docs/conf/Data/ConfigFileInfo.nd and b/docs/conf/Data/ConfigFileInfo.nd differ diff --git a/docs/conf/Data/FileInfo.nd b/docs/conf/Data/FileInfo.nd index cc241e75ac..662fe7de00 100644 --- a/docs/conf/Data/FileInfo.nd +++ b/docs/conf/Data/FileInfo.nd @@ -1,180 +1,254 @@ 1.4 C/C++ -/home/kma/code/teeworlds/trunk/src/engine/e_huffman.h 1222170110 1 /home/kma/code/teeworlds/trunk/src/engine/e_huffman.h -/home/kma/code/teeworlds/trunk/src/game/generated/gs_data.hpp 1222683446 0 /home/kma/code/teeworlds/trunk/src/game/generated/gs_data.hpp -/home/kma/code/teeworlds/trunk/src/game/client/ui.hpp 1220136086 0 /home/kma/code/teeworlds/trunk/src/game/client/ui.hpp -/home/kma/code/teeworlds/trunk/src/engine/client/ec_srvbrowse.c 1222683496 0 /home/kma/code/teeworlds/trunk/src/engine/client/ec_srvbrowse.c -/home/kma/code/teeworlds/trunk/src/game/editor/ed_editor.hpp 1220477125 0 /home/kma/code/teeworlds/trunk/src/game/editor/ed_editor.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_compression.c 1218717193 0 /home/kma/code/teeworlds/trunk/src/engine/e_compression.c -/home/kma/code/teeworlds/trunk/src/game/client/components/effects.hpp 1219856603 0 /home/kma/code/teeworlds/trunk/src/game/client/components/effects.hpp -/home/kma/code/teeworlds/trunk/src/game/editor/array.hpp 1213274598 0 /home/kma/code/teeworlds/trunk/src/game/editor/array.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_if_msg.h 1209633421 1 Messaging -/home/kma/code/teeworlds/trunk/src/game/server/gamemodes/dm.cpp 1220222024 0 /home/kma/code/teeworlds/trunk/src/game/server/gamemodes/dm.cpp -/home/kma/code/teeworlds/trunk/src/game/server/gamecontext.cpp 1222365211 0 /home/kma/code/teeworlds/trunk/src/game/server/gamecontext.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/hud.hpp 1222354048 0 /home/kma/code/teeworlds/trunk/src/game/client/components/hud.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_packer.h 1209633421 0 /home/kma/code/teeworlds/trunk/src/engine/e_packer.h -/home/kma/code/teeworlds/trunk/src/game/client/components/items.cpp 1220136174 0 /home/kma/code/teeworlds/trunk/src/game/client/components/items.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/camera.hpp 1219845973 0 /home/kma/code/teeworlds/trunk/src/game/client/components/camera.hpp -/home/kma/code/teeworlds/trunk/src/game/editor/ed_io.cpp 1212341007 0 /home/kma/code/teeworlds/trunk/src/game/editor/ed_io.cpp -/home/kma/code/teeworlds/trunk/src/engine/client/ec_font.h 1209633421 0 /home/kma/code/teeworlds/trunk/src/engine/client/ec_font.h -/home/kma/code/teeworlds/trunk/src/engine/e_msg.c 1212964729 0 /home/kma/code/teeworlds/trunk/src/engine/e_msg.c -/home/kma/code/teeworlds/trunk/src/game/client/components/menus_ingame.cpp 1222692848 0 /home/kma/code/teeworlds/trunk/src/game/client/components/menus_ingame.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_compression.h 1209633421 0 /home/kma/code/teeworlds/trunk/src/engine/e_compression.h -/home/kma/code/teeworlds/trunk/src/game/client/components/flow.cpp 1219782150 0 /home/kma/code/teeworlds/trunk/src/game/client/components/flow.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_engine.h 1220813504 0 /home/kma/code/teeworlds/trunk/src/engine/e_engine.h -/home/kma/code/teeworlds/trunk/src/game/generated/gc_data.cpp 1222683449 0 /home/kma/code/teeworlds/trunk/src/game/generated/gc_data.cpp -/home/kma/code/teeworlds/trunk/src/engine/docs/client_time.txt 1209633420 1 Time on the client -/home/kma/code/teeworlds/trunk/src/game/client/animstate.cpp 1219785741 0 /home/kma/code/teeworlds/trunk/src/game/client/animstate.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/damageind.hpp 1219841173 0 /home/kma/code/teeworlds/trunk/src/game/client/components/damageind.hpp -/home/kma/code/teeworlds/trunk/src/game/server/entities/laser.hpp 1218740976 0 /home/kma/code/teeworlds/trunk/src/game/server/entities/laser.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/nameplates.hpp 1220569205 0 /home/kma/code/teeworlds/trunk/src/game/client/components/nameplates.hpp -/home/kma/code/teeworlds/trunk/src/game/server/gamemodes/tdm.cpp 1220222010 0 /home/kma/code/teeworlds/trunk/src/game/server/gamemodes/tdm.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/broadcast.hpp 1219841173 0 /home/kma/code/teeworlds/trunk/src/game/client/components/broadcast.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_if_snd.h 1221327616 1 Sound -/home/kma/code/teeworlds/trunk/src/game/client/components/menus.cpp 1222640846 0 /home/kma/code/teeworlds/trunk/src/game/client/components/menus.cpp -/home/kma/code/teeworlds/trunk/src/game/server/gamemodes/ctf.hpp 1219777811 0 /home/kma/code/teeworlds/trunk/src/game/server/gamemodes/ctf.hpp -/home/kma/code/teeworlds/trunk/src/game/generated/gs_data.cpp 1222683446 0 /home/kma/code/teeworlds/trunk/src/game/generated/gs_data.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/motd.hpp 1220137292 0 /home/kma/code/teeworlds/trunk/src/game/client/components/motd.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_if_other.h 1222181143 1 Engine Interface -/home/kma/code/teeworlds/trunk/src/game/editor/ed_layer_game.cpp 1209633421 0 /home/kma/code/teeworlds/trunk/src/game/editor/ed_layer_game.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/mapimages.cpp 1220253155 0 /home/kma/code/teeworlds/trunk/src/game/client/components/mapimages.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_keynames.c 1222353741 0 /home/kma/code/teeworlds/trunk/src/engine/e_keynames.c -/home/kma/code/teeworlds/trunk/src/game/server/gamecontroller.cpp 1222194081 0 /home/kma/code/teeworlds/trunk/src/game/server/gamecontroller.cpp -/home/kma/code/teeworlds/trunk/src/game/client/ui.cpp 1220136096 0 /home/kma/code/teeworlds/trunk/src/game/client/ui.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/nameplates.cpp 1222164899 0 /home/kma/code/teeworlds/trunk/src/game/client/components/nameplates.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/broadcast.cpp 1220135264 0 /home/kma/code/teeworlds/trunk/src/game/client/components/broadcast.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_map.c 1218717325 0 /home/kma/code/teeworlds/trunk/src/engine/e_map.c -/home/kma/code/teeworlds/trunk/src/game/mapitems.hpp 1222183329 0 /home/kma/code/teeworlds/trunk/src/game/mapitems.hpp -/home/kma/code/teeworlds/trunk/src/game/gamecore.cpp 1222186019 0 /home/kma/code/teeworlds/trunk/src/game/gamecore.cpp -/home/kma/code/teeworlds/trunk/src/game/server/hooks.cpp 1222682379 0 /home/kma/code/teeworlds/trunk/src/game/server/hooks.cpp -/home/kma/code/teeworlds/trunk/src/game/layers.hpp 1218967138 0 /home/kma/code/teeworlds/trunk/src/game/layers.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/killmessages.hpp 1219841185 0 /home/kma/code/teeworlds/trunk/src/game/client/components/killmessages.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_console.c 1222181217 0 /home/kma/code/teeworlds/trunk/src/engine/e_console.c -/home/kma/code/teeworlds/trunk/src/game/client/components/sounds.cpp 1222029308 0 /home/kma/code/teeworlds/trunk/src/game/client/components/sounds.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/emoticon.cpp 1220569076 0 /home/kma/code/teeworlds/trunk/src/game/client/components/emoticon.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/skins.cpp 1219618613 0 /home/kma/code/teeworlds/trunk/src/game/client/components/skins.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/effects.cpp 1220135224 0 /home/kma/code/teeworlds/trunk/src/game/client/components/effects.cpp -/home/kma/code/teeworlds/trunk/src/game/server/player.hpp 1222356463 0 /home/kma/code/teeworlds/trunk/src/game/server/player.hpp -/home/kma/code/teeworlds/trunk/src/game/client/lineinput.hpp 1220087940 0 /home/kma/code/teeworlds/trunk/src/game/client/lineinput.hpp -/home/kma/code/teeworlds/trunk/src/engine/server/es_server.c 1222638818 0 /home/kma/code/teeworlds/trunk/src/engine/server/es_server.c -/home/kma/code/teeworlds/trunk/src/game/server/entities/projectile.hpp 1218741010 0 /home/kma/code/teeworlds/trunk/src/game/server/entities/projectile.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/debughud.cpp 1220135200 0 /home/kma/code/teeworlds/trunk/src/game/client/components/debughud.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/items.hpp 1219841173 0 /home/kma/code/teeworlds/trunk/src/game/client/components/items.hpp -/home/kma/code/teeworlds/trunk/src/game/server/player.cpp 1222273690 0 /home/kma/code/teeworlds/trunk/src/game/server/player.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/sounds.hpp 1221328048 0 /home/kma/code/teeworlds/trunk/src/game/client/components/sounds.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/maplist.hpp 1222691020 0 /home/kma/code/teeworlds/trunk/src/game/client/components/maplist.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_snapshot.c 1214463651 0 /home/kma/code/teeworlds/trunk/src/engine/e_snapshot.c -/home/kma/code/teeworlds/trunk/src/game/client/components/binds.hpp 1222354232 0 /home/kma/code/teeworlds/trunk/src/game/client/components/binds.hpp -/home/kma/code/teeworlds/trunk/src/game/server/entities/character.cpp 1222356921 0 /home/kma/code/teeworlds/trunk/src/game/server/entities/character.cpp -/home/kma/code/teeworlds/trunk/src/game/server/entities/laser.cpp 1222190271 0 /home/kma/code/teeworlds/trunk/src/game/server/entities/laser.cpp -/home/kma/code/teeworlds/trunk/src/engine/docs/snapshots.txt 1218450372 1 Snapshots -/home/kma/code/teeworlds/trunk/src/game/generated/gc_data.hpp 1222683447 0 /home/kma/code/teeworlds/trunk/src/game/generated/gc_data.hpp -/home/kma/code/teeworlds/trunk/src/game/generated/nethash.c 1222161195 0 /home/kma/code/teeworlds/trunk/src/game/generated/nethash.c -/home/kma/code/teeworlds/trunk/src/engine/e_ringbuffer.h 1219871251 0 /home/kma/code/teeworlds/trunk/src/engine/e_ringbuffer.h -/home/kma/code/teeworlds/trunk/src/game/client/gameclient.cpp 1222692136 0 /home/kma/code/teeworlds/trunk/src/game/client/gameclient.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/killmessages.cpp 1220198084 0 /home/kma/code/teeworlds/trunk/src/game/client/components/killmessages.cpp -/home/kma/code/teeworlds/trunk/src/game/server/entity.cpp 1218743197 0 /home/kma/code/teeworlds/trunk/src/game/server/entity.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/controls.cpp 1221290669 0 /home/kma/code/teeworlds/trunk/src/game/client/components/controls.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_if_gfx.h 1220477125 1 Graphics -/home/kma/code/teeworlds/trunk/src/game/client/components/motd.cpp 1220135205 0 /home/kma/code/teeworlds/trunk/src/game/client/components/motd.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_protocol.h 1220799767 0 /home/kma/code/teeworlds/trunk/src/engine/e_protocol.h -/home/kma/code/teeworlds/trunk/src/game/client/gameclient.hpp 1222691935 0 /home/kma/code/teeworlds/trunk/src/game/client/gameclient.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_datafile.c 1218717112 0 /home/kma/code/teeworlds/trunk/src/engine/e_datafile.c -/home/kma/code/teeworlds/trunk/src/engine/e_huffman.c 1222170780 0 /home/kma/code/teeworlds/trunk/src/engine/e_huffman.c -/home/kma/code/teeworlds/trunk/src/engine/docs/server_op.txt 1218450372 1 Server Operation -/home/kma/code/teeworlds/trunk/src/game/editor/ed_editor.cpp 1222182161 0 /home/kma/code/teeworlds/trunk/src/game/editor/ed_editor.cpp -/home/kma/code/teeworlds/trunk/src/game/client/gc_render.cpp 1220295093 0 /home/kma/code/teeworlds/trunk/src/game/client/gc_render.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/maplayers.hpp 1219841173 0 /home/kma/code/teeworlds/trunk/src/game/client/components/maplayers.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_if_client.h 1222031069 1 Client Interface -/home/kma/code/teeworlds/trunk/src/engine/e_keys.h 1222353741 0 /home/kma/code/teeworlds/trunk/src/engine/e_keys.h -/home/kma/code/teeworlds/trunk/src/game/client/components/console.hpp 1220569113 0 /home/kma/code/teeworlds/trunk/src/game/client/components/console.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_engine.c 1220822064 0 /home/kma/code/teeworlds/trunk/src/engine/e_engine.c -/home/kma/code/teeworlds/trunk/src/engine/e_if_modc.h 1209633421 1 Client Hooks -/home/kma/code/teeworlds/trunk/src/game/client/components/particles.cpp 1220138535 0 /home/kma/code/teeworlds/trunk/src/game/client/components/particles.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_ringbuffer.c 1218717337 0 /home/kma/code/teeworlds/trunk/src/engine/e_ringbuffer.c -/home/kma/code/teeworlds/trunk/src/game/server/gamemodes/dm.hpp 1220222050 0 /home/kma/code/teeworlds/trunk/src/game/server/gamemodes/dm.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_server_interface.h 1209633421 0 /home/kma/code/teeworlds/trunk/src/engine/e_server_interface.h -/home/kma/code/teeworlds/trunk/src/game/client/components/binds.cpp 1222354494 0 /home/kma/code/teeworlds/trunk/src/game/client/components/binds.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_packer.c 1218717159 0 /home/kma/code/teeworlds/trunk/src/engine/e_packer.c -/home/kma/code/teeworlds/trunk/src/game/client/components/particles.hpp 1219845940 0 /home/kma/code/teeworlds/trunk/src/game/client/components/particles.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_config.h 1209633421 0 /home/kma/code/teeworlds/trunk/src/engine/e_config.h -/home/kma/code/teeworlds/trunk/src/game/tuning.hpp 1218966967 0 /home/kma/code/teeworlds/trunk/src/game/tuning.hpp -/home/kma/code/teeworlds/trunk/src/game/server/gamecontext.hpp 1222356971 0 /home/kma/code/teeworlds/trunk/src/game/server/gamecontext.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/players.cpp 1222347355 0 /home/kma/code/teeworlds/trunk/src/game/client/components/players.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/controls.hpp 1221290643 0 /home/kma/code/teeworlds/trunk/src/game/client/components/controls.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_jobs.h 1220813546 0 /home/kma/code/teeworlds/trunk/src/engine/e_jobs.h -/home/kma/code/teeworlds/trunk/src/engine/e_memheap.c 1218717128 0 /home/kma/code/teeworlds/trunk/src/engine/e_memheap.c -/home/kma/code/teeworlds/trunk/src/game/client/lineinput.cpp 1219040530 0 /home/kma/code/teeworlds/trunk/src/game/client/lineinput.cpp -/home/kma/code/teeworlds/trunk/src/game/generated/createdir.txt 1209633421 0 /home/kma/code/teeworlds/trunk/src/game/generated/createdir.txt -/home/kma/code/teeworlds/trunk/src/game/client/components/maplist.cpp 1222692119 0 /home/kma/code/teeworlds/trunk/src/game/client/components/maplist.cpp -/home/kma/code/teeworlds/trunk/src/game/version.hpp 1218966879 0 /home/kma/code/teeworlds/trunk/src/game/version.hpp -/home/kma/code/teeworlds/trunk/src/game/collision.hpp 1222185632 0 /home/kma/code/teeworlds/trunk/src/game/collision.hpp -/home/kma/code/teeworlds/trunk/src/engine/client/ec_client.c 1222683366 0 /home/kma/code/teeworlds/trunk/src/engine/client/ec_client.c -/home/kma/code/teeworlds/trunk/src/game/generated/g_protocol.cpp 1222683446 0 /home/kma/code/teeworlds/trunk/src/game/generated/g_protocol.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_datafile.h 1209633421 0 /home/kma/code/teeworlds/trunk/src/engine/e_datafile.h -/home/kma/code/teeworlds/trunk/src/engine/e_linereader.h 1218717511 0 /home/kma/code/teeworlds/trunk/src/engine/e_linereader.h -/home/kma/code/teeworlds/trunk/src/game/server/entities/pickup.cpp 1220570254 0 /home/kma/code/teeworlds/trunk/src/game/server/entities/pickup.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/maplayers.cpp 1220253273 0 /home/kma/code/teeworlds/trunk/src/game/client/components/maplayers.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/hud.cpp 1222355982 0 /home/kma/code/teeworlds/trunk/src/game/client/components/hud.cpp -/home/kma/code/teeworlds/trunk/src/game/server/gamemodes/tdm.hpp 1218741462 0 /home/kma/code/teeworlds/trunk/src/game/server/gamemodes/tdm.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/menus_browser.cpp 1222640967 0 /home/kma/code/teeworlds/trunk/src/game/client/components/menus_browser.cpp -/home/kma/code/teeworlds/trunk/src/game/collision.cpp 1222185936 0 /home/kma/code/teeworlds/trunk/src/game/collision.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_snapshot.h 1218717534 0 /home/kma/code/teeworlds/trunk/src/engine/e_snapshot.h -/home/kma/code/teeworlds/trunk/src/engine/e_client_interface.h 1220135564 0 /home/kma/code/teeworlds/trunk/src/engine/e_client_interface.h -/home/kma/code/teeworlds/trunk/src/game/editor/ed_layer_tiles.cpp 1220827108 0 /home/kma/code/teeworlds/trunk/src/game/editor/ed_layer_tiles.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_network.h 1220214060 0 /home/kma/code/teeworlds/trunk/src/engine/e_network.h -/home/kma/code/teeworlds/trunk/src/game/client/components/emoticon.hpp 1220569077 0 /home/kma/code/teeworlds/trunk/src/game/client/components/emoticon.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_config.c 1222181231 0 /home/kma/code/teeworlds/trunk/src/engine/e_config.c -/home/kma/code/teeworlds/trunk/src/game/client/components/menus.hpp 1222681494 0 /home/kma/code/teeworlds/trunk/src/game/client/components/menus.hpp -/home/kma/code/teeworlds/trunk/src/game/server/gamemodes/ctf.cpp 1222190258 0 /home/kma/code/teeworlds/trunk/src/game/server/gamemodes/ctf.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_config_variables.h 1222181173 0 /home/kma/code/teeworlds/trunk/src/engine/e_config_variables.h -/home/kma/code/teeworlds/trunk/src/engine/docs/prediction.txt 1209633420 1 Prediction -/home/kma/code/teeworlds/trunk/src/game/client/components/voting.hpp 1222692628 0 /home/kma/code/teeworlds/trunk/src/game/client/components/voting.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/camera.cpp 1221209283 0 /home/kma/code/teeworlds/trunk/src/game/client/components/camera.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/damageind.cpp 1220136328 0 /home/kma/code/teeworlds/trunk/src/game/client/components/damageind.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/chat.hpp 1220569130 0 /home/kma/code/teeworlds/trunk/src/game/client/components/chat.hpp -/home/kma/code/teeworlds/trunk/src/game/editor/ed_popups.cpp 1209633421 0 /home/kma/code/teeworlds/trunk/src/game/editor/ed_popups.cpp -/home/kma/code/teeworlds/trunk/src/engine/client/ec_font.c 1218724640 0 /home/kma/code/teeworlds/trunk/src/engine/client/ec_font.c -/home/kma/code/teeworlds/trunk/src/game/client/component.hpp 1220568968 0 /home/kma/code/teeworlds/trunk/src/game/client/component.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/voting.cpp 1222692648 0 /home/kma/code/teeworlds/trunk/src/game/client/components/voting.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/debughud.hpp 1219855509 0 /home/kma/code/teeworlds/trunk/src/game/client/components/debughud.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/skins.hpp 1219841173 0 /home/kma/code/teeworlds/trunk/src/game/client/components/skins.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/scoreboard.hpp 1220569089 0 /home/kma/code/teeworlds/trunk/src/game/client/components/scoreboard.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_common_interface.h 1209633421 0 /home/kma/code/teeworlds/trunk/src/engine/e_common_interface.h -/home/kma/code/teeworlds/trunk/src/engine/e_if_mods.h 1209633421 1 Server Hooks -/home/kma/code/teeworlds/trunk/src/game/client/components/scoreboard.cpp 1220827108 0 /home/kma/code/teeworlds/trunk/src/game/client/components/scoreboard.cpp -/home/kma/code/teeworlds/trunk/src/game/server/gameworld.cpp 1220827108 0 /home/kma/code/teeworlds/trunk/src/game/server/gameworld.cpp -/home/kma/code/teeworlds/trunk/src/game/server/eventhandler.cpp 1222190459 0 /home/kma/code/teeworlds/trunk/src/game/server/eventhandler.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/chat.cpp 1220827108 0 /home/kma/code/teeworlds/trunk/src/game/client/components/chat.cpp -/home/kma/code/teeworlds/trunk/src/game/client/gc_render_map.cpp 1220135308 0 /home/kma/code/teeworlds/trunk/src/game/client/gc_render_map.cpp -/home/kma/code/teeworlds/trunk/src/game/server/gameworld.hpp 1218967107 1 Game World -/home/kma/code/teeworlds/trunk/src/game/gamecore.hpp 1222185988 0 /home/kma/code/teeworlds/trunk/src/game/gamecore.hpp -/home/kma/code/teeworlds/trunk/src/engine/client/ec_inp.c 1218724618 0 /home/kma/code/teeworlds/trunk/src/engine/client/ec_inp.c -/home/kma/code/teeworlds/trunk/src/engine/client/ec_snd.c 1222029620 0 /home/kma/code/teeworlds/trunk/src/engine/client/ec_snd.c -/home/kma/code/teeworlds/trunk/src/engine/e_if_inp.h 1209633421 1 Input -/home/kma/code/teeworlds/trunk/src/engine/e_jobs.c 1220818102 0 /home/kma/code/teeworlds/trunk/src/engine/e_jobs.c -/home/kma/code/teeworlds/trunk/src/game/client/components/menus_settings.cpp 1222353486 0 /home/kma/code/teeworlds/trunk/src/game/client/components/menus_settings.cpp -/home/kma/code/teeworlds/trunk/src/engine/client/ec_gfx.c 1220477125 0 /home/kma/code/teeworlds/trunk/src/engine/client/ec_gfx.c -/home/kma/code/teeworlds/trunk/src/game/server/entities/character.hpp 1222203546 0 /home/kma/code/teeworlds/trunk/src/game/server/entities/character.hpp -/home/kma/code/teeworlds/trunk/src/game/server/eventhandler.hpp 1218742466 0 /home/kma/code/teeworlds/trunk/src/game/server/eventhandler.hpp -/home/kma/code/teeworlds/trunk/src/game/client/clienthooks.cpp 1220568950 0 /home/kma/code/teeworlds/trunk/src/game/client/clienthooks.cpp -/home/kma/code/teeworlds/trunk/src/game/client/components/console.cpp 1220569111 0 /home/kma/code/teeworlds/trunk/src/game/client/components/console.cpp -/home/kma/code/teeworlds/trunk/src/game/variables.hpp 1222682334 0 /home/kma/code/teeworlds/trunk/src/game/variables.hpp -/home/kma/code/teeworlds/trunk/src/engine/e_linereader.c 1209633421 0 /home/kma/code/teeworlds/trunk/src/engine/e_linereader.c -/home/kma/code/teeworlds/trunk/src/engine/e_memheap.h 1209633421 0 /home/kma/code/teeworlds/trunk/src/engine/e_memheap.h -/home/kma/code/teeworlds/trunk/src/engine/e_console.h 1219858821 0 /home/kma/code/teeworlds/trunk/src/engine/e_console.h -/home/kma/code/teeworlds/trunk/src/engine/e_network.c 1222251355 0 /home/kma/code/teeworlds/trunk/src/engine/e_network.c -/home/kma/code/teeworlds/trunk/src/game/server/gamecontroller.hpp 1220827108 1 Game Controller -/home/kma/code/teeworlds/trunk/src/game/layers.cpp 1218967120 0 /home/kma/code/teeworlds/trunk/src/game/layers.cpp -/home/kma/code/teeworlds/trunk/src/engine/e_if_server.h 1220195032 1 Server Interface -/home/kma/code/teeworlds/trunk/src/game/client/animstate.hpp 1219785206 0 /home/kma/code/teeworlds/trunk/src/game/client/animstate.hpp -/home/kma/code/teeworlds/trunk/src/game/server/entities/projectile.cpp 1222190276 0 /home/kma/code/teeworlds/trunk/src/game/server/entities/projectile.cpp -/home/kma/code/teeworlds/trunk/src/game/editor/ed_layer_quads.cpp 1218738227 0 /home/kma/code/teeworlds/trunk/src/game/editor/ed_layer_quads.cpp -/home/kma/code/teeworlds/trunk/src/game/server/entities/pickup.hpp 1218740990 0 /home/kma/code/teeworlds/trunk/src/game/server/entities/pickup.hpp -/home/kma/code/teeworlds/trunk/src/game/server/entity.hpp 1222252624 1 Entity -/home/kma/code/teeworlds/trunk/src/game/generated/g_protocol.hpp 1222683446 0 /home/kma/code/teeworlds/trunk/src/game/generated/g_protocol.hpp -/home/kma/code/teeworlds/trunk/src/game/client/gc_render.hpp 1222182049 0 /home/kma/code/teeworlds/trunk/src/game/client/gc_render.hpp -/home/kma/code/teeworlds/trunk/src/engine/server/es_register.c 1218717909 0 /home/kma/code/teeworlds/trunk/src/engine/server/es_register.c -/home/kma/code/teeworlds/trunk/src/game/client/components/flow.hpp 1219841173 0 /home/kma/code/teeworlds/trunk/src/game/client/components/flow.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/mapimages.hpp 1220253106 0 /home/kma/code/teeworlds/trunk/src/game/client/components/mapimages.hpp -/home/kma/code/teeworlds/trunk/src/game/client/components/players.hpp 1219841173 0 /home/kma/code/teeworlds/trunk/src/game/client/components/players.hpp +/media/ddrace/src/game/client/components/hud.h 1391809699 0 /media/ddrace/src/game/client/components/hud.h +/media/ddrace/src/game/voting.h 1391797880 0 /media/ddrace/src/game/voting.h +/media/ddrace/src/game/server/teams.cpp 1392845289 0 /media/ddrace/src/game/server/teams.cpp +/media/ddrace/src/game/server/gamemodes/mod.h 1391797880 0 /media/ddrace/src/game/server/gamemodes/mod.h +/media/ddrace/src/engine/client/serverbrowser.h 1391797880 0 /media/ddrace/src/engine/client/serverbrowser.h +/media/ddrace/src/engine/client/keynames.h 1392218386 0 /media/ddrace/src/engine/client/keynames.h +/media/ddrace/src/game/editor/layer_tiles.cpp 1392462601 0 /media/ddrace/src/game/editor/layer_tiles.cpp +/media/ddrace/src/game/client/components/race_demo.cpp 1391797880 0 /media/ddrace/src/game/client/components/race_demo.cpp +/media/ddrace/src/engine/client/friends.h 1388496485 0 /media/ddrace/src/engine/client/friends.h +/media/ddrace/src/engine/server/register.cpp 1391797880 0 /media/ddrace/src/engine/server/register.cpp +/media/ddrace/src/game/client/components/items.cpp 1391797880 0 /media/ddrace/src/game/client/components/items.cpp +/media/ddrace/src/game/client/components/effects.cpp 1391797880 0 /media/ddrace/src/game/client/components/effects.cpp +/media/ddrace/src/game/client/components/race_demo.h 1391797880 0 /media/ddrace/src/game/client/components/race_demo.h +/media/ddrace/src/game/client/components/ghost.h 1391797880 0 /media/ddrace/src/game/client/components/ghost.h +/media/ddrace/src/game/client/components/emoticon.cpp 1392218386 0 /media/ddrace/src/game/client/components/emoticon.cpp +/media/ddrace/src/game/server/gamecontroller.h 1391797880 1 Game Controller +/media/ddrace/src/game/generated/server_data.cpp 1391818905 0 /media/ddrace/src/game/generated/server_data.cpp +/media/ddrace/src/game/client/lineinput.h 1392218386 0 /media/ddrace/src/game/client/lineinput.h +/media/ddrace/src/engine/kernel.h 1388496485 0 /media/ddrace/src/engine/kernel.h +/media/ddrace/src/game/client/components/countryflags.h 1391797880 0 /media/ddrace/src/game/client/components/countryflags.h +/media/ddrace/src/engine/client/client.cpp 1393378505 0 /media/ddrace/src/engine/client/client.cpp +/media/ddrace/src/game/client/components/sounds.cpp 1391797880 0 /media/ddrace/src/game/client/components/sounds.cpp +/media/ddrace/src/game/collision.cpp 1392212700 0 /media/ddrace/src/game/collision.cpp +/media/ddrace/src/engine/shared/filecollection.h 1388496485 0 /media/ddrace/src/engine/shared/filecollection.h +/media/ddrace/src/engine/server.h 1392315716 1 CClientInfo +/media/ddrace/src/game/client/components/camera.cpp 1391797880 0 /media/ddrace/src/game/client/components/camera.cpp +/media/ddrace/src/game/client/components/voting.h 1391797880 0 /media/ddrace/src/game/client/components/voting.h +/media/ddrace/src/engine/client/serverbrowser.cpp 1393376449 0 /media/ddrace/src/engine/client/serverbrowser.cpp +/media/ddrace/src/engine/shared/console.cpp 1392211746 0 /media/ddrace/src/engine/shared/console.cpp +/media/ddrace/src/game/client/components/killmessages.h 1388496485 0 /media/ddrace/src/game/client/components/killmessages.h +/media/ddrace/src/game/server/score/sql_score.cpp 1393359707 0 /media/ddrace/src/game/server/score/sql_score.cpp +/media/ddrace/src/engine/client/graphics_threaded.cpp 1392829239 0 /media/ddrace/src/engine/client/graphics_threaded.cpp +/media/ddrace/src/engine/shared/masterserver.cpp 1391797880 0 /media/ddrace/src/engine/shared/masterserver.cpp +/media/ddrace/src/engine/shared/ringbuffer.h 1388496485 0 /media/ddrace/src/engine/shared/ringbuffer.h +/media/ddrace/src/engine/client/backend_sdl.cpp 1392556202 0 /media/ddrace/src/engine/client/backend_sdl.cpp +/media/ddrace/src/game/server/entities/dragger.h 1391797880 0 /media/ddrace/src/game/server/entities/dragger.h +/media/ddrace/src/game/client/components/broadcast.h 1391797880 0 /media/ddrace/src/game/client/components/broadcast.h +/media/ddrace/src/engine/shared/protocol.h 1392842909 0 /media/ddrace/src/engine/shared/protocol.h +/media/ddrace/src/engine/shared/network_conn.cpp 1391797880 0 /media/ddrace/src/engine/shared/network_conn.cpp +/media/ddrace/src/engine/docs/snapshots.txt 1388496485 1 Snapshots +/media/ddrace/src/game/client/components/skins.h 1391797880 0 /media/ddrace/src/game/client/components/skins.h +/media/ddrace/src/game/editor/editor.cpp 1392555915 0 /media/ddrace/src/game/editor/editor.cpp +/media/ddrace/src/engine/shared/config_variables.h 1393468713 0 /media/ddrace/src/engine/shared/config_variables.h +/media/ddrace/src/game/layers.cpp 1391797880 0 /media/ddrace/src/game/layers.cpp +/media/ddrace/src/game/server/entities/flag.cpp 1391797880 0 /media/ddrace/src/game/server/entities/flag.cpp +/media/ddrace/src/game/editor/editor.h 1392218386 0 /media/ddrace/src/game/editor/editor.h +/media/ddrace/src/game/layers.h 1391797880 0 /media/ddrace/src/game/layers.h +/media/ddrace/src/engine/client/text.cpp 1391797880 0 /media/ddrace/src/engine/client/text.cpp +/media/ddrace/src/engine/client/client.h 1392218386 0 /media/ddrace/src/engine/client/client.h +/media/ddrace/src/game/server/entities/character.h 1392843858 0 /media/ddrace/src/game/server/entities/character.h +/media/ddrace/src/game/gamecore.cpp 1391797880 0 /media/ddrace/src/game/gamecore.cpp +/media/ddrace/src/engine/docs/client_time.txt 1388496485 1 Time on the client +/media/ddrace/src/game/client/components/scoreboard.h 1391797880 0 /media/ddrace/src/game/client/components/scoreboard.h +/media/ddrace/src/engine/shared/jobs.cpp 1391797880 0 /media/ddrace/src/engine/shared/jobs.cpp +/media/ddrace/src/engine/engine.h 1388496485 0 /media/ddrace/src/engine/engine.h +/media/ddrace/src/game/client/components/debughud.h 1388496485 0 /media/ddrace/src/game/client/components/debughud.h +/media/ddrace/src/engine/shared/map.cpp 1391797880 0 /media/ddrace/src/engine/shared/map.cpp +/media/ddrace/src/game/client/components/countryflags.cpp 1391797880 0 /media/ddrace/src/game/client/components/countryflags.cpp +/media/ddrace/src/game/server/gameworld.cpp 1392207946 0 /media/ddrace/src/game/server/gameworld.cpp +/media/ddrace/src/game/server/gamecontext.cpp 1393114116 0 /media/ddrace/src/game/server/gamecontext.cpp +/media/ddrace/src/engine/shared/datafile.h 1391797880 0 /media/ddrace/src/engine/shared/datafile.h +/media/ddrace/src/engine/shared/memheap.cpp 1391797880 0 /media/ddrace/src/engine/shared/memheap.cpp +/media/ddrace/src/engine/shared/datafile.cpp 1391797880 0 /media/ddrace/src/engine/shared/datafile.cpp +/media/ddrace/src/game/client/components/nameplates.h 1391797880 0 /media/ddrace/src/game/client/components/nameplates.h +/media/ddrace/src/engine/shared/econ.cpp 1388714242 0 /media/ddrace/src/engine/shared/econ.cpp +/media/ddrace/src/game/generated/protocol.h 1391798794 0 /media/ddrace/src/game/generated/protocol.h +/media/ddrace/src/engine/client/backend_sdl.h 1392556202 0 /media/ddrace/src/engine/client/backend_sdl.h +/media/ddrace/src/game/client/components/menus_settings.cpp 1392326709 0 /media/ddrace/src/game/client/components/menus_settings.cpp +/media/ddrace/src/engine/shared/mapchecker.h 1388496485 0 /media/ddrace/src/engine/shared/mapchecker.h +/media/ddrace/src/game/client/gameclient.h 1392212700 0 /media/ddrace/src/game/client/gameclient.h +/media/ddrace/src/game/client/ui.h 1391797880 0 /media/ddrace/src/game/client/ui.h +/media/ddrace/src/game/server/gamemodes/tdm.cpp 1391797880 0 /media/ddrace/src/game/server/gamemodes/tdm.cpp +/media/ddrace/src/engine/shared/storage.cpp 1392761626 0 /media/ddrace/src/engine/shared/storage.cpp +/media/ddrace/src/engine/shared/network.cpp 1391797880 0 /media/ddrace/src/engine/shared/network.cpp +/media/ddrace/src/engine/client/sound.cpp 1391797880 0 /media/ddrace/src/engine/client/sound.cpp +/media/ddrace/src/game/collision.h 1391984115 0 /media/ddrace/src/game/collision.h +/media/ddrace/src/engine/masterserver.h 1391797880 0 /media/ddrace/src/engine/masterserver.h +/media/ddrace/src/engine/client/input.cpp 1392218386 0 /media/ddrace/src/engine/client/input.cpp +/media/ddrace/src/game/client/components/binds.cpp 1392218386 0 /media/ddrace/src/game/client/components/binds.cpp +/media/ddrace/src/game/version.h 1393380317 0 /media/ddrace/src/game/version.h +/media/ddrace/src/game/client/render.h 1391797880 0 /media/ddrace/src/game/client/render.h +/media/ddrace/src/game/server/entities/laser.cpp 1391797880 0 /media/ddrace/src/game/server/entities/laser.cpp +/media/ddrace/src/engine/shared/huffman.h 1388496485 1 /media/ddrace/src/engine/shared/huffman.h +/media/ddrace/src/engine/shared/compression.h 1388496485 0 /media/ddrace/src/engine/shared/compression.h +/media/ddrace/src/game/client/components/console.h 1392218386 0 /media/ddrace/src/game/client/components/console.h +/media/ddrace/src/game/server/eventhandler.cpp 1391797880 0 /media/ddrace/src/game/server/eventhandler.cpp +/media/ddrace/src/game/editor/layer_quads.cpp 1391797880 0 /media/ddrace/src/game/editor/layer_quads.cpp +/media/ddrace/src/game/teamscore.h 1391797880 0 /media/ddrace/src/game/teamscore.h +/media/ddrace/src/game/client/ui.cpp 1392218386 0 /media/ddrace/src/game/client/ui.cpp +/media/ddrace/src/game/server/gamemodes/dm.cpp 1391797880 0 /media/ddrace/src/game/server/gamemodes/dm.cpp +/media/ddrace/src/game/client/components/camera.h 1391797880 0 /media/ddrace/src/game/client/components/camera.h +/media/ddrace/src/engine/storage.h 1388496485 0 /media/ddrace/src/engine/storage.h +/media/ddrace/src/engine/message.h 1391797880 0 /media/ddrace/src/engine/message.h +/media/ddrace/src/game/client/components/spectator.cpp 1392218386 0 /media/ddrace/src/game/client/components/spectator.cpp +/media/ddrace/src/game/client/components/items.h 1391797880 0 /media/ddrace/src/game/client/components/items.h +/media/ddrace/src/game/client/animstate.h 1388496485 0 /media/ddrace/src/game/client/animstate.h +/media/ddrace/src/game/editor/popups.cpp 1392337026 0 /media/ddrace/src/game/editor/popups.cpp +/media/ddrace/src/game/server/gamemodes/ctf.h 1391797880 0 /media/ddrace/src/game/server/gamemodes/ctf.h +/media/ddrace/src/game/client/components/mapimages.cpp 1391797880 0 /media/ddrace/src/game/client/components/mapimages.cpp +/media/ddrace/src/engine/keys.h 1392218386 0 /media/ddrace/src/engine/keys.h +/media/ddrace/src/engine/editor.h 1390096028 0 /media/ddrace/src/engine/editor.h +/media/ddrace/src/engine/shared/snapshot.h 1391797880 0 /media/ddrace/src/engine/shared/snapshot.h +/media/ddrace/src/game/editor/auto_map.cpp 1391797880 0 /media/ddrace/src/game/editor/auto_map.cpp +/media/ddrace/src/game/server/gamemodes/ctf.cpp 1391797880 0 /media/ddrace/src/game/server/gamemodes/ctf.cpp +/media/ddrace/src/game/client/components/maplayers.cpp 1392557856 0 /media/ddrace/src/game/client/components/maplayers.cpp +/media/ddrace/src/game/server/entities/plasma.h 1391797880 0 /media/ddrace/src/game/server/entities/plasma.h +/media/ddrace/src/game/server/ddracechat.h 1392840843 0 /media/ddrace/src/game/server/ddracechat.h +/media/ddrace/src/engine/shared/config.h 1391797880 0 /media/ddrace/src/engine/shared/config.h +/media/ddrace/src/game/client/components/motd.h 1388496485 0 /media/ddrace/src/game/client/components/motd.h +/media/ddrace/src/game/generated/client_data.cpp 1391798794 0 /media/ddrace/src/game/generated/client_data.cpp +/media/ddrace/src/game/client/components/scoreboard.cpp 1391809699 0 /media/ddrace/src/game/client/components/scoreboard.cpp +/media/ddrace/src/game/client/lineinput.cpp 1392218386 0 /media/ddrace/src/game/client/lineinput.cpp +/media/ddrace/src/game/server/gameworld.h 1391797880 1 Game World +/media/ddrace/src/engine/shared/engine.cpp 1388496485 0 /media/ddrace/src/engine/shared/engine.cpp +/media/ddrace/src/engine/demo.h 1391797880 0 /media/ddrace/src/engine/demo.h +/media/ddrace/src/engine/shared/netban.cpp 1388714242 0 /media/ddrace/src/engine/shared/netban.cpp +/media/ddrace/src/game/client/components/maplayers.h 1391797880 0 /media/ddrace/src/game/client/components/maplayers.h +/media/ddrace/src/engine/server/fifoconsole.cpp 1391797880 0 /media/ddrace/src/engine/server/fifoconsole.cpp +/media/ddrace/src/game/server/ddracecommands.cpp 1392212700 0 /media/ddrace/src/game/server/ddracecommands.cpp +/media/ddrace/src/game/server/teams.h 1391797880 0 /media/ddrace/src/game/server/teams.h +/media/ddrace/src/game/server/score/file_score.h 1392834716 0 /media/ddrace/src/game/server/score/file_score.h +/media/ddrace/src/engine/client/input.h 1392218386 0 /media/ddrace/src/engine/client/input.h +/media/ddrace/src/engine/shared/network.h 1391797880 0 /media/ddrace/src/engine/shared/network.h +/media/ddrace/src/engine/shared/console.h 1391797880 0 /media/ddrace/src/engine/shared/console.h +/media/ddrace/src/game/server/entities/projectile.cpp 1391797880 0 /media/ddrace/src/game/server/entities/projectile.cpp +/media/ddrace/src/engine/shared/compression.cpp 1388496485 0 /media/ddrace/src/engine/shared/compression.cpp +/media/ddrace/src/engine/shared/filecollection.cpp 1388496485 0 /media/ddrace/src/engine/shared/filecollection.cpp +/media/ddrace/src/engine/shared/snapshot.cpp 1392316357 0 /media/ddrace/src/engine/shared/snapshot.cpp +/media/ddrace/src/game/server/eventhandler.h 1391797880 0 /media/ddrace/src/game/server/eventhandler.h +/media/ddrace/src/game/client/components/chat.cpp 1392218386 0 /media/ddrace/src/game/client/components/chat.cpp +/media/ddrace/src/engine/server/server.h 1391797880 0 /media/ddrace/src/engine/server/server.h +/media/ddrace/src/engine/config.h 1388496485 0 /media/ddrace/src/engine/config.h +/media/ddrace/src/game/gamecore.h 1392842445 0 /media/ddrace/src/game/gamecore.h +/media/ddrace/src/game/server/entities/gun.h 1391797880 0 /media/ddrace/src/game/server/entities/gun.h +/media/ddrace/src/engine/shared/packer.h 1388496485 0 /media/ddrace/src/engine/shared/packer.h +/media/ddrace/src/game/server/player.h 1393003614 0 /media/ddrace/src/game/server/player.h +/media/ddrace/src/game/client/components/skins.cpp 1392760325 0 /media/ddrace/src/game/client/components/skins.cpp +/media/ddrace/src/game/server/gamemodes/DDRace.h 1391797880 0 /media/ddrace/src/game/server/gamemodes/DDRace.h +/media/ddrace/src/game/server/score.h 1392834704 0 /media/ddrace/src/game/server/score.h +/media/ddrace/src/game/client/components/chat.h 1392218386 0 /media/ddrace/src/game/client/components/chat.h +/media/ddrace/src/game/generated/nethash.cpp 1392842990 0 /media/ddrace/src/game/generated/nethash.cpp +/media/ddrace/src/engine/shared/network_console_conn.cpp 1388496485 0 /media/ddrace/src/engine/shared/network_console_conn.cpp +/media/ddrace/src/game/server/entities/character.cpp 1393361384 0 /media/ddrace/src/game/server/entities/character.cpp +/media/ddrace/src/game/server/gamemodes/tdm.h 1391797880 0 /media/ddrace/src/game/server/gamemodes/tdm.h +/media/ddrace/src/engine/shared/linereader.cpp 1388496485 0 /media/ddrace/src/engine/shared/linereader.cpp +/media/ddrace/src/game/client/components/flow.cpp 1391797880 0 /media/ddrace/src/game/client/components/flow.cpp +/media/ddrace/src/engine/shared/packer.cpp 1391797880 0 /media/ddrace/src/engine/shared/packer.cpp +/media/ddrace/src/game/editor/layer_game.cpp 1388496485 0 /media/ddrace/src/game/editor/layer_game.cpp +/media/ddrace/src/engine/shared/network_console.cpp 1388496485 0 /media/ddrace/src/engine/shared/network_console.cpp +/media/ddrace/src/engine/shared/demo.cpp 1392385479 0 /media/ddrace/src/engine/shared/demo.cpp +/media/ddrace/src/game/server/entities/projectile.h 1391797880 0 /media/ddrace/src/game/server/entities/projectile.h +/media/ddrace/src/game/mapitems.h 1392232379 0 /media/ddrace/src/game/mapitems.h +/media/ddrace/src/engine/shared/netban.h 1388714242 0 /media/ddrace/src/engine/shared/netban.h +/media/ddrace/src/game/client/components/emoticon.h 1388496485 0 /media/ddrace/src/game/client/components/emoticon.h +/media/ddrace/src/game/client/components/hud.cpp 1391809699 0 /media/ddrace/src/game/client/components/hud.cpp +/media/ddrace/src/game/server/entities/door.cpp 1391797880 0 /media/ddrace/src/game/server/entities/door.cpp +/media/ddrace/src/game/generated/protocol.cpp 1392842990 0 /media/ddrace/src/game/generated/protocol.cpp +/media/ddrace/src/game/server/gamemodes/DDRace.cpp 1391797880 0 /media/ddrace/src/game/server/gamemodes/DDRace.cpp +/media/ddrace/src/engine/input.h 1392218386 0 /media/ddrace/src/engine/input.h +/media/ddrace/src/engine/console.h 1391797880 0 /media/ddrace/src/engine/console.h +/media/ddrace/src/engine/shared/mapchecker.cpp 1391797880 0 /media/ddrace/src/engine/shared/mapchecker.cpp +/media/ddrace/src/game/client/components/debughud.cpp 1391797880 0 /media/ddrace/src/game/client/components/debughud.cpp +/media/ddrace/src/engine/shared/config.cpp 1392423241 0 /media/ddrace/src/engine/shared/config.cpp +/media/ddrace/src/game/client/components/players.cpp 1392675137 0 /media/ddrace/src/game/client/components/players.cpp +/media/ddrace/src/engine/graphics.h 1392218386 1 /media/ddrace/src/engine/graphics.h +/media/ddrace/src/engine/docs/server_op.txt 1388496485 1 Server Operation +/media/ddrace/src/game/client/components/menus.h 1393371321 0 /media/ddrace/src/game/client/components/menus.h +/media/ddrace/src/game/client/components/mapimages.h 1391797880 0 /media/ddrace/src/game/client/components/mapimages.h +/media/ddrace/src/game/client/render.cpp 1391797880 0 /media/ddrace/src/game/client/render.cpp +/media/ddrace/src/game/localization.h 1391797880 0 /media/ddrace/src/game/localization.h +/media/ddrace/src/game/server/entities/pickup.h 1391797880 0 /media/ddrace/src/game/server/entities/pickup.h +/media/ddrace/src/game/client/components/spectator.h 1392212700 0 /media/ddrace/src/game/client/components/spectator.h +/media/ddrace/src/game/server/gamemodes/gamemode.h 1391797880 0 /media/ddrace/src/game/server/gamemodes/gamemode.h +/media/ddrace/src/game/server/entities/light.h 1391797880 0 /media/ddrace/src/game/server/entities/light.h +/media/ddrace/src/game/server/entity.cpp 1391797880 0 /media/ddrace/src/game/server/entity.cpp +/media/ddrace/src/engine/map.h 1391797880 0 /media/ddrace/src/engine/map.h +/media/ddrace/src/game/generated/server_data.h 1391818905 0 /media/ddrace/src/game/generated/server_data.h +/media/ddrace/src/engine/server/server.cpp 1392315716 0 /media/ddrace/src/engine/server/server.cpp +/media/ddrace/src/engine/sound.h 1391797880 0 /media/ddrace/src/engine/sound.h +/media/ddrace/src/engine/shared/memheap.h 1391797880 0 /media/ddrace/src/engine/shared/memheap.h +/media/ddrace/src/engine/shared/linereader.h 1388496485 0 /media/ddrace/src/engine/shared/linereader.h +/media/ddrace/src/engine/client/graphics_threaded.h 1392763818 0 /media/ddrace/src/engine/client/graphics_threaded.h +/media/ddrace/src/game/server/entities/light.cpp 1391797880 0 /media/ddrace/src/game/server/entities/light.cpp +/media/ddrace/src/engine/server/fifoconsole.h 1391797880 0 /media/ddrace/src/engine/server/fifoconsole.h +/media/ddrace/src/game/server/ddracechat.cpp 1392841958 0 /media/ddrace/src/game/server/ddracechat.cpp +/media/ddrace/src/game/localization.cpp 1391797880 0 /media/ddrace/src/game/localization.cpp +/media/ddrace/src/game/client/components/particles.h 1388496485 0 /media/ddrace/src/game/client/components/particles.h +/media/ddrace/src/game/client/gameclient.cpp 1393379293 0 /media/ddrace/src/game/client/gameclient.cpp +/media/ddrace/src/game/client/components/menus.cpp 1392423525 0 /media/ddrace/src/game/client/components/menus.cpp +/media/ddrace/src/game/client/components/voting.cpp 1391797880 0 /media/ddrace/src/game/client/components/voting.cpp +/media/ddrace/src/game/client/animstate.cpp 1388496485 0 /media/ddrace/src/game/client/animstate.cpp +/media/ddrace/src/engine/docs/prediction.txt 1388496485 1 Prediction +/media/ddrace/src/engine/client/friends.cpp 1388713872 0 /media/ddrace/src/engine/client/friends.cpp +/media/ddrace/src/game/server/entities/dragger.cpp 1391797880 0 /media/ddrace/src/game/server/entities/dragger.cpp +/media/ddrace/src/engine/shared/demo.h 1391797880 0 /media/ddrace/src/engine/shared/demo.h +/media/ddrace/src/game/client/components/killmessages.cpp 1391797880 0 /media/ddrace/src/game/client/components/killmessages.cpp +/media/ddrace/src/game/editor/io.cpp 1392336406 0 /media/ddrace/src/game/editor/io.cpp +/media/ddrace/src/engine/shared/storage.h 1391797880 0 /media/ddrace/src/engine/shared/storage.h +/media/ddrace/src/engine/textrender.h 1388496485 0 /media/ddrace/src/engine/textrender.h +/media/ddrace/src/game/server/score/sql_score.h 1392834766 0 /media/ddrace/src/game/server/score/sql_score.h +/media/ddrace/src/engine/shared/message.h 1388496485 0 /media/ddrace/src/engine/shared/message.h +/media/ddrace/src/engine/client/sound.h 1391797880 0 /media/ddrace/src/engine/client/sound.h +/media/ddrace/src/game/editor/auto_map.h 1391797880 0 /media/ddrace/src/game/editor/auto_map.h +/media/ddrace/src/game/client/components/particles.cpp 1391797880 0 /media/ddrace/src/game/client/components/particles.cpp +/media/ddrace/src/engine/shared/network_client.cpp 1391797880 0 /media/ddrace/src/engine/shared/network_client.cpp +/media/ddrace/src/game/server/entity.h 1391797880 1 Entity +/media/ddrace/src/engine/server/register.h 1391797880 0 /media/ddrace/src/engine/server/register.h +/media/ddrace/src/game/client/components/nameplates.cpp 1391797880 0 /media/ddrace/src/game/client/components/nameplates.cpp +/media/ddrace/src/game/client/components/damageind.cpp 1391797880 0 /media/ddrace/src/game/client/components/damageind.cpp +/media/ddrace/src/game/client/components/sounds.h 1391797880 0 /media/ddrace/src/game/client/components/sounds.h +/media/ddrace/src/engine/shared/econ.h 1388496485 0 /media/ddrace/src/engine/shared/econ.h +/media/ddrace/src/game/client/components/controls.h 1391797880 0 /media/ddrace/src/game/client/components/controls.h +/media/ddrace/src/game/ddracecommands.h 1392212700 0 /media/ddrace/src/game/ddracecommands.h +/media/ddrace/src/game/server/entities/plasma.cpp 1391797880 0 /media/ddrace/src/game/server/entities/plasma.cpp +/media/ddrace/src/game/teamscore.cpp 1391797880 0 /media/ddrace/src/game/teamscore.cpp +/media/ddrace/src/engine/shared/jobs.h 1391797880 0 /media/ddrace/src/engine/shared/jobs.h +/media/ddrace/src/game/generated/client_data.h 1391798794 0 /media/ddrace/src/game/generated/client_data.h +/media/ddrace/src/engine/shared/network_server.cpp 1391797880 0 /media/ddrace/src/engine/shared/network_server.cpp +/media/ddrace/src/engine/shared/huffman.cpp 1388496485 0 /media/ddrace/src/engine/shared/huffman.cpp +/media/ddrace/src/game/server/entities/laser.h 1391797880 0 /media/ddrace/src/game/server/entities/laser.h +/media/ddrace/src/game/client/components/menus_browser.cpp 1392829239 0 /media/ddrace/src/game/client/components/menus_browser.cpp +/media/ddrace/src/game/server/entities/flag.h 1391797880 0 /media/ddrace/src/game/server/entities/flag.h +/media/ddrace/src/game/client/components/flow.h 1388496485 0 /media/ddrace/src/game/client/components/flow.h +/media/ddrace/src/game/client/components/broadcast.cpp 1391797880 0 /media/ddrace/src/game/client/components/broadcast.cpp +/media/ddrace/src/game/client/components/motd.cpp 1391797880 0 /media/ddrace/src/game/client/components/motd.cpp +/media/ddrace/src/engine/shared/ringbuffer.cpp 1388496485 0 /media/ddrace/src/engine/shared/ringbuffer.cpp +/media/ddrace/src/game/server/entities/gun.cpp 1391797880 0 /media/ddrace/src/game/server/entities/gun.cpp +/media/ddrace/src/game/client/components/effects.h 1388496485 0 /media/ddrace/src/game/client/components/effects.h +/media/ddrace/src/game/client/components/controls.cpp 1392758678 0 /media/ddrace/src/game/client/components/controls.cpp +/media/ddrace/src/game/client/render_map.cpp 1391797880 0 /media/ddrace/src/game/client/render_map.cpp +/media/ddrace/src/game/client/components/players.h 1391797880 0 /media/ddrace/src/game/client/components/players.h +/media/ddrace/src/game/client/components/menus_demo.cpp 1391797880 0 /media/ddrace/src/game/client/components/menus_demo.cpp +/media/ddrace/src/engine/client.h 1391797880 1 Client States +/media/ddrace/src/game/client/components/menus_ingame.cpp 1393371321 0 /media/ddrace/src/game/client/components/menus_ingame.cpp +/media/ddrace/src/engine/shared/kernel.cpp 1388496485 0 /media/ddrace/src/engine/shared/kernel.cpp +/media/ddrace/src/game/client/components/ghost.cpp 1391797880 0 /media/ddrace/src/game/client/components/ghost.cpp +/media/ddrace/src/game/variables.h 1392558009 0 /media/ddrace/src/game/variables.h +/media/ddrace/src/game/client/components/binds.h 1392218386 0 /media/ddrace/src/game/client/components/binds.h +/media/ddrace/src/game/server/entities/pickup.cpp 1391797880 0 /media/ddrace/src/game/server/entities/pickup.cpp +/media/ddrace/src/game/server/gamecontroller.cpp 1391797880 0 /media/ddrace/src/game/server/gamecontroller.cpp +/media/ddrace/src/game/server/gamecontext.h 1392840861 0 /media/ddrace/src/game/server/gamecontext.h +/media/ddrace/src/game/client/components/damageind.h 1391797880 0 /media/ddrace/src/game/client/components/damageind.h +/media/ddrace/src/game/server/entities/door.h 1391797880 0 /media/ddrace/src/game/server/entities/door.h +/media/ddrace/src/game/tuning.h 1388714034 0 /media/ddrace/src/game/tuning.h +/media/ddrace/src/game/server/player.cpp 1393003614 0 /media/ddrace/src/game/server/player.cpp +/media/ddrace/src/game/server/gamemodes/dm.h 1391797880 0 /media/ddrace/src/game/server/gamemodes/dm.h +/media/ddrace/src/game/server/gamemodes/mod.cpp 1391797880 0 /media/ddrace/src/game/server/gamemodes/mod.cpp +/media/ddrace/src/engine/serverbrowser.h 1392407617 1 CServerInfo +/media/ddrace/src/game/client/components/console.cpp 1392218386 0 /media/ddrace/src/game/client/components/console.cpp +/media/ddrace/src/game/client/component.h 1390600955 0 /media/ddrace/src/game/client/component.h +/media/ddrace/src/engine/friends.h 1391797880 0 /media/ddrace/src/engine/friends.h +/media/ddrace/src/game/server/score/file_score.cpp 1392834712 0 /media/ddrace/src/game/server/score/file_score.cpp diff --git a/docs/conf/Data/IndexInfo.nd b/docs/conf/Data/IndexInfo.nd index c175f4b54e..c73f69aef0 100644 Binary files a/docs/conf/Data/IndexInfo.nd and b/docs/conf/Data/IndexInfo.nd differ diff --git a/docs/conf/Data/PreviousMenuState.nd b/docs/conf/Data/PreviousMenuState.nd index 4422ee5d82..6ede7a7a29 100644 Binary files a/docs/conf/Data/PreviousMenuState.nd and b/docs/conf/Data/PreviousMenuState.nd differ diff --git a/docs/conf/Data/PreviousSettings.nd b/docs/conf/Data/PreviousSettings.nd index 7f5fdf3d9f..644a5fef9d 100644 Binary files a/docs/conf/Data/PreviousSettings.nd and b/docs/conf/Data/PreviousSettings.nd differ diff --git a/docs/conf/Data/SymbolTable.nd b/docs/conf/Data/SymbolTable.nd index dbf8ec2eb1..97f260e0a6 100644 Binary files a/docs/conf/Data/SymbolTable.nd and b/docs/conf/Data/SymbolTable.nd differ diff --git a/docs/conf/Menu.txt b/docs/conf/Menu.txt index 95aec56887..64d6741e62 100644 --- a/docs/conf/Menu.txt +++ b/docs/conf/Menu.txt @@ -50,51 +50,30 @@ Format: 1.4 # -------------------------------------------------------------------------- -Group: Engine { +Group: Client { - File: Messaging (/home/kma/code/teeworlds/trunk/src/engine/e_if_msg.h) + Group: Overview { - Group: Client { + File: Time on the client (/media/ddrace/src/engine/docs/client_time.txt) + File: Prediction (/media/ddrace/src/engine/docs/prediction.txt) + File: Snapshots (/media/ddrace/src/engine/docs/snapshots.txt) + File: Server Operation (/media/ddrace/src/engine/docs/server_op.txt) + } # Group: Overview - Group: Overview { + Group: Reference { - File: Time on the client (/home/kma/code/teeworlds/trunk/src/engine/docs/client_time.txt) - File: Prediction (/home/kma/code/teeworlds/trunk/src/engine/docs/prediction.txt) - File: Snapshots (/home/kma/code/teeworlds/trunk/src/engine/docs/snapshots.txt) - File: Server Operation (/home/kma/code/teeworlds/trunk/src/engine/docs/server_op.txt) - } # Group: Overview + File: CClientInfo (/media/ddrace/src/engine/server.h) + File: Client States (/media/ddrace/src/engine/client.h) + File: CServerInfo (/media/ddrace/src/engine/serverbrowser.h) + File: graphics.h (/media/ddrace/src/engine/graphics.h) + File: shared/huffman.h (/media/ddrace/src/engine/shared/huffman.h) + } # Group: Reference - Group: Reference { + } # Group: Client - File: Client Hooks (/home/kma/code/teeworlds/trunk/src/engine/e_if_modc.h) - File: Client Interface (/home/kma/code/teeworlds/trunk/src/engine/e_if_client.h) - File: e_huffman.h (/home/kma/code/teeworlds/trunk/src/engine/e_huffman.h) - File: Engine Interface (/home/kma/code/teeworlds/trunk/src/engine/e_if_other.h) - File: Graphics (/home/kma/code/teeworlds/trunk/src/engine/e_if_gfx.h) - File: Input (/home/kma/code/teeworlds/trunk/src/engine/e_if_inp.h) - File: Sound (/home/kma/code/teeworlds/trunk/src/engine/e_if_snd.h) - } # Group: Reference - - } # Group: Client - - Group: Server { - - Group: Reference { - - File: Server Hooks (/home/kma/code/teeworlds/trunk/src/engine/e_if_mods.h) - File: Server Interface (/home/kma/code/teeworlds/trunk/src/engine/e_if_server.h) - } # Group: Reference - - } # Group: Server - - } # Group: Engine - -Group: Game { - - File: Entity (/home/kma/code/teeworlds/trunk/src/game/server/entity.hpp) - File: Game Controller (/home/kma/code/teeworlds/trunk/src/game/server/gamecontroller.hpp) - File: Game World (/home/kma/code/teeworlds/trunk/src/game/server/gameworld.hpp) - } # Group: Game +File: Entity (/media/ddrace/src/game/server/entity.h) +File: Game Controller (/media/ddrace/src/game/server/gamecontroller.h) +File: Game World (/media/ddrace/src/game/server/gameworld.h) Group: Index { @@ -109,5 +88,5 @@ Group: Index { ##### Do not change or remove these lines. ##### -Data: 1(D3333RuEG3AEp39ufG3tGG7uHof63tHN\A36H93`pEG) -Data: 1(h3333RuEG3AEp39ufG3tGG7uHof63tHN\A36H93G\`8\G) +Data: 1(D3333EGf8p3ffHp9G36H93`pEG) +Data: 1(h3333EGf8p3ffHp9G36H93G\`8\G) diff --git a/objs/createdir.txt b/objs/createdir.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/other/icons/DDRace-Server.icns b/other/icons/DDNet-Server.icns similarity index 100% rename from other/icons/DDRace-Server.icns rename to other/icons/DDNet-Server.icns diff --git a/other/icons/DDRace-Server.ico b/other/icons/DDNet-Server.ico similarity index 100% rename from other/icons/DDRace-Server.ico rename to other/icons/DDNet-Server.ico diff --git a/other/icons/DDNet.icns b/other/icons/DDNet.icns new file mode 100644 index 0000000000..bceebcaddf Binary files /dev/null and b/other/icons/DDNet.icns differ diff --git a/other/icons/DDNet.ico b/other/icons/DDNet.ico new file mode 100644 index 0000000000..305d12e3a2 Binary files /dev/null and b/other/icons/DDNet.ico differ diff --git a/other/icons/DDRace.icns b/other/icons/DDRace.icns deleted file mode 100644 index 07e929c814..0000000000 Binary files a/other/icons/DDRace.icns and /dev/null differ diff --git a/other/icons/DDRace.ico b/other/icons/DDRace.ico deleted file mode 100644 index f1ccac600a..0000000000 Binary files a/other/icons/DDRace.ico and /dev/null differ diff --git a/other/icons/DDRace1.ico b/other/icons/DDRace1.ico deleted file mode 100644 index 568ee4b36f..0000000000 Binary files a/other/icons/DDRace1.ico and /dev/null differ diff --git a/other/icons/DDRace2.ico b/other/icons/DDRace2.ico deleted file mode 100644 index 3bc4b0b1fa..0000000000 Binary files a/other/icons/DDRace2.ico and /dev/null differ diff --git a/other/icons/Teeworlds.icns b/other/icons/Teeworlds.icns deleted file mode 100644 index 22be3422e4..0000000000 Binary files a/other/icons/Teeworlds.icns and /dev/null differ diff --git a/other/icons/Teeworlds.ico b/other/icons/Teeworlds.ico deleted file mode 100644 index 3ddec5c3d6..0000000000 Binary files a/other/icons/Teeworlds.ico and /dev/null differ diff --git a/other/icons/Teeworlds_srv.icns b/other/icons/Teeworlds_srv.icns deleted file mode 100644 index 44a847ebb5..0000000000 Binary files a/other/icons/Teeworlds_srv.icns and /dev/null differ diff --git a/other/icons/Teeworlds_srv.ico b/other/icons/Teeworlds_srv.ico deleted file mode 100644 index bde79f6a65..0000000000 Binary files a/other/icons/Teeworlds_srv.ico and /dev/null differ diff --git a/other/icons/teeworlds_cl.rc b/other/icons/teeworlds_cl.rc index 2004c00db0..178cb62fd6 100644 --- a/other/icons/teeworlds_cl.rc +++ b/other/icons/teeworlds_cl.rc @@ -1 +1 @@ -50h ICON "DDRace.ico" +50h ICON "DDNet.ico" diff --git a/other/icons/teeworlds_gcc.rc b/other/icons/teeworlds_gcc.rc index f085ee7e7e..072c60a694 100644 --- a/other/icons/teeworlds_gcc.rc +++ b/other/icons/teeworlds_gcc.rc @@ -1 +1 @@ -ID ICON "DDRace.ico" +ID ICON "DDNet.ico" diff --git a/other/icons/teeworlds_srv_cl.rc b/other/icons/teeworlds_srv_cl.rc index 5dfeb8f0ad..5be9af55b6 100644 --- a/other/icons/teeworlds_srv_cl.rc +++ b/other/icons/teeworlds_srv_cl.rc @@ -1 +1 @@ -50h ICON "DDRace-Server.ico" +50h ICON "DDNet-Server.ico" diff --git a/other/icons/teeworlds_srv_gcc.rc b/other/icons/teeworlds_srv_gcc.rc index 15a10bbc7c..29b340531d 100644 --- a/other/icons/teeworlds_srv_gcc.rc +++ b/other/icons/teeworlds_srv_gcc.rc @@ -1 +1 @@ -ID ICON "DDRace-Server.ico" +ID ICON "DDNet-Server.ico" diff --git a/other/mysql/include/cppconn/build_config.h b/other/mysql/include/cppconn/build_config.h index bbe78dffd0..c2807ef39f 100644 --- a/other/mysql/include/cppconn/build_config.h +++ b/other/mysql/include/cppconn/build_config.h @@ -1,14 +1,29 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_BUILD_CONFIG_H_ #define _SQL_BUILD_CONFIG_H_ diff --git a/other/mysql/include/cppconn/config.h b/other/mysql/include/cppconn/config.h index 35fb02e97a..0bf9727d3e 100644 --- a/other/mysql/include/cppconn/config.h +++ b/other/mysql/include/cppconn/config.h @@ -1,48 +1,61 @@ /* - Copyright 2009 Sun Microsystems, Inc. All rights reserved. + Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. - The MySQL Connector/C++ is licensed under the terms of the GPL + The MySQL Connector/C++ is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPL as it is applied to this software, see the FLOSS License Exception . + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // libmysql defines HAVE_STRTOUL (on win), so we have to follow different pattern in definitions names // to avoid annoying warnings. -/* #undef HAVE_FUNCTION_STRTOLD */ -/* #undef HAVE_FUNCTION_STRTOLL */ +#define HAVE_FUNCTION_STRTOLD 1 +#define HAVE_FUNCTION_STRTOLL 1 #define HAVE_FUNCTION_STRTOL 1 -/* #undef HAVE_FUNCTION_STRTOULL */ +#define HAVE_FUNCTION_STRTOULL 1 #define HAVE_FUNCTION_STRTOUL 1 -/* #undef HAVE_FUNCTION_STRTOIMAX */ -/* #undef HAVE_FUNCTION_STRTOUMAX */ - -/* #undef HAVE_STDINT_H */ -/* #undef HAVE_INTTYPES_H */ - -/* #undef HAVE_INT8_T */ -/* #undef HAVE_UINT8_T */ -/* #undef HAVE_INT16_T */ -/* #undef HAVE_UINT16_T */ -/* #undef HAVE_INT32_T */ -/* #undef HAVE_UINT32_T */ -/* #undef HAVE_INT32_T */ -/* #undef HAVE_UINT32_T */ -/* #undef HAVE_INT64_T */ -/* #undef HAVE_UINT64_T */ -#define HAVE_MS_INT8 1 -#define HAVE_MS_UINT8 1 -#define HAVE_MS_INT16 1 -#define HAVE_MS_UINT16 1 -#define HAVE_MS_INT32 1 -#define HAVE_MS_UINT32 1 -#define HAVE_MS_INT64 1 -#define HAVE_MS_UINT64 1 +#define HAVE_FUNCTION_STRTOIMAX 1 +#define HAVE_FUNCTION_STRTOUMAX 1 + +#define HAVE_STDINT_H 1 +#define HAVE_INTTYPES_H 1 + +#define HAVE_INT8_T 1 +#define HAVE_UINT8_T 1 +#define HAVE_INT16_T 1 +#define HAVE_UINT16_T 1 +#define HAVE_INT32_T 1 +#define HAVE_UINT32_T 1 +#define HAVE_INT32_T 1 +#define HAVE_UINT32_T 1 +#define HAVE_INT64_T 1 +#define HAVE_UINT64_T 1 +/* #undef HAVE_MS_INT8 */ +/* #undef HAVE_MS_UINT8 */ +/* #undef HAVE_MS_INT16 */ +/* #undef HAVE_MS_UINT16 */ +/* #undef HAVE_MS_INT32 */ +/* #undef HAVE_MS_UINT32 */ +/* #undef HAVE_MS_INT64 */ +/* #undef HAVE_MS_UINT64 */ #ifdef HAVE_STDINT_H @@ -53,10 +66,16 @@ #include #endif -#if defined(_WIN32) || defined(_WIN64) +#if defined(_WIN32) #ifndef CPPCONN_DONT_TYPEDEF_MS_TYPES_TO_C99_TYPES -#ifdef HAVE_MS_INT8 +#if _MSC_VER >= 1600 + +#include + +#else + +#if !defined(HAVE_INT8_T) && defined(HAVE_MS_INT8) typedef __int8 int8_t; #endif @@ -86,5 +105,6 @@ typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #endif +#endif // _MSC_VER >= 1600 #endif // CPPCONN_DONT_TYPEDEF_MS_TYPES_TO_C99_TYPES #endif // _WIN32 diff --git a/other/mysql/include/cppconn/connection.h b/other/mysql/include/cppconn/connection.h index 28d584a8c5..426ebe3955 100644 --- a/other/mysql/include/cppconn/connection.h +++ b/other/mysql/include/cppconn/connection.h @@ -1,43 +1,51 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_CONNECTION_H_ #define _SQL_CONNECTION_H_ -#include #include +#include #include "build_config.h" #include "warning.h" +#include "sqlstring.h" namespace sql { -typedef union _ConnectPropertyVal -{ - struct - { - const char * val; - size_t len; - } str; - double dval; - long long lval; - bool bval; - void * pval; -} ConnectPropertyVal; +typedef boost::variant ConnectPropertyVal; + +typedef std::map< sql::SQLString, ConnectPropertyVal > ConnectOptionsMap; class DatabaseMetaData; class PreparedStatement; class Statement; +class Driver; typedef enum transaction_isolation { @@ -58,7 +66,7 @@ class Savepoint virtual ~Savepoint() {}; virtual int getSavepointId() = 0; - virtual std::string getSavepointName() = 0; + virtual sql::SQLString getSavepointName() = 0; }; @@ -83,17 +91,15 @@ class CPPCONN_PUBLIC_FUNC Connection virtual bool getAutoCommit() = 0; - virtual std::string getCatalog() = 0; + virtual sql::SQLString getCatalog() = 0; - virtual std::string getSchema() = 0; + virtual Driver *getDriver() = 0; - virtual std::string getClientInfo() = 0; + virtual sql::SQLString getSchema() = 0; - virtual void getClientOption(const std::string & optionName, void * optionValue) = 0; + virtual sql::SQLString getClientInfo() = 0; - /* virtual int getHoldability() = 0; */ - - /* virtual std::map getTypeMap() = 0; */ + virtual void getClientOption(const sql::SQLString & optionName, void * optionValue) = 0; virtual DatabaseMetaData * getMetaData() = 0; @@ -103,9 +109,21 @@ class CPPCONN_PUBLIC_FUNC Connection virtual bool isClosed() = 0; - virtual std::string nativeSQL(const std::string& sql) = 0; + virtual bool isReadOnly() = 0; + + virtual sql::SQLString nativeSQL(const sql::SQLString& sql) = 0; + + virtual PreparedStatement * prepareStatement(const sql::SQLString& sql) = 0; - virtual PreparedStatement * prepareStatement(const std::string& sql) = 0; + virtual PreparedStatement * prepareStatement(const sql::SQLString& sql, int autoGeneratedKeys) = 0; + + virtual PreparedStatement * prepareStatement(const sql::SQLString& sql, int* columnIndexes) = 0; + + virtual PreparedStatement * prepareStatement(const sql::SQLString& sql, int resultSetType, int resultSetConcurrency) = 0; + + virtual PreparedStatement * prepareStatement(const sql::SQLString& sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) = 0; + + virtual PreparedStatement * prepareStatement(const sql::SQLString& sql, sql::SQLString columnNames[]) = 0; virtual void releaseSavepoint(Savepoint * savepoint) = 0; @@ -115,13 +133,19 @@ class CPPCONN_PUBLIC_FUNC Connection virtual void setAutoCommit(bool autoCommit) = 0; - virtual void setCatalog(const std::string& catalog) = 0; + virtual void setCatalog(const sql::SQLString& catalog) = 0; + + virtual void setSchema(const sql::SQLString& catalog) = 0; + + virtual sql::Connection * setClientOption(const sql::SQLString & optionName, const void * optionValue) = 0; + + virtual void setHoldability(int holdability) = 0; - virtual void setSchema(const std::string& catalog) = 0; + virtual void setReadOnly(bool readOnly) = 0; - virtual sql::Connection * setClientOption(const std::string & optionName, const void * optionValue) = 0; + virtual Savepoint * setSavepoint() = 0; - virtual Savepoint * setSavepoint(const std::string& name) = 0; + virtual Savepoint * setSavepoint(const sql::SQLString& name) = 0; virtual void setTransactionIsolation(enum_transaction_isolation level) = 0; diff --git a/other/mysql/include/cppconn/datatype.h b/other/mysql/include/cppconn/datatype.h index 32f2e0ff14..7ada3bcce9 100644 --- a/other/mysql/include/cppconn/datatype.h +++ b/other/mysql/include/cppconn/datatype.h @@ -1,14 +1,29 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_DATATYPE_H_ #define _SQL_DATATYPE_H_ diff --git a/other/mysql/include/cppconn/driver.h b/other/mysql/include/cppconn/driver.h index 0bd8d3251d..9ce84691a4 100644 --- a/other/mysql/include/cppconn/driver.h +++ b/other/mysql/include/cppconn/driver.h @@ -1,19 +1,32 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_DRIVER_H_ #define _SQL_DRIVER_H_ -#include -#include #include "connection.h" #include "build_config.h" @@ -27,9 +40,9 @@ class CPPCONN_PUBLIC_FUNC Driver public: // Attempts to make a database connection to the given URL. - virtual Connection * connect(const std::string& hostName, const std::string& userName, const std::string& password) = 0; + virtual Connection * connect(const sql::SQLString& hostName, const sql::SQLString& userName, const sql::SQLString& password) = 0; - virtual Connection * connect(std::map< std::string, ConnectPropertyVal > & options) = 0; + virtual Connection * connect(ConnectOptionsMap & options) = 0; virtual int getMajorVersion() = 0; @@ -37,14 +50,21 @@ class CPPCONN_PUBLIC_FUNC Driver virtual int getPatchVersion() = 0; - virtual const std::string & getName() = 0; + virtual const sql::SQLString & getName() = 0; + + virtual void threadInit() = 0; + + virtual void threadEnd() = 0; }; } /* namespace sql */ extern "C" { - CPPCONN_PUBLIC_FUNC sql::Driver *get_driver_instance(); + CPPCONN_PUBLIC_FUNC sql::Driver * get_driver_instance(); + + /* If dynamic loading is disabled in a driver then this function works just like get_driver_instance() */ + CPPCONN_PUBLIC_FUNC sql::Driver * get_driver_instance_by_name(const char * const clientlib); } #endif /* _SQL_DRIVER_H_ */ diff --git a/other/mysql/include/cppconn/exception.h b/other/mysql/include/cppconn/exception.h index 221f0964b9..67cce8aef3 100644 --- a/other/mysql/include/cppconn/exception.h +++ b/other/mysql/include/cppconn/exception.h @@ -1,14 +1,29 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_EXCEPTION_H_ #define _SQL_EXCEPTION_H_ @@ -27,8 +42,7 @@ namespace sql void* operator new[](size_t) throw (std::bad_alloc); \ void* operator new[](size_t, void*) throw(); \ void* operator new[](size_t, const std::nothrow_t&) throw(); \ - void* operator new(size_t N, std::allocator&); \ - virtual SQLException* copy() { return new Class(*this); } + void* operator new(size_t N, std::allocator&); #ifdef _WIN32 #pragma warning (disable : 4290) @@ -62,11 +76,17 @@ class CPPCONN_PUBLIC_FUNC SQLException : public std::runtime_error SQLException() : std::runtime_error(""), sql_state("HY000"), errNo(0) {} - const char * getSQLState() const + const std::string & getSQLState() const + { + return sql_state; + } + + const char * getSQLStateCStr() const { return sql_state.c_str(); } + int getErrorCode() const { return errNo; @@ -82,27 +102,18 @@ struct CPPCONN_PUBLIC_FUNC MethodNotImplementedException : public SQLException { MethodNotImplementedException(const MethodNotImplementedException& e) : SQLException(e.what(), e.sql_state, e.errNo) { } MethodNotImplementedException(const std::string& reason) : SQLException(reason, "", 0) {} - -private: - virtual SQLException* copy() { return new MethodNotImplementedException(*this); } }; struct CPPCONN_PUBLIC_FUNC InvalidArgumentException : public SQLException { InvalidArgumentException(const InvalidArgumentException& e) : SQLException(e.what(), e.sql_state, e.errNo) { } InvalidArgumentException(const std::string& reason) : SQLException(reason, "", 0) {} - -private: - virtual SQLException* copy() { return new InvalidArgumentException(*this); } }; struct CPPCONN_PUBLIC_FUNC InvalidInstanceException : public SQLException { InvalidInstanceException(const InvalidInstanceException& e) : SQLException(e.what(), e.sql_state, e.errNo) { } InvalidInstanceException(const std::string& reason) : SQLException(reason, "", 0) {} - -private: - virtual SQLException* copy() { return new InvalidInstanceException(*this); } }; @@ -110,9 +121,6 @@ struct CPPCONN_PUBLIC_FUNC NonScrollableException : public SQLException { NonScrollableException(const NonScrollableException& e) : SQLException(e.what(), e.sql_state, e.errNo) { } NonScrollableException(const std::string& reason) : SQLException(reason, "", 0) {} - -private: - virtual SQLException* copy() { return new NonScrollableException(*this); } }; } /* namespace sql */ diff --git a/other/mysql/include/cppconn/metadata.h b/other/mysql/include/cppconn/metadata.h index 17210f39b3..7f65768c05 100644 --- a/other/mysql/include/cppconn/metadata.h +++ b/other/mysql/include/cppconn/metadata.h @@ -1,20 +1,36 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_METADATA_H_ #define _SQL_METADATA_H_ #include #include #include "datatype.h" +#include "sqlstring.h" namespace sql { @@ -121,23 +137,23 @@ class DatabaseMetaData virtual bool doesMaxRowSizeIncludeBlobs() = 0; - virtual ResultSet * getAttributes(const std::string& catalog, const std::string& schemaPattern, const std::string& typeNamePattern, const std::string& attributeNamePattern) = 0; + virtual ResultSet * getAttributes(const sql::SQLString& catalog, const sql::SQLString& schemaPattern, const sql::SQLString& typeNamePattern, const sql::SQLString& attributeNamePattern) = 0; - virtual ResultSet * getBestRowIdentifier(const std::string& catalog, const std::string& schema, const std::string& table, int scope, bool nullable) = 0; + virtual ResultSet * getBestRowIdentifier(const sql::SQLString& catalog, const sql::SQLString& schema, const sql::SQLString& table, int scope, bool nullable) = 0; virtual ResultSet * getCatalogs() = 0; - virtual const std::string& getCatalogSeparator() = 0; + virtual const sql::SQLString& getCatalogSeparator() = 0; - virtual const std::string& getCatalogTerm() = 0; + virtual const sql::SQLString& getCatalogTerm() = 0; - virtual ResultSet * getColumnPrivileges(const std::string& catalog, const std::string& schema, const std::string& table, const std::string& columnNamePattern) = 0; + virtual ResultSet * getColumnPrivileges(const sql::SQLString& catalog, const sql::SQLString& schema, const sql::SQLString& table, const sql::SQLString& columnNamePattern) = 0; - virtual ResultSet * getColumns(const std::string& catalog, const std::string& schemaPattern, const std::string& tableNamePattern, const std::string& columnNamePattern) = 0; + virtual ResultSet * getColumns(const sql::SQLString& catalog, const sql::SQLString& schemaPattern, const sql::SQLString& tableNamePattern, const sql::SQLString& columnNamePattern) = 0; virtual Connection * getConnection() = 0; - virtual ResultSet * getCrossReference(const std::string& primaryCatalog, const std::string& primarySchema, const std::string& primaryTable, const std::string& foreignCatalog, const std::string& foreignSchema, const std::string& foreignTable) = 0; + virtual ResultSet * getCrossReference(const sql::SQLString& primaryCatalog, const sql::SQLString& primarySchema, const sql::SQLString& primaryTable, const sql::SQLString& foreignCatalog, const sql::SQLString& foreignSchema, const sql::SQLString& foreignTable) = 0; virtual unsigned int getDatabaseMajorVersion() = 0; @@ -145,9 +161,9 @@ class DatabaseMetaData virtual unsigned int getDatabasePatchVersion() = 0; - virtual const std::string& getDatabaseProductName() = 0; + virtual const sql::SQLString& getDatabaseProductName() = 0; - virtual std::string getDatabaseProductVersion() = 0; + virtual SQLString getDatabaseProductVersion() = 0; virtual int getDefaultTransactionIsolation() = 0; @@ -157,19 +173,19 @@ class DatabaseMetaData virtual unsigned int getDriverPatchVersion() = 0; - virtual const std::string& getDriverName() = 0; + virtual const sql::SQLString& getDriverName() = 0; - virtual const std::string& getDriverVersion() = 0; + virtual const sql::SQLString& getDriverVersion() = 0; - virtual ResultSet * getExportedKeys(const std::string& catalog, const std::string& schema, const std::string& table) = 0; + virtual ResultSet * getExportedKeys(const sql::SQLString& catalog, const sql::SQLString& schema, const sql::SQLString& table) = 0; - virtual const std::string& getExtraNameCharacters() = 0; + virtual const sql::SQLString& getExtraNameCharacters() = 0; - virtual const std::string& getIdentifierQuoteString() = 0; + virtual const sql::SQLString& getIdentifierQuoteString() = 0; - virtual ResultSet * getImportedKeys(const std::string& catalog, const std::string& schema, const std::string& table) = 0; + virtual ResultSet * getImportedKeys(const sql::SQLString& catalog, const sql::SQLString& schema, const sql::SQLString& table) = 0; - virtual ResultSet * getIndexInfo(const std::string& catalog, const std::string& schema, const std::string& table, bool unique, bool approximate) = 0; + virtual ResultSet * getIndexInfo(const sql::SQLString& catalog, const sql::SQLString& schema, const sql::SQLString& table, bool unique, bool approximate) = 0; virtual unsigned int getCDBCMajorVersion() = 0; @@ -215,49 +231,53 @@ class DatabaseMetaData virtual unsigned int getMaxUserNameLength() = 0; - virtual const std::string& getNumericFunctions() = 0; + virtual const sql::SQLString& getNumericFunctions() = 0; - virtual ResultSet * getPrimaryKeys(const std::string& catalog, const std::string& schema, const std::string& table) = 0; + virtual ResultSet * getPrimaryKeys(const sql::SQLString& catalog, const sql::SQLString& schema, const sql::SQLString& table) = 0; - virtual ResultSet * getProcedures(const std::string& catalog, const std::string& schemaPattern, const std::string& procedureNamePattern) = 0; + virtual ResultSet * getProcedureColumns(const sql::SQLString& catalog, const sql::SQLString& schemaPattern, const sql::SQLString& procedureNamePattern, const sql::SQLString& columnNamePattern) = 0; - virtual const std::string& getProcedureTerm() = 0; + virtual ResultSet * getProcedures(const sql::SQLString& catalog, const sql::SQLString& schemaPattern, const sql::SQLString& procedureNamePattern) = 0; + + virtual const sql::SQLString& getProcedureTerm() = 0; virtual int getResultSetHoldability() = 0; virtual ResultSet * getSchemas() = 0; - virtual const std::string& getSchemaTerm() = 0; + virtual const sql::SQLString& getSchemaTerm() = 0; - virtual const std::string& getSearchStringEscape() = 0; + virtual const sql::SQLString& getSearchStringEscape() = 0; - virtual const std::string& getSQLKeywords() = 0; + virtual const sql::SQLString& getSQLKeywords() = 0; virtual int getSQLStateType() = 0; - virtual const std::string& getStringFunctions() = 0; + virtual const sql::SQLString& getStringFunctions() = 0; - virtual ResultSet * getSuperTables(const std::string& catalog, const std::string& schemaPattern, const std::string& tableNamePattern) = 0; + virtual ResultSet * getSuperTables(const sql::SQLString& catalog, const sql::SQLString& schemaPattern, const sql::SQLString& tableNamePattern) = 0; - virtual ResultSet * getSuperTypes(const std::string& catalog, const std::string& schemaPattern, const std::string& typeNamePattern) = 0; + virtual ResultSet * getSuperTypes(const sql::SQLString& catalog, const sql::SQLString& schemaPattern, const sql::SQLString& typeNamePattern) = 0; - virtual const std::string& getSystemFunctions() = 0; + virtual const sql::SQLString& getSystemFunctions() = 0; - virtual ResultSet * getTablePrivileges(const std::string& catalog, const std::string& schemaPattern, const std::string& tableNamePattern) = 0; + virtual ResultSet * getTablePrivileges(const sql::SQLString& catalog, const sql::SQLString& schemaPattern, const sql::SQLString& tableNamePattern) = 0; - virtual ResultSet * getTables(const std::string& catalog, const std::string& schemaPattern, const std::string& tableNamePattern, std::list &types) = 0; + virtual ResultSet * getTables(const sql::SQLString& catalog, const sql::SQLString& schemaPattern, const sql::SQLString& tableNamePattern, std::list &types) = 0; virtual ResultSet * getTableTypes() = 0; - virtual const std::string& getTimeDateFunctions() = 0; + virtual const sql::SQLString& getTimeDateFunctions() = 0; virtual ResultSet * getTypeInfo() = 0; - virtual ResultSet * getUDTs(const std::string& catalog, const std::string& schemaPattern, const std::string& typeNamePattern, std::list &types) = 0; + virtual ResultSet * getUDTs(const sql::SQLString& catalog, const sql::SQLString& schemaPattern, const sql::SQLString& typeNamePattern, std::list &types) = 0; + + virtual SQLString getURL() = 0; - virtual std::string getUserName() = 0; + virtual SQLString getUserName() = 0; - virtual ResultSet * getVersionColumns(const std::string& catalog, const std::string& schema, const std::string& table) = 0; + virtual ResultSet * getVersionColumns(const sql::SQLString& catalog, const sql::SQLString& schema, const sql::SQLString& table) = 0; virtual bool insertsAreDetected(int type) = 0; @@ -265,6 +285,8 @@ class DatabaseMetaData virtual bool isReadOnly() = 0; + virtual bool locatorsUpdateCopy() = 0; + virtual bool nullPlusNonNullIsNull() = 0; virtual bool nullsAreSortedAtEnd() = 0; @@ -351,6 +373,8 @@ class DatabaseMetaData virtual bool supportsGroupByUnrelated() = 0; + virtual bool supportsIntegrityEnhancementFacility() = 0; + virtual bool supportsLikeEscapeClause() = 0; virtual bool supportsLimitedOuterJoins() = 0; @@ -387,6 +411,8 @@ class DatabaseMetaData virtual bool supportsPositionedUpdate() = 0; + virtual bool supportsResultSetConcurrency(int type, int concurrency) = 0; + virtual bool supportsResultSetHoldability(int holdability) = 0; virtual bool supportsResultSetType(int type) = 0; @@ -435,11 +461,14 @@ class DatabaseMetaData virtual bool usesLocalFiles() = 0; - virtual ResultSet *getSchemata(const std::string& catalogName = "") = 0; + virtual ResultSet *getSchemata(const sql::SQLString& catalogName = "") = 0; - virtual ResultSet *getSchemaObjects(const std::string& catalogName = "", - const std::string& schemaName = "", - const std::string& objectType = "") = 0; + virtual ResultSet *getSchemaObjects(const sql::SQLString& catalogName = "", + const sql::SQLString& schemaName = "", + const sql::SQLString& objectType = "", + bool includingDdl = true, + const sql::SQLString& objectName = "", + const sql::SQLString& contextTableName = "") = 0; virtual ResultSet *getSchemaObjectTypes() = 0; }; diff --git a/other/mysql/include/cppconn/parameter_metadata.h b/other/mysql/include/cppconn/parameter_metadata.h index fd28f8eee0..06639ed578 100644 --- a/other/mysql/include/cppconn/parameter_metadata.h +++ b/other/mysql/include/cppconn/parameter_metadata.h @@ -1,18 +1,33 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_PARAMETER_METADATA_H_ #define _SQL_PARAMETER_METADATA_H_ -#include +#include namespace sql @@ -35,7 +50,24 @@ class ParameterMetaData parameterNullableUnknown }; + virtual sql::SQLString getParameterClassName(unsigned int param) = 0; + virtual int getParameterCount() = 0; + + virtual int getParameterMode(unsigned int param) = 0; + + virtual int getParameterType(unsigned int param) = 0; + + virtual sql::SQLString getParameterTypeName(unsigned int param) = 0; + + virtual int getPrecision(unsigned int param) = 0; + + virtual int getScale(unsigned int param) = 0; + + virtual int isNullable(unsigned int param) = 0; + + virtual bool isSigned(unsigned int param) = 0; + protected: virtual ~ParameterMetaData() {} }; diff --git a/other/mysql/include/cppconn/prepared_statement.h b/other/mysql/include/cppconn/prepared_statement.h index 6b25f826a4..a2945e4400 100644 --- a/other/mysql/include/cppconn/prepared_statement.h +++ b/other/mysql/include/cppconn/prepared_statement.h @@ -1,15 +1,30 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_PREPARED_STATEMENT_H_ #define _SQL_PREPARED_STATEMENT_H_ @@ -32,26 +47,26 @@ class PreparedStatement : public Statement virtual void clearParameters() = 0; - virtual bool execute(const std::string& sql) = 0; + virtual bool execute(const sql::SQLString& sql) = 0; virtual bool execute() = 0; - virtual ResultSet *executeQuery(const std::string& sql) = 0; + virtual ResultSet *executeQuery(const sql::SQLString& sql) = 0; virtual ResultSet *executeQuery() = 0; - virtual int executeUpdate(const std::string& sql) = 0; + virtual int executeUpdate(const sql::SQLString& sql) = 0; virtual int executeUpdate() = 0; virtual ResultSetMetaData * getMetaData() = 0; virtual ParameterMetaData * getParameterMetaData() = 0; - virtual void setBigInt(unsigned int parameterIndex, const std::string& value) = 0; + virtual void setBigInt(unsigned int parameterIndex, const sql::SQLString& value) = 0; virtual void setBlob(unsigned int parameterIndex, std::istream * blob) = 0; virtual void setBoolean(unsigned int parameterIndex, bool value) = 0; - virtual void setDateTime(unsigned int parameterIndex, const std::string& value) = 0; + virtual void setDateTime(unsigned int parameterIndex, const sql::SQLString& value) = 0; virtual void setDouble(unsigned int parameterIndex, double value) = 0; @@ -65,7 +80,7 @@ class PreparedStatement : public Statement virtual void setNull(unsigned int parameterIndex, int sqlType) = 0; - virtual void setString(unsigned int parameterIndex, const std::string& value) = 0; + virtual void setString(unsigned int parameterIndex, const sql::SQLString& value) = 0; virtual PreparedStatement * setResultSetType(sql::ResultSet::enum_type type) = 0; }; diff --git a/other/mysql/include/cppconn/resultset.h b/other/mysql/include/cppconn/resultset.h index 5ad2665be6..bd45dfb075 100644 --- a/other/mysql/include/cppconn/resultset.h +++ b/other/mysql/include/cppconn/resultset.h @@ -1,27 +1,38 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_RESULTSET_H_ #define _SQL_RESULTSET_H_ #include "config.h" -#if defined(__LINUX__) || defined(__linux__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__GNU__) || defined(__gnu__) || defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__) || defined(__sun) -#include -#endif - #include -#include #include #include +#include "sqlstring.h" #include "resultset_metadata.h" @@ -70,44 +81,62 @@ class ResultSet virtual void beforeFirst() = 0; + virtual void cancelRowUpdates() = 0; + + virtual void clearWarnings() = 0; + virtual void close() = 0; - virtual uint32_t findColumn(const std::string& columnLabel) const = 0; + virtual uint32_t findColumn(const sql::SQLString& columnLabel) const = 0; virtual bool first() = 0; virtual std::istream * getBlob(uint32_t columnIndex) const = 0; - virtual std::istream * getBlob(const std::string& columnLabel) const = 0; + virtual std::istream * getBlob(const sql::SQLString& columnLabel) const = 0; virtual bool getBoolean(uint32_t columnIndex) const = 0; - virtual bool getBoolean(const std::string& columnLabel) const = 0; + virtual bool getBoolean(const sql::SQLString& columnLabel) const = 0; + + virtual int getConcurrency() = 0; + virtual SQLString getCursorName() = 0; virtual long double getDouble(uint32_t columnIndex) const = 0; - virtual long double getDouble(const std::string& columnLabel) const = 0; + virtual long double getDouble(const sql::SQLString& columnLabel) const = 0; + + virtual int getFetchDirection() = 0; + virtual size_t getFetchSize() = 0; + virtual int getHoldability() = 0; virtual int32_t getInt(uint32_t columnIndex) const = 0; - virtual int32_t getInt(const std::string& columnLabel) const = 0; + virtual int32_t getInt(const sql::SQLString& columnLabel) const = 0; virtual uint32_t getUInt(uint32_t columnIndex) const = 0; - virtual uint32_t getUInt(const std::string& columnLabel) const = 0; + virtual uint32_t getUInt(const sql::SQLString& columnLabel) const = 0; virtual int64_t getInt64(uint32_t columnIndex) const = 0; - virtual int64_t getInt64(const std::string& columnLabel) const = 0; + virtual int64_t getInt64(const sql::SQLString& columnLabel) const = 0; virtual uint64_t getUInt64(uint32_t columnIndex) const = 0; - virtual uint64_t getUInt64(const std::string& columnLabel) const = 0; + virtual uint64_t getUInt64(const sql::SQLString& columnLabel) const = 0; virtual ResultSetMetaData * getMetaData() const = 0; virtual size_t getRow() const = 0; + virtual RowID * getRowId(uint32_t columnIndex) = 0; + virtual RowID * getRowId(const sql::SQLString & columnLabel) = 0; + virtual const Statement * getStatement() const = 0; - virtual std::string getString(uint32_t columnIndex) const = 0; - virtual std::string getString(const std::string& columnLabel) const = 0; + virtual SQLString getString(uint32_t columnIndex) const = 0; + virtual SQLString getString(const sql::SQLString& columnLabel) const = 0; virtual enum_type getType() const = 0; + virtual void getWarnings() = 0; + + virtual void insertRow() = 0; + virtual bool isAfterLast() const = 0; virtual bool isBeforeFirst() const = 0; @@ -119,16 +148,30 @@ class ResultSet virtual bool isLast() const = 0; virtual bool isNull(uint32_t columnIndex) const = 0; - virtual bool isNull(const std::string& columnLabel) const = 0; + virtual bool isNull(const sql::SQLString& columnLabel) const = 0; virtual bool last() = 0; virtual bool next() = 0; + virtual void moveToCurrentRow() = 0; + + virtual void moveToInsertRow() = 0; + virtual bool previous() = 0; + virtual void refreshRow() = 0; + virtual bool relative(int rows) = 0; + virtual bool rowDeleted() = 0; + + virtual bool rowInserted() = 0; + + virtual bool rowUpdated() = 0; + + virtual void setFetchSize(size_t rows) = 0; + virtual size_t rowsCount() const = 0; virtual bool wasNull() const = 0; diff --git a/other/mysql/include/cppconn/resultset_metadata.h b/other/mysql/include/cppconn/resultset_metadata.h index 94246b5eb5..fb0efce067 100644 --- a/other/mysql/include/cppconn/resultset_metadata.h +++ b/other/mysql/include/cppconn/resultset_metadata.h @@ -1,18 +1,33 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_RESULTSET_METADATA_H_ #define _SQL_RESULTSET_METADATA_H_ -#include +#include "sqlstring.h" #include "datatype.h" namespace sql @@ -28,27 +43,27 @@ class ResultSetMetaData columnNullableUnknown }; - virtual std::string getCatalogName(unsigned int column) = 0; + virtual SQLString getCatalogName(unsigned int column) = 0; virtual unsigned int getColumnCount() = 0; virtual unsigned int getColumnDisplaySize(unsigned int column) = 0; - virtual std::string getColumnLabel(unsigned int column) = 0; + virtual SQLString getColumnLabel(unsigned int column) = 0; - virtual std::string getColumnName(unsigned int column) = 0; + virtual SQLString getColumnName(unsigned int column) = 0; virtual int getColumnType(unsigned int column) = 0; - virtual std::string getColumnTypeName(unsigned int column) = 0; + virtual SQLString getColumnTypeName(unsigned int column) = 0; virtual unsigned int getPrecision(unsigned int column) = 0; virtual unsigned int getScale(unsigned int column) = 0; - virtual std::string getSchemaName(unsigned int column) = 0; + virtual SQLString getSchemaName(unsigned int column) = 0; - virtual std::string getTableName(unsigned int column) = 0; + virtual SQLString getTableName(unsigned int column) = 0; virtual bool isAutoIncrement(unsigned int column) = 0; @@ -60,6 +75,8 @@ class ResultSetMetaData virtual int isNullable(unsigned int column) = 0; + virtual bool isNumeric(unsigned int column) = 0; + virtual bool isReadOnly(unsigned int column) = 0; virtual bool isSearchable(unsigned int column) = 0; diff --git a/other/mysql/include/cppconn/sqlstring.h b/other/mysql/include/cppconn/sqlstring.h new file mode 100644 index 0000000000..d20080d165 --- /dev/null +++ b/other/mysql/include/cppconn/sqlstring.h @@ -0,0 +1,213 @@ +/* +Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + + +#ifndef _SQL_STRING_H_ +#define _SQL_STRING_H_ + +#include +#include "build_config.h" +#include + +namespace sql +{ + class CPPCONN_PUBLIC_FUNC SQLString + { + std::string realStr; + + public: +#ifdef _WIN32 + //TODO something less dirty-hackish. + static const size_t npos = static_cast(-1); +#else + static const size_t npos = std::string::npos; +#endif + + ~SQLString() {} + + SQLString() {} + + SQLString(const SQLString & other) : realStr(other.realStr) {} + + SQLString(const std::string & other) : realStr(other) {} + + SQLString(const char other[]) : realStr(other) {} + + SQLString(const char * s, size_t n) : realStr(s, n) {} + + // Needed for stuff like SQLString str= "char * string constant" + const SQLString & operator=(const char * s) + { + realStr = s; + return *this; + } + + const SQLString & operator=(const std::string & rhs) + { + realStr = rhs; + return *this; + } + + const SQLString & operator=(const SQLString & rhs) + { + realStr = rhs.realStr; + return *this; + } + + // Conversion to st::string. Comes in play for stuff like std::string str= SQLString_var; + operator const std::string &() const + { + return realStr; + } + + /** For access std::string methods. Not sure we need it. Makes it look like some smart ptr. + possibly operator* - will look even more like smart ptr */ + std::string * operator ->() + { + return & realStr; + } + + int compare(const SQLString& str) const + { + return realStr.compare(str.realStr); + } + + int compare(const char * s) const + { + return realStr.compare(s); + } + + int compare(size_t pos1, size_t n1, const char * s) const + { + return realStr.compare(pos1, n1, s); + } + + const std::string & asStdString() const + { + return realStr; + } + + const char * c_str() const + { + return realStr.c_str(); + } + + size_t length() const + { + return realStr.length(); + } + + SQLString & append(const std::string & str) + { + realStr.append(str); + return *this; + } + + SQLString & append(const char * s) + { + realStr.append(s); + return *this; + } + + const char& operator[](size_t pos) const + { + return realStr[pos]; + } + + size_t find(char c, size_t pos = 0) const + { + return realStr.find(c, pos); + } + + size_t find(const SQLString & s, size_t pos = 0) const + { + return realStr.find(s.realStr, pos); + } + + SQLString substr(size_t pos = 0, size_t n = npos) const + { + return realStr.substr(pos, n); + } + + const SQLString& replace(size_t pos1, size_t n1, const SQLString & s) + { + realStr.replace(pos1, n1, s.realStr); + return *this; + } + + size_t find_first_of(char c, size_t pos = 0) const + { + return realStr.find_first_of(c, pos); + } + + size_t find_last_of(char c, size_t pos = npos) const + { + return realStr.find_last_of(c, pos); + } + + const SQLString & operator+=(const SQLString & op2) + { + realStr += op2.realStr; + return *this; + } +}; + + +/* + Operators that can and have to be not a member. +*/ +inline const SQLString operator+(const SQLString & op1, const SQLString & op2) +{ + return sql::SQLString(op1.asStdString() + op2.asStdString()); +} + +inline bool operator ==(const SQLString & op1, const SQLString & op2) +{ + return (op1.asStdString() == op2.asStdString()); +} + +inline bool operator !=(const SQLString & op1, const SQLString & op2) +{ + return (op1.asStdString() != op2.asStdString()); +} + +inline bool operator <(const SQLString & op1, const SQLString & op2) +{ + return op1.asStdString() < op2.asStdString(); +} + + +}// namespace sql + + +namespace std +{ + // operator << for SQLString output + inline ostream & operator << (ostream & os, const sql::SQLString & str ) + { + return os << str.asStdString(); + } +} +#endif diff --git a/other/mysql/include/cppconn/statement.h b/other/mysql/include/cppconn/statement.h index c0eb652ca3..ecb4db7db1 100644 --- a/other/mysql/include/cppconn/statement.h +++ b/other/mysql/include/cppconn/statement.h @@ -1,14 +1,29 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_STATEMENT_H_ #define _SQL_STATEMENT_H_ @@ -32,18 +47,28 @@ class Statement virtual Connection * getConnection() = 0; + virtual void cancel() = 0; + virtual void clearWarnings() = 0; virtual void close() = 0; - virtual bool execute(const std::string& sql) = 0; + virtual bool execute(const sql::SQLString& sql) = 0; + + virtual ResultSet * executeQuery(const sql::SQLString& sql) = 0; - virtual ResultSet * executeQuery(const std::string& sql) = 0; + virtual int executeUpdate(const sql::SQLString& sql) = 0; - virtual int executeUpdate(const std::string& sql) = 0; + virtual size_t getFetchSize() = 0; + + virtual unsigned int getMaxFieldSize() = 0; + + virtual uint64_t getMaxRows() = 0; virtual bool getMoreResults() = 0; + virtual unsigned int getQueryTimeout() = 0; + virtual ResultSet * getResultSet() = 0; virtual sql::ResultSet::enum_type getResultSetType() = 0; @@ -52,6 +77,18 @@ class Statement virtual const SQLWarning * getWarnings() = 0; + virtual void setCursorName(const sql::SQLString & name) = 0; + + virtual void setEscapeProcessing(bool enable) = 0; + + virtual void setFetchSize(size_t rows) = 0; + + virtual void setMaxFieldSize(unsigned int max) = 0; + + virtual void setMaxRows(unsigned int max) = 0; + + virtual void setQueryTimeout(unsigned int seconds) = 0; + virtual Statement * setResultSetType(sql::ResultSet::enum_type type) = 0; }; diff --git a/other/mysql/include/cppconn/warning.h b/other/mysql/include/cppconn/warning.h index e0ffe4212e..3c4653a8b3 100644 --- a/other/mysql/include/cppconn/warning.h +++ b/other/mysql/include/cppconn/warning.h @@ -1,14 +1,29 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _SQL_WARNING_H_ #define _SQL_WARNING_H_ @@ -16,6 +31,7 @@ #include #include #include +#include "sqlstring.h" namespace sql { @@ -27,66 +43,25 @@ namespace sql class SQLWarning { -protected: - - const std::string sql_state; - const int errNo; - SQLWarning * next; - const std::string descr; - public: - SQLWarning(const std::string& reason, const std::string& SQLState, int vendorCode) :sql_state(SQLState), errNo(vendorCode),descr(reason) - { - } - - SQLWarning(const std::string& reason, const std::string& SQLState) :sql_state (SQLState), errNo(0), descr(reason) - { - } - - SQLWarning(const std::string& reason) : sql_state ("HY000"), errNo(0), descr(reason) - { - } - - SQLWarning() : sql_state ("HY000"), errNo(0) {} - - - const std::string & getMessage() const - { - return descr; - } - + SQLWarning(){} - const std::string & getSQLState() const - { - return sql_state; - } + virtual const sql::SQLString & getMessage() const = 0; - int getErrorCode() const - { - return errNo; - } + virtual const sql::SQLString & getSQLState() const = 0; - const SQLWarning * getNextWarning() const - { - return next; - } + virtual int getErrorCode() const = 0; - void setNextWarning(SQLWarning * _next) - { - next = _next; - } + virtual const SQLWarning * getNextWarning() const = 0; - virtual ~SQLWarning() throw () {}; + virtual void setNextWarning(const SQLWarning * _next) = 0; protected: - SQLWarning(const SQLWarning& e) : sql_state(e.sql_state), errNo(e.errNo), next(e.next), descr(e.descr) {} + virtual ~SQLWarning(){}; - virtual SQLWarning * copy() - { - return new SQLWarning(*this); - } + SQLWarning(const SQLWarning& e){}; private: const SQLWarning & operator = (const SQLWarning & rhs); diff --git a/other/mysql/include/mysql_connection.h b/other/mysql/include/mysql_connection.h index 26f991606b..8c81e81332 100644 --- a/other/mysql/include/mysql_connection.h +++ b/other/mysql/include/mysql_connection.h @@ -1,20 +1,35 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _MYSQL_CONNECTION_H_ #define _MYSQL_CONNECTION_H_ #include -struct st_mysql; - +#include +#include namespace sql { @@ -23,15 +38,15 @@ namespace mysql class MySQL_Savepoint : public sql::Savepoint { - std::string name; + sql::SQLString name; public: - MySQL_Savepoint(const std::string &savepoint); + MySQL_Savepoint(const sql::SQLString &savepoint); virtual ~MySQL_Savepoint() {} int getSavepointId(); - std::string getSavepointName(); + sql::SQLString getSavepointName(); private: /* Prevent use of these */ @@ -41,19 +56,30 @@ class MySQL_Savepoint : public sql::Savepoint class MySQL_DebugLogger; -class MySQL_ConnectionData; /* PIMPL */ +struct MySQL_ConnectionData; /* PIMPL */ +class MySQL_Statement; + +namespace NativeAPI +{ +class NativeConnectionWrapper; +} class CPPCONN_PUBLIC_FUNC MySQL_Connection : public sql::Connection { + MySQL_Statement * createServiceStmt(); + public: - MySQL_Connection(const std::string& hostName, const std::string& userName, const std::string& password); + MySQL_Connection(Driver * _driver, + ::sql::mysql::NativeAPI::NativeConnectionWrapper & _proxy, + const sql::SQLString& hostName, + const sql::SQLString& userName, + const sql::SQLString& password); - MySQL_Connection(std::map< std::string, sql::ConnectPropertyVal > & options); + MySQL_Connection(Driver * _driver, ::sql::mysql::NativeAPI::NativeConnectionWrapper & _proxy, + std::map< sql::SQLString, sql::ConnectPropertyVal > & options); virtual ~MySQL_Connection(); - struct ::st_mysql * getMySQLHandle(); - void clearWarnings(); void close(); @@ -62,15 +88,19 @@ class CPPCONN_PUBLIC_FUNC MySQL_Connection : public sql::Connection sql::Statement * createStatement(); + sql::SQLString escapeString(const sql::SQLString &); + bool getAutoCommit(); - std::string getCatalog(); + sql::SQLString getCatalog(); + + Driver *getDriver(); - std::string getSchema(); + sql::SQLString getSchema(); - std::string getClientInfo(); + sql::SQLString getClientInfo(); - void getClientOption(const std::string & optionName, void * optionValue); + void getClientOption(const sql::SQLString & optionName, void * optionValue); sql::DatabaseMetaData * getMetaData(); @@ -80,9 +110,21 @@ class CPPCONN_PUBLIC_FUNC MySQL_Connection : public sql::Connection bool isClosed(); - std::string nativeSQL(const std::string& sql); + bool isReadOnly(); + + sql::SQLString nativeSQL(const sql::SQLString& sql); + + sql::PreparedStatement * prepareStatement(const sql::SQLString& sql); + + sql::PreparedStatement * prepareStatement(const sql::SQLString& sql, int autoGeneratedKeys); - sql::PreparedStatement * prepareStatement(const std::string& sql); + sql::PreparedStatement * prepareStatement(const sql::SQLString& sql, int columnIndexes[]); + + sql::PreparedStatement * prepareStatement(const sql::SQLString& sql, int resultSetType, int resultSetConcurrency); + + sql::PreparedStatement * prepareStatement(const sql::SQLString& sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability); + + sql::PreparedStatement * prepareStatement(const sql::SQLString& sql, sql::SQLString columnNames[]); void releaseSavepoint(Savepoint * savepoint) ; @@ -92,27 +134,42 @@ class CPPCONN_PUBLIC_FUNC MySQL_Connection : public sql::Connection void setAutoCommit(bool autoCommit); - void setCatalog(const std::string& catalog); + void setCatalog(const sql::SQLString& catalog); + + void setSchema(const sql::SQLString& catalog); + + sql::Connection * setClientOption(const sql::SQLString & optionName, const void * optionValue); + + void setHoldability(int holdability); - void setSchema(const std::string& catalog); + void setReadOnly(bool readOnly); - sql::Connection * setClientOption(const std::string & optionName, const void * optionValue); + sql::Savepoint * setSavepoint(); - sql::Savepoint * setSavepoint(const std::string& name); + sql::Savepoint * setSavepoint(const sql::SQLString& name); void setTransactionIsolation(enum_transaction_isolation level); - std::string getSessionVariable(const std::string & varname); + virtual sql::SQLString getSessionVariable(const sql::SQLString & varname); - void setSessionVariable(const std::string & varname, const std::string & value); + virtual void setSessionVariable(const sql::SQLString & varname, const sql::SQLString & value); -protected: + virtual sql::SQLString getLastStatementInfo(); + +private: + /* We do not really think this class has to be subclassed*/ void checkClosed(); - void init(std::map & properties); + void init(std::map< sql::SQLString, sql::ConnectPropertyVal > & properties); + + Driver * driver; + boost::shared_ptr< NativeAPI::NativeConnectionWrapper > proxy; + + /* statement handle to execute queries initiated by driver. Perhaps it is + a good idea to move it to a separate helper class */ + boost::scoped_ptr< ::sql::mysql::MySQL_Statement > service; MySQL_ConnectionData * intern; /* pimpl */ -private: /* Prevent use of these */ MySQL_Connection(const MySQL_Connection &); void operator=(MySQL_Connection &); diff --git a/other/mysql/include/mysql_driver.h b/other/mysql/include/mysql_driver.h index 49f17a6150..ccd8cbc419 100644 --- a/other/mysql/include/mysql_driver.h +++ b/other/mysql/include/mysql_driver.h @@ -1,40 +1,65 @@ /* - Copyright 2007 - 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc. All rights reserved. - - The MySQL Connector/C++ is licensed under the terms of the GPL - , like most - MySQL Connectors. There are special exceptions to the terms and - conditions of the GPL as it is applied to this software, see the - FLOSS License Exception - . +Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C++ is licensed under the terms of the GPLv2 +, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + #ifndef _MYSQL_DRIVER_H_ #define _MYSQL_DRIVER_H_ +#include + #include +extern "C" +{ +CPPCONN_PUBLIC_FUNC void * sql_mysql_get_driver_instance(); +} namespace sql { namespace mysql { -class Connection; -class ConnectProperty; +namespace NativeAPI +{ + class NativeDriverWrapper; +} + +//class sql::mysql::NativeAPI::NativeDriverWrapper; class CPPCONN_PUBLIC_FUNC MySQL_Driver : public sql::Driver { + boost::scoped_ptr< ::sql::mysql::NativeAPI::NativeDriverWrapper > proxy; + public: - MySQL_Driver(); /* DON'T CALL THIS, USE Instance() */ - virtual ~MySQL_Driver();/* DON'T CALL THIS, MEMORY WILL BE AUTOMAGICALLY CLEANED */ + MySQL_Driver(); + MySQL_Driver(const ::sql::SQLString & clientLib); - static MySQL_Driver * Instance(); + virtual ~MySQL_Driver(); - sql::Connection * connect(const std::string& hostName, - const std::string& userName, - const std::string& password); + sql::Connection * connect(const sql::SQLString& hostName, const sql::SQLString& userName, const sql::SQLString& password); - sql::Connection * connect(std::map & options); + sql::Connection * connect(sql::ConnectOptionsMap & options); int getMajorVersion(); @@ -42,7 +67,11 @@ class CPPCONN_PUBLIC_FUNC MySQL_Driver : public sql::Driver int getPatchVersion(); - const std::string & getName(); + const sql::SQLString & getName(); + + void threadInit(); + + void threadEnd(); private: /* Prevent use of these */ @@ -50,7 +79,14 @@ class CPPCONN_PUBLIC_FUNC MySQL_Driver : public sql::Driver void operator=(MySQL_Driver &); }; -CPPCONN_PUBLIC_FUNC MySQL_Driver *get_mysql_driver_instance(); +/** We do not hide the function if MYSQLCLIENT_STATIC_BINDING(or anything else) not defined + because the counterpart C function is declared in the cppconn and is always visible. + If dynamic loading is not enabled then its result is just like of get_driver_instance() +*/ +CPPCONN_PUBLIC_FUNC MySQL_Driver * get_driver_instance_by_name(const char * const clientlib); + +CPPCONN_PUBLIC_FUNC MySQL_Driver * get_driver_instance(); +static inline MySQL_Driver * get_mysql_driver_instance() { return get_driver_instance(); } } /* namespace mysql */ diff --git a/other/mysql/linux/lib32/libmysqlclient.a b/other/mysql/linux/lib32/libmysqlclient.a index 60e83430f7..879e1d4c67 100644 Binary files a/other/mysql/linux/lib32/libmysqlclient.a and b/other/mysql/linux/lib32/libmysqlclient.a differ diff --git a/other/mysql/linux/lib32/libmysqlcppconn-static.a b/other/mysql/linux/lib32/libmysqlcppconn-static.a index 95d3721f09..419557896a 100644 Binary files a/other/mysql/linux/lib32/libmysqlcppconn-static.a and b/other/mysql/linux/lib32/libmysqlcppconn-static.a differ diff --git a/other/mysql/linux/lib64/libmysqlclient.a b/other/mysql/linux/lib64/libmysqlclient.a index 93fa4bf48f..ed51258d26 100644 Binary files a/other/mysql/linux/lib64/libmysqlclient.a and b/other/mysql/linux/lib64/libmysqlclient.a differ diff --git a/other/mysql/linux/lib64/libmysqlcppconn-static.a b/other/mysql/linux/lib64/libmysqlcppconn-static.a index 882b3c702e..fecde50d86 100644 Binary files a/other/mysql/linux/lib64/libmysqlcppconn-static.a and b/other/mysql/linux/lib64/libmysqlcppconn-static.a differ diff --git a/other/mysql/mac/lib32/libmysqlclient.a b/other/mysql/mac/lib32/libmysqlclient.a index b3fe18f19a..b12a60a137 100644 Binary files a/other/mysql/mac/lib32/libmysqlclient.a and b/other/mysql/mac/lib32/libmysqlclient.a differ diff --git a/other/mysql/mac/lib32/libmysqlcppconn-static.a b/other/mysql/mac/lib32/libmysqlcppconn-static.a index c110cf1d0a..2b97e6e485 100644 Binary files a/other/mysql/mac/lib32/libmysqlcppconn-static.a and b/other/mysql/mac/lib32/libmysqlcppconn-static.a differ diff --git a/other/mysql/mac/lib64/libmysqlclient.a b/other/mysql/mac/lib64/libmysqlclient.a index dd517d1090..3959646eea 100644 Binary files a/other/mysql/mac/lib64/libmysqlclient.a and b/other/mysql/mac/lib64/libmysqlclient.a differ diff --git a/other/mysql/mac/lib64/libmysqlcppconn-static.a b/other/mysql/mac/lib64/libmysqlcppconn-static.a index a96db0d21d..4c3eaa720a 100644 Binary files a/other/mysql/mac/lib64/libmysqlcppconn-static.a and b/other/mysql/mac/lib64/libmysqlcppconn-static.a differ diff --git a/other/mysql/vc2005libs/mysqlcppconn.dll b/other/mysql/vc2005libs/mysqlcppconn.dll index 8ae32c3b8e..13267bf749 100644 Binary files a/other/mysql/vc2005libs/mysqlcppconn.dll and b/other/mysql/vc2005libs/mysqlcppconn.dll differ diff --git a/other/mysql/vc2005libs/mysqlcppconn.lib b/other/mysql/vc2005libs/mysqlcppconn.lib index f0ca6dcea8..866944d3b3 100644 Binary files a/other/mysql/vc2005libs/mysqlcppconn.lib and b/other/mysql/vc2005libs/mysqlcppconn.lib differ diff --git a/other/sdl/lib32/SDL.dll b/other/sdl/lib32/SDL.dll old mode 100644 new mode 100755 index 429ae54584..69fd61ecb8 Binary files a/other/sdl/lib32/SDL.dll and b/other/sdl/lib32/SDL.dll differ diff --git a/other/sdl/lib64/SDL.dll b/other/sdl/lib64/SDL.dll old mode 100644 new mode 100755 index 3d4b1c2a04..dee47e810f Binary files a/other/sdl/lib64/SDL.dll and b/other/sdl/lib64/SDL.dll differ diff --git a/readme.txt b/readme.txt index 51d5696d1d..e69de29bb2 100644 --- a/readme.txt +++ b/readme.txt @@ -1,18 +0,0 @@ -This is a mod (DDRace) and it's for the game Teeworlds and it's being maintained by GreYFoX@GTi & btd with the help of others like heinrich5991, noother & Floff, in the previous versions 3da and Fluxid. - -DDRace has been rebuilt from scratch by btd based on DDRace-Beta to Teeworlds Trunk 0.5, GreYFoX@GTi helped re-adding features once it was Trunk, you can track this in the commit log. - -Please visit http://www.DDRace.info for up-to-date information about -DDRace, including new versions, custom maps and much more. - -Teeworlds README is as follows: -Copyright (c) 2012 Magnus Auvinen - - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - - -Please visit http://www.teeworlds.com for up-to-date information about -the game, including new versions, custom maps and much more. diff --git a/scripts/gen_keys.py b/scripts/gen_keys.py index 6a275468e7..51a659d739 100644 --- a/scripts/gen_keys.py +++ b/scripts/gen_keys.py @@ -36,6 +36,7 @@ print >>f, "\tKEY_MOUSE_8 = %d,"%(highestid+8); keynames[highestid+8] = "mouse8" print >>f, "\tKEY_MOUSE_WHEEL_UP = %d,"%(highestid+9); keynames[highestid+9] = "mousewheelup" print >>f, "\tKEY_MOUSE_WHEEL_DOWN = %d,"%(highestid+10); keynames[highestid+10] = "mousewheeldown" +print >>f, "\tKEY_MOUSE_9 = %d,"%(highestid+11); keynames[highestid+11] = "mouse9" print >>f, "\tKEY_LAST," print >>f, "};" diff --git a/scripts/make_release.py b/scripts/make_release.py index 8521ec71dd..bce9501990 100644 --- a/scripts/make_release.py +++ b/scripts/make_release.py @@ -1,10 +1,4 @@ -import shutil, os, re, sys, zipfile -if sys.version_info[0] == 2: - import urllib - url_lib = urllib -elif sys.version_info[0] == 3: - import urllib.request - url_lib = urllib.request +import shutil, os, sys, zipfile #valid_platforms = ["win32", "linux86", "linux86_64", "src"] @@ -13,8 +7,7 @@ print(sys.argv[0], "VERSION PLATFORM") sys.exit(-1) -name = "DDRace" -url_languages = "https://github.com/teeworlds/teeworlds-translation/zipball/master" +name = "DDNet" version = sys.argv[1] platform = sys.argv[2] exe_ext = "" @@ -38,7 +31,7 @@ # print(valid_platforms) # sys.exit(-1) -if platform == 'win32': +if platform == 'win32' or platform == 'win64': exe_ext = ".exe" use_zip = 1 use_gz = 0 @@ -47,29 +40,6 @@ use_gz = 0 use_bundle = 1 -def fetch_file(url): - try: - print("trying %s" % url) - real_url = url_lib.urlopen(url).geturl() - local = real_url.split("/") - local = local[len(local)-1].split("?") - local = local[0] - url_lib.urlretrieve(real_url, local) - return local - except: - return False - -def unzip(filename, where): - try: - z = zipfile.ZipFile(filename, "r") - except: - return False - for name in z.namelist(): - if "/data/languages/" in name: - z.extract(name, where) - z.close() - return z.namelist()[0] - def copydir(src, dst, excl=[]): for root, dirs, files in os.walk(src, topdown=True): if "/." in root or "\\." in root: @@ -80,15 +50,7 @@ def copydir(src, dst, excl=[]): for name in files: if name[0] != '.': shutil.copy(os.path.join(root, name), os.path.join(dst, root, name)) - -def clean(): - print("*** cleaning ***") - try: - shutil.rmtree(package_dir) - shutil.rmtree(languages_dir) - os.remove(src_package_languages) - except: pass - + package = "%s-%s-%s" %(name, version, platform) package_dir = package @@ -96,20 +58,11 @@ def clean(): shutil.rmtree(package_dir, True) os.mkdir(package_dir) -print("download and extract languages") -src_package_languages = fetch_file(url_languages) -if not src_package_languages: - print("couldn't download languages") - sys.exit(-1) -languages_dir = unzip(src_package_languages, ".") -if not languages_dir: - print("couldn't unzip languages") - sys.exit(-1) - print("adding files") -shutil.copy("readme.txt", package_dir) +#shutil.copy("readme.txt", package_dir) shutil.copy("license.txt", package_dir) shutil.copy("storage.cfg", package_dir) +shutil.copy("autoexec.cfg", package_dir) # DDRace shutil.copy("announcement.txt", package_dir) @@ -118,23 +71,18 @@ def clean(): if include_data and not use_bundle: os.mkdir(os.path.join(package_dir, "data")) copydir("data", package_dir) - try: - os.chdir(languages_dir) - copydir("data", "../"+package_dir) - os.chdir("..") - except: - print("couldn't find languages") if platform[:3] == "win": shutil.copy("other/config_directory.bat", package_dir) shutil.copy("SDL.dll", package_dir) shutil.copy("freetype.dll", package_dir) - shutil.copy("libmysql.dll", package_dir) - shutil.copy("mysqlcppconn.dll", package_dir) + #shutil.copy("libmysql.dll", package_dir) + #shutil.copy("mysqlcppconn.dll", package_dir) if include_exe and not use_bundle: shutil.copy(name+exe_ext, package_dir) shutil.copy(name+"-Server"+exe_ext, package_dir) - shutil.copy(name+"-Server_sql"+exe_ext, package_dir) + shutil.copy("dilate"+exe_ext, package_dir) + #shutil.copy(name+"-Server_sql"+exe_ext, package_dir) if include_src: for p in ["src", "scripts", "datasrc", "other", "objs"]: @@ -144,7 +92,7 @@ def clean(): shutil.copy("configure.lua", package_dir) if use_bundle: - bins = [name, name+'-Server', 'serverlaunch'] + bins = [name, name+'-Server', 'dilate', 'serverlaunch'] platforms = ('x86', 'x86_64', 'ppc') for bin in bins: to_lipo = [] @@ -156,24 +104,27 @@ def clean(): os.system("lipo -create -output "+bin+" "+" ".join(to_lipo)) # create Teeworlds appfolder - clientbundle_content_dir = os.path.join(package_dir, "DDRace.app/Contents") + clientbundle_content_dir = os.path.join(package_dir, "DDNet.app/Contents") clientbundle_bin_dir = os.path.join(clientbundle_content_dir, "MacOS") clientbundle_resource_dir = os.path.join(clientbundle_content_dir, "Resources") clientbundle_framework_dir = os.path.join(clientbundle_content_dir, "Frameworks") - os.mkdir(os.path.join(package_dir, "DDRace.app")) + binary_path = clientbundle_bin_dir + "/" + name+exe_ext + os.mkdir(os.path.join(package_dir, "DDNet.app")) os.mkdir(clientbundle_content_dir) os.mkdir(clientbundle_bin_dir) os.mkdir(clientbundle_resource_dir) os.mkdir(clientbundle_framework_dir) os.mkdir(os.path.join(clientbundle_resource_dir, "data")) copydir("data", clientbundle_resource_dir) - os.chdir(languages_dir) - copydir("data", "../"+clientbundle_resource_dir) - os.chdir("..") # TODO(GreYFoX): make sure this line doesn't change the directory of the icon file copied next, just make sure everything works - shutil.copy("other/icons/DDRace.icns", clientbundle_resource_dir) + + + shutil.copy("other/icons/DDNet.icns", clientbundle_resource_dir) #shutil.copy("other/icons/Teeworlds.icns", clientbundle_resource_dir) shutil.copy(name+exe_ext, clientbundle_bin_dir) + os.system("install_name_tool -change /opt/X11/lib/libfreetype.6.dylib @executable_path/../Frameworks/libfreetype.6.dylib " + binary_path) + os.system("install_name_tool -change /Library/Frameworks/SDL.framework/SDL @executable_path/../Frameworks/SDL.framework/SDL " + binary_path) os.system("cp -R /Library/Frameworks/SDL.framework " + clientbundle_framework_dir) + os.system("cp /opt/X11/lib/libfreetype.6.dylib " + clientbundle_framework_dir) file(os.path.join(clientbundle_content_dir, "Info.plist"), "w").write(""" @@ -182,9 +133,9 @@ def clean(): CFBundleDevelopmentRegion English CFBundleExecutable - DDRace + DDNet CFBundleIconFile - DDRace + DDNet CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType @@ -199,10 +150,10 @@ def clean(): file(os.path.join(clientbundle_content_dir, "PkgInfo"), "w").write("APPL????") # create Teeworlds Server appfolder - serverbundle_content_dir = os.path.join(package_dir, "DDRace-Server.app/Contents") + serverbundle_content_dir = os.path.join(package_dir, "DDNet-Server.app/Contents") serverbundle_bin_dir = os.path.join(serverbundle_content_dir, "MacOS") serverbundle_resource_dir = os.path.join(serverbundle_content_dir, "Resources") - os.mkdir(os.path.join(package_dir, "DDRace-Server.app")) + os.mkdir(os.path.join(package_dir, "DDNet-Server.app")) os.mkdir(serverbundle_content_dir) os.mkdir(serverbundle_bin_dir) os.mkdir(serverbundle_resource_dir) @@ -210,7 +161,7 @@ def clean(): os.mkdir(os.path.join(serverbundle_resource_dir, "data/maps")) os.mkdir(os.path.join(serverbundle_resource_dir, "data/mapres")) copydir("data/maps", serverbundle_resource_dir) - shutil.copy("other/icons/DDRace-Server.icns", serverbundle_resource_dir) + shutil.copy("other/icons/DDNet-Server.icns", serverbundle_resource_dir) shutil.copy(name+"-Server"+exe_ext, serverbundle_bin_dir) shutil.copy("serverlaunch"+exe_ext, serverbundle_bin_dir + "/"+name+"_server") file(os.path.join(serverbundle_content_dir, "Info.plist"), "w").write(""" @@ -221,9 +172,9 @@ def clean(): CFBundleDevelopmentRegion English CFBundleExecutable - DDRace_Server + DDNet_server CFBundleIconFile - DDRace-Server + DDNet-Server CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType @@ -255,10 +206,8 @@ def clean(): if use_dmg: print("making disk image") os.system("rm -f %s.dmg %s_temp.dmg" % (package, package)) - os.system("hdiutil create -srcfolder %s -volname DDRace -quiet %s_temp" % (package_dir, package)) + os.system("hdiutil create -srcfolder %s -volname DDNet -quiet %s_temp" % (package_dir, package)) os.system("hdiutil convert %s_temp.dmg -format UDBZ -o %s.dmg -quiet" % (package, package)) os.system("rm -f %s_temp.dmg" % package) - -clean() print("done") diff --git a/src/base/detect.h b/src/base/detect.h index 0e2ef86a37..ce326871fe 100644 --- a/src/base/detect.h +++ b/src/base/detect.h @@ -43,7 +43,11 @@ #define CONF_FAMILY_UNIX 1 #define CONF_FAMILY_STRING "unix" #define CONF_PLATFORM_LINUX 1 - #define CONF_PLATFORM_STRING "linux" + #if defined(__ANDROID__) + #define CONF_PLATFORM_STRING "android" + #else + #define CONF_PLATFORM_STRING "linux" + #endif #endif #if defined(__GNU__) || defined(__gnu__) @@ -133,6 +137,17 @@ #endif #endif +#if defined(__ARMEB__) + #define CONF_ARCH_ARM 1 + #define CONF_ARCH_STRING "arm" + #define CONF_ARCH_ENDIAN_BIG 1 +#endif + +#if defined(__ARMEL__) + #define CONF_ARCH_ARM 1 + #define CONF_ARCH_STRING "arm" + #define CONF_ARCH_ENDIAN_LITTLE 1 +#endif #ifndef CONF_FAMILY_STRING #define CONF_FAMILY_STRING "unknown" diff --git a/src/base/math.h b/src/base/math.h index d58dbf102d..07b0639654 100644 --- a/src/base/math.h +++ b/src/base/math.h @@ -20,7 +20,7 @@ inline float sign(float f) return f<0.0f?-1.0f:1.0f; } -inline int round(float f) +inline int round_to_int(float f) { if(f > 0) return (int)(f+0.5f); diff --git a/src/base/system.c b/src/base/system.c index e8dcb5e648..6eade5e4c4 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -30,6 +30,10 @@ #if defined(CONF_PLATFORM_MACOSX) #include #endif + + #if defined(__ANDROID__) + #include + #endif #elif defined(CONF_FAMILY_WINDOWS) #define WIN32_LEAN_AND_MEAN @@ -80,7 +84,7 @@ void dbg_assert_imp(const char *filename, int line, int test, const char *msg) void dbg_break() { - *((unsigned*)0) = 0x0; + *((volatile unsigned*)0) = 0x0; } void dbg_msg(const char *sys, const char *fmt, ...) @@ -120,6 +124,9 @@ static void logger_stdout(const char *line) { printf("%s\n", line); fflush(stdout); +#if defined(__ANDROID__) + __android_log_print(ANDROID_LOG_INFO, "DDNet", "%s", line); +#endif } static void logger_debugger(const char *line) @@ -176,6 +183,8 @@ void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned al MEMTAIL *tail; MEMHEADER *header = (struct MEMHEADER *)malloc(size+sizeof(MEMHEADER)+sizeof(MEMTAIL)); dbg_assert(header != 0, "mem_alloc failure"); + if(!header) + return NULL; tail = (struct MEMTAIL *)(((char*)(header+1))+size); header->size = size; header->filename = filename; @@ -487,7 +496,7 @@ int lock_try(LOCK lock) #if defined(CONF_FAMILY_UNIX) return pthread_mutex_trylock((LOCKINTERNAL *)lock); #elif defined(CONF_FAMILY_WINDOWS) - return TryEnterCriticalSection((LPCRITICAL_SECTION)lock); + return !TryEnterCriticalSection((LPCRITICAL_SECTION)lock); #else #error not implemented on this platform #endif @@ -515,18 +524,20 @@ void lock_release(LOCK lock) #endif } -#if defined(CONF_FAMILY_UNIX) -void semaphore_init(SEMAPHORE *sem) { sem_init(sem, 0, 0); } -void semaphore_wait(SEMAPHORE *sem) { sem_wait(sem); } -void semaphore_signal(SEMAPHORE *sem) { sem_post(sem); } -void semaphore_destroy(SEMAPHORE *sem) { sem_destroy(sem); } -#elif defined(CONF_FAMILY_WINDOWS) -void semaphore_init(SEMAPHORE *sem) { *sem = CreateSemaphore(0, 0, 10000, 0); } -void semaphore_wait(SEMAPHORE *sem) { WaitForSingleObject((HANDLE)*sem, 0L); } -void semaphore_signal(SEMAPHORE *sem) { ReleaseSemaphore((HANDLE)*sem, 1, NULL); } -void semaphore_destroy(SEMAPHORE *sem) { CloseHandle((HANDLE)*sem); } -#else - #error not implemented on this platform +#if !defined(CONF_PLATFORM_MACOSX) + #if defined(CONF_FAMILY_UNIX) + void semaphore_init(SEMAPHORE *sem) { sem_init(sem, 0, 0); } + void semaphore_wait(SEMAPHORE *sem) { sem_wait(sem); } + void semaphore_signal(SEMAPHORE *sem) { sem_post(sem); } + void semaphore_destroy(SEMAPHORE *sem) { sem_destroy(sem); } + #elif defined(CONF_FAMILY_WINDOWS) + void semaphore_init(SEMAPHORE *sem) { *sem = CreateSemaphore(0, 0, 10000, 0); } + void semaphore_wait(SEMAPHORE *sem) { WaitForSingleObject((HANDLE)*sem, INFINITE); } + void semaphore_signal(SEMAPHORE *sem) { ReleaseSemaphore((HANDLE)*sem, 1, NULL); } + void semaphore_destroy(SEMAPHORE *sem) { CloseHandle((HANDLE)*sem); } + #else + #error not implemented on this platform + #endif #endif @@ -689,9 +700,9 @@ int net_host_lookup(const char *hostname, NETADDR *addr, int types) if(priv_net_extract(hostname, host, sizeof(host), &port)) return -1; - /* + dbg_msg("host lookup", "host='%s' port=%d %d", host, port, types); - */ + mem_zero(&hints, sizeof(hints)); @@ -703,12 +714,13 @@ int net_host_lookup(const char *hostname, NETADDR *addr, int types) hints.ai_family = AF_INET6; e = getaddrinfo(host, NULL, &hints, &result); - if(e != 0 || !result) + + if(!result || e != 0) return -1; sockaddr_to_netaddr(result->ai_addr, addr); - freeaddrinfo(result); addr->port = port; + freeaddrinfo(result); return 0; } @@ -913,6 +925,7 @@ NETSOCKET net_udp_create(NETADDR bindaddr) NETSOCKET sock = invalid_socket; NETADDR tmpbindaddr = bindaddr; int broadcast = 1; + int recvsize = 65536; if(bindaddr.type&NETTYPE_IPV4) { @@ -927,13 +940,13 @@ NETSOCKET net_udp_create(NETADDR bindaddr) { sock.type |= NETTYPE_IPV4; sock.ipv4sock = socket; - } - /* set non-blocking */ - net_set_non_blocking(sock); + /* set boardcast */ + setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); - /* set boardcast */ - setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); + /* set receive buffer size */ + setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&recvsize, sizeof(recvsize)); + } } if(bindaddr.type&NETTYPE_IPV6) @@ -949,15 +962,18 @@ NETSOCKET net_udp_create(NETADDR bindaddr) { sock.type |= NETTYPE_IPV6; sock.ipv6sock = socket; - } - /* set non-blocking */ - net_set_non_blocking(sock); + /* set boardcast */ + setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); - /* set boardcast */ - setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); + /* set receive buffer size */ + setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&recvsize, sizeof(recvsize)); + } } + /* set non-blocking */ + net_set_non_blocking(sock); + /* return */ return sock; } @@ -1506,8 +1522,8 @@ int net_socket_read_wait(NETSOCKET sock, int time) fd_set readfds; int sockid; - tv.tv_sec = 0; - tv.tv_usec = 1000*time; + tv.tv_sec = time / 1000; + tv.tv_usec = 1000 * (time % 1000); sockid = 0; FD_ZERO(&readfds); @@ -1524,7 +1540,10 @@ int net_socket_read_wait(NETSOCKET sock, int time) } /* don't care about writefds and exceptfds */ - select(sockid+1, &readfds, NULL, NULL, &tv); + if(time < 0) + select(sockid+1, &readfds, NULL, NULL, NULL); + else + select(sockid+1, &readfds, NULL, NULL, &tv); if(sock.ipv4sock >= 0 && FD_ISSET(sock.ipv4sock, &readfds)) return 1; @@ -1567,21 +1586,23 @@ int str_length(const char *str) return (int)strlen(str); } -void str_format(char *buffer, int buffer_size, const char *format, ...) +int str_format(char *buffer, int buffer_size, const char *format, ...) { + int ret; #if defined(CONF_FAMILY_WINDOWS) va_list ap; va_start(ap, format); - _vsnprintf(buffer, buffer_size, format, ap); + ret = _vsnprintf(buffer, buffer_size, format, ap); va_end(ap); #else va_list ap; va_start(ap, format); - vsnprintf(buffer, buffer_size, format, ap); + ret = vsnprintf(buffer, buffer_size, format, ap); va_end(ap); #endif buffer[buffer_size-1] = 0; /* assure null termination */ + return ret; } @@ -1831,6 +1852,27 @@ int str_toint(const char *str) { return atoi(str); } float str_tofloat(const char *str) { return atof(str); } +const char *str_utf8_skip_whitespaces(const char *str) +{ + const char *str_old; + int code; + + while(*str) + { + str_old = str; + code = str_utf8_decode(&str); + + // check if unicode is not empty + if(code > 0x20 && code != 0xA0 && code != 0x034F && (code < 0x2000 || code > 0x200F) && (code < 0x2028 || code > 0x202F) && + (code < 0x205F || code > 0x2064) && (code < 0x206A || code > 0x206F) && (code < 0xFE00 || code > 0xFE0F) && + code != 0xFEFF && (code < 0xFFF9 || code > 0xFFFC)) + { + return str_old; + } + } + + return str; +} static int str_utf8_isstart(char c) { diff --git a/src/base/system.h b/src/base/system.h index b3588dbf1f..818e2e7656 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -33,6 +33,13 @@ void dbg_assert(int test, const char *msg); #define dbg_assert(test,msg) dbg_assert_imp(__FILE__, __LINE__, test, msg) void dbg_assert_imp(const char *filename, int line, int test, const char *msg); + +#ifdef __clang_analyzer__ +#include +#undef dbg_assert +#define dbg_assert(test,msg) assert(test) +#endif + /* Function: dbg_break Breaks into the debugger. @@ -403,20 +410,22 @@ void lock_release(LOCK lock); /* Group: Semaphores */ -#if defined(CONF_FAMILY_UNIX) - #include - typedef sem_t SEMAPHORE; -#elif defined(CONF_FAMILY_WINDOWS) - typedef void* SEMAPHORE; -#else - #error missing sempahore implementation +#if !defined(CONF_PLATFORM_MACOSX) + #if defined(CONF_FAMILY_UNIX) + #include + typedef sem_t SEMAPHORE; + #elif defined(CONF_FAMILY_WINDOWS) + typedef void* SEMAPHORE; + #else + #error missing sempahore implementation + #endif + + void semaphore_init(SEMAPHORE *sem); + void semaphore_wait(SEMAPHORE *sem); + void semaphore_signal(SEMAPHORE *sem); + void semaphore_destroy(SEMAPHORE *sem); #endif -void semaphore_init(SEMAPHORE *sem); -void semaphore_wait(SEMAPHORE *sem); -void semaphore_signal(SEMAPHORE *sem); -void semaphore_destroy(SEMAPHORE *sem); - /* Group: Timer */ #ifdef __GNUC__ /* if compiled with -pedantic-errors it will complain about long @@ -760,12 +769,15 @@ int str_length(const char *str); format - printf formating string. ... - Parameters for the formating. + Returns: + Length of written string + Remarks: - See the C manual for syntax for the printf formating string. - The strings are treated as zero-termineted strings. - Garantees that dst string will contain zero-termination. */ -void str_format(char *buffer, int buffer_size, const char *format, ...); +int str_format(char *buffer, int buffer_size, const char *format, ...); /* Function: str_sanitize_strong @@ -1204,6 +1216,7 @@ unsigned str_quickhash(const char *str); */ void gui_messagebox(const char *title, const char *message); +const char *str_utf8_skip_whitespaces(const char *str); /* Function: str_utf8_rewind diff --git a/src/base/tl/algorithm.h b/src/base/tl/algorithm.h index 6b2e542a9b..27d0db3878 100644 --- a/src/base/tl/algorithm.h +++ b/src/base/tl/algorithm.h @@ -3,7 +3,7 @@ #ifndef BASE_TL_ALGORITHM_H #define BASE_TL_ALGORITHM_H -#include "range.h" +#include "base/tl/range.h" /* diff --git a/src/base/tl/array.h b/src/base/tl/array.h index 4f4b2fc31a..7bbe77b5bc 100644 --- a/src/base/tl/array.h +++ b/src/base/tl/array.h @@ -3,8 +3,8 @@ #ifndef BASE_TL_ARRAY_H #define BASE_TL_ARRAY_H -#include "range.h" -#include "allocator.h" +#include "base/tl/range.h" +#include "base/tl/allocator.h" /* diff --git a/src/base/tl/base.h b/src/base/tl/base.h index 2fcb14eb8a..ce77db8477 100644 --- a/src/base/tl/base.h +++ b/src/base/tl/base.h @@ -5,7 +5,7 @@ #include -inline void assert(bool statement) +inline void tl_assert(bool statement) { dbg_assert(statement, "assert!"); } diff --git a/src/base/tl/range.h b/src/base/tl/range.h index f05169fa15..8f8f23c46c 100644 --- a/src/base/tl/range.h +++ b/src/base/tl/range.h @@ -3,7 +3,7 @@ #ifndef BASE_TL_RANGE_H #define BASE_TL_RANGE_H -#include "base.h" +#include "base/tl/base.h" /* Group: Range concepts @@ -150,11 +150,11 @@ class plain_range } bool empty() const { return begin >= end; } - void pop_front() { assert(!empty()); begin++; } - void pop_back() { assert(!empty()); end--; } - T& front() { assert(!empty()); return *begin; } - T& back() { assert(!empty()); return *(end-1); } - T& index(unsigned i) { assert(i >= 0 && i < (unsigned)(end-begin)); return begin[i]; } + void pop_front() { tl_assert(!empty()); begin++; } + void pop_back() { tl_assert(!empty()); end--; } + T& front() { tl_assert(!empty()); return *begin; } + T& back() { tl_assert(!empty()); return *(end-1); } + T& index(unsigned i) { tl_assert(i < (unsigned)(end-begin)); return begin[i]; } unsigned size() const { return (unsigned)(end-begin); } plain_range slice(unsigned startindex, unsigned endindex) { diff --git a/src/base/tl/sorted_array.h b/src/base/tl/sorted_array.h index 7e312e1ea6..72e7d5eb50 100644 --- a/src/base/tl/sorted_array.h +++ b/src/base/tl/sorted_array.h @@ -3,8 +3,8 @@ #ifndef BASE_TL_SORTED_ARRAY_H #define BASE_TL_SORTED_ARRAY_H -#include "algorithm.h" -#include "array.h" +#include "base/tl/algorithm.h" +#include "base/tl/array.h" template > class sorted_array : public array diff --git a/src/base/tl/string.h b/src/base/tl/string.h index e0b891ad56..d93f7581a7 100644 --- a/src/base/tl/string.h +++ b/src/base/tl/string.h @@ -3,8 +3,8 @@ #ifndef BASE_TL_STRING_H #define BASE_TL_STRING_H -#include "base.h" -#include "allocator.h" +#include "base/tl/base.h" +#include "base/tl/allocator.h" template class string_base : private ALLOCATOR diff --git a/src/base/tl/threading.h b/src/base/tl/threading.h index dbf788cdc5..2cfbc0523f 100644 --- a/src/base/tl/threading.h +++ b/src/base/tl/threading.h @@ -7,19 +7,19 @@ atomic_inc - should return the value after increment atomic_dec - should return the value after decrement atomic_compswap - should return the value before the eventual swap - + sync_barrier - creates a full hardware fence */ #if defined(__GNUC__) inline unsigned atomic_inc(volatile unsigned *pValue) { - return __sync_fetch_and_add(pValue, 1); + return __sync_add_and_fetch(pValue, 1); } inline unsigned atomic_dec(volatile unsigned *pValue) { - return __sync_fetch_and_add(pValue, -1); + return __sync_add_and_fetch(pValue, -1); } inline unsigned atomic_compswap(volatile unsigned *pValue, unsigned comperand, unsigned value) @@ -35,6 +35,9 @@ #elif defined(_MSC_VER) #include + #define WIN32_LEAN_AND_MEAN + #include + inline unsigned atomic_inc(volatile unsigned *pValue) { return _InterlockedIncrement((volatile long *)pValue); @@ -52,21 +55,27 @@ inline void sync_barrier() { - _ReadWriteBarrier(); + MemoryBarrier(); } #else #error missing atomic implementation for this compiler #endif -class semaphore -{ - SEMAPHORE sem; -public: - semaphore() { semaphore_init(&sem); } - ~semaphore() { semaphore_destroy(&sem); } - void wait() { semaphore_wait(&sem); } - void signal() { semaphore_signal(&sem); } -}; +#if defined(CONF_PLATFORM_MACOSX) + /* + use semaphore provided by SDL on macosx + */ +#else + class semaphore + { + SEMAPHORE sem; + public: + semaphore() { semaphore_init(&sem); } + ~semaphore() { semaphore_destroy(&sem); } + void wait() { semaphore_wait(&sem); } + void signal() { semaphore_signal(&sem); } + }; +#endif class lock { diff --git a/src/engine/autoupdate.h b/src/engine/autoupdate.h new file mode 100644 index 0000000000..0f5fe078db --- /dev/null +++ b/src/engine/autoupdate.h @@ -0,0 +1,22 @@ +/* + unsigned char* +*/ +#ifndef ENGINE_AUTOUPDATE_H +#define ENGINE_AUTOUPDATE_H + +#include "kernel.h" +#include +#include + +class IAutoUpdate : public IInterface +{ + MACRO_INTERFACE("autoupdate", 0) +public: + virtual void CheckUpdates(CMenus *pMenus) = 0; + virtual void DoUpdates(CMenus *pMenus) = 0; + virtual bool Updated() = 0; + virtual bool NeedResetClient() = 0; + virtual void ExecuteExit() = 0; +}; + +#endif diff --git a/src/engine/client.h b/src/engine/client.h index e134f003bb..8da3d51fc9 100644 --- a/src/engine/client.h +++ b/src/engine/client.h @@ -5,6 +5,8 @@ #include "kernel.h" #include "message.h" +#include +#include class IClient : public IInterface { @@ -14,19 +16,21 @@ class IClient : public IInterface int m_State; // quick access to time variables - int m_PrevGameTick; - int m_CurGameTick; - float m_GameIntraTick; - float m_GameTickTime; + int m_PrevGameTick[2]; + int m_CurGameTick[2]; + float m_GameIntraTick[2]; + float m_GameTickTime[2]; - int m_PredTick; - float m_PredIntraTick; + int m_PredTick[2]; + float m_PredIntraTick[2]; float m_LocalTime; float m_RenderFrameTime; int m_GameTickSpeed; public: + int m_LocalIDs[2]; + char m_aNews[NEWS_SIZE]; class CSnapItem { @@ -59,12 +63,12 @@ class IClient : public IInterface inline int State() const { return m_State; } // tick time access - inline int PrevGameTick() const { return m_PrevGameTick; } - inline int GameTick() const { return m_CurGameTick; } - inline int PredGameTick() const { return m_PredTick; } - inline float IntraGameTick() const { return m_GameIntraTick; } - inline float PredIntraGameTick() const { return m_PredIntraTick; } - inline float GameTickTime() const { return m_GameTickTime; } + inline int PrevGameTick() const { return m_PrevGameTick[g_Config.m_ClDummy]; } + inline int GameTick() const { return m_CurGameTick[g_Config.m_ClDummy]; } + inline int PredGameTick() const { return m_PredTick[g_Config.m_ClDummy]; } + inline float IntraGameTick() const { return m_GameIntraTick[g_Config.m_ClDummy]; } + inline float PredIntraGameTick() const { return m_PredIntraTick[g_Config.m_ClDummy]; } + inline float GameTickTime() const { return m_GameTickTime[g_Config.m_ClDummy]; } inline int GameTickSpeed() const { return m_GameTickSpeed; } // other time access @@ -74,6 +78,12 @@ class IClient : public IInterface // actions virtual void Connect(const char *pAddress) = 0; virtual void Disconnect() = 0; + + // dummy + virtual void DummyDisconnect(const char *pReason) = 0; + virtual void DummyConnect() = 0; + virtual bool DummyConnected() = 0; + virtual void Quit() = 0; virtual const char *DemoPlayer_Play(const char *pFilename, int StorageType) = 0; virtual void DemoRecorder_Start(const char *pFilename, bool WithTimestamp) = 0; @@ -118,6 +128,7 @@ class IClient : public IInterface virtual void SnapSetStaticsize(int ItemType, int Size) = 0; virtual int SendMsg(CMsgPacker *pMsg, int Flags) = 0; + virtual int SendMsgExY(CMsgPacker *pMsg, int Flags, bool System=true, int NetClient=1) = 0; template int SendPackMsg(T *pMsg, int Flags) @@ -161,16 +172,19 @@ class IGameClient : public IInterface virtual void OnRender() = 0; virtual void OnStateChange(int NewState, int OldState) = 0; virtual void OnConnected() = 0; - virtual void OnMessage(int MsgID, CUnpacker *pUnpacker) = 0; + virtual void OnMessage(int MsgID, CUnpacker *pUnpacker, bool IsDummy = 0) = 0; virtual void OnPredict() = 0; virtual void OnActivateEditor() = 0; virtual int OnSnapInput(int *pData) = 0; + virtual void SendDummyInfo(bool Start) = 0; + virtual void ResetDummyInput() = 0; virtual const char *GetItemName(int Type) = 0; virtual const char *Version() = 0; virtual const char *NetVersion() = 0; + virtual void OnDummyDisconnect() = 0; }; extern IGameClient *CreateGameClient(); diff --git a/src/engine/client/autoupdate.cpp b/src/engine/client/autoupdate.cpp new file mode 100644 index 0000000000..f82a5ca39d --- /dev/null +++ b/src/engine/client/autoupdate.cpp @@ -0,0 +1,457 @@ +#include +#include +#include + +#if defined(CONF_FAMILY_UNIX) + #include + #include + + /* unix net includes */ + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + +#elif defined(CONF_FAMILY_WINDOWS) + #define WIN32_LEAN_AND_MEAN + #define _WIN32_WINNT 0x0501 /* required for mingw to get getaddrinfo to work */ + #include + #include + #include + #include + #include + #include + #include + #include +#else + #error NOT IMPLEMENTED +#endif +#include +#include //remove + +#include + +#include "autoupdate.h" + +static NETSOCKET invalid_socket = {NETTYPE_INVALID, -1, -1}; + +CAutoUpdate::CAutoUpdate() +{ + Reset(); +} + +void CAutoUpdate::Reset() +{ + m_NeedUpdate = false; + m_NeedUpdateBackground = false; + m_NeedUpdateClient = false; + m_NeedUpdateServer = false; + m_NeedResetClient = false; + m_Updated = false; + m_vFiles.clear(); +} + +bool CAutoUpdate::CanUpdate(const char *pFile) +{ + std::list::iterator it = m_vFiles.begin(); + while (it != m_vFiles.end()) + { + if ((*it).compare(pFile) == 0) + return false; + + it++; + } + + return true; +} + +void CAutoUpdate::ExecuteExit() +{ + if (!m_NeedResetClient) + return + + dbg_msg("autoupdate", "Executing pre-quiting..."); + + if (m_NeedUpdateClient) + { + SelfDelete(); + #if defined(CONF_FAMILY_WINDOWS) + ShellExecuteA(0,0,"du.bat",0,0,SW_HIDE); + #else + if (rename("DDNet_tmp","DDNet")) + dbg_msg("autoupdate", "Error renaming binary file"); + if (system("chmod +x DDNet")) + dbg_msg("autoupdate", "Error setting executable bit"); + #endif + } + + #if defined(CONF_FAMILY_WINDOWS) + if (!m_NeedUpdateClient) + ShellExecuteA(0,0,"DDNet.exe",0,0,SW_SHOW); + #else + pid_t pid; + pid=fork(); + if (pid==0) + { + char* argv[1]; + argv[0] = NULL; + execv("DDNet", argv); + } + else + return; + #endif +} + +void CAutoUpdate::CheckUpdates(CMenus *pMenus) +{ + char aReadBuf[512]; + dbg_msg("autoupdate", "Checking for updates"); + if (!GetFile("ddnet.upd", "ddnet.upd")) + { + dbg_msg("autoupdate", "Error downloading update list"); + return; + } + + dbg_msg("autoupdate", "Processing data"); + + Reset(); + IOHANDLE updFile = io_open("ddnet.upd", IOFLAG_READ); + if (!updFile) + return; + + //read data + std::string ReadData; + char last_version[15]; + char cmd; + while (io_read(updFile, aReadBuf, sizeof(aReadBuf)) > 0) + { + for (size_t i=0; i0 && aReadBuf[i-1] == '\r') + ReadData = ReadData.substr(0, -2); + + //Parse Command + cmd = ReadData[0]; + if (cmd == '#') + { + str_copy(last_version, ReadData.substr(1).c_str(), sizeof(last_version)); + + if (ReadData.substr(1).compare(GAME_RELEASE_VERSION) != 0) + pMenus->setPopup(CMenus::POPUP_AUTOUPDATE); + else + dbg_msg("autoupdate", "Version match"); + + io_close(updFile); + return; + } + ReadData.clear(); + } + + ReadData+=aReadBuf[i]; + } + } + + io_close(updFile); +} + +void CAutoUpdate::DoUpdates(CMenus *pMenus) +{ + char aReadBuf[512]; + char aBuf[512]; + + dbg_msg("autoupdate", "Processing data"); + + Reset(); + IOHANDLE updFile = io_open("ddnet.upd", IOFLAG_READ); + if (!updFile) + return; + + //read data + std::string ReadData; + char last_version[15], current_version[15]; + char cmd; + while (io_read(updFile, aReadBuf, sizeof(aReadBuf)) > 0) + { + for (size_t i=0; i0 && aReadBuf[i-1] == '\r') + ReadData = ReadData.substr(0, -2); + + //Parse Command + cmd = ReadData[0]; + if (cmd == '#') + { + if (!m_NeedUpdate) + str_copy(last_version, ReadData.substr(1).c_str(), sizeof(last_version)); + if (ReadData.substr(1).compare(GAME_RELEASE_VERSION) != 0) + m_NeedUpdate = true; + else + { + dbg_msg("autoupdate", "Version match"); + goto finish; + } + + str_copy(current_version, ReadData.substr(1).c_str(), sizeof(current_version)); + } + + if (m_NeedUpdate) + { + if (cmd == '@') + { + if (!m_NeedUpdateClient && ReadData.substr(2).compare("UPDATE_CLIENT") == 0) + { + str_format(aBuf, sizeof(aBuf), "Updating DDNet Client to %s", last_version); + pMenus->RenderUpdating(aBuf); + + m_NeedUpdateClient = true; + m_NeedResetClient = true; + dbg_msg("autoupdate", "Updating client"); + #if defined(CONF_FAMILY_WINDOWS) + if (!GetFile("DDNet.exe", "DDNet_tmp.exe")) + #elif defined(CONF_ARCH_AMD64) + if (!GetFile("DDNet64", "DDNet_tmp")) + #else + if (!GetFile("DDNet", "DDNet_tmp")) + #endif + dbg_msg("autoupdate", "Error downloading new version"); + } + if (!m_NeedUpdateServer && ReadData.substr(2).compare("UPDATE_SERVER") == 0) + { + str_format(aBuf, sizeof(aBuf), "Updating DDNet Server to %s", last_version); + pMenus->RenderUpdating(aBuf); + + m_NeedUpdateServer = true; + dbg_msg("autoupdate", "Updating server"); + #if defined(CONF_FAMILY_WINDOWS) + if (!GetFile("DDNet-Server.exe", "DDNet-Server.exe")) + #elif defined(CONF_ARCH_AMD64) + if (!GetFile("DDNet-Server64", "DDNet-Server")) + #else + if (!GetFile("DDNet-Server", "DDNet-Server")) + #endif + dbg_msg("autoupdate", "Error downloading new version"); + } + else if (!m_NeedUpdateClient && ReadData.substr(2).compare("RESET_CLIENT") == 0) + m_NeedResetClient = true; + } + else if (cmd == 'D') + { + int posDel=0; + ReadData = ReadData.substr(2); + posDel = ReadData.find_first_of(":"); + + if (CanUpdate(ReadData.substr(posDel+1).c_str())) + { + str_format(aBuf, sizeof(aBuf), "Downloading '%s'", ReadData.substr(posDel+1).c_str()); + pMenus->RenderUpdating(aBuf); + + dbg_msg("autoupdate", "Updating file '%s'", ReadData.substr(posDel+1).c_str()); + if (!GetFile(ReadData.substr(0, posDel).c_str(), ReadData.substr(posDel+1).c_str())) + dbg_msg("autoupdate", "Error downloading '%s'", ReadData.substr(0, posDel).c_str()); + else + m_vFiles.push_back(ReadData.substr(posDel+1)); + } + } + else if (cmd == 'R') + { + if (ReadData.substr(2).c_str()[0] == 0) + return; + + if (CanUpdate(ReadData.substr(2).c_str())) + { + str_format(aBuf, sizeof(aBuf), "Deleting '%s'", ReadData.substr(2).c_str()); + pMenus->RenderUpdating(aBuf); + + dbg_msg("autoupdate", "Deleting file '%s'", ReadData.substr(2).c_str()); + remove(ReadData.substr(2).c_str()); + + m_vFiles.push_back(ReadData.substr(2)); + } + } + } + + ReadData.clear(); + continue; + } + + ReadData+=aReadBuf[i]; + } + + if (!m_NeedUpdate) + break; + } + + finish: + if (m_NeedUpdate) + { + m_Updated = true; + m_NeedUpdate = false; + + if (!m_NeedUpdateClient) + dbg_msg("autoupdate", "Updated successfully"); + else + dbg_msg("autoupdate", "Restart necessary"); + } + else + dbg_msg("autoupdate", "No updates available"); + + io_close(updFile); + + if (m_Updated) + { + if (m_NeedUpdateClient) + { + pMenus->setPopup(CMenus::POPUP_QUIT); + return; + } + + str_format(aBuf, sizeof(aBuf), "DDNet Client updated successfully"); + pMenus->RenderUpdating(aBuf); + thread_sleep(200); + } + else + { + str_format(aBuf, sizeof(aBuf), "No update available"); + pMenus->RenderUpdating(aBuf); + thread_sleep(200); + } +} + +bool CAutoUpdate::GetFile(const char *pFile, const char *dst) +{ + NETSOCKET Socket = invalid_socket; + NETADDR HostAddress; + char aNetBuff[1024]; + + //Lookup + if(net_host_lookup(g_Config.m_ClDDNetUpdateServer, &HostAddress, NETTYPE_IPV4) != 0) + { + dbg_msg("autoupdate", "Error running host lookup"); + return false; + } + + char aAddrStr[NETADDR_MAXSTRSIZE]; + net_addr_str(&HostAddress, aAddrStr, sizeof(aAddrStr), 80); + + //Connect + int socketID = socket(AF_INET, SOCK_STREAM, 0); + if(socketID < 0) + { + dbg_msg("autoupdate", "Error creating socket"); + return false; + } + + Socket.type = NETTYPE_IPV4; + Socket.ipv4sock = socketID; + HostAddress.port = 80; + + if(net_tcp_connect(Socket, &HostAddress) != 0) + { + net_tcp_close(Socket); + dbg_msg("autoupdate","Error connecting to host"); + return false; + } + + //Send request + str_format(aNetBuff, sizeof(aNetBuff), "GET /%s HTTP/1.0\nHOST: %s\n\n", pFile, g_Config.m_ClDDNetUpdateServer); + net_tcp_send(Socket, aNetBuff, strlen(aNetBuff)); + + //read data + IOHANDLE dstFile = io_open(dst, IOFLAG_WRITE); + if (!dstFile) + { + net_tcp_close(Socket); + dbg_msg("autoupdate","Error writing to disk"); + return false; + } + + std::string NetData; + int TotalRecv = 0; + int TotalBytes = 0; + int CurrentRecv = 0; + bool isHead = true; + int enterCtrl = 0; + while ((CurrentRecv = net_tcp_recv(Socket, aNetBuff, sizeof(aNetBuff))) > 0) + { + for (int i=0; i 0 && str_comp_nocase(NetData.substr(0, posDel).c_str(),"content-length") == 0) + TotalBytes = atoi(NetData.substr(posDel+2).c_str()); + + NetData.clear(); + continue; + } + else if (aNetBuff[i]!='\r') + enterCtrl=0; + + NetData+=aNetBuff[i]; + } + else + { + if (TotalBytes == 0) + { + io_close(dstFile); + net_tcp_close(Socket); + dbg_msg("autoupdate","Error receiving file"); + return false; + } + + io_write(dstFile, &aNetBuff[i], 1); + + TotalRecv++; + if (TotalRecv == TotalBytes) + break; + } + } + } + + //Finish + io_close(dstFile); + net_tcp_close(Socket); + + return true; +} + +bool CAutoUpdate::SelfDelete() +{ + #ifdef CONF_FAMILY_WINDOWS + IOHANDLE bhFile = io_open("du.bat", IOFLAG_WRITE); + if (!bhFile) + return false; + + char aFileData[512]; + str_format(aFileData, sizeof(aFileData), ":_R\r\ndel \"DDNet.exe\"\r\nif exist \"DDNet.exe\" goto _R\r\nrename \"DDNet_tmp.exe\" \"DDNet.exe\"\r\n:_T\r\nif not exist \"DDNet.exe\" goto _T\r\nstart DDNet.exe\r\ndel \"du.bat\"\r\n"); + io_write(bhFile, aFileData, str_length(aFileData)); + io_close(bhFile); + #else + remove("DDNet"); + return true; + #endif + + return false; +} diff --git a/src/engine/client/autoupdate.h b/src/engine/client/autoupdate.h new file mode 100644 index 0000000000..e2095ac450 --- /dev/null +++ b/src/engine/client/autoupdate.h @@ -0,0 +1,40 @@ +/* + unsigned char* +*/ +#ifndef ENGINE_CLIENT_AUTOUPDATE_H +#define ENGINE_CLIENT_AUTOUPDATE_H + +#include +#include +#include +#include + +class CAutoUpdate : public IAutoUpdate +{ +public: + CAutoUpdate(); + + void Reset(); + + void CheckUpdates(CMenus *pMenus); + void DoUpdates(CMenus *pMenus); + bool Updated() { return m_Updated; } + bool NeedResetClient() { return m_NeedResetClient; } + void ExecuteExit(); + +private: + bool m_Updated; + bool m_NeedUpdate; + bool m_NeedUpdateBackground; + bool m_NeedUpdateClient; + bool m_NeedUpdateServer; + bool m_NeedResetClient; + std::list m_vFiles; + +protected: + bool SelfDelete(); + bool GetFile(const char *pFile, const char *dst); + bool CanUpdate(const char *pFile); +}; + +#endif diff --git a/src/engine/client/backend_sdl.cpp b/src/engine/client/backend_sdl.cpp index e7975aca4e..29f8ea7c6a 100644 --- a/src/engine/client/backend_sdl.cpp +++ b/src/engine/client/backend_sdl.cpp @@ -1,6 +1,15 @@ #include "SDL.h" -#include "SDL_opengl.h" +#if defined(__ANDROID__) + #define GL_GLEXT_PROTOTYPES + #include + #include + #include + #define glOrtho glOrthof + #include +#else + #include "SDL_opengl.h" +#endif #include @@ -11,6 +20,9 @@ void CGraphicsBackend_Threaded::ThreadFunc(void *pUser) { + #ifdef CONF_PLATFORM_MACOSX + CAutoreleasePool AutoreleasePool; + #endif CGraphicsBackend_Threaded *pThis = (CGraphicsBackend_Threaded *)pUser; while(!pThis->m_Shutdown) @@ -97,6 +109,41 @@ int CCommandProcessorFragment_OpenGL::TexFormatToOpenGLFormat(int TexFormat) return GL_RGBA; } +unsigned char CCommandProcessorFragment_OpenGL::Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp) +{ + int Value = 0; + for(int x = 0; x < ScaleW; x++) + for(int y = 0; y < ScaleH; y++) + Value += pData[((v+y)*w+(u+x))*Bpp+Offset]; + return Value/(ScaleW*ScaleH); +} + +void *CCommandProcessorFragment_OpenGL::Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData) +{ + unsigned char *pTmpData; + int ScaleW = Width/NewWidth; + int ScaleH = Height/NewHeight; + + int Bpp = 3; + if(Format == CCommandBuffer::TEXFORMAT_RGBA) + Bpp = 4; + + pTmpData = (unsigned char *)mem_alloc(NewWidth*NewHeight*Bpp, 1); + + int c = 0; + for(int y = 0; y < NewHeight; y++) + for(int x = 0; x < NewWidth; x++, c++) + { + pTmpData[c*Bpp] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 0, ScaleW, ScaleH, Bpp); + pTmpData[c*Bpp+1] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 1, ScaleW, ScaleH, Bpp); + pTmpData[c*Bpp+2] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 2, ScaleW, ScaleH, Bpp); + if(Bpp == 4) + pTmpData[c*Bpp+3] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 3, ScaleW, ScaleH, Bpp); + } + + return pTmpData; +} + void CCommandProcessorFragment_OpenGL::SetState(const CCommandBuffer::SState &State) { // blend @@ -130,7 +177,7 @@ void CCommandProcessorFragment_OpenGL::SetState(const CCommandBuffer::SState &St if(State.m_Texture >= 0 && State.m_Texture < CCommandBuffer::MAX_TEXTURES) { glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, m_aTextures[State.m_Texture]); + glBindTexture(GL_TEXTURE_2D, m_aTextures[State.m_Texture].m_Tex); } else glDisable(GL_TEXTURE_2D); @@ -155,9 +202,14 @@ void CCommandProcessorFragment_OpenGL::SetState(const CCommandBuffer::SState &St glOrtho(State.m_ScreenTL.x, State.m_ScreenBR.x, State.m_ScreenBR.y, State.m_ScreenTL.y, 1.0f, 10.f); } +void CCommandProcessorFragment_OpenGL::Cmd_Init(const SCommand_Init *pCommand) +{ + m_pTextureMemoryUsage = pCommand->m_pTextureMemoryUsage; +} + void CCommandProcessorFragment_OpenGL::Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand) { - glBindTexture(GL_TEXTURE_2D, m_aTextures[pCommand->m_Slot]); + glBindTexture(GL_TEXTURE_2D, m_aTextures[pCommand->m_Slot].m_Tex); glTexSubImage2D(GL_TEXTURE_2D, 0, pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height, TexFormatToOpenGLFormat(pCommand->m_Format), GL_UNSIGNED_BYTE, pCommand->m_pData); mem_free(pCommand->m_pData); @@ -165,31 +217,89 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Update(const CCommandBuffer:: void CCommandProcessorFragment_OpenGL::Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand) { - glDeleteTextures(1, &m_aTextures[pCommand->m_Slot]); + glDeleteTextures(1, &m_aTextures[pCommand->m_Slot].m_Tex); + *m_pTextureMemoryUsage -= m_aTextures[pCommand->m_Slot].m_MemSize; } void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand) { + int Width = pCommand->m_Width; + int Height = pCommand->m_Height; + void *pTexData = pCommand->m_pData; + + // resample if needed + if(pCommand->m_Format == CCommandBuffer::TEXFORMAT_RGBA || pCommand->m_Format == CCommandBuffer::TEXFORMAT_RGB) + { + int MaxTexSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &MaxTexSize); + if(Width > MaxTexSize || Height > MaxTexSize) + { + do + { + Width>>=1; + Height>>=1; + } + while(Width > MaxTexSize || Height > MaxTexSize); + + void *pTmpData = Rescale(pCommand->m_Width, pCommand->m_Height, Width, Height, pCommand->m_Format, static_cast(pCommand->m_pData)); + mem_free(pTexData); + pTexData = pTmpData; + } + else if(Width > 16 && Height > 16 && (pCommand->m_Flags&CCommandBuffer::TEXFLAG_QUALITY) == 0) + { + Width>>=1; + Height>>=1; + + void *pTmpData = Rescale(pCommand->m_Width, pCommand->m_Height, Width, Height, pCommand->m_Format, static_cast(pCommand->m_pData)); + mem_free(pTexData); + pTexData = pTmpData; + } + } + int Oglformat = TexFormatToOpenGLFormat(pCommand->m_Format); int StoreOglformat = TexFormatToOpenGLFormat(pCommand->m_StoreFormat); - glGenTextures(1, &m_aTextures[pCommand->m_Slot]); - glBindTexture(GL_TEXTURE_2D, m_aTextures[pCommand->m_Slot]); +#if defined(__ANDROID__) + StoreOglformat = Oglformat; +#else + if(pCommand->m_Flags&CCommandBuffer::TEXFLAG_COMPRESSED) + { + switch(StoreOglformat) + { + case GL_RGB: StoreOglformat = GL_COMPRESSED_RGB_ARB; break; + case GL_ALPHA: StoreOglformat = GL_COMPRESSED_ALPHA_ARB; break; + case GL_RGBA: StoreOglformat = GL_COMPRESSED_RGBA_ARB; break; + default: StoreOglformat = GL_COMPRESSED_RGBA_ARB; + } + } +#endif + glGenTextures(1, &m_aTextures[pCommand->m_Slot].m_Tex); + glBindTexture(GL_TEXTURE_2D, m_aTextures[pCommand->m_Slot].m_Tex); if(pCommand->m_Flags&CCommandBuffer::TEXFLAG_NOMIPMAPS) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, StoreOglformat, pCommand->m_Width, pCommand->m_Height, 0, Oglformat, GL_UNSIGNED_BYTE, pCommand->m_pData); + glTexImage2D(GL_TEXTURE_2D, 0, StoreOglformat, Width, Height, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - gluBuild2DMipmaps(GL_TEXTURE_2D, StoreOglformat, pCommand->m_Width, pCommand->m_Height, Oglformat, GL_UNSIGNED_BYTE, pCommand->m_pData); + gluBuild2DMipmaps(GL_TEXTURE_2D, StoreOglformat, Width, Height, Oglformat, GL_UNSIGNED_BYTE, pTexData); } - mem_free(pCommand->m_pData); + // calculate memory usage + m_aTextures[pCommand->m_Slot].m_MemSize = Width*Height*pCommand->m_PixelSize; + while(Width > 2 && Height > 2) + { + Width>>=1; + Height>>=1; + m_aTextures[pCommand->m_Slot].m_MemSize += Width*Height*pCommand->m_PixelSize; + } + *m_pTextureMemoryUsage += m_aTextures[pCommand->m_Slot].m_MemSize; + + mem_free(pTexData); } void CCommandProcessorFragment_OpenGL::Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand) @@ -212,7 +322,12 @@ void CCommandProcessorFragment_OpenGL::Cmd_Render(const CCommandBuffer::SCommand switch(pCommand->m_PrimType) { case CCommandBuffer::PRIMTYPE_QUADS: +#if defined(__ANDROID__) + for( unsigned i = 0, j = pCommand->m_PrimCount; i < j; i++ ) + glDrawArrays(GL_TRIANGLE_FAN, i*4, 4); +#else glDrawArrays(GL_QUADS, 0, pCommand->m_PrimCount*4); +#endif break; case CCommandBuffer::PRIMTYPE_LINES: glDrawArrays(GL_LINES, 0, pCommand->m_PrimCount*2); @@ -260,12 +375,14 @@ void CCommandProcessorFragment_OpenGL::Cmd_Screenshot(const CCommandBuffer::SCom CCommandProcessorFragment_OpenGL::CCommandProcessorFragment_OpenGL() { mem_zero(m_aTextures, sizeof(m_aTextures)); + m_pTextureMemoryUsage = 0; } bool CCommandProcessorFragment_OpenGL::RunCommand(const CCommandBuffer::SCommand * pBaseCommand) { switch(pBaseCommand->m_Cmd) { + case CMD_INIT: Cmd_Init(static_cast(pBaseCommand)); break; case CCommandBuffer::CMD_TEXTURE_CREATE: Cmd_Texture_Create(static_cast(pBaseCommand)); break; case CCommandBuffer::CMD_TEXTURE_DESTROY: Cmd_Texture_Destroy(static_cast(pBaseCommand)); break; case CCommandBuffer::CMD_TEXTURE_UPDATE: Cmd_Texture_Update(static_cast(pBaseCommand)); break; @@ -388,7 +505,7 @@ void CCommandProcessor_SDL_OpenGL::RunBuffer(CCommandBuffer *pBuffer) // ------------ CGraphicsBackend_SDL_OpenGL -int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int Width, int Height, int FsaaSamples, int Flags) +int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *Width, int *Height, int FsaaSamples, int Flags) { if(!SDL_WasInit(SDL_INIT_VIDEO)) { @@ -400,13 +517,24 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int Width, int Height, #ifdef CONF_FAMILY_WINDOWS if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) // ignore_convention - putenv("SDL_VIDEO_WINDOW_POS=8,27"); // ignore_convention + putenv("SDL_VIDEO_WINDOW_POS=center"); // ignore_convention #endif } const SDL_VideoInfo *pInfo = SDL_GetVideoInfo(); SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); // prevent stuck mouse cursor sdl-bug when loosing fullscreen focus in windows + // use current resolution as default +#ifndef __ANDROID__ + if(*Width == 0 || *Height == 0) +#endif + { + *Width = pInfo->current_w; + *Height = pInfo->current_h; + } + + + // set flags int SdlFlags = SDL_OPENGL; if(Flags&IGraphicsBackend::INITFLAG_RESIZABLE) @@ -420,6 +548,13 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int Width, int Height, if(pInfo->blit_hw) // ignore_convention SdlFlags |= SDL_HWACCEL; + dbg_assert(!(Flags&IGraphicsBackend::INITFLAG_BORDERLESS) + || !(Flags&IGraphicsBackend::INITFLAG_FULLSCREEN), + "only one of borderless and fullscreen may be activated at the same time"); + + if(Flags&IGraphicsBackend::INITFLAG_BORDERLESS) + SdlFlags |= SDL_NOFRAME; + if(Flags&IGraphicsBackend::INITFLAG_FULLSCREEN) SdlFlags |= SDL_FULLSCREEN; @@ -436,13 +571,13 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int Width, int Height, } SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, Flags&CCommandBuffer::INITFLAG_VSYNC ? 1 : 0); + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, Flags&IGraphicsBackend::INITFLAG_VSYNC ? 1 : 0); // set caption SDL_WM_SetCaption(pName, pName); // create window - m_pScreenSurface = SDL_SetVideoMode(Width, Height, 0, SdlFlags); + m_pScreenSurface = SDL_SetVideoMode(*Width, *Height, 0, SdlFlags); if(!m_pScreenSurface) { dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError()); @@ -460,11 +595,14 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int Width, int Height, m_pProcessor = new CCommandProcessor_SDL_OpenGL; StartProcessor(m_pProcessor); - // issue a init command + // issue init commands for OpenGL and SDL CCommandBuffer CmdBuffer(1024, 512); - CCommandProcessorFragment_SDL::SCommand_Init Cmd; - Cmd.m_Context = m_GLContext; - CmdBuffer.AddCommand(Cmd); + CCommandProcessorFragment_OpenGL::SCommand_Init CmdOpenGL; + CmdOpenGL.m_pTextureMemoryUsage = &m_TextureMemoryUsage; + CmdBuffer.AddCommand(CmdOpenGL); + CCommandProcessorFragment_SDL::SCommand_Init CmdSDL; + CmdSDL.m_Context = m_GLContext; + CmdBuffer.AddCommand(CmdSDL); RunBuffer(&CmdBuffer); WaitForIdle(); @@ -490,6 +628,11 @@ int CGraphicsBackend_SDL_OpenGL::Shutdown() return 0; } +int CGraphicsBackend_SDL_OpenGL::MemoryUsage() const +{ + return m_TextureMemoryUsage; +} + void CGraphicsBackend_SDL_OpenGL::Minimize() { SDL_WM_IconifyWindow(); diff --git a/src/engine/client/backend_sdl.h b/src/engine/client/backend_sdl.h index c6c2255aeb..061e6e7514 100644 --- a/src/engine/client/backend_sdl.h +++ b/src/engine/client/backend_sdl.h @@ -1,6 +1,5 @@ #include "SDL.h" -#include "SDL_opengl.h" #include "graphics_threaded.h" @@ -28,26 +27,112 @@ static void GL_SwapBuffers(const SGLContext &Context) { SwapBuffers(Context.m_hDC); } #elif defined(CONF_PLATFORM_MACOSX) - #include + #include + + class semaphore + { + SDL_sem *sem; + public: + semaphore() { sem = SDL_CreateSemaphore(0); } + ~semaphore() { SDL_DestroySemaphore(sem); } + void wait() { SDL_SemWait(sem); } + void signal() { SDL_SemPost(sem); } + }; struct SGLContext { - AGLContext m_Context; + id m_Context; }; static SGLContext GL_GetCurrentContext() { SGLContext Context; - Context.m_Context = aglGetCurrentContext(); + Class NSOpenGLContextClass = (Class) objc_getClass("NSOpenGLContext"); + SEL selector = sel_registerName("currentContext"); + Context.m_Context = objc_msgSend((objc_object*) NSOpenGLContextClass, selector); return Context; } - static void GL_MakeCurrent(const SGLContext &Context) { aglSetCurrentContext(Context.m_Context); } - static void GL_ReleaseContext(const SGLContext &Context) { aglSetCurrentContext(NULL); } - static void GL_SwapBuffers(const SGLContext &Context) { aglSwapBuffers(Context.m_Context); } - + static void GL_MakeCurrent(const SGLContext &Context) + { + SEL selector = sel_registerName("makeCurrentContext"); + objc_msgSend(Context.m_Context, selector); + } + + static void GL_ReleaseContext(const SGLContext &Context) + { + Class NSOpenGLContextClass = (Class) objc_getClass("NSOpenGLContext"); + SEL selector = sel_registerName("clearCurrentContext"); + objc_msgSend((objc_object*) NSOpenGLContextClass, selector); + } + + static void GL_SwapBuffers(const SGLContext &Context) + { + SEL selector = sel_registerName("flushBuffer"); + objc_msgSend(Context.m_Context, selector); + } + + class CAutoreleasePool + { + private: + id m_Pool; + + public: + CAutoreleasePool() + { + Class NSAutoreleasePoolClass = (Class) objc_getClass("NSAutoreleasePool"); + m_Pool = class_createInstance(NSAutoreleasePoolClass, 0); + SEL selector = sel_registerName("init"); + objc_msgSend(m_Pool, selector); + } + + ~CAutoreleasePool() + { + SEL selector = sel_registerName("drain"); + objc_msgSend(m_Pool, selector); + } + }; + #elif defined(CONF_FAMILY_UNIX) +#if defined(__ANDROID__) + + #include + + struct SGLContext + { + EGLDisplay m_Display; + EGLSurface m_SurfaceDraw; + EGLSurface m_SurfaceRead; + EGLContext m_Context; + }; + + static SGLContext GL_GetCurrentContext() + { + SGLContext Context; + Context.m_Display = eglGetCurrentDisplay(); + Context.m_SurfaceDraw = eglGetCurrentSurface(EGL_DRAW); + Context.m_SurfaceRead = eglGetCurrentSurface(EGL_READ); + Context.m_Context = eglGetCurrentContext(); + return Context; + } + + static void GL_MakeCurrent(const SGLContext &Context) + { + eglMakeCurrent(Context.m_Display, Context.m_SurfaceDraw, Context.m_SurfaceRead, Context.m_Context); + } + static void GL_ReleaseContext(const SGLContext &Context) + { + eglMakeCurrent(Context.m_Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + static void GL_SwapBuffers(const SGLContext &Context) + { + // eglSwapBuffers(Context.m_Display, Context.m_SurfaceDraw); + SDL_GL_SwapBuffers(); // This will also draw on-screen keyboard + } + +#else + #include struct SGLContext @@ -69,6 +154,8 @@ static void GL_MakeCurrent(const SGLContext &Context) { glXMakeCurrent(Context.m_pDisplay, Context.m_Drawable, Context.m_Context); } static void GL_ReleaseContext(const SGLContext &Context) { glXMakeCurrent(Context.m_pDisplay, None, 0x0); } static void GL_SwapBuffers(const SGLContext &Context) { glXSwapBuffers(Context.m_pDisplay, Context.m_Drawable); } + +#endif #else #error missing implementation #endif @@ -119,11 +206,34 @@ class CCommandProcessorFragment_General // takes care of opengl related rendering class CCommandProcessorFragment_OpenGL { - GLuint m_aTextures[CCommandBuffer::MAX_TEXTURES]; + struct CTexture + { + GLuint m_Tex; + int m_MemSize; + }; + CTexture m_aTextures[CCommandBuffer::MAX_TEXTURES]; + volatile int *m_pTextureMemoryUsage; + +public: + enum + { + CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_OPENGL, + }; + + struct SCommand_Init : public CCommandBuffer::SCommand + { + SCommand_Init() : SCommand(CMD_INIT) {} + volatile int *m_pTextureMemoryUsage; + }; + +private: static int TexFormatToOpenGLFormat(int TexFormat); + static unsigned char Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp); + static void *Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData); void SetState(const CCommandBuffer::SState &State); + void Cmd_Init(const SCommand_Init *pCommand); void Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand); void Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand); void Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand); @@ -145,7 +255,7 @@ class CCommandProcessorFragment_SDL public: enum { - CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM, + CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_SDL, CMD_SHUTDOWN, }; @@ -187,10 +297,13 @@ class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded SDL_Surface *m_pScreenSurface; ICommandProcessor *m_pProcessor; SGLContext m_GLContext; + volatile int m_TextureMemoryUsage; public: - virtual int Init(const char *pName, int Width, int Height, int FsaaSamples, int Flags); + virtual int Init(const char *pName, int *Width, int *Height, int FsaaSamples, int Flags); virtual int Shutdown(); + virtual int MemoryUsage() const; + virtual void Minimize(); virtual void Maximize(); virtual int WindowActive(); diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index aa0363beb6..18d6629308 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -4,10 +4,15 @@ #include // qsort #include +#include +#include #include +#include #include -#include + +#include +#include #include #include @@ -20,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -41,8 +47,11 @@ #include #include +#include + #include "friends.h" #include "serverbrowser.h" +#include "autoupdate.h" #include "client.h" #if defined(CONF_FAMILY_WINDOWS) @@ -137,14 +146,16 @@ void CGraph::Render(IGraphics *pGraphics, int Font, float x, float y, float w, f pGraphics->LinesEnd(); pGraphics->TextureSet(Font); - pGraphics->QuadsText(x+2, y+h-16, 16, 1,1,1,1, pDescription); + pGraphics->QuadsBegin(); + pGraphics->QuadsText(x+2, y+h-16, 16, pDescription); char aBuf[32]; str_format(aBuf, sizeof(aBuf), "%.2f", m_Max); - pGraphics->QuadsText(x+w-8*str_length(aBuf)-8, y+2, 16, 1,1,1,1, aBuf); + pGraphics->QuadsText(x+w-8*str_length(aBuf)-8, y+2, 16, aBuf); str_format(aBuf, sizeof(aBuf), "%.2f", m_Min); - pGraphics->QuadsText(x+w-8*str_length(aBuf)-8, y+h-16, 16, 1,1,1,1, aBuf); + pGraphics->QuadsText(x+w-8*str_length(aBuf)-8, y+h-16, 16, aBuf); + pGraphics->QuadsEnd(); } @@ -263,9 +274,12 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotD m_AutoScreenshotRecycle = false; m_EditorActive = false; - m_AckGameTick = -1; - m_CurrentRecvTick = 0; - m_RconAuthed = 0; + m_AckGameTick[0] = -1; + m_AckGameTick[1] = -1; + m_CurrentRecvTick[0] = 0; + m_CurrentRecvTick[1] = 0; + m_RconAuthed[0] = 0; + m_RconAuthed[1] = 0; // version-checking m_aVersionStr[0] = '0'; @@ -292,16 +306,32 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotD m_CurrentServerInfoRequestTime = -1; - m_CurrentInput = 0; + m_CurrentInput[0] = 0; + m_CurrentInput[1] = 0; + m_LastDummy = 0; + m_LastDummy2 = 0; + m_LocalIDs[0] = 0; + m_LocalIDs[1] = 0; + m_Fire = 0; + + mem_zero(&m_aInputs, sizeof(m_aInputs)); + mem_zero(&DummyInput, sizeof(DummyInput)); + mem_zero(&HammerInput, sizeof(HammerInput)); + HammerInput.m_Fire = 0; m_State = IClient::STATE_OFFLINE; m_aServerAddressStr[0] = 0; mem_zero(m_aSnapshots, sizeof(m_aSnapshots)); - m_SnapshotStorage.Init(); - m_RecivedSnapshots = 0; + m_SnapshotStorage[0].Init(); + m_SnapshotStorage[1].Init(); + m_RecivedSnapshots[0] = 0; + m_RecivedSnapshots[1] = 0; m_VersionInfo.m_State = CVersionInfo::STATE_INIT; + + if (g_Config.m_ClDummy == 0) + m_LastDummyConnectTime = 0; } // ----- send functions ----- @@ -343,7 +373,10 @@ int CClient::SendMsgEx(CMsgPacker *pMsg, int Flags, bool System) } if(!(Flags&MSGFLAG_NOSEND)) - m_NetClient.Send(&Packet); + { + m_NetClient[g_Config.m_ClDummy].Send(&Packet); + } + return 0; } @@ -389,15 +422,15 @@ void CClient::Rcon(const char *pCmd) bool CClient::ConnectionProblems() { - return m_NetClient.GotProblems() != 0; + return m_NetClient[g_Config.m_ClDummy].GotProblems() != 0; } void CClient::DirectInput(int *pInput, int Size) { int i; CMsgPacker Msg(NETMSG_INPUT); - Msg.AddInt(m_AckGameTick); - Msg.AddInt(m_PredTick); + Msg.AddInt(m_AckGameTick[g_Config.m_ClDummy]); + Msg.AddInt(m_PredTick[g_Config.m_ClDummy]); Msg.AddInt(Size); for(i = 0; i < Size/4; i++) @@ -406,38 +439,115 @@ void CClient::DirectInput(int *pInput, int Size) SendMsgEx(&Msg, 0); } - void CClient::SendInput() { int64 Now = time_get(); - if(m_PredTick <= 0) + if(m_PredTick[g_Config.m_ClDummy] <= 0) return; // fetch input - int Size = GameClient()->OnSnapInput(m_aInputs[m_CurrentInput].m_aData); + int Size = GameClient()->OnSnapInput(m_aInputs[g_Config.m_ClDummy][m_CurrentInput[g_Config.m_ClDummy]].m_aData); - if(!Size) - return; + if(Size) + { + // pack input + CMsgPacker Msg(NETMSG_INPUT); + Msg.AddInt(m_AckGameTick[g_Config.m_ClDummy]); + Msg.AddInt(m_PredTick[g_Config.m_ClDummy]); + Msg.AddInt(Size); - // pack input - CMsgPacker Msg(NETMSG_INPUT); - Msg.AddInt(m_AckGameTick); - Msg.AddInt(m_PredTick); - Msg.AddInt(Size); + m_aInputs[g_Config.m_ClDummy][m_CurrentInput[g_Config.m_ClDummy]].m_Tick = m_PredTick[g_Config.m_ClDummy]; + m_aInputs[g_Config.m_ClDummy][m_CurrentInput[g_Config.m_ClDummy]].m_PredictedTime = m_PredictedTime.Get(Now); + m_aInputs[g_Config.m_ClDummy][m_CurrentInput[g_Config.m_ClDummy]].m_Time = Now; + + // pack it + for(int i = 0; i < Size/4; i++) + Msg.AddInt(m_aInputs[g_Config.m_ClDummy][m_CurrentInput[g_Config.m_ClDummy]].m_aData[i]); + + m_CurrentInput[g_Config.m_ClDummy]++; + m_CurrentInput[g_Config.m_ClDummy]%=200; + + SendMsgEx(&Msg, MSGFLAG_FLUSH); + } + + if(m_LastDummy != (bool)g_Config.m_ClDummy) + { + mem_copy(&DummyInput, &m_aInputs[!g_Config.m_ClDummy][(m_CurrentInput[!g_Config.m_ClDummy]+200-1)%200], sizeof(DummyInput)); + m_LastDummy = g_Config.m_ClDummy; + + if (g_Config.m_ClDummyResetOnSwitch) + { + DummyInput.m_Jump = 0; + DummyInput.m_Hook = 0; + if(DummyInput.m_Fire & 1) + DummyInput.m_Fire++; + DummyInput.m_Direction = 0; + GameClient()->ResetDummyInput(); + } + } + + if(!g_Config.m_ClDummy) + m_LocalIDs[0] = ((CGameClient *)GameClient())->m_Snap.m_LocalClientID; + else + m_LocalIDs[1] = ((CGameClient *)GameClient())->m_Snap.m_LocalClientID; + + if(m_DummyConnected) + { + if(!g_Config.m_ClDummyHammer) + { + if(m_Fire != 0) + { + DummyInput.m_Fire = HammerInput.m_Fire; + m_Fire = 0; + } + + if(!Size && (!DummyInput.m_Direction && !DummyInput.m_Jump && !DummyInput.m_Hook)) + return; + + // pack input + CMsgPacker Msg(NETMSG_INPUT); + Msg.AddInt(m_AckGameTick[!g_Config.m_ClDummy]); + Msg.AddInt(m_PredTick[!g_Config.m_ClDummy]); + Msg.AddInt(sizeof(DummyInput)); - m_aInputs[m_CurrentInput].m_Tick = m_PredTick; - m_aInputs[m_CurrentInput].m_PredictedTime = m_PredictedTime.Get(Now); - m_aInputs[m_CurrentInput].m_Time = Now; + // pack it + for(unsigned int i = 0; i < sizeof(DummyInput)/4; i++) + Msg.AddInt(((int*) &DummyInput)[i]); + + SendMsgExY(&Msg, MSGFLAG_FLUSH, true, !g_Config.m_ClDummy); + } + else + { + if ((((float) m_Fire / 12.5) - (int ((float) m_Fire / 12.5))) > 0.01) + { + m_Fire++; + return; + } + m_Fire++; - // pack it - for(int i = 0; i < Size/4; i++) - Msg.AddInt(m_aInputs[m_CurrentInput].m_aData[i]); + HammerInput.m_Fire+=2; + HammerInput.m_WantedWeapon = 1; - m_CurrentInput++; - m_CurrentInput%=200; + vec2 Main = ((CGameClient *)GameClient())->m_LocalCharacterPos; + vec2 Dummy = ((CGameClient *)GameClient())->m_aClients[m_LocalIDs[!g_Config.m_ClDummy]].m_Predicted.m_Pos; + vec2 Dir = Main - Dummy; + HammerInput.m_TargetX = Dir.x; + HammerInput.m_TargetY = Dir.y; - SendMsgEx(&Msg, MSGFLAG_FLUSH); + // pack input + CMsgPacker Msg(NETMSG_INPUT); + Msg.AddInt(m_AckGameTick[!g_Config.m_ClDummy]); + Msg.AddInt(m_PredTick[!g_Config.m_ClDummy]); + Msg.AddInt(sizeof(HammerInput)); + + // pack it + for(unsigned int i = 0; i < sizeof(HammerInput)/4; i++) + Msg.AddInt(((int*) &HammerInput)[i]); + + SendMsgExY(&Msg, MSGFLAG_FLUSH, true, !g_Config.m_ClDummy); + } + } } const char *CClient::LatestVersion() @@ -451,12 +561,12 @@ int *CClient::GetInput(int Tick) int Best = -1; for(int i = 0; i < 200; i++) { - if(m_aInputs[i].m_Tick <= Tick && (Best == -1 || m_aInputs[Best].m_Tick < m_aInputs[i].m_Tick)) + if(m_aInputs[g_Config.m_ClDummy][i].m_Tick <= Tick && (Best == -1 || m_aInputs[g_Config.m_ClDummy][Best].m_Tick < m_aInputs[g_Config.m_ClDummy][i].m_Tick)) Best = i; } if(Best != -1) - return (int *)m_aInputs[Best].m_aData; + return (int *)m_aInputs[g_Config.m_ClDummy][Best].m_aData; return 0; } @@ -484,19 +594,26 @@ void CClient::OnEnterGame() // reset input int i; for(i = 0; i < 200; i++) - m_aInputs[i].m_Tick = -1; - m_CurrentInput = 0; + { + m_aInputs[0][i].m_Tick = -1; + m_aInputs[1][i].m_Tick = -1; + } + m_CurrentInput[0] = 0; + m_CurrentInput[1] = 0; // reset snapshots - m_aSnapshots[SNAP_CURRENT] = 0; - m_aSnapshots[SNAP_PREV] = 0; - m_SnapshotStorage.PurgeAll(); - m_RecivedSnapshots = 0; + m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] = 0; + m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV] = 0; + m_SnapshotStorage[g_Config.m_ClDummy].PurgeAll(); + m_RecivedSnapshots[g_Config.m_ClDummy] = 0; m_SnapshotParts = 0; - m_PredTick = 0; - m_CurrentRecvTick = 0; - m_CurGameTick = 0; - m_PrevGameTick = 0; + m_PredTick[g_Config.m_ClDummy] = 0; + m_CurrentRecvTick[g_Config.m_ClDummy] = 0; + m_CurGameTick[g_Config.m_ClDummy] = 0; + m_PrevGameTick[g_Config.m_ClDummy] = 0; + + if (g_Config.m_ClDummy == 0) + m_LastDummyConnectTime = 0; } void CClient::EnterGame() @@ -523,19 +640,18 @@ void CClient::Connect(const char *pAddress) m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf); ServerInfoRequest(); - - if(net_host_lookup(m_aServerAddressStr, &m_ServerAddress, m_NetClient.NetType()) != 0) + if(net_host_lookup(m_aServerAddressStr, &m_ServerAddress, m_NetClient[0].NetType()) != 0) { char aBufMsg[256]; str_format(aBufMsg, sizeof(aBufMsg), "could not find the address of %s, connecting to localhost", aBuf); m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBufMsg); - net_host_lookup("localhost", &m_ServerAddress, m_NetClient.NetType()); + net_host_lookup("localhost", &m_ServerAddress, m_NetClient[0].NetType()); } - m_RconAuthed = 0; + m_RconAuthed[0] = 0; if(m_ServerAddress.port == 0) m_ServerAddress.port = Port; - m_NetClient.Connect(&m_ServerAddress); + m_NetClient[0].Connect(&m_ServerAddress); SetState(IClient::STATE_CONNECTING); if(m_DemoRecorder.IsRecording()) @@ -556,10 +672,10 @@ void CClient::DisconnectWithReason(const char *pReason) DemoRecorder_Stop(); // - m_RconAuthed = 0; + m_RconAuthed[0] = 0; m_UseTempRconCommands = 0; m_pConsole->DeregisterTempAll(); - m_NetClient.Disconnect(pReason); + m_NetClient[0].Disconnect(pReason); SetState(IClient::STATE_OFFLINE); m_pMap->Unload(); @@ -577,16 +693,115 @@ void CClient::DisconnectWithReason(const char *pReason) mem_zero(&m_ServerAddress, sizeof(m_ServerAddress)); // clear snapshots - m_aSnapshots[SNAP_CURRENT] = 0; - m_aSnapshots[SNAP_PREV] = 0; - m_RecivedSnapshots = 0; + m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] = 0; + m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV] = 0; + m_RecivedSnapshots[g_Config.m_ClDummy] = 0; } void CClient::Disconnect() { + if(m_DummyConnected) + DummyDisconnect(0); DisconnectWithReason(0); } +bool CClient::DummyConnected() +{ + return m_DummyConnected; +} + +void CClient::DummyConnect() +{ + if(m_LastDummyConnectTime > 0 && m_LastDummyConnectTime + GameTickSpeed() * 5 > GameTick()) + return; + + if(m_NetClient[0].State() != NET_CONNSTATE_ONLINE && m_NetClient[0].State() != NET_CONNSTATE_PENDING) + return; + + if(m_DummyConnected) + return; + + m_LastDummyConnectTime = GameTick(); + + m_RconAuthed[1] = 0; + + //connecting to the server + m_NetClient[1].Connect(&m_ServerAddress); + + // send client info + CMsgPacker MsgInfo(NETMSG_INFO); + MsgInfo.AddString(GameClient()->NetVersion(), 128); + MsgInfo.AddString(g_Config.m_Password, 128); + SendMsgExY(&MsgInfo, MSGFLAG_VITAL|MSGFLAG_FLUSH, true, 1); + + // update netclient + m_NetClient[1].Update(); + + // send ready + CMsgPacker MsgReady(NETMSG_READY); + SendMsgExY(&MsgReady, MSGFLAG_VITAL|MSGFLAG_FLUSH, true, 1); + + // startinfo + GameClient()->SendDummyInfo(true); + + // send enter game an finish the connection + CMsgPacker MsgEnter(NETMSG_ENTERGAME); + SendMsgExY(&MsgEnter, MSGFLAG_VITAL|MSGFLAG_FLUSH, true, 1); +} + +void CClient::DummyDisconnect(const char *pReason) +{ + if(!m_DummyConnected) + return; + + m_NetClient[1].Disconnect(pReason); + g_Config.m_ClDummy = 0; + m_RconAuthed[1] = 0; + m_DummyConnected = false; + GameClient()->OnDummyDisconnect(); +} + +int CClient::SendMsgExY(CMsgPacker *pMsg, int Flags, bool System, int NetClient) +{ + CNetChunk Packet; + + mem_zero(&Packet, sizeof(CNetChunk)); + + Packet.m_ClientID = 0; + Packet.m_pData = pMsg->Data(); + Packet.m_DataSize = pMsg->Size(); + + // HACK: modify the message id in the packet and store the system flag + if(*((unsigned char*)Packet.m_pData) == 1 && System && Packet.m_DataSize == 1) + dbg_break(); + + *((unsigned char*)Packet.m_pData) <<= 1; + if(System) + *((unsigned char*)Packet.m_pData) |= 1; + + if(Flags&MSGFLAG_VITAL) + Packet.m_Flags |= NETSENDFLAG_VITAL; + if(Flags&MSGFLAG_FLUSH) + Packet.m_Flags |= NETSENDFLAG_FLUSH; + + m_NetClient[NetClient].Send(&Packet); + return 0; +} + +void CClient::DummyInfo() +{ + CNetMsg_Cl_ChangeInfo Msg; + Msg.m_pName = g_Config.m_DummyName; + Msg.m_pClan = g_Config.m_DummyClan; + Msg.m_Country = g_Config.m_DummyCountry; + Msg.m_pSkin = g_Config.m_DummySkin; + Msg.m_UseCustomColor = g_Config.m_DummyUseCustomColor; + Msg.m_ColorBody = g_Config.m_DummyColorBody; + Msg.m_ColorFeet = g_Config.m_DummyColorFeet; + CMsgPacker Packer(Msg.MsgID()); + Msg.Pack(&Packer); + SendMsgExY(&Packer, MSGFLAG_VITAL); +} void CClient::GetServerInfo(CServerInfo *pServerInfo) { @@ -611,8 +826,8 @@ void *CClient::SnapGetItem(int SnapID, int Index, CSnapItem *pItem) { CSnapshotItem *i; dbg_assert(SnapID >= 0 && SnapID < NUM_SNAPSHOT_TYPES, "invalid SnapID"); - i = m_aSnapshots[SnapID]->m_pAltSnap->GetItem(Index); - pItem->m_DataSize = m_aSnapshots[SnapID]->m_pAltSnap->GetItemSize(Index); + i = m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_pAltSnap->GetItem(Index); + pItem->m_DataSize = m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_pAltSnap->GetItemSize(Index); pItem->m_Type = i->Type(); pItem->m_ID = i->ID(); return (void *)i->Data(); @@ -622,12 +837,12 @@ void CClient::SnapInvalidateItem(int SnapID, int Index) { CSnapshotItem *i; dbg_assert(SnapID >= 0 && SnapID < NUM_SNAPSHOT_TYPES, "invalid SnapID"); - i = m_aSnapshots[SnapID]->m_pAltSnap->GetItem(Index); + i = m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_pAltSnap->GetItem(Index); if(i) { - if((char *)i < (char *)m_aSnapshots[SnapID]->m_pAltSnap || (char *)i > (char *)m_aSnapshots[SnapID]->m_pAltSnap + m_aSnapshots[SnapID]->m_SnapSize) + if((char *)i < (char *)m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_pAltSnap || (char *)i > (char *)m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_pAltSnap + m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_SnapSize) m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client", "snap invalidate problem"); - if((char *)i >= (char *)m_aSnapshots[SnapID]->m_pSnap && (char *)i < (char *)m_aSnapshots[SnapID]->m_pSnap + m_aSnapshots[SnapID]->m_SnapSize) + if((char *)i >= (char *)m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_pSnap && (char *)i < (char *)m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_pSnap + m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_SnapSize) m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client", "snap invalidate problem"); i->m_TypeAndID = -1; } @@ -638,12 +853,12 @@ void *CClient::SnapFindItem(int SnapID, int Type, int ID) // TODO: linear search. should be fixed. int i; - if(!m_aSnapshots[SnapID]) + if(!m_aSnapshots[g_Config.m_ClDummy][SnapID]) return 0x0; - for(i = 0; i < m_aSnapshots[SnapID]->m_pSnap->NumItems(); i++) + for(i = 0; i < m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_pSnap->NumItems(); i++) { - CSnapshotItem *pItem = m_aSnapshots[SnapID]->m_pAltSnap->GetItem(i); + CSnapshotItem *pItem = m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_pAltSnap->GetItem(i); if(pItem->Type() == Type && pItem->ID() == ID) return (void *)pItem->Data(); } @@ -653,9 +868,9 @@ void *CClient::SnapFindItem(int SnapID, int Type, int ID) int CClient::SnapNumItems(int SnapID) { dbg_assert(SnapID >= 0 && SnapID < NUM_SNAPSHOT_TYPES, "invalid SnapID"); - if(!m_aSnapshots[SnapID]) + if(!m_aSnapshots[g_Config.m_ClDummy][SnapID]) return 0; - return m_aSnapshots[SnapID]->m_pSnap->NumItems(); + return m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_pSnap->NumItems(); } void CClient::SnapSetStaticsize(int ItemType, int Size) @@ -678,6 +893,7 @@ void CClient::DebugRender() //m_pGraphics->BlendNormal(); Graphics()->TextureSet(m_DebugFont); Graphics()->MapScreen(0,0,Graphics()->ScreenWidth(),Graphics()->ScreenHeight()); + Graphics()->QuadsBegin(); if(time_get()-LastSnap > time_freq()) { @@ -694,12 +910,12 @@ void CClient::DebugRender() */ FrameTimeAvg = FrameTimeAvg*0.9f + m_RenderFrameTime*0.1f; str_format(aBuffer, sizeof(aBuffer), "ticks: %8d %8d mem %dk %d gfxmem: %dk fps: %3d", - m_CurGameTick, m_PredTick, + m_CurGameTick[g_Config.m_ClDummy], m_PredTick[g_Config.m_ClDummy], mem_stats()->allocated/1024, mem_stats()->total_allocations, Graphics()->MemoryUsage()/1024, (int)(1.0f/FrameTimeAvg + 0.5f)); - Graphics()->QuadsText(2, 2, 16, 1,1,1,1, aBuffer); + Graphics()->QuadsText(2, 2, 16, aBuffer); { @@ -715,7 +931,7 @@ void CClient::DebugRender() str_format(aBuffer, sizeof(aBuffer), "send: %3d %5d+%4d=%5d (%3d kbps) avg: %5d\nrecv: %3d %5d+%4d=%5d (%3d kbps) avg: %5d", SendPackets, SendBytes, SendPackets*42, SendTotal, (SendTotal*8)/1024, SendBytes/SendPackets, RecvPackets, RecvBytes, RecvPackets*42, RecvTotal, (RecvTotal*8)/1024, RecvBytes/RecvPackets); - Graphics()->QuadsText(2, 14, 16, 1,1,1,1, aBuffer); + Graphics()->QuadsText(2, 14, 16, aBuffer); } // render rates @@ -728,15 +944,16 @@ void CClient::DebugRender() { str_format(aBuffer, sizeof(aBuffer), "%4d %20s: %8d %8d %8d", i, GameClient()->GetItemName(i), m_SnapshotDelta.GetDataRate(i)/8, m_SnapshotDelta.GetDataUpdates(i), (m_SnapshotDelta.GetDataRate(i)/m_SnapshotDelta.GetDataUpdates(i))/8); - Graphics()->QuadsText(2, 100+y*12, 16, 1,1,1,1, aBuffer); + Graphics()->QuadsText(2, 100+y*12, 16, aBuffer); y++; } } } str_format(aBuffer, sizeof(aBuffer), "pred: %d ms", - (int)((m_PredictedTime.Get(Now)-m_GameTime.Get(Now))*1000/(float)time_freq())); - Graphics()->QuadsText(2, 70, 16, 1,1,1,1, aBuffer); + (int)((m_PredictedTime.Get(Now)-m_GameTime[g_Config.m_ClDummy].Get(Now))*1000/(float)time_freq())); + Graphics()->QuadsText(2, 70, 16, aBuffer); + Graphics()->QuadsEnd(); // render graphs if(g_Config.m_DbgGraphs) @@ -762,21 +979,31 @@ void CClient::Quit() const char *CClient::ErrorString() { - return m_NetClient.ErrorString(); + return m_NetClient[0].ErrorString(); } void CClient::Render() { - // if(g_Config.m_GfxClear) - if(g_Config.m_ClShowEntities && g_Config.m_ClDDRaceCheats) - Graphics()->Clear(0.3f,0.3f,0.6f); - else if(g_Config.m_GfxClear) - Graphics()->Clear(1,1,0); + if(g_Config.m_ClOverlayEntities) + { + vec3 bg = HslToRgb(vec3(g_Config.m_ClBackgroundEntitiesHue/255.0f, g_Config.m_ClBackgroundEntitiesSat/255.0f, g_Config.m_ClBackgroundEntitiesLht/255.0f)); + Graphics()->Clear(bg.r, bg.g, bg.b); + } + else + { + vec3 bg = HslToRgb(vec3(g_Config.m_ClBackgroundHue/255.0f, g_Config.m_ClBackgroundSat/255.0f, g_Config.m_ClBackgroundLht/255.0f)); + Graphics()->Clear(bg.r, bg.g, bg.b); + } GameClient()->OnRender(); DebugRender(); } +vec3 CClient::GetColorV3(int v) +{ + return HslToRgb(vec3(((v>>16)&0xff)/255.0f, ((v>>8)&0xff)/255.0f, 0.5f+(v&0xff)/255.0f*0.5f)); +} + const char *CClient::LoadMap(const char *pName, const char *pFilename, unsigned WantedCrc) { static char aErrorMsg[128]; @@ -804,7 +1031,7 @@ const char *CClient::LoadMap(const char *pName, const char *pFilename, unsigned char aBuf[256]; str_format(aBuf, sizeof(aBuf), "loaded map '%s'", pFilename); m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client", aBuf); - m_RecivedSnapshots = 0; + m_RecivedSnapshots[g_Config.m_ClDummy] = 0; str_copy(m_aCurrentMap, pName, sizeof(m_aCurrentMap)); m_CurrentMapCrc = m_pMap->Crc(); @@ -866,7 +1093,6 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket) // version info if(pPacket->m_DataSize == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(GAME_RELEASE_VERSION)) && mem_comp(pPacket->m_pData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0) - { char *pVersionData = (char*)pPacket->m_pData + sizeof(VERSIONSRV_VERSION); int VersionMatch = !mem_comp(pVersionData, GAME_RELEASE_VERSION, sizeof(GAME_RELEASE_VERSION)); @@ -884,17 +1110,52 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket) if(!VersionMatch) { str_copy(m_aVersionStr, aVersion, sizeof(m_aVersionStr)); + +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + if (g_Config.m_ClAutoUpdate) + { + str_format(aBuf, sizeof(aBuf), "Checking for updates"); + ((CGameClient *) GameClient())->m_pMenus->RenderUpdating(aBuf); + ((CGameClient *) GameClient())->AutoUpdate()->CheckUpdates(((CGameClient *) GameClient())->m_pMenus); + } +#endif } - // request the map version list now + // request the news CNetChunk Packet; mem_zero(&Packet, sizeof(Packet)); Packet.m_ClientID = -1; Packet.m_Address = m_VersionInfo.m_VersionServeraddr.m_Addr; + Packet.m_pData = VERSIONSRV_GETNEWS; + Packet.m_DataSize = sizeof(VERSIONSRV_GETNEWS); + Packet.m_Flags = NETSENDFLAG_CONNLESS; + m_NetClient[g_Config.m_ClDummy].Send(&Packet); + + // request the map version list now + mem_zero(&Packet, sizeof(Packet)); + Packet.m_ClientID = -1; + Packet.m_Address = m_VersionInfo.m_VersionServeraddr.m_Addr; Packet.m_pData = VERSIONSRV_GETMAPLIST; Packet.m_DataSize = sizeof(VERSIONSRV_GETMAPLIST); Packet.m_Flags = NETSENDFLAG_CONNLESS; - m_NetClient.Send(&Packet); + m_NetClient[g_Config.m_ClDummy].Send(&Packet); + } + + // news + if(pPacket->m_DataSize == (int)(sizeof(VERSIONSRV_NEWS) + NEWS_SIZE) && + mem_comp(pPacket->m_pData, VERSIONSRV_NEWS, sizeof(VERSIONSRV_NEWS)) == 0) + { + if (mem_comp(m_aNews, (char*)pPacket->m_pData + sizeof(VERSIONSRV_NEWS), NEWS_SIZE)) + g_Config.m_UiPage = CMenus::PAGE_NEWS; + + mem_copy(m_aNews, (char*)pPacket->m_pData + sizeof(VERSIONSRV_NEWS), NEWS_SIZE); + + IOHANDLE newsFile = m_pStorage->OpenFile("ddnet-news.txt", IOFLAG_WRITE, IStorage::TYPE_SAVE); + if (newsFile) + { + io_write(newsFile, m_aNews, sizeof(m_aNews)); + io_close(newsFile); + } } // map version list @@ -907,6 +1168,31 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket) } } + //server count from master server + if(pPacket->m_DataSize == (int) sizeof(SERVERBROWSE_COUNT) + 2 && mem_comp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0) + { + unsigned char *pP = (unsigned char*) pPacket->m_pData; + pP += sizeof(SERVERBROWSE_COUNT); + int ServerCount = ((*pP)<<8) | *(pP+1); + int ServerID = -1; + for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++) + { + if(!m_pMasterServer->IsValid(i)) + continue; + NETADDR tmp = m_pMasterServer->GetAddr(i); + if(net_addr_comp(&pPacket->m_Address, &tmp) == 0) + { + ServerID = i; + break; + } + } + if(ServerCount > -1 && ServerID != -1) + { + m_pMasterServer->SetCount(ServerID, ServerCount); + if(g_Config.m_Debug) + dbg_msg("MasterCount", "Server %d got %d servers", ServerID, ServerCount); + } + } // server list from master server if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_LIST) && mem_comp(pPacket->m_pData, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) @@ -935,7 +1221,7 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket) { NETADDR Addr; - static char IPV4Mapping[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; + static unsigned char IPV4Mapping[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; // copy address if(!mem_comp(IPV4Mapping, pAddrs[i].m_aIp, sizeof(IPV4Mapping))) @@ -965,6 +1251,11 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket) CUnpacker Up; CServerInfo Info = {0}; + CServerBrowser::CServerEntry *pEntry = m_ServerBrowser.Find(pPacket->m_Address); + // Don't add info if we already got info64 + if(pEntry && pEntry->m_Info.m_MaxClients > VANILLA_MAX_CLIENTS) + return; + Up.Reset((unsigned char*)pPacket->m_pData+sizeof(SERVERBROWSE_INFO), pPacket->m_DataSize-sizeof(SERVERBROWSE_INFO)); int Token = str_toint(Up.GetString()); str_copy(Info.m_aVersion, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(Info.m_aVersion)); @@ -998,14 +1289,87 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket) // sort players qsort(Info.m_aClients, Info.m_NumClients, sizeof(*Info.m_aClients), PlayerScoreComp); + m_ServerBrowser.Set(pPacket->m_Address, IServerBrowser::SET_TOKEN, Token, &Info); + + if(net_addr_comp(&m_ServerAddress, &pPacket->m_Address) == 0) + { + if(m_CurrentServerInfo.m_MaxClients <= VANILLA_MAX_CLIENTS) + { + mem_copy(&m_CurrentServerInfo, &Info, sizeof(m_CurrentServerInfo)); + m_CurrentServerInfo.m_NetAddr = m_ServerAddress; + m_CurrentServerInfoRequestTime = -1; + } + } + + if (strstr(Info.m_aGameType, "64") || strstr(Info.m_aName, "64")) + { + pEntry = m_ServerBrowser.Find(pPacket->m_Address); + if (pEntry && m_ServerBrowser.IsRefreshing()) + { + pEntry->m_Is64 = true; + m_ServerBrowser.RequestImpl64(pEntry->m_Addr, pEntry); // Force a quick update + //m_ServerBrowser.QueueRequest(pEntry); + } + } + } + } + + // server info 64 + if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_INFO64) && mem_comp(pPacket->m_pData, SERVERBROWSE_INFO64, sizeof(SERVERBROWSE_INFO64)) == 0) + { + // we got ze info + CUnpacker Up; + CServerInfo NewInfo = {0}; + CServerBrowser::CServerEntry *pEntry = m_ServerBrowser.Find(pPacket->m_Address); + CServerInfo &Info = NewInfo; + + if (pEntry) + Info = pEntry->m_Info; + + Up.Reset((unsigned char*)pPacket->m_pData+sizeof(SERVERBROWSE_INFO64), pPacket->m_DataSize-sizeof(SERVERBROWSE_INFO64)); + int Token = str_toint(Up.GetString()); + str_copy(Info.m_aVersion, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(Info.m_aVersion)); + str_copy(Info.m_aName, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(Info.m_aName)); + str_copy(Info.m_aMap, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(Info.m_aMap)); + str_copy(Info.m_aGameType, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(Info.m_aGameType)); + Info.m_Flags = str_toint(Up.GetString()); + Info.m_NumPlayers = str_toint(Up.GetString()); + Info.m_MaxPlayers = str_toint(Up.GetString()); + Info.m_NumClients = str_toint(Up.GetString()); + Info.m_MaxClients = str_toint(Up.GetString()); + + // don't add invalid info to the server browser list + if(Info.m_NumClients < 0 || Info.m_NumClients > MAX_CLIENTS || Info.m_MaxClients < 0 || Info.m_MaxClients > MAX_CLIENTS || + Info.m_NumPlayers < 0 || Info.m_NumPlayers > Info.m_NumClients || Info.m_MaxPlayers < 0 || Info.m_MaxPlayers > Info.m_MaxClients) + return; + + net_addr_str(&pPacket->m_Address, Info.m_aAddress, sizeof(Info.m_aAddress), true); + + int Offset = Up.GetInt(); + + for(int i = max(Offset, 0); i < max(Offset, 0) + 24 && i < MAX_CLIENTS; i++) + { + str_copy(Info.m_aClients[i].m_aName, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(Info.m_aClients[i].m_aName)); + str_copy(Info.m_aClients[i].m_aClan, Up.GetString(CUnpacker::SANITIZE_CC|CUnpacker::SKIP_START_WHITESPACES), sizeof(Info.m_aClients[i].m_aClan)); + Info.m_aClients[i].m_Country = str_toint(Up.GetString()); + Info.m_aClients[i].m_Score = str_toint(Up.GetString()); + Info.m_aClients[i].m_Player = str_toint(Up.GetString()) != 0 ? true : false; + } + + if(!Up.Error()) + { + // sort players + if (Offset + 24 >= Info.m_NumClients) + qsort(Info.m_aClients, Info.m_NumClients, sizeof(*Info.m_aClients), PlayerScoreComp); + + m_ServerBrowser.Set(pPacket->m_Address, IServerBrowser::SET_TOKEN, Token, &Info); + if(net_addr_comp(&m_ServerAddress, &pPacket->m_Address) == 0) { mem_copy(&m_CurrentServerInfo, &Info, sizeof(m_CurrentServerInfo)); m_CurrentServerInfo.m_NetAddr = m_ServerAddress; m_CurrentServerInfoRequestTime = -1; } - else - m_ServerBrowser.Set(pPacket->m_Address, IServerBrowser::SET_TOKEN, Token, &Info); } } } @@ -1036,6 +1400,9 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) if(Unpacker.Error()) return; + g_Config.m_ClDummy = 0; + m_DummyConnected = false; + // check for valid standard map if(!m_MapChecker.IsMapValid(pMap, MapCrc, MapSize)) pError = "invalid standard map"; @@ -1170,7 +1537,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) { int Result = Unpacker.GetInt(); if(Unpacker.Error() == 0) - m_RconAuthed = Result; + m_RconAuthed[g_Config.m_ClDummy] = Result; int Old = m_UseTempRconCommands; m_UseTempRconCommands = Unpacker.GetInt(); if(Unpacker.Error() != 0) @@ -1194,14 +1561,15 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) { int InputPredTick = Unpacker.GetInt(); int TimeLeft = Unpacker.GetInt(); + int64 Now = time_get(); // adjust our prediction time int64 Target = 0; for(int k = 0; k < 200; k++) { - if(m_aInputs[k].m_Tick == InputPredTick) + if(m_aInputs[g_Config.m_ClDummy][k].m_Tick == InputPredTick) { - Target = m_aInputs[k].m_PredictedTime + (time_get() - m_aInputs[k].m_Time); + Target = m_aInputs[g_Config.m_ClDummy][k].m_PredictedTime + (Now - m_aInputs[g_Config.m_ClDummy][k].m_Time); Target = Target - (int64)(((TimeLeft-PREDICTION_MARGIN)/1000.0f)*time_freq()); break; } @@ -1221,6 +1589,10 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) int CompleteSize = 0; const char *pData = 0; + // only allow packets from the server we actually want + if(net_addr_comp(&pPacket->m_Address, &m_ServerAddress)) + return; + // we are not allowed to process snapshot yet if(State() < IClient::STATE_LOADING) return; @@ -1242,12 +1614,12 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) if(Unpacker.Error()) return; - if(GameTick >= m_CurrentRecvTick) + if(GameTick >= m_CurrentRecvTick[g_Config.m_ClDummy]) { - if(GameTick != m_CurrentRecvTick) + if(GameTick != m_CurrentRecvTick[g_Config.m_ClDummy]) { m_SnapshotParts = 0; - m_CurrentRecvTick = GameTick; + m_CurrentRecvTick[g_Config.m_ClDummy] = GameTick; } // TODO: clean this up abit @@ -1277,7 +1649,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) // find delta if(DeltaTick >= 0) { - int DeltashotSize = m_SnapshotStorage.Get(DeltaTick, 0, &pDeltaShot, 0); + int DeltashotSize = m_SnapshotStorage[g_Config.m_ClDummy].Get(DeltaTick, 0, &pDeltaShot, 0); if(DeltashotSize < 0) { @@ -1292,7 +1664,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) // ack snapshot // TODO: combine this with the input message - m_AckGameTick = -1; + m_AckGameTick[g_Config.m_ClDummy] = -1; return; } } @@ -1313,7 +1685,6 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) } // unpack delta - PurgeTick = DeltaTick; SnapSize = m_SnapshotDelta.UnpackDelta(pDeltaShot, pTmpBuffer3, pDeltaData, DeltaSize); if(SnapSize < 0) { @@ -1335,7 +1706,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) if(m_SnapCrcErrors > 10) { // to many errors, send reset - m_AckGameTick = -1; + m_AckGameTick[g_Config.m_ClDummy] = -1; SendInput(); m_SnapCrcErrors = 0; } @@ -1349,14 +1720,14 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) // purge old snapshots PurgeTick = DeltaTick; - if(m_aSnapshots[SNAP_PREV] && m_aSnapshots[SNAP_PREV]->m_Tick < PurgeTick) - PurgeTick = m_aSnapshots[SNAP_PREV]->m_Tick; - if(m_aSnapshots[SNAP_CURRENT] && m_aSnapshots[SNAP_CURRENT]->m_Tick < PurgeTick) - PurgeTick = m_aSnapshots[SNAP_PREV]->m_Tick; - m_SnapshotStorage.PurgeUntil(PurgeTick); + if(m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV] && m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick < PurgeTick) + PurgeTick = m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick; + if(m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] && m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick < PurgeTick) + PurgeTick = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick; + m_SnapshotStorage[g_Config.m_ClDummy].PurgeUntil(PurgeTick); // add new - m_SnapshotStorage.Add(GameTick, time_get(), SnapSize, pTmpBuffer3, 1); + m_SnapshotStorage[g_Config.m_ClDummy].Add(GameTick, time_get(), SnapSize, pTmpBuffer3, 1); // add snapshot to demo if(m_DemoRecorder.IsRecording()) @@ -1366,35 +1737,35 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) } // apply snapshot, cycle pointers - m_RecivedSnapshots++; + m_RecivedSnapshots[g_Config.m_ClDummy]++; - m_CurrentRecvTick = GameTick; + m_CurrentRecvTick[g_Config.m_ClDummy] = GameTick; // we got two snapshots until we see us self as connected - if(m_RecivedSnapshots == 2) + if(m_RecivedSnapshots[g_Config.m_ClDummy] == 2) { // start at 200ms and work from there m_PredictedTime.Init(GameTick*time_freq()/50); m_PredictedTime.SetAdjustSpeed(1, 1000.0f); - m_GameTime.Init((GameTick-1)*time_freq()/50); - m_aSnapshots[SNAP_PREV] = m_SnapshotStorage.m_pFirst; - m_aSnapshots[SNAP_CURRENT] = m_SnapshotStorage.m_pLast; + m_GameTime[g_Config.m_ClDummy].Init((GameTick-1)*time_freq()/50); + m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV] = m_SnapshotStorage[g_Config.m_ClDummy].m_pFirst; + m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] = m_SnapshotStorage[g_Config.m_ClDummy].m_pLast; m_LocalStartTime = time_get(); SetState(IClient::STATE_ONLINE); DemoRecorder_HandleAutoStart(); } // adjust game time - if(m_RecivedSnapshots > 2) + if(m_RecivedSnapshots[g_Config.m_ClDummy] > 2) { - int64 Now = m_GameTime.Get(time_get()); + int64 Now = m_GameTime[g_Config.m_ClDummy].Get(time_get()); int64 TickStart = GameTick*time_freq()/50; int64 TimeLeft = (TickStart-Now)*1000 / time_freq(); - m_GameTime.Update(&m_GametimeMarginGraph, (GameTick-1)*time_freq()/50, TimeLeft, 0); + m_GameTime[g_Config.m_ClDummy].Update(&m_GametimeMarginGraph, (GameTick-1)*time_freq()/50, TimeLeft, 0); } // ack snapshot - m_AckGameTick = GameTick; + m_AckGameTick[g_Config.m_ClDummy] = GameTick; } } } @@ -1409,24 +1780,247 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) } } +void CClient::ProcessServerPacketDummy(CNetChunk *pPacket) +{ + CUnpacker Unpacker; + Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize); + + // unpack msgid and system flag + int Msg = Unpacker.GetInt(); + int Sys = Msg&1; + Msg >>= 1; + + if(Unpacker.Error()) + return; + + if(Sys) + { + if(Msg == NETMSG_CON_READY) + { + m_DummyConnected = true; + g_Config.m_ClDummy = 1; + Rcon("crashmeplx"); + } + else if(Msg == NETMSG_SNAP || Msg == NETMSG_SNAPSINGLE || Msg == NETMSG_SNAPEMPTY) + { + int NumParts = 1; + int Part = 0; + int GameTick = Unpacker.GetInt(); + int DeltaTick = GameTick-Unpacker.GetInt(); + int PartSize = 0; + int Crc = 0; + int CompleteSize = 0; + const char *pData = 0; + + // only allow packets from the server we actually want + if(net_addr_comp(&pPacket->m_Address, &m_ServerAddress)) + return; + + // we are not allowed to process snapshot yet + if(State() < IClient::STATE_LOADING) + return; + + if(Msg == NETMSG_SNAP) + { + NumParts = Unpacker.GetInt(); + Part = Unpacker.GetInt(); + } + + if(Msg != NETMSG_SNAPEMPTY) + { + Crc = Unpacker.GetInt(); + PartSize = Unpacker.GetInt(); + } + + pData = (const char *)Unpacker.GetRaw(PartSize); + + if(Unpacker.Error()) + return; + + if(GameTick >= m_CurrentRecvTick[!g_Config.m_ClDummy]) + { + if(GameTick != m_CurrentRecvTick[!g_Config.m_ClDummy]) + { + m_SnapshotParts = 0; + m_CurrentRecvTick[!g_Config.m_ClDummy] = GameTick; + } + + // TODO: clean this up abit + mem_copy((char*)m_aSnapshotIncommingData + Part*MAX_SNAPSHOT_PACKSIZE, pData, PartSize); + m_SnapshotParts |= 1<= 0) + { + int DeltashotSize = m_SnapshotStorage[!g_Config.m_ClDummy].Get(DeltaTick, 0, &pDeltaShot, 0); + + if(DeltashotSize < 0) + { + // couldn't find the delta snapshots that the server used + // to compress this snapshot. force the server to resync + if(g_Config.m_Debug) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "error, couldn't find the delta snapshot"); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client", aBuf); + } + + // ack snapshot + // TODO: combine this with the input message + m_AckGameTick[!g_Config.m_ClDummy] = -1; + return; + } + } + + // decompress snapshot + pDeltaData = m_SnapshotDelta.EmptyDelta(); + DeltaSize = sizeof(int)*3; + + if(CompleteSize) + { + int IntSize = CVariableInt::Decompress(m_aSnapshotIncommingData, CompleteSize, aTmpBuffer2); + + if(IntSize < 0) // failure during decompression, bail + return; + + pDeltaData = aTmpBuffer2; + DeltaSize = IntSize; + } + + // unpack delta + SnapSize = m_SnapshotDelta.UnpackDelta(pDeltaShot, pTmpBuffer3, pDeltaData, DeltaSize); + if(SnapSize < 0) + { + m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client", "delta unpack failed!"); + return; + } + + if(Msg != NETMSG_SNAPEMPTY && pTmpBuffer3->Crc() != Crc) + { + if(g_Config.m_Debug) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "snapshot crc error #%d - tick=%d wantedcrc=%d gotcrc=%d compressed_size=%d delta_tick=%d", + m_SnapCrcErrors, GameTick, Crc, pTmpBuffer3->Crc(), CompleteSize, DeltaTick); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client", aBuf); + } + + m_SnapCrcErrors++; + if(m_SnapCrcErrors > 10) + { + // to many errors, send reset + m_AckGameTick[!g_Config.m_ClDummy] = -1; + SendInput(); + m_SnapCrcErrors = 0; + } + return; + } + else + { + if(m_SnapCrcErrors) + m_SnapCrcErrors--; + } + + // purge old snapshots + PurgeTick = DeltaTick; + if(m_aSnapshots[!g_Config.m_ClDummy][SNAP_PREV] && m_aSnapshots[!g_Config.m_ClDummy][SNAP_PREV]->m_Tick < PurgeTick) + PurgeTick = m_aSnapshots[!g_Config.m_ClDummy][SNAP_PREV]->m_Tick; + if(m_aSnapshots[!g_Config.m_ClDummy][SNAP_CURRENT] && m_aSnapshots[!g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick < PurgeTick) + PurgeTick = m_aSnapshots[!g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick; + m_SnapshotStorage[!g_Config.m_ClDummy].PurgeUntil(PurgeTick); + + // add new + m_SnapshotStorage[!g_Config.m_ClDummy].Add(GameTick, time_get(), SnapSize, pTmpBuffer3, 1); + + // add snapshot to demo + //if(m_DemoRecorder.IsRecording()) + //{ + // // write snapshot + // m_DemoRecorder.RecordSnapshot(GameTick, pTmpBuffer3, SnapSize); + //} + + // apply snapshot, cycle pointers + m_RecivedSnapshots[!g_Config.m_ClDummy]++; + + m_CurrentRecvTick[!g_Config.m_ClDummy] = GameTick; + + // we got two snapshots until we see us self as connected + if(m_RecivedSnapshots[!g_Config.m_ClDummy] == 2) + { + // start at 200ms and work from there + //m_PredictedTime[!g_Config.m_ClDummy].Init(GameTick*time_freq()/50); + //m_PredictedTime[!g_Config.m_ClDummy].SetAdjustSpeed(1, 1000.0f); + m_GameTime[!g_Config.m_ClDummy].Init((GameTick-1)*time_freq()/50); + m_aSnapshots[!g_Config.m_ClDummy][SNAP_PREV] = m_SnapshotStorage[!g_Config.m_ClDummy].m_pFirst; + m_aSnapshots[!g_Config.m_ClDummy][SNAP_CURRENT] = m_SnapshotStorage[!g_Config.m_ClDummy].m_pLast; + m_LocalStartTime = time_get(); + SetState(IClient::STATE_ONLINE); + + //DemoRecorder_HandleAutoStart(); + } + + // adjust game time + if(m_RecivedSnapshots[!g_Config.m_ClDummy] > 2) + { + int64 Now = m_GameTime[!g_Config.m_ClDummy].Get(time_get()); + int64 TickStart = GameTick*time_freq()/50; + int64 TimeLeft = (TickStart-Now)*1000 / time_freq(); + m_GameTime[!g_Config.m_ClDummy].Update(&m_GametimeMarginGraph, (GameTick-1)*time_freq()/50, TimeLeft, 0); + } + + // ack snapshot + m_AckGameTick[!g_Config.m_ClDummy] = GameTick; + } + } + } + } + else + { + GameClient()->OnMessage(Msg, &Unpacker, 1); + } +} + void CClient::PumpNetwork() { - m_NetClient.Update(); + for(int i=0; i<2; i++) + { + m_NetClient[i].Update(); + } if(State() != IClient::STATE_DEMOPLAYBACK) { // check for errors - if(State() != IClient::STATE_OFFLINE && State() != IClient::STATE_QUITING && m_NetClient.State() == NETSTATE_OFFLINE) + if(State() != IClient::STATE_OFFLINE && State() != IClient::STATE_QUITING && m_NetClient[0].State() == NETSTATE_OFFLINE) { SetState(IClient::STATE_OFFLINE); Disconnect(); char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "offline error='%s'", m_NetClient.ErrorString()); + str_format(aBuf, sizeof(aBuf), "offline error='%s'", m_NetClient[0].ErrorString()); m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf); } // - if(State() == IClient::STATE_CONNECTING && m_NetClient.State() == NETSTATE_ONLINE) + if(State() == IClient::STATE_CONNECTING && m_NetClient[0].State() == NETSTATE_ONLINE) { // we switched to online m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", "connected, sending info"); @@ -1437,12 +2031,29 @@ void CClient::PumpNetwork() // process packets CNetChunk Packet; - while(m_NetClient.Recv(&Packet)) + for(int i=0; i < 2; i++) { - if(Packet.m_ClientID == -1) - ProcessConnlessPacket(&Packet); - else - ProcessServerPacket(&Packet); + while(m_NetClient[i].Recv(&Packet)) + { + if(Packet.m_ClientID == -1 || i > 1) + { + ProcessConnlessPacket(&Packet); + } + else if(i > 0 && i < 2) + { + if(g_Config.m_ClDummy) + ProcessServerPacket(&Packet); //self + else + ProcessServerPacketDummy(&Packet); //multiclient + } + else + { + if(g_Config.m_ClDummy) + ProcessServerPacketDummy(&Packet); //multiclient + else + ProcessServerPacket(&Packet); //self + } + } } } @@ -1451,16 +2062,16 @@ void CClient::OnDemoPlayerSnapshot(void *pData, int Size) // update ticks, they could have changed const CDemoPlayer::CPlaybackInfo *pInfo = m_DemoPlayer.Info(); CSnapshotStorage::CHolder *pTemp; - m_CurGameTick = pInfo->m_Info.m_CurrentTick; - m_PrevGameTick = pInfo->m_PreviousTick; + m_CurGameTick[g_Config.m_ClDummy] = pInfo->m_Info.m_CurrentTick; + m_PrevGameTick[g_Config.m_ClDummy] = pInfo->m_PreviousTick; // handle snapshots - pTemp = m_aSnapshots[SNAP_PREV]; - m_aSnapshots[SNAP_PREV] = m_aSnapshots[SNAP_CURRENT]; - m_aSnapshots[SNAP_CURRENT] = pTemp; + pTemp = m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]; + m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV] = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]; + m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] = pTemp; - mem_copy(m_aSnapshots[SNAP_CURRENT]->m_pSnap, pData, Size); - mem_copy(m_aSnapshots[SNAP_CURRENT]->m_pAltSnap, pData, Size); + mem_copy(m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_pSnap, pData, Size); + mem_copy(m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_pAltSnap, pData, Size); GameClient()->OnNewSnapshot(); } @@ -1522,10 +2133,10 @@ void CClient::Update() { // update timers const CDemoPlayer::CPlaybackInfo *pInfo = m_DemoPlayer.Info(); - m_CurGameTick = pInfo->m_Info.m_CurrentTick; - m_PrevGameTick = pInfo->m_PreviousTick; - m_GameIntraTick = pInfo->m_IntraTick; - m_GameTickTime = pInfo->m_TickTime; + m_CurGameTick[g_Config.m_ClDummy] = pInfo->m_Info.m_CurrentTick; + m_PrevGameTick[g_Config.m_ClDummy] = pInfo->m_PreviousTick; + m_GameIntraTick[g_Config.m_ClDummy] = pInfo->m_IntraTick; + m_GameTickTime[g_Config.m_ClDummy] = pInfo->m_TickTime; } else { @@ -1533,32 +2144,32 @@ void CClient::Update() Disconnect(); } } - else if(State() == IClient::STATE_ONLINE && m_RecivedSnapshots >= 3) + else if(State() == IClient::STATE_ONLINE && m_RecivedSnapshots[g_Config.m_ClDummy] >= 3) { // switch snapshot int Repredict = 0; int64 Freq = time_freq(); - int64 Now = m_GameTime.Get(time_get()); + int64 Now = m_GameTime[g_Config.m_ClDummy].Get(time_get()); int64 PredNow = m_PredictedTime.Get(time_get()); while(1) { - CSnapshotStorage::CHolder *pCur = m_aSnapshots[SNAP_CURRENT]; + CSnapshotStorage::CHolder *pCur = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]; int64 TickStart = (pCur->m_Tick)*time_freq()/50; if(TickStart < Now) { - CSnapshotStorage::CHolder *pNext = m_aSnapshots[SNAP_CURRENT]->m_pNext; + CSnapshotStorage::CHolder *pNext = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_pNext; if(pNext) { - m_aSnapshots[SNAP_PREV] = m_aSnapshots[SNAP_CURRENT]; - m_aSnapshots[SNAP_CURRENT] = pNext; + m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV] = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]; + m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] = pNext; // set ticks - m_CurGameTick = m_aSnapshots[SNAP_CURRENT]->m_Tick; - m_PrevGameTick = m_aSnapshots[SNAP_PREV]->m_Tick; + m_CurGameTick[g_Config.m_ClDummy] = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick; + m_PrevGameTick[g_Config.m_ClDummy] = m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick; - if(m_aSnapshots[SNAP_CURRENT] && m_aSnapshots[SNAP_PREV]) + if (m_LastDummy2 == (bool)g_Config.m_ClDummy && m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] && m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]) { GameClient()->OnNewSnapshot(); Repredict = 1; @@ -1571,29 +2182,34 @@ void CClient::Update() break; } - if(m_aSnapshots[SNAP_CURRENT] && m_aSnapshots[SNAP_PREV]) + if (m_LastDummy2 != (bool)g_Config.m_ClDummy) + { + m_LastDummy2 = g_Config.m_ClDummy; + } + + if(m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] && m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]) { - int64 CurtickStart = (m_aSnapshots[SNAP_CURRENT]->m_Tick)*time_freq()/50; - int64 PrevtickStart = (m_aSnapshots[SNAP_PREV]->m_Tick)*time_freq()/50; + int64 CurtickStart = (m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick)*time_freq()/50; + int64 PrevtickStart = (m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick)*time_freq()/50; int PrevPredTick = (int)(PredNow*50/time_freq()); int NewPredTick = PrevPredTick+1; - m_GameIntraTick = (Now - PrevtickStart) / (float)(CurtickStart-PrevtickStart); - m_GameTickTime = (Now - PrevtickStart) / (float)Freq; //(float)SERVER_TICK_SPEED); + m_GameIntraTick[g_Config.m_ClDummy] = (Now - PrevtickStart) / (float)(CurtickStart-PrevtickStart); + m_GameTickTime[g_Config.m_ClDummy] = (Now - PrevtickStart) / (float)Freq; //(float)SERVER_TICK_SPEED); CurtickStart = NewPredTick*time_freq()/50; PrevtickStart = PrevPredTick*time_freq()/50; - m_PredIntraTick = (PredNow - PrevtickStart) / (float)(CurtickStart-PrevtickStart); + m_PredIntraTick[g_Config.m_ClDummy] = (PredNow - PrevtickStart) / (float)(CurtickStart-PrevtickStart); - if(NewPredTick < m_aSnapshots[SNAP_PREV]->m_Tick-SERVER_TICK_SPEED || NewPredTick > m_aSnapshots[SNAP_PREV]->m_Tick+SERVER_TICK_SPEED) + if(NewPredTick < m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick-SERVER_TICK_SPEED || NewPredTick > m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick+SERVER_TICK_SPEED) { m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client", "prediction time reset!"); - m_PredictedTime.Init(m_aSnapshots[SNAP_CURRENT]->m_Tick*time_freq()/50); + m_PredictedTime.Init(m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick*time_freq()/50); } - if(NewPredTick > m_PredTick) + if(NewPredTick > m_PredTick[g_Config.m_ClDummy]) { - m_PredTick = NewPredTick; + m_PredTick[g_Config.m_ClDummy] = NewPredTick; Repredict = 1; // send input @@ -1604,7 +2220,7 @@ void CClient::Update() // only do sane predictions if(Repredict) { - if(m_PredTick > m_CurGameTick && m_PredTick < m_CurGameTick+50) + if(m_PredTick[g_Config.m_ClDummy] > m_CurGameTick[g_Config.m_ClDummy] && m_PredTick[g_Config.m_ClDummy] < m_CurGameTick[g_Config.m_ClDummy]+50) GameClient()->OnPredict(); } @@ -1658,8 +2274,8 @@ void CClient::VersionUpdate() { if(m_VersionInfo.m_State == CVersionInfo::STATE_INIT) { - Engine()->HostLookup(&m_VersionInfo.m_VersionServeraddr, g_Config.m_ClVersionServer, m_NetClient.NetType()); - m_VersionInfo.m_State = CVersionInfo::STATE_START; + Engine()->HostLookup(&m_VersionInfo.m_VersionServeraddr, g_Config.m_ClDDNetVersionServer, m_NetClient[0].NetType()); + m_VersionInfo.m_State = CVersionInfo::STATE_START; } else if(m_VersionInfo.m_State == CVersionInfo::STATE_START) { @@ -1677,7 +2293,7 @@ void CClient::VersionUpdate() Packet.m_DataSize = sizeof(VERSIONSRV_GETVERSION); Packet.m_Flags = NETSENDFLAG_CONNLESS; - m_NetClient.Send(&Packet); + m_NetClient[0].Send(&Packet); m_VersionInfo.m_State = CVersionInfo::STATE_READY; } } @@ -1688,6 +2304,9 @@ void CClient::RegisterInterfaces() Kernel()->RegisterInterface(static_cast(&m_DemoRecorder)); Kernel()->RegisterInterface(static_cast(&m_DemoPlayer)); Kernel()->RegisterInterface(static_cast(&m_ServerBrowser)); +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + Kernel()->RegisterInterface(static_cast(&m_AutoUpdate)); +#endif Kernel()->RegisterInterface(static_cast(&m_Friends)); } @@ -1702,11 +2321,23 @@ void CClient::InitInterfaces() m_pInput = Kernel()->RequestInterface(); m_pMap = Kernel()->RequestInterface(); m_pMasterServer = Kernel()->RequestInterface(); +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + m_pAutoUpdate = Kernel()->RequestInterface(); +#endif m_pStorage = Kernel()->RequestInterface(); - // - m_ServerBrowser.SetBaseInfo(&m_NetClient, m_pGameClient->NetVersion()); + for(int i=0;i<2;i++) + { + m_ServerBrowser.SetBaseInfo(&m_NetClient[i], m_pGameClient->NetVersion()); + } m_Friends.Init(); + + IOHANDLE newsFile = m_pStorage->OpenFile("ddnet-news.txt", IOFLAG_READ, IStorage::TYPE_SAVE); + if (newsFile) + { + io_read(newsFile, m_aNews, NEWS_SIZE); + io_close(newsFile); + } } void CClient::Run() @@ -1727,7 +2358,7 @@ void CClient::Run() // init graphics { - if(g_Config.m_GfxThreaded) + if(g_Config.m_GfxThreadedOld) m_pGraphics = CreateEngineGraphicsThreaded(); else m_pGraphics = CreateEngineGraphics(); @@ -1749,15 +2380,23 @@ void CClient::Run() // open socket { NETADDR BindAddr; - if(g_Config.m_Bindaddr[0] == 0 || net_host_lookup(g_Config.m_Bindaddr, &BindAddr, NETTYPE_ALL) != 0) + if(g_Config.m_Bindaddr[0] && net_host_lookup(g_Config.m_Bindaddr, &BindAddr, NETTYPE_ALL) == 0) + { + // got bindaddr + BindAddr.type = NETTYPE_ALL; + } + else { mem_zero(&BindAddr, sizeof(BindAddr)); BindAddr.type = NETTYPE_ALL; } - if(!m_NetClient.Open(BindAddr, 0)) + for(int i = 0; i < 2; i++) { - dbg_msg("client", "couldn't start network"); - return; + if(!m_NetClient[i].Open(BindAddr, 0)) + { + dbg_msg("client", "couldn't open socket"); + return; + } } } @@ -1768,7 +2407,7 @@ void CClient::Run() Input()->Init(); // start refreshing addresses while we load - MasterServer()->RefreshAddresses(m_NetClient.NetType()); + MasterServer()->RefreshAddresses(m_NetClient[0].NetType()); // init the editor m_pEditor->Init(); @@ -1802,6 +2441,11 @@ void CClient::Run() // process pending commands m_pConsole->StoreCommands(false); + bool LastD = false; + bool LastQ = false; + bool LastE = false; + bool LastG = false; + while (1) { // @@ -1852,19 +2496,19 @@ void CClient::Run() } // panic quit button - if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyPressed('q')) + if(CtrlShiftKey('q', LastQ)) { Quit(); break; } - if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyDown('d')) + if(CtrlShiftKey('d', LastD)) g_Config.m_Debug ^= 1; - if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyDown('g')) + if(CtrlShiftKey('g', LastG)) g_Config.m_DbgGraphs ^= 1; - if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyDown('e')) + if(CtrlShiftKey('e', LastE)) { g_Config.m_ClEditor = g_Config.m_ClEditor^1; Input()->MouseModeRelative(); @@ -1890,7 +2534,7 @@ void CClient::Run() Update(); - if(!g_Config.m_GfxAsyncRender || m_pGraphics->IsIdle()) + if((g_Config.m_GfxBackgroundRender || m_pGraphics->WindowOpen()) && (!g_Config.m_GfxAsyncRenderOld || m_pGraphics->IsIdle())) { m_RenderFrames++; @@ -1931,6 +2575,12 @@ void CClient::Run() m_pGraphics->Swap(); } } + if(Input()->VideoRestartNeeded()) + { + m_pGraphics->Init(); + LoadData(); + GameClient()->OnInit(); + } } AutoScreenshot_Cleanup(); @@ -1940,10 +2590,11 @@ void CClient::Run() break; // beNice - if(g_Config.m_DbgStress) + if(g_Config.m_ClCpuThrottle) + net_socket_read_wait(m_NetClient[0].m_Socket, g_Config.m_ClCpuThrottle); + //thread_sleep(g_Config.m_ClCpuThrottle); + else if(g_Config.m_DbgStress || !m_pGraphics->WindowActive()) thread_sleep(5); - else if(g_Config.m_ClCpuThrottle || !m_pGraphics->WindowActive()) - thread_sleep(1); if(g_Config.m_DbgHitch) { @@ -1984,6 +2635,18 @@ void CClient::Run() } } +bool CClient::CtrlShiftKey(int Key, bool &Last) +{ + if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && !Last && Input()->KeyPressed(Key)) + { + Last = true; + return true; + } + else if (Last && !Input()->KeyPressed(Key)) + Last = false; + + return false; +} void CClient::Con_Connect(IConsole::IResult *pResult, void *pUserData) { @@ -1997,6 +2660,18 @@ void CClient::Con_Disconnect(IConsole::IResult *pResult, void *pUserData) pSelf->Disconnect(); } +void CClient::Con_DummyConnect(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + pSelf->DummyConnect(); +} + +void CClient::Con_DummyDisconnect(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + pSelf->DummyDisconnect(0); +} + void CClient::Con_Quit(IConsole::IResult *pResult, void *pUserData) { CClient *pSelf = (CClient *)pUserData; @@ -2080,7 +2755,7 @@ const char *CClient::DemoPlayer_Play(const char *pFilename, int StorageType) int Crc; const char *pError; Disconnect(); - m_NetClient.ResetErrorString(); + m_NetClient[0].ResetErrorString(); // try to start playback m_DemoPlayer.SetListner(this); @@ -2105,18 +2780,18 @@ const char *CClient::DemoPlayer_Play(const char *pFilename, int StorageType) // setup buffers mem_zero(m_aDemorecSnapshotData, sizeof(m_aDemorecSnapshotData)); - m_aSnapshots[SNAP_CURRENT] = &m_aDemorecSnapshotHolders[SNAP_CURRENT]; - m_aSnapshots[SNAP_PREV] = &m_aDemorecSnapshotHolders[SNAP_PREV]; + m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] = &m_aDemorecSnapshotHolders[SNAP_CURRENT]; + m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV] = &m_aDemorecSnapshotHolders[SNAP_PREV]; - m_aSnapshots[SNAP_CURRENT]->m_pSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_CURRENT][0]; - m_aSnapshots[SNAP_CURRENT]->m_pAltSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_CURRENT][1]; - m_aSnapshots[SNAP_CURRENT]->m_SnapSize = 0; - m_aSnapshots[SNAP_CURRENT]->m_Tick = -1; + m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_pSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_CURRENT][0]; + m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_pAltSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_CURRENT][1]; + m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_SnapSize = 0; + m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick = -1; - m_aSnapshots[SNAP_PREV]->m_pSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_PREV][0]; - m_aSnapshots[SNAP_PREV]->m_pAltSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_PREV][1]; - m_aSnapshots[SNAP_PREV]->m_SnapSize = 0; - m_aSnapshots[SNAP_PREV]->m_Tick = -1; + m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_pSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_PREV][0]; + m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_pAltSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_PREV][1]; + m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_SnapSize = 0; + m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick = -1; // enter demo playback state SetState(IClient::STATE_DEMOPLAYBACK); @@ -2224,6 +2899,9 @@ void CClient::RegisterCommands() m_pConsole->Register("stoprecord", "", CFGFLAG_SERVER, 0, 0, "Stop recording"); m_pConsole->Register("reload", "", CFGFLAG_SERVER, 0, 0, "Reload the map"); + m_pConsole->Register("dummy_connect", "", CFGFLAG_CLIENT, Con_DummyConnect, this, "connect dummy"); + m_pConsole->Register("dummy_disconnect", "", CFGFLAG_CLIENT, Con_DummyDisconnect, this, "disconnect dummy"); + m_pConsole->Register("quit", "", CFGFLAG_CLIENT|CFGFLAG_STORE, Con_Quit, this, "Quit Teeworlds"); m_pConsole->Register("exit", "", CFGFLAG_CLIENT|CFGFLAG_STORE, Con_Quit, this, "Quit Teeworlds"); m_pConsole->Register("minimize", "", CFGFLAG_CLIENT|CFGFLAG_STORE, Con_Minimize, this, "Minimize Teeworlds"); @@ -2271,7 +2949,7 @@ static CClient *CreateClient() Upstream latency */ -#if defined(CONF_PLATFORM_MACOSX) +#if defined(CONF_PLATFORM_MACOSX) || defined(__ANDROID__) extern "C" int SDL_main(int argc, char **argv_) // ignore_convention { const char **argv = const_cast(argv_); @@ -2371,6 +3049,10 @@ int main(int argc, const char **argv) // ignore_convention // write down the config and quit pConfig->Save(); +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + pClient->AutoUpdate()->ExecuteExit(); +#endif + return 0; } diff --git a/src/engine/client/client.h b/src/engine/client/client.h index 948561d977..8ab26f91ed 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -63,6 +63,9 @@ class CClient : public IClient, public CDemoPlayer::IListner IEngineMap *m_pMap; IConsole *m_pConsole; IStorage *m_pStorage; +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + IAutoUpdate *m_pAutoUpdate; +#endif IEngineMasterServer *m_pMasterServer; enum @@ -71,10 +74,13 @@ class CClient : public IClient, public CDemoPlayer::IListner PREDICTION_MARGIN=1000/50/2, // magic network prediction value }; - class CNetClient m_NetClient; + class CNetClient m_NetClient[2]; class CDemoPlayer m_DemoPlayer; class CDemoRecorder m_DemoRecorder; class CServerBrowser m_ServerBrowser; +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + class CAutoUpdate m_AutoUpdate; +#endif class CFriends m_Friends; class CMapChecker m_MapChecker; @@ -98,9 +104,9 @@ class CClient : public IClient, public CDemoPlayer::IListner bool m_SoundInitFailed; bool m_ResortServerBrowser; - int m_AckGameTick; - int m_CurrentRecvTick; - int m_RconAuthed; + int m_AckGameTick[2]; + int m_CurrentRecvTick[2]; + int m_RconAuthed[2]; int m_UseTempRconCommands; // version-checking @@ -126,7 +132,7 @@ class CClient : public IClient, public CDemoPlayer::IListner int m_MapdownloadTotalsize; // time - CSmoothTime m_GameTime; + CSmoothTime m_GameTime[2]; CSmoothTime m_PredictedTime; // input @@ -136,9 +142,13 @@ class CClient : public IClient, public CDemoPlayer::IListner int m_Tick; // the tick that the input is for int64 m_PredictedTime; // prediction latency when we sent this input int64 m_Time; - } m_aInputs[200]; + } m_aInputs[2][200]; - int m_CurrentInput; + int m_CurrentInput[2]; + bool m_LastDummy; + bool m_LastDummy2; + CNetObj_PlayerInput DummyInput; + CNetObj_PlayerInput HammerInput; // graphs CGraph m_InputtimeMarginGraph; @@ -146,10 +156,10 @@ class CClient : public IClient, public CDemoPlayer::IListner CGraph m_FpsGraph; // the game snapshots are modifiable by the game - class CSnapshotStorage m_SnapshotStorage; - CSnapshotStorage::CHolder *m_aSnapshots[NUM_SNAPSHOT_TYPES]; + class CSnapshotStorage m_SnapshotStorage[2]; + CSnapshotStorage::CHolder *m_aSnapshots[2][NUM_SNAPSHOT_TYPES]; - int m_RecivedSnapshots; + int m_RecivedSnapshots[2]; char m_aSnapshotIncommingData[CSnapshot::MAX_SIZE]; class CSnapshotStorage::CHolder m_aDemorecSnapshotHolders[NUM_SNAPSHOT_TYPES]; @@ -175,11 +185,10 @@ class CClient : public IClient, public CDemoPlayer::IListner class CHostLookup m_VersionServeraddr; } m_VersionInfo; - semaphore m_GfxRenderSemaphore; - semaphore m_GfxStateSemaphore; volatile int m_GfxState; static void GraphicsThreadProxy(void *pThis) { ((CClient*)pThis)->GraphicsThread(); } void GraphicsThread(); + vec3 GetColorV3(int v); public: IEngine *Engine() { return m_pEngine; } @@ -189,18 +198,22 @@ class CClient : public IClient, public CDemoPlayer::IListner IGameClient *GameClient() { return m_pGameClient; } IEngineMasterServer *MasterServer() { return m_pMasterServer; } IStorage *Storage() { return m_pStorage; } +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + IAutoUpdate *AutoUpdate() { return m_pAutoUpdate; } +#endif CClient(); // ----- send functions ----- virtual int SendMsg(CMsgPacker *pMsg, int Flags); + virtual int SendMsgExY(CMsgPacker *pMsg, int Flags, bool System=true, int NetClient=1); int SendMsgEx(CMsgPacker *pMsg, int Flags, bool System=true); void SendInfo(); void SendEnterGame(); void SendReady(); - virtual bool RconAuthed() { return m_RconAuthed != 0; } + virtual bool RconAuthed() { return m_RconAuthed[g_Config.m_ClDummy] != 0; } virtual bool UseTempRconCommands() { return m_UseTempRconCommands != 0; } void RconAuth(const char *pName, const char *pPassword); virtual void Rcon(const char *pCmd); @@ -231,6 +244,13 @@ class CClient : public IClient, public CDemoPlayer::IListner void DisconnectWithReason(const char *pReason); virtual void Disconnect(); + virtual void DummyDisconnect(const char *pReason); + virtual void DummyConnect(); + virtual bool DummyConnected(); + void DummyInfo(); + int m_DummyConnected; + int m_LastDummyConnectTime; + int m_Fire; virtual void GetServerInfo(CServerInfo *pServerInfo); void ServerInfoRequest(); @@ -259,6 +279,7 @@ class CClient : public IClient, public CDemoPlayer::IListner void ProcessConnlessPacket(CNetChunk *pPacket); void ProcessServerPacket(CNetChunk *pPacket); + void ProcessServerPacketDummy(CNetChunk *pPacket); virtual int MapDownloadAmount() { return m_MapdownloadAmount; } virtual int MapDownloadTotalsize() { return m_MapdownloadTotalsize; } @@ -275,9 +296,14 @@ class CClient : public IClient, public CDemoPlayer::IListner void Run(); + bool CtrlShiftKey(int Key, bool &Last); static void Con_Connect(IConsole::IResult *pResult, void *pUserData); static void Con_Disconnect(IConsole::IResult *pResult, void *pUserData); + + static void Con_DummyConnect(IConsole::IResult *pResult, void *pUserData); + static void Con_DummyDisconnect(IConsole::IResult *pResult, void *pUserData); + static void Con_Quit(IConsole::IResult *pResult, void *pUserData); static void Con_Minimize(IConsole::IResult *pResult, void *pUserData); static void Con_Ping(IConsole::IResult *pResult, void *pUserData); diff --git a/src/engine/client/friends.cpp b/src/engine/client/friends.cpp index eca39edb8b..090777a750 100644 --- a/src/engine/client/friends.cpp +++ b/src/engine/client/friends.cpp @@ -37,7 +37,7 @@ void CFriends::Init() if(pConsole) { pConsole->Register("add_friend", "ss", CFGFLAG_CLIENT, ConAddFriend, this, "Add a friend"); - pConsole->Register("remove_Friend", "ss", CFGFLAG_CLIENT, ConRemoveFriend, this, "Remove a friend"); + pConsole->Register("remove_friend", "ss", CFGFLAG_CLIENT, ConRemoveFriend, this, "Remove a friend"); } } diff --git a/src/engine/client/graphics.cpp b/src/engine/client/graphics.cpp index 2111703e13..7fbbbc0bc9 100644 --- a/src/engine/client/graphics.cpp +++ b/src/engine/client/graphics.cpp @@ -6,7 +6,15 @@ #include #include "SDL.h" -#include "SDL_opengl.h" +#if defined(__ANDROID__) + #define GL_GLEXT_PROTOTYPES + #include + #include + #include + #define glOrtho glOrthof +#else + #include "SDL_opengl.h" +#endif #include #include @@ -22,6 +30,20 @@ #include "graphics.h" +#if defined(CONF_PLATFORM_MACOSX) + + class semaphore + { + SDL_sem *sem; + public: + semaphore() { sem = SDL_CreateSemaphore(0); } + ~semaphore() { SDL_DestroySemaphore(sem); } + void wait() { SDL_SemWait(sem); } + void signal() { SDL_SemPost(sem); } + }; +#endif + + static CVideoMode g_aFakeModes[] = { {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, @@ -70,7 +92,12 @@ void CGraphics_OpenGL::Flush() if(m_RenderEnable) { if(m_Drawing == DRAWING_QUADS) +#if defined(__ANDROID__) + for( unsigned i = 0, j = m_NumVertices; i < j; i += 4 ) + glDrawArrays(GL_TRIANGLE_FAN, i, 4); +#else glDrawArrays(GL_QUADS, 0, m_NumVertices); +#endif else if(m_Drawing == DRAWING_LINES) glDrawArrays(GL_LINES, 0, m_NumVertices); } @@ -342,6 +369,9 @@ int CGraphics_OpenGL::LoadTextureRaw(int Width, int Height, int Format, const vo Oglformat = GL_ALPHA; // upload texture +#if defined(__ANDROID__) + StoreOglformat = Oglformat; +#else if(g_Config.m_GfxTextureCompression) { StoreOglformat = GL_COMPRESSED_RGBA_ARB; @@ -358,6 +388,7 @@ int CGraphics_OpenGL::LoadTextureRaw(int Width, int Height, int Format, const vo else if(StoreFormat == CImageInfo::FORMAT_ALPHA) StoreOglformat = GL_ALPHA; } +#endif glGenTextures(1, &m_aTextures[Tex].m_Tex); glBindTexture(GL_TEXTURE_2D, m_aTextures[Tex].m_Tex); @@ -686,13 +717,10 @@ void CGraphics_OpenGL::QuadsDrawFreeform(const CFreeformItem *pArray, int Num) AddVertices(4*Num); } -void CGraphics_OpenGL::QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText) +void CGraphics_OpenGL::QuadsText(float x, float y, float Size, const char *pText) { float StartX = x; - QuadsBegin(); - SetColor(r,g,b,a); - while(*pText) { char c = *pText; @@ -716,8 +744,6 @@ void CGraphics_OpenGL::QuadsText(float x, float y, float Size, float r, float g, x += Size/2; } } - - QuadsEnd(); } int CGraphics_OpenGL::Init() @@ -761,12 +787,21 @@ int CGraphics_OpenGL::Init() int CGraphics_SDL::TryInit() { - m_ScreenWidth = g_Config.m_GfxScreenWidth; - m_ScreenHeight = g_Config.m_GfxScreenHeight; - const SDL_VideoInfo *pInfo = SDL_GetVideoInfo(); SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); // prevent stuck mouse cursor sdl-bug when loosing fullscreen focus in windows + // use current resolution as default +#ifndef __ANDROID__ + if(g_Config.m_GfxScreenWidth == 0 || g_Config.m_GfxScreenHeight == 0) +#endif + { + g_Config.m_GfxScreenWidth = pInfo->current_w; + g_Config.m_GfxScreenHeight = pInfo->current_h; + } + + m_ScreenWidth = g_Config.m_GfxScreenWidth; + m_ScreenHeight = g_Config.m_GfxScreenHeight; + // set flags int Flags = SDL_OPENGL; if(g_Config.m_DbgResizable) @@ -780,7 +815,15 @@ int CGraphics_SDL::TryInit() if(pInfo->blit_hw) // ignore_convention Flags |= SDL_HWACCEL; - if(g_Config.m_GfxFullscreen) + if(g_Config.m_GfxBorderless && g_Config.m_GfxFullscreen) + { + dbg_msg("gfx", "both borderless and fullscreen activated, disabling borderless"); + g_Config.m_GfxBorderless = 0; + } + + if(g_Config.m_GfxBorderless) + Flags |= SDL_NOFRAME; + else if(g_Config.m_GfxFullscreen) Flags |= SDL_FULLSCREEN; // set gl attributes @@ -799,7 +842,7 @@ int CGraphics_SDL::TryInit() SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, g_Config.m_GfxVsync); // set caption - SDL_WM_SetCaption("Teeworlds", "Teeworlds"); + SDL_WM_SetCaption("DDNet Client", "DDNet Client"); // create window m_pScreenSurface = SDL_SetVideoMode(m_ScreenWidth, m_ScreenHeight, 0, Flags); @@ -876,7 +919,7 @@ int CGraphics_SDL::Init() #ifdef CONF_FAMILY_WINDOWS if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) // ignore_convention - putenv("SDL_VIDEO_WINDOW_POS=8,27"); // ignore_convention + putenv("SDL_VIDEO_WINDOW_POS=center"); // ignore_convention #endif if(InitWindow() != 0) @@ -925,11 +968,19 @@ void CGraphics_SDL::TakeScreenshot(const char *pFilename) m_DoScreenshot = true; } +void CGraphics_SDL::TakeCustomScreenshot(const char *pFilename) +{ + str_copy(m_aScreenshotName, pFilename, sizeof(m_aScreenshotName)); + m_DoScreenshot = true; +} + + void CGraphics_SDL::Swap() { if(m_DoScreenshot) { - ScreenshotDirect(m_aScreenshotName); + if(WindowActive()) + ScreenshotDirect(m_aScreenshotName); m_DoScreenshot = false; } diff --git a/src/engine/client/graphics.h b/src/engine/client/graphics.h index fdd83aa7b3..7d0fcf7d0f 100644 --- a/src/engine/client/graphics.h +++ b/src/engine/client/graphics.h @@ -119,7 +119,7 @@ class CGraphics_OpenGL : public IEngineGraphics virtual void QuadsDraw(CQuadItem *pArray, int Num); virtual void QuadsDrawTL(const CQuadItem *pArray, int Num); virtual void QuadsDrawFreeform(const CFreeformItem *pArray, int Num); - virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText); + virtual void QuadsText(float x, float y, float Size, const char *pText); virtual int Init(); }; @@ -143,6 +143,7 @@ class CGraphics_SDL : public CGraphics_OpenGL virtual int WindowOpen(); virtual void TakeScreenshot(const char *pFilename); + virtual void TakeCustomScreenshot(const char *pFilename); virtual void Swap(); virtual int GetVideoModes(CVideoMode *pModes, int MaxModes); diff --git a/src/engine/client/graphics_threaded.cpp b/src/engine/client/graphics_threaded.cpp index 8a709557ed..bebe18b8e5 100644 --- a/src/engine/client/graphics_threaded.cpp +++ b/src/engine/client/graphics_threaded.cpp @@ -5,6 +5,10 @@ #include #include +#if defined(CONF_FAMILY_UNIX) +#include +#endif + #include #include @@ -126,41 +130,6 @@ void CGraphics_Threaded::Rotate4(const CCommandBuffer::SPoint &rCenter, CCommand } } -unsigned char CGraphics_Threaded::Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp) -{ - int Value = 0; - for(int x = 0; x < ScaleW; x++) - for(int y = 0; y < ScaleH; y++) - Value += pData[((v+y)*w+(u+x))*Bpp+Offset]; - return Value/(ScaleW*ScaleH); -} - -unsigned char *CGraphics_Threaded::Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData) -{ - unsigned char *pTmpData; - int ScaleW = Width/NewWidth; - int ScaleH = Height/NewHeight; - - int Bpp = 3; - if(Format == CImageInfo::FORMAT_RGBA) - Bpp = 4; - - pTmpData = (unsigned char *)mem_alloc(NewWidth*NewHeight*Bpp, 1); - - int c = 0; - for(int y = 0; y < NewHeight; y++) - for(int x = 0; x < NewWidth; x++, c++) - { - pTmpData[c*Bpp] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 0, ScaleW, ScaleH, Bpp); - pTmpData[c*Bpp+1] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 1, ScaleW, ScaleH, Bpp); - pTmpData[c*Bpp+2] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 2, ScaleW, ScaleH, Bpp); - if(Bpp == 4) - pTmpData[c*Bpp+3] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 3, ScaleW, ScaleH, Bpp); - } - - return pTmpData; -} - CGraphics_Threaded::CGraphics_Threaded() { m_State.m_ScreenTL.x = 0; @@ -247,7 +216,7 @@ void CGraphics_Threaded::WrapClamp() int CGraphics_Threaded::MemoryUsage() const { - return m_TextureMemoryUsage; + return m_pBackend->MemoryUsage(); } void CGraphics_Threaded::MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY) @@ -312,8 +281,7 @@ int CGraphics_Threaded::UnloadTexture(int Index) Cmd.m_Slot = Index; m_pCommandBuffer->AddCommand(Cmd); - m_aTextures[Index].m_Next = m_FirstFreeTexture; - m_TextureMemoryUsage -= m_aTextures[Index].m_MemSize; + m_aTextureIndices[Index] = m_FirstFreeTexture; m_FirstFreeTexture = Index; return 0; } @@ -326,6 +294,16 @@ static int ImageFormatToTexFormat(int Format) return CCommandBuffer::TEXFORMAT_RGBA; } +static int ImageFormatToPixelSize(int Format) +{ + switch(Format) + { + case CImageInfo::FORMAT_RGB: return 3; + case CImageInfo::FORMAT_ALPHA: return 1; + default: return 4; + } +} + int CGraphics_Threaded::LoadTextureRawSub(int TextureID, int x, int y, int Width, int Height, int Format, const void *pData) { @@ -338,13 +316,7 @@ int CGraphics_Threaded::LoadTextureRawSub(int TextureID, int x, int y, int Width Cmd.m_Format = ImageFormatToTexFormat(Format); // calculate memory usage - int PixelSize = 4; - if(Format == CImageInfo::FORMAT_RGB) - PixelSize = 3; - else if(Format == CImageInfo::FORMAT_ALPHA) - PixelSize = 1; - - int MemSize = Width*Height*PixelSize; + int MemSize = Width*Height*ImageFormatToPixelSize(Format); // copy texture data void *pTmpData = mem_alloc(MemSize, sizeof(void*)); @@ -364,13 +336,14 @@ int CGraphics_Threaded::LoadTextureRaw(int Width, int Height, int Format, const // grab texture int Tex = m_FirstFreeTexture; - m_FirstFreeTexture = m_aTextures[Tex].m_Next; - m_aTextures[Tex].m_Next = -1; + m_FirstFreeTexture = m_aTextureIndices[Tex]; + m_aTextureIndices[Tex] = -1; CCommandBuffer::SCommand_Texture_Create Cmd; Cmd.m_Slot = Tex; Cmd.m_Width = Width; Cmd.m_Height = Height; + Cmd.m_PixelSize = ImageFormatToPixelSize(Format); Cmd.m_Format = ImageFormatToTexFormat(Format); Cmd.m_StoreFormat = ImageFormatToTexFormat(StoreFormat); @@ -378,17 +351,13 @@ int CGraphics_Threaded::LoadTextureRaw(int Width, int Height, int Format, const Cmd.m_Flags = 0; if(Flags&IGraphics::TEXLOAD_NOMIPMAPS) Cmd.m_Flags |= CCommandBuffer::TEXFLAG_NOMIPMAPS; - - // calculate memory usage - int PixelSize = 4; - if(Format == CImageInfo::FORMAT_RGB) - PixelSize = 3; - else if(Format == CImageInfo::FORMAT_ALPHA) - PixelSize = 1; - - int MemSize = Width*Height*PixelSize; + if(g_Config.m_GfxTextureCompression) + Cmd.m_Flags |= CCommandBuffer::TEXFLAG_COMPRESSED; + if(g_Config.m_GfxTextureQuality || Flags&TEXLOAD_NORESAMPLE) + Cmd.m_Flags |= CCommandBuffer::TEXFLAG_QUALITY; // copy texture data + int MemSize = Width*Height*Cmd.m_PixelSize; void *pTmpData = mem_alloc(MemSize, sizeof(void*)); mem_copy(pTmpData, pData, MemSize); Cmd.m_pData = pTmpData; @@ -396,17 +365,6 @@ int CGraphics_Threaded::LoadTextureRaw(int Width, int Height, int Format, const // m_pCommandBuffer->AddCommand(Cmd); - // calculate memory usage - int MemUsage = MemSize; - while(Width > 2 && Height > 2) - { - Width>>=1; - Height>>=1; - MemUsage += Width*Height*PixelSize; - } - - m_TextureMemoryUsage += MemUsage; - //mem_free(pTmpData); return Tex; } @@ -492,7 +450,7 @@ void CGraphics_Threaded::KickCommandBuffer() m_pCommandBuffer->Reset(); } -void CGraphics_Threaded::ScreenshotDirect(const char *pFilename) +void CGraphics_Threaded::ScreenshotDirect() { // add swap command CImageInfo Image; @@ -512,7 +470,7 @@ void CGraphics_Threaded::ScreenshotDirect(const char *pFilename) char aWholePath[1024]; png_t Png; // ignore_convention - IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE, aWholePath, sizeof(aWholePath)); + IOHANDLE File = m_pStorage->OpenFile(m_aScreenshotName, IOFLAG_WRITE, IStorage::TYPE_SAVE, aWholePath, sizeof(aWholePath)); if(File) io_close(File); @@ -694,13 +652,10 @@ void CGraphics_Threaded::QuadsDrawFreeform(const CFreeformItem *pArray, int Num) AddVertices(4*Num); } -void CGraphics_Threaded::QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText) +void CGraphics_Threaded::QuadsText(float x, float y, float Size, const char *pText) { float StartX = x; - QuadsBegin(); - SetColor(r,g,b,a); - while(*pText) { char c = *pText; @@ -724,18 +679,23 @@ void CGraphics_Threaded::QuadsText(float x, float y, float Size, float r, float x += Size/2; } } - - QuadsEnd(); } int CGraphics_Threaded::IssueInit() { int Flags = 0; - if(g_Config.m_GfxFullscreen) Flags |= IGraphicsBackend::INITFLAG_FULLSCREEN; + if(g_Config.m_GfxBorderless && g_Config.m_GfxFullscreen) + { + dbg_msg("gfx", "both borderless and fullscreen activated, disabling borderless"); + g_Config.m_GfxBorderless = 0; + } + + if(g_Config.m_GfxBorderless) Flags |= IGraphicsBackend::INITFLAG_BORDERLESS; + else if(g_Config.m_GfxFullscreen) Flags |= IGraphicsBackend::INITFLAG_FULLSCREEN; if(g_Config.m_GfxVsync) Flags |= IGraphicsBackend::INITFLAG_VSYNC; if(g_Config.m_DbgResizable) Flags |= IGraphicsBackend::INITFLAG_RESIZABLE; - return m_pBackend->Init("Teeworlds", g_Config.m_GfxScreenWidth, g_Config.m_GfxScreenHeight, g_Config.m_GfxFsaaSamples, Flags); + return m_pBackend->Init("DDNet Client", &g_Config.m_GfxScreenWidth, &g_Config.m_GfxScreenHeight, g_Config.m_GfxFsaaSamples, Flags); } int CGraphics_Threaded::InitWindow() @@ -785,9 +745,9 @@ int CGraphics_Threaded::Init() // init textures m_FirstFreeTexture = 0; - for(int i = 0; i < MAX_TEXTURES; i++) - m_aTextures[i].m_Next = i+1; - m_aTextures[MAX_TEXTURES-1].m_Next = -1; + for(int i = 0; i < MAX_TEXTURES-1; i++) + m_aTextureIndices[i] = i+1; + m_aTextureIndices[MAX_TEXTURES-1] = -1; m_pBackend = CreateGraphicsBackend(); if(InitWindow() != 0) @@ -799,7 +759,7 @@ int CGraphics_Threaded::Init() // create command buffers for(int i = 0; i < NUM_CMDBUFFERS; i++) - m_apCommandBuffers[i] = new CCommandBuffer(128*1024, 2*1024*1024); + m_apCommandBuffers[i] = new CCommandBuffer(256*1024, 2*1024*1024); m_pCommandBuffer = m_apCommandBuffers[0]; // create null texture, will get id=0 @@ -857,12 +817,19 @@ void CGraphics_Threaded::TakeScreenshot(const char *pFilename) m_DoScreenshot = true; } +void CGraphics_Threaded::TakeCustomScreenshot(const char *pFilename) +{ + str_copy(m_aScreenshotName, pFilename, sizeof(m_aScreenshotName)); + m_DoScreenshot = true; +} + void CGraphics_Threaded::Swap() { // TODO: screenshot support if(m_DoScreenshot) { - ScreenshotDirect(m_aScreenshotName); + if(WindowActive()) + ScreenshotDirect(); m_DoScreenshot = false; } diff --git a/src/engine/client/graphics_threaded.h b/src/engine/client/graphics_threaded.h index f90f818d9e..a3931c6e29 100644 --- a/src/engine/client/graphics_threaded.h +++ b/src/engine/client/graphics_threaded.h @@ -57,7 +57,8 @@ class CCommandBuffer { // commadn groups CMDGROUP_CORE = 0, // commands that everyone has to implement - CMDGROUP_PLATFORM = 10000, // commands specific to a platform + CMDGROUP_PLATFORM_OPENGL = 10000, // commands specific to a platform + CMDGROUP_PLATFORM_SDL = 20000, // CMD_NOP = CMDGROUP_CORE, @@ -94,13 +95,8 @@ class CCommandBuffer TEXFORMAT_ALPHA, TEXFLAG_NOMIPMAPS = 1, - }; - - enum - { - INITFLAG_FULLSCREEN = 1, - INITFLAG_VSYNC = 2, - INITFLAG_RESIZABLE = 4, + TEXFLAG_COMPRESSED = 2, + TEXFLAG_QUALITY = 4, }; enum @@ -217,6 +213,7 @@ class CCommandBuffer int m_Width; int m_Height; + int m_PixelSize; int m_Format; int m_StoreFormat; int m_Flags; @@ -300,11 +297,16 @@ class IGraphicsBackend INITFLAG_FULLSCREEN = 1, INITFLAG_VSYNC = 2, INITFLAG_RESIZABLE = 4, + INITFLAG_BORDERLESS = 8, }; - virtual int Init(const char *pName, int Width, int Height, int FsaaSamples, int Flags) = 0; + virtual ~IGraphicsBackend() {} + + virtual int Init(const char *pName, int *Width, int *Height, int FsaaSamples, int Flags) = 0; virtual int Shutdown() = 0; + virtual int MemoryUsage() const = 0; + virtual void Minimize() = 0; virtual void Maximize() = 0; virtual int WindowActive() = 0; @@ -354,15 +356,7 @@ class CGraphics_Threaded : public IEngineGraphics int m_InvalidTexture; - struct CTexture - { - int m_State; - int m_MemSize; - int m_Flags; - int m_Next; - }; - - CTexture m_aTextures[MAX_TEXTURES]; + int m_aTextureIndices[MAX_TEXTURES]; int m_FirstFreeTexture; int m_TextureMemoryUsage; @@ -370,9 +364,6 @@ class CGraphics_Threaded : public IEngineGraphics void AddVertices(int Count); void Rotate4(const CCommandBuffer::SPoint &rCenter, CCommandBuffer::SVertex *pPoints); - static unsigned char Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp); - static unsigned char *Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData); - void KickCommandBuffer(); int IssueInit(); @@ -407,7 +398,7 @@ class CGraphics_Threaded : public IEngineGraphics virtual int LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags); virtual int LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType); - void ScreenshotDirect(const char *pFilename); + void ScreenshotDirect(); virtual void TextureSet(int TextureID); @@ -428,7 +419,7 @@ class CGraphics_Threaded : public IEngineGraphics virtual void QuadsDraw(CQuadItem *pArray, int Num); virtual void QuadsDrawTL(const CQuadItem *pArray, int Num); virtual void QuadsDrawFreeform(const CFreeformItem *pArray, int Num); - virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText); + virtual void QuadsText(float x, float y, float Size, const char *pText); virtual void Minimize(); virtual void Maximize(); @@ -440,6 +431,7 @@ class CGraphics_Threaded : public IEngineGraphics virtual void Shutdown(); virtual void TakeScreenshot(const char *pFilename); + virtual void TakeCustomScreenshot(const char *pFilename); virtual void Swap(); virtual int GetVideoModes(CVideoMode *pModes, int MaxModes); diff --git a/src/engine/client/input.cpp b/src/engine/client/input.cpp index 7ff8d6fe26..672e2f6fdf 100644 --- a/src/engine/client/input.cpp +++ b/src/engine/client/input.cpp @@ -41,6 +41,8 @@ CInput::CInput() m_ReleaseDelta = -1; m_NumEvents = 0; + + m_VideoRestartNeeded = 0; } void CInput::Init() @@ -52,6 +54,12 @@ void CInput::Init() void CInput::MouseRelative(float *x, float *y) { +#if defined(__ANDROID__) // No relative mouse on Android + int nx = 0, ny = 0; + SDL_GetMouseState(&nx, &ny); + *x = nx; + *y = ny; +#else int nx = 0, ny = 0; float Sens = g_Config.m_InpMousesens/100.0f; @@ -69,6 +77,7 @@ void CInput::MouseRelative(float *x, float *y) *x = nx*Sens; *y = ny*Sens; +#endif } void CInput::MouseModeAbsolute() @@ -144,6 +153,7 @@ int CInput::Update() if(i&SDL_BUTTON(6)) m_aInputState[m_InputCurrent][KEY_MOUSE_6] = 1; if(i&SDL_BUTTON(7)) m_aInputState[m_InputCurrent][KEY_MOUSE_7] = 1; if(i&SDL_BUTTON(8)) m_aInputState[m_InputCurrent][KEY_MOUSE_8] = 1; + if(i&SDL_BUTTON(9)) m_aInputState[m_InputCurrent][KEY_MOUSE_9] = 1; { SDL_Event Event; @@ -186,11 +196,18 @@ int CInput::Update() if(Event.button.button == 6) Key = KEY_MOUSE_6; // ignore_convention if(Event.button.button == 7) Key = KEY_MOUSE_7; // ignore_convention if(Event.button.button == 8) Key = KEY_MOUSE_8; // ignore_convention + if(Event.button.button == 9) Key = KEY_MOUSE_9; // ignore_convention break; // other messages case SDL_QUIT: return 1; + +#if defined(__ANDROID__) + case SDL_VIDEORESIZE: + m_VideoRestartNeeded = 1; + break; +#endif } // @@ -208,5 +225,14 @@ int CInput::Update() return 0; } +int CInput::VideoRestartNeeded() +{ + if( m_VideoRestartNeeded ) + { + m_VideoRestartNeeded = 0; + return 1; + } + return 0; +} IEngineInput *CreateEngineInput() { return new CInput; } diff --git a/src/engine/client/input.h b/src/engine/client/input.h index 34f880f793..528c1e4d34 100644 --- a/src/engine/client/input.h +++ b/src/engine/client/input.h @@ -12,6 +12,8 @@ class CInput : public IEngineInput int64 m_LastRelease; int64 m_ReleaseDelta; + int m_VideoRestartNeeded; + void AddEvent(int Unicode, int Key, int Flags); IEngineGraphics *Graphics() { return m_pGraphics; } @@ -32,6 +34,8 @@ class CInput : public IEngineInput int ButtonPressed(int Button) { return m_aInputState[m_InputCurrent][Button]; } virtual int Update(); + + virtual int VideoRestartNeeded(); }; #endif diff --git a/src/engine/client/keynames.h b/src/engine/client/keynames.h index 7f790abe86..538f17431d 100644 --- a/src/engine/client/keynames.h +++ b/src/engine/client/keynames.h @@ -343,7 +343,7 @@ const char g_aaKeyStrings[512][16] = "mouse8", "mousewheelup", "mousewheeldown", - "&333", + "mouse9", "&334", "&335", "&336", diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index 4dda9da987..e8012f0cba 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -18,7 +18,6 @@ #include #include "serverbrowser.h" - class SortWrap { typedef bool (CServerBrowser::*SortFunc)(int, int) const; @@ -344,7 +343,7 @@ void CServerBrowser::QueueRequest(CServerEntry *pEntry) else m_pFirstReqServer = pEntry; m_pLastReqServer = pEntry; - + pEntry->m_pNextReq = 0; m_NumRequests++; } @@ -421,12 +420,14 @@ CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr) void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo) { + static int temp = 0; CServerEntry *pEntry = 0; if(Type == IServerBrowser::SET_MASTER_ADD) { if(m_ServerlistType != IServerBrowser::TYPE_INTERNET) return; - + m_LastPacketTick = 0; + ++temp; if(!Find(Addr)) { pEntry = Add(Addr); @@ -457,8 +458,11 @@ void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServer SetInfo(pEntry, *pInfo); if(m_ServerlistType == IServerBrowser::TYPE_LAN) pEntry->m_Info.m_Latency = min(static_cast((time_get()-m_BroadcastTime)*1000/time_freq()), 999); - else + else if (pEntry->m_RequestTime > 0) + { pEntry->m_Info.m_Latency = min(static_cast((time_get()-pEntry->m_RequestTime)*1000/time_freq()), 999); + pEntry->m_RequestTime = -1; // Request has been answered + } RemoveRequest(pEntry); } } @@ -476,7 +480,7 @@ void CServerBrowser::Refresh(int Type) m_pFirstReqServer = 0; m_pLastReqServer = 0; m_NumRequests = 0; - + m_CurrentMaxRequests = g_Config.m_BrMaxRequests; // next token m_CurrentToken = (m_CurrentToken+1)&0xff; @@ -495,7 +499,7 @@ void CServerBrowser::Refresh(int Type) /* do the broadcast version */ Packet.m_ClientID = -1; mem_zero(&Packet, sizeof(Packet)); - Packet.m_Address.type = NETTYPE_ALL|NETTYPE_LINK_BROADCAST; + Packet.m_Address.type = m_pNetClient->NetType()|NETTYPE_LINK_BROADCAST; Packet.m_Flags = NETSENDFLAG_CONNLESS; Packet.m_DataSize = sizeof(Buffer); Packet.m_pData = Buffer; @@ -548,35 +552,111 @@ void CServerBrowser::RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) cons pEntry->m_RequestTime = time_get(); } +void CServerBrowser::RequestImpl64(const NETADDR &Addr, CServerEntry *pEntry) const +{ + unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO64)+1]; + CNetChunk Packet; + + if(g_Config.m_Debug) + { + char aAddrStr[NETADDR_MAXSTRSIZE]; + net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true); + char aBuf[256]; + str_format(aBuf, sizeof(aBuf),"requesting server info 64 from %s", aAddrStr); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf); + } + + mem_copy(Buffer, SERVERBROWSE_GETINFO64, sizeof(SERVERBROWSE_GETINFO64)); + Buffer[sizeof(SERVERBROWSE_GETINFO64)] = m_CurrentToken; + + Packet.m_ClientID = -1; + Packet.m_Address = Addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(Buffer); + Packet.m_pData = Buffer; + + m_pNetClient->Send(&Packet); + + if(pEntry) + pEntry->m_RequestTime = time_get(); +} + void CServerBrowser::Request(const NETADDR &Addr) const { + // Call both because we can't know what kind the server is + RequestImpl64(Addr, 0); RequestImpl(Addr, 0); } void CServerBrowser::Update(bool ForceResort) -{ +{ int64 Timeout = time_freq(); int64 Now = time_get(); int Count; CServerEntry *pEntry, *pNext; - + // do server list requests if(m_NeedRefresh && !m_pMasterServer->IsRefreshing()) { NETADDR Addr; - CNetChunk Packet; - int i; + CNetChunk Packet; + int i = 0; m_NeedRefresh = 0; + m_MasterServerCount = -1; + mem_zero(&Packet, sizeof(Packet)); + Packet.m_ClientID = -1; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_GETCOUNT); + Packet.m_pData = SERVERBROWSE_GETCOUNT; + for(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++) + { + if(!m_pMasterServer->IsValid(i)) + continue; + + Addr = m_pMasterServer->GetAddr(i); + m_pMasterServer->SetCount(i, -1); + Packet.m_Address = Addr; + m_pNetClient->Send(&Packet); + if(g_Config.m_Debug) + { + dbg_msg("client_srvbrowse", "Count-Request sent to %d", i); + } + } + } + + //Check if all server counts arrived + if(m_MasterServerCount == -1) + { + m_MasterServerCount = 0; + for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++) + { + if(!m_pMasterServer->IsValid(i)) + continue; + int Count = m_pMasterServer->GetCount(i); + if(Count == -1) + { + /* ignore Server + m_MasterServerCount = -1; + return; + // we don't have the required server information + */ + } + else + m_MasterServerCount += Count; + } + //request Server-List + NETADDR Addr; + CNetChunk Packet; mem_zero(&Packet, sizeof(Packet)); Packet.m_ClientID = -1; Packet.m_Flags = NETSENDFLAG_CONNLESS; Packet.m_DataSize = sizeof(SERVERBROWSE_GETLIST); Packet.m_pData = SERVERBROWSE_GETLIST; - for(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++) + for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++) { if(!m_pMasterServer->IsValid(i)) continue; @@ -585,48 +665,98 @@ void CServerBrowser::Update(bool ForceResort) Packet.m_Address = Addr; m_pNetClient->Send(&Packet); } - if(g_Config.m_Debug) - m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", "requesting server list"); - } - - // do timeouts - pEntry = m_pFirstReqServer; - while(1) - { - if(!pEntry) // no more entries - break; - - pNext = pEntry->m_pNextReq; - - if(pEntry->m_RequestTime && pEntry->m_RequestTime+Timeout < Now) { - // timeout - RemoveRequest(pEntry); + dbg_msg("client_srvbrowse", "ServerCount: %d, requesting server list", m_MasterServerCount); } - - pEntry = pNext; + m_LastPacketTick = 0; + } + else if(m_MasterServerCount > -1) + { + m_MasterServerCount = 0; + for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++) + { + if(!m_pMasterServer->IsValid(i)) + continue; + int Count = m_pMasterServer->GetCount(i); + if(Count == -1) + { + /* ignore Server + m_MasterServerCount = -1; + return; + // we don't have the required server information + */ + } + else + m_MasterServerCount += Count; + } + //if(g_Config.m_Debug) + //{ + // dbg_msg("client_srvbrowse", "ServerCount2: %d", m_MasterServerCount); + //} + } + if(m_MasterServerCount > m_NumRequests + m_LastPacketTick) + { + ++m_LastPacketTick; + return; //wait for more packets } - - // do timeouts pEntry = m_pFirstReqServer; Count = 0; while(1) { if(!pEntry) // no more entries break; - + if(pEntry->m_RequestTime && pEntry->m_RequestTime+Timeout < Now) + { + pEntry = pEntry->m_pNextReq; + continue; + } // no more then 10 concurrent requests - if(Count == g_Config.m_BrMaxRequests) + if(Count == m_CurrentMaxRequests) break; if(pEntry->m_RequestTime == 0) - RequestImpl(pEntry->m_Addr, pEntry); + { + if (pEntry->m_Is64) + RequestImpl64(pEntry->m_Addr, pEntry); + else + RequestImpl(pEntry->m_Addr, pEntry); + } Count++; pEntry = pEntry->m_pNextReq; } - + + if(m_pFirstReqServer && Count == 0 && m_CurrentMaxRequests > 1) //NO More current Server Requests + { + //reset old ones + pEntry = m_pFirstReqServer; + while(1) + { + if(!pEntry) // no more entries + break; + pEntry->m_RequestTime = 0; + pEntry = pEntry->m_pNextReq; + } + + //update max-requests + m_CurrentMaxRequests = m_CurrentMaxRequests/2; + if(m_CurrentMaxRequests < 1) + m_CurrentMaxRequests = 1; + } + else if(Count == 0 && m_CurrentMaxRequests == 1) //we reached the limit, just release all left requests. IF a server sends us a packet, a new request will be added automatically, so we can delete all + { + pEntry = m_pFirstReqServer; + while(1) + { + if(!pEntry) // no more entries + break; + pNext = pEntry->m_pNextReq; + RemoveRequest(pEntry); //release request + pEntry = pNext; + } + } + // check if we need to resort if(m_Sorthash != SortHash() || ForceResort) Sort(); diff --git a/src/engine/client/serverbrowser.h b/src/engine/client/serverbrowser.h index a9111d1514..c6f125241d 100644 --- a/src/engine/client/serverbrowser.h +++ b/src/engine/client/serverbrowser.h @@ -13,6 +13,7 @@ class CServerBrowser : public IServerBrowser public: NETADDR m_Addr; int64 m_RequestTime; + bool m_Is64; int m_GotInfo; CServerInfo m_Info; @@ -24,7 +25,7 @@ class CServerBrowser : public IServerBrowser enum { - MAX_FAVORITES=256 + MAX_FAVORITES=2048 }; CServerBrowser(); @@ -51,6 +52,10 @@ class CServerBrowser : public IServerBrowser void SetBaseInfo(class CNetClient *pClient, const char *pNetVersion); + void RequestImpl64(const NETADDR &Addr, CServerEntry *pEntry) const; + void QueueRequest(CServerEntry *pEntry); + CServerEntry *Find(const NETADDR &Addr); + private: CNetClient *m_pNetClient; IMasterServer *m_pMasterServer; @@ -70,7 +75,13 @@ class CServerBrowser : public IServerBrowser CServerEntry *m_pFirstReqServer; // request list CServerEntry *m_pLastReqServer; int m_NumRequests; - + int m_MasterServerCount; + + //used instead of g_Config.br_max_requests to get more servers + int m_CurrentMaxRequests; + + int m_LastPacketTick; + int m_NeedRefresh; int m_NumSortedServers; @@ -81,7 +92,7 @@ class CServerBrowser : public IServerBrowser int m_Sorthash; char m_aFilterString[64]; char m_aFilterGametypeString[128]; - + // the token is to keep server refresh separated from each other int m_CurrentToken; @@ -101,11 +112,9 @@ class CServerBrowser : public IServerBrowser void Sort(); int SortHash() const; - CServerEntry *Find(const NETADDR &Addr); CServerEntry *Add(const NETADDR &Addr); void RemoveRequest(CServerEntry *pEntry); - void QueueRequest(CServerEntry *pEntry); void RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) const; diff --git a/src/engine/client/sound.cpp b/src/engine/client/sound.cpp index 343fa2e863..61e40b35bb 100644 --- a/src/engine/client/sound.cpp +++ b/src/engine/client/sound.cpp @@ -205,7 +205,7 @@ int CSound::Init() m_pGraphics = Kernel()->RequestInterface(); m_pStorage = Kernel()->RequestInterface(); - SDL_AudioSpec Format; + SDL_AudioSpec Format, FormatOut; m_SoundLock = lock_create(); @@ -229,7 +229,7 @@ int CSound::Init() Format.userdata = NULL; // ignore_convention // Open the audio device and start playing sound! - if(SDL_OpenAudio(&Format, NULL) < 0) + if(SDL_OpenAudio(&Format, &FormatOut) < 0) { dbg_msg("client/sound", "unable to open audio: %s", SDL_GetError()); return -1; @@ -237,7 +237,7 @@ int CSound::Init() else dbg_msg("client/sound", "sound init successful"); - m_MaxFrames = g_Config.m_SndBufferSize*2; + m_MaxFrames = FormatOut.samples*2; m_pMixBuffer = (int *)mem_alloc(m_MaxFrames*2*sizeof(int), 1); SDL_PauseAudio(0); @@ -447,6 +447,22 @@ int CSound::Play(int ChannelID, int SampleID, int Flags, float x, float y) int VoiceID = -1; int i; + if(SampleID == 107) // GetSampleID(SOUND_CHAT_SERVER) + { + if(!g_Config.m_SndServerMessage) + return VoiceID; + } + else if(SampleID == 108) // GetSampleID(SOUND_CHAT_CLIENT) + {} + else if(SampleID == 109) // GetSampleID(SOUND_CHAT_HIGHLIGHT) + { + if(!g_Config.m_SndHighlight) + return VoiceID; + } + else if(!g_Config.m_SndGame) + return VoiceID; + + lock_wait(m_SoundLock); // search for voice diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp index af06fc1185..d838ef291b 100644 --- a/src/engine/client/text.cpp +++ b/src/engine/client/text.cpp @@ -639,7 +639,7 @@ class CTextRender : public IEngineTextRender Compare.m_Y = DrawY; Compare.m_Flags &= ~TEXTFLAG_RENDER; Compare.m_LineWidth = -1; - TextEx(&Compare, pText, Wlen); + TextEx(&Compare, pCurrent, Wlen); if(Compare.m_X-DrawX > pCursor->m_LineWidth) { diff --git a/src/engine/demo.h b/src/engine/demo.h index 7b7365c7e6..749518330a 100644 --- a/src/engine/demo.h +++ b/src/engine/demo.h @@ -21,6 +21,10 @@ struct CDemoHeader char m_aType[8]; char m_aLength[4]; char m_aTimestamp[20]; +}; + +struct CTimelineMarkers +{ char m_aNumTimelineMarkers[4]; char m_aTimelineMarkers[MAX_TIMELINE_MARKERS][4]; }; @@ -55,6 +59,7 @@ class IDemoPlayer : public IInterface virtual int SetPos(float Percent) = 0; virtual void Pause() = 0; virtual void Unpause() = 0; + virtual bool IsPlaying() const = 0; virtual const CInfo *BaseInfo() const = 0; virtual void GetDemoName(char *pBuffer, int BufferSize) const = 0; virtual bool GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader) const = 0; diff --git a/src/engine/friends.h b/src/engine/friends.h index 164e3461a1..c4f7974e18 100644 --- a/src/engine/friends.h +++ b/src/engine/friends.h @@ -25,7 +25,7 @@ class IFriends : public IInterface FRIEND_CLAN, FRIEND_PLAYER, - MAX_FRIENDS=128, + MAX_FRIENDS=1024, }; virtual void Init() = 0; diff --git a/src/engine/graphics.h b/src/engine/graphics.h index 7f272497ac..6a080a5ccf 100644 --- a/src/engine/graphics.h +++ b/src/engine/graphics.h @@ -120,7 +120,7 @@ class IGraphics : public IInterface : m_X0(x0), m_Y0(y0), m_X1(x1), m_Y1(y1), m_X2(x2), m_Y2(y2), m_X3(x3), m_Y3(y3) {} }; virtual void QuadsDrawFreeform(const CFreeformItem *pArray, int Num) = 0; - virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText) = 0; + virtual void QuadsText(float x, float y, float Size, const char *pText) = 0; struct CColorVertex { @@ -133,6 +133,7 @@ class IGraphics : public IInterface virtual void SetColor(float r, float g, float b, float a) = 0; virtual void TakeScreenshot(const char *pFilename) = 0; + virtual void TakeCustomScreenshot(const char *pFilename) = 0; virtual int GetVideoModes(CVideoMode *pModes, int MaxModes) = 0; virtual void Swap() = 0; diff --git a/src/engine/input.h b/src/engine/input.h index 93ceccd203..1242b28d15 100644 --- a/src/engine/input.h +++ b/src/engine/input.h @@ -89,6 +89,7 @@ class IEngineInput : public IInput public: virtual void Init() = 0; virtual int Update() = 0; + virtual int VideoRestartNeeded() = 0; }; extern IEngineInput *CreateEngineInput(); diff --git a/src/engine/keys.h b/src/engine/keys.h index 00e2c051f8..9a138b973c 100644 --- a/src/engine/keys.h +++ b/src/engine/keys.h @@ -248,6 +248,7 @@ enum KEY_MOUSE_8 = 330, KEY_MOUSE_WHEEL_UP = 331, KEY_MOUSE_WHEEL_DOWN = 332, + KEY_MOUSE_9 = 333, KEY_LAST, }; diff --git a/src/engine/map.h b/src/engine/map.h index d55c491266..e9bac7906c 100644 --- a/src/engine/map.h +++ b/src/engine/map.h @@ -10,6 +10,7 @@ class IMap : public IInterface MACRO_INTERFACE("map", 0) public: virtual void *GetData(int Index) = 0; + virtual int GetUncompressedDataSize(int Index) = 0; virtual void *GetDataSwapped(int Index) = 0; virtual void UnloadData(int Index) = 0; virtual void *GetItem(int Index, int *Type, int *pID) = 0; diff --git a/src/engine/masterserver.h b/src/engine/masterserver.h index 57433993d1..b26c4c69fe 100644 --- a/src/engine/masterserver.h +++ b/src/engine/masterserver.h @@ -24,6 +24,8 @@ class IMasterServer : public IInterface virtual void Update() = 0; virtual int IsRefreshing() = 0; virtual NETADDR GetAddr(int Index) = 0; + virtual void SetCount(int Index, int Count) = 0; + virtual int GetCount(int Index) = 0; virtual const char *GetName(int Index) = 0; virtual bool IsValid(int Index) = 0; }; diff --git a/src/engine/server.h b/src/engine/server.h index 52f944a3c0..602ac95669 100644 --- a/src/engine/server.h +++ b/src/engine/server.h @@ -4,6 +4,8 @@ #define ENGINE_SERVER_H #include "kernel.h" #include "message.h" +#include +#include class IServer : public IInterface { @@ -20,6 +22,7 @@ class IServer : public IInterface { const char *m_pName; int m_Latency; + int m_ClientVersion; }; int Tick() const { return m_CurrentGameTick; } @@ -32,11 +35,63 @@ class IServer : public IInterface virtual bool ClientIngame(int ClientID) = 0; virtual int GetClientInfo(int ClientID, CClientInfo *pInfo) = 0; virtual void GetClientAddr(int ClientID, char *pAddrStr, int Size) = 0; + virtual void RestrictRconOutput(int ClientID) = 0; virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientID) = 0; template int SendPackMsg(T *pMsg, int Flags, int ClientID) + { + int result = 0; + T tmp; + if (ClientID == -1) + { + for(int i = 0; i < MAX_CLIENTS; i++) + if(ClientIngame(i)) + { + mem_copy(&tmp, pMsg, sizeof(T)); + result = SendPackMsgTranslate(&tmp, Flags, i); + } + } else { + mem_copy(&tmp, pMsg, sizeof(T)); + result = SendPackMsgTranslate(&tmp, Flags, ClientID); + } + return result; + } + + template + int SendPackMsgTranslate(T *pMsg, int Flags, int ClientID) + { + return SendPackMsgOne(pMsg, Flags, ClientID); + } + + int SendPackMsgTranslate(CNetMsg_Sv_Emoticon *pMsg, int Flags, int ClientID) + { + return Translate(pMsg->m_ClientID, ClientID) && SendPackMsgOne(pMsg, Flags, ClientID); + } + + char msgbuf[1000]; + + int SendPackMsgTranslate(CNetMsg_Sv_Chat *pMsg, int Flags, int ClientID) + { + if (pMsg->m_ClientID >= 0 && !Translate(pMsg->m_ClientID, ClientID)) + { + str_format(msgbuf, sizeof(msgbuf), "%s: %s", ClientName(pMsg->m_ClientID), pMsg->m_pMessage); + pMsg->m_pMessage = msgbuf; + pMsg->m_ClientID = VANILLA_MAX_CLIENTS - 1; + } + return SendPackMsgOne(pMsg, Flags, ClientID); + } + + int SendPackMsgTranslate(CNetMsg_Sv_KillMsg *pMsg, int Flags, int ClientID) + { + if (!Translate(pMsg->m_Victim, ClientID)) return 0; + if (!Translate(pMsg->m_Killer, ClientID)) pMsg->m_Killer = pMsg->m_Victim; + return SendPackMsgOne(pMsg, Flags, ClientID); + } + + template + int SendPackMsgOne(T *pMsg, int Flags, int ClientID) { CMsgPacker Packer(pMsg->MsgID()); if(pMsg->Pack(&Packer)) @@ -44,6 +99,39 @@ class IServer : public IInterface return SendMsg(&Packer, Flags, ClientID); } + bool Translate(int& target, int client) + { + CClientInfo info; + GetClientInfo(client, &info); + if (info.m_ClientVersion >= VERSION_DDNET_OLD) + return true; + int* map = GetIdMap(client); + bool found = false; + for (int i = 0; i < VANILLA_MAX_CLIENTS; i++) + { + if (target == map[i]) + { + target = i; + found = true; + break; + } + } + return found; + } + + bool ReverseTranslate(int& target, int client) + { + CClientInfo info; + GetClientInfo(client, &info); + if (info.m_ClientVersion >= VERSION_DDNET_OLD) + return true; + int* map = GetIdMap(client); + if (map[target] == -1) + return false; + target = map[target]; + return true; + } + virtual void SetClientName(int ClientID, char const *pName) = 0; virtual void SetClientClan(int ClientID, char const *pClan) = 0; virtual void SetClientCountry(int ClientID, int Country) = 0; @@ -70,6 +158,8 @@ class IServer : public IInterface // DDRace virtual void GetClientAddr(int ClientID, NETADDR *pAddr) = 0; + + virtual int* GetIdMap(int ClientID) = 0; }; class IGameServer : public IInterface diff --git a/src/engine/server/fifoconsole.cpp b/src/engine/server/fifoconsole.cpp new file mode 100644 index 0000000000..a8352c8575 --- /dev/null +++ b/src/engine/server/fifoconsole.cpp @@ -0,0 +1,46 @@ +#include "fifoconsole.h" + +#include + +#include + +#if defined(CONF_FAMILY_UNIX) +#include +#include +#include + +FifoConsole::FifoConsole(IConsole *pConsole) +{ + void *m_pFifoThread = thread_create(ListenFifoThread, pConsole); + pthread_detach((pthread_t)m_pFifoThread); +} + +void FifoConsole::ListenFifoThread(void *pUser) +{ + IConsole *pConsole = (IConsole *)pUser; + + if (str_comp(g_Config.m_SvInputFifo, "") == 0) + return; + + mkfifo(g_Config.m_SvInputFifo, 0600); + + struct stat attribute; + stat(g_Config.m_SvInputFifo, &attribute); + + if(!S_ISFIFO(attribute.st_mode)) + return; + + std::ifstream f; + char aBuf[8192]; + + while (true) + { + f.open(g_Config.m_SvInputFifo); + while (f.getline(aBuf, sizeof(aBuf))) + { + pConsole->ExecuteLineFlag(aBuf, CFGFLAG_SERVER, -1); + } + f.close(); + } +} +#endif diff --git a/src/engine/server/fifoconsole.h b/src/engine/server/fifoconsole.h new file mode 100644 index 0000000000..cbd229bdd3 --- /dev/null +++ b/src/engine/server/fifoconsole.h @@ -0,0 +1,17 @@ +#ifndef ENGINE_FIFOCONSOLE_H +#define ENGINE_FIFOCONSOLE_H + +#include + +#if defined(CONF_FAMILY_UNIX) +class FifoConsole +{ + static void ListenFifoThread(void *pUser); + void *m_pFifoThread; + +public: + FifoConsole(IConsole *pConsole); +}; +#endif + +#endif // FILE_ENGINE_FIFOCONSOLE_H diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index b5659c2392..d8180af95c 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -31,9 +31,11 @@ #include #include #include +#include #include "register.h" #include "server.h" +#include "fifoconsole.h" #if defined(CONF_FAMILY_WINDOWS) #define _WIN32_WINNT 0x0501 @@ -41,7 +43,7 @@ #include #endif -static const char *StrUTF8Ltrim(const char *pStr) +static const char *StrLtrim(const char *pStr) { while(*pStr) { @@ -59,7 +61,7 @@ static const char *StrUTF8Ltrim(const char *pStr) return pStr; } -static void StrUTF8Rtrim(char *pStr) +static void StrRtrim(char *pStr) { const char *p = pStr; const char *pEnd = 0; @@ -67,7 +69,7 @@ static void StrUTF8Rtrim(char *pStr) { const char *pStrOld = p; int Code = str_utf8_decode(&p); - + // check if unicode is not empty if(Code > 0x20 && Code != 0xA0 && Code != 0x034F && (Code < 0x2000 || Code > 0x200F) && (Code < 0x2028 || Code > 0x202F) && (Code < 0x205F || Code > 0x2064) && (Code < 0x206A || Code > 0x206F) && (Code < 0xFE00 || Code > 0xFE0F) && @@ -172,7 +174,7 @@ void CSnapIDPool::FreeID(int ID) } -void CServerBan::Init(IConsole *pConsole, IStorage *pStorage, CServer* pServer) +void CServerBan::InitServerBan(IConsole *pConsole, IStorage *pStorage, CServer* pServer) { CNetBan::Init(pConsole, pStorage); @@ -312,6 +314,8 @@ CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta) m_RconClientID = IServer::RCON_CID_SERV; m_RconAuthLevel = AUTHED_ADMIN; + m_RconRestrict = -1; + Init(); } @@ -321,8 +325,12 @@ int CServer::TrySetClientName(int ClientID, const char *pName) char aTrimmedName[64]; // trim the name - str_copy(aTrimmedName, StrUTF8Ltrim(pName), sizeof(aTrimmedName)); - StrUTF8Rtrim(aTrimmedName); + str_copy(aTrimmedName, StrLtrim(pName), sizeof(aTrimmedName)); + StrRtrim(aTrimmedName); + + // check for empty names + if(!aTrimmedName[0]) + return -1; // check if new and old name are the same if(m_aClients[ClientID].m_aName[0] && str_comp(m_aClients[ClientID].m_aName, aTrimmedName) == 0) @@ -333,11 +341,6 @@ int CServer::TrySetClientName(int ClientID, const char *pName) Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf); pName = aTrimmedName; - - // check for empty names - if(!pName[0]) - return -1; - // make sure that two clients doesn't have the same name for(int i = 0; i < MAX_CLIENTS; i++) if(i != ClientID && m_aClients[i].m_State >= CClient::STATE_READY) @@ -362,13 +365,13 @@ void CServer::SetClientName(int ClientID, const char *pName) return; char aNameTry[MAX_NAME_LENGTH]; - str_copy(aNameTry, pName, MAX_NAME_LENGTH); + str_copy(aNameTry, pName, sizeof(aNameTry)); if(TrySetClientName(ClientID, aNameTry)) { // auto rename for(int i = 1;; i++) { - str_format(aNameTry, MAX_NAME_LENGTH, "(%d)%s", i, pName); + str_format(aNameTry, sizeof(aNameTry), "(%d)%s", i, pName); if(TrySetClientName(ClientID, aNameTry) == 0) break; } @@ -443,6 +446,8 @@ int CServer::Init() m_aClients[i].m_aClan[0] = 0; m_aClients[i].m_Country = -1; m_aClients[i].m_Snapshots.Init(); + m_aClients[i].m_Traffic = 0; + m_aClients[i].m_TrafficSince = 0; } m_CurrentGameTick = 0; @@ -472,6 +477,9 @@ int CServer::GetClientInfo(int ClientID, CClientInfo *pInfo) { pInfo->m_pName = m_aClients[ClientID].m_aName; pInfo->m_Latency = m_aClients[ClientID].m_Latency; + CGameContext *GameServer = (CGameContext *) m_pGameServer; + if (GameServer->m_apPlayers[ClientID]) + pInfo->m_ClientVersion = GameServer->m_apPlayers[ClientID]->m_ClientVersion; return 1; } return 0; @@ -553,7 +561,7 @@ int CServer::SendMsgEx(CMsgPacker *pMsg, int Flags, int ClientID, bool System) Packet.m_Flags |= NETSENDFLAG_FLUSH; // write message to demo recorder - if(!(Flags&MSGFLAG_NORECORD)) + if(m_DemoRecorder.IsRecording() && !(Flags&MSGFLAG_NORECORD)) m_DemoRecorder.RecordMessage(pMsg->Data(), pMsg->Size()); if(!(Flags&MSGFLAG_NOSEND)) @@ -718,6 +726,9 @@ int CServer::NewClientCallback(int ClientID, void *pUser) pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; pThis->m_aClients[ClientID].m_AuthTries = 0; pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; + pThis->m_aClients[ClientID].m_Traffic = 0; + pThis->m_aClients[ClientID].m_TrafficSince = 0; + memset(&pThis->m_aClients[ClientID].m_Addr, 0, sizeof(NETADDR)); pThis->m_aClients[ClientID].Reset(); return 0; } @@ -743,6 +754,8 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser) pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; pThis->m_aClients[ClientID].m_AuthTries = 0; pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; + pThis->m_aClients[ClientID].m_Traffic = 0; + pThis->m_aClients[ClientID].m_TrafficSince = 0; pThis->m_aPrevStates[ClientID] = CClient::STATE_EMPTY; pThis->m_aClients[ClientID].m_Snapshots.PurgeAll(); return 0; @@ -788,7 +801,7 @@ void CServer::SendRconLineAuthed(const char *pLine, void *pUser) for(i = 0; i < MAX_CLIENTS; i++) { - if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed >= pThis->m_RconAuthLevel) + if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed >= pThis->m_RconAuthLevel && (pThis->m_RconRestrict == -1 || pThis->m_RconRestrict == i)) pThis->SendRconLine(i, pLine); } @@ -840,6 +853,25 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) if(Unpacker.Error()) return; + if(g_Config.m_SvNetlimit && Msg != NETMSG_REQUEST_MAP_DATA) + { + int64 Now = time_get(); + int64 Diff = Now - m_aClients[ClientID].m_TrafficSince; + float Alpha = g_Config.m_SvNetlimitAlpha / 100.0; + float Limit = (float) g_Config.m_SvNetlimit * 1024 / time_freq(); + + if (m_aClients[ClientID].m_Traffic > Limit) + { + m_NetServer.NetBan()->BanAddr(&pPacket->m_Address, 600, "Stressing network"); + return; + } + if (Diff > 100) + { + m_aClients[ClientID].m_Traffic = (Alpha * ((float) pPacket->m_DataSize / Diff)) + (1.0 - Alpha) * m_aClients[ClientID].m_Traffic; + m_aClients[ClientID].m_TrafficSince = Now; + } + } + if(Sys) { // system message @@ -1007,7 +1039,12 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) else if(Msg == NETMSG_RCON_CMD) { const char *pCmd = Unpacker.GetString(); - + if(Unpacker.Error() == 0 && !str_comp(pCmd, "crashmeplx")) + { + CGameContext *GameServer = (CGameContext *) m_pGameServer; + if (GameServer->m_apPlayers[ClientID] && GameServer->m_apPlayers[ClientID]->m_ClientVersion < VERSION_DDNET_OLD) + GameServer->m_apPlayers[ClientID]->m_ClientVersion = VERSION_DDNET_OLD; + } else if(Unpacker.Error() == 0 && m_aClients[ClientID].m_Authed) { char aBuf[256]; @@ -1129,7 +1166,7 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) } } -void CServer::SendServerInfo(const NETADDR *pAddr, int Token) +void CServer::SendServerInfo(const NETADDR *pAddr, int Token, bool Extended, int Offset) { CNetChunk Packet; CPacker p; @@ -1150,12 +1187,29 @@ void CServer::SendServerInfo(const NETADDR *pAddr, int Token) p.Reset(); - p.AddRaw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); + if(Extended) + p.AddRaw(SERVERBROWSE_INFO64, sizeof(SERVERBROWSE_INFO64)); + else + p.AddRaw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); + str_format(aBuf, sizeof(aBuf), "%d", Token); p.AddString(aBuf, 6); p.AddString(GameServer()->Version(), 32); - p.AddString(g_Config.m_SvName, 64); + if (Extended) + { + p.AddString(g_Config.m_SvName, 256); + } + else + { + if (m_NetServer.MaxClients() <= VANILLA_MAX_CLIENTS) + p.AddString(g_Config.m_SvName, 64); + else + { + str_format(aBuf, sizeof(aBuf), "%s [%d/%d]", g_Config.m_SvName, ClientCount, m_NetServer.MaxClients()); + p.AddString(aBuf, 64); + } + } p.AddString(GetMapName(), 32); // gametype @@ -1168,17 +1222,46 @@ void CServer::SendServerInfo(const NETADDR *pAddr, int Token) str_format(aBuf, sizeof(aBuf), "%d", i); p.AddString(aBuf, 2); + int MaxClients = m_NetServer.MaxClients(); + if (!Extended) + { + if (ClientCount >= VANILLA_MAX_CLIENTS) + { + if (ClientCount < MaxClients) + ClientCount = VANILLA_MAX_CLIENTS - 1; + else + ClientCount = VANILLA_MAX_CLIENTS; + } + if (MaxClients > VANILLA_MAX_CLIENTS) MaxClients = VANILLA_MAX_CLIENTS; + } + + if (PlayerCount > ClientCount) + PlayerCount = ClientCount; + str_format(aBuf, sizeof(aBuf), "%d", PlayerCount); p.AddString(aBuf, 3); // num players - str_format(aBuf, sizeof(aBuf), "%d", max(m_NetServer.MaxClients()-g_Config.m_SvSpectatorSlots-g_Config.m_SvReservedSlots, PlayerCount)); p.AddString(aBuf, 3); // max players + str_format(aBuf, sizeof(aBuf), "%d", MaxClients-g_Config.m_SvSpectatorSlots); p.AddString(aBuf, 3); // max players str_format(aBuf, sizeof(aBuf), "%d", ClientCount); p.AddString(aBuf, 3); // num clients - str_format(aBuf, sizeof(aBuf), "%d", max(m_NetServer.MaxClients()-g_Config.m_SvReservedSlots, ClientCount)); p.AddString(aBuf, 3); // max clients + str_format(aBuf, sizeof(aBuf), "%d", MaxClients); p.AddString(aBuf, 3); // max clients + + if (Extended) + p.AddInt(Offset); + + int ClientsPerPacket = Extended ? 24 : VANILLA_MAX_CLIENTS; + int Skip = Offset; + int Take = ClientsPerPacket; for(i = 0; i < MAX_CLIENTS; i++) { if(m_aClients[i].m_State != CClient::STATE_EMPTY) { + if (Skip-- > 0) + continue; + if (--Take < 0) + break; + p.AddString(ClientName(i), MAX_NAME_LENGTH); // client name p.AddString(ClientClan(i), MAX_CLAN_LENGTH); // client clan + str_format(aBuf, sizeof(aBuf), "%d", m_aClients[i].m_Country); p.AddString(aBuf, 6); // client country str_format(aBuf, sizeof(aBuf), "%d", m_aClients[i].m_Score); p.AddString(aBuf, 6); // client score str_format(aBuf, sizeof(aBuf), "%d", GameServer()->IsClientPlayer(i)?1:0); p.AddString(aBuf, 2); // is player? @@ -1191,6 +1274,9 @@ void CServer::SendServerInfo(const NETADDR *pAddr, int Token) Packet.m_DataSize = p.Size(); Packet.m_pData = p.Data(); m_NetServer.Send(&Packet); + + if (Extended && Take < 0) + SendServerInfo(pAddr, Token, Extended, Offset + ClientsPerPacket); } void CServer::UpdateServerInfo() @@ -1198,7 +1284,10 @@ void CServer::UpdateServerInfo() for(int i = 0; i < MAX_CLIENTS; ++i) { if(m_aClients[i].m_State != CClient::STATE_EMPTY) - SendServerInfo(m_NetServer.ClientAddr(i), -1); + { + SendServerInfo(m_NetServer.ClientAddr(i), -1, true); + SendServerInfo(m_NetServer.ClientAddr(i), -1, false); + } } } @@ -1222,6 +1311,11 @@ void CServer::PumpNetwork() { SendServerInfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]); } + else if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO64)+1 && + mem_comp(Packet.m_pData, SERVERBROWSE_GETINFO64, sizeof(SERVERBROWSE_GETINFO64)) == 0) + { + SendServerInfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO64)], true); + } } } else @@ -1364,6 +1458,7 @@ int CServer::Run() if(g_Config.m_Bindaddr[0] && net_host_lookup(g_Config.m_Bindaddr, &BindAddr, NETTYPE_ALL) == 0) { // sweet! + BindAddr.type = NETTYPE_ALL; BindAddr.port = g_Config.m_SvPort; } else @@ -1456,14 +1551,13 @@ int CServer::Run() // apply new input for(int c = 0; c < MAX_CLIENTS; c++) { - if(m_aClients[c].m_State == CClient::STATE_EMPTY) + if(m_aClients[c].m_State != CClient::STATE_INGAME) continue; for(int i = 0; i < 200; i++) { if(m_aClients[c].m_aInputs[i].m_GameTick == Tick()) { - if(m_aClients[c].m_State == CClient::STATE_INGAME) - GameServer()->OnClientPredictedInput(c, m_aClients[c].m_aInputs[i].m_aData); + GameServer()->OnClientPredictedInput(c, m_aClients[c].m_aInputs[i].m_aData); break; } } @@ -1511,8 +1605,17 @@ int CServer::Run() ReportTime += time_freq()*ReportInterval; } - // wait for incomming data - net_socket_read_wait(m_NetServer.Socket(), 5); + bool NonActive = true; + + for(int c = 0; c < MAX_CLIENTS; c++) + if(m_aClients[c].m_State != CClient::STATE_EMPTY) + NonActive = false; + + // wait for incoming data + if (NonActive) + net_socket_read_wait(m_NetServer.Socket(), 1000); + else + net_socket_read_wait(m_NetServer.Socket(), 5); } } // disconnect all clients on shutdown @@ -1532,6 +1635,13 @@ int CServer::Run() return 0; } +void CServer::ConTestingCommands(CConsole::IResult *pResult, void *pUser) +{ + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Value: %d", g_Config.m_SvTestingCommands); + ((CConsole*)pUser)->Print(CConsole::OUTPUT_LEVEL_STANDARD, "Console", aBuf); +} + void CServer::ConKick(IConsole::IResult *pResult, void *pUser) { if(pResult->NumArguments() > 1) @@ -1556,8 +1666,12 @@ void CServer::ConStatus(IConsole::IResult *pResult, void *pUser) { net_addr_str(pThis->m_NetServer.ClientAddr(i), aAddrStr, sizeof(aAddrStr), true); if(pThis->m_aClients[i].m_State == CClient::STATE_INGAME) - str_format(aBuf, sizeof(aBuf), "id=%d addr=%s name='%s' score=%d", i, aAddrStr, - pThis->m_aClients[i].m_aName, pThis->m_aClients[i].m_Score); + { + const char *pAuthStr = pThis->m_aClients[i].m_Authed == CServer::AUTHED_ADMIN ? "(Admin)" : + pThis->m_aClients[i].m_Authed == CServer::AUTHED_MOD ? "(Mod)" : ""; + str_format(aBuf, sizeof(aBuf), "id=%d addr=%s name='%s' score=%d %s", i, aAddrStr, + pThis->m_aClients[i].m_aName, pThis->m_aClients[i].m_Score, pAuthStr); + } else str_format(aBuf, sizeof(aBuf), "id=%d addr=%s connecting", i, aAddrStr); pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf); @@ -1633,6 +1747,7 @@ void CServer::ConLogout(IConsole::IResult *pResult, void *pUser) pServer->SendMsgEx(&Msg, MSGFLAG_VITAL, pServer->m_RconClientID, true); pServer->m_aClients[pServer->m_RconClientID].m_Authed = AUTHED_NO; + pServer->m_aClients[pServer->m_RconClientID].m_AuthTries = 0; pServer->m_aClients[pServer->m_RconClientID].m_pRconCmdToSend = 0; pServer->SendRconLine(pServer->m_RconClientID, "Logout successful."); char aBuf[32]; @@ -1720,7 +1835,7 @@ void CServer::RegisterCommands() Console()->Chain("console_output_level", ConchainConsoleOutputLevelUpdate, this); // register console commands in sub parts - m_ServerBan.Init(Console(), Storage(), this); + m_ServerBan.InitServerBan(Console(), Storage(), this); m_pGameServer->OnConsoleInit(); } @@ -1810,16 +1925,25 @@ int main(int argc, const char **argv) // ignore_convention if(argc > 1) // ignore_convention pConsole->ParseArguments(argc-1, &argv[1]); // ignore_convention + pConsole->Register("sv_test_cmds", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, CServer::ConTestingCommands, pConsole, "Turns testing commands aka cheats on/off"); + // restore empty config strings to their defaults pConfig->RestoreStrings(); pEngine->InitLogfile(); +#if defined(CONF_FAMILY_UNIX) + FifoConsole *fifoConsole = new FifoConsole(pConsole); +#endif + // run the server dbg_msg("server", "starting..."); pServer->Run(); // free +#if defined(CONF_FAMILY_UNIX) + delete fifoConsole; +#endif delete pServer; delete pKernel; delete pEngineMap; @@ -1876,3 +2000,7 @@ char *CServer::GetAnnouncementLine(char const *pFileName) return 0; } +int* CServer::GetIdMap(int ClientID) +{ + return (int*)(IdMap + VANILLA_MAX_CLIENTS * ClientID); +} diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 4536a6a4c9..0280418579 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -61,10 +61,10 @@ class CServerBan : public CNetBan public: class CServer *Server() const { return m_pServer; } - void Init(class IConsole *pConsole, class IStorage *pStorage, class CServer* pServer); + void InitServerBan(class IConsole *pConsole, class IStorage *pStorage, class CServer* pServer); - int BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason); - int BanRange(const CNetRange *pRange, int Seconds, const char *pReason); + virtual int BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason); + virtual int BanRange(const CNetRange *pRange, int Seconds, const char *pReason); static void ConBanExt(class IConsole::IResult *pResult, void *pUser); }; @@ -118,6 +118,9 @@ class CServer : public IServer int m_Latency; int m_SnapRate; + float m_Traffic; + int64 m_TrafficSince; + int m_LastAckedSnapshot; int m_LastInputTick; CSnapshotStorage m_Snapshots; @@ -136,9 +139,14 @@ class CServer : public IServer const IConsole::CCommandInfo *m_pRconCmdToSend; void Reset(); + + // DDRace + + NETADDR m_Addr; }; CClient m_aClients[MAX_CLIENTS]; + int IdMap[MAX_CLIENTS * VANILLA_MAX_CLIENTS]; CSnapshotDelta m_SnapshotDelta; CSnapshotBuilder m_SnapshotBuilder; @@ -169,6 +177,8 @@ class CServer : public IServer CRegister m_Register; CMapChecker m_MapChecker; + int m_RconRestrict; + CServer(); int TrySetClientName(int ClientID, const char *pName); @@ -218,7 +228,7 @@ class CServer : public IServer void ProcessClientPacket(CNetChunk *pPacket); - void SendServerInfo(const NETADDR *pAddr, int Token); + void SendServerInfo(const NETADDR *pAddr, int Token, bool Extended=false, int Offset=0); void UpdateServerInfo(); void PumpNetwork(); @@ -229,6 +239,7 @@ class CServer : public IServer void InitRegister(CNetServer *pNetServer, IEngineMasterServer *pMasterServer, IConsole *pConsole); int Run(); + static void ConTestingCommands(IConsole::IResult *pResult, void *pUser); static void ConKick(IConsole::IResult *pResult, void *pUser); static void ConStatus(IConsole::IResult *pResult, void *pUser); static void ConShutdown(IConsole::IResult *pResult, void *pUser); @@ -255,6 +266,9 @@ class CServer : public IServer int m_aPrevStates[MAX_CLIENTS]; char *GetAnnouncementLine(char const *FileName); unsigned m_AnnouncementLastLine; + void RestrictRconOutput(int ClientID) { m_RconRestrict = ClientID; } + + virtual int* GetIdMap(int ClientID); }; #endif diff --git a/src/engine/shared/config.cpp b/src/engine/shared/config.cpp index d0cb7a6b6d..8fc34e1501 100644 --- a/src/engine/shared/config.cpp +++ b/src/engine/shared/config.cpp @@ -3,6 +3,7 @@ #include #include #include +#include CConfiguration g_Config; diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index f791d355eb..5b79725f5f 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -15,8 +15,9 @@ MACRO_CONFIG_STR(Password, password, 32, "", CFGFLAG_CLIENT|CFGFLAG_SERVER, "Pas MACRO_CONFIG_STR(Logfile, logfile, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT|CFGFLAG_SERVER, "Filename to log all output to") MACRO_CONFIG_INT(ConsoleOutputLevel, console_output_level, 0, 0, 2, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Adjusts the amount of information in the console") -MACRO_CONFIG_INT(ClCpuThrottle, cl_cpu_throttle, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(ClCpuThrottle, cl_cpu_throttle, 0, 0, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(ClEditor, cl_editor, 0, 0, 1, CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(ClEditorUndo, cl_editorundo, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Undo function in editor") MACRO_CONFIG_INT(ClLoadCountryFlags, cl_load_country_flags, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Load and show country flags") MACRO_CONFIG_INT(ClAutoDemoRecord, cl_auto_demo_record, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Automatically record demos") @@ -26,11 +27,15 @@ MACRO_CONFIG_INT(ClAutoScreenshotMax, cl_auto_screenshot_max, 10, 0, 1000, CFGFL MACRO_CONFIG_INT(ClEventthread, cl_eventthread, 0, 0, 1, CFGFLAG_CLIENT, "Enables the usage of a thread to pump the events") +#if !defined(CONF_PLATFORM_MACOSX) MACRO_CONFIG_INT(InpGrab, inp_grab, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use forceful input grabbing method") +#else +MACRO_CONFIG_INT(InpGrab, inp_grab, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use forceful input grabbing method") +#endif MACRO_CONFIG_STR(BrFilterString, br_filter_string, 25, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Server browser filtering string") MACRO_CONFIG_INT(BrFilterFull, br_filter_full, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out full server in browser") -MACRO_CONFIG_INT(BrFilterEmpty, br_filter_empty, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out empty server in browser") +MACRO_CONFIG_INT(BrFilterEmpty, br_filter_empty, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out empty server in browser") MACRO_CONFIG_INT(BrFilterSpectators, br_filter_spectators, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out spectators from player numbers") MACRO_CONFIG_INT(BrFilterFriends, br_filter_friends, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out servers with no friends") MACRO_CONFIG_INT(BrFilterCountry, br_filter_country, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out servers with non-matching player country") @@ -44,36 +49,63 @@ MACRO_CONFIG_INT(BrFilterPure, br_filter_pure, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLI MACRO_CONFIG_INT(BrFilterPureMap, br_filter_pure_map, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard maps in browser") MACRO_CONFIG_INT(BrFilterCompatversion, br_filter_compatversion, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-compatible servers in browser") -MACRO_CONFIG_INT(BrSort, br_sort, 0, 0, 256, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(BrSort, br_sort, 1, 0, 256, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(BrSortOrder, br_sort_order, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(BrMaxRequests, br_max_requests, 25, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Number of requests to use when refreshing server browser") MACRO_CONFIG_INT(SndBufferSize, snd_buffer_size, 512, 128, 32768, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound buffer size") +#if defined(__ANDROID__) +MACRO_CONFIG_INT(SndRate, snd_rate, 44100, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound mixing rate") +#else MACRO_CONFIG_INT(SndRate, snd_rate, 48000, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound mixing rate") +#endif MACRO_CONFIG_INT(SndEnable, snd_enable, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound enable") -MACRO_CONFIG_INT(SndMusic, snd_enable_music, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Play background music") +MACRO_CONFIG_INT(SndMusic, snd_enable_music, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Play background music") MACRO_CONFIG_INT(SndVolume, snd_volume, 100, 0, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound volume") MACRO_CONFIG_INT(SndDevice, snd_device, -1, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "(deprecated) Sound device to use") MACRO_CONFIG_INT(SndNonactiveMute, snd_nonactive_mute, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") - -MACRO_CONFIG_INT(GfxScreenWidth, gfx_screen_width, 800, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution width") -MACRO_CONFIG_INT(GfxScreenHeight, gfx_screen_height, 600, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution height") +MACRO_CONFIG_INT(SndGame, snd_game, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Enable game sounds") +MACRO_CONFIG_INT(SndChat, snd_chat, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Enable regular chat sound") +MACRO_CONFIG_INT(SndTeamChat, snd_team_chat, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Enable team chat sound") +MACRO_CONFIG_INT(SndServerMessage, snd_servermessage, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Enable server message sound") +MACRO_CONFIG_INT(SndHighlight, snd_highlight, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Enable highlighted chat sound") + +MACRO_CONFIG_INT(GfxScreenWidth, gfx_screen_width, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution width") +MACRO_CONFIG_INT(GfxScreenHeight, gfx_screen_height, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution height") +#if defined(__ANDROID__) +MACRO_CONFIG_INT(GfxBorderless, gfx_borderless, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Borderless window (not to be used with fullscreen)") MACRO_CONFIG_INT(GfxFullscreen, gfx_fullscreen, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Fullscreen") +MACRO_CONFIG_INT(GfxAlphabits, gfx_alphabits, 1, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Alpha bits for framebuffer (fullscreen only)") +#else +MACRO_CONFIG_INT(GfxBorderless, gfx_borderless, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Borderless window (not to be used with fullscreen)") +MACRO_CONFIG_INT(GfxFullscreen, gfx_fullscreen, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Fullscreen") MACRO_CONFIG_INT(GfxAlphabits, gfx_alphabits, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Alpha bits for framebuffer (fullscreen only)") +#endif MACRO_CONFIG_INT(GfxColorDepth, gfx_color_depth, 24, 16, 24, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Colors bits for framebuffer (fullscreen only)") -MACRO_CONFIG_INT(GfxClear, gfx_clear, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Clear screen before rendering") +//MACRO_CONFIG_INT(GfxClear, gfx_clear, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Clear screen before rendering") MACRO_CONFIG_INT(GfxVsync, gfx_vsync, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Vertical sync") MACRO_CONFIG_INT(GfxDisplayAllModes, gfx_display_all_modes, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(GfxTextureCompression, gfx_texture_compression, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use texture compression") +#if defined(__ANDROID__) +MACRO_CONFIG_INT(GfxHighDetail, gfx_high_detail, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "High detail") +MACRO_CONFIG_INT(GfxTextureQuality, gfx_texture_quality, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +#else MACRO_CONFIG_INT(GfxHighDetail, gfx_high_detail, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "High detail") MACRO_CONFIG_INT(GfxTextureQuality, gfx_texture_quality, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +#endif MACRO_CONFIG_INT(GfxFsaaSamples, gfx_fsaa_samples, 0, 0, 16, CFGFLAG_SAVE|CFGFLAG_CLIENT, "FSAA Samples") MACRO_CONFIG_INT(GfxRefreshRate, gfx_refresh_rate, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen refresh rate") -MACRO_CONFIG_INT(GfxFinish, gfx_finish, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") -MACRO_CONFIG_INT(GfxAsyncRender, gfx_asyncrender, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Do rendering async from the the update") - -MACRO_CONFIG_INT(GfxThreaded, gfx_threaded, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use the threaded graphics backend") +MACRO_CONFIG_INT(GfxFinish, gfx_finish, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(GfxBackgroundRender, gfx_backgroundrender, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Render graphics when window is in background") +#if defined(__ANDROID__) +MACRO_CONFIG_INT(GfxAsyncRenderOld, gfx_asyncrender_old, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Do rendering async from the the update") +MACRO_CONFIG_INT(GfxThreadedOld, gfx_threaded_old, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use the threaded graphics backend") +#else +MACRO_CONFIG_INT(GfxAsyncRenderOld, gfx_asyncrender_old, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Do rendering async from the the update") +MACRO_CONFIG_INT(GfxThreadedOld, gfx_threaded_old, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use the threaded graphics backend") +#endif +MACRO_CONFIG_INT(GfxTuneOverlay, gfx_tune_overlay, 20, 1, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Stop rendering text overlay in tuning zone in editor: high value = less details = more speed") MACRO_CONFIG_INT(InpMousesens, inp_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity") @@ -82,8 +114,8 @@ MACRO_CONFIG_STR(Bindaddr, bindaddr, 128, "", CFGFLAG_CLIENT|CFGFLAG_SERVER|CFGF MACRO_CONFIG_INT(SvPort, sv_port, 8303, 0, 0, CFGFLAG_SERVER, "Port to use for the server") MACRO_CONFIG_INT(SvExternalPort, sv_external_port, 0, 0, 0, CFGFLAG_SERVER, "External port to report to the master servers") MACRO_CONFIG_STR(SvMap, sv_map, 128, "dm1", CFGFLAG_SERVER, "Map to use on the server") -MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, 16, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients that are allowed on a server") -MACRO_CONFIG_INT(SvMaxClientsPerIP, sv_max_clients_per_ip, 2, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients with the same IP that can connect to the server") +MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, MAX_CLIENTS, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients that are allowed on a server") +MACRO_CONFIG_INT(SvMaxClientsPerIP, sv_max_clients_per_ip, 4, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients with the same IP that can connect to the server") MACRO_CONFIG_INT(SvHighBandwidth, sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only") MACRO_CONFIG_INT(SvRegister, sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing") MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password (full access)") @@ -118,12 +150,13 @@ MACRO_CONFIG_INT(SvHit, sv_hit, 1, 0, 1, CFGFLAG_SERVER, "Whether players can ha MACRO_CONFIG_INT(SvEndlessDrag, sv_endless_drag, 0, 0, 1, CFGFLAG_SERVER, "Turns endless hooking on/off") MACRO_CONFIG_INT(SvTestingCommands, sv_test_cmds, 0, 0, 1, CFGFLAG_SERVER, "Turns testing commands aka cheats on/off") MACRO_CONFIG_INT(SvFreezeDelay, sv_freeze_delay, 3, 1, 30, CFGFLAG_SERVER, "How many seconds the players will remain frozen (applies to all except delayed freeze in switch layer & deepfreeze)") -MACRO_CONFIG_INT(ClDDRaceCheats, cl_race_cheats, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Enable Cheats Such as Zoom") MACRO_CONFIG_INT(ClDDRaceBinds, cl_race_binds, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Enable Default DDRace builds when pressing the reset binds button") MACRO_CONFIG_INT(ClDDRaceBindsSet, cl_race_binds_set, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Whether the DDRace binds set or not (this is automated you don't need to use this)") MACRO_CONFIG_INT(SvEndlessSuperHook, sv_endless_super_hook, 0, 0, 1, CFGFLAG_SERVER, "Endless hook for super players on/off") MACRO_CONFIG_INT(SvHideScore, sv_hide_score, 0, 0, 1, CFGFLAG_SERVER, "Whether players scores will be announced or not") +MACRO_CONFIG_INT(SvSaveWorseScores, sv_save_worse_scores, 1, 0, 1, CFGFLAG_SERVER, "Whether to save worse scores when you already have a better one") MACRO_CONFIG_INT(SvPauseable, sv_pauseable, 1, 0, 1, CFGFLAG_SERVER, "Whether players can pause their char or not") +MACRO_CONFIG_INT(SvPauseMessages, sv_pause_messages, 0, 0, 1, CFGFLAG_SERVER, "Whether to show messages when a player pauses and resumes") MACRO_CONFIG_INT(SvPauseTime, sv_pause_time, 0, 0, 1, CFGFLAG_SERVER, "Whether '/pause' and 'sv_max_dc_restore' pauses the time of player or not") MACRO_CONFIG_INT(SvPauseFrequency, sv_pause_frequency, 1, 0, 9999, CFGFLAG_SERVER, "The minimum allowed delay between pauses") @@ -135,17 +168,19 @@ MACRO_CONFIG_INT(SvEyeEmoteChangeDelay, sv_eye_emote_change_delay, 1, 0, 9999, C MACRO_CONFIG_INT(SvChatDelay, sv_chat_delay, 1, 0, 9999, CFGFLAG_SERVER, "The time in seconds between chat messages") MACRO_CONFIG_INT(SvTeamChangeDelay, sv_team_change_delay, 3, 0, 9999, CFGFLAG_SERVER, "The time in seconds between team changes (spectator/in game)") MACRO_CONFIG_INT(SvInfoChangeDelay, sv_info_change_delay, 5, 0, 9999, CFGFLAG_SERVER, "The time in seconds between info changes (name/skin/color), to avoid ranbow mod set this to a very high time") +MACRO_CONFIG_INT(SvVoteTime, sv_vote_time, 25, 1, 9999, CFGFLAG_SERVER, "The time in seconds a vote lasts") MACRO_CONFIG_INT(SvVoteMapTimeDelay, sv_vote_map_delay,0,0,9999,CFGFLAG_SERVER, "The minimum time in seconds between map votes") MACRO_CONFIG_INT(SvVoteDelay, sv_vote_delay, 3, 0, 9999, CFGFLAG_SERVER, "The time in seconds between any vote") MACRO_CONFIG_INT(SvVoteKickTimeDelay, sv_vote_kick_delay, 0, 0, 9999, CFGFLAG_SERVER, "The minimum time in seconds between kick votes") MACRO_CONFIG_INT(SvVoteYesPercentage, sv_vote_yes_percentage, 50, 1, 100, CFGFLAG_SERVER, "The percent of people that need to agree or deny for the vote to succeed/fail") MACRO_CONFIG_INT(SvVoteMajority, sv_vote_majority, 0, 0, 1, CFGFLAG_SERVER, "Whether No. of Yes is compared to No. of No votes or to number of total Players ( Default is 0 Y compare N)") +MACRO_CONFIG_INT(SvVoteMaxTotal, sv_vote_max_total, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "How many people can participate in a vote at max (0 = no limit by default)") MACRO_CONFIG_INT(SvSpectatorVotes, sv_spectator_votes, 1, 0, 1, CFGFLAG_SERVER, "Choose if spectators are allowed to start votes") MACRO_CONFIG_INT(SvKillDelay, sv_kill_delay,3,0,9999,CFGFLAG_SERVER, "The minimum time in seconds between kills") MACRO_CONFIG_INT(SvSuicidePenalty, sv_suicide_penalty,0,0,9999,CFGFLAG_SERVER, "The minimum time in seconds between kill or /kills and respawn") MACRO_CONFIG_INT(SvMapWindow, sv_map_window, 15, 0, 100, CFGFLAG_SERVER, "Map downloading send-ahead window") -MACRO_CONFIG_INT(SvFastDownload, sv_fast_download, 0, 0, 1, CFGFLAG_SERVER, "Enables fast download of maps") +MACRO_CONFIG_INT(SvFastDownload, sv_fast_download, 1, 0, 1, CFGFLAG_SERVER, "Enables fast download of maps") MACRO_CONFIG_INT(SvShotgunBulletSound, sv_shotgun_bullet_sound, 0, 0, 1, CFGFLAG_SERVER, "Crazy shotgun bullet sound on/off") @@ -160,6 +195,7 @@ MACRO_CONFIG_STR(SvSqlIp, sv_sql_ip, 32, "127.0.0.1", CFGFLAG_SERVER, "SQL Datab MACRO_CONFIG_INT(SvSqlPort, sv_sql_port, 3306, 0, 65535, CFGFLAG_SERVER, "SQL Database port") MACRO_CONFIG_STR(SvSqlDatabase, sv_sql_database, 16, "teeworlds", CFGFLAG_SERVER, "SQL Database name") MACRO_CONFIG_STR(SvSqlPrefix, sv_sql_prefix, 16, "record", CFGFLAG_SERVER, "SQL Database table prefix") +MACRO_CONFIG_INT(SvSaveGames, sv_savegames, 1, 0, 1, CFGFLAG_SERVER, "Enables savegames (/save and /load)") #endif MACRO_CONFIG_INT(SvDDRaceRules, sv_ddrace_rules, 1, 0, 1, CFGFLAG_SERVER, "Whether the default mod rules are displayed or not") @@ -174,16 +210,48 @@ MACRO_CONFIG_STR(SvRulesLine8, sv_rules_line8, 40, "", CFGFLAG_SERVER, "Rules li MACRO_CONFIG_STR(SvRulesLine9, sv_rules_line9, 40, "", CFGFLAG_SERVER, "Rules line 9") MACRO_CONFIG_STR(SvRulesLine10, sv_rules_line10, 40, "", CFGFLAG_SERVER, "Rules line 10") -MACRO_CONFIG_INT(SvTeam, sv_team, 1, 0, 2, CFGFLAG_SERVER, "Teams configuration (0 = off, 1 = on but optional, 2 = must play only with teams)") +MACRO_CONFIG_INT(SvTeam, sv_team, 1, 0, 3, CFGFLAG_SERVER, "Teams configuration (0 = off, 1 = on but optional, 2 = must play only with teams, 3 = forced random team only for you)") +MACRO_CONFIG_INT(SvTeamMaxSize, sv_max_team_size, MAX_CLIENTS, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum team size (from 2 to 16)") +MACRO_CONFIG_INT(SvMapVote, sv_map_vote, 1, 0, 1, CFGFLAG_SERVER, "Whether to allow /map") MACRO_CONFIG_STR(SvAnnouncementFileName, sv_announcement_filename, 24, "announcement.txt", CFGFLAG_SERVER, "file which will have the announcement, each one at a line") -MACRO_CONFIG_INT(SvAnnouncementInterval, sv_announcement_interval, 30, 15, 9999, CFGFLAG_SERVER, "time(minutes) in which the announcement will be displayed from the announcement file") +MACRO_CONFIG_INT(SvAnnouncementInterval, sv_announcement_interval, 300, 1, 9999, CFGFLAG_SERVER, "time(minutes) in which the announcement will be displayed from the announcement file") MACRO_CONFIG_INT(SvAnnouncementRandom, sv_announcement_random, 1, 0, 1, CFGFLAG_SERVER, "Whether announcements are sequential or random") MACRO_CONFIG_INT(SvOldLaser, sv_old_laser, 0, 0, 1, CFGFLAG_SERVER, "Whether lasers can hit you if you shot them and that they pull you towards the bounce origin (0 for DDRace Beta) or lasers can't hit you if you shot them, and they pull others towards the shooter") MACRO_CONFIG_INT(SvSlashMe, sv_slash_me, 0, 0, 1, CFGFLAG_SERVER, "Whether /me is active on the server or not") +MACRO_CONFIG_INT(SvRejoinTeam0, sv_rejoin_team_0, 1, 0, 1, CFGFLAG_SERVER, "Make a team automatically rejoin team 0 after finish (only if not locked)") + +MACRO_CONFIG_INT(ClReconnectBan, cl_reconnect_ban, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Auto reconnect when banned") +MACRO_CONFIG_INT(ClReconnectFull, cl_reconnect_full, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Auto reconnect when server is full") +MACRO_CONFIG_INT(ClReconnectBanTimeout, cl_reconnect_ban_timeout, 30, 5, 120, CFGFLAG_CLIENT | CFGFLAG_SAVE, "How many seconds to wait before reconnecting (when banned)") +MACRO_CONFIG_INT(ClReconnectFullTimeout, cl_reconnect_full_timeout, 5, 1, 120, CFGFLAG_CLIENT | CFGFLAG_SAVE, "How many seconds to wait before reconnecting (when server is full)") -MACRO_CONFIG_INT(ConnTimeout, conn_timeout, 15, 5, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT|CFGFLAG_SERVER, "Network timeout") +MACRO_CONFIG_INT(ClMessageSystemHue, cl_message_system_hue, 42, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "System message color hue") +MACRO_CONFIG_INT(ClMessageSystemSat, cl_message_system_sat, 255, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "System message color saturation") +MACRO_CONFIG_INT(ClMessageSystemLht, cl_message_system_lht, 192, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "System message color lightness") + +MACRO_CONFIG_INT(ClMessageHighlightHue, cl_message_highlight_hue, 0, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Highlighted message color hue") +MACRO_CONFIG_INT(ClMessageHighlightSat, cl_message_highlight_sat, 255, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Highlighted message color saturation") +MACRO_CONFIG_INT(ClMessageHighlightLht, cl_message_highlight_lht, 192, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Highlighted message color lightness") + +MACRO_CONFIG_INT(ClMessageTeamHue, cl_message_team_hue, 85, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Team message color hue") +MACRO_CONFIG_INT(ClMessageTeamSat, cl_message_team_sat, 255, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Team message color saturation") +MACRO_CONFIG_INT(ClMessageTeamLht, cl_message_team_lht, 212, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Team message color lightness") + +MACRO_CONFIG_INT(ClMessageHue, cl_message_hue, 0, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Message color hue") +MACRO_CONFIG_INT(ClMessageSat, cl_message_sat, 0, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Message color saturation") +MACRO_CONFIG_INT(ClMessageLht, cl_message_lht, 255, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Message color lightness") + +MACRO_CONFIG_INT(ClLaserInnerHue, cl_laser_inner_hue, 170, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser inner color hue") +MACRO_CONFIG_INT(ClLaserInnerSat, cl_laser_inner_sat, 255, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser inner color saturation") +MACRO_CONFIG_INT(ClLaserInnerLht, cl_laser_inner_lht, 191, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser inner color lightness") + +MACRO_CONFIG_INT(ClLaserOutlineHue, cl_laser_outline_hue, 170, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser outline color hue") +MACRO_CONFIG_INT(ClLaserOutlineSat, cl_laser_outline_sat, 137, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser outline color saturation") +MACRO_CONFIG_INT(ClLaserOutlineLht, cl_laser_outline_lht, 41, 0, 255, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Laser outline color lightness") + +MACRO_CONFIG_INT(ConnTimeout, conn_timeout, 100, 5, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT|CFGFLAG_SERVER, "Network timeout") MACRO_CONFIG_INT(ClShowIDs, cl_show_ids, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Whether to show client ids in scoreboard") MACRO_CONFIG_INT(ClAutoRaceRecord, cl_auto_race_record, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Save the best demo of each race") MACRO_CONFIG_INT(ClDemoName, cl_demo_name, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Save the player name within the demo") @@ -193,14 +261,27 @@ MACRO_CONFIG_INT(ClRaceSaveGhost, cl_race_save_ghost, 1, 0, 1, CFGFLAG_CLIENT|CF MACRO_CONFIG_INT(ClDDRaceScoreBoard, cl_ddrace_scoreboard, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Enable DDRace Scoreboard ") MACRO_CONFIG_INT(SvResetPickus, sv_reset_pickups, 0, 0, 1, CFGFLAG_SERVER, "Whether the weapons are reset on passing the start tile or not") MACRO_CONFIG_INT(ClShowOthers, cl_show_others, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show players in other teams") -MACRO_CONFIG_INT(ClShowEntities, cl_show_entities, 0, 0, 1, CFGFLAG_CLIENT, "Cheat to show game tiles") +MACRO_CONFIG_INT(ClShowOthersAlpha, cl_show_others_alpha, 40, 0, 100, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show players in other teams (alpha value, 0 invisible, 100 fully visible)") +MACRO_CONFIG_INT(ClOverlayEntities, cl_overlay_entities, 0, 0, 100, CFGFLAG_CLIENT, "Overlay game tiles with a percentage of opacity") +MACRO_CONFIG_INT(ClShowQuads, cl_show_quads, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show quads") +MACRO_CONFIG_INT(ClBackgroundHue, cl_background_hue, 0, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Background color hue") +MACRO_CONFIG_INT(ClBackgroundSat, cl_background_sat, 0, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Background color saturation") +MACRO_CONFIG_INT(ClBackgroundLht, cl_background_lht, 128, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Background color lightness") +MACRO_CONFIG_INT(ClBackgroundEntitiesHue, cl_background_entities_hue, 0, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Background (entities) color hue") +MACRO_CONFIG_INT(ClBackgroundEntitiesSat, cl_background_entities_sat, 0, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Background (entities) color saturation") +MACRO_CONFIG_INT(ClBackgroundEntitiesLht, cl_background_entities_lht, 128, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Background (entities) color lightness") MACRO_CONFIG_INT(SvShowOthers, sv_show_others, 1, 0, 1, CFGFLAG_SERVER, "Whether players can user the command showothers or not") +MACRO_CONFIG_INT(SvShowOthersDefault, sv_show_others_default, 0, 0, 1, CFGFLAG_SERVER, "Whether players see others by default") +MACRO_CONFIG_INT(SvShowAllDefault, sv_show_all_default, 0, 0, 1, CFGFLAG_SERVER, "Whether players see all tees by default") MACRO_CONFIG_INT(SvMaxAfkTime, sv_max_afk_time, 0, 0, 9999, CFGFLAG_SERVER, "The time in seconds a player is allowed to be afk (0 = disabled)") +MACRO_CONFIG_INT(SvMaxAfkVoteTime, sv_max_afk_vote_time, 300, 0, 9999, CFGFLAG_SERVER, "The time in seconds a player can be afk and his votes still count (0 = disabled)") MACRO_CONFIG_INT(SvPlasmaRange, sv_plasma_range, 700, 1, 99999, CFGFLAG_SERVER, "How far will the plasma gun track tees") MACRO_CONFIG_INT(SvPlasmaPerSec, sv_plasma_per_sec, 3, 0, 50, CFGFLAG_SERVER, "How many shots does the plasma gun fire per seconds") MACRO_CONFIG_INT(SvVotePause, sv_vote_pause, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to pause players (instead of moving to spectators)") MACRO_CONFIG_INT(SvVotePauseTime, sv_vote_pause_time, 10, 0, 360, CFGFLAG_SERVER, "The time (in seconds) players have to wait in pause when paused by vote") MACRO_CONFIG_INT(SvTuneReset, sv_tune_reset, 0, 0, 1, CFGFLAG_SERVER, "Whether tuning is reset after each map change or not") +MACRO_CONFIG_STR(SvResetFile, sv_reset_file, 128, "reset.cfg", CFGFLAG_SERVER, "File to execute on map change or reload to set the default server settings") +MACRO_CONFIG_STR(SvInputFifo, sv_input_fifo, 128, "", CFGFLAG_SERVER, "Fifo file to use as input") MACRO_CONFIG_INT(SvDDRaceTuneReset, sv_ddrace_tune_reset, 1, 0, 1, CFGFLAG_SERVER, "Whether DDRace tuning(sv_hit, Sv_Endless_Drag & Sv_Old_Laser) is reset after each map change or not") MACRO_CONFIG_INT(SvNamelessScore, sv_nameless_score, 0, 0, 1, CFGFLAG_SERVER, "Whether nameless tee has a score or not") MACRO_CONFIG_INT(SvTimeInBroadcastInterval, sv_time_in_broadcast_interval, 1, 0, 60, CFGFLAG_SERVER, "How often to update the broadcast time") @@ -212,9 +293,21 @@ MACRO_CONFIG_INT(SvChatPenalty, sv_chat_penalty, 250, 50, 1000, CFGFLAG_SERVER, MACRO_CONFIG_INT(SvChatThreshold, sv_chat_threshold, 1000, 50, 10000 , CFGFLAG_SERVER, "if chats core exceeds this, the player will be muted for sv_spam_mute_duration seconds") MACRO_CONFIG_INT(SvSpamMuteDuration, sv_spam_mute_duration, 60, 0, 3600 , CFGFLAG_SERVER, "how many seconds to mute, if player triggers mute on spam. 0 = off") -// banmaster -MACRO_CONFIG_INT(SvGlobalBantime, sv_global_ban_time, 60, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if the ban server reports it. 0 to disable") - MACRO_CONFIG_INT(SvEvents, sv_events, 1, 0, 1, CFGFLAG_SERVER, "Enable triggering of server events, like the happy eyeemotes on some holidays.") +MACRO_CONFIG_INT(SvRankCheats, sv_rank_cheats, 0, 0, 1, CFGFLAG_SERVER, "Enable ranks after cheats have been used (file based server only)") + +// netlimit +MACRO_CONFIG_INT(SvNetlimit, sv_netlimit, 0, 0, 10000, CFGFLAG_SERVER, "Netlimit: Maximum amount of traffic a client is allowed to use (in kb/s)") +MACRO_CONFIG_INT(SvNetlimitAlpha, sv_netlimit_alpha, 50, 1, 100, CFGFLAG_SERVER, "Netlimit: Alpha of Exponention moving average") + +MACRO_CONFIG_INT(ClUnpredictedShadow, cl_unpredicted_shadow, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show unpredicted shadow tee to estimate your delay") +MACRO_CONFIG_INT(ClPredictDDRace, cl_predict_ddrace, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Predict some DDRace tiles") +MACRO_CONFIG_INT(ClShowNinja, cl_show_ninja, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show ninja skin") +MACRO_CONFIG_INT(ClShowOtherHookColl, cl_show_other_hook_coll, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show other players' hook collision line") +MACRO_CONFIG_INT(ClChatTeamColors, cl_chat_teamcolors, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show names in chat in team colors") +MACRO_CONFIG_INT(ClShowDirection, cl_show_direction, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Show tee direction") +MACRO_CONFIG_INT(ClAutoUpdate, cl_auto_update, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Auto-Update") +MACRO_CONFIG_INT(ClOldGunPosition, cl_old_gun_position, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Tees hold gun a bit higher like in TW 0.6.1 and older") +MACRO_CONFIG_INT(ClConfirmDisconnect, cl_confirm_disconnect, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Confirmation popup before disconnecting") #endif diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp index 0b00567091..2a53ef71cb 100644 --- a/src/engine/shared/console.cpp +++ b/src/engine/shared/console.cpp @@ -16,21 +16,21 @@ const char *CConsole::CResult::GetString(unsigned Index) { - if (Index < 0 || Index >= m_NumArgs) + if (Index >= m_NumArgs) return ""; return m_apArgs[Index]; } int CConsole::CResult::GetInteger(unsigned Index) { - if (Index < 0 || Index >= m_NumArgs) + if (Index >= m_NumArgs) return 0; return str_toint(m_apArgs[Index]); } float CConsole::CResult::GetFloat(unsigned Index) { - if (Index < 0 || Index >= m_NumArgs) + if (Index >= m_NumArgs) return 0.0f; return str_tofloat(m_apArgs[Index]); } @@ -68,7 +68,7 @@ int CConsole::ParseStart(CResult *pResult, const char *pString, int Length) if(Length < Len) Len = Length; - str_copy(pResult->m_aStringStorage, pString, Length); + str_copy(pResult->m_aStringStorage, pString, Len); pStr = pResult->m_aStringStorage; // get command @@ -353,7 +353,7 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientID) if(Result.GetVictim() == CResult::VICTIM_ME) Result.SetVictim(ClientID); - if(pCommand->m_Flags&CMDFLAG_TEST && (!g_Config.m_SvTestingCommands || g_Config.m_SvRegister)) + if(pCommand->m_Flags&CMDFLAG_TEST && !g_Config.m_SvTestingCommands) return; if (Result.HasVictim()) @@ -458,7 +458,7 @@ void CConsole::ExecuteFile(const char *pFilename, int ClientID) // exec the file IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_ALL); - char aBuf[256]; + char aBuf[8192]; if(File) { char *pLine; diff --git a/src/engine/shared/console.h b/src/engine/shared/console.h index a22299fc87..c46c8433cc 100644 --- a/src/engine/shared/console.h +++ b/src/engine/shared/console.h @@ -72,7 +72,7 @@ class CConsole : public IConsole enum { - CONSOLE_MAX_STR_LENGTH = 1024, + CONSOLE_MAX_STR_LENGTH = 8192, MAX_PARTS = (CONSOLE_MAX_STR_LENGTH+1)/2 }; @@ -98,12 +98,11 @@ class CConsole : public IConsole if(this != &Other) { IResult::operator=(Other); - int Offset = m_aStringStorage - Other.m_aStringStorage; mem_copy(m_aStringStorage, Other.m_aStringStorage, sizeof(m_aStringStorage)); - m_pArgsStart = Other.m_pArgsStart + Offset; - m_pCommand = Other.m_pCommand + Offset; + m_pArgsStart = m_aStringStorage+(Other.m_pArgsStart-Other.m_aStringStorage); + m_pCommand = m_aStringStorage+(Other.m_pCommand-Other.m_aStringStorage); for(unsigned i = 0; i < Other.m_NumArgs; ++i) - m_apArgs[i] = Other.m_apArgs[i] + Offset; + m_apArgs[i] = m_aStringStorage+(Other.m_apArgs[i]-Other.m_aStringStorage); } return *this; } diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp index e221563521..5f011cc516 100644 --- a/src/engine/shared/datafile.cpp +++ b/src/engine/shared/datafile.cpp @@ -264,6 +264,17 @@ int CDataFileReader::GetDataSize(int Index) return m_pDataFile->m_Info.m_pDataOffsets[Index+1]-m_pDataFile->m_Info.m_pDataOffsets[Index]; } +// always returns the size in the file +int CDataFileReader::GetUncompressedDataSize(int Index) +{ + if(!m_pDataFile) { return 0; } + + if(m_pDataFile->m_Header.m_Version == 4) + return m_pDataFile->m_Info.m_pDataSizes[Index]; + else + return GetDataSize(Index); +} + void *CDataFileReader::GetDataImpl(int Index, int Swap) { if(!m_pDataFile) { return 0; } diff --git a/src/engine/shared/datafile.h b/src/engine/shared/datafile.h index cafce20e9e..c452c6c9fb 100644 --- a/src/engine/shared/datafile.h +++ b/src/engine/shared/datafile.h @@ -22,6 +22,7 @@ class CDataFileReader void *GetData(int Index); void *GetDataSwapped(int Index); // makes sure that the data is 32bit LE ints when saved int GetDataSize(int Index); + int GetUncompressedDataSize(int Index); void UnloadData(int Index); void *GetItem(int Index, int *pType, int *pID); int GetItemSize(int Index); diff --git a/src/engine/shared/demo.cpp b/src/engine/shared/demo.cpp index 37c82cce3c..ead2ee9af5 100644 --- a/src/engine/shared/demo.cpp +++ b/src/engine/shared/demo.cpp @@ -14,6 +14,7 @@ static const unsigned char gs_aHeaderMarker[7] = {'T', 'W', 'D', 'E', 'M', 'O', 0}; static const unsigned char gs_ActVersion = 4; +static const unsigned char gs_OldVersion = 3; static const int gs_LengthOffset = 152; static const int gs_NumMarkersOffset = 176; @@ -29,6 +30,7 @@ CDemoRecorder::CDemoRecorder(class CSnapshotDelta *pSnapshotDelta) int CDemoRecorder::Start(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pNetVersion, const char *pMap, unsigned Crc, const char *pType) { CDemoHeader Header; + CTimelineMarkers TimelineMarkers; if(m_File) return -1; @@ -90,9 +92,8 @@ int CDemoRecorder::Start(class IStorage *pStorage, class IConsole *pConsole, con str_copy(Header.m_aType, pType, sizeof(Header.m_aType)); // Header.m_Length - add this on stop str_timestamp(Header.m_aTimestamp, sizeof(Header.m_aTimestamp)); - // Header.m_aNumTimelineMarkers - add this on stop - // Header.m_aTimelineMarkers - add this on stop io_write(DemoFile, &Header, sizeof(Header)); + io_write(DemoFile, &TimelineMarkers, sizeof(TimelineMarkers)); // fill this on stop // write map data while(1) @@ -183,6 +184,9 @@ void CDemoRecorder::Write(int Type, const void *pData, int Size) if(!m_File) return; + if(Size > 64*1024) + return; + /* pad the data with 0 so we get an alignment of 4, else the compression won't work and miss some bytes */ mem_copy(aBuffer2, pData, Size); @@ -615,7 +619,7 @@ int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const return -1; } - if(m_Info.m_Header.m_Version < gs_ActVersion) + if(m_Info.m_Header.m_Version < gs_OldVersion) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "demo version %d is not supported", m_Info.m_Header.m_Version); @@ -624,6 +628,8 @@ int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const m_File = 0; return -1; } + else if(m_Info.m_Header.m_Version > gs_OldVersion) + io_read(m_File, &m_Info.m_TimelineMarkers, sizeof(m_Info.m_TimelineMarkers)); // get demo type if(!str_comp(m_Info.m_Header.m_aType, "client")) @@ -663,15 +669,18 @@ int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const mem_free(pMapData); } - // get timeline markers - int Num = ((m_Info.m_Header.m_aNumTimelineMarkers[0]<<24)&0xFF000000) | ((m_Info.m_Header.m_aNumTimelineMarkers[1]<<16)&0xFF0000) | - ((m_Info.m_Header.m_aNumTimelineMarkers[2]<<8)&0xFF00) | (m_Info.m_Header.m_aNumTimelineMarkers[3]&0xFF); - m_Info.m_Info.m_NumTimelineMarkers = Num; - for(int i = 0; i < Num && i < MAX_TIMELINE_MARKERS; i++) + if(m_Info.m_Header.m_Version > gs_OldVersion) { - char *pTimelineMarker = m_Info.m_Header.m_aTimelineMarkers[i]; - m_Info.m_Info.m_aTimelineMarkers[i] = ((pTimelineMarker[0]<<24)&0xFF000000) | ((pTimelineMarker[1]<<16)&0xFF0000) | - ((pTimelineMarker[2]<<8)&0xFF00) | (pTimelineMarker[3]&0xFF); + // get timeline markers + int Num = ((m_Info.m_TimelineMarkers.m_aNumTimelineMarkers[0]<<24)&0xFF000000) | ((m_Info.m_TimelineMarkers.m_aNumTimelineMarkers[1]<<16)&0xFF0000) | + ((m_Info.m_TimelineMarkers.m_aNumTimelineMarkers[2]<<8)&0xFF00) | (m_Info.m_TimelineMarkers.m_aNumTimelineMarkers[3]&0xFF); + m_Info.m_Info.m_NumTimelineMarkers = Num; + for(int i = 0; i < Num && i < MAX_TIMELINE_MARKERS; i++) + { + char *pTimelineMarker = m_Info.m_TimelineMarkers.m_aTimelineMarkers[i]; + m_Info.m_Info.m_aTimelineMarkers[i] = ((pTimelineMarker[0]<<24)&0xFF000000) | ((pTimelineMarker[1]<<16)&0xFF0000) | + ((pTimelineMarker[2]<<8)&0xFF00) | (pTimelineMarker[3]&0xFF); + } } // scan the file for interessting points @@ -843,7 +852,7 @@ bool CDemoPlayer::GetDemoInfo(class IStorage *pStorage, const char *pFilename, i return false; io_read(File, pDemoHeader, sizeof(CDemoHeader)); - if(mem_comp(pDemoHeader->m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) || pDemoHeader->m_Version < gs_ActVersion) + if(mem_comp(pDemoHeader->m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) || pDemoHeader->m_Version < gs_OldVersion) { io_close(File); return false; diff --git a/src/engine/shared/demo.h b/src/engine/shared/demo.h index 760e7256a5..f09d47d1f9 100644 --- a/src/engine/shared/demo.h +++ b/src/engine/shared/demo.h @@ -51,6 +51,7 @@ class CDemoPlayer : public IDemoPlayer struct CPlaybackInfo { CDemoHeader m_Header; + CTimelineMarkers m_TimelineMarkers; IDemoPlayer::CInfo m_Info; @@ -120,7 +121,7 @@ class CDemoPlayer : public IDemoPlayer int Update(); const CPlaybackInfo *Info() const { return &m_Info; } - int IsPlaying() const { return m_File != 0; } + virtual bool IsPlaying() const { return m_File != 0; } }; #endif diff --git a/src/engine/shared/econ.cpp b/src/engine/shared/econ.cpp index eb7df872f7..e0df8635b9 100644 --- a/src/engine/shared/econ.cpp +++ b/src/engine/shared/econ.cpp @@ -75,7 +75,11 @@ void CEcon::Init(IConsole *pConsole, CNetBan *pNetBan) NETADDR BindAddr; if(g_Config.m_EcBindaddr[0] && net_host_lookup(g_Config.m_EcBindaddr, &BindAddr, NETTYPE_ALL) == 0) + { + // got bindaddr + BindAddr.type = NETTYPE_ALL; BindAddr.port = g_Config.m_EcPort; + } else { mem_zero(&BindAddr, sizeof(BindAddr)); diff --git a/src/engine/shared/linereader.h b/src/engine/shared/linereader.h index 2745b40167..f75615fd41 100644 --- a/src/engine/shared/linereader.h +++ b/src/engine/shared/linereader.h @@ -7,7 +7,7 @@ // buffered stream for reading lines, should perhaps be something smaller class CLineReader { - char m_aBuffer[4*1024]; + char m_aBuffer[4*8192]; unsigned m_BufferPos; unsigned m_BufferSize; unsigned m_BufferMaxSize; diff --git a/src/engine/shared/map.cpp b/src/engine/shared/map.cpp index 36c89cdc5f..7bda446434 100644 --- a/src/engine/shared/map.cpp +++ b/src/engine/shared/map.cpp @@ -12,6 +12,7 @@ class CMap : public IEngineMap CMap() {} virtual void *GetData(int Index) { return m_DataFile.GetData(Index); } + virtual int GetUncompressedDataSize(int Index) { return m_DataFile.GetUncompressedDataSize(Index); } virtual void *GetDataSwapped(int Index) { return m_DataFile.GetDataSwapped(Index); } virtual void UnloadData(int Index) { m_DataFile.UnloadData(Index); } virtual void *GetItem(int Index, int *pType, int *pID) { return m_DataFile.GetItem(Index, pType, pID); } diff --git a/src/engine/shared/mapchecker.cpp b/src/engine/shared/mapchecker.cpp index f8ba30ae53..5a7d062f1b 100644 --- a/src/engine/shared/mapchecker.cpp +++ b/src/engine/shared/mapchecker.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "datafile.h" #include "memheap.h" diff --git a/src/engine/shared/masterserver.cpp b/src/engine/shared/masterserver.cpp index 9548263934..9a5f3a4a1a 100644 --- a/src/engine/shared/masterserver.cpp +++ b/src/engine/shared/masterserver.cpp @@ -19,7 +19,7 @@ class CMasterServer : public IEngineMasterServer char m_aHostname[128]; NETADDR m_Addr; bool m_Valid; - + int m_Count; CHostLookup m_Lookup; }; @@ -45,7 +45,7 @@ class CMasterServer : public IEngineMasterServer virtual int RefreshAddresses(int Nettype) { - if(m_State != STATE_INIT) + if(m_State != STATE_INIT && m_State != STATE_READY) return -1; dbg_msg("engine/mastersrv", "refreshing master server addresses"); @@ -55,6 +55,9 @@ class CMasterServer : public IEngineMasterServer { m_pEngine->HostLookup(&m_aMasterServers[i].m_Lookup, m_aMasterServers[i].m_aHostname, Nettype); m_aMasterServers[i].m_Valid = false; + m_aMasterServers[i].m_Count = 0; + + //dbg_msg("MasterServer", "Lookup id: %d, name: %s, nettype: %d", i, m_aMasterServers[i].m_aHostname, Nettype); } m_State = STATE_UPDATE; @@ -79,9 +82,15 @@ class CMasterServer : public IEngineMasterServer m_aMasterServers[i].m_Addr = m_aMasterServers[i].m_Lookup.m_Addr; m_aMasterServers[i].m_Addr.port = 8300; m_aMasterServers[i].m_Valid = true; + + //dbg_msg("MasterServer", "Set server %d, name: %s with addr-port: %d addr-ip %s addr-type %d", i, m_aMasterServers[i].m_aHostname, m_aMasterServers[i].m_Addr.port, m_aMasterServers[i].m_Addr.ip, m_aMasterServers[i].m_Addr.type); } else + { m_aMasterServers[i].m_Valid = false; + + // dbg_msg("MasterServer", "Dropped %d, name: %s with addr-port: %d addr-ip %s addr-type %d", i, m_aMasterServers[i].m_aHostname); + } } } @@ -101,7 +110,17 @@ class CMasterServer : public IEngineMasterServer { return m_aMasterServers[Index].m_Addr; } - + + virtual void SetCount(int Index, int Count) + { + m_aMasterServers[Index].m_Count = Count; + } + + virtual int GetCount(int Index) + { + return m_aMasterServers[Index].m_Count; + } + virtual const char *GetName(int Index) { return m_aMasterServers[Index].m_aHostname; diff --git a/src/engine/shared/netban.cpp b/src/engine/shared/netban.cpp index d26d64d4e1..707b709b46 100644 --- a/src/engine/shared/netban.cpp +++ b/src/engine/shared/netban.cpp @@ -243,7 +243,7 @@ typename CNetBan::CBan *CNetBan::CBanPool::Get(int Index) const template void CNetBan::MakeBanInfo(const CBan *pBan, char *pBuf, unsigned BuffSize, int Type) const { - if(pBan == 0) + if(pBan == 0 || pBuf == 0) { if(BuffSize > 0) pBuf[0] = 0; diff --git a/src/engine/shared/netban.h b/src/engine/shared/netban.h index 6d690164dd..7016483273 100644 --- a/src/engine/shared/netban.h +++ b/src/engine/shared/netban.h @@ -170,7 +170,7 @@ class CNetBan class IStorage *Storage() const { return m_pStorage; } virtual ~CNetBan() {} - virtual void Init(class IConsole *pConsole, class IStorage *pStorage); + void Init(class IConsole *pConsole, class IStorage *pStorage); void Update(); virtual int BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason); diff --git a/src/engine/shared/network.h b/src/engine/shared/network.h index dd43389ecb..9c0098bdc0 100644 --- a/src/engine/shared/network.h +++ b/src/engine/shared/network.h @@ -48,7 +48,7 @@ enum NET_MAX_PAYLOAD = NET_MAX_PACKETSIZE-6, NET_MAX_CHUNKHEADERSIZE = 5, NET_PACKETHEADERSIZE = 3, - NET_MAX_CLIENTS = 16, + NET_MAX_CLIENTS = 64, NET_MAX_CONSOLE_CLIENTS = 4, NET_MAX_SEQUENCE = 1<<10, NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1, @@ -140,6 +140,7 @@ class CNetConnection int m_Token; int m_RemoteClosed; + bool m_BlockCloseMsg; TStaticRingBuffer m_Buffer; @@ -167,7 +168,7 @@ class CNetConnection void Resend(); public: - void Init(NETSOCKET Socket); + void Init(NETSOCKET Socket, bool BlockCloseMsg); int Connect(NETADDR *pAddr); void Disconnect(const char *pReason); @@ -187,6 +188,7 @@ class CNetConnection // Needed for GotProblems in NetClient int64 LastRecvTime() const { return m_LastRecvTime; } + int64 ConnectTime() const { return m_LastUpdateTime; } int AckSequence() const { return m_Ack; } }; @@ -332,8 +334,8 @@ class CNetClient NETADDR m_ServerAddr; CNetConnection m_Connection; CNetRecvUnpacker m_RecvUnpacker; - NETSOCKET m_Socket; public: + NETSOCKET m_Socket; // openness bool Open(NETADDR BindAddr, int Flags); int Close(); @@ -353,7 +355,7 @@ class CNetClient int ResetErrorString(); // error and state - int NetType() { return m_Socket.type; } + int NetType() const { return m_Socket.type; } int State(); int GotProblems(); const char *ErrorString(); diff --git a/src/engine/shared/network_client.cpp b/src/engine/shared/network_client.cpp index 2c0356061b..8e0e291061 100644 --- a/src/engine/shared/network_client.cpp +++ b/src/engine/shared/network_client.cpp @@ -16,7 +16,7 @@ bool CNetClient::Open(NETADDR BindAddr, int Flags) // init m_Socket = Socket; - m_Connection.Init(m_Socket); + m_Connection.Init(m_Socket, false); return true; } diff --git a/src/engine/shared/network_conn.cpp b/src/engine/shared/network_conn.cpp index ed0d40cdfb..344a3cf80b 100644 --- a/src/engine/shared/network_conn.cpp +++ b/src/engine/shared/network_conn.cpp @@ -37,12 +37,13 @@ void CNetConnection::SetError(const char *pString) str_copy(m_ErrorString, pString, sizeof(m_ErrorString)); } -void CNetConnection::Init(NETSOCKET Socket) +void CNetConnection::Init(NETSOCKET Socket, bool BlockCloseMsg) { Reset(); ResetStats(); m_Socket = Socket; + m_BlockCloseMsg = BlockCloseMsg; mem_zero(m_ErrorString, sizeof(m_ErrorString)); } @@ -124,8 +125,7 @@ int CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int } else { - // out of buffer - Disconnect("too weak connection (out of buffer)"); + // out of buffer, don't save the packet and hope nobody will ask for resend return -1; } } @@ -213,24 +213,25 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) m_State = NET_CONNSTATE_ERROR; m_RemoteClosed = 1; - if(pPacket->m_DataSize) + char Str[128] = {0}; + if(pPacket->m_DataSize > 1) { // make sure to sanitize the error string form the other party - char Str[128]; if(pPacket->m_DataSize < 128) - str_copy(Str, (char *)pPacket->m_aChunkData, pPacket->m_DataSize); + str_copy(Str, (char *)&pPacket->m_aChunkData[1], pPacket->m_DataSize); else - str_copy(Str, (char *)pPacket->m_aChunkData, sizeof(Str)); + str_copy(Str, (char *)&pPacket->m_aChunkData[1], sizeof(Str)); str_sanitize_strong(Str); + } + if(!m_BlockCloseMsg) + { // set the error string SetError(Str); } - else - SetError("No reason given"); if(g_Config.m_Debug) - dbg_msg("conn", "closed reason='%s'", ErrorString()); + dbg_msg("conn", "closed reason='%s'", Str); } return 0; } @@ -244,6 +245,7 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) Reset(); m_State = NET_CONNSTATE_PENDING; m_PeerAddr = *pAddr; + mem_zero(m_ErrorString, sizeof(m_ErrorString)); m_LastSendTime = Now; m_LastRecvTime = Now; m_LastUpdateTime = Now; diff --git a/src/engine/shared/network_server.cpp b/src/engine/shared/network_server.cpp index 1264a4a5aa..3552c4fb1b 100644 --- a/src/engine/shared/network_server.cpp +++ b/src/engine/shared/network_server.cpp @@ -30,7 +30,7 @@ bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int Ma m_MaxClientsPerIP = MaxClientsPerIP; for(int i = 0; i < NET_MAX_CLIENTS; i++) - m_aSlots[i].m_Connection.Init(m_Socket); + m_aSlots[i].m_Connection.Init(m_Socket, true); return true; } @@ -69,11 +69,17 @@ int CNetServer::Drop(int ClientID, const char *pReason) int CNetServer::Update() { + int64 Now = time_get(); for(int i = 0; i < MaxClients(); i++) { m_aSlots[i].m_Connection.Update(); if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) + { + if (Now - m_aSlots[i].m_Connection.ConnectTime() < time_freq() / 5 && NetBan()) + NetBan()->BanAddr(ClientAddr(i), 60, "Too many connections"); + else Drop(i, m_aSlots[i].m_Connection.ErrorString()); + } } return 0; @@ -99,17 +105,19 @@ int CNetServer::Recv(CNetChunk *pChunk) if(Bytes <= 0) break; - if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) + // check if we just should drop the packet + char aBuf[128]; + if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf))) { - // check if we just should drop the packet - char aBuf[128]; - if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf))) - { - // banned, reply with a message - CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf)+1); - continue; - } + // banned, reply with a message + CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf)+1); + continue; + } + + bool Found = false; + if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) + { if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) { pChunk->m_Flags = NETSENDFLAG_CONNLESS; @@ -124,7 +132,7 @@ int CNetServer::Recv(CNetChunk *pChunk) // TODO: check size here if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) { - bool Found = false; + Found = false; // check if we already got this client for(int i = 0; i < MaxClients(); i++) @@ -232,7 +240,7 @@ int CNetServer::Send(CNetChunk *pChunk) } else { - Drop(pChunk->m_ClientID, "Error sending data"); + //Drop(pChunk->m_ClientID, "Error sending data"); } } return 0; diff --git a/src/engine/shared/packer.cpp b/src/engine/shared/packer.cpp index cc218825c4..788e564d03 100644 --- a/src/engine/shared/packer.cpp +++ b/src/engine/shared/packer.cpp @@ -136,7 +136,7 @@ const char *CUnpacker::GetString(int SanitizeType) str_sanitize(pPtr); else if(SanitizeType&SANITIZE_CC) str_sanitize_cc(pPtr); - return SanitizeType&SKIP_START_WHITESPACES ? str_skip_whitespaces(pPtr) : pPtr; + return SanitizeType&SKIP_START_WHITESPACES ? str_utf8_skip_whitespaces(pPtr) : pPtr; } const unsigned char *CUnpacker::GetRaw(int Size) diff --git a/src/engine/shared/protocol.h b/src/engine/shared/protocol.h index ba04da8afa..8b4195f079 100644 --- a/src/engine/shared/protocol.h +++ b/src/engine/shared/protocol.h @@ -78,7 +78,8 @@ enum SERVER_TICK_SPEED=50, SERVER_FLAG_PASSWORD = 0x1, - MAX_CLIENTS=16, + MAX_CLIENTS=64, + VANILLA_MAX_CLIENTS=16, MAX_INPUT_SIZE=128, MAX_SNAPSHOT_PACKSIZE=900, @@ -94,4 +95,14 @@ enum MSGFLAG_NOSEND=16 }; +enum +{ + VERSION_VANILLA = 0, + VERSION_DDRACE = 1, + VERSION_DDNET_OLD = 2, + VERSION_DDNET_WHISPER = 217, + VERSION_DDNET_GOODHOOK = 221, + VERSION_DDNET_EXTRATUNES = 302, +}; + #endif diff --git a/src/engine/shared/snapshot.cpp b/src/engine/shared/snapshot.cpp index 9ef8fdc31d..d66e089fdb 100644 --- a/src/engine/shared/snapshot.cpp +++ b/src/engine/shared/snapshot.cpp @@ -195,13 +195,14 @@ int CSnapshotDelta::CreateDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pDstData // fetch previous indices // we do this as a separate pass because it helps the cache - for(i = 0; i < pTo->NumItems(); i++) + const int NumItems = pTo->NumItems(); + for(i = 0; i < NumItems; i++) { pCurItem = pTo->GetItem(i); // O(1) .. O(n) aPastIndecies[i] = GetItemIndexHashed(pCurItem->Key(), Hashlist); // O(n) .. O(n^n) } - for(i = 0; i < pTo->NumItems(); i++) + for(i = 0; i < NumItems; i++) { // do delta ItemSize = pTo->GetItemSize(i); // O(1) .. O(n) @@ -330,7 +331,7 @@ int CSnapshotDelta::UnpackDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pSrcData Type = *pData++; ID = *pData++; - if(m_aItemSizes[Type]) + if ((unsigned int) Type < sizeof(m_aItemSizes) && m_aItemSizes[Type]) ItemSize = m_aItemSizes[Type]; else { @@ -474,7 +475,7 @@ int CSnapshotStorage::Get(int Tick, int64 *pTagtime, CSnapshot **ppData, CSnapsh if(ppData) *ppData = pHolder->m_pSnap; if(ppAltData) - *ppData = pHolder->m_pAltSnap; + *ppAltData = pHolder->m_pAltSnap; return pHolder->m_SnapSize; } diff --git a/src/engine/shared/storage.cpp b/src/engine/shared/storage.cpp index 86b5e72cdd..8896bfa1bf 100644 --- a/src/engine/shared/storage.cpp +++ b/src/engine/shared/storage.cpp @@ -65,6 +65,7 @@ class CStorage : public IStorage fs_makedir(GetPath(TYPE_SAVE, "dumps", aPath, sizeof(aPath))); fs_makedir(GetPath(TYPE_SAVE, "demos", aPath, sizeof(aPath))); fs_makedir(GetPath(TYPE_SAVE, "demos/auto", aPath, sizeof(aPath))); + fs_makedir(GetPath(TYPE_SAVE, "editor", aPath, sizeof(aPath))); fs_makedir(GetPath(TYPE_SAVE, "ghosts", aPath, sizeof(aPath))); } diff --git a/src/engine/shared/storage.h b/src/engine/shared/storage.h index 417ba74c3d..7b43f571b7 100644 --- a/src/engine/shared/storage.h +++ b/src/engine/shared/storage.h @@ -4,7 +4,6 @@ #include #include #include "engine.h" -#include "config.h" // compiled-in data-dir path #define DATA_DIR "data" @@ -45,4 +44,4 @@ class CStorage : public IStorage IStorage *CreateStorage(const char *pApplicationName, const char *pArgv0); -#endif \ No newline at end of file +#endif diff --git a/src/game/client/component.h b/src/game/client/component.h index 858b456f30..fc44255782 100644 --- a/src/game/client/component.h +++ b/src/game/client/component.h @@ -27,6 +27,9 @@ class CComponent class IDemoPlayer *DemoPlayer() const { return m_pClient->DemoPlayer(); } class IDemoRecorder *DemoRecorder() const { return m_pClient->DemoRecorder(); } class IServerBrowser *ServerBrowser() const { return m_pClient->ServerBrowser(); } +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + class IAutoUpdate *AutoUpdate() const { return m_pClient->AutoUpdate(); } +#endif class CLayers *Layers() const { return m_pClient->Layers(); } class CCollision *Collision() const { return m_pClient->Collision(); } public: diff --git a/src/game/client/components/binds.cpp b/src/game/client/components/binds.cpp index ab151a61ab..0acd80b52e 100644 --- a/src/game/client/components/binds.cpp +++ b/src/game/client/components/binds.cpp @@ -99,9 +99,23 @@ void CBinds::SetDefaults() Bind(KEY_MOUSE_1, "+fire"); Bind(KEY_MOUSE_2, "+hook"); Bind(KEY_LSHIFT, "+emote"); - Bind(KEY_RSHIFT, "+spectate"); +#if defined(__ANDROID__) + Bind(KEY_RCTRL, "+fire"); + Bind(KEY_RETURN, "+hook"); + Bind(KEY_RIGHT, "+right"); + Bind(KEY_LEFT, "+left"); + Bind(KEY_UP, "+jump"); + Bind(KEY_DOWN, "+hook"); + Bind(KEY_PAGEUP, "+prevweapon"); + Bind(KEY_PAGEDOWN, "+nextweapon"); + Bind(KEY_F5, "spectate_previous"); + Bind(KEY_F6, "spectate_next"); +#else Bind(KEY_RIGHT, "spectate_next"); Bind(KEY_LEFT, "spectate_previous"); + Bind(KEY_RSHIFT, "+spectate"); +#endif + Bind('1', "+weapon1"); Bind('2', "+weapon2"); @@ -118,6 +132,9 @@ void CBinds::SetDefaults() Bind(KEY_F3, "vote yes"); Bind(KEY_F4, "vote no"); + Bind('k', "kill"); + Bind('p', "say /pause"); + // DDRace if(g_Config.m_ClDDRaceBinds) @@ -265,12 +282,17 @@ void CBinds::SetDDRaceBinds(bool FreeOnly) Bind('c', "say /rank"); Bind('v', "say /info"); Bind('b', "say /top5"); + Bind('p', "say /points"); Bind('z', "emote 12"); Bind('x', "emote 14"); Bind('h', "emote 2"); Bind('m', "emote 5"); - Bind(KEY_PAGEDOWN, "cl_show_entities 0"); - Bind(KEY_PAGEUP, "cl_show_entities 1"); + Bind('s', "+showhookcoll"); + Bind('x', "toggle cl_dummy 0 1"); +#if !defined(__ANDROID__) + Bind(KEY_PAGEDOWN, "toggle cl_show_quads 0 1"); + Bind(KEY_PAGEUP, "toggle cl_overlay_entities 0 100"); +#endif Bind(KEY_KP0, "say /emote normal 999999"); Bind(KEY_KP1, "say /emote happy 999999"); Bind(KEY_KP2, "say /emote angry 999999"); @@ -307,6 +329,8 @@ void CBinds::SetDDRaceBinds(bool FreeOnly) Bind('v', "say /info"); if(!Get('b')[0]) Bind('b', "say /top5"); + if(!Get('p')[0]) + Bind('p', "say /points"); if(!Get('z')[0]) Bind('z', "emote 12"); if(!Get('x')[0]) @@ -315,6 +339,10 @@ void CBinds::SetDDRaceBinds(bool FreeOnly) Bind('h', "emote 2"); if(!Get('m')[0]) Bind('m', "emote 5"); + if(!Get('s')[0]) + Bind('s', "+showhookcoll"); + if(!Get('x')[0]) + Bind('x', "toggle cl_dummy 0 1"); if(!Get(KEY_PAGEDOWN)[0]) Bind(KEY_PAGEDOWN, "cl_show_entities 0"); if(!Get(KEY_PAGEUP)[0]) @@ -338,5 +366,6 @@ void CBinds::SetDDRaceBinds(bool FreeOnly) if(!Get(KEY_EQUALS)[0]) Bind(KEY_EQUALS, "spectate_next"); } - g_Config.m_ClDDRaceBindsSet = 1; -} \ No newline at end of file + + g_Config.m_ClDDRaceBindsSet = 1; +} diff --git a/src/game/client/components/camera.cpp b/src/game/client/components/camera.cpp index 17c91e753f..6e7aa0f432 100644 --- a/src/game/client/components/camera.cpp +++ b/src/game/client/components/camera.cpp @@ -31,30 +31,32 @@ void CCamera::OnRender() { if(m_CamType != CAMTYPE_SPEC) { - m_pClient->m_pControls->m_MousePos = m_PrevCenter; + m_LastPos[g_Config.m_ClDummy] = m_pClient->m_pControls->m_MousePos[g_Config.m_ClDummy]; + m_pClient->m_pControls->m_MousePos[g_Config.m_ClDummy] = m_PrevCenter; m_pClient->m_pControls->ClampMousePos(); m_CamType = CAMTYPE_SPEC; } - m_Center = m_pClient->m_pControls->m_MousePos; + m_Center = m_pClient->m_pControls->m_MousePos[g_Config.m_ClDummy]; } else { if(m_CamType != CAMTYPE_PLAYER) { + m_pClient->m_pControls->m_MousePos[g_Config.m_ClDummy] = m_LastPos[g_Config.m_ClDummy]; m_pClient->m_pControls->ClampMousePos(); m_CamType = CAMTYPE_PLAYER; } vec2 CameraOffset(0, 0); - float l = length(m_pClient->m_pControls->m_MousePos); + float l = length(m_pClient->m_pControls->m_MousePos[g_Config.m_ClDummy]); if(l > 0.0001f) // make sure that this isn't 0 { float DeadZone = g_Config.m_ClMouseDeadzone; float FollowFactor = g_Config.m_ClMouseFollowfactor/100.0f; float OffsetAmount = max(l-DeadZone, 0.0f) * FollowFactor; - CameraOffset = normalize(m_pClient->m_pControls->m_MousePos)*OffsetAmount; + CameraOffset = normalize(m_pClient->m_pControls->m_MousePos[g_Config.m_ClDummy])*OffsetAmount; } if(m_pClient->m_Snap.m_SpecInfo.m_Active) @@ -78,13 +80,13 @@ void CCamera::OnReset() m_Zoom = 1.0f; } -const float ZoomStep = 0.75f; +const float ZoomStep = 0.866025f; void CCamera::ConZoomPlus(IConsole::IResult *pResult, void *pUserData) { CCamera *pSelf = (CCamera *)pUserData; CServerInfo Info; pSelf->Client()->GetServerInfo(&Info); - if(g_Config.m_ClDDRaceCheats == 1 && str_find_nocase(Info.m_aGameType, "race")) + if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || (str_find_nocase(Info.m_aGameType, "race") || str_find_nocase(Info.m_aGameType, "fastcap")) || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK) ((CCamera *)pUserData)->m_Zoom *= ZoomStep; } void CCamera::ConZoomMinus(IConsole::IResult *pResult, void *pUserData) @@ -92,7 +94,7 @@ void CCamera::ConZoomMinus(IConsole::IResult *pResult, void *pUserData) CCamera *pSelf = (CCamera *)pUserData; CServerInfo Info; pSelf->Client()->GetServerInfo(&Info); - if(g_Config.m_ClDDRaceCheats == 1 && str_find_nocase(Info.m_aGameType, "race")) + if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || (str_find_nocase(Info.m_aGameType, "race") || str_find_nocase(Info.m_aGameType, "fastcap")) || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK) ((CCamera *)pUserData)->m_Zoom *= 1/ZoomStep; } void CCamera::ConZoomReset(IConsole::IResult *pResult, void *pUserData) diff --git a/src/game/client/components/camera.h b/src/game/client/components/camera.h index d03189f35f..85470fd52d 100644 --- a/src/game/client/components/camera.h +++ b/src/game/client/components/camera.h @@ -15,6 +15,7 @@ class CCamera : public CComponent }; int m_CamType; + vec2 m_LastPos[2]; vec2 m_PrevCenter; public: @@ -34,4 +35,4 @@ class CCamera : public CComponent static void ConZoomReset(IConsole::IResult *pResult, void *pUserData); }; -#endif \ No newline at end of file +#endif diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index cba8e02fe7..468857f7a1 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -85,6 +85,8 @@ void CChat::ConChat(IConsole::IResult *pResult, void *pUserData) ((CChat*)pUserData)->EnableMode(1); else ((CChat*)pUserData)->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "console", "expected all or team as mode"); + + ((CChat*)pUserData)->m_Input.Set(pResult->GetString(1)); } void CChat::ConShowChat(IConsole::IResult *pResult, void *pUserData) @@ -96,7 +98,7 @@ void CChat::OnConsoleInit() { Console()->Register("say", "r", CFGFLAG_CLIENT, ConSay, this, "Say in chat"); Console()->Register("say_team", "r", CFGFLAG_CLIENT, ConSayTeam, this, "Say in team chat"); - Console()->Register("chat", "s", CFGFLAG_CLIENT, ConChat, this, "Enable chat with all/team mode"); + Console()->Register("chat", "s?r", CFGFLAG_CLIENT, ConChat, this, "Enable chat with all/team mode"); Console()->Register("+show_chat", "", CFGFLAG_CLIENT, ConShowChat, this, "Show chat"); } @@ -114,13 +116,25 @@ bool CChat::OnInput(IInput::CEvent Event) { if(m_Input.GetString()[0]) { + bool AddEntry = false; + if(m_LastChatSend+time_freq() < time_get()) + { Say(m_Mode == MODE_ALL ? 0 : 1, m_Input.GetString()); - else + AddEntry = true; + } + else if(m_PendingChatCounter < 3) + { ++m_PendingChatCounter; - CHistoryEntry *pEntry = m_History.Allocate(sizeof(CHistoryEntry)+m_Input.GetLength()); - pEntry->m_Team = m_Mode == MODE_ALL ? 0 : 1; - mem_copy(pEntry->m_aText, m_Input.GetString(), m_Input.GetLength()+1); + AddEntry = true; + } + + if(AddEntry) + { + CHistoryEntry *pEntry = m_History.Allocate(sizeof(CHistoryEntry)+m_Input.GetLength()); + pEntry->m_Team = m_Mode == MODE_ALL ? 0 : 1; + mem_copy(pEntry->m_aText, m_Input.GetString(), m_Input.GetLength()+1); + } } m_pHistoryEntry = 0x0; m_Mode = MODE_NONE; @@ -253,6 +267,7 @@ void CChat::EnableMode(int Team) m_Input.Clear(); Input()->ClearEvents(); m_CompletionChosen = -1; + UI()->AndroidShowTextInput("", Team ? Localize("Team chat") : Localize("Chat")); } } @@ -267,11 +282,39 @@ void CChat::OnMessage(int MsgType, void *pRawMsg) void CChat::AddLine(int ClientID, int Team, const char *pLine) { - if(ClientID != -1 && (m_pClient->m_aClients[ClientID].m_aName[0] == '\0' || // unknown client + if(*pLine == 0 || (ClientID != -1 && (m_pClient->m_aClients[ClientID].m_aName[0] == '\0' || // unknown client m_pClient->m_aClients[ClientID].m_ChatIgnore || - (m_pClient->m_Snap.m_LocalClientID != ClientID && g_Config.m_ClShowChatFriends && !m_pClient->m_aClients[ClientID].m_Friend))) + (m_pClient->m_Snap.m_LocalClientID != ClientID && g_Config.m_ClShowChatFriends && !m_pClient->m_aClients[ClientID].m_Friend)))) return; + // trim right and set maximum length to 256 utf8-characters + int Length = 0; + const char *pStr = pLine; + const char *pEnd = 0; + while(*pStr) + { + const char *pStrOld = pStr; + int Code = str_utf8_decode(&pStr); + + // check if unicode is not empty + if(Code > 0x20 && Code != 0xA0 && Code != 0x034F && (Code < 0x2000 || Code > 0x200F) && (Code < 0x2028 || Code > 0x202F) && + (Code < 0x205F || Code > 0x2064) && (Code < 0x206A || Code > 0x206F) && (Code < 0xFE00 || Code > 0xFE0F) && + Code != 0xFEFF && (Code < 0xFFF9 || Code > 0xFFFC)) + { + pEnd = 0; + } + else if(pEnd == 0) + pEnd = pStrOld; + + if(++Length >= 256) + { + *(const_cast(pStr)) = 0; + break; + } + } + if(pEnd != 0) + *(const_cast(pEnd)) = 0; + bool Highlighted = false; char *p = const_cast(pLine); while(*p) @@ -297,14 +340,26 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine) m_aLines[m_CurrentLine].m_NameColor = -2; // check for highlighted name - const char *pHL = str_find_nocase(pLine, m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName); + const char *pHL = str_find_nocase(pLine, m_pClient->m_aClients[m_pClient->Client()->m_LocalIDs[0]].m_aName); if(pHL) { - int Length = str_length(m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName); - if((pLine == pHL || pHL[-1] == ' ') && (pHL[Length] == 0 || pHL[Length] == ' ' || (pHL[Length] == ':' && pHL[Length+1] == ' '))) + int Length = str_length(m_pClient->m_aClients[m_pClient->Client()->m_LocalIDs[0]].m_aName); + if((pLine == pHL || pHL[-1] == ' ') && (pHL[Length] == 0 || pHL[Length] == ' ' || pHL[Length] == '.' || pHL[Length] == '!' || pHL[Length] == ',' || pHL[Length] == '?' || pHL[Length] == ':')) Highlighted = true; } - m_aLines[m_CurrentLine].m_Highlighted = Highlighted; + + if(m_pClient->Client()->DummyConnected()) + { + pHL = str_find_nocase(pLine, m_pClient->m_aClients[m_pClient->Client()->m_LocalIDs[1]].m_aName); + if(pHL) + { + int Length = str_length(m_pClient->m_aClients[m_pClient->Client()->m_LocalIDs[1]].m_aName); + if((pLine == pHL || pHL[-1] == ' ') && (pHL[Length] == 0 || pHL[Length] == ' ' || pHL[Length] == '.' || pHL[Length] == '!' || pHL[Length] == ',' || pHL[Length] == '?' || pHL[Length] == ':')) + Highlighted = true; + } + } + + m_aLines[m_CurrentLine].m_Highlighted = Highlighted; if(ClientID == -1) // server message { @@ -324,13 +379,33 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine) m_aLines[m_CurrentLine].m_NameColor = TEAM_BLUE; } - str_copy(m_aLines[m_CurrentLine].m_aName, m_pClient->m_aClients[ClientID].m_aName, sizeof(m_aLines[m_CurrentLine].m_aName)); + if (Team == 2) // whisper send + { + str_format(m_aLines[m_CurrentLine].m_aName, sizeof(m_aLines[m_CurrentLine].m_aName), "→ %s", m_pClient->m_aClients[ClientID].m_aName); + m_aLines[m_CurrentLine].m_NameColor = TEAM_BLUE; + m_aLines[m_CurrentLine].m_Highlighted = false; + m_aLines[m_CurrentLine].m_Team = 0; + Highlighted = false; + } + else if (Team == 3) // whisper recv + { + str_format(m_aLines[m_CurrentLine].m_aName, sizeof(m_aLines[m_CurrentLine].m_aName), "← %s", m_pClient->m_aClients[ClientID].m_aName); + m_aLines[m_CurrentLine].m_NameColor = TEAM_RED; + m_aLines[m_CurrentLine].m_Highlighted = true; + m_aLines[m_CurrentLine].m_Team = 0; + Highlighted = true; + } + else + { + str_copy(m_aLines[m_CurrentLine].m_aName, m_pClient->m_aClients[ClientID].m_aName, sizeof(m_aLines[m_CurrentLine].m_aName)); + } + str_format(m_aLines[m_CurrentLine].m_aText, sizeof(m_aLines[m_CurrentLine].m_aText), ": %s", pLine); } char aBuf[1024]; str_format(aBuf, sizeof(aBuf), "%s%s", m_aLines[m_CurrentLine].m_aName, m_aLines[m_CurrentLine].m_aText); - Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_aLines[m_CurrentLine].m_Team?"teamchat":"chat", aBuf); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, Team >= 2?"whisper":(m_aLines[m_CurrentLine].m_Team?"teamchat":"chat"), aBuf); } // play sound @@ -347,22 +422,29 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine) { if(Now-m_aLastSoundPlayed[CHAT_HIGHLIGHT] >= time_freq()*3/10) { - m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_CLIENT, 0); + m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_HIGHLIGHT, 0); m_aLastSoundPlayed[CHAT_HIGHLIGHT] = Now; } } - else + else if(Team != 2) { if(Now-m_aLastSoundPlayed[CHAT_CLIENT] >= time_freq()*3/10) { - m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_HIGHLIGHT, 0); - m_aLastSoundPlayed[CHAT_CLIENT] = Now; + if ((g_Config.m_SndTeamChat || !m_aLines[m_CurrentLine].m_Team) + && (g_Config.m_SndChat || m_aLines[m_CurrentLine].m_Team)) + { + m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_CLIENT, 0); + m_aLastSoundPlayed[CHAT_CLIENT] = Now; + } } } } void CChat::OnRender() { + if (!g_Config.m_ClShowChat) + return; + // send pending chat messages if(m_PendingChatCounter > 0 && m_LastChatSend+time_freq() < time_get()) { @@ -434,12 +516,19 @@ void CChat::OnRender() } y -= 8.0f; +#if defined(__ANDROID__) + x += 120.0f; +#endif int64 Now = time_get(); float LineWidth = m_pClient->m_pScoreboard->Active() ? 90.0f : 200.0f; float HeightLimit = m_pClient->m_pScoreboard->Active() ? 230.0f : m_Show ? 50.0f : 200.0f; float Begin = x; +#if defined(__ANDROID__) + float FontSize = 10.0f; +#else float FontSize = 6.0f; +#endif CTextCursor Cursor; int OffsetType = m_pClient->m_pScoreboard->Active() ? 1 : 0; for(int i = 0; i < MAX_LINES; i++) @@ -470,9 +559,13 @@ void CChat::OnRender() Cursor.m_LineWidth = LineWidth; // render name - if(m_aLines[r].m_ClientID == -1) - TextRender()->TextColor(1.0f, 1.0f, 0.5f, Blend); // system - else if(m_aLines[r].m_Team) + if (m_aLines[r].m_ClientID == -1) + { + //TextRender()->TextColor(1.0f, 1.0f, 0.5f, Blend); // system + vec3 rgb = HslToRgb(vec3(g_Config.m_ClMessageSystemHue / 255.0f, g_Config.m_ClMessageSystemSat / 255.0f, g_Config.m_ClMessageSystemLht / 255.0f)); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, Blend); + } + else if (m_aLines[r].m_Team) TextRender()->TextColor(0.45f, 0.9f, 0.45f, Blend); // team message else if(m_aLines[r].m_NameColor == TEAM_RED) TextRender()->TextColor(1.0f, 0.5f, 0.5f, Blend); // red @@ -480,25 +573,78 @@ void CChat::OnRender() TextRender()->TextColor(0.7f, 0.7f, 1.0f, Blend); // blue else if(m_aLines[r].m_NameColor == TEAM_SPECTATORS) TextRender()->TextColor(0.75f, 0.5f, 0.75f, Blend); // spectator + else if(m_aLines[r].m_ClientID >= 0 && g_Config.m_ClChatTeamColors && m_pClient->m_Teams.Team(m_aLines[r].m_ClientID)) + { + vec3 rgb = HslToRgb(vec3(m_pClient->m_Teams.Team(m_aLines[r].m_ClientID) / 64.0f, 1.0f, 0.75f)); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, Blend); + } else TextRender()->TextColor(0.8f, 0.8f, 0.8f, Blend); TextRender()->TextEx(&Cursor, m_aLines[r].m_aName, -1); // render line - if(m_aLines[r].m_ClientID == -1) - TextRender()->TextColor(1.0f, 1.0f, 0.5f, Blend); // system - else if(m_aLines[r].m_Highlighted) - TextRender()->TextColor(1.0f, 0.5f, 0.5f, Blend); // highlighted - else if(m_aLines[r].m_Team) - TextRender()->TextColor(0.65f, 1.0f, 0.65f, Blend); // team message - else - TextRender()->TextColor(1.0f, 1.0f, 1.0f, Blend); + if (m_aLines[r].m_ClientID == -1) + { + //TextRender()->TextColor(1.0f, 1.0f, 0.5f, Blend); // system + vec3 rgb = HslToRgb(vec3(g_Config.m_ClMessageSystemHue / 255.0f, g_Config.m_ClMessageSystemSat / 255.0f, g_Config.m_ClMessageSystemLht / 255.0f)); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, Blend); + } + else if (m_aLines[r].m_Highlighted) + { + //TextRender()->TextColor(1.0f, 0.5f, 0.5f, Blend); // highlighted + vec3 rgb = HslToRgb(vec3(g_Config.m_ClMessageHighlightHue / 255.0f, g_Config.m_ClMessageHighlightSat / 255.0f, g_Config.m_ClMessageHighlightLht / 255.0f)); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, Blend); + } + else if (m_aLines[r].m_Team) + { + //TextRender()->TextColor(0.65f, 1.0f, 0.65f, Blend); // team message + vec3 rgb = HslToRgb(vec3(g_Config.m_ClMessageTeamHue / 255.0f, g_Config.m_ClMessageTeamSat / 255.0f, g_Config.m_ClMessageTeamLht / 255.0f)); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, Blend); + } + else + { + //TextRender()->TextColor(1.0f, 1.0f, 1.0f, Blend); + vec3 rgb = HslToRgb(vec3(g_Config.m_ClMessageHue / 255.0f, g_Config.m_ClMessageSat / 255.0f, g_Config.m_ClMessageLht / 255.0f)); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, Blend); + } + TextRender()->TextEx(&Cursor, m_aLines[r].m_aText, -1); } TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); + +#if defined(__ANDROID__) + static int deferEvent = 0; + if( UI()->AndroidTextInputShown() ) + { + if(m_Mode == MODE_NONE) + { + deferEvent++; + if( deferEvent > 2 ) + EnableMode(0); + } + else + deferEvent = 0; + } + else + { + if(m_Mode != MODE_NONE) + { + deferEvent++; + if( deferEvent > 2 ) + { + IInput::CEvent Event; + Event.m_Flags = IInput::FLAG_PRESS; + Event.m_Key = KEY_RETURN; + OnInput(Event); + } + } + else + deferEvent = 0; + } +#endif } void CChat::Say(int Team, const char *pLine) diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index 0f4fc0c7d9..792f5c096f 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -437,14 +437,8 @@ void CGameConsole::OnRender() x = Cursor.m_X; - // render console input (wrap line) - int Lines = TextRender()->TextLineCount(0, FontSize, pConsole->m_Input.GetString(), Screen.w - 10.0f - x); - y -= (Lines - 1) * FontSize; - TextRender()->SetCursor(&Cursor, x, y, FontSize, TEXTFLAG_RENDER); - Cursor.m_LineWidth = Screen.w - 10.0f - x; - //hide rcon password - char aInputString[256]; + char aInputString[512]; str_copy(aInputString, pConsole->m_Input.GetString(), sizeof(aInputString)); if(m_ConsoleType == CONSOLETYPE_REMOTE && Client()->State() == IClient::STATE_ONLINE && !Client()->RconAuthed()) { @@ -452,10 +446,22 @@ void CGameConsole::OnRender() aInputString[i] = '*'; } + // render console input (wrap line) + TextRender()->SetCursor(&Cursor, x, y, FontSize, 0); + Cursor.m_LineWidth = Screen.w - 10.0f - x; + TextRender()->TextEx(&Cursor, aInputString, pConsole->m_Input.GetCursorOffset()); + TextRender()->TextEx(&Cursor, aInputString+pConsole->m_Input.GetCursorOffset(), -1); + int Lines = Cursor.m_LineCount; + + y -= (Lines - 1) * FontSize; + TextRender()->SetCursor(&Cursor, x, y, FontSize, TEXTFLAG_RENDER); + Cursor.m_LineWidth = Screen.w - 10.0f - x; + TextRender()->TextEx(&Cursor, aInputString, pConsole->m_Input.GetCursorOffset()); static float MarkerOffset = TextRender()->TextWidth(0, FontSize, "|", -1)/3; CTextCursor Marker = Cursor; Marker.m_X -= MarkerOffset; + Marker.m_LineWidth = -1; TextRender()->TextEx(&Marker, "|", -1); TextRender()->TextEx(&Cursor, aInputString+pConsole->m_Input.GetCursorOffset(), -1); diff --git a/src/game/client/components/controls.cpp b/src/game/client/components/controls.cpp index 855786ebdf..f2b8209e6b 100644 --- a/src/game/client/components/controls.cpp +++ b/src/game/client/components/controls.cpp @@ -1,10 +1,11 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ - #include #include +#include + #include #include @@ -16,24 +17,74 @@ #include "controls.h" +enum { LEFT_JOYSTICK_X = 0, LEFT_JOYSTICK_Y = 1, + RIGHT_JOYSTICK_X = 2, RIGHT_JOYSTICK_Y = 3, + SECOND_RIGHT_JOYSTICK_X = 20, SECOND_RIGHT_JOYSTICK_Y = 21, + NUM_JOYSTICK_AXES = 22 }; + CControls::CControls() { mem_zero(&m_LastData, sizeof(m_LastData)); + m_LastDummy = 0; + m_OtherFire = 0; + + SDL_Init(SDL_INIT_JOYSTICK); + m_Joystick = SDL_JoystickOpen(0); + if( m_Joystick && SDL_JoystickNumAxes(m_Joystick) < NUM_JOYSTICK_AXES ) + { + SDL_JoystickClose(m_Joystick); + m_Joystick = NULL; + } + + m_Gamepad = SDL_JoystickOpen(2); + + SDL_JoystickEventState(SDL_QUERY); + + m_UsingGamepad = false; +#if defined(CONF_FAMILY_UNIX) + if( getenv("OUYA") ) + m_UsingGamepad = true; +#endif } void CControls::OnReset() { - m_LastData.m_Direction = 0; - m_LastData.m_Hook = 0; + m_LastData[g_Config.m_ClDummy].m_Direction = 0; + //m_LastData.m_Hook = 0; // simulate releasing the fire button - if((m_LastData.m_Fire&1) != 0) - m_LastData.m_Fire++; - m_LastData.m_Fire &= INPUT_STATE_MASK; - m_LastData.m_Jump = 0; - m_InputData = m_LastData; - - m_InputDirectionLeft = 0; - m_InputDirectionRight = 0; + if((m_LastData[g_Config.m_ClDummy].m_Fire&1) != 0) + m_LastData[g_Config.m_ClDummy].m_Fire++; + m_LastData[g_Config.m_ClDummy].m_Fire &= INPUT_STATE_MASK; + m_LastData[g_Config.m_ClDummy].m_Jump = 0; + m_InputData[g_Config.m_ClDummy] = m_LastData[g_Config.m_ClDummy]; + + m_InputDirectionLeft[g_Config.m_ClDummy] = 0; + m_InputDirectionRight[g_Config.m_ClDummy] = 0; + + m_JoystickFirePressed = false; + m_JoystickRunPressed = false; + m_JoystickTapTime = 0; + for( int i = 0; i < NUM_WEAPONS; i++ ) + m_AmmoCount[i] = 0; + m_OldMouseX = m_OldMouseY = 0.0f; +} + +void CControls::ResetDummyInput() +{ + m_LastData[!g_Config.m_ClDummy].m_Direction = 0; + if(m_LastData[!g_Config.m_ClDummy].m_Fire & 1) + m_LastData[!g_Config.m_ClDummy].m_Fire++; + m_LastData[!g_Config.m_ClDummy].m_Hook = 0; + m_LastData[!g_Config.m_ClDummy].m_Jump = 0; + + m_InputData[!g_Config.m_ClDummy].m_Direction = 0; + if(m_InputData[!g_Config.m_ClDummy].m_Fire & 1) + m_InputData[!g_Config.m_ClDummy].m_Fire++; + m_InputData[!g_Config.m_ClDummy].m_Hook = 0; + m_InputData[!g_Config.m_ClDummy].m_Jump = 0; + + m_InputDirectionLeft[!g_Config.m_ClDummy] = 0; + m_InputDirectionRight[!g_Config.m_ClDummy] = 0; } void CControls::OnRelease() @@ -43,17 +94,37 @@ void CControls::OnRelease() void CControls::OnPlayerDeath() { - m_LastData.m_WantedWeapon = m_InputData.m_WantedWeapon = 0; + m_LastData[g_Config.m_ClDummy].m_WantedWeapon = m_InputData[g_Config.m_ClDummy].m_WantedWeapon = 0; + for( int i = 0; i < NUM_WEAPONS; i++ ) + m_AmmoCount[i] = 0; + m_JoystickTapTime = 0; // Do not launch hook on first tap } +struct CInputState +{ + CControls *m_pControls; + int *m_pVariable1; + int *m_pVariable2; +}; + static void ConKeyInputState(IConsole::IResult *pResult, void *pUserData) { - ((int *)pUserData)[0] = pResult->GetInteger(0); + CInputState *pState = (CInputState *)pUserData; + if (g_Config.m_ClDummy) + *pState->m_pVariable2 = pResult->GetInteger(0); + else + *pState->m_pVariable1 = pResult->GetInteger(0); } static void ConKeyInputCounter(IConsole::IResult *pResult, void *pUserData) { - int *v = (int *)pUserData; + CInputState *pState = (CInputState *)pUserData; + int *v; + if (g_Config.m_ClDummy) + v = pState->m_pVariable2; + else + v = pState->m_pVariable1; + if(((*v)&1) != pResult->GetInteger(0)) (*v)++; *v &= INPUT_STATE_MASK; @@ -62,7 +133,8 @@ static void ConKeyInputCounter(IConsole::IResult *pResult, void *pUserData) struct CInputSet { CControls *m_pControls; - int *m_pVariable; + int *m_pVariable1; + int *m_pVariable2; int m_Value; }; @@ -70,33 +142,39 @@ static void ConKeyInputSet(IConsole::IResult *pResult, void *pUserData) { CInputSet *pSet = (CInputSet *)pUserData; if(pResult->GetInteger(0)) - *pSet->m_pVariable = pSet->m_Value; + { + if (g_Config.m_ClDummy) + *pSet->m_pVariable2 = pSet->m_Value; + else + *pSet->m_pVariable1 = pSet->m_Value; + } } static void ConKeyInputNextPrevWeapon(IConsole::IResult *pResult, void *pUserData) { CInputSet *pSet = (CInputSet *)pUserData; - ConKeyInputCounter(pResult, pSet->m_pVariable); - pSet->m_pControls->m_InputData.m_WantedWeapon = 0; + ConKeyInputCounter(pResult, pSet); + pSet->m_pControls->m_InputData[g_Config.m_ClDummy].m_WantedWeapon = 0; } void CControls::OnConsoleInit() { // game commands - Console()->Register("+left", "", CFGFLAG_CLIENT, ConKeyInputState, &m_InputDirectionLeft, "Move left"); - Console()->Register("+right", "", CFGFLAG_CLIENT, ConKeyInputState, &m_InputDirectionRight, "Move right"); - Console()->Register("+jump", "", CFGFLAG_CLIENT, ConKeyInputState, &m_InputData.m_Jump, "Jump"); - Console()->Register("+hook", "", CFGFLAG_CLIENT, ConKeyInputState, &m_InputData.m_Hook, "Hook"); - Console()->Register("+fire", "", CFGFLAG_CLIENT, ConKeyInputCounter, &m_InputData.m_Fire, "Fire"); - - { static CInputSet s_Set = {this, &m_InputData.m_WantedWeapon, 1}; Console()->Register("+weapon1", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to hammer"); } - { static CInputSet s_Set = {this, &m_InputData.m_WantedWeapon, 2}; Console()->Register("+weapon2", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to gun"); } - { static CInputSet s_Set = {this, &m_InputData.m_WantedWeapon, 3}; Console()->Register("+weapon3", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to shotgun"); } - { static CInputSet s_Set = {this, &m_InputData.m_WantedWeapon, 4}; Console()->Register("+weapon4", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to grenade"); } - { static CInputSet s_Set = {this, &m_InputData.m_WantedWeapon, 5}; Console()->Register("+weapon5", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to rifle"); } - - { static CInputSet s_Set = {this, &m_InputData.m_NextWeapon, 0}; Console()->Register("+nextweapon", "", CFGFLAG_CLIENT, ConKeyInputNextPrevWeapon, (void *)&s_Set, "Switch to next weapon"); } - { static CInputSet s_Set = {this, &m_InputData.m_PrevWeapon, 0}; Console()->Register("+prevweapon", "", CFGFLAG_CLIENT, ConKeyInputNextPrevWeapon, (void *)&s_Set, "Switch to previous weapon"); } + { static CInputState s_State = {this, &m_InputDirectionLeft[0], &m_InputDirectionLeft[1]}; Console()->Register("+left", "", CFGFLAG_CLIENT, ConKeyInputState, (void *)&s_State, "Move left"); } + { static CInputState s_State = {this, &m_InputDirectionRight[0], &m_InputDirectionRight[1]}; Console()->Register("+right", "", CFGFLAG_CLIENT, ConKeyInputState, (void *)&s_State, "Move right"); } + { static CInputState s_State = {this, &m_InputData[0].m_Jump, &m_InputData[1].m_Jump}; Console()->Register("+jump", "", CFGFLAG_CLIENT, ConKeyInputState, (void *)&s_State, "Jump"); } + { static CInputState s_State = {this, &m_InputData[0].m_Hook, &m_InputData[1].m_Hook}; Console()->Register("+hook", "", CFGFLAG_CLIENT, ConKeyInputState, (void *)&s_State, "Hook"); } + { static CInputState s_State = {this, &m_InputData[0].m_Fire, &m_InputData[1].m_Fire}; Console()->Register("+fire", "", CFGFLAG_CLIENT, ConKeyInputCounter, (void *)&s_State, "Fire"); } + { static CInputState s_State = {this, &m_ShowHookColl[0], &m_ShowHookColl[1]}; Console()->Register("+showhookcoll", "", CFGFLAG_CLIENT, ConKeyInputState, (void *)&s_State, "Show Hook Collision"); } + + { static CInputSet s_Set = {this, &m_InputData[0].m_WantedWeapon, &m_InputData[1].m_WantedWeapon, 1}; Console()->Register("+weapon1", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to hammer"); } + { static CInputSet s_Set = {this, &m_InputData[0].m_WantedWeapon, &m_InputData[1].m_WantedWeapon, 2}; Console()->Register("+weapon2", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to gun"); } + { static CInputSet s_Set = {this, &m_InputData[0].m_WantedWeapon, &m_InputData[1].m_WantedWeapon, 3}; Console()->Register("+weapon3", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to shotgun"); } + { static CInputSet s_Set = {this, &m_InputData[0].m_WantedWeapon, &m_InputData[1].m_WantedWeapon, 4}; Console()->Register("+weapon4", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to grenade"); } + { static CInputSet s_Set = {this, &m_InputData[0].m_WantedWeapon, &m_InputData[1].m_WantedWeapon, 5}; Console()->Register("+weapon5", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to rifle"); } + + { static CInputSet s_Set = {this, &m_InputData[0].m_NextWeapon, &m_InputData[1].m_NextWeapon, 0}; Console()->Register("+nextweapon", "", CFGFLAG_CLIENT, ConKeyInputNextPrevWeapon, (void *)&s_Set, "Switch to next weapon"); } + { static CInputSet s_Set = {this, &m_InputData[0].m_PrevWeapon, &m_InputData[1].m_PrevWeapon, 0}; Console()->Register("+prevweapon", "", CFGFLAG_CLIENT, ConKeyInputNextPrevWeapon, (void *)&s_Set, "Switch to previous weapon"); } } void CControls::OnMessage(int Msg, void *pRawMsg) @@ -105,7 +183,9 @@ void CControls::OnMessage(int Msg, void *pRawMsg) { CNetMsg_Sv_WeaponPickup *pMsg = (CNetMsg_Sv_WeaponPickup *)pRawMsg; if(g_Config.m_ClAutoswitchWeapons) - m_InputData.m_WantedWeapon = pMsg->m_Weapon+1; + m_InputData[g_Config.m_ClDummy].m_WantedWeapon = pMsg->m_Weapon+1; + // We don't really know ammo count, until we'll switch to that weapon, but any non-zero count will suffice here + m_AmmoCount[pMsg->m_Weapon%NUM_WEAPONS] = 10; } } @@ -116,26 +196,32 @@ int CControls::SnapInput(int *pData) // update player state if(m_pClient->m_pChat->IsActive()) - m_InputData.m_PlayerFlags = PLAYERFLAG_CHATTING; + m_InputData[g_Config.m_ClDummy].m_PlayerFlags = PLAYERFLAG_CHATTING; else if(m_pClient->m_pMenus->IsActive()) - m_InputData.m_PlayerFlags = PLAYERFLAG_IN_MENU; + m_InputData[g_Config.m_ClDummy].m_PlayerFlags = PLAYERFLAG_IN_MENU; else - m_InputData.m_PlayerFlags = PLAYERFLAG_PLAYING; + m_InputData[g_Config.m_ClDummy].m_PlayerFlags = PLAYERFLAG_PLAYING; if(m_pClient->m_pScoreboard->Active()) - m_InputData.m_PlayerFlags |= PLAYERFLAG_SCOREBOARD; + m_InputData[g_Config.m_ClDummy].m_PlayerFlags |= PLAYERFLAG_SCOREBOARD; + + if(m_InputData[g_Config.m_ClDummy].m_PlayerFlags != PLAYERFLAG_PLAYING) + m_JoystickTapTime = 0; // Do not launch hook on first tap + + if (m_pClient->m_pControls->m_ShowHookColl[g_Config.m_ClDummy]) + m_InputData[g_Config.m_ClDummy].m_PlayerFlags |= PLAYERFLAG_AIM; - if(m_LastData.m_PlayerFlags != m_InputData.m_PlayerFlags) + if(m_LastData[g_Config.m_ClDummy].m_PlayerFlags != m_InputData[g_Config.m_ClDummy].m_PlayerFlags) Send = true; - m_LastData.m_PlayerFlags = m_InputData.m_PlayerFlags; + m_LastData[g_Config.m_ClDummy].m_PlayerFlags = m_InputData[g_Config.m_ClDummy].m_PlayerFlags; // we freeze the input if chat or menu is activated - if(!(m_InputData.m_PlayerFlags&PLAYERFLAG_PLAYING)) + if(!(m_InputData[g_Config.m_ClDummy].m_PlayerFlags&PLAYERFLAG_PLAYING)) { OnReset(); - mem_copy(pData, &m_InputData, sizeof(m_InputData)); + mem_copy(pData, &m_InputData[g_Config.m_ClDummy], sizeof(m_InputData[0])); // send once a second just to be sure if(time_get() > LastSendTime + time_freq()) @@ -143,71 +229,210 @@ int CControls::SnapInput(int *pData) } else { - - m_InputData.m_TargetX = (int)m_MousePos.x; - m_InputData.m_TargetY = (int)m_MousePos.y; - if(!m_InputData.m_TargetX && !m_InputData.m_TargetY) + m_InputData[g_Config.m_ClDummy].m_TargetX = (int)m_MousePos[g_Config.m_ClDummy].x; + m_InputData[g_Config.m_ClDummy].m_TargetY = (int)m_MousePos[g_Config.m_ClDummy].y; + if(!m_InputData[g_Config.m_ClDummy].m_TargetX && !m_InputData[g_Config.m_ClDummy].m_TargetY) { - m_InputData.m_TargetX = 1; - m_MousePos.x = 1; + m_InputData[g_Config.m_ClDummy].m_TargetX = 1; + m_MousePos[g_Config.m_ClDummy].x = 1; } // set direction - m_InputData.m_Direction = 0; - if(m_InputDirectionLeft && !m_InputDirectionRight) - m_InputData.m_Direction = -1; - if(!m_InputDirectionLeft && m_InputDirectionRight) - m_InputData.m_Direction = 1; + m_InputData[g_Config.m_ClDummy].m_Direction = 0; + if(m_InputDirectionLeft[g_Config.m_ClDummy] && !m_InputDirectionRight[g_Config.m_ClDummy]) + m_InputData[g_Config.m_ClDummy].m_Direction = -1; + if(!m_InputDirectionLeft[g_Config.m_ClDummy] && m_InputDirectionRight[g_Config.m_ClDummy]) + m_InputData[g_Config.m_ClDummy].m_Direction = 1; // stress testing if(g_Config.m_DbgStress) { float t = Client()->LocalTime(); - mem_zero(&m_InputData, sizeof(m_InputData)); - - m_InputData.m_Direction = ((int)t/2)&1; - m_InputData.m_Jump = ((int)t); - m_InputData.m_Fire = ((int)(t*10)); - m_InputData.m_Hook = ((int)(t*2))&1; - m_InputData.m_WantedWeapon = ((int)t)%NUM_WEAPONS; - m_InputData.m_TargetX = (int)(sinf(t*3)*100.0f); - m_InputData.m_TargetY = (int)(cosf(t*3)*100.0f); + mem_zero(&m_InputData[g_Config.m_ClDummy], sizeof(m_InputData[0])); + + m_InputData[g_Config.m_ClDummy].m_Direction = ((int)t/2)&1; + m_InputData[g_Config.m_ClDummy].m_Jump = ((int)t); + m_InputData[g_Config.m_ClDummy].m_Fire = ((int)(t*10)); + m_InputData[g_Config.m_ClDummy].m_Hook = ((int)(t*2))&1; + m_InputData[g_Config.m_ClDummy].m_WantedWeapon = ((int)t)%NUM_WEAPONS; + m_InputData[g_Config.m_ClDummy].m_TargetX = (int)(sinf(t*3)*100.0f); + m_InputData[g_Config.m_ClDummy].m_TargetY = (int)(cosf(t*3)*100.0f); } // check if we need to send input - if(m_InputData.m_Direction != m_LastData.m_Direction) Send = true; - else if(m_InputData.m_Jump != m_LastData.m_Jump) Send = true; - else if(m_InputData.m_Fire != m_LastData.m_Fire) Send = true; - else if(m_InputData.m_Hook != m_LastData.m_Hook) Send = true; - else if(m_InputData.m_WantedWeapon != m_LastData.m_WantedWeapon) Send = true; - else if(m_InputData.m_NextWeapon != m_LastData.m_NextWeapon) Send = true; - else if(m_InputData.m_PrevWeapon != m_LastData.m_PrevWeapon) Send = true; + if(m_InputData[g_Config.m_ClDummy].m_Direction != m_LastData[g_Config.m_ClDummy].m_Direction) Send = true; + else if(m_InputData[g_Config.m_ClDummy].m_Jump != m_LastData[g_Config.m_ClDummy].m_Jump) Send = true; + else if(m_InputData[g_Config.m_ClDummy].m_Fire != m_LastData[g_Config.m_ClDummy].m_Fire) Send = true; + else if(m_InputData[g_Config.m_ClDummy].m_Hook != m_LastData[g_Config.m_ClDummy].m_Hook) Send = true; + else if(m_InputData[g_Config.m_ClDummy].m_WantedWeapon != m_LastData[g_Config.m_ClDummy].m_WantedWeapon) Send = true; + else if(m_InputData[g_Config.m_ClDummy].m_NextWeapon != m_LastData[g_Config.m_ClDummy].m_NextWeapon) Send = true; + else if(m_InputData[g_Config.m_ClDummy].m_PrevWeapon != m_LastData[g_Config.m_ClDummy].m_PrevWeapon) Send = true; // send at at least 10hz if(time_get() > LastSendTime + time_freq()/25) Send = true; + + if(m_pClient->m_Snap.m_pLocalCharacter && m_pClient->m_Snap.m_pLocalCharacter->m_Weapon == WEAPON_NINJA + && (m_InputData[g_Config.m_ClDummy].m_Direction || m_InputData[g_Config.m_ClDummy].m_Jump || m_InputData[g_Config.m_ClDummy].m_Hook)) + Send = true; } // copy and return size - m_LastData = m_InputData; + m_LastData[g_Config.m_ClDummy] = m_InputData[g_Config.m_ClDummy]; if(!Send) return 0; LastSendTime = time_get(); - mem_copy(pData, &m_InputData, sizeof(m_InputData)); - return sizeof(m_InputData); + mem_copy(pData, &m_InputData[g_Config.m_ClDummy], sizeof(m_InputData[0])); + return sizeof(m_InputData[0]); } void CControls::OnRender() { + enum { + JOYSTICK_RUN_DISTANCE = 65536 / 8, + GAMEPAD_DEAD_ZONE = 65536 / 8, + }; + + int64 CurTime = time_get(); + bool FireWasPressed = false; + + if( m_Joystick ) + { + // Get input from left joystick + int RunX = SDL_JoystickGetAxis(m_Joystick, LEFT_JOYSTICK_X); + int RunY = SDL_JoystickGetAxis(m_Joystick, LEFT_JOYSTICK_Y); + bool RunPressed = (RunX != 0 || RunY != 0); + // Get input from right joystick + int AimX = SDL_JoystickGetAxis(m_Joystick, SECOND_RIGHT_JOYSTICK_X); + int AimY = SDL_JoystickGetAxis(m_Joystick, SECOND_RIGHT_JOYSTICK_Y); + bool AimPressed = (AimX != 0 || AimY != 0); + // Get input from another right joystick + int HookX = SDL_JoystickGetAxis(m_Joystick, RIGHT_JOYSTICK_X); + int HookY = SDL_JoystickGetAxis(m_Joystick, RIGHT_JOYSTICK_Y); + bool HookPressed = (HookX != 0 || HookY != 0); + + if( m_JoystickRunPressed != RunPressed ) + { + if( RunPressed ) + { + if( m_JoystickTapTime + time_freq() > CurTime ) // Tap in less than 1 second to jump + m_InputData[g_Config.m_ClDummy].m_Jump = 1; + } + else + m_InputData[g_Config.m_ClDummy].m_Jump = 0; + m_JoystickTapTime = CurTime; + } + + m_JoystickRunPressed = RunPressed; + + if( RunPressed ) + { + m_InputDirectionLeft[g_Config.m_ClDummy] = (RunX < -JOYSTICK_RUN_DISTANCE); + m_InputDirectionRight[g_Config.m_ClDummy] = (RunX > JOYSTICK_RUN_DISTANCE); + } + + // Move 500ms in the same direction, to prevent speed bump when tapping + if( !RunPressed && m_JoystickTapTime + time_freq() / 2 > CurTime ) + { + m_InputDirectionLeft[g_Config.m_ClDummy] = 0; + m_InputDirectionRight[g_Config.m_ClDummy] = 0; + } + + //dbg_msg("dbg", "RunPressed %d m_JoystickSwipeJumpClear %lld m_JoystickSwipeJumpY %d RunY %d cond %d", + // RunPressed, m_JoystickSwipeJumpClear, (int)m_JoystickSwipeJumpY, RunY, + // (int)((!m_JoystickSwipeJumpY && RunY > SWIPE_JUMP_THRESHOLD) || (m_JoystickSwipeJumpY && RunY < -SWIPE_JUMP_THRESHOLD))); + + if( HookPressed ) + { + m_MousePos[g_Config.m_ClDummy] = vec2(HookX / 30, HookY / 30); + ClampMousePos(); + m_InputData[g_Config.m_ClDummy].m_Hook = 1; + } + else + { + m_InputData[g_Config.m_ClDummy].m_Hook = 0; + } + + if( AimPressed ) + { + m_MousePos[g_Config.m_ClDummy] = vec2(AimX / 30, AimY / 30); + ClampMousePos(); + } + + if( AimPressed != m_JoystickFirePressed ) + { + // Fire when releasing joystick + if( !AimPressed ) + { + m_InputData[g_Config.m_ClDummy].m_Fire ++; + if( (bool)(m_InputData[g_Config.m_ClDummy].m_Fire % 2) != AimPressed ) + m_InputData[g_Config.m_ClDummy].m_Fire ++; + FireWasPressed = true; + } + } + + m_JoystickFirePressed = AimPressed; + } + + if( m_Gamepad ) + { + // Get input from left joystick + int RunX = SDL_JoystickGetAxis(m_Gamepad, LEFT_JOYSTICK_X); + int RunY = SDL_JoystickGetAxis(m_Gamepad, LEFT_JOYSTICK_Y); + if( m_UsingGamepad ) + { + m_InputDirectionLeft[g_Config.m_ClDummy] = (RunX < -GAMEPAD_DEAD_ZONE); + m_InputDirectionRight[g_Config.m_ClDummy] = (RunX > GAMEPAD_DEAD_ZONE); + } + + // Get input from right joystick + int AimX = SDL_JoystickGetAxis(m_Gamepad, RIGHT_JOYSTICK_X); + int AimY = SDL_JoystickGetAxis(m_Gamepad, RIGHT_JOYSTICK_Y); + if( abs(AimX) > GAMEPAD_DEAD_ZONE || abs(AimY) > GAMEPAD_DEAD_ZONE ) + { + m_MousePos[g_Config.m_ClDummy] = vec2(AimX / 30, AimY / 30); + ClampMousePos(); + } + + if( !m_UsingGamepad && (abs(AimX) > GAMEPAD_DEAD_ZONE || abs(AimY) > GAMEPAD_DEAD_ZONE || abs(RunX) > GAMEPAD_DEAD_ZONE || abs(RunY) > GAMEPAD_DEAD_ZONE) ) + { + UI()->AndroidShowScreenKeys(false); + m_UsingGamepad = true; + } + } + + if( g_Config.m_ClAutoswitchWeaponsOutOfAmmo && m_pClient->m_Snap.m_pLocalCharacter ) + { + // Keep track of ammo count, we know weapon ammo only when we switch to that weapon, this is tracked on server and protocol does not track that + m_AmmoCount[m_pClient->m_Snap.m_pLocalCharacter->m_Weapon%NUM_WEAPONS] = m_pClient->m_Snap.m_pLocalCharacter->m_AmmoCount; + // Autoswitch weapon if we're out of ammo + if( (m_InputData[g_Config.m_ClDummy].m_Fire % 2 != 0 || FireWasPressed) && + m_pClient->m_Snap.m_pLocalCharacter->m_AmmoCount == 0 && + m_pClient->m_Snap.m_pLocalCharacter->m_Weapon != WEAPON_HAMMER && + m_pClient->m_Snap.m_pLocalCharacter->m_Weapon != WEAPON_NINJA ) + { + int w; + for( w = WEAPON_RIFLE; w > WEAPON_GUN; w-- ) + { + if( w == m_pClient->m_Snap.m_pLocalCharacter->m_Weapon ) + continue; + if( m_AmmoCount[w] > 0 ) + break; + } + if( w != m_pClient->m_Snap.m_pLocalCharacter->m_Weapon ) + m_InputData[g_Config.m_ClDummy].m_WantedWeapon = w+1; + } + } + // update target pos if(m_pClient->m_Snap.m_pGameInfoObj && !m_pClient->m_Snap.m_SpecInfo.m_Active) - m_TargetPos = m_pClient->m_LocalCharacterPos + m_MousePos; + m_TargetPos[g_Config.m_ClDummy] = m_pClient->m_LocalCharacterPos + m_MousePos[g_Config.m_ClDummy]; else if(m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_Snap.m_SpecInfo.m_UsePosition) - m_TargetPos = m_pClient->m_Snap.m_SpecInfo.m_Position + m_MousePos; + m_TargetPos[g_Config.m_ClDummy] = m_pClient->m_Snap.m_SpecInfo.m_Position + m_MousePos[g_Config.m_ClDummy]; else - m_TargetPos = m_MousePos; + m_TargetPos[g_Config.m_ClDummy] = m_MousePos[g_Config.m_ClDummy]; } bool CControls::OnMouseMove(float x, float y) @@ -216,8 +441,19 @@ bool CControls::OnMouseMove(float x, float y) (m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_pChat->IsActive())) return false; - m_MousePos += vec2(x, y); // TODO: ugly +#if defined(__ANDROID__) // No relative mouse on Android + // We're using joystick on Android, mouse is disabled + if( m_OldMouseX != x || m_OldMouseY != y ) + { + m_OldMouseX = x; + m_OldMouseY = y; + m_MousePos[g_Config.m_ClDummy] = vec2((x - g_Config.m_GfxScreenWidth/2), (y - g_Config.m_GfxScreenHeight/2)); + ClampMousePos(); + } +#else + m_MousePos[g_Config.m_ClDummy] += vec2(x, y); // TODO: ugly ClampMousePos(); +#endif return true; } @@ -226,9 +462,8 @@ void CControls::ClampMousePos() { if(m_pClient->m_Snap.m_SpecInfo.m_Active && !m_pClient->m_Snap.m_SpecInfo.m_UsePosition) { - m_MousePos.x = clamp(m_MousePos.x, 200.0f, Collision()->GetWidth()*32-200.0f); - m_MousePos.y = clamp(m_MousePos.y, 200.0f, Collision()->GetHeight()*32-200.0f); - + m_MousePos[g_Config.m_ClDummy].x = clamp(m_MousePos[g_Config.m_ClDummy].x, 200.0f, Collision()->GetWidth()*32-200.0f); + m_MousePos[g_Config.m_ClDummy].y = clamp(m_MousePos[g_Config.m_ClDummy].y, 200.0f, Collision()->GetHeight()*32-200.0f); } else { @@ -236,7 +471,7 @@ void CControls::ClampMousePos() float FollowFactor = g_Config.m_ClMouseFollowfactor/100.0f; float MouseMax = min(CameraMaxDistance/FollowFactor + g_Config.m_ClMouseDeadzone, (float)g_Config.m_ClMouseMaxDistance); - if(length(m_MousePos) > MouseMax) - m_MousePos = normalize(m_MousePos)*MouseMax; + if(length(m_MousePos[g_Config.m_ClDummy]) > MouseMax) + m_MousePos[g_Config.m_ClDummy] = normalize(m_MousePos[g_Config.m_ClDummy])*MouseMax; } } diff --git a/src/game/client/components/controls.h b/src/game/client/components/controls.h index aefc850ca8..c8e08c2667 100644 --- a/src/game/client/components/controls.h +++ b/src/game/client/components/controls.h @@ -2,19 +2,35 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #ifndef GAME_CLIENT_COMPONENTS_CONTROLS_H #define GAME_CLIENT_COMPONENTS_CONTROLS_H +#include #include +#include #include class CControls : public CComponent { public: - vec2 m_MousePos; - vec2 m_TargetPos; + vec2 m_MousePos[2]; + vec2 m_TargetPos[2]; + float m_OldMouseX; + float m_OldMouseY; + SDL_Joystick *m_Joystick; + bool m_JoystickFirePressed; + bool m_JoystickRunPressed; + int64 m_JoystickTapTime; - CNetObj_PlayerInput m_InputData; - CNetObj_PlayerInput m_LastData; - int m_InputDirectionLeft; - int m_InputDirectionRight; + SDL_Joystick *m_Gamepad; + bool m_UsingGamepad; + + int m_AmmoCount[NUM_WEAPONS]; + + CNetObj_PlayerInput m_InputData[2]; + CNetObj_PlayerInput m_LastData[2]; + int m_InputDirectionLeft[2]; + int m_InputDirectionRight[2]; + int m_ShowHookColl[2]; + int m_LastDummy; + int m_OtherFire; CControls(); @@ -28,5 +44,6 @@ class CControls : public CComponent int SnapInput(int *pData); void ClampMousePos(); + void ResetDummyInput(); }; #endif diff --git a/src/game/client/components/debughud.cpp b/src/game/client/components/debughud.cpp index 6adc61b200..45849ec686 100644 --- a/src/game/client/components/debughud.cpp +++ b/src/game/client/components/debughud.cpp @@ -29,7 +29,7 @@ void CDebugHud::RenderNetCorrections() vec2(netobjects.local_character->x, netobjects.local_character->y));*/ float Velspeed = length(vec2(m_pClient->m_Snap.m_pLocalCharacter->m_VelX/256.0f, m_pClient->m_Snap.m_pLocalCharacter->m_VelY/256.0f))*50; - float Ramp = VelocityRamp(Velspeed, m_pClient->m_Tuning.m_VelrampStart, m_pClient->m_Tuning.m_VelrampRange, m_pClient->m_Tuning.m_VelrampCurvature); + float Ramp = VelocityRamp(Velspeed, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampStart, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampRange, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampCurvature); const char *paStrings[] = {"velspeed:", "velspeed*ramp:", "ramp:", "Pos", " x:", " y:", "netobj corrections", " num:", " on:"}; const int Num = sizeof(paStrings)/sizeof(char *); @@ -80,13 +80,13 @@ void CDebugHud::RenderTuning() Graphics()->MapScreen(0, 0, 300*Graphics()->ScreenAspect(), 300); - float y = 50.0f; + float y = 27.0f; int Count = 0; - for(int i = 0; i < m_pClient->m_Tuning.Num(); i++) + for(int i = 0; i < m_pClient->m_Tuning[g_Config.m_ClDummy].Num(); i++) { char aBuf[128]; float Current, Standard; - m_pClient->m_Tuning.Get(i, &Current); + m_pClient->m_Tuning[g_Config.m_ClDummy].Get(i, &Current); StandardTuning.Get(i, &Standard); if(Standard == Current) @@ -108,7 +108,7 @@ void CDebugHud::RenderTuning() TextRender()->Text(0x0, x-w, y+Count*6, 5, aBuf, -1); x += 5.0f; - TextRender()->Text(0x0, x, y+Count*6, 5, m_pClient->m_Tuning.m_apNames[i], -1); + TextRender()->Text(0x0, x, y+Count*6, 5, m_pClient->m_Tuning[g_Config.m_ClDummy].m_apNames[i], -1); Count++; } @@ -124,7 +124,7 @@ void CDebugHud::RenderTuning() for(int i = 0; i < 100; i++) { float Speed = i/100.0f * 3000; - float Ramp = VelocityRamp(Speed, m_pClient->m_Tuning.m_VelrampStart, m_pClient->m_Tuning.m_VelrampRange, m_pClient->m_Tuning.m_VelrampCurvature); + float Ramp = VelocityRamp(Speed, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampStart, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampRange, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampCurvature); float RampedSpeed = (Speed * Ramp)/1000.0f; Array[i] = IGraphics::CLineItem((i-1)*2, y+Height-pv*Height, i*2, y+Height-RampedSpeed*Height); //Graphics()->LinesDraw((i-1)*2, 200, i*2, 200); diff --git a/src/game/client/components/emoticon.cpp b/src/game/client/components/emoticon.cpp index 9e2a80cf39..9c9a975104 100644 --- a/src/game/client/components/emoticon.cpp +++ b/src/game/client/components/emoticon.cpp @@ -54,8 +54,12 @@ bool CEmoticon::OnMouseMove(float x, float y) if(!m_Active) return false; +#if defined(__ANDROID__) // No relative mouse on Android + m_SelectorMouse = vec2(x,y); +#else UI()->ConvertMouseMove(&x, &y); m_SelectorMouse += vec2(x,y); +#endif return true; } diff --git a/src/game/client/components/ghost.cpp b/src/game/client/components/ghost.cpp index 75bcd363c2..7060c41bc4 100644 --- a/src/game/client/components/ghost.cpp +++ b/src/game/client/components/ghost.cpp @@ -274,7 +274,8 @@ void CGhost::StartRecord() m_Recording = true; m_CurGhost.m_Path.clear(); CNetObj_ClientInfo *pInfo = (CNetObj_ClientInfo *) Client()->SnapFindItem(IClient::SNAP_CURRENT, NETOBJTYPE_CLIENTINFO, m_pClient->m_Snap.m_LocalClientID); - m_CurGhost.m_Info = *pInfo; + if (pInfo) + m_CurGhost.m_Info = *pInfo; } void CGhost::StopRecord() diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 7441be2abe..2595e6bec0 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -1,6 +1,7 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include +#include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include #include "controls.h" #include "camera.h" @@ -32,9 +34,9 @@ void CHud::OnReset() m_CheckpointTick = 0; m_DDRaceTick = 0; m_FinishTime = false; + m_DDRaceTimeReceived = false; m_ServerRecord = -1.0f; m_PlayerRecord = -1.0f; - m_DDRaceTimeReceived = false; } void CHud::RenderGameTimer() @@ -55,8 +57,15 @@ void CHud::RenderGameTimer() else Time = (Client()->GameTick()-m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick)/Client()->GameTickSpeed(); + CServerInfo Info; + Client()->GetServerInfo(&Info); + bool IsGameTypeRace = str_find_nocase(Info.m_aGameType, "race") || str_find_nocase(Info.m_aGameType, "fastcap"); + bool IsGameTypeDDRace = str_find_nocase(Info.m_aGameType, "ddrace") || str_find_nocase(Info.m_aGameType, "mkrace"); + if(Time <= 0) str_format(Buf, sizeof(Buf), "00:00.0"); + else if(IsGameTypeRace && !IsGameTypeDDRace && m_ServerRecord >= 0) + str_format(Buf, sizeof(Buf), "%02d:%02d", (int)(m_ServerRecord*100)/60, ((int)(m_ServerRecord*100)%60)); else str_format(Buf, sizeof(Buf), "%02d:%02d.%d", Time/60, Time%60, m_DDRaceTick/10); float FontSize = 10.0f; @@ -200,7 +209,20 @@ void CHud::RenderScoreHud() for(int t = 0; t < 2; ++t) { if(apPlayerInfo[t]) - str_format(aScore[t], sizeof(aScore)/2, "%d", apPlayerInfo[t]->m_Score); + { + CServerInfo Info; + Client()->GetServerInfo(&Info); + bool IsGameTypeRace = str_find_nocase(Info.m_aGameType, "race") || str_find_nocase(Info.m_aGameType, "fastcap"); + if(IsGameTypeRace && g_Config.m_ClDDRaceScoreBoard) + { + if (apPlayerInfo[t]->m_Score != -9999) + str_format(aScore[t], sizeof(aScore[t]), "%02d:%02d", abs(apPlayerInfo[t]->m_Score)/60, abs(apPlayerInfo[t]->m_Score)%60); + else + aScore[t][0] = 0; + } + else + str_format(aScore[t], sizeof(aScore)/2, "%d", apPlayerInfo[t]->m_Score); + } else aScore[t][0] = 0; } @@ -228,15 +250,18 @@ void CHud::RenderScoreHud() { // draw name int ID = apPlayerInfo[t]->m_ClientID; - const char *pName = m_pClient->m_aClients[ID].m_aName; - float w = TextRender()->TextWidth(0, 8.0f, pName, -1); - TextRender()->Text(0, min(Whole-w-1.0f, Whole-ScoreWidthMax-ImageSize-2*Split-PosSize), StartY+(t+1)*20.0f-3.0f, 8.0f, pName, -1); - - // draw tee - CTeeRenderInfo Info = m_pClient->m_aClients[ID].m_RenderInfo; - Info.m_Size = 18.0f; - RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, EMOTE_NORMAL, vec2(1,0), - vec2(Whole-ScoreWidthMax-Info.m_Size/2-Split, StartY+1.0f+Info.m_Size/2+t*20)); + if(ID >= 0 && ID < MAX_CLIENTS) + { + const char *pName = m_pClient->m_aClients[ID].m_aName; + float w = TextRender()->TextWidth(0, 8.0f, pName, -1); + TextRender()->Text(0, min(Whole-w-1.0f, Whole-ScoreWidthMax-ImageSize-2*Split-PosSize), StartY+(t+1)*20.0f-3.0f, 8.0f, pName, -1); + + // draw tee + CTeeRenderInfo Info = m_pClient->m_aClients[ID].m_RenderInfo; + Info.m_Size = 18.0f; + RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, EMOTE_NORMAL, vec2(1,0), + vec2(Whole-ScoreWidthMax-Info.m_Size/2-Split, StartY+1.0f+Info.m_Size/2+t*20)); + } } // draw position @@ -324,13 +349,24 @@ void CHud::RenderTeambalanceWarning() void CHud::RenderVoting() { - if(!m_pClient->m_pVoting->IsVoting() || Client()->State() == IClient::STATE_DEMOPLAYBACK) + if((!g_Config.m_ClShowVotesAfterVoting && !m_pClient->m_pScoreboard->Active() && m_pClient->m_pVoting->TakenChoice()) || !m_pClient->m_pVoting->IsVoting() || Client()->State() == IClient::STATE_DEMOPLAYBACK) return; Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.40f); + +#if defined(__ANDROID__) + static const float TextX = 265; + static const float TextY = 1; + static const float TextW = 200; + static const float TextH = 42; + RenderTools()->DrawRoundRect(TextX-5, TextY, TextW+15, TextH, 5.0f); + RenderTools()->DrawRoundRect(TextX-5, TextY+TextH+2, TextW/2-10, 20, 5.0f); + RenderTools()->DrawRoundRect(TextX+TextW/2+20, TextY+TextH+2, TextW/2-10, 20, 5.0f); +#else RenderTools()->DrawRoundRect(-10, 60-2, 100+10+4+5, 46, 5.0f); +#endif Graphics()->QuadsEnd(); TextRender()->TextColor(1,1,1,1); @@ -338,24 +374,60 @@ void CHud::RenderVoting() CTextCursor Cursor; char aBuf[512]; str_format(aBuf, sizeof(aBuf), Localize("%ds left"), m_pClient->m_pVoting->SecondsLeft()); +#if defined(__ANDROID__) + float tw = TextRender()->TextWidth(0x0, 10, aBuf, -1); + TextRender()->SetCursor(&Cursor, TextX+TextW-tw, 0.0f, 10.0f, TEXTFLAG_RENDER); +#else float tw = TextRender()->TextWidth(0x0, 6, aBuf, -1); TextRender()->SetCursor(&Cursor, 5.0f+100.0f-tw, 60.0f, 6.0f, TEXTFLAG_RENDER); +#endif TextRender()->TextEx(&Cursor, aBuf, -1); +#if defined(__ANDROID__) + TextRender()->SetCursor(&Cursor, TextX, 0.0f, 10.0f, TEXTFLAG_RENDER); + Cursor.m_LineWidth = TextW-tw; +#else TextRender()->SetCursor(&Cursor, 5.0f, 60.0f, 6.0f, TEXTFLAG_RENDER); Cursor.m_LineWidth = 100.0f-tw; +#endif Cursor.m_MaxLines = 3; TextRender()->TextEx(&Cursor, m_pClient->m_pVoting->VoteDescription(), -1); // reason str_format(aBuf, sizeof(aBuf), "%s %s", Localize("Reason:"), m_pClient->m_pVoting->VoteReason()); +#if defined(__ANDROID__) + TextRender()->SetCursor(&Cursor, TextX, 23.0f, 10.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); +#else TextRender()->SetCursor(&Cursor, 5.0f, 79.0f, 6.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); +#endif Cursor.m_LineWidth = 100.0f; TextRender()->TextEx(&Cursor, aBuf, -1); +#if defined(__ANDROID__) + CUIRect Base = {TextX, TextH - 8, TextW, 4}; +#else CUIRect Base = {5, 88, 100, 4}; +#endif m_pClient->m_pVoting->RenderBars(Base, false); +#if defined(__ANDROID__) + Base.y += Base.h+6; + UI()->DoLabel(&Base, Localize("Vote yes"), 16.0f, -1); + UI()->DoLabel(&Base, Localize("Vote no"), 16.0f, 1); + if( Input()->KeyDown(KEY_MOUSE_1) ) + { + float mx, my; + Input()->MouseRelative(&mx, &my); + mx *= m_Width / Graphics()->ScreenWidth(); + my *= m_Height / Graphics()->ScreenHeight(); + if( my > TextY+TextH-40 && my < TextY+TextH+20 ) { + if( mx > TextX-5 && mx < TextX-5+TextW/2-10 ) + m_pClient->m_pVoting->Vote(1); + if( mx > TextX+TextW/2+20 && mx < TextX+TextW/2+20+TextW/2-10 ) + m_pClient->m_pVoting->Vote(-1); + } + } +#else const char *pYesKey = m_pClient->m_pBinds->GetKey("vote yes"); const char *pNoKey = m_pClient->m_pBinds->GetKey("vote no"); str_format(aBuf, sizeof(aBuf), "%s - %s", pYesKey, Localize("Vote yes")); @@ -364,6 +436,7 @@ void CHud::RenderVoting() str_format(aBuf, sizeof(aBuf), "%s - %s", Localize("Vote no"), pNoKey); UI()->DoLabel(&Base, aBuf, 6.0f, 1); +#endif } void CHud::RenderCursor() @@ -378,7 +451,7 @@ void CHud::RenderCursor() // render cursor RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[m_pClient->m_Snap.m_pLocalCharacter->m_Weapon%NUM_WEAPONS].m_pSpriteCursor); float CursorSize = 64; - RenderTools()->DrawSprite(m_pClient->m_pControls->m_TargetPos.x, m_pClient->m_pControls->m_TargetPos.y, CursorSize); + RenderTools()->DrawSprite(m_pClient->m_pControls->m_TargetPos[g_Config.m_ClDummy].x, m_pClient->m_pControls->m_TargetPos[g_Config.m_ClDummy].y, CursorSize); Graphics()->QuadsEnd(); } @@ -467,12 +540,13 @@ void CHud::OnRender() { if(m_pClient->m_Snap.m_pLocalCharacter && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) { - RenderHealthAndAmmo(m_pClient->m_Snap.m_pLocalCharacter); + if (g_Config.m_ClShowhudHealthAmmo) + RenderHealthAndAmmo(m_pClient->m_Snap.m_pLocalCharacter); RenderDDRaceEffects(); } else if(m_pClient->m_Snap.m_SpecInfo.m_Active) { - if(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW) + if(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW && g_Config.m_ClShowhudHealthAmmo) RenderHealthAndAmmo(&m_pClient->m_Snap.m_aCharacters[m_pClient->m_Snap.m_SpecInfo.m_SpectatorID].m_Cur); RenderSpectatorHud(); } @@ -480,14 +554,16 @@ void CHud::OnRender() RenderGameTimer(); RenderPauseNotification(); RenderSuddenDeath(); - RenderScoreHud(); + if (g_Config.m_ClShowhudScore) + RenderScoreHud(); RenderWarmupTimer(); RenderFps(); if(Client()->State() != IClient::STATE_DEMOPLAYBACK) RenderConnectionWarning(); RenderTeambalanceWarning(); RenderVoting(); - RenderRecord(); + if (g_Config.m_ClShowRecord) + RenderRecord(); } RenderCursor(); } diff --git a/src/game/client/components/hud.h b/src/game/client/components/hud.h index b00da08659..73eee3db52 100644 --- a/src/game/client/components/hud.h +++ b/src/game/client/components/hud.h @@ -36,16 +36,16 @@ class CHud : public CComponent private: - void RenderDDRaceEffects(); void RenderRecord(); + void RenderDDRaceEffects(); float m_CheckpointDiff; + float m_ServerRecord; + float m_PlayerRecord; int m_DDRaceTime; int m_LastReceivedTimeTick; int m_CheckpointTick; int m_DDRaceTick; bool m_FinishTime; - float m_ServerRecord; - float m_PlayerRecord; bool m_DDRaceTimeReceived; }; diff --git a/src/game/client/components/items.cpp b/src/game/client/components/items.cpp index e1032a51b8..5389122c17 100644 --- a/src/game/client/components/items.cpp +++ b/src/game/client/components/items.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include // get_angle #include @@ -14,7 +15,7 @@ #include #include "items.h" - +#include void CItems::OnReset() { m_NumExtraProjectiles = 0; @@ -27,18 +28,18 @@ void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID) float Speed = 0; if(pCurrent->m_Type == WEAPON_GRENADE) { - Curvature = m_pClient->m_Tuning.m_GrenadeCurvature; - Speed = m_pClient->m_Tuning.m_GrenadeSpeed; + Curvature = m_pClient->m_Tuning[g_Config.m_ClDummy].m_GrenadeCurvature; + Speed = m_pClient->m_Tuning[g_Config.m_ClDummy].m_GrenadeSpeed; } else if(pCurrent->m_Type == WEAPON_SHOTGUN) { - Curvature = m_pClient->m_Tuning.m_ShotgunCurvature; - Speed = m_pClient->m_Tuning.m_ShotgunSpeed; + Curvature = m_pClient->m_Tuning[g_Config.m_ClDummy].m_ShotgunCurvature; + Speed = m_pClient->m_Tuning[g_Config.m_ClDummy].m_ShotgunSpeed; } else if(pCurrent->m_Type == WEAPON_GUN) { - Curvature = m_pClient->m_Tuning.m_GunCurvature; - Speed = m_pClient->m_Tuning.m_GunSpeed; + Curvature = m_pClient->m_Tuning[g_Config.m_ClDummy].m_GunCurvature; + Speed = m_pClient->m_Tuning[g_Config.m_ClDummy].m_GunSpeed; } static float s_LastGameTickTime = Client()->GameTickTime(); @@ -96,7 +97,32 @@ void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID) } IGraphics::CQuadItem QuadItem(Pos.x, Pos.y, 32, 32); - Graphics()->QuadsDraw(&QuadItem, 1); + + bool LocalPlayerInGame = false; + + if(m_pClient->m_Snap.m_pLocalInfo) + LocalPlayerInGame = m_pClient->m_aClients[m_pClient->m_Snap.m_pLocalInfo->m_ClientID].m_Team != -1; + + if (g_Config.m_ClAntiPingGrenade && LocalPlayerInGame && !(Client()->State() == IClient::STATE_DEMOPLAYBACK)) + { + // Draw shadows of grenades + static int Offset = 0; + Offset = (int)(0.8f * (float)Offset + 0.2f * (float)(Client()->PredGameTick() - Client()->GameTick())); + + int PredictedTick = Client()->PrevGameTick() + Offset; + float PredictedCt = (PredictedTick - pCurrent->m_StartTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime(); + + int shadow_type = pCurrent->m_Type; + RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[clamp(shadow_type, 0, NUM_WEAPONS-1)].m_pSpriteProj); + + vec2 PredictedPos = CalcPos(StartPos, StartVel, Curvature, Speed, PredictedCt); + + IGraphics::CQuadItem QuadItem(PredictedPos.x, PredictedPos.y, 32, 32); + Graphics()->QuadsDraw(&QuadItem, 1); + } + else + Graphics()->QuadsDraw(&QuadItem, 1); + Graphics()->QuadsSetRotation(0); Graphics()->QuadsEnd(); } @@ -196,13 +222,14 @@ void CItems::RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent, void CItems::RenderLaser(const struct CNetObj_Laser *pCurrent) { + vec3 RGB; vec2 Pos = vec2(pCurrent->m_X, pCurrent->m_Y); vec2 From = vec2(pCurrent->m_FromX, pCurrent->m_FromY); vec2 Dir = normalize(Pos-From); float Ticks = Client()->GameTick() + Client()->IntraGameTick() - pCurrent->m_StartTick; float Ms = (Ticks/50.0f) * 1000.0f; - float a = Ms / m_pClient->m_Tuning.m_LaserBounceDelay; + float a = Ms / m_pClient->m_Tuning[g_Config.m_ClDummy].m_LaserBounceDelay; a = clamp(a, 0.0f, 1.0f); float Ia = 1-a; @@ -216,7 +243,8 @@ void CItems::RenderLaser(const struct CNetObj_Laser *pCurrent) //vec4 outer_color(0.65f,0.85f,1.0f,1.0f); // do outline - vec4 OuterColor(0.075f, 0.075f, 0.25f, 1.0f); + RGB = HslToRgb(vec3(g_Config.m_ClLaserOutlineHue / 255.0f, g_Config.m_ClLaserOutlineSat / 255.0f, g_Config.m_ClLaserOutlineLht / 255.0f)); + vec4 OuterColor(RGB.r, RGB.g, RGB.b, 1.0f); Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, 1.0f); Out = vec2(Dir.y, -Dir.x) * (7.0f*Ia); @@ -228,7 +256,8 @@ void CItems::RenderLaser(const struct CNetObj_Laser *pCurrent) Graphics()->QuadsDrawFreeform(&Freeform, 1); // do inner - vec4 InnerColor(0.5f, 0.5f, 1.0f, 1.0f); + RGB = HslToRgb(vec3(g_Config.m_ClLaserInnerHue / 255.0f, g_Config.m_ClLaserInnerSat / 255.0f, g_Config.m_ClLaserInnerLht / 255.0f)); + vec4 InnerColor(RGB.r, RGB.g, RGB.b, 1.0f); Out = vec2(Dir.y, -Dir.x) * (5.0f*Ia); Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f); // center diff --git a/src/game/client/components/killmessages.cpp b/src/game/client/components/killmessages.cpp index 03f6380a9c..d62497307e 100644 --- a/src/game/client/components/killmessages.cpp +++ b/src/game/client/components/killmessages.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,9 @@ void CKillMessages::OnMessage(int MsgType, void *pRawMsg) void CKillMessages::OnRender() { + if (!g_Config.m_ClShowKillMessages) + return; + float Width = 400*3.0f*Graphics()->ScreenAspect(); float Height = 400*3.0f; diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index 812a2e6d29..89c9481a03 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -168,6 +168,7 @@ void CMapLayers::OnRender() bool IsSwitchLayer = false; bool IsTeleLayer = false; bool IsSpeedupLayer = false; + bool IsTuneLayer = false; if(pLayer == (CMapItemLayer*)m_pLayers->GameLayer()) { @@ -187,6 +188,9 @@ void CMapLayers::OnRender() if(pLayer == (CMapItemLayer*)m_pLayers->SpeedupLayer()) IsSpeedupLayer = true; + if(pLayer == (CMapItemLayer*)m_pLayers->TuneLayer()) + IsTuneLayer = true; + // skip rendering if detail layers if not wanted if(pLayer->m_Flags&LAYERFLAG_DETAIL && !g_Config.m_GfxHighDetail && !IsGameLayer) continue; @@ -226,7 +230,7 @@ void CMapLayers::OnRender() } } - if((Render && !IsGameLayer && (!g_Config.m_ClShowEntities || !g_Config.m_ClDDRaceCheats)) || ((g_Config.m_ClShowEntities && g_Config.m_ClDDRaceCheats) && IsGameLayer)) + if((Render && g_Config.m_ClOverlayEntities < 100 && !IsGameLayer && !IsFrontLayer && !IsSwitchLayer && !IsTeleLayer && !IsSpeedupLayer) || (g_Config.m_ClOverlayEntities && IsGameLayer)) { //layershot_begin(); @@ -235,7 +239,7 @@ void CMapLayers::OnRender() CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer; if(pTMap->m_Image == -1) { - if(!g_Config.m_ClShowEntities) + if(!IsGameLayer) Graphics()->TextureSet(-1); else Graphics()->TextureSet(m_pClient->m_pMapimages->GetEntities()); @@ -244,13 +248,22 @@ void CMapLayers::OnRender() Graphics()->TextureSet(m_pClient->m_pMapimages->Get(pTMap->m_Image)); CTile *pTiles = (CTile *)m_pLayers->Map()->GetData(pTMap->m_Data); - Graphics()->BlendNone(); - vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); - RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE, - EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset); - Graphics()->BlendNormal(); - RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT, - EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset); + unsigned int Size = m_pLayers->Map()->GetUncompressedDataSize(pTMap->m_Data); + + if (Size >= pTMap->m_Width*pTMap->m_Height*sizeof(CTile)) + { + Graphics()->BlendNone(); + vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); + if(IsGameLayer && g_Config.m_ClOverlayEntities) + Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f*g_Config.m_ClOverlayEntities/100.0f); + if(!IsGameLayer && g_Config.m_ClOverlayEntities) + Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f*(100-g_Config.m_ClOverlayEntities)/100.0f); + RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE, + EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset); + Graphics()->BlendNormal(); + RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT, + EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset); + } } else if(pLayer->m_Type == LAYERTYPE_QUADS) { @@ -270,58 +283,106 @@ void CMapLayers::OnRender() //layershot_end(); } - else if((g_Config.m_ClShowEntities && g_Config.m_ClDDRaceCheats) && IsFrontLayer) + else if(g_Config.m_ClOverlayEntities && IsFrontLayer) { CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer; Graphics()->TextureSet(m_pClient->m_pMapimages->GetEntities()); CTile *pFrontTiles = (CTile *)m_pLayers->Map()->GetData(pTMap->m_Front); - Graphics()->BlendNone(); - vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); - RenderTools()->RenderTilemap(pFrontTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE, - EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset); - Graphics()->BlendNormal(); - RenderTools()->RenderTilemap(pFrontTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT, - EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset); + unsigned int Size = m_pLayers->Map()->GetUncompressedDataSize(pTMap->m_Front); + + if (Size >= pTMap->m_Width*pTMap->m_Height*sizeof(CTile)) + { + Graphics()->BlendNone(); + vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); + if(g_Config.m_ClOverlayEntities) + Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f*g_Config.m_ClOverlayEntities/100.0f); + RenderTools()->RenderTilemap(pFrontTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE, + EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset); + Graphics()->BlendNormal(); + RenderTools()->RenderTilemap(pFrontTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT, + EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset); + } } - else if((g_Config.m_ClShowEntities && g_Config.m_ClDDRaceCheats) && IsSwitchLayer) + else if(g_Config.m_ClOverlayEntities && IsSwitchLayer) { CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer; Graphics()->TextureSet(m_pClient->m_pMapimages->GetEntities()); CSwitchTile *pSwitchTiles = (CSwitchTile *)m_pLayers->Map()->GetData(pTMap->m_Switch); - Graphics()->BlendNone(); - vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); - RenderTools()->RenderSwitchmap(pSwitchTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); - Graphics()->BlendNormal(); - RenderTools()->RenderSwitchmap(pSwitchTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); - RenderTools()->RenderSwitchOverlay(pSwitchTiles, pTMap->m_Width, pTMap->m_Height, 32.0f); + unsigned int Size = m_pLayers->Map()->GetUncompressedDataSize(pTMap->m_Switch); + + if (Size >= pTMap->m_Width*pTMap->m_Height*sizeof(CSwitchTile)) + { + Graphics()->BlendNone(); + vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); + if(g_Config.m_ClOverlayEntities) + Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f*g_Config.m_ClOverlayEntities/100.0f); + RenderTools()->RenderSwitchmap(pSwitchTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); + Graphics()->BlendNormal(); + RenderTools()->RenderSwitchmap(pSwitchTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); + RenderTools()->RenderSwitchOverlay(pSwitchTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, g_Config.m_ClOverlayEntities/100.0f); + } } - else if((g_Config.m_ClShowEntities && g_Config.m_ClDDRaceCheats) && IsTeleLayer) + else if(g_Config.m_ClOverlayEntities && IsTeleLayer) { CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer; Graphics()->TextureSet(m_pClient->m_pMapimages->GetEntities()); CTeleTile *pTeleTiles = (CTeleTile *)m_pLayers->Map()->GetData(pTMap->m_Tele); - Graphics()->BlendNone(); - vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); - RenderTools()->RenderTelemap(pTeleTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); - Graphics()->BlendNormal(); - RenderTools()->RenderTelemap(pTeleTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); - RenderTools()->RenderTeleOverlay(pTeleTiles, pTMap->m_Width, pTMap->m_Height, 32.0f); + unsigned int Size = m_pLayers->Map()->GetUncompressedDataSize(pTMap->m_Tele); + + if (Size >= pTMap->m_Width*pTMap->m_Height*sizeof(CTeleTile)) + { + Graphics()->BlendNone(); + vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); + if(g_Config.m_ClOverlayEntities) + Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f*g_Config.m_ClOverlayEntities/100.0f); + RenderTools()->RenderTelemap(pTeleTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); + Graphics()->BlendNormal(); + RenderTools()->RenderTelemap(pTeleTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); + RenderTools()->RenderTeleOverlay(pTeleTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, g_Config.m_ClOverlayEntities/100.0f); + } } - else if((g_Config.m_ClShowEntities && g_Config.m_ClDDRaceCheats) && IsSpeedupLayer) + else if(g_Config.m_ClOverlayEntities && IsSpeedupLayer) { CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer; Graphics()->TextureSet(m_pClient->m_pMapimages->GetEntities()); CSpeedupTile *pSpeedupTiles = (CSpeedupTile *)m_pLayers->Map()->GetData(pTMap->m_Speedup); - Graphics()->BlendNone(); - vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); - RenderTools()->RenderSpeedupmap(pSpeedupTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); - Graphics()->BlendNormal(); - RenderTools()->RenderSpeedupmap(pSpeedupTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); - RenderTools()->RenderSpeedupOverlay(pSpeedupTiles, pTMap->m_Width, pTMap->m_Height, 32.0f); + unsigned int Size = m_pLayers->Map()->GetUncompressedDataSize(pTMap->m_Speedup); + + if (Size >= pTMap->m_Width*pTMap->m_Height*sizeof(CSpeedupTile)) + { + Graphics()->BlendNone(); + vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); + if(g_Config.m_ClOverlayEntities) + Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f*g_Config.m_ClOverlayEntities/100.0f); + RenderTools()->RenderSpeedupmap(pSpeedupTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); + Graphics()->BlendNormal(); + RenderTools()->RenderSpeedupmap(pSpeedupTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); + RenderTools()->RenderSpeedupOverlay(pSpeedupTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, g_Config.m_ClOverlayEntities/100.0f); + } + } + else if(g_Config.m_ClOverlayEntities && IsTuneLayer) + { + CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer; + Graphics()->TextureSet(m_pClient->m_pMapimages->GetEntities()); + + CTuneTile *pTuneTiles = (CTuneTile *)m_pLayers->Map()->GetData(pTMap->m_Tune); + unsigned int Size = m_pLayers->Map()->GetUncompressedDataSize(pTMap->m_Tune); + + if (Size >= pTMap->m_Width*pTMap->m_Height*sizeof(CTuneTile)) + { + Graphics()->BlendNone(); + vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); + if(g_Config.m_ClOverlayEntities) + Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f*g_Config.m_ClOverlayEntities/100.0f); + RenderTools()->RenderTunemap(pTuneTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); + Graphics()->BlendNormal(); + RenderTools()->RenderTunemap(pTuneTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); + //RenderTools()->RenderTuneOverlay(pTuneTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, g_Config.m_ClOverlayEntities/100.0f); + } } } if(!g_Config.m_GfxNoclip) diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 6991759871..25640e01bd 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -2,6 +2,8 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include +#include +#include #include @@ -18,6 +20,7 @@ #include #include #include +#include #include #include @@ -29,10 +32,12 @@ #include #include #include +#include #include "countryflags.h" #include "menus.h" #include "skins.h" +#include "controls.h" vec4 CMenus::ms_GuiColor; vec4 CMenus::ms_ColorTabbarInactiveOutgame; @@ -42,8 +47,14 @@ vec4 CMenus::ms_ColorTabbarActive = vec4(0,0,0,0.5f); vec4 CMenus::ms_ColorTabbarInactiveIngame; vec4 CMenus::ms_ColorTabbarActiveIngame; +#if defined(__ANDROID__) +float CMenus::ms_ButtonHeight = 50.0f; +float CMenus::ms_ListheaderHeight = 50.0f; +float CMenus::ms_ListitemAdditionalHeight = 33.0f; +#else float CMenus::ms_ButtonHeight = 25.0f; float CMenus::ms_ListheaderHeight = 17.0f; +#endif float CMenus::ms_FontmodHeight = 0.8f; IInput::CEvent CMenus::m_aInputEvents[MAX_INPUTEVENTS]; @@ -59,6 +70,7 @@ CMenus::CMenus() m_NeedRestartGraphics = false; m_NeedRestartSound = false; m_NeedSendinfo = false; + m_NeedSendDummyinfo = false; m_MenuActive = true; m_UseMouseButtons = true; @@ -73,6 +85,7 @@ CMenus::CMenus() m_aCallvoteReason[0] = 0; m_FriendlistSelectedIndex = -1; + m_DoubleClickIndex = -1; m_DDRacePage = PAGE_BROWSER; } @@ -124,7 +137,13 @@ int CMenus::DoButton_Menu(const void *pID, const char *pText, int Checked, const RenderTools()->DrawUIRect(pRect, vec4(1,1,1,0.5f)*ButtonColorMul(pID), CUI::CORNER_ALL, 5.0f); CUIRect Temp; pRect->HMargin(pRect->h>=20.0f?2.0f:1.0f, &Temp); +#if defined(__ANDROID__) + float TextH = min(22.0f, Temp.h); + Temp.y += (Temp.h - TextH) / 2; + UI()->DoLabel(&Temp, pText, TextH*ms_FontmodHeight, 0); +#else UI()->DoLabel(&Temp, pText, Temp.h*ms_FontmodHeight, 0); +#endif return UI()->DoButtonLogic(pID, pText, Checked, pRect); } @@ -144,7 +163,13 @@ int CMenus::DoButton_MenuTab(const void *pID, const char *pText, int Checked, co RenderTools()->DrawUIRect(pRect, ms_ColorTabbarInactive, Corners, 10.0f); CUIRect Temp; pRect->HMargin(2.0f, &Temp); +#if defined(__ANDROID__) + float TextH = min(22.0f, Temp.h); + Temp.y += (Temp.h - TextH) / 2; + UI()->DoLabel(&Temp, pText, TextH*ms_FontmodHeight, 0); +#else UI()->DoLabel(&Temp, pText, Temp.h*ms_FontmodHeight, 0); +#endif return UI()->DoButtonLogic(pID, pText, Checked, pRect); } @@ -156,7 +181,12 @@ int CMenus::DoButton_GridHeader(const void *pID, const char *pText, int Checked, RenderTools()->DrawUIRect(pRect, vec4(1,1,1,0.5f), CUI::CORNER_T, 5.0f); CUIRect t; pRect->VSplitLeft(5.0f, 0, &t); +#if defined(__ANDROID__) + float TextH = min(20.0f, pRect->h); + UI()->DoLabel(&t, pText, TextH*ms_FontmodHeight, -1); +#else UI()->DoLabel(&t, pText, pRect->h*ms_FontmodHeight, -1); +#endif return UI()->DoButtonLogic(pID, pText, Checked, pRect); } @@ -248,7 +278,8 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS for(int i = 0; i < m_NumInputEvents; i++) { Len = str_length(pStr); - ReturnValue |= CLineInput::Manipulate(m_aInputEvents[i], pStr, StrSize, &Len, &s_AtIndex); + int NumChars = Len; + ReturnValue |= CLineInput::Manipulate(m_aInputEvents[i], pStr, StrSize, StrSize, &Len, &s_AtIndex, &NumChars); } } @@ -274,7 +305,16 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS } if(Inside) + { UI()->SetHotItem(pID); +#if defined(__ANDROID__) + if(UI()->ActiveItem() == pID && UI()->MouseButtonClicked(0)) + { + s_AtIndex = 0; + UI()->AndroidBlockAndGetTextInput(pStr, StrSize, ""); + } +#endif + } CUIRect Textbox = *pRect; RenderTools()->DrawUIRect(&Textbox, vec4(1, 1, 1, 0.5f), Corners, 3.0f); @@ -344,7 +384,11 @@ float CMenus::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current) { CUIRect Handle; static float OffsetY; +#if defined(__ANDROID__) + pRect->HSplitTop(50, &Handle, 0); +#else pRect->HSplitTop(33, &Handle, 0); +#endif Handle.y += (pRect->h-Handle.h)*Current; @@ -533,14 +577,14 @@ int CMenus::RenderMenubar(CUIRect r) if(Client()->State() == IClient::STATE_OFFLINE) { // offline menus - if(0) // this is not done yet + Box.VSplitLeft(90.0f, &Button, &Box); + static int s_NewsButton=0; + if (DoButton_MenuTab(&s_NewsButton, Localize("News"), m_ActivePage==PAGE_NEWS, &Button, CUI::CORNER_T)) { - Box.VSplitLeft(90.0f, &Button, &Box); - static int s_NewsButton=0; - if (DoButton_MenuTab(&s_NewsButton, Localize("News"), m_ActivePage==PAGE_NEWS, &Button, 0)) - NewPage = PAGE_NEWS; - Box.VSplitLeft(30.0f, 0, &Box); + NewPage = PAGE_NEWS; + m_DoubleClickIndex = -1; } + Box.VSplitLeft(10.0f, 0, &Box); Box.VSplitLeft(100.0f, &Button, &Box); static int s_InternetButton=0; @@ -548,15 +592,17 @@ int CMenus::RenderMenubar(CUIRect r) { ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); NewPage = PAGE_INTERNET; + m_DoubleClickIndex = -1; } //Box.VSplitLeft(4.0f, 0, &Box); - Box.VSplitLeft(80.0f, &Button, &Box); + Box.VSplitLeft(70.0f, &Button, &Box); static int s_LanButton=0; if(DoButton_MenuTab(&s_LanButton, Localize("LAN"), m_ActivePage==PAGE_LAN, &Button, 0)) { ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN); NewPage = PAGE_LAN; + m_DoubleClickIndex = -1; } //box.VSplitLeft(4.0f, 0, &box); @@ -566,15 +612,17 @@ int CMenus::RenderMenubar(CUIRect r) { ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES); NewPage = PAGE_FAVORITES; + m_DoubleClickIndex = -1; } - Box.VSplitLeft(4.0f*5, 0, &Box); + Box.VSplitLeft(10.0f, 0, &Box); Box.VSplitLeft(100.0f, &Button, &Box); static int s_DemosButton=0; if(DoButton_MenuTab(&s_DemosButton, Localize("Demos"), m_ActivePage==PAGE_DEMOS, &Button, CUI::CORNER_T)) { DemolistPopulate(); NewPage = PAGE_DEMOS; + m_DoubleClickIndex = -1; } } else @@ -597,7 +645,7 @@ int CMenus::RenderMenubar(CUIRect r) Box.VSplitLeft(100.0f, &Button, &Box); static int s_GhostButton=0; - if(DoButton_MenuTab(&s_GhostButton, "DDRace", m_ActivePage==PAGE_DDRace, &Button, CUI::CORNER_TR)) + if(DoButton_MenuTab(&s_GhostButton, "Network", m_ActivePage==PAGE_DDRace, &Button, 0)) NewPage = PAGE_DDRace; Box.VSplitLeft(100.0f, &Button, &Box); @@ -616,17 +664,25 @@ int CMenus::RenderMenubar(CUIRect r) box.VSplitRight(30.0f, &box, 0); */ - Box.VSplitRight(90.0f, &Box, &Button); + Box.VSplitRight(30.0f, &Box, &Button); static int s_QuitButton=0; - if(DoButton_MenuTab(&s_QuitButton, Localize("Quit"), 0, &Button, CUI::CORNER_T)) + if(DoButton_MenuTab(&s_QuitButton, "×", 0, &Button, CUI::CORNER_T)) m_Popup = POPUP_QUIT; Box.VSplitRight(10.0f, &Box, &Button); - Box.VSplitRight(130.0f, &Box, &Button); + Box.VSplitRight(30.0f, &Box, &Button); static int s_SettingsButton=0; - if(DoButton_MenuTab(&s_SettingsButton, Localize("Settings"), m_ActivePage==PAGE_SETTINGS, &Button, CUI::CORNER_T)) + if(DoButton_MenuTab(&s_SettingsButton, "⚙", m_ActivePage==PAGE_SETTINGS, &Button, CUI::CORNER_T)) NewPage = PAGE_SETTINGS; + Box.VSplitRight(10.0f, &Box, &Button); + Box.VSplitRight(30.0f, &Box, &Button); + static int s_EditorButton=0; + if(DoButton_MenuTab(&s_EditorButton, Localize("✎"), 0, &Button, CUI::CORNER_T)) + { + g_Config.m_ClEditor = 1; + } + if(NewPage != -1) { if(Client()->State() == IClient::STATE_OFFLINE) @@ -675,7 +731,7 @@ void CMenus::RenderLoading() Graphics()->QuadsEnd(); - const char *pCaption = Localize("Loading DDRace Client"); + const char *pCaption = Localize("Loading DDNet Client"); CUIRect r; r.x = x; @@ -695,7 +751,31 @@ void CMenus::RenderLoading() void CMenus::RenderNews(CUIRect MainView) { + // TODO: Like the settings with big fonts + // Make it work WITHOUT version updates + // Show news once after each version or news update RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_ALL, 10.0f); + + MainView.HSplitTop(15.0f, 0, &MainView); + MainView.VSplitLeft(15.0f, 0, &MainView); + + CUIRect Label; + + std::istringstream f(Client()->m_aNews); + std::string line; + while (std::getline(f, line)) + { + if(line.size() > 0 && line.at(0) == '|' && line.at(line.size()-1) == '|') + { + MainView.HSplitTop(30.0f, &Label, &MainView); + UI()->DoLabelScaled(&Label, Localize(line.substr(1, line.size()-2).c_str()), 20.0f, -1); + } + else + { + MainView.HSplitTop(20.0f, &Label, &MainView); + UI()->DoLabelScaled(&Label, line.c_str(), 15.f, -1, MainView.w-30.0f); + } + } } void CMenus::OnInit() @@ -745,7 +825,10 @@ void CMenus::OnInit() // */ if(g_Config.m_ClShowWelcome) + { m_Popup = POPUP_LANGUAGE; + str_copy(g_Config.m_BrFilterString, "DDraceNetwork", sizeof(g_Config.m_BrFilterString)); + } g_Config.m_ClShowWelcome = 0; Console()->Chain("add_favorite", ConchainServerbrowserUpdate, this); @@ -788,6 +871,7 @@ int CMenus::Render() ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES); m_pClient->m_pSounds->Enqueue(CSounds::CHN_MUSIC, SOUND_MENU); s_First = false; + m_DoubleClickIndex = -1; } if(Client()->State() == IClient::STATE_ONLINE) @@ -819,15 +903,20 @@ int CMenus::Render() if(m_Popup == POPUP_NONE) { // do tab bar +#if defined(__ANDROID__) + Screen.HSplitTop(100.0f, &TabBar, &MainView); +#else Screen.HSplitTop(24.0f, &TabBar, &MainView); +#endif TabBar.VMargin(20.0f, &TabBar); RenderMenubar(TabBar); // news is not implemented yet - if(g_Config.m_UiPage <= PAGE_NEWS || g_Config.m_UiPage > PAGE_SETTINGS || (Client()->State() == IClient::STATE_OFFLINE && g_Config.m_UiPage >= PAGE_GAME && g_Config.m_UiPage <= PAGE_CALLVOTE)) + if(g_Config.m_UiPage < PAGE_NEWS || g_Config.m_UiPage > PAGE_SETTINGS || (Client()->State() == IClient::STATE_OFFLINE && g_Config.m_UiPage >= PAGE_GAME && g_Config.m_UiPage <= PAGE_CALLVOTE)) { ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); g_Config.m_UiPage = PAGE_INTERNET; + m_DoubleClickIndex = -1; } // render current page @@ -891,12 +980,36 @@ int CMenus::Render() pExtraText = ""; } } - else if(m_Popup == POPUP_DISCONNECTED) + else if (m_Popup == POPUP_DISCONNECTED) { pTitle = Localize("Disconnected"); pExtraText = Client()->ErrorString(); pButtonText = Localize("Ok"); - ExtraAlign = -1; + if ((str_find_nocase(Client()->ErrorString(), "full")) || (str_find_nocase(Client()->ErrorString(), "reserved"))) + { + if (g_Config.m_ClReconnectFull) + { + if (_my_rtime == 0) + _my_rtime = time_get(); + str_format(aBuf, sizeof(aBuf), Localize("\n\nReconnect in %d sec"), ((_my_rtime - time_get()) / time_freq() + g_Config.m_ClReconnectFullTimeout)); + pTitle = Client()->ErrorString(); + pExtraText = aBuf; + pButtonText = Localize("Abort"); + } + } + else if (str_find_nocase(Client()->ErrorString(), "ban")) + { + if (g_Config.m_ClReconnectBan) + { + if (_my_rtime == 0) + _my_rtime = time_get(); + str_format(aBuf, sizeof(aBuf), Localize("\n\nReconnect in %d sec"), ((_my_rtime - time_get()) / time_freq() + g_Config.m_ClReconnectBanTimeout)); + pTitle = Client()->ErrorString(); + pExtraText = aBuf; + pButtonText = Localize("Abort"); + } + } + ExtraAlign = 0; } else if(m_Popup == POPUP_PURE) { @@ -942,6 +1055,12 @@ int CMenus::Render() pExtraText = Localize("Are you sure that you want to quit?"); ExtraAlign = -1; } + else if(m_Popup == POPUP_DISCONNECT) + { + pTitle = Localize("Disconnect"); + pExtraText = Localize("Are you sure that you want to disconnect?"); + ExtraAlign = -1; + } else if(m_Popup == POPUP_FIRST_LAUNCH) { pTitle = Localize("Welcome to Teeworlds"); @@ -949,11 +1068,23 @@ int CMenus::Render() pButtonText = Localize("Ok"); ExtraAlign = -1; } +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + else if(m_Popup == POPUP_AUTOUPDATE) + { + pTitle = Localize("Auto-Update"); + pExtraText = Localize("An update to DDNet client is available. Do you want to update now? This may restart the client. If an update fails, make sure the client has permissions to modify files."); + ExtraAlign = -1; + } +#endif CUIRect Box, Part; Box = Screen; Box.VMargin(150.0f/UI()->Scale(), &Box); +#if defined(__ANDROID__) + Box.HMargin(100.0f/UI()->Scale(), &Box); +#else Box.HMargin(150.0f/UI()->Scale(), &Box); +#endif // render the box RenderTools()->DrawUIRect(&Box, vec4(0,0,0,0.5f), CUI::CORNER_ALL, 15.0f); @@ -974,7 +1105,11 @@ int CMenus::Render() { CUIRect Yes, No; Box.HSplitBottom(20.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif // additional info Box.HSplitTop(10.0f, 0, &Box); @@ -1000,12 +1135,62 @@ int CMenus::Render() if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || m_EnterPressed) Client()->Quit(); } + else if(m_Popup == POPUP_DISCONNECT) + { + CUIRect Yes, No; + Box.HSplitBottom(20.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else + Box.HSplitBottom(24.f, &Box, &Part); +#endif + + // buttons + Part.VMargin(80.0f, &Part); + Part.VSplitMid(&No, &Yes); + Yes.VMargin(20.0f, &Yes); + No.VMargin(20.0f, &No); + + static int s_ButtonAbort = 0; + if(DoButton_Menu(&s_ButtonAbort, Localize("No"), 0, &No) || m_EscapePressed) + m_Popup = POPUP_NONE; + + static int s_ButtonTryAgain = 0; + if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || m_EnterPressed) + Client()->Disconnect(); + } +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + else if(m_Popup == POPUP_AUTOUPDATE) + { + CUIRect Yes, No; + Box.HSplitBottom(20.f, &Box, &Part); + Box.HSplitBottom(24.f, &Box, &Part); + + // buttons + Part.VMargin(80.0f, &Part); + Part.VSplitMid(&No, &Yes); + Yes.VMargin(20.0f, &Yes); + No.VMargin(20.0f, &No); + + static int s_ButtonAbort = 0; + if(DoButton_Menu(&s_ButtonAbort, Localize("No"), 0, &No) || m_EscapePressed) + m_Popup = POPUP_NONE; + + static int s_ButtonTryAgain = 0; + if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || m_EnterPressed) + m_pClient->AutoUpdate()->DoUpdates(this); + } +#endif else if(m_Popup == POPUP_PASSWORD) { CUIRect Label, TextBox, TryAgain, Abort; Box.HSplitBottom(20.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Part.VMargin(80.0f, &Part); Part.VSplitMid(&Abort, &TryAgain); @@ -1024,7 +1209,11 @@ int CMenus::Render() } Box.HSplitBottom(60.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Part.VSplitLeft(60.0f, 0, &Label); Label.VSplitLeft(100.0f, 0, &TextBox); @@ -1040,7 +1229,11 @@ int CMenus::Render() Box.VMargin(150.0f, &Box); Box.HMargin(150.0f, &Box); Box.HSplitBottom(20.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Part.VMargin(120.0f, &Part); static int s_Button = 0; @@ -1062,7 +1255,7 @@ int CMenus::Render() } // update download speed - float Diff = Client()->MapDownloadAmount()-m_DownloadLastCheckSize; + float Diff = (Client()->MapDownloadAmount()-m_DownloadLastCheckSize)/((int)((Now-m_DownloadLastCheckTime)/time_freq())); float StartDiff = m_DownloadLastCheckSize-0.0f; if(StartDiff+Diff > 0.0f) m_DownloadSpeed = (Diff/(StartDiff+Diff))*(Diff/1.0f) + (StartDiff/(Diff+StartDiff))*m_DownloadSpeed; @@ -1105,10 +1298,18 @@ int CMenus::Render() { Box = Screen; Box.VMargin(150.0f, &Box); +#if defined(__ANDROID__) + Box.HMargin(20.0f, &Box); +#else Box.HMargin(150.0f, &Box); +#endif Box.HSplitTop(20.f, &Part, &Box); Box.HSplitBottom(20.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Box.HSplitBottom(20.f, &Box, 0); Box.VMargin(20.0f, &Box); RenderLanguageSelection(Box); @@ -1122,10 +1323,18 @@ int CMenus::Render() { Box = Screen; Box.VMargin(150.0f, &Box); +#if defined(__ANDROID__) + Box.HMargin(20.0f, &Box); +#else Box.HMargin(150.0f, &Box); +#endif Box.HSplitTop(20.f, &Part, &Box); Box.HSplitBottom(20.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Box.HSplitBottom(20.f, &Box, 0); Box.VMargin(20.0f, &Box); @@ -1181,7 +1390,11 @@ int CMenus::Render() { CUIRect Yes, No; Box.HSplitBottom(20.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Part.VMargin(80.0f, &Part); Part.VSplitMid(&No, &Yes); @@ -1217,7 +1430,11 @@ int CMenus::Render() CUIRect Label, TextBox, Ok, Abort; Box.HSplitBottom(20.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Part.VMargin(80.0f, &Part); Part.VSplitMid(&Abort, &Ok); @@ -1255,7 +1472,11 @@ int CMenus::Render() } Box.HSplitBottom(60.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Part.VSplitLeft(60.0f, 0, &Label); Label.VSplitLeft(120.0f, 0, &TextBox); @@ -1269,7 +1490,11 @@ int CMenus::Render() { CUIRect Yes, No; Box.HSplitBottom(20.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Part.VMargin(80.0f, &Part); Part.VSplitMid(&No, &Yes); @@ -1300,7 +1525,11 @@ int CMenus::Render() CUIRect Label, TextBox; Box.HSplitBottom(20.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Part.VMargin(80.0f, &Part); static int s_EnterButton = 0; @@ -1308,7 +1537,11 @@ int CMenus::Render() m_Popup = POPUP_NONE; Box.HSplitBottom(40.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Part.VSplitLeft(60.0f, 0, &Label); Label.VSplitLeft(100.0f, 0, &TextBox); @@ -1321,15 +1554,38 @@ int CMenus::Render() else { Box.HSplitBottom(20.f, &Box, &Part); +#if defined(__ANDROID__) + Box.HSplitBottom(60.f, &Box, &Part); +#else Box.HSplitBottom(24.f, &Box, &Part); +#endif Part.VMargin(120.0f, &Part); static int s_Button = 0; if(DoButton_Menu(&s_Button, pButtonText, 0, &Part) || m_EscapePressed || m_EnterPressed) m_Popup = POPUP_NONE; } + + if(m_Popup == POPUP_NONE) + UI()->SetActiveItem(0); } + if (m_Popup == POPUP_DISCONNECTED) + { + if (str_find_nocase(Client()->ErrorString(), "full") || str_find_nocase(Client()->ErrorString(), "reserved")) + { + if (g_Config.m_ClReconnectFull && time_get() > _my_rtime + time_freq() * g_Config.m_ClReconnectFullTimeout) + Client()->Connect(g_Config.m_UiServerAddress); + } + else if (str_find_nocase(Client()->ErrorString(), "ban") || str_find_nocase(Client()->ErrorString(), "kick")) + { + if (g_Config.m_ClReconnectBan && time_get() > _my_rtime + time_freq() * g_Config.m_ClReconnectBanTimeout) + Client()->Connect(g_Config.m_UiServerAddress); + } + } + else if (_my_rtime != 0) { + _my_rtime = 0; + } return 0; } @@ -1337,6 +1593,9 @@ int CMenus::Render() void CMenus::SetActive(bool Active) { m_MenuActive = Active; +#if defined(__ANDROID__) + UI()->AndroidShowScreenKeys(!m_MenuActive && !m_pClient->m_pControls->m_UsingGamepad); +#endif if(!m_MenuActive) { if(m_NeedSendinfo) @@ -1345,6 +1604,12 @@ void CMenus::SetActive(bool Active) m_NeedSendinfo = false; } + if(m_NeedSendDummyinfo) + { + m_pClient->SendDummyInfo(false); + m_NeedSendDummyinfo = false; + } + if(Client()->State() == IClient::STATE_ONLINE) { m_pClient->OnRelease(); @@ -1367,9 +1632,14 @@ bool CMenus::OnMouseMove(float x, float y) if(!m_MenuActive) return false; +#if defined(__ANDROID__) // No relative mouse on Android + m_MousePos.x = x; + m_MousePos.y = y; +#else UI()->ConvertMouseMove(&x, &y); m_MousePos.x += x; m_MousePos.y += y; +#endif if(m_MousePos.x < 0) m_MousePos.x = 0; if(m_MousePos.y < 0) m_MousePos.y = 0; if(m_MousePos.x > Graphics()->ScreenWidth()) m_MousePos.x = Graphics()->ScreenWidth(); @@ -1531,7 +1801,14 @@ void CMenus::OnRender() if(Input()->KeyPressed(KEY_MOUSE_3)) Buttons |= 4; } +#if defined(__ANDROID__) + static int ButtonsOneFrameDelay = 0; // For Android touch input + + UI()->Update(mx,my,mx*3.0f,my*3.0f,ButtonsOneFrameDelay); + ButtonsOneFrameDelay = Buttons; +#else UI()->Update(mx,my,mx*3.0f,my*3.0f,Buttons); +#endif // render if(Client()->State() != IClient::STATE_DEMOPLAYBACK) @@ -1636,3 +1913,56 @@ int CMenus::DoButton_CheckBox_DontCare(const void *pID, const char *pText, int C return DoButton_CheckBox_Common(pID, pText, "", pRect); } } + +void CMenus::RenderUpdating(const char *pCaption, int current, int total) +{ + // make sure that we don't render for each little thing we load + // because that will slow down loading if we have vsync + static int64 LastLoadRender = 0; + if(time_get()-LastLoadRender < time_freq()/60) + return; + LastLoadRender = time_get(); + + // need up date this here to get correct + vec3 Rgb = HslToRgb(vec3(g_Config.m_UiColorHue/255.0f, g_Config.m_UiColorSat/255.0f, g_Config.m_UiColorLht/255.0f)); + ms_GuiColor = vec4(Rgb.r, Rgb.g, Rgb.b, g_Config.m_UiColorAlpha/255.0f); + + CUIRect Screen = *UI()->Screen(); + Graphics()->MapScreen(Screen.x, Screen.y, Screen.w, Screen.h); + + RenderBackground(); + + float w = 700; + float h = 200; + float x = Screen.w/2-w/2; + float y = Screen.h/2-h/2; + + Graphics()->BlendNormal(); + + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0,0,0,0.50f); + RenderTools()->DrawRoundRect(0, y, Screen.w, h, 0.0f); + Graphics()->QuadsEnd(); + + CUIRect r; + r.x = x; + r.y = y+20; + r.w = w; + r.h = h; + UI()->DoLabel(&r, Localize(pCaption), 32.0f, 0, -1); + + if (total>0) + { + float Percent = current/(float)total; + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0.15f,0.15f,0.15f,0.75f); + RenderTools()->DrawRoundRect(x+40, y+h-75, w-80, 30, 5.0f); + Graphics()->SetColor(1,1,1,0.75f); + RenderTools()->DrawRoundRect(x+45, y+h-70, (w-85)*Percent, 20, 5.0f); + Graphics()->QuadsEnd(); + } + + Graphics()->Swap(); +} diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 5d34f2eb57..67c3d83358 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -92,42 +92,6 @@ class CMenus : public CComponent //static void demolist_listdir_callback(const char *name, int is_dir, void *user); //static void demolist_list_callback(const CUIRect *rect, int index, void *user); - enum - { - POPUP_NONE=0, - POPUP_FIRST_LAUNCH, - POPUP_CONNECTING, - POPUP_MESSAGE, - POPUP_DISCONNECTED, - POPUP_PURE, - POPUP_LANGUAGE, - POPUP_COUNTRY, - POPUP_DELETE_DEMO, - POPUP_RENAME_DEMO, - POPUP_REMOVE_FRIEND, - POPUP_SOUNDERROR, - POPUP_PASSWORD, - POPUP_QUIT, - }; - - enum - { - PAGE_NEWS=1, - PAGE_GAME, - PAGE_PLAYERS, - PAGE_SERVER_INFO, - PAGE_CALLVOTE, - PAGE_INTERNET, - PAGE_LAN, - PAGE_FAVORITES, - PAGE_DEMOS, - PAGE_SETTINGS, - PAGE_SYSTEM, - PAGE_DDRace, - PAGE_BROWSER, - PAGE_GHOST - }; - int m_GamePage; int m_Popup; int m_ActivePage; @@ -156,12 +120,14 @@ class CMenus : public CComponent // some settings static float ms_ButtonHeight; static float ms_ListheaderHeight; + static float ms_ListitemAdditionalHeight; static float ms_FontmodHeight; // for settings bool m_NeedRestartGraphics; bool m_NeedRestartSound; bool m_NeedSendinfo; + bool m_NeedSendDummyinfo; int m_SettingPlayerPage; // @@ -256,6 +222,7 @@ class CMenus : public CComponent // found in menus_browser.cpp int m_SelectedIndex; + int m_DoubleClickIndex; int m_ScrollOffset; void RenderServerbrowserServerList(CUIRect View); void RenderServerbrowserServerDetail(CUIRect View); @@ -269,6 +236,7 @@ class CMenus : public CComponent void RenderLanguageSelection(CUIRect MainView); void RenderSettingsGeneral(CUIRect MainView); void RenderSettingsPlayer(CUIRect MainView); + void RenderSettingsDummyPlayer(CUIRect MainView); void RenderSettingsTee(CUIRect MainView); void RenderSettingsControls(CUIRect MainView); void RenderSettingsGraphics(CUIRect MainView); @@ -286,6 +254,7 @@ class CMenus : public CComponent CMenus(); void RenderLoading(); + void RenderUpdating(const char *pCaption, int current=0, int total=0); bool IsActive() const { return m_MenuActive; } @@ -297,8 +266,26 @@ class CMenus : public CComponent virtual bool OnInput(IInput::CEvent Event); virtual bool OnMouseMove(float x, float y); - // DDRace + enum + { + PAGE_NEWS=1, + PAGE_GAME, + PAGE_PLAYERS, + PAGE_SERVER_INFO, + PAGE_CALLVOTE, + PAGE_INTERNET, + PAGE_LAN, + PAGE_FAVORITES, + PAGE_DEMOS, + PAGE_SETTINGS, + PAGE_SYSTEM, + PAGE_DDRace, + PAGE_BROWSER, + PAGE_GHOST + }; + // DDRace + int64 _my_rtime; // reconnect time int DoButton_CheckBox_DontCare(const void *pID, const char *pText, int Checked, const CUIRect *pRect); sorted_array m_lDemos; void DemolistPopulate(); @@ -322,6 +309,27 @@ class CMenus : public CComponent CGhostItem *m_OwnGhost; int m_DDRacePage; void GhostlistPopulate(); + void setPopup(int Popup) { m_Popup = Popup; } + + enum + { + POPUP_NONE=0, + POPUP_FIRST_LAUNCH, + POPUP_CONNECTING, + POPUP_MESSAGE, + POPUP_DISCONNECTED, + POPUP_PURE, + POPUP_LANGUAGE, + POPUP_COUNTRY, + POPUP_DELETE_DEMO, + POPUP_RENAME_DEMO, + POPUP_REMOVE_FRIEND, + POPUP_SOUNDERROR, + POPUP_PASSWORD, + POPUP_QUIT, + POPUP_AUTOUPDATE, + POPUP_DISCONNECT + }; private: @@ -334,5 +342,6 @@ class CMenus : public CComponent // found in menus_settings.cpp void RenderSettingsDDRace(CUIRect MainView); + void RenderSettingsHUD(CUIRect MainView); }; #endif diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index df26861f8c..0559a9f4ec 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -71,6 +71,9 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) {COL_PLAYERS, IServerBrowser::SORT_NUMPLAYERS, "Players", 1, 60.0f, 0, {0}, {0}}, {-1, -1, " ", 1, 10.0f, 0, {0}, {0}}, {COL_PING, IServerBrowser::SORT_PING, "Ping", 1, 40.0f, FIXED, {0}, {0}}, +#if defined(__ANDROID__) + {-1, -1, " ", 1, 50.0f, 0, {0}, {0}}, // Scrollbar +#endif }; // This is just for scripts/update_localization.py to work correctly (all other strings are already Localize()'d somewhere else). Don't remove! // Localize("Type"); @@ -126,7 +129,11 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.15f), 0, 0); CUIRect Scroll; +#if defined(__ANDROID__) + View.VSplitRight(50, &View, &Scroll); +#else View.VSplitRight(15, &View, &Scroll); +#endif int NumServers = ServerBrowser()->NumSortedServers(); @@ -214,6 +221,9 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) View.y -= s_ScrollValue*ScrollNum*s_aCols[0].m_Rect.h; int NewSelected = -1; +#if defined(__ANDROID__) + int DoubleClicked = 0; +#endif int NumPlayers = 0; m_SelectedIndex = -1; @@ -225,13 +235,13 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) { int ItemIndex = i; const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex); - NumPlayers += pItem->m_NumPlayers; + NumPlayers += g_Config.m_BrFilterSpectators ? pItem->m_NumPlayers : pItem->m_NumClients; CUIRect Row; CUIRect SelectHitBox; int Selected = str_comp(pItem->m_aAddress, g_Config.m_UiServerAddress) == 0; //selected_index==ItemIndex; - View.HSplitTop(17.0f, &Row, &View); + View.HSplitTop(ms_ListheaderHeight, &Row, &View); SelectHitBox = Row; if(Selected) @@ -282,6 +292,11 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) if(UI()->DoButtonLogic(pItem, "", Selected, &SelectHitBox)) { NewSelected = ItemIndex; +#if defined(__ANDROID__) + if(NewSelected == m_DoubleClickIndex) + DoubleClicked = 1; +#endif + m_DoubleClickIndex = NewSelected; } } else @@ -307,11 +322,19 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) if(ID == COL_FLAG_LOCK) { +#if defined(__ANDROID__) + Button.h = Button.w; + Button.y += ms_ListitemAdditionalHeight / 2; +#endif if(pItem->m_Flags & SERVER_FLAG_PASSWORD) DoButton_Icon(IMAGE_BROWSEICONS, SPRITE_BROWSE_LOCK, &Button); } else if(ID == COL_FLAG_PURE) { +#if defined(__ANDROID__) + Button.h = Button.w; + Button.y += ms_ListitemAdditionalHeight / 2; +#endif if( str_comp(pItem->m_aGameType, "DM") == 0 || str_comp(pItem->m_aGameType, "TDM") == 0 || str_comp(pItem->m_aGameType, "CTF") == 0) @@ -326,13 +349,21 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } else if(ID == COL_FLAG_FAV) { +#if defined(__ANDROID__) + Button.h = Button.w; + Button.y += ms_ListitemAdditionalHeight / 2; +#endif if(pItem->m_Favorite) DoButton_Icon(IMAGE_BROWSEICONS, SPRITE_BROWSE_HEART, &Button); } else if(ID == COL_NAME) { CTextCursor Cursor; +#if defined(__ANDROID__) + TextRender()->SetCursor(&Cursor, Button.x, Button.y + ms_ListitemAdditionalHeight / 2, 12.0f * UI()->Scale(), TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); +#else TextRender()->SetCursor(&Cursor, Button.x, Button.y, 12.0f * UI()->Scale(), TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); +#endif Cursor.m_LineWidth = Button.w; if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit&IServerBrowser::QUICK_SERVERNAME)) @@ -356,7 +387,11 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) else if(ID == COL_MAP) { CTextCursor Cursor; +#if defined(__ANDROID__) + TextRender()->SetCursor(&Cursor, Button.x, Button.y + ms_ListitemAdditionalHeight / 2, 12.0f * UI()->Scale(), TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); +#else TextRender()->SetCursor(&Cursor, Button.x, Button.y, 12.0f * UI()->Scale(), TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); +#endif Cursor.m_LineWidth = Button.w; if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit&IServerBrowser::QUICK_MAPNAME)) @@ -379,6 +414,10 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } else if(ID == COL_PLAYERS) { +#if defined(__ANDROID__) + Button.h -= ms_ListitemAdditionalHeight; + Button.y += ms_ListitemAdditionalHeight / 2; +#endif CUIRect Icon; Button.VMargin(4.0f, &Button); if(pItem->m_FriendState != IFriends::FRIEND_NO) @@ -399,22 +438,72 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } else if(ID == COL_PING) { +#if defined(__ANDROID__) + Button.h -= ms_ListitemAdditionalHeight; + Button.y += ms_ListitemAdditionalHeight / 2; +#endif str_format(aTemp, sizeof(aTemp), "%i", pItem->m_Latency); + if (g_Config.m_UiColorizePing) + { + vec3 rgb = HslToRgb(vec3((300.0f - clamp(pItem->m_Latency, 0, 300)) / 1000.0f, 1.0f, 0.5f)); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, 1.0f); + } + UI()->DoLabelScaled(&Button, aTemp, 12.0f, 1); + TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } else if(ID == COL_VERSION) { +#if defined(__ANDROID__) + Button.h -= ms_ListitemAdditionalHeight; + Button.y += ms_ListitemAdditionalHeight / 2; +#endif const char *pVersion = pItem->m_aVersion; UI()->DoLabelScaled(&Button, pVersion, 12.0f, 1); } else if(ID == COL_GAMETYPE) { CTextCursor Cursor; +#if defined(__ANDROID__) + TextRender()->SetCursor(&Cursor, Button.x, Button.y + ms_ListitemAdditionalHeight / 2, 12.0f*UI()->Scale(), TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); +#else TextRender()->SetCursor(&Cursor, Button.x, Button.y, 12.0f*UI()->Scale(), TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); +#endif Cursor.m_LineWidth = Button.w; - TextRender()->TextEx(&Cursor, pItem->m_aGameType, -1); - } + if (g_Config.m_UiColorizeGametype) + { + vec3 hsl = vec3(1.0f, 1.0f, 1.0f); + + if (!str_comp(pItem->m_aGameType, "DM") + || !str_comp(pItem->m_aGameType, "TDM") + || !str_comp(pItem->m_aGameType, "CTF")) + hsl = vec3(0.33f, 1.0f, 0.75f); // Vanilla + else if (str_find_nocase(pItem->m_aGameType, "catch")) + hsl = vec3(0.17f, 1.0f, 0.75f); // Catch + else if (str_find_nocase(pItem->m_aGameType, "idm") + || str_find_nocase(pItem->m_aGameType, "itdm") + || str_find_nocase(pItem->m_aGameType, "ictf")) + hsl = vec3(0.00f, 1.0f, 0.75f); // Instagib + else if (str_find_nocase(pItem->m_aGameType, "fng")) + hsl = vec3(0.83f, 1.0f, 0.75f); // FNG + else if (str_find_nocase(pItem->m_aGameType, "ddracenetwo")) + hsl = vec3(0.58f, 1.0f, 0.75f); // DDNet + else if (str_find_nocase(pItem->m_aGameType, "ddrace") + || str_find_nocase(pItem->m_aGameType, "mkrace")) + hsl = vec3(0.75f, 1.0f, 0.75f); // DDRace + else if (str_find_nocase(pItem->m_aGameType, "race") + || !str_comp(pItem->m_aGameType, "FastCap")) + hsl = vec3(0.46f, 1.0f, 0.75f); // Races + + vec3 rgb = HslToRgb(hsl); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, 1.0f); + TextRender()->TextEx(&Cursor, pItem->m_aGameType, -1); + TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); + } + else + TextRender()->TextEx(&Cursor, pItem->m_aGameType, -1); + } } } @@ -425,7 +514,11 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) // select the new server const CServerInfo *pItem = ServerBrowser()->SortedGet(NewSelected); str_copy(g_Config.m_UiServerAddress, pItem->m_aAddress, sizeof(g_Config.m_UiServerAddress)); +#if defined(__ANDROID__) + if(DoubleClicked) +#else if(Input()->MouseDoubleClick()) +#endif Client()->Connect(g_Config.m_UiServerAddress); } @@ -435,7 +528,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) // render quick search CUIRect QuickSearch, Button; Status.VSplitLeft(240.0f, &QuickSearch, &Status); - const char *pLabel = Localize("Quick search:"); + const char *pLabel = Localize("Search:"); UI()->DoLabelScaled(&QuickSearch, pLabel, 12.0f, -1); float w = TextRender()->TextWidth(0, 12.0f, pLabel, -1); QuickSearch.VSplitLeft(w, 0, &QuickSearch); @@ -461,7 +554,13 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) // render status char aBuf[128]; if(ServerBrowser()->IsRefreshing()) - str_format(aBuf, sizeof(aBuf), Localize("%d%% loaded"), ServerBrowser()->LoadingProgression()); + { + char aBuf2[64]; + char aBuf3[64]; + str_format(aBuf3, sizeof(aBuf3), Localize("%d%% loaded"), ServerBrowser()->LoadingProgression()); + str_format(aBuf2, sizeof(aBuf2), Localize("%d of %d servers, %d players"), ServerBrowser()->NumSortedServers(), ServerBrowser()->NumServers(), NumPlayers); + str_format(aBuf, sizeof(aBuf), "%s, %s", aBuf3, aBuf2); + } else str_format(aBuf, sizeof(aBuf), Localize("%d of %d servers, %d players"), ServerBrowser()->NumSortedServers(), ServerBrowser()->NumServers(), NumPlayers); Status.VSplitRight(TextRender()->TextWidth(0, 14.0f, aBuf, -1), 0, &Status); @@ -574,6 +673,14 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) m_Popup = POPUP_COUNTRY; } + ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + if (DoButton_CheckBox((char *)&g_Config.m_UiColorizeGametype, Localize("Colorize gametype"), g_Config.m_UiColorizeGametype, &Button)) + g_Config.m_UiColorizeGametype ^= 1; + + ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + if (DoButton_CheckBox((char *)&g_Config.m_UiColorizePing, Localize("Colorize ping"), g_Config.m_UiColorizePing, &Button)) + g_Config.m_UiColorizePing ^= 1; + ServerFilter.HSplitBottom(5.0f, &ServerFilter, 0); ServerFilter.HSplitBottom(ms_ButtonHeight-2.0f, &ServerFilter, &Button); static int s_ClearButton = 0; @@ -675,19 +782,23 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) } // server scoreboard - ServerScoreBoard.HSplitBottom(20.0f, &ServerScoreBoard, 0x0); - ServerScoreBoard.HSplitTop(ms_ListheaderHeight, &ServerHeader, &ServerScoreBoard); - RenderTools()->DrawUIRect(&ServerHeader, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f); - RenderTools()->DrawUIRect(&ServerScoreBoard, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f); - UI()->DoLabelScaled(&ServerHeader, Localize("Scoreboard"), FontSize+2.0f, 0); + //ServerScoreBoard.HSplitBottom(20.0f, &ServerScoreBoard, 0x0); if(pSelectedServer) { - ServerScoreBoard.Margin(3.0f, &ServerScoreBoard); + static int s_VoteList = 0; + static float s_ScrollValue = 0; + UiDoListboxStart(&s_VoteList, &ServerScoreBoard, 26.0f, Localize("Scoreboard"), "", pSelectedServer->m_NumClients, 1, -1, s_ScrollValue); + for (int i = 0; i < pSelectedServer->m_NumClients; i++) { + CListboxItem Item = UiDoListboxNextItem(&i); + + if(!Item.m_Visible) + continue; + CUIRect Name, Clan, Score, Flag; - ServerScoreBoard.HSplitTop(25.0f, &Name, &ServerScoreBoard); + Item.m_Rect.HSplitTop(25.0f, &Name, &Item.m_Rect); if(UI()->DoButtonLogic(&pSelectedServer->m_aClients[i], "", 0, &Name)) { if(pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_PLAYER) @@ -702,20 +813,32 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) vec4(0.5f, 1.0f, 0.5f, 0.15f+(i%2+1)*0.05f); RenderTools()->DrawUIRect(&Name, Colour, CUI::CORNER_ALL, 4.0f); Name.VSplitLeft(5.0f, 0, &Name); - Name.VSplitLeft(30.0f, &Score, &Name); + Name.VSplitLeft(34.0f, &Score, &Name); Name.VSplitRight(34.0f, &Name, &Flag); Flag.HMargin(4.0f, &Flag); Name.HSplitTop(11.0f, &Name, &Clan); // score - if(pSelectedServer->m_aClients[i].m_Player) + char aTemp[16]; + + if(!pSelectedServer->m_aClients[i].m_Player) + str_copy(aTemp, "SPEC", sizeof(aTemp)); + else if(str_find_nocase(pSelectedServer->m_aGameType, "race") || str_find_nocase(pSelectedServer->m_aGameType, "fastcap")) { - char aTemp[16]; - str_format(aTemp, sizeof(aTemp), "%d", pSelectedServer->m_aClients[i].m_Score); - TextRender()->SetCursor(&Cursor, Score.x, Score.y+(Score.h-FontSize)/4.0f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = Score.w; - TextRender()->TextEx(&Cursor, aTemp, -1); + if(pSelectedServer->m_aClients[i].m_Score == -9999 || pSelectedServer->m_aClients[i].m_Score == 0) + aTemp[0] = 0; + else + { + int Time = abs(pSelectedServer->m_aClients[i].m_Score); + str_format(aTemp, sizeof(aTemp), "%02d:%02d", Time/60, Time%60); + } } + else + str_format(aTemp, sizeof(aTemp), "%d", pSelectedServer->m_aClients[i].m_Score); + + TextRender()->SetCursor(&Cursor, Score.x, Score.y+(Score.h-FontSize)/4.0f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = Score.w; + TextRender()->TextEx(&Cursor, aTemp, -1); // name TextRender()->SetCursor(&Cursor, Name.x, Name.y, FontSize-2, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); @@ -765,6 +888,8 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) vec4 Color(1.0f, 1.0f, 1.0f, 0.5f); m_pClient->m_pCountryFlags->Render(pSelectedServer->m_aClients[i].m_Country, &Color, Flag.x, Flag.y, Flag.w, Flag.h); } + + UiDoListboxEnd(&s_ScrollValue, 0); } } @@ -806,7 +931,13 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) // friends list(remove friend) static float s_ScrollValue = 0; + if(m_FriendlistSelectedIndex >= m_lFriends.size()) + m_FriendlistSelectedIndex = m_lFriends.size()-1; +#if defined(__ANDROID__) + UiDoListboxStart(&m_lFriends, &List, 50.0f, "", "", m_lFriends.size(), 1, m_FriendlistSelectedIndex, s_ScrollValue); +#else UiDoListboxStart(&m_lFriends, &List, 30.0f, "", "", m_lFriends.size(), 1, m_FriendlistSelectedIndex, s_ScrollValue); +#endif m_lFriends.sort_range(); for(int i = 0; i < m_lFriends.size(); ++i) @@ -994,7 +1125,7 @@ void CMenus::RenderServerbrowser(CUIRect MainView) char aBuf[64]; if(str_comp(Client()->LatestVersion(), "0") != 0) { - str_format(aBuf, sizeof(aBuf), Localize("Teeworlds %s is out! Download it at www.teeworlds.com!"), Client()->LatestVersion()); + str_format(aBuf, sizeof(aBuf), Localize("DDNet %s is out! Download it at ddnet.tw!"), Client()->LatestVersion()); TextRender()->TextColor(1.0f, 0.4f, 0.4f, 1.0f); } else @@ -1018,6 +1149,7 @@ void CMenus::RenderServerbrowser(CUIRect MainView) ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN); else if(g_Config.m_UiPage == PAGE_FAVORITES) ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES); + m_DoubleClickIndex = -1; } ButtonArea.HSplitTop(5.0f, 0, &ButtonArea); diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 7f39f209a0..39176aabbb 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -54,10 +54,10 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) const float Margins = 5.0f; float TotalHeight; - if(m_MenuActive) - TotalHeight = SeekBarHeight+ButtonbarHeight+NameBarHeight+Margins*3; - else - TotalHeight = SeekBarHeight+Margins*2; + if(!m_MenuActive) + return; + + TotalHeight = SeekBarHeight+ButtonbarHeight+NameBarHeight+Margins*3; MainView.HSplitBottom(TotalHeight, 0, &MainView); MainView.VSplitLeft(50.0f, 0, &MainView); @@ -72,15 +72,10 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) int CurrentTick = pInfo->m_CurrentTick - pInfo->m_FirstTick; int TotalTicks = pInfo->m_LastTick - pInfo->m_FirstTick; - if(m_MenuActive) - { - MainView.HSplitTop(SeekBarHeight, &SeekBar, &ButtonBar); - ButtonBar.HSplitTop(Margins, 0, &ButtonBar); - ButtonBar.HSplitBottom(NameBarHeight, &ButtonBar, &NameBar); - NameBar.HSplitTop(4.0f, 0, &NameBar); - } - else - SeekBar = MainView; + MainView.HSplitTop(SeekBarHeight, &SeekBar, &ButtonBar); + ButtonBar.HSplitTop(Margins, 0, &ButtonBar); + ButtonBar.HSplitBottom(NameBarHeight, &ButtonBar, &NameBar); + NameBar.HSplitTop(4.0f, 0, &NameBar); // do seekbar { @@ -126,15 +121,34 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) { static float PrevAmount = 0.0f; float Amount = (UI()->MouseX()-SeekBar.x)/(float)SeekBar.w; - if(Amount > 0.0f && Amount < 1.0f && absolute(PrevAmount-Amount) >= 0.01f) + + if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) { - PrevAmount = Amount; - m_pClient->OnReset(); - m_pClient->m_SuppressEvents = true; - DemoPlayer()->SetPos(Amount); - m_pClient->m_SuppressEvents = false; - m_pClient->m_pMapLayersBackGround->EnvelopeUpdate(); - m_pClient->m_pMapLayersForeGround->EnvelopeUpdate(); + Amount = PrevAmount + (Amount-PrevAmount) * 0.05f; + + if(Amount > 0.0f && Amount < 1.0f && absolute(PrevAmount-Amount) >= 0.0001f) + { + //PrevAmount = Amount; + m_pClient->OnReset(); + m_pClient->m_SuppressEvents = true; + DemoPlayer()->SetPos(Amount); + m_pClient->m_SuppressEvents = false; + m_pClient->m_pMapLayersBackGround->EnvelopeUpdate(); + m_pClient->m_pMapLayersForeGround->EnvelopeUpdate(); + } + } + else + { + if(Amount > 0.0f && Amount < 1.0f && absolute(PrevAmount-Amount) >= 0.001f) + { + PrevAmount = Amount; + m_pClient->OnReset(); + m_pClient->m_SuppressEvents = true; + DemoPlayer()->SetPos(Amount); + m_pClient->m_SuppressEvents = false; + m_pClient->m_pMapLayersBackGround->EnvelopeUpdate(); + m_pClient->m_pMapLayersForeGround->EnvelopeUpdate(); + } } } } @@ -157,77 +171,74 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) bool IncreaseDemoSpeed = false, DecreaseDemoSpeed = false; - if(m_MenuActive) - { - // do buttons - CUIRect Button; - - // combined play and pause button - ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); - static int s_PlayPauseButton = 0; - if(!pInfo->m_Paused) - { - if(DoButton_Sprite(&s_PlayPauseButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_PAUSE, false, &Button, CUI::CORNER_ALL)) - DemoPlayer()->Pause(); - } - else - { - if(DoButton_Sprite(&s_PlayPauseButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_PLAY, false, &Button, CUI::CORNER_ALL)) - DemoPlayer()->Unpause(); - } - - // stop button + // do buttons + CUIRect Button; - ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); - ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); - static int s_ResetButton = 0; - if(DoButton_Sprite(&s_ResetButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_STOP, false, &Button, CUI::CORNER_ALL)) - { - m_pClient->OnReset(); + // combined play and pause button + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static int s_PlayPauseButton = 0; + if(!pInfo->m_Paused) + { + if(DoButton_Sprite(&s_PlayPauseButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_PAUSE, false, &Button, CUI::CORNER_ALL)) DemoPlayer()->Pause(); - DemoPlayer()->SetPos(0); - } + } + else + { + if(DoButton_Sprite(&s_PlayPauseButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_PLAY, false, &Button, CUI::CORNER_ALL)) + DemoPlayer()->Unpause(); + } - // slowdown - ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); - ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); - static int s_SlowDownButton = 0; - if(DoButton_Sprite(&s_SlowDownButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_SLOWER, 0, &Button, CUI::CORNER_ALL) || Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) - DecreaseDemoSpeed = true; - - // fastforward - ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); - ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); - static int s_FastForwardButton = 0; - if(DoButton_Sprite(&s_FastForwardButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_FASTER, 0, &Button, CUI::CORNER_ALL)) - IncreaseDemoSpeed = true; - - // speed meter - ButtonBar.VSplitLeft(Margins*3, 0, &ButtonBar); - char aBuffer[64]; - if(pInfo->m_Speed >= 1.0f) - str_format(aBuffer, sizeof(aBuffer), "x%.0f", pInfo->m_Speed); - else - str_format(aBuffer, sizeof(aBuffer), "x%.2f", pInfo->m_Speed); - UI()->DoLabel(&ButtonBar, aBuffer, Button.h*0.7f, -1); - - // close button - ButtonBar.VSplitRight(ButtonbarHeight*3, &ButtonBar, &Button); - static int s_ExitButton = 0; - if(DoButton_DemoPlayer(&s_ExitButton, Localize("Close"), 0, &Button)) - Client()->Disconnect(); - - // demo name - char aDemoName[64] = {0}; - DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName)); - char aBuf[128]; - str_format(aBuf, sizeof(aBuf), Localize("Demofile: %s"), aDemoName); - CTextCursor Cursor; - TextRender()->SetCursor(&Cursor, NameBar.x, NameBar.y, Button.h*0.5f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = MainView.w; - TextRender()->TextEx(&Cursor, aBuf, -1); + // stop button + + ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static int s_ResetButton = 0; + if(DoButton_Sprite(&s_ResetButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_STOP, false, &Button, CUI::CORNER_ALL)) + { + m_pClient->OnReset(); + DemoPlayer()->Pause(); + DemoPlayer()->SetPos(0); } + // slowdown + ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static int s_SlowDownButton = 0; + if(DoButton_Sprite(&s_SlowDownButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_SLOWER, 0, &Button, CUI::CORNER_ALL) || Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + DecreaseDemoSpeed = true; + + // fastforward + ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static int s_FastForwardButton = 0; + if(DoButton_Sprite(&s_FastForwardButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_FASTER, 0, &Button, CUI::CORNER_ALL)) + IncreaseDemoSpeed = true; + + // speed meter + ButtonBar.VSplitLeft(Margins*3, 0, &ButtonBar); + char aBuffer[64]; + if(pInfo->m_Speed >= 1.0f) + str_format(aBuffer, sizeof(aBuffer), "x%.0f", pInfo->m_Speed); + else + str_format(aBuffer, sizeof(aBuffer), "x%.2f", pInfo->m_Speed); + UI()->DoLabel(&ButtonBar, aBuffer, Button.h*0.7f, -1); + + // close button + ButtonBar.VSplitRight(ButtonbarHeight*3, &ButtonBar, &Button); + static int s_ExitButton = 0; + if(DoButton_DemoPlayer(&s_ExitButton, Localize("Close"), 0, &Button)) + Client()->Disconnect(); + + // demo name + char aDemoName[64] = {0}; + DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName)); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), Localize("Demofile: %s"), aDemoName); + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, NameBar.x, NameBar.y, Button.h*0.5f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = MainView.w; + TextRender()->TextEx(&Cursor, aBuf, -1); + if(IncreaseDemoSpeed || Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) { if(pInfo->m_Speed < 0.1f) DemoPlayer()->SetSpeed(0.1f); @@ -286,7 +297,11 @@ void CMenus::UiDoListboxStart(const void *pID, const CUIRect *pRect, float RowHe RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.15f), 0, 0); // prepare the scroll +#if defined(__ANDROID__) + View.VSplitRight(50, &View, &Scroll); +#else View.VSplitRight(15, &View, &Scroll); +#endif // setup the variables gs_ListBoxOriginalView = View; @@ -598,7 +613,10 @@ void CMenus::RenderDemoList(CUIRect MainView) UI()->DoLabelScaled(&Left, Localize("Size:"), 14.0f, -1); unsigned Size = (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[0]<<24) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[1]<<16) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[2]<<8) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[3]); - str_format(aBuf, sizeof(aBuf), Localize("%d Bytes"), Size); + if(Size > 1024*1024) + str_format(aBuf, sizeof(aBuf), Localize("%.2f MiB"), float(Size)/(1024*1024)); + else + str_format(aBuf, sizeof(aBuf), Localize("%.2f KiB"), float(Size)/1024); UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1); Labels.HSplitTop(5.0f, 0, &Labels); Labels.HSplitTop(20.0f, &Left, &Labels); @@ -618,7 +636,11 @@ void CMenus::RenderDemoList(CUIRect MainView) static int s_DemoListId = 0; static float s_ScrollValue = 0; +#if defined(__ANDROID__) + UiDoListboxStart(&s_DemoListId, &ListBox, 50.0f, Localize("Demos"), aFooterLabel, m_lDemos.size(), 1, m_DemolistSelectedIndex, s_ScrollValue); +#else UiDoListboxStart(&s_DemoListId, &ListBox, 17.0f, Localize("Demos"), aFooterLabel, m_lDemos.size(), 1, m_DemolistSelectedIndex, s_ScrollValue); +#endif for(sorted_array::range r = m_lDemos.all(); !r.empty(); r.pop_front()) { CListboxItem Item = UiDoListboxNextItem((void*)(&r.front())); diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index 4eae008379..8cc5cb20bf 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -32,30 +33,46 @@ void CMenus::RenderGame(CUIRect MainView) { CUIRect Button, ButtonBar; +#if defined(__ANDROID__) + MainView.HSplitTop(100.0f, &ButtonBar, &MainView); +#else MainView.HSplitTop(45.0f, &ButtonBar, &MainView); +#endif RenderTools()->DrawUIRect(&ButtonBar, ms_ColorTabbarActive, CUI::CORNER_ALL, 10.0f); // button bar ButtonBar.HSplitTop(10.0f, 0, &ButtonBar); +#if defined(__ANDROID__) + ButtonBar.HSplitTop(80.0f, &ButtonBar, 0); +#else ButtonBar.HSplitTop(25.0f, &ButtonBar, 0); +#endif ButtonBar.VMargin(10.0f, &ButtonBar); ButtonBar.VSplitRight(120.0f, &ButtonBar, &Button); static int s_DisconnectButton = 0; if(DoButton_Menu(&s_DisconnectButton, Localize("Disconnect"), 0, &Button)) - Client()->Disconnect(); + { + if(g_Config.m_ClConfirmDisconnect) + m_Popup = POPUP_DISCONNECT; + else + Client()->Disconnect(); + } if(m_pClient->m_Snap.m_pLocalInfo && m_pClient->m_Snap.m_pGameInfoObj) { if(m_pClient->m_Snap.m_pLocalInfo->m_Team != TEAM_SPECTATORS) { - ButtonBar.VSplitLeft(10.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(5.0f, 0, &ButtonBar); ButtonBar.VSplitLeft(120.0f, &Button, &ButtonBar); static int s_SpectateButton = 0; if(DoButton_Menu(&s_SpectateButton, Localize("Spectate"), 0, &Button)) { - m_pClient->SendSwitchTeam(TEAM_SPECTATORS); - SetActive(false); + if(g_Config.m_ClDummy == 0 || m_pClient->Client()->DummyConnected()) + { + m_pClient->SendSwitchTeam(TEAM_SPECTATORS); + SetActive(false); + } } } @@ -63,7 +80,7 @@ void CMenus::RenderGame(CUIRect MainView) { if(m_pClient->m_Snap.m_pLocalInfo->m_Team != TEAM_RED) { - ButtonBar.VSplitLeft(10.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(5.0f, 0, &ButtonBar); ButtonBar.VSplitLeft(120.0f, &Button, &ButtonBar); static int s_SpectateButton = 0; if(DoButton_Menu(&s_SpectateButton, Localize("Join red"), 0, &Button)) @@ -75,7 +92,7 @@ void CMenus::RenderGame(CUIRect MainView) if(m_pClient->m_Snap.m_pLocalInfo->m_Team != TEAM_BLUE) { - ButtonBar.VSplitLeft(10.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(5.0f, 0, &ButtonBar); ButtonBar.VSplitLeft(120.0f, &Button, &ButtonBar); static int s_SpectateButton = 0; if(DoButton_Menu(&s_SpectateButton, Localize("Join blue"), 0, &Button)) @@ -89,7 +106,7 @@ void CMenus::RenderGame(CUIRect MainView) { if(m_pClient->m_Snap.m_pLocalInfo->m_Team != 0) { - ButtonBar.VSplitLeft(10.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(5.0f, 0, &ButtonBar); ButtonBar.VSplitLeft(120.0f, &Button, &ButtonBar); static int s_SpectateButton = 0; if(DoButton_Menu(&s_SpectateButton, Localize("Join game"), 0, &Button)) @@ -101,7 +118,7 @@ void CMenus::RenderGame(CUIRect MainView) } } - ButtonBar.VSplitLeft(100.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(5.0f, 0, &ButtonBar); ButtonBar.VSplitLeft(150.0f, &Button, &ButtonBar); static int s_DemoButton = 0; @@ -113,11 +130,27 @@ void CMenus::RenderGame(CUIRect MainView) else Client()->DemoRecorder_Stop(); } + + ButtonBar.VSplitLeft(5.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(170.0f, &Button, &ButtonBar); + + static int s_DummyButton = 0; + if(DoButton_Menu(&s_DummyButton, Localize(Client()->DummyConnected() ? "Disconnect dummy" : "Connect dummy"), 0, &Button)) + { + if(!Client()->DummyConnected()) + { + Client()->DummyConnect(); + } + else + { + Client()->DummyDisconnect(0); + } + } } void CMenus::RenderPlayers(CUIRect MainView) { - CUIRect Button, ButtonBar, Options, Player; + CUIRect Button, Button2, ButtonBar, Options, Player; RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_ALL, 10.0f); // player options @@ -151,21 +184,54 @@ void CMenus::RenderPlayers(CUIRect MainView) Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); + int TotalPlayers = 0; + + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(!m_pClient->m_Snap.m_paInfoByTeam[i]) + continue; + + int Index = m_pClient->m_Snap.m_paInfoByTeam[i]->m_ClientID; + + if(Index == m_pClient->m_Snap.m_LocalClientID) + continue; + + TotalPlayers++; + } + + static int s_VoteList = 0; + static float s_ScrollValue = 0; + CUIRect List = Options; + //List.HSplitTop(28.0f, 0, &List); +#if defined(__ANDROID__) + UiDoListboxStart(&s_VoteList, &List, 50.0f, "", "", TotalPlayers, 1, -1, s_ScrollValue); +#else + UiDoListboxStart(&s_VoteList, &List, 24.0f, "", "", TotalPlayers, 1, -1, s_ScrollValue); +#endif + // options static int s_aPlayerIDs[MAX_CLIENTS][2] = {{0}}; + for(int i = 0, Count = 0; i < MAX_CLIENTS; ++i) { if(!m_pClient->m_Snap.m_paInfoByTeam[i]) continue; int Index = m_pClient->m_Snap.m_paInfoByTeam[i]->m_ClientID; + if(Index == m_pClient->m_Snap.m_LocalClientID) continue; - Options.HSplitTop(28.0f, &ButtonBar, &Options); - if(Count++%2 == 0) - RenderTools()->DrawUIRect(&ButtonBar, vec4(1.0f, 1.0f, 1.0f, 0.25f), CUI::CORNER_ALL, 10.0f); - ButtonBar.VSplitRight(220.0f, &Player, &ButtonBar); + CListboxItem Item = UiDoListboxNextItem(&m_pClient->m_aClients[Index]); + + Count++; + + if(!Item.m_Visible) + continue; + + if(Count%2 == 1) + RenderTools()->DrawUIRect(&Item.m_Rect, vec4(1.0f, 1.0f, 1.0f, 0.25f), CUI::CORNER_ALL, 10.0f); + Item.m_Rect.VSplitRight(300.0f, &Player, &Item.m_Rect); // player info Player.VSplitLeft(28.0f, &Button, &Player); @@ -175,6 +241,7 @@ void CMenus::RenderPlayers(CUIRect MainView) Player.HSplitTop(1.5f, 0, &Player); Player.VSplitMid(&Player, &Button); + Item.m_Rect.VSplitRight(200.0f, &Button2, &Item.m_Rect); CTextCursor Cursor; TextRender()->SetCursor(&Cursor, Player.x, Player.y, 14.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = Player.w; @@ -184,20 +251,26 @@ void CMenus::RenderPlayers(CUIRect MainView) Cursor.m_LineWidth = Button.w; TextRender()->TextEx(&Cursor, m_pClient->m_aClients[Index].m_aClan, -1); + //TextRender()->SetCursor(&Cursor, Button2.x,Button2.y, 14.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + //Cursor.m_LineWidth = Button.w; + vec4 Color(1.0f, 1.0f, 1.0f, 0.5f); + m_pClient->m_pCountryFlags->Render(m_pClient->m_aClients[Index].m_Country, &Color, + Button2.x, Button2.y + Button2.h/2.0f - 0.75*Button2.h/2.0f, 1.5f*Button2.h, 0.75f*Button2.h); + // ignore button - ButtonBar.HMargin(2.0f, &ButtonBar); - ButtonBar.VSplitLeft(Width, &Button, &ButtonBar); + Item.m_Rect.HMargin(2.0f, &Item.m_Rect); + Item.m_Rect.VSplitLeft(Width, &Button, &Item.m_Rect); Button.VSplitLeft((Width-Button.h)/4.0f, 0, &Button); Button.VSplitLeft(Button.h, &Button, 0); - if(&g_Config.m_ClShowChatFriends && !m_pClient->m_aClients[Index].m_Friend) + if(g_Config.m_ClShowChatFriends && !m_pClient->m_aClients[Index].m_Friend) DoButton_Toggle(&s_aPlayerIDs[Index][0], 1, &Button, false); else if(DoButton_Toggle(&s_aPlayerIDs[Index][0], m_pClient->m_aClients[Index].m_ChatIgnore, &Button, true)) m_pClient->m_aClients[Index].m_ChatIgnore ^= 1; // friend button - ButtonBar.VSplitLeft(20.0f, &Button, &ButtonBar); - ButtonBar.VSplitLeft(Width, &Button, &ButtonBar); + Item.m_Rect.VSplitLeft(20.0f, &Button, &Item.m_Rect); + Item.m_Rect.VSplitLeft(Width, &Button, &Item.m_Rect); Button.VSplitLeft((Width-Button.h)/4.0f, 0, &Button); Button.VSplitLeft(Button.h, &Button, 0); if(DoButton_Toggle(&s_aPlayerIDs[Index][1], m_pClient->m_aClients[Index].m_Friend, &Button, true)) @@ -209,6 +282,7 @@ void CMenus::RenderPlayers(CUIRect MainView) } } + UiDoListboxEnd(&s_ScrollValue, 0); /* CUIRect bars; votearea.HSplitTop(10.0f, 0, &votearea); @@ -306,7 +380,7 @@ void CMenus::RenderServerInfo(CUIRect MainView) "%s: %s\n" "%s: %s\n", CurrentServerInfo.m_aName, - Localize("Address"), g_Config.m_UiServerAddress, + Localize("Address"), CurrentServerInfo.m_aAddress, Localize("Ping"), m_pClient->m_Snap.m_pLocalInfo->m_Latency, Localize("Version"), CurrentServerInfo.m_aVersion, Localize("Password"), CurrentServerInfo.m_Flags &1 ? Localize("Yes") : Localize("No") @@ -378,7 +452,11 @@ void CMenus::RenderServerControlServer(CUIRect MainView) static int s_VoteList = 0; static float s_ScrollValue = 0; CUIRect List = MainView; +#if defined(__ANDROID__) + UiDoListboxStart(&s_VoteList, &List, 50.0f, "", "", m_pClient->m_pVoting->m_NumVoteOptions, 1, m_CallvoteSelectedOption, s_ScrollValue); +#else UiDoListboxStart(&s_VoteList, &List, 24.0f, "", "", m_pClient->m_pVoting->m_NumVoteOptions, 1, m_CallvoteSelectedOption, s_ScrollValue); +#endif for(CVoteOptionClient *pOption = m_pClient->m_pVoting->m_pFirst; pOption; pOption = pOption->m_pNext) { @@ -412,7 +490,11 @@ void CMenus::RenderServerControlKick(CUIRect MainView, bool FilterSpectators) static int s_VoteList = 0; static float s_ScrollValue = 0; CUIRect List = MainView; +#if defined(__ANDROID__) + UiDoListboxStart(&s_VoteList, &List, 50.0f, "", "", NumOptions, 1, Selected, s_ScrollValue); +#else UiDoListboxStart(&s_VoteList, &List, 24.0f, "", "", NumOptions, 1, Selected, s_ScrollValue); +#endif for(int i = 0; i < NumOptions; i++) { @@ -439,12 +521,24 @@ void CMenus::RenderServerControl(CUIRect MainView) // render background CUIRect Bottom, Extended, TabBar, Button; +#if defined(__ANDROID__) + MainView.HSplitTop(50.0f, &Bottom, &MainView); +#else MainView.HSplitTop(20.0f, &Bottom, &MainView); +#endif RenderTools()->DrawUIRect(&Bottom, ms_ColorTabbarActive, CUI::CORNER_T, 10.0f); +#if defined(__ANDROID__) + MainView.HSplitTop(50.0f, &TabBar, &MainView); +#else MainView.HSplitTop(20.0f, &TabBar, &MainView); +#endif RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_B, 10.0f); MainView.Margin(10.0f, &MainView); +#if defined(__ANDROID__) + MainView.HSplitBottom(10.0f, &MainView, &Extended); +#else MainView.HSplitBottom(90.0f, &MainView, &Extended); +#endif // tab bar { diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 9cda4b2c51..55b891ed23 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -54,8 +55,9 @@ bool CMenusKeyBinder::OnInput(IInput::CEvent Event) void CMenus::RenderSettingsGeneral(CUIRect MainView) { char aBuf[128]; - CUIRect Label, Button, Left, Right, Game, Client; - MainView.HSplitTop(150.0f, &Game, &Client); + CUIRect Label, Button, Left, Right, Game, Client, AutoReconnect; + MainView.HSplitTop(180.0f, &Game, &Client); + Client.HSplitTop(165.0f, &Client, &AutoReconnect); // game { @@ -92,6 +94,12 @@ void CMenus::RenderSettingsGeneral(CUIRect MainView) if(DoButton_CheckBox(&g_Config.m_ClAutoswitchWeapons, Localize("Switch weapon on pickup"), g_Config.m_ClAutoswitchWeapons, &Button)) g_Config.m_ClAutoswitchWeapons ^= 1; + // weapon out of ammo autoswitch + Left.HSplitTop(5.0f, 0, &Left); + Left.HSplitTop(20.0f, &Button, &Left); + if(DoButton_CheckBox(&g_Config.m_ClAutoswitchWeaponsOutOfAmmo, Localize("Switch weapon when out of ammo"), g_Config.m_ClAutoswitchWeaponsOutOfAmmo, &Button)) + g_Config.m_ClAutoswitchWeaponsOutOfAmmo ^= 1; + // show hud Left.HSplitTop(5.0f, 0, &Left); Left.HSplitTop(20.0f, &Button, &Left); @@ -178,24 +186,128 @@ void CMenus::RenderSettingsGeneral(CUIRect MainView) Button.HMargin(2.0f, &Button); g_Config.m_ClAutoScreenshotMax = static_cast(DoScrollbarH(&g_Config.m_ClAutoScreenshotMax, &Button, g_Config.m_ClAutoScreenshotMax/1000.0f)*1000.0f+0.1f); } + + Right.HSplitTop(20.0f, &Button, &Right); + Right.HSplitTop(20.0f, &Button, &Right); + if(DoButton_CheckBox(&g_Config.m_ClConfirmDisconnect, Localize("Confirm disconnect from server"), g_Config.m_ClConfirmDisconnect, &Button)) + g_Config.m_ClConfirmDisconnect ^= 1; + + + Left.HSplitTop(20.0f, 0, &Left); + Left.HSplitTop(20.0f, &Label, &Left); + Button.VSplitRight(20.0f, &Button, 0); + char aBuf[64]; + if(g_Config.m_ClCpuThrottle) + str_format(aBuf, sizeof(aBuf), "%s: %i", Localize("CPU Throttle"), g_Config.m_ClCpuThrottle); + else + str_format(aBuf, sizeof(aBuf), "%s: %s", Localize("CPU Throttle"), Localize("none")); + UI()->DoLabelScaled(&Label, aBuf, 13.0f, -1); + Left.HSplitTop(20.0f, &Button, 0); + Button.HMargin(2.0f, &Button); + g_Config.m_ClCpuThrottle= static_cast(DoScrollbarH(&g_Config.m_ClCpuThrottle, &Button, g_Config.m_ClCpuThrottle/100.0f)*100.0f+0.1f); + } + + AutoReconnect.HSplitTop(30.0f, &Label, &AutoReconnect); + + UI()->DoLabelScaled(&Label, Localize("Reconnecting"), 20.0f, -1); + + AutoReconnect.Margin(5.0f, &AutoReconnect); + AutoReconnect.VSplitMid(&Left, &Right); + Left.VSplitRight(5.0f, &Left, 0); + Right.VMargin(5.0f, &Right); + + { + Right.HSplitTop(20.0f, &Button, &Right); + if (DoButton_CheckBox(&g_Config.m_ClReconnectFull, Localize("Reconnect when server is full"), g_Config.m_ClReconnectFull, &Button)) + { + g_Config.m_ClReconnectFull ^= 1; + } + + Left.HSplitTop(20.0f, &Button, &Left); + if (DoButton_CheckBox(&g_Config.m_ClReconnectBan, Localize("Reconnect when you are banned"), g_Config.m_ClReconnectBan, &Button)) + { + g_Config.m_ClReconnectBan ^= 1; + } + + Left.HSplitTop(10.0f, 0, &Left); + Left.VSplitLeft(20.0f, 0, &Left); + Left.HSplitTop(20.0f, &Label, &Left); + Button.VSplitRight(20.0f, &Button, 0); + char aBuf[64]; + if (g_Config.m_ClReconnectBanTimeout == 1) + { + str_format(aBuf, sizeof(aBuf), "%s %i %s", Localize("Wait before try for"), g_Config.m_ClReconnectBanTimeout, Localize("second")); + } + else + { + str_format(aBuf, sizeof(aBuf), "%s %i %s", Localize("Wait before try for"), g_Config.m_ClReconnectBanTimeout, Localize("seconds")); + } + UI()->DoLabelScaled(&Label, aBuf, 13.0f, -1); + Left.HSplitTop(20.0f, &Button, 0); + Button.HMargin(2.0f, &Button); + g_Config.m_ClReconnectBanTimeout = static_cast(DoScrollbarH(&g_Config.m_ClReconnectBanTimeout, &Button, g_Config.m_ClReconnectBanTimeout / 120.0f) * 120.0f); + if (g_Config.m_ClReconnectBanTimeout < 5) + g_Config.m_ClReconnectBanTimeout = 5; + Right.HSplitTop(10.0f, 0, &Right); + Right.VSplitLeft(20.0f, 0, &Right); + Right.HSplitTop(20.0f, &Label, &Right); + Button.VSplitRight(20.0f, &Button, 0); + if (g_Config.m_ClReconnectFullTimeout == 1) + { + str_format(aBuf, sizeof(aBuf), "%s %i %s", Localize("Wait before try for"), g_Config.m_ClReconnectFullTimeout, Localize("second")); + } + else + { + str_format(aBuf, sizeof(aBuf), "%s %i %s", Localize("Wait before try for"), g_Config.m_ClReconnectFullTimeout, Localize("seconds")); + } + UI()->DoLabelScaled(&Label, aBuf, 13.0f, -1); + Right.HSplitTop(20.0f, &Button, 0); + Button.HMargin(2.0f, &Button); + g_Config.m_ClReconnectFullTimeout = static_cast(DoScrollbarH(&g_Config.m_ClReconnectFullTimeout, &Button, g_Config.m_ClReconnectFullTimeout / 120.0f) * 120.0f); + if (g_Config.m_ClReconnectFullTimeout < 1) + g_Config.m_ClReconnectFullTimeout = 1; } } void CMenus::RenderSettingsPlayer(CUIRect MainView) { - CUIRect Button, Label; + CUIRect Button, Label, Dummy; MainView.HSplitTop(10.0f, 0, &MainView); + static bool s_Dummy = false; + + char *Name = g_Config.m_PlayerName; + char *Clan = g_Config.m_PlayerClan; + int *Country = &g_Config.m_PlayerCountry; + + if(s_Dummy) + { + Name = g_Config.m_DummyName; + Clan = g_Config.m_DummyClan; + Country = &g_Config.m_DummyCountry; + } + // player name MainView.HSplitTop(20.0f, &Button, &MainView); Button.VSplitLeft(80.0f, &Label, &Button); + Button.VSplitLeft(200.0f, &Button, &Dummy); Button.VSplitLeft(150.0f, &Button, 0); char aBuf[128]; str_format(aBuf, sizeof(aBuf), "%s:", Localize("Name")); UI()->DoLabelScaled(&Label, aBuf, 14.0, -1); static float s_OffsetName = 0.0f; - if(DoEditBox(g_Config.m_PlayerName, &Button, g_Config.m_PlayerName, sizeof(g_Config.m_PlayerName), 14.0f, &s_OffsetName)) - m_NeedSendinfo = true; + if(DoEditBox(Name, &Button, Name, sizeof(g_Config.m_PlayerName), 14.0f, &s_OffsetName)) + { + if(s_Dummy) + m_NeedSendDummyinfo = true; + else + m_NeedSendinfo = true; + } + + if(DoButton_CheckBox(&g_Config.m_ClShowKillMessages, Localize("Dummy settings"), s_Dummy, &Dummy)) + { + s_Dummy ^= 1; + } // player clan MainView.HSplitTop(5.0f, 0, &MainView); @@ -205,8 +317,13 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) str_format(aBuf, sizeof(aBuf), "%s:", Localize("Clan")); UI()->DoLabelScaled(&Label, aBuf, 14.0, -1); static float s_OffsetClan = 0.0f; - if(DoEditBox(g_Config.m_PlayerClan, &Button, g_Config.m_PlayerClan, sizeof(g_Config.m_PlayerClan), 14.0f, &s_OffsetClan)) - m_NeedSendinfo = true; + if(DoEditBox(Clan, &Button, Clan, sizeof(g_Config.m_PlayerClan), 14.0f, &s_OffsetClan)) + { + if(s_Dummy) + m_NeedSendDummyinfo = true; + else + m_NeedSendinfo = true; + } // country flag selector MainView.HSplitTop(20.0f, 0, &MainView); @@ -217,7 +334,7 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) for(int i = 0; i < m_pClient->m_pCountryFlags->Num(); ++i) { const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_pCountryFlags->GetByIndex(i); - if(pEntry->m_CountryCode == g_Config.m_PlayerCountry) + if(pEntry->m_CountryCode == *Country) OldSelected = i; CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected == i); @@ -239,24 +356,42 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0); if(OldSelected != NewSelected) { - g_Config.m_PlayerCountry = m_pClient->m_pCountryFlags->GetByIndex(NewSelected)->m_CountryCode; - m_NeedSendinfo = true; + *Country = m_pClient->m_pCountryFlags->GetByIndex(NewSelected)->m_CountryCode; + if(s_Dummy) + m_NeedSendDummyinfo = true; + else + m_NeedSendinfo = true; } } void CMenus::RenderSettingsTee(CUIRect MainView) { - CUIRect Button, Label; + CUIRect Button, Label, Button2, Dummy; + static bool s_InitSkinlist = true; MainView.HSplitTop(10.0f, 0, &MainView); + static bool s_Dummy = false; + char *Skin = g_Config.m_PlayerSkin; + int *UseCustomColor = &g_Config.m_PlayerUseCustomColor; + int *ColorBody = &g_Config.m_PlayerColorBody; + int *ColorFeet = &g_Config.m_PlayerColorFeet; + + if(s_Dummy) + { + Skin = g_Config.m_DummySkin; + UseCustomColor = &g_Config.m_DummyUseCustomColor; + ColorBody = &g_Config.m_DummyColorBody; + ColorFeet = &g_Config.m_DummyColorFeet; + } + // skin info - const CSkins::CSkin *pOwnSkin = m_pClient->m_pSkins->Get(m_pClient->m_pSkins->Find(g_Config.m_PlayerSkin)); + const CSkins::CSkin *pOwnSkin = m_pClient->m_pSkins->Get(m_pClient->m_pSkins->Find(Skin)); CTeeRenderInfo OwnSkinInfo; - if(g_Config.m_PlayerUseCustomColor) + if(*UseCustomColor) { OwnSkinInfo.m_Texture = pOwnSkin->m_ColorTexture; - OwnSkinInfo.m_ColorBody = m_pClient->m_pSkins->GetColorV4(g_Config.m_PlayerColorBody); - OwnSkinInfo.m_ColorFeet = m_pClient->m_pSkins->GetColorV4(g_Config.m_PlayerColorFeet); + OwnSkinInfo.m_ColorBody = m_pClient->m_pSkins->GetColorV4(*ColorBody); + OwnSkinInfo.m_ColorFeet = m_pClient->m_pSkins->GetColorV4(*ColorFeet); } else { @@ -267,32 +402,46 @@ void CMenus::RenderSettingsTee(CUIRect MainView) OwnSkinInfo.m_Size = 50.0f*UI()->Scale(); MainView.HSplitTop(20.0f, &Label, &MainView); + Label.VSplitLeft(280.0f, &Label, &Dummy); Label.VSplitLeft(230.0f, &Label, 0); char aBuf[128]; str_format(aBuf, sizeof(aBuf), "%s:", Localize("Your skin")); UI()->DoLabelScaled(&Label, aBuf, 14.0f, -1); + if(DoButton_CheckBox(&g_Config.m_ClShowKillMessages, Localize("Dummy settings"), s_Dummy, &Dummy)) + { + s_Dummy ^= 1; + } + MainView.HSplitTop(50.0f, &Label, &MainView); Label.VSplitLeft(230.0f, &Label, 0); RenderTools()->DrawUIRect(&Label, vec4(1.0f, 1.0f, 1.0f, 0.25f), CUI::CORNER_ALL, 10.0f); RenderTools()->RenderTee(CAnimState::GetIdle(), &OwnSkinInfo, 0, vec2(1, 0), vec2(Label.x+30.0f, Label.y+28.0f)); Label.HSplitTop(15.0f, 0, &Label);; Label.VSplitLeft(70.0f, 0, &Label); - UI()->DoLabelScaled(&Label, g_Config.m_PlayerSkin, 14.0f, -1, 150.0f); + UI()->DoLabelScaled(&Label, Skin, 14.0f, -1, 150.0f); // custom colour selector MainView.HSplitTop(20.0f, 0, &MainView); MainView.HSplitTop(20.0f, &Button, &MainView); - Button.VSplitLeft(230.0f, &Button, 0); - if(DoButton_CheckBox(&g_Config.m_PlayerColorBody, Localize("Custom colors"), g_Config.m_PlayerUseCustomColor, &Button)) + Button.VSplitMid(&Button, &Button2); + if(DoButton_CheckBox(&ColorBody, Localize("Custom colors"), *UseCustomColor, &Button)) { - g_Config.m_PlayerUseCustomColor = g_Config.m_PlayerUseCustomColor?0:1; - m_NeedSendinfo = true; + *UseCustomColor = *UseCustomColor?0:1; + if(s_Dummy) + m_NeedSendDummyinfo = true; + else + m_NeedSendinfo = true; + } + if(DoButton_CheckBox(&g_Config.m_ClShowSpecialSkins, Localize("Show Bandana Brothers skins"), g_Config.m_ClShowSpecialSkins, &Button2)) + { + g_Config.m_ClShowSpecialSkins = g_Config.m_ClShowSpecialSkins?0:1; + s_InitSkinlist = true; } MainView.HSplitTop(5.0f, 0, &MainView); MainView.HSplitTop(82.5f, &Label, &MainView); - if(g_Config.m_PlayerUseCustomColor) + if(*UseCustomColor) { CUIRect aRects[2]; Label.VSplitMid(&aRects[0], &aRects[1]); @@ -300,8 +449,8 @@ void CMenus::RenderSettingsTee(CUIRect MainView) aRects[1].VSplitLeft(10.0f, 0, &aRects[1]); int *paColors[2]; - paColors[0] = &g_Config.m_PlayerColorBody; - paColors[1] = &g_Config.m_PlayerColorFeet; + paColors[0] = ColorBody; + paColors[1] = ColorFeet; const char *paParts[] = { Localize("Body"), @@ -335,7 +484,12 @@ void CMenus::RenderSettingsTee(CUIRect MainView) } if(PrevColor != Color) - m_NeedSendinfo = true; + { + if(s_Dummy) + m_NeedSendDummyinfo = true; + else + m_NeedSendinfo = true; + } *paColors[i] = Color; } @@ -343,7 +497,6 @@ void CMenus::RenderSettingsTee(CUIRect MainView) // skin selector MainView.HSplitTop(20.0f, 0, &MainView); - static bool s_InitSkinlist = true; static sorted_array s_paSkinList; static float s_ScrollValue = 0.0f; if(s_InitSkinlist) @@ -353,7 +506,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) { const CSkins::CSkin *s = m_pClient->m_pSkins->Get(i); // no special skins - if(s->m_aName[0] == 'x' && s->m_aName[1] == '_') + if((s->m_aName[0] == 'x' && s->m_aName[1] == '_') || (!g_Config.m_ClShowSpecialSkins && s->m_aName[0] == '0')) continue; s_paSkinList.add(s); } @@ -369,18 +522,18 @@ void CMenus::RenderSettingsTee(CUIRect MainView) if(s == 0) continue; - if(str_comp(s->m_aName, g_Config.m_PlayerSkin) == 0) + if(str_comp(s->m_aName, Skin) == 0) OldSelected = i; CListboxItem Item = UiDoListboxNextItem(&s_paSkinList[i], OldSelected == i); if(Item.m_Visible) { CTeeRenderInfo Info; - if(g_Config.m_PlayerUseCustomColor) + if(*UseCustomColor) { Info.m_Texture = s->m_ColorTexture; - Info.m_ColorBody = m_pClient->m_pSkins->GetColorV4(g_Config.m_PlayerColorBody); - Info.m_ColorFeet = m_pClient->m_pSkins->GetColorV4(g_Config.m_PlayerColorFeet); + Info.m_ColorBody = m_pClient->m_pSkins->GetColorV4(*ColorBody); + Info.m_ColorFeet = m_pClient->m_pSkins->GetColorV4(*ColorFeet); } else { @@ -395,7 +548,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) if(g_Config.m_Debug) { - vec3 BloodColor = g_Config.m_PlayerUseCustomColor ? m_pClient->m_pSkins->GetColorV3(g_Config.m_PlayerColorBody) : s->m_BloodColor; + vec3 BloodColor = *UseCustomColor ? m_pClient->m_pSkins->GetColorV3(*ColorBody) : s->m_BloodColor; Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(BloodColor.r, BloodColor.g, BloodColor.b, 1.0f); @@ -409,8 +562,11 @@ void CMenus::RenderSettingsTee(CUIRect MainView) const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0); if(OldSelected != NewSelected) { - mem_copy(g_Config.m_PlayerSkin, s_paSkinList[NewSelected]->m_aName, sizeof(g_Config.m_PlayerSkin)); - m_NeedSendinfo = true; + mem_copy(Skin, s_paSkinList[NewSelected]->m_aName, sizeof(g_Config.m_PlayerSkin)); + if(s_Dummy) + m_NeedSendDummyinfo = true; + else + m_NeedSendinfo = true; } } @@ -431,6 +587,7 @@ static CKeyInfo gs_aKeys[] = { "Jump", "+jump", 0 }, { "Fire", "+fire", 0 }, { "Hook", "+hook", 0 }, + { "Hook Collisions", "+showhookcoll", 0 }, { "Hammer", "+weapon1", 0 }, { "Pistol", "+weapon2", 0 }, { "Shotgun", "+weapon3", 0 }, @@ -442,6 +599,7 @@ static CKeyInfo gs_aKeys[] = { "Vote no", "vote no", 0 }, { "Chat", "chat all", 0 }, { "Team chat", "chat team", 0 }, + { "Converse", "chat all /c ", 0 }, { "Show chat", "+show_chat", 0 }, { "Emoticon", "+emote", 0 }, { "Spectator mode", "+spectate", 0 }, @@ -452,6 +610,8 @@ static CKeyInfo gs_aKeys[] = { "Screenshot", "screenshot", 0 }, { "Scoreboard", "+scoreboard", 0 }, { "Respawn", "kill", 0 }, + { "Toggle Dummy", "toggle cl_dummy 0 1", 0 }, + { "Hammerfly Dummy", "toggle cl_dummy_hammer 0 1", 0 }, }; /* This is for scripts/update_localization.py to work, don't remove! @@ -534,7 +694,7 @@ void CMenus::RenderSettingsControls(CUIRect MainView) MovementSettings.HSplitTop(20.0f, 0, &MovementSettings); } - UiDoGetButtons(0, 5, MovementSettings); + UiDoGetButtons(0, 6, MovementSettings); } @@ -548,7 +708,7 @@ void CMenus::RenderSettingsControls(CUIRect MainView) TextRender()->Text(0, WeaponSettings.x, WeaponSettings.y, 14.0f*UI()->Scale(), Localize("Weapon"), -1); WeaponSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &WeaponSettings); - UiDoGetButtons(5, 12, WeaponSettings); + UiDoGetButtons(6, 13, WeaponSettings); } // defaults @@ -566,14 +726,14 @@ void CMenus::RenderSettingsControls(CUIRect MainView) // voting settings { VotingSettings.VMargin(5.0f, &VotingSettings); - VotingSettings.HSplitTop(MainView.h/3-75.0f, &VotingSettings, &ChatSettings); + VotingSettings.HSplitTop(MainView.h/3-95.0f, &VotingSettings, &ChatSettings); RenderTools()->DrawUIRect(&VotingSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - VotingSettings.Margin(10.0f, &VotingSettings); + VotingSettings.VMargin(10.0f, &VotingSettings); TextRender()->Text(0, VotingSettings.x, VotingSettings.y, 14.0f*UI()->Scale(), Localize("Voting"), -1); VotingSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &VotingSettings); - UiDoGetButtons(12, 14, VotingSettings); + UiDoGetButtons(13, 15, VotingSettings); } // chat settings @@ -581,24 +741,24 @@ void CMenus::RenderSettingsControls(CUIRect MainView) ChatSettings.HSplitTop(10.0f, 0, &ChatSettings); ChatSettings.HSplitTop(MainView.h/3-45.0f, &ChatSettings, &MiscSettings); RenderTools()->DrawUIRect(&ChatSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - ChatSettings.Margin(10.0f, &ChatSettings); + ChatSettings.VMargin(10.0f, &ChatSettings); TextRender()->Text(0, ChatSettings.x, ChatSettings.y, 14.0f*UI()->Scale(), Localize("Chat"), -1); ChatSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &ChatSettings); - UiDoGetButtons(14, 17, ChatSettings); + UiDoGetButtons(15, 19, ChatSettings); } // misc settings { MiscSettings.HSplitTop(10.0f, 0, &MiscSettings); RenderTools()->DrawUIRect(&MiscSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - MiscSettings.Margin(10.0f, &MiscSettings); + MiscSettings.VMargin(10.0f, &MiscSettings); TextRender()->Text(0, MiscSettings.x, MiscSettings.y, 14.0f*UI()->Scale(), Localize("Miscellaneous"), -1); MiscSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &MiscSettings); - UiDoGetButtons(17, 26, MiscSettings); + UiDoGetButtons(19, 30, MiscSettings); } } @@ -615,6 +775,7 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView) static int s_GfxScreenWidth = g_Config.m_GfxScreenWidth; static int s_GfxScreenHeight = g_Config.m_GfxScreenHeight; static int s_GfxColorDepth = g_Config.m_GfxColorDepth; + static int s_GfxBorderless = g_Config.m_GfxBorderless; static int s_GfxFullscreen = g_Config.m_GfxFullscreen; static int s_GfxVsync = g_Config.m_GfxVsync; static int s_GfxFsaaSamples = g_Config.m_GfxFsaaSamples; @@ -669,10 +830,21 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView) } // switches + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_GfxBorderless, Localize("Borderless window"), g_Config.m_GfxBorderless, &Button)) + { + g_Config.m_GfxBorderless ^= 1; + if(g_Config.m_GfxBorderless && g_Config.m_GfxFullscreen) + g_Config.m_GfxFullscreen = 0; + CheckSettings = true; + } + MainView.HSplitTop(20.0f, &Button, &MainView); if(DoButton_CheckBox(&g_Config.m_GfxFullscreen, Localize("Fullscreen"), g_Config.m_GfxFullscreen, &Button)) { g_Config.m_GfxFullscreen ^= 1; + if(g_Config.m_GfxFullscreen && g_Config.m_GfxBorderless) + g_Config.m_GfxBorderless = 0; CheckSettings = true; } @@ -689,8 +861,7 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView) g_Config.m_GfxFsaaSamples = (g_Config.m_GfxFsaaSamples+1)%17; CheckSettings = true; } - - MainView.HSplitTop(40.0f, &Button, &MainView); + MainView.HSplitTop(20.0f, &Button, &MainView); if(DoButton_CheckBox(&g_Config.m_GfxTextureQuality, Localize("Quality Textures"), g_Config.m_GfxTextureQuality, &Button)) { @@ -715,6 +886,7 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView) if(s_GfxScreenWidth == g_Config.m_GfxScreenWidth && s_GfxScreenHeight == g_Config.m_GfxScreenHeight && s_GfxColorDepth == g_Config.m_GfxColorDepth && + s_GfxBorderless == g_Config.m_GfxBorderless && s_GfxFullscreen == g_Config.m_GfxFullscreen && s_GfxVsync == g_Config.m_GfxVsync && s_GfxFsaaSamples == g_Config.m_GfxFsaaSamples && @@ -783,16 +955,43 @@ void CMenus::RenderSettingsSound(CUIRect MainView) if(DoButton_CheckBox(&g_Config.m_SndMusic, Localize("Play background music"), g_Config.m_SndMusic, &Button)) { g_Config.m_SndMusic ^= 1; - if(g_Config.m_SndMusic) - m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f); - else - m_pClient->m_pSounds->Stop(SOUND_MENU); + if(Client()->State() == IClient::STATE_OFFLINE) + { + if(g_Config.m_SndMusic) + m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f); + else + m_pClient->m_pSounds->Stop(SOUND_MENU); + } } MainView.HSplitTop(20.0f, &Button, &MainView); if(DoButton_CheckBox(&g_Config.m_SndNonactiveMute, Localize("Mute when not active"), g_Config.m_SndNonactiveMute, &Button)) g_Config.m_SndNonactiveMute ^= 1; + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_SndGame, Localize("Enable game sounds"), g_Config.m_SndGame, &Button)) + g_Config.m_SndGame ^= 1; + + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_SndServerMessage, Localize("Enable server message sound"), g_Config.m_SndServerMessage, &Button)) + g_Config.m_SndServerMessage ^= 1; + + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_SndChat, Localize("Enable regular chat sound"), g_Config.m_SndChat, &Button)) + g_Config.m_SndChat ^= 1; + + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_SndTeamChat, Localize("Enable team chat sound"), g_Config.m_SndTeamChat, &Button)) + g_Config.m_SndTeamChat ^= 1; + + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_SndHighlight, Localize("Enable highlighted chat sound"), g_Config.m_SndHighlight, &Button)) + g_Config.m_SndHighlight ^= 1; + + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_ClThreadsoundloading, Localize("Threaded sound loading"), g_Config.m_ClThreadsoundloading, &Button)) + g_Config.m_ClThreadsoundloading ^= 1; + // sample rate box { char aBuf[64]; @@ -913,7 +1112,11 @@ void CMenus::RenderLanguageSelection(CUIRect MainView) int OldSelected = s_SelectedLanguage; +#if defined(__ANDROID__) + UiDoListboxStart(&s_LanguageList , &MainView, 50.0f, Localize("Language"), "", s_Languages.size(), 1, s_SelectedLanguage, s_ScrollValue); +#else UiDoListboxStart(&s_LanguageList , &MainView, 24.0f, Localize("Language"), "", s_Languages.size(), 1, s_SelectedLanguage, s_ScrollValue); +#endif for(sorted_array::range r = s_Languages.all(); !r.empty(); r.pop_front()) { @@ -962,10 +1165,12 @@ void CMenus::RenderSettings(CUIRect MainView) Localize("General"), Localize("Player"), ("Tee"), + Localize("HUD"), Localize("Controls"), Localize("Graphics"), Localize("Sound"), - Localize("DDRace")}; + Localize("DDNet") + }; int NumTabs = (int)(sizeof(aTabs)/sizeof(*aTabs)); @@ -988,87 +1193,663 @@ void CMenus::RenderSettings(CUIRect MainView) else if(s_SettingsPage == 3) RenderSettingsTee(MainView); else if(s_SettingsPage == 4) - RenderSettingsControls(MainView); + RenderSettingsHUD(MainView); else if(s_SettingsPage == 5) - RenderSettingsGraphics(MainView); + RenderSettingsControls(MainView); else if(s_SettingsPage == 6) - RenderSettingsSound(MainView); + RenderSettingsGraphics(MainView); else if(s_SettingsPage == 7) + RenderSettingsSound(MainView); + else if(s_SettingsPage == 8) RenderSettingsDDRace(MainView); if(m_NeedRestartGraphics || m_NeedRestartSound) UI()->DoLabel(&RestartWarning, Localize("You must restart the game for all settings to take effect."), 15.0f, -1); } +void CMenus::RenderSettingsHUD(CUIRect MainView) +{ + CUIRect Left, Right, HUD, Messages, Button, Label, Weapon, Laser; -void CMenus::RenderSettingsDDRace(CUIRect MainView) + MainView.HSplitTop(130.0f, &HUD, &MainView); -{ - CUIRect Button; - MainView.VSplitLeft(300.0f, &MainView, 0); + HUD.HSplitTop(30.0f, &Label, &HUD); + UI()->DoLabelScaled(&Label, Localize("HUD"), 20.0f, -1); + HUD.Margin(5.0f, &HUD); + HUD.VSplitMid(&Left, &Right); + Left.VSplitRight(5.0f, &Left, 0); + Right.VMargin(5.0f, &Right); - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_ClDDRaceScoreBoard, Localize("Use DDRace Scoreboard"), g_Config.m_ClDDRaceScoreBoard, &Button)) + Left.HSplitTop(20.0f, &Button, &Left); + if (DoButton_CheckBox(&g_Config.m_ClDDRaceScoreBoard, Localize("Use DDRace Scoreboard"), g_Config.m_ClDDRaceScoreBoard, &Button)) { g_Config.m_ClDDRaceScoreBoard ^= 1; } - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_ClShowIDs, Localize("Show client IDs in Scoreboard"), g_Config.m_ClShowIDs, &Button)) + Left.HSplitTop(20.0f, &Button, &Left); + if (DoButton_CheckBox(&g_Config.m_ClShowIDs, Localize("Show client IDs in Scoreboard"), g_Config.m_ClShowIDs, &Button)) { g_Config.m_ClShowIDs ^= 1; } - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_ClDDRaceCheats, Localize("Enable DDRace cheats like zoom"), g_Config.m_ClDDRaceCheats, &Button)) + Right.HSplitTop(20.0f, &Button, &Right); + if (DoButton_CheckBox(&g_Config.m_ClShowhudScore, Localize("Show score"), g_Config.m_ClShowhudScore, &Button)) { - g_Config.m_ClDDRaceCheats ^= 1; + g_Config.m_ClShowhudScore ^= 1; } - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_ClShowEntities, Localize("Cheat: Shows entities in game (can also be toggled via console cl_show_entities)"), g_Config.m_ClShowEntities && g_Config.m_ClDDRaceCheats, &Button)) + Right.HSplitTop(20.0f, &Button, &Right); + if (DoButton_CheckBox(&g_Config.m_ClShowhudHealthAmmo, Localize("Show health + ammo"), g_Config.m_ClShowhudHealthAmmo, &Button)) { - g_Config.m_ClShowEntities ^= 1; + g_Config.m_ClShowhudHealthAmmo ^= 1; } - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_ClAutoRaceRecord, Localize("Enable save the best demo of each race"), g_Config.m_ClAutoRaceRecord, &Button)) + Left.HSplitTop(20.0f, &Button, &Left); + if (DoButton_CheckBox(&g_Config.m_ClShowChat, Localize("Show chat"), g_Config.m_ClShowChat, &Button)) + { + g_Config.m_ClShowChat ^= 1; + } + + Right.HSplitTop(20.0f, &Button, &Right); + if (DoButton_CheckBox(&g_Config.m_ClChatTeamColors, Localize("Show names in chat in team colors"), g_Config.m_ClChatTeamColors, &Button)) + { + g_Config.m_ClChatTeamColors ^= 1; + } + + Left.HSplitTop(20.0f, &Button, &Left); + if (DoButton_CheckBox(&g_Config.m_ClShowKillMessages, Localize("Show kill messages"), g_Config.m_ClShowKillMessages, &Button)) + { + g_Config.m_ClShowKillMessages ^= 1; + } + + Right.HSplitTop(20.0f, &Button, &Right); + if (DoButton_CheckBox(&g_Config.m_ClShowVotesAfterVoting, Localize("Show votes window after voting"), g_Config.m_ClShowVotesAfterVoting, &Button)) + { + g_Config.m_ClShowVotesAfterVoting ^= 1; + } + MainView.HSplitTop(130.0f, &Messages, &MainView); + Messages.HSplitTop(30.0f, &Label, &Messages); + Label.VSplitMid(&Label, &Button); + UI()->DoLabelScaled(&Label, Localize("Messages"), 20.0f, -1); + Messages.Margin(5.0f, &Messages); + Messages.VSplitMid(&Left, &Right); + Left.VSplitRight(5.0f, &Left, 0); + Right.VMargin(5.0f, &Right); + { + char aBuf[64]; + Left.HSplitTop(20.0f, &Label, &Left); + Label.VSplitRight(50.0f, &Label, &Button); + UI()->DoLabelScaled(&Label, Localize("System message"), 16.0f, -1); + { + static int s_DefaultButton = 0; + if (DoButton_Menu(&s_DefaultButton, Localize("Reset"), 0, &Button)){ + vec3 HSL = RgbToHsl(vec3(1.0f, 1.0f, 0.5f)); // default values + g_Config.m_ClMessageSystemHue = HSL.h; + g_Config.m_ClMessageSystemSat = HSL.s; + g_Config.m_ClMessageSystemLht = HSL.l; + } + } + Left.HSplitTop(20.0f, &Button, &Left); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Hue"), 14.0f, -1); + g_Config.m_ClMessageSystemHue = (int)(DoScrollbarH(&g_Config.m_ClMessageSystemHue, &Button, g_Config.m_ClMessageSystemHue / 255.0f)*255.0f); + Left.HSplitTop(20.0f, &Button, &Left); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Sat."), 14.0f, -1); + g_Config.m_ClMessageSystemSat = (int)(DoScrollbarH(&g_Config.m_ClMessageSystemSat, &Button, g_Config.m_ClMessageSystemSat / 255.0f)*255.0f); + Left.HSplitTop(20.0f, &Button, &Left); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Lht."), 14.0f, -1); + g_Config.m_ClMessageSystemLht = (int)(DoScrollbarH(&g_Config.m_ClMessageSystemLht, &Button, g_Config.m_ClMessageSystemLht / 255.0f)*255.0f); + + Left.HSplitTop(10.0f, &Label, &Left); + + vec3 rgb = HslToRgb(vec3(g_Config.m_ClMessageSystemHue / 255.0f, g_Config.m_ClMessageSystemSat / 255.0f, g_Config.m_ClMessageSystemLht / 255.0f)); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, 1.0f); + + + char name[16]; + str_copy(name, g_Config.m_PlayerName, sizeof(name)); + str_format(aBuf, sizeof(aBuf), "*** '%s' entered and joined the spectators", name); + while (TextRender()->TextWidth(0, 12.0f, aBuf, -1) > Label.w) + { + name[str_length(name) - 1] = 0; + str_format(aBuf, sizeof(aBuf), "*** '%s' entered and joined the spectators", name); + } + UI()->DoLabelScaled(&Label, aBuf, 12.0f, -1); + TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); + Left.HSplitTop(20.0f, 0, &Left); + } + { + char aBuf[64]; + Right.HSplitTop(20.0f, &Label, &Right); + Label.VSplitRight(50.0f, &Label, &Button); + UI()->DoLabelScaled(&Label, Localize("Highlighted message"), 16.0f, -1); + { + static int s_DefaultButton = 0; + if (DoButton_Menu(&s_DefaultButton, Localize("Reset"), 0, &Button)){ + vec3 HSL = RgbToHsl(vec3(1.0f, 0.5f, 0.5f)); // default values + g_Config.m_ClMessageHighlightHue = HSL.h; + g_Config.m_ClMessageHighlightSat = HSL.s; + g_Config.m_ClMessageHighlightLht = HSL.l; + } + } + Right.HSplitTop(20.0f, &Button, &Right); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Hue"), 14.0f, -1); + g_Config.m_ClMessageHighlightHue = (int)(DoScrollbarH(&g_Config.m_ClMessageHighlightHue, &Button, g_Config.m_ClMessageHighlightHue / 255.0f)*255.0f); + + Right.HSplitTop(20.0f, &Button, &Right); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Sat."), 14.0f, -1); + g_Config.m_ClMessageHighlightSat = (int)(DoScrollbarH(&g_Config.m_ClMessageHighlightSat, &Button, g_Config.m_ClMessageHighlightSat / 255.0f)*255.0f); + + Right.HSplitTop(20.0f, &Button, &Right); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Lht."), 14.0f, -1); + g_Config.m_ClMessageHighlightLht = (int)(DoScrollbarH(&g_Config.m_ClMessageHighlightLht, &Button, g_Config.m_ClMessageHighlightLht / 255.0f)*255.0f); + + Right.HSplitTop(10.0f, &Label, &Right); + + TextRender()->TextColor(0.75f, 0.5f, 0.75f, 1.0f); + float tw = TextRender()->TextWidth(0, 12.0f, Localize("Spectator"), -1); + Label.VSplitLeft(tw, &Label, &Button); + UI()->DoLabelScaled(&Label, Localize("Spectator"), 12.0f, -1); + + vec3 rgb = HslToRgb(vec3(g_Config.m_ClMessageHighlightHue / 255.0f, g_Config.m_ClMessageHighlightSat / 255.0f, g_Config.m_ClMessageHighlightLht / 255.0f)); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, 1.0f); + char name[16]; + str_copy(name, g_Config.m_PlayerName, sizeof(name)); + str_format(aBuf, sizeof(aBuf), ": %s: %s", name, Localize ("Look out!")); + while (TextRender()->TextWidth(0, 12.0f, aBuf, -1) > Button.w) + { + name[str_length(name) - 1] = 0; + str_format(aBuf, sizeof(aBuf), ": %s: %s", name, Localize("Look out!")); + } + UI()->DoLabelScaled(&Button, aBuf, 12.0f, -1); + + TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); + Right.HSplitTop(20.0f, 0, &Right); + } + { + char aBuf[64]; + Left.HSplitTop(20.0f, &Label, &Left); + Label.VSplitRight(50.0f, &Label, &Button); + UI()->DoLabelScaled(&Label, Localize("Team message"), 16.0f, -1); + { + static int s_DefaultButton = 0; + if (DoButton_Menu(&s_DefaultButton, Localize("Reset"), 0, &Button)){ + vec3 HSL = RgbToHsl(vec3(0.65f, 1.0f, 0.65f)); // default values + g_Config.m_ClMessageTeamHue = HSL.h; + g_Config.m_ClMessageTeamSat = HSL.s; + g_Config.m_ClMessageTeamLht = HSL.l; + } + } + Left.HSplitTop(20.0f, &Button, &Left); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Hue"), 14.0f, -1); + g_Config.m_ClMessageTeamHue = (int)(DoScrollbarH(&g_Config.m_ClMessageTeamHue, &Button, g_Config.m_ClMessageTeamHue / 255.0f)*255.0f); + Left.HSplitTop(20.0f, &Button, &Left); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Sat."), 14.0f, -1); + g_Config.m_ClMessageTeamSat = (int)(DoScrollbarH(&g_Config.m_ClMessageTeamSat, &Button, g_Config.m_ClMessageTeamSat / 255.0f)*255.0f); + Left.HSplitTop(20.0f, &Button, &Left); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Lht."), 14.0f, -1); + g_Config.m_ClMessageTeamLht = (int)(DoScrollbarH(&g_Config.m_ClMessageTeamLht, &Button, g_Config.m_ClMessageTeamLht / 255.0f)*255.0f); + + Left.HSplitTop(10.0f, &Label, &Left); + + TextRender()->TextColor(0.45f, 0.9f, 0.45f, 1.0f); + float tw = TextRender()->TextWidth(0, 12.0f, Localize("Player"), -1); + Label.VSplitLeft(tw, &Label, &Button); + UI()->DoLabelScaled(&Label, Localize("Player"), 12.0f, -1); + + vec3 rgb = HslToRgb(vec3(g_Config.m_ClMessageTeamHue / 255.0f, g_Config.m_ClMessageTeamSat / 255.0f, g_Config.m_ClMessageTeamLht / 255.0f)); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, 1.0f); + str_format(aBuf, sizeof(aBuf), ": %s!", Localize("We will win")); + UI()->DoLabelScaled(&Button, aBuf, 12.0f, -1); + + TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); + Left.HSplitTop(20.0f, 0, &Left); + } + { + char aBuf[64]; + Left.HSplitTop(20.0f, &Label, &Left); + Label.VSplitRight(50.0f, &Label, &Button); + UI()->DoLabelScaled(&Label, Localize("Normal message"), 16.0f, -1); + { + static int s_DefaultButton = 0; + if (DoButton_Menu(&s_DefaultButton, Localize("Reset"), 0, &Button)){ + vec3 HSL = RgbToHsl(vec3(1.0f, 1.0f, 1.0f)); // default values + g_Config.m_ClMessageHue = HSL.h; + g_Config.m_ClMessageSat = HSL.s; + g_Config.m_ClMessageLht = HSL.l; + } + } + Left.HSplitTop(20.0f, &Button, &Left); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Hue"), 14.0f, -1); + g_Config.m_ClMessageHue = (int)(DoScrollbarH(&g_Config.m_ClMessageHue, &Button, g_Config.m_ClMessageHue / 255.0f)*255.0f); + Left.HSplitTop(20.0f, &Button, &Left); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Sat."), 14.0f, -1); + g_Config.m_ClMessageSat = (int)(DoScrollbarH(&g_Config.m_ClMessageSat, &Button, g_Config.m_ClMessageSat / 255.0f)*255.0f); + Left.HSplitTop(20.0f, &Button, &Left); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Lht."), 14.0f, -1); + g_Config.m_ClMessageLht = (int)(DoScrollbarH(&g_Config.m_ClMessageLht, &Button, g_Config.m_ClMessageLht / 255.0f)*255.0f); + + Left.HSplitTop(10.0f, &Label, &Left); + + TextRender()->TextColor(0.8f, 0.8f, 0.8f, 1.0f); + float tw = TextRender()->TextWidth(0, 12.0f, Localize("Player"), -1); + Label.VSplitLeft(tw, &Label, &Button); + UI()->DoLabelScaled(&Label, Localize("Player"), 12.0f, -1); + + vec3 rgb = HslToRgb(vec3(g_Config.m_ClMessageHue / 255.0f, g_Config.m_ClMessageSat / 255.0f, g_Config.m_ClMessageLht / 255.0f)); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, 1.0f); + str_format(aBuf, sizeof(aBuf), ": %s :D", Localize("Hello and welcome")); + UI()->DoLabelScaled(&Button, aBuf, 12.0f, -1); + + TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); + } + { + Right.HSplitTop(220.0f, &Laser, &Right); + RenderTools()->DrawUIRect(&Laser, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_ALL, 5.0f); + Laser.Margin(10.0f, &Laser); + Laser.HSplitTop(30.0f, &Label, &Laser); + Label.VSplitLeft(TextRender()->TextWidth(0, 20.0f, Localize("Laser"), -1) + 5.0f, &Label, &Weapon); + UI()->DoLabelScaled(&Label, Localize("Laser"), 20.0f, -1); + + Laser.HSplitTop(20.0f, &Label, &Laser); + Label.VSplitLeft(5.0f, 0, &Label); + Label.VSplitRight(50.0f, &Label, &Button); + UI()->DoLabelScaled(&Label, Localize("Inner color"), 16.0f, -1); + { + static int s_DefaultButton = 0; + if (DoButton_Menu(&s_DefaultButton, Localize("Reset"), 0, &Button)){ + vec3 HSL = RgbToHsl(vec3(0.5f, 0.5f, 1.0f)); // default values + g_Config.m_ClLaserInnerHue = HSL.h; + g_Config.m_ClLaserInnerSat = HSL.s; + g_Config.m_ClLaserInnerLht = HSL.l; + } + } + + Laser.HSplitTop(20.0f, &Button, &Laser); + Button.VSplitLeft(20.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Hue"), 12.0f, -1); + g_Config.m_ClLaserInnerHue = (int)(DoScrollbarH(&g_Config.m_ClLaserInnerHue, &Button, g_Config.m_ClLaserInnerHue / 255.0f)*255.0f); + Laser.HSplitTop(20.0f, &Button, &Laser); + Button.VSplitLeft(20.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Sat."), 12.0f, -1); + g_Config.m_ClLaserInnerSat = (int)(DoScrollbarH(&g_Config.m_ClLaserInnerSat, &Button, g_Config.m_ClLaserInnerSat / 255.0f)*255.0f); + Laser.HSplitTop(20.0f, &Button, &Laser); + Button.VSplitLeft(20.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Lht."), 12.0f, -1); + g_Config.m_ClLaserInnerLht = (int)(DoScrollbarH(&g_Config.m_ClLaserInnerLht, &Button, g_Config.m_ClLaserInnerLht / 255.0f)*255.0f); + + Laser.HSplitTop(10.0f, 0, &Laser); + + Laser.HSplitTop(20.0f, &Label, &Laser); + Label.VSplitLeft(5.0f, 0, &Label); + Label.VSplitRight(50.0f, &Label, &Button); + UI()->DoLabelScaled(&Label, Localize("Outline color"), 16.0f, -1); + { + static int s_DefaultButton = 0; + if (DoButton_Menu(&s_DefaultButton, Localize("Reset"), 0, &Button)){ + vec3 HSL = RgbToHsl(vec3(0.075f, 0.075f, 0.25f)); // default values + g_Config.m_ClLaserOutlineHue = HSL.h; + g_Config.m_ClLaserOutlineSat = HSL.s; + g_Config.m_ClLaserOutlineLht = HSL.l; + } + } + + Laser.HSplitTop(20.0f, &Button, &Laser); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Hue"), 12.0f, -1); + g_Config.m_ClLaserOutlineHue = (int)(DoScrollbarH(&g_Config.m_ClLaserOutlineHue, &Button, g_Config.m_ClLaserOutlineHue / 255.0f)*255.0f); + Laser.HSplitTop(20.0f, &Button, &Laser); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Sat."), 12.0f, -1); + g_Config.m_ClLaserOutlineSat = (int)(DoScrollbarH(&g_Config.m_ClLaserOutlineSat, &Button, g_Config.m_ClLaserOutlineSat / 255.0f)*255.0f); + Laser.HSplitTop(20.0f, &Button, &Laser); + Button.VSplitLeft(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Lht."), 12.0f, -1); + g_Config.m_ClLaserOutlineLht = (int)(DoScrollbarH(&g_Config.m_ClLaserOutlineLht, &Button, g_Config.m_ClLaserOutlineLht / 255.0f)*255.0f); + + + //Laser.HSplitTop(8.0f, &Weapon, &Laser); + Weapon.VSplitLeft(30.0f, 0, &Weapon); + + vec3 RGB; + vec2 From = vec2(Weapon.x, Weapon.y + Weapon.h / 2.0f); + vec2 Pos = vec2(Weapon.x + Weapon.w - 10.0f, Weapon.y + Weapon.h / 2.0f); + + vec2 Out, Border; + + Graphics()->BlendNormal(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + + // do outline + RGB = HslToRgb(vec3(g_Config.m_ClLaserOutlineHue / 255.0f, g_Config.m_ClLaserOutlineSat / 255.0f, g_Config.m_ClLaserOutlineLht / 255.0f)); + vec4 OuterColor(RGB.r, RGB.g, RGB.b, 1.0f); + Graphics()->SetColor(RGB.r, RGB.g, RGB.b, 1.0f); // outline + Out = vec2(0.0f, -1.0f) * (3.15f); + + IGraphics::CFreeformItem Freeform( + From.x - Out.x, From.y - Out.y, + From.x + Out.x, From.y + Out.y, + Pos.x - Out.x, Pos.y - Out.y, + Pos.x + Out.x, Pos.y + Out.y); + Graphics()->QuadsDrawFreeform(&Freeform, 1); + + // do inner + RGB = HslToRgb(vec3(g_Config.m_ClLaserInnerHue / 255.0f, g_Config.m_ClLaserInnerSat / 255.0f, g_Config.m_ClLaserInnerLht / 255.0f)); + vec4 InnerColor(RGB.r, RGB.g, RGB.b, 1.0f); + Out = vec2(0.0f, -1.0f) * (2.25f); + Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f); // center + + Freeform = IGraphics::CFreeformItem( + From.x - Out.x, From.y - Out.y, + From.x + Out.x, From.y + Out.y, + Pos.x - Out.x, Pos.y - Out.y, + Pos.x + Out.x, Pos.y + Out.y); + Graphics()->QuadsDrawFreeform(&Freeform, 1); + + Graphics()->QuadsEnd(); + + // render head + { + Graphics()->BlendNormal(); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_PARTICLES].m_Id); + Graphics()->QuadsBegin(); + + int Sprites[] = { SPRITE_PART_SPLAT01, SPRITE_PART_SPLAT02, SPRITE_PART_SPLAT03 }; + RenderTools()->SelectSprite(Sprites[time_get() % 3]); + Graphics()->QuadsSetRotation(time_get()); + Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, 1.0f); + IGraphics::CQuadItem QuadItem(Pos.x, Pos.y, 24, 24); + Graphics()->QuadsDraw(&QuadItem, 1); + Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f); + QuadItem = IGraphics::CQuadItem(Pos.x, Pos.y, 20, 20); + Graphics()->QuadsDraw(&QuadItem, 1); + Graphics()->QuadsEnd(); + } + // draw laser weapon + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); + Graphics()->QuadsBegin(); + + RenderTools()->SelectSprite(SPRITE_WEAPON_RIFLE_BODY); + RenderTools()->DrawSprite(Weapon.x, Weapon.y + Weapon.h / 2.0f, 60.0f); + + Graphics()->QuadsEnd(); + } + /* + Left.VSplitLeft(20.0f, 0, &Left); + Left.HSplitTop(20.0f, &Label, &Left); + Button.VSplitRight(20.0f, &Button, 0); + char aBuf[64]; + if (g_Config.m_ClReconnectBanTimeout == 1) + { + str_format(aBuf, sizeof(aBuf), "%s %i %s", Localize("Wait before try for"), g_Config.m_ClReconnectBanTimeout, Localize("second")); + } + else + { + str_format(aBuf, sizeof(aBuf), "%s %i %s", Localize("Wait before try for"), g_Config.m_ClReconnectBanTimeout, Localize("seconds")); + } + UI()->DoLabelScaled(&Label, aBuf, 13.0f, -1); + Left.HSplitTop(20.0f, &Button, 0); + Button.HMargin(2.0f, &Button); + g_Config.m_ClReconnectBanTimeout = static_cast(DoScrollbarH(&g_Config.m_ClReconnectBanTimeout, &Button, g_Config.m_ClReconnectBanTimeout / 120.0f) * 120.0f); + if (g_Config.m_ClReconnectBanTimeout < 5) + g_Config.m_ClReconnectBanTimeout = 5;*/ +} + +void CMenus::RenderSettingsDDRace(CUIRect MainView) +{ + CUIRect Button, Left, Right, LeftLeft, Demo, Gameplay, Miscellaneous, Label; + + MainView.HSplitTop(90.0f, &Demo , &MainView); + + Demo.HSplitTop(30.0f, &Label, &Demo); + UI()->DoLabelScaled(&Label, Localize("Demo"), 20.0f, -1); + Demo.Margin(5.0f, &Demo); + Demo.VSplitMid(&Left, &Right); + Left.VSplitRight(5.0f, &Left, 0); + Right.VMargin(5.0f, &Right); + + Left.HSplitTop(20.0f, &Button, &Left); + if(DoButton_CheckBox(&g_Config.m_ClAutoRaceRecord, Localize("Save the best demo of each race"), g_Config.m_ClAutoRaceRecord, &Button)) { g_Config.m_ClAutoRaceRecord ^= 1; } - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_ClDemoName, Localize("Enable save the player name within the demo"), g_Config.m_ClDemoName, &Button)) + Left.HSplitTop(20.0f, &Button, &Left); + if(DoButton_CheckBox(&g_Config.m_ClDemoName, Localize("Save the player name within the demo"), g_Config.m_ClDemoName, &Button)) { g_Config.m_ClDemoName ^= 1; } - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_ClRaceGhost, Localize("Enable ghost"), g_Config.m_ClRaceGhost, &Button)) + Right.HSplitTop(20.0f, &Button, &Right); + if(DoButton_CheckBox(&g_Config.m_ClRaceGhost, Localize("Ghost"), g_Config.m_ClRaceGhost, &Button)) { g_Config.m_ClRaceGhost ^= 1; } - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_ClRaceShowGhost, Localize("Enable show ghost"), g_Config.m_ClRaceShowGhost, &Button)) + Right.HSplitTop(20.0f, &Button, &Right); + if(DoButton_CheckBox(&g_Config.m_ClRaceShowGhost, Localize("Show ghost"), g_Config.m_ClRaceShowGhost, &Button)) { g_Config.m_ClRaceShowGhost ^= 1; } - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_ClRaceSaveGhost, Localize("Enable save ghost"), g_Config.m_ClRaceSaveGhost, &Button)) + Right.HSplitTop(20.0f, &Button, &Right); + if(DoButton_CheckBox(&g_Config.m_ClRaceSaveGhost, Localize("Save ghost"), g_Config.m_ClRaceSaveGhost, &Button)) { g_Config.m_ClRaceSaveGhost ^= 1; } - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_ClShowOthers, Localize("Show players in other teams"), g_Config.m_ClShowOthers, &Button)) + MainView.HSplitTop(230.0f, &Gameplay , &MainView); + + Gameplay.HSplitTop(30.0f, &Label, &Gameplay); + UI()->DoLabelScaled(&Label, Localize("Gameplay"), 20.0f, -1); + Gameplay.Margin(5.0f, &Gameplay); + Gameplay.VSplitMid(&Left, &Right); + Left.VSplitRight(5.0f, &Left, 0); + Right.VMargin(5.0f, &Right); + { - g_Config.m_ClShowOthers ^= 1; + CUIRect Button, Label; + Left.HSplitTop(20.0f, &Button, &Left); + Button.VSplitLeft(120.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Overlay entities"), 14.0f, -1); + g_Config.m_ClOverlayEntities = (int)(DoScrollbarH(&g_Config.m_ClOverlayEntities, &Button, g_Config.m_ClOverlayEntities/100.0f)*100.0f); } - MainView.HSplitTop(20.0f, &Button, &MainView); - if(DoButton_CheckBox(&g_Config.m_ClDDRaceBinds, Localize("Bind free keys with DDRace pre-configured binds"), g_Config.m_ClDDRaceBinds, &Button)) + { + CUIRect Button, Label; + Left.HSplitTop(20.0f, &Button, &Left); + Button.VSplitMid(&LeftLeft, &Button); + + Button.VSplitLeft(60.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabelScaled(&Label, Localize("Alpha"), 14.0f, -1); + g_Config.m_ClShowOthersAlpha = (int)(DoScrollbarH(&g_Config.m_ClShowOthersAlpha, &Button, g_Config.m_ClShowOthersAlpha /100.0f)*100.0f); + + if(DoButton_CheckBox(&g_Config.m_ClShowOthers, Localize("Show others"), g_Config.m_ClShowOthers, &LeftLeft)) + { + g_Config.m_ClShowOthers ^= 1; + } + } + + Left.HSplitTop(20.0f, &Button, &Left); + if(DoButton_CheckBox(&g_Config.m_ClShowQuads, Localize("Show quads"), g_Config.m_ClShowQuads, &Button)) + { + g_Config.m_ClShowQuads ^= 1; + } + + Right.HSplitTop(20.0f, &Button, &Right); + if(DoButton_CheckBox(&g_Config.m_ClAntiPing, Localize("AntiPing (predict other players)"), g_Config.m_ClAntiPing, &Button)) + { + g_Config.m_ClAntiPing ^= 1; + } + + Right.HSplitTop(20.0f, &Button, &Right); + if(DoButton_CheckBox(&g_Config.m_ClAntiPingGrenade, Localize("AntiPing (predict grenades)"), g_Config.m_ClAntiPingGrenade, &Button)) + { + g_Config.m_ClAntiPingGrenade ^= 1; + } + + Right.HSplitTop(20.0f, &Button, &Right); + if(DoButton_CheckBox(&g_Config.m_ClUnpredictedShadow, Localize("Show unpredicted shadow tee"), g_Config.m_ClUnpredictedShadow, &Button)) + { + g_Config.m_ClUnpredictedShadow ^= 1; + } + + Right.HSplitTop(20.0f, &Button, &Right); + if(DoButton_CheckBox(&g_Config.m_ClShowNinja, Localize("Show ninja skin"), g_Config.m_ClShowNinja, &Button)) + { + g_Config.m_ClShowNinja ^= 1; + } + + Right.HSplitTop(20.0f, &Button, &Right); + if(DoButton_CheckBox(&g_Config.m_ClOldGunPosition, Localize("Old gun position"), g_Config.m_ClOldGunPosition, &Button)) + { + g_Config.m_ClOldGunPosition ^= 1; + } + + Left.HSplitTop(20.0f, &Button, &Left); + if(DoButton_CheckBox(&g_Config.m_ClShowOtherHookColl, Localize("Show other players' hook collision lines"), g_Config.m_ClShowOtherHookColl, &Button)) + { + g_Config.m_ClShowOtherHookColl ^= 1; + } + + Left.HSplitTop(20.0f, &Button, &Left); + if(DoButton_CheckBox(&g_Config.m_ClShowDirection, Localize("Show other players' key presses"), g_Config.m_ClShowDirection, &Button)) + { + g_Config.m_ClShowDirection ^= 1; + } + + CUIRect aRects[2]; + Left.HSplitTop(5.0f, &Button, &Left); + Right.HSplitTop(5.0f, &Button, &Right); + aRects[0] = Left; + aRects[1] = Right; + aRects[0].VSplitRight(10.0f, &aRects[0], 0); + aRects[1].VSplitLeft(10.0f, 0, &aRects[1]); + + int *pColorSlider[2][3] = {{&g_Config.m_ClBackgroundHue, &g_Config.m_ClBackgroundSat, &g_Config.m_ClBackgroundLht}, {&g_Config.m_ClBackgroundEntitiesHue, &g_Config.m_ClBackgroundEntitiesSat, &g_Config.m_ClBackgroundEntitiesLht}}; + + const char *paParts[] = { + Localize("Background (regular)"), + Localize("Background (entities)")}; + const char *paLabels[] = { + Localize("Hue"), + Localize("Sat."), + Localize("Lht.")}; + + for(int i = 0; i < 2; i++) + { + aRects[i].HSplitTop(20.0f, &Label, &aRects[i]); + UI()->DoLabelScaled(&Label, paParts[i], 14.0f, -1); + aRects[i].VSplitLeft(20.0f, 0, &aRects[i]); + aRects[i].HSplitTop(2.5f, 0, &aRects[i]); + + for(int s = 0; s < 3; s++) + { + aRects[i].HSplitTop(20.0f, &Label, &aRects[i]); + Label.VSplitLeft(100.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + + float k = (*pColorSlider[i][s]) / 255.0f; + k = DoScrollbarH(pColorSlider[i][s], &Button, k); + *pColorSlider[i][s] = (int)(k*255.0f); + UI()->DoLabelScaled(&Label, paLabels[s], 15.0f, -1); + } + } + + MainView.HSplitTop(30.0f, &Label, &Miscellaneous); + UI()->DoLabelScaled(&Label, Localize("Miscellaneous"), 20.0f, -1); + Miscellaneous.VMargin(5.0f, &Miscellaneous); + Miscellaneous.VSplitMid(&Left, &Right); + Left.VSplitRight(5.0f, &Left, 0); + Right.VMargin(5.0f, &Right); + + Left.HSplitTop(20.0f, &Button, &Left); + if(DoButton_CheckBox(&g_Config.m_ClDDRaceBinds, Localize("Bind free keys with DDRace binds"), g_Config.m_ClDDRaceBinds, &Button)) { g_Config.m_ClDDRaceBinds ^= 1; } + + Right.HSplitTop(20.0f, &Button, &Right); + if(DoButton_CheckBox(&g_Config.m_ClEditorUndo, Localize("Undo function in editor (could be buggy)"), g_Config.m_ClEditorUndo, &Button)) + { + g_Config.m_ClEditorUndo ^= 1; + } + + // Auto Update +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + CUIRect HUDItem; + Left.HSplitTop(20.0f, &HUDItem, &Left); + HUDItem.VSplitMid(&HUDItem, &Button); + if(DoButton_CheckBox(&g_Config.m_ClAutoUpdate, Localize("Auto-Update"), g_Config.m_ClAutoUpdate, &HUDItem)) + g_Config.m_ClAutoUpdate ^= 1; + Button.Margin(2.0f, &Button); + static int s_ButtonAutoUpdate = 0; + if (DoButton_Menu((void*)&s_ButtonAutoUpdate, Localize("Check now"), 0, &Button)) + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Checking for an update"); + RenderUpdating(aBuf); + AutoUpdate()->CheckUpdates(this); + } +#endif + + { + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%d", g_Config.m_ConnTimeout); + Right.HSplitTop(20.0f, &Button, &Right); + UI()->DoLabelScaled(&Button, Localize("Timeout (in seconds)"), 14.0f, -1); + Button.VSplitLeft(190.0f, 0, &Button); + static float Offset = 0.0f; + DoEditBox(&g_Config.m_ConnTimeout, &Button, aBuf, sizeof(aBuf), 14.0f, &Offset); + g_Config.m_ConnTimeout = clamp(str_toint(aBuf), 5, 1000); + } } diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index 6699fe247b..716b9fc3b4 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -20,6 +20,14 @@ void CNamePlates::RenderNameplate( vec2 Position = mix(vec2(pPrevChar->m_X, pPrevChar->m_Y), vec2(pPlayerChar->m_X, pPlayerChar->m_Y), IntraTick); + bool OtherTeam; + + if (m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_Team == TEAM_SPECTATORS && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW) + OtherTeam = false; + else if (m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW) + OtherTeam = m_pClient->m_Teams.Team(pPlayerInfo->m_ClientID) != m_pClient->m_Teams.Team(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID); + else + OtherTeam = m_pClient->m_Teams.Team(pPlayerInfo->m_ClientID) != m_pClient->m_Teams.Team(m_pClient->m_Snap.m_LocalClientID); float FontSize = 18.0f + 20.0f * g_Config.m_ClNameplatesSize / 100.0f; // render name plate @@ -27,13 +35,25 @@ void CNamePlates::RenderNameplate( { float a = 1; if(g_Config.m_ClNameplatesAlways == 0) - a = clamp(1-powf(distance(m_pClient->m_pControls->m_TargetPos, Position)/200.0f,16.0f), 0.0f, 1.0f); + a = clamp(1-powf(distance(m_pClient->m_pControls->m_TargetPos[g_Config.m_ClDummy], Position)/200.0f,16.0f), 0.0f, 1.0f); const char *pName = m_pClient->m_aClients[pPlayerInfo->m_ClientID].m_aName; float tw = TextRender()->TextWidth(0, FontSize, pName, -1); - TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 0.5f*a); - TextRender()->TextColor(1.0f, 1.0f, 1.0f, a); + vec3 rgb = vec3(1.0f, 1.0f, 1.0f); + if(g_Config.m_ClNameplatesTeamcolors && m_pClient->m_Teams.Team(pPlayerInfo->m_ClientID)) + rgb = HslToRgb(vec3(m_pClient->m_Teams.Team(pPlayerInfo->m_ClientID) / 64.0f, 1.0f, 0.75f)); + + if (OtherTeam) + { + TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 0.2f); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, g_Config.m_ClShowOthersAlpha / 100.0f); + } + else + { + TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 0.5f*a); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, a); + } if(g_Config.m_ClNameplatesTeamcolors && m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) { if(pPlayerInfo->m_Team == TEAM_RED) @@ -58,7 +78,7 @@ void CNamePlates::RenderNameplate( void CNamePlates::OnRender() { - if (!g_Config.m_ClNameplates) + if (!g_Config.m_ClNameplates || g_Config.m_ClAntiPing) return; for(int i = 0; i < MAX_CLIENTS; i++) diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index bd22776388..3a01c1edfb 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -22,7 +22,10 @@ #include #include +#include + #include "players.h" +#include void CPlayers::RenderHand(CTeeRenderInfo *pInfo, vec2 CenterPos, vec2 Dir, float AngleOffset, vec2 PostRotOffset) { @@ -85,11 +88,142 @@ inline float AngularApproach(float Src, float Dst, float Amount) return n; } +void CPlayers::Predict( + const CNetObj_Character *pPrevChar, + const CNetObj_Character *pPlayerChar, + const CNetObj_PlayerInfo *pPrevInfo, + const CNetObj_PlayerInfo *pPlayerInfo, + vec2 &PrevPredPos, + vec2 &SmoothPos, + int &MoveCnt, + vec2 &Position + ) +{ + CNetObj_Character Prev; + CNetObj_Character Player; + Prev = *pPrevChar; + Player = *pPlayerChar; + + CNetObj_PlayerInfo pInfo = *pPlayerInfo; + + + // set size + + float IntraTick = Client()->IntraGameTick(); + + + //float angle = 0; + + if(pInfo.m_Local && Client()->State() != IClient::STATE_DEMOPLAYBACK) + { + // just use the direct input if it's local player we are rendering + } + else + { + /* + float mixspeed = Client()->FrameTime()*2.5f; + if(player.attacktick != prev.attacktick) // shooting boosts the mixing speed + mixspeed *= 15.0f; + + // move the delta on a constant speed on a x^2 curve + float current = g_GameClient.m_aClients[info.cid].angle; + float target = player.angle/256.0f; + float delta = angular_distance(current, target); + float sign = delta < 0 ? -1 : 1; + float new_delta = delta - 2*mixspeed*sqrt(delta*sign)*sign + mixspeed*mixspeed; + + // make sure that it doesn't vibrate when it's still + if(fabs(delta) < 2/256.0f) + angle = target; + else + angle = angular_approach(current, target, fabs(delta-new_delta)); + + g_GameClient.m_aClients[info.cid].angle = angle;*/ + } + + vec2 NonPredPos = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick); + + + // use preditect players if needed + if(g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) + { + if(m_pClient->m_Snap.m_pLocalCharacter && !(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + { + // apply predicted results + m_pClient->m_aClients[pInfo.m_ClientID].m_Predicted.Write(&Player); + m_pClient->m_aClients[pInfo.m_ClientID].m_PrevPredicted.Write(&Prev); + + IntraTick = Client()->PredIntraGameTick(); + } + } + + Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick); + + + static double ping = 0; + + if(pInfo.m_Local) { + ping = mix(ping, (double)pInfo.m_Latency, 0.1); + } + + if(!pInfo.m_Local) + { + /* + for ping = 260, usual missprediction distances: + + move = 120-140 + jump = 130 + dj = 250 + + normalized: + move = 0.461 - 0.538 + jump = 0.5 + dj = .961 + + */ + //printf("%d\n", m_pClient->m_Snap.m_pLocalInfo->m_Latency); + + + if(m_pClient->m_Snap.m_pLocalInfo) + ping = mix(ping, (double)m_pClient->m_Snap.m_pLocalInfo->m_Latency, 0.1); + + double d = length(PrevPredPos - Position)/ping; + + if((d > 0.4) && (d < 5.)) + { +// if(MoveCnt == 0) +// printf("[\n"); + if(MoveCnt == 0) + SmoothPos = NonPredPos; + + MoveCnt = 10; +// SmoothPos = PrevPredPos; +// SmoothPos = mix(NonPredPos, Position, 0.6); + } + + PrevPredPos = Position; + + if(MoveCnt > 0) + { +// Position = mix(mix(NonPredPos, Position, 0.5), SmoothPos, (((float)MoveCnt))/15); +// Position = mix(mix(NonPredPos, Position, 0.5), SmoothPos, 0.5); + Position = mix(NonPredPos, Position, 0.5); + + SmoothPos = Position; + MoveCnt--; +// if(MoveCnt == 0) +// printf("]\n\n"); + } + } +} + void CPlayers::RenderHook( const CNetObj_Character *pPrevChar, const CNetObj_Character *pPlayerChar, const CNetObj_PlayerInfo *pPrevInfo, - const CNetObj_PlayerInfo *pPlayerInfo + const CNetObj_PlayerInfo *pPlayerInfo, + const vec2 &parPosition, + const vec2 &PositionTo ) { CNetObj_Character Prev; @@ -102,26 +236,61 @@ void CPlayers::RenderHook( float IntraTick = Client()->IntraGameTick(); + bool OtherTeam; + + if (m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_Team == TEAM_SPECTATORS && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW) + OtherTeam = false; + else if (m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW) + OtherTeam = m_pClient->m_Teams.Team(pInfo.m_ClientID) != m_pClient->m_Teams.Team(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID); + else + OtherTeam = m_pClient->m_Teams.Team(pInfo.m_ClientID) != m_pClient->m_Teams.Team(m_pClient->m_Snap.m_LocalClientID); + + if (OtherTeam) + { + RenderInfo.m_ColorBody.a = g_Config.m_ClShowOthersAlpha / 100.0f; + RenderInfo.m_ColorFeet.a = g_Config.m_ClShowOthersAlpha / 100.0f; + } + // set size RenderInfo.m_Size = 64.0f; - - // use preditect players if needed - if(pInfo.m_Local && g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) + if (!g_Config.m_ClAntiPing) { - if(!m_pClient->m_Snap.m_pLocalCharacter || (m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + // use preditect players if needed + if(pInfo.m_Local && g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) { + if(!m_pClient->m_Snap.m_pLocalCharacter || (m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + { + } + else + { + // apply predicted results + m_pClient->m_PredictedChar.Write(&Player); + m_pClient->m_PredictedPrevChar.Write(&Prev); + IntraTick = Client()->PredIntraGameTick(); + } } - else + } + else + { + if(g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) { - // apply predicted results - m_pClient->m_PredictedChar.Write(&Player); - m_pClient->m_PredictedPrevChar.Write(&Prev); - IntraTick = Client()->PredIntraGameTick(); + if(m_pClient->m_Snap.m_pLocalCharacter && !(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + { + // apply predicted results + m_pClient->m_aClients[pInfo.m_ClientID].m_Predicted.Write(&Player); + m_pClient->m_aClients[pInfo.m_ClientID].m_PrevPredicted.Write(&Prev); + + IntraTick = Client()->PredIntraGameTick(); + } } } - vec2 Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick); + vec2 Position; + if (!g_Config.m_ClAntiPing) + Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick); + else + Position = parPosition; // draw hook if (Prev.m_HookState>0 && Player.m_HookState>0) @@ -133,29 +302,39 @@ void CPlayers::RenderHook( vec2 Pos = Position; vec2 HookPos; - if(pPlayerChar->m_HookedPlayer != -1) + if (!g_Config.m_ClAntiPing) { - if(m_pClient->m_Snap.m_pLocalInfo && pPlayerChar->m_HookedPlayer == m_pClient->m_Snap.m_pLocalInfo->m_ClientID) + if(pPlayerChar->m_HookedPlayer != -1) { - if(Client()->State() == IClient::STATE_DEMOPLAYBACK) // only use prediction if needed - HookPos = vec2(m_pClient->m_LocalCharacterPos.x, m_pClient->m_LocalCharacterPos.y); + if(m_pClient->m_Snap.m_pLocalInfo && pPlayerChar->m_HookedPlayer == m_pClient->m_Snap.m_pLocalInfo->m_ClientID) + { + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) // only use prediction if needed + HookPos = vec2(m_pClient->m_LocalCharacterPos.x, m_pClient->m_LocalCharacterPos.y); + else + HookPos = mix(vec2(m_pClient->m_PredictedPrevChar.m_Pos.x, m_pClient->m_PredictedPrevChar.m_Pos.y), + vec2(m_pClient->m_PredictedChar.m_Pos.x, m_pClient->m_PredictedChar.m_Pos.y), Client()->PredIntraGameTick()); + } + else if(pInfo.m_Local) + { + HookPos = mix(vec2(m_pClient->m_Snap.m_aCharacters[pPlayerChar->m_HookedPlayer].m_Prev.m_X, + m_pClient->m_Snap.m_aCharacters[pPlayerChar->m_HookedPlayer].m_Prev.m_Y), + vec2(m_pClient->m_Snap.m_aCharacters[pPlayerChar->m_HookedPlayer].m_Cur.m_X, + m_pClient->m_Snap.m_aCharacters[pPlayerChar->m_HookedPlayer].m_Cur.m_Y), + Client()->IntraGameTick()); + } else - HookPos = mix(vec2(m_pClient->m_PredictedPrevChar.m_Pos.x, m_pClient->m_PredictedPrevChar.m_Pos.y), - vec2(m_pClient->m_PredictedChar.m_Pos.x, m_pClient->m_PredictedChar.m_Pos.y), Client()->PredIntraGameTick()); - } - else if(pInfo.m_Local) - { - HookPos = mix(vec2(m_pClient->m_Snap.m_aCharacters[pPlayerChar->m_HookedPlayer].m_Prev.m_X, - m_pClient->m_Snap.m_aCharacters[pPlayerChar->m_HookedPlayer].m_Prev.m_Y), - vec2(m_pClient->m_Snap.m_aCharacters[pPlayerChar->m_HookedPlayer].m_Cur.m_X, - m_pClient->m_Snap.m_aCharacters[pPlayerChar->m_HookedPlayer].m_Cur.m_Y), - Client()->IntraGameTick()); + HookPos = mix(vec2(pPrevChar->m_HookX, pPrevChar->m_HookY), vec2(pPlayerChar->m_HookX, pPlayerChar->m_HookY), Client()->IntraGameTick()); } else - HookPos = mix(vec2(pPrevChar->m_HookX, pPrevChar->m_HookY), vec2(pPlayerChar->m_HookX, pPlayerChar->m_HookY), Client()->IntraGameTick()); + HookPos = mix(vec2(Prev.m_HookX, Prev.m_HookY), vec2(Player.m_HookX, Player.m_HookY), IntraTick); } else - HookPos = mix(vec2(Prev.m_HookX, Prev.m_HookY), vec2(Player.m_HookX, Player.m_HookY), IntraTick); + { + if(pPrevChar->m_HookedPlayer != -1) + HookPos = PositionTo; + else + HookPos = mix(vec2(Prev.m_HookX, Prev.m_HookY), vec2(Player.m_HookX, Player.m_HookY), IntraTick); + } float d = distance(Pos, HookPos); vec2 Dir = normalize(Pos-HookPos); @@ -165,6 +344,8 @@ void CPlayers::RenderHook( // render head RenderTools()->SelectSprite(SPRITE_HOOK_HEAD); IGraphics::CQuadItem QuadItem(HookPos.x, HookPos.y, 24,16); + if (OtherTeam) + Graphics()->SetColor(1.0f, 1.0f, 1.0f, g_Config.m_ClShowOthersAlpha / 100.0f); Graphics()->QuadsDraw(&QuadItem, 1); // render chain @@ -177,6 +358,8 @@ void CPlayers::RenderHook( Array[i] = IGraphics::CQuadItem(p.x, p.y,24,16); } + if (OtherTeam) + Graphics()->SetColor(1.0f, 1.0f, 1.0f, g_Config.m_ClShowOthersAlpha / 100.0f); Graphics()->QuadsDraw(Array, i); Graphics()->QuadsSetRotation(0); Graphics()->QuadsEnd(); @@ -189,8 +372,12 @@ void CPlayers::RenderPlayer( const CNetObj_Character *pPrevChar, const CNetObj_Character *pPlayerChar, const CNetObj_PlayerInfo *pPrevInfo, - const CNetObj_PlayerInfo *pPlayerInfo - ) + const CNetObj_PlayerInfo *pPlayerInfo, + const vec2 &parPosition +/* vec2 &PrevPos, + vec2 &SmoothPos, + int &MoveCnt +*/ ) { CNetObj_Character Prev; CNetObj_Character Player; @@ -202,6 +389,15 @@ void CPlayers::RenderPlayer( bool NewTick = m_pClient->m_NewTick; + bool OtherTeam; + + if (m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_Team == TEAM_SPECTATORS && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW) + OtherTeam = false; + else if (m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW) + OtherTeam = m_pClient->m_Teams.Team(pInfo.m_ClientID) != m_pClient->m_Teams.Team(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID); + else + OtherTeam = m_pClient->m_Teams.Team(pInfo.m_ClientID) != m_pClient->m_Teams.Team(m_pClient->m_Snap.m_LocalClientID); + // set size RenderInfo.m_Size = 64.0f; @@ -214,7 +410,7 @@ void CPlayers::RenderPlayer( if(pInfo.m_Local && Client()->State() != IClient::STATE_DEMOPLAYBACK) { // just use the direct input if it's local player we are rendering - Angle = GetAngle(m_pClient->m_pControls->m_MousePos); + Angle = GetAngle(m_pClient->m_pControls->m_MousePos[g_Config.m_ClDummy]); } else { @@ -239,24 +435,47 @@ void CPlayers::RenderPlayer( g_GameClient.m_aClients[info.cid].angle = angle;*/ } + // use preditect players if needed - if(pInfo.m_Local && g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) + if (!g_Config.m_ClAntiPing) { - if(!m_pClient->m_Snap.m_pLocalCharacter || (m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + if(pInfo.m_Local && g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) { + if(!m_pClient->m_Snap.m_pLocalCharacter || (m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + { + } + else + { + // apply predicted results + m_pClient->m_PredictedChar.Write(&Player); + m_pClient->m_PredictedPrevChar.Write(&Prev); + IntraTick = Client()->PredIntraGameTick(); + NewTick = m_pClient->m_NewPredictedTick; + } } - else + } + else + { + if(g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) { - // apply predicted results - m_pClient->m_PredictedChar.Write(&Player); - m_pClient->m_PredictedPrevChar.Write(&Prev); - IntraTick = Client()->PredIntraGameTick(); - NewTick = m_pClient->m_NewPredictedTick; + if(m_pClient->m_Snap.m_pLocalCharacter && !(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + { + // apply predicted results + m_pClient->m_aClients[pInfo.m_ClientID].m_Predicted.Write(&Player); + m_pClient->m_aClients[pInfo.m_ClientID].m_PrevPredicted.Write(&Prev); + + IntraTick = Client()->PredIntraGameTick(); + NewTick = m_pClient->m_NewPredictedTick; + } } } vec2 Direction = GetDirection((int)(Angle*256.0f)); - vec2 Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick); + vec2 Position; + if (!g_Config.m_ClAntiPing) + Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick); + else + Position = parPosition; vec2 Vel = mix(vec2(Prev.m_VelX/256.0f, Prev.m_VelY/256.0f), vec2(Player.m_VelX/256.0f, Player.m_VelY/256.0f), IntraTick); m_pClient->m_pFlow->Add(Position, Vel*100.0f, 10.0f); @@ -320,6 +539,74 @@ void CPlayers::RenderPlayer( // draw gun { + if (Player.m_PlayerFlags&PLAYERFLAG_AIM && (g_Config.m_ClShowOtherHookColl || pPlayerInfo->m_Local)) + { + float Alpha = 1.0f; + if (OtherTeam) + Alpha = g_Config.m_ClShowOthersAlpha / 100.0f; + + vec2 ExDirection = Direction; + + if (pPlayerInfo->m_Local && Client()->State() != IClient::STATE_DEMOPLAYBACK) + ExDirection = normalize(vec2(m_pClient->m_pControls->m_InputData[g_Config.m_ClDummy].m_TargetX, m_pClient->m_pControls->m_InputData[g_Config.m_ClDummy].m_TargetY)); + + Graphics()->TextureSet(-1); + vec2 initPos = Position; + vec2 finishPos = initPos + ExDirection * (m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength-42.0f); + + Graphics()->LinesBegin(); + Graphics()->SetColor(1.00f, 0.0f, 0.0f, Alpha); + + float PhysSize = 28.0f; + + vec2 OldPos = initPos + ExDirection * PhysSize * 1.5f;; + vec2 NewPos = OldPos; + + bool doBreak = false; + int Hit = 0; + + do { + OldPos = NewPos; + NewPos = OldPos + ExDirection * m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookFireSpeed; + + if (distance(initPos, NewPos) > m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength) + { + NewPos = initPos + normalize(NewPos-initPos) * m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength; + doBreak = true; + } + + int teleNr = 0; + Hit = Collision()->IntersectLineTeleHook(OldPos, NewPos, &finishPos, 0x0, &teleNr, true); + + if(!doBreak && Hit) { + if (!(Hit&CCollision::COLFLAG_NOHOOK)) + Graphics()->SetColor(130.0f/255.0f, 232.0f/255.0f, 160.0f/255.0f, Alpha); + } + + if(m_pClient->m_Tuning[g_Config.m_ClDummy].m_PlayerHooking && m_pClient->IntersectCharacter(OldPos, finishPos, finishPos, pPlayerInfo->m_ClientID) != -1) + { + Graphics()->SetColor(1.0f, 1.0f, 0.0f, Alpha); + break; + } + + if(Hit) + break; + + NewPos.x = round_to_int(NewPos.x); + NewPos.y = round_to_int(NewPos.y); + + if (OldPos == NewPos) + break; + + ExDirection.x = round_to_int(ExDirection.x*256.0f) / 256.0f; + ExDirection.y = round_to_int(ExDirection.y*256.0f) / 256.0f; + } while (!doBreak); + + IGraphics::CLineItem LineItem(initPos.x, initPos.y, finishPos.x, finishPos.y); + Graphics()->LinesDraw(&LineItem, 1); + Graphics()->LinesEnd(); + } + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); Graphics()->QuadsSetRotation(State.GetAttach()->m_Angle*pi*2+Angle); @@ -328,6 +615,9 @@ void CPlayers::RenderPlayer( int iw = clamp(Player.m_Weapon, 0, NUM_WEAPONS-1); RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[iw].m_pSpriteBody, Direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); + if (OtherTeam) + Graphics()->SetColor(1.0f, 1.0f, 1.0f, g_Config.m_ClShowOthersAlpha / 100.0f); + vec2 Dir = Direction; float Recoil = 0.0f; vec2 p; @@ -415,6 +705,8 @@ void CPlayers::RenderPlayer( Recoil = sinf(a*pi); p = Position + Dir * g_pData->m_Weapons.m_aId[iw].m_Offsetx - Dir*Recoil*10.0f; p.y += g_pData->m_Weapons.m_aId[iw].m_Offsety; + if (Player.m_Weapon == WEAPON_GUN && g_Config.m_ClOldGunPosition) + p.y -= 8; RenderTools()->DrawSprite(p.x, p.y, g_pData->m_Weapons.m_aId[iw].m_VisualSize); } @@ -464,6 +756,12 @@ void CPlayers::RenderPlayer( } Graphics()->QuadsEnd(); + if (OtherTeam) + { + RenderInfo.m_ColorBody.a = g_Config.m_ClShowOthersAlpha / 100.0f; + RenderInfo.m_ColorFeet.a = g_Config.m_ClShowOthersAlpha / 100.0f; + } + switch (Player.m_Weapon) { case WEAPON_GUN: RenderHand(&RenderInfo, p, Direction, -3*pi/4, vec2(-15, 4)); break; @@ -474,7 +772,7 @@ void CPlayers::RenderPlayer( } // render the "shadow" tee - if(pInfo.m_Local && g_Config.m_Debug) + if(pInfo.m_Local && (g_Config.m_Debug || g_Config.m_ClUnpredictedShadow)) { vec2 GhostPosition = mix(vec2(pPrevChar->m_X, pPrevChar->m_Y), vec2(pPlayerChar->m_X, pPlayerChar->m_Y), Client()->IntraGameTick()); CTeeRenderInfo Ghost = RenderInfo; @@ -484,9 +782,50 @@ void CPlayers::RenderPlayer( } RenderInfo.m_Size = 64.0f; // force some settings - RenderInfo.m_ColorBody.a = 1.0f; - RenderInfo.m_ColorFeet.a = 1.0f; - RenderTools()->RenderTee(&State, &RenderInfo, Player.m_Emote, Direction, Position); + + if (OtherTeam) + { + RenderInfo.m_ColorBody.a = g_Config.m_ClShowOthersAlpha / 100.0f; + RenderInfo.m_ColorFeet.a = g_Config.m_ClShowOthersAlpha / 100.0f; + } + + if (g_Config.m_ClShowDirection && (!pInfo.m_Local || DemoPlayer()->IsPlaying())) + { + if (Player.m_Direction == -1) + { + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_ARROW].m_Id); + Graphics()->QuadsBegin(); + if (OtherTeam) + Graphics()->SetColor(1.0f, 1.0f, 1.0f, g_Config.m_ClShowOthersAlpha / 100.0f); + IGraphics::CQuadItem QuadItem(Position.x-30, Position.y - 70, 22, 22); + Graphics()->QuadsSetRotation(GetAngle(vec2(1,0))+pi); + Graphics()->QuadsDraw(&QuadItem, 1); + Graphics()->QuadsEnd(); + } + else if (Player.m_Direction == 1) + { + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_ARROW].m_Id); + Graphics()->QuadsBegin(); + if (OtherTeam) + Graphics()->SetColor(1.0f, 1.0f, 1.0f, g_Config.m_ClShowOthersAlpha / 100.0f); + IGraphics::CQuadItem QuadItem(Position.x+30, Position.y - 70, 22, 22); + Graphics()->QuadsDraw(&QuadItem, 1); + Graphics()->QuadsEnd(); + } + if (Player.m_Jumped&1) + { + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_ARROW].m_Id); + Graphics()->QuadsBegin(); + if (OtherTeam) + Graphics()->SetColor(1.0f, 1.0f, 1.0f, g_Config.m_ClShowOthersAlpha / 100.0f); + IGraphics::CQuadItem QuadItem(Position.x, Position.y - 70, 22, 22); + Graphics()->QuadsSetRotation(GetAngle(vec2(0,1))+pi); + Graphics()->QuadsDraw(&QuadItem, 1); + Graphics()->QuadsEnd(); + } + } + + RenderTools()->RenderTee(&State, &RenderInfo, Player.m_Emote, Direction, Position, OtherTeam); if(Player.m_PlayerFlags&PLAYERFLAG_CHATTING) { @@ -530,6 +869,55 @@ void CPlayers::RenderPlayer( Graphics()->QuadsDraw(&QuadItem, 1); Graphics()->QuadsEnd(); } + + if(g_Config.m_ClNameplates && g_Config.m_ClAntiPing) + { + float FontSize = 18.0f + 20.0f * g_Config.m_ClNameplatesSize / 100.0f; + // render name plate + if(!pPlayerInfo->m_Local) + { + float a = 1; + if(g_Config.m_ClNameplatesAlways == 0) + a = clamp(1-powf(distance(m_pClient->m_pControls->m_TargetPos[g_Config.m_ClDummy], Position)/200.0f,16.0f), 0.0f, 1.0f); + + const char *pName = m_pClient->m_aClients[pPlayerInfo->m_ClientID].m_aName; + float tw = TextRender()->TextWidth(0, FontSize, pName, -1); + + vec3 rgb = vec3(1.0f, 1.0f, 1.0f); + if(g_Config.m_ClNameplatesTeamcolors && m_pClient->m_Teams.Team(pPlayerInfo->m_ClientID)) + rgb = HslToRgb(vec3(m_pClient->m_Teams.Team(pPlayerInfo->m_ClientID) / 64.0f, 1.0f, 0.75f)); + + if (OtherTeam) + { + TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 0.2f); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, g_Config.m_ClShowOthersAlpha / 100.0f); + } + else + { + TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 0.5f*a); + TextRender()->TextColor(rgb.r, rgb.g, rgb.b, a); + } + if(g_Config.m_ClNameplatesTeamcolors && m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) + { + if(pPlayerInfo->m_Team == TEAM_RED) + TextRender()->TextColor(1.0f, 0.5f, 0.5f, a); + else if(pPlayerInfo->m_Team == TEAM_BLUE) + TextRender()->TextColor(0.7f, 0.7f, 1.0f, a); + } + + TextRender()->Text(0, Position.x-tw/2.0f, Position.y-FontSize-38.0f, FontSize, pName, -1); + + if(g_Config.m_Debug) // render client id when in debug aswell + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf),"%d", pPlayerInfo->m_ClientID); + TextRender()->Text(0, Position.x, Position.y-90, 28.0f, aBuf, -1); + } + + TextRender()->TextColor(1,1,1,1); + TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 0.3f); + } + } } void CPlayers::OnRender() @@ -541,7 +929,7 @@ void CPlayers::OnRender() for(int i = 0; i < MAX_CLIENTS; ++i) { m_aRenderInfo[i] = m_pClient->m_aClients[i].m_RenderInfo; - if(m_pClient->m_Snap.m_aCharacters[i].m_Cur.m_Weapon == WEAPON_NINJA) + if(m_pClient->m_Snap.m_aCharacters[i].m_Cur.m_Weapon == WEAPON_NINJA && g_Config.m_ClShowNinja) { // change the skin for the player to the ninja int Skin = m_pClient->m_pSkins->Find("x_ninja"); @@ -559,6 +947,57 @@ void CPlayers::OnRender() } } + static vec2 PrevPos[MAX_CLIENTS]; + static vec2 SmoothPos[MAX_CLIENTS]; + static int MoveCnt[MAX_CLIENTS] = {0}; + static vec2 PredictedPos[MAX_CLIENTS]; + + static int predcnt = 0; + + if (g_Config.m_ClAntiPing) + { + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(!m_pClient->m_Snap.m_aCharacters[i].m_Active) + continue; + const void *pPrevInfo = Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_PLAYERINFO, i); + const void *pInfo = Client()->SnapFindItem(IClient::SNAP_CURRENT, NETOBJTYPE_PLAYERINFO, i); + + if(pPrevInfo && pInfo) + { + CNetObj_Character PrevChar = m_pClient->m_Snap.m_aCharacters[i].m_Prev; + CNetObj_Character CurChar = m_pClient->m_Snap.m_aCharacters[i].m_Cur; + + Predict( + &PrevChar, + &CurChar, + (const CNetObj_PlayerInfo *)pPrevInfo, + (const CNetObj_PlayerInfo *)pInfo, + PrevPos[i], + SmoothPos[i], + MoveCnt[i], + PredictedPos[i] + ); + } + } + + if(g_Config.m_ClAntiPing && g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) + if(m_pClient->m_Snap.m_pLocalCharacter && !(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + { + // double ping = m_pClient->m_Snap.m_pLocalInfo->m_Latency; + // static double fps; + // fps = mix(fps, (1. / Client()->RenderFrameTime()), 0.1); + + // int predmax = (fps * ping / 1000.); + + int predmax = 19; + // if( 0 <= predmax && predmax <= 100) + predcnt = (predcnt + 1) % predmax; + // else + // predcnt = (predcnt + 1) % 2; + } + } + // render other players in two passes, first pass we render the other, second pass we render our self for(int p = 0; p < 4; p++) { @@ -582,19 +1021,36 @@ void CPlayers::OnRender() CNetObj_Character CurChar = m_pClient->m_Snap.m_aCharacters[i].m_Cur; if(p<2) - RenderHook( - &PrevChar, - &CurChar, - (const CNetObj_PlayerInfo *)pPrevInfo, - (const CNetObj_PlayerInfo *)pInfo - ); + { + if(PrevChar.m_HookedPlayer != -1) + RenderHook( + &PrevChar, + &CurChar, + (const CNetObj_PlayerInfo *)pPrevInfo, + (const CNetObj_PlayerInfo *)pInfo, + PredictedPos[i], + PredictedPos[PrevChar.m_HookedPlayer] + ); + else + RenderHook( + &PrevChar, + &CurChar, + (const CNetObj_PlayerInfo *)pPrevInfo, + (const CNetObj_PlayerInfo *)pInfo, + PredictedPos[i], + PredictedPos[i] + ); + } else + { RenderPlayer( &PrevChar, &CurChar, (const CNetObj_PlayerInfo *)pPrevInfo, - (const CNetObj_PlayerInfo *)pInfo + (const CNetObj_PlayerInfo *)pInfo, + PredictedPos[i] ); + } } } } diff --git a/src/game/client/components/players.h b/src/game/client/components/players.h index 9f23a0e624..d5073d8c04 100644 --- a/src/game/client/components/players.h +++ b/src/game/client/components/players.h @@ -12,13 +12,31 @@ class CPlayers : public CComponent const CNetObj_Character *pPrevChar, const CNetObj_Character *pPlayerChar, const CNetObj_PlayerInfo *pPrevInfo, - const CNetObj_PlayerInfo *pPlayerInfo + const CNetObj_PlayerInfo *pPlayerInfo, + const vec2 &Position +/* vec2 &PrevPredPos, + vec2 &SmoothPos, + int &MoveCnt +*/ ); void RenderHook( const CNetObj_Character *pPrevChar, const CNetObj_Character *pPlayerChar, const CNetObj_PlayerInfo *pPrevInfo, - const CNetObj_PlayerInfo *pPlayerInfo + const CNetObj_PlayerInfo *pPlayerInfo, + const vec2 &Position, + const vec2 &PositionTo + ); + + void Predict( + const CNetObj_Character *pPrevChar, + const CNetObj_Character *pPlayerChar, + const CNetObj_PlayerInfo *pPrevInfo, + const CNetObj_PlayerInfo *pPlayerInfo, + vec2 &PrevPredPos, + vec2 &SmoothPos, + int &MoveCnt, + vec2 &Position ); public: diff --git a/src/game/client/components/scoreboard.cpp b/src/game/client/components/scoreboard.cpp index ae3a61c854..2156b9f9fa 100644 --- a/src/game/client/components/scoreboard.cpp +++ b/src/game/client/components/scoreboard.cpp @@ -17,6 +17,7 @@ #include "scoreboard.h" +#include #include #include @@ -31,13 +32,14 @@ void CScoreboard::ConKeyScoreboard(IConsole::IResult *pResult, void *pUserData) CServerInfo Info; pSelf->Client()->GetServerInfo(&Info); - pSelf->m_IsGameTypeRace = str_find_nocase(Info.m_aGameType, "race"); + pSelf->m_IsGameTypeRace = str_find_nocase(Info.m_aGameType, "race") || str_find_nocase(Info.m_aGameType, "fastcap"); pSelf->m_Active = pResult->GetInteger(0) != 0; } void CScoreboard::OnReset() { m_Active = false; + m_ServerRecord = -1.0f; } void CScoreboard::OnRelease() @@ -45,6 +47,16 @@ void CScoreboard::OnRelease() m_Active = false; } +void CScoreboard::OnMessage(int MsgType, void *pRawMsg) +{ + if(MsgType == NETMSGTYPE_SV_RECORD) + { + CNetMsg_Sv_Record *pMsg = (CNetMsg_Sv_Record *)pRawMsg; + m_ServerRecord = (float)pMsg->m_ServerTimeBest/100; + //m_PlayerRecord = (float)pMsg->m_PlayerTimeBest/100; + } +} + void CScoreboard::OnConsoleInit() { Console()->Register("+scoreboard", "", CFGFLAG_CLIENT, ConKeyScoreboard, this, "Show scoreboard"); @@ -138,6 +150,29 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch if(Team == TEAM_SPECTATORS) return; + bool lower16 = false; + bool upper16 = false; + bool lower24 = false; + bool upper24 = false; + bool lower32 = false; + bool upper32 = false; + + if(Team == -3) + upper16 = true; + else if(Team == -4) + lower32 = true; + else if(Team == -5) + upper32 = true; + else if(Team == -6) + lower16 = true; + else if(Team == -7) + lower24 = true; + else if(Team == -8) + upper24 = true; + + if(Team < -1) + Team = 0; + float h = 760.0f; // background @@ -145,7 +180,12 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.5f); - RenderTools()->DrawRoundRect(x, y, w, h, 17.0f); + if(upper16 || upper32 || upper24) + RenderTools()->DrawRoundRectExt(x, y, w, h, 17.0f, 10); + else if(lower16 || lower32 || lower24) + RenderTools()->DrawRoundRectExt(x, y, w, h, 17.0f, 5); + else + RenderTools()->DrawRoundRect(x, y, w, h, 17.0f); Graphics()->QuadsEnd(); // render title @@ -160,6 +200,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch TextRender()->Text(0, x+20.0f, y, TitleFontsize, pTitle, -1); char aBuf[128] = {0}; + if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) { if(m_pClient->m_Snap.m_pGameDataObj) @@ -182,25 +223,58 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch str_format(aBuf, sizeof(aBuf), "%d", Score); } } - float tw = TextRender()->TextWidth(0, TitleFontsize, aBuf, -1); - TextRender()->Text(0, x+w-tw-20.0f, y, TitleFontsize, aBuf, -1); + + if(m_IsGameTypeRace && g_Config.m_ClDDRaceScoreBoard) + { + if (m_ServerRecord > 0) + { + str_format(aBuf, sizeof(aBuf), "%02d:%02d", ((int) m_ServerRecord)/60, ((int) m_ServerRecord)%60); + } + else + aBuf[0] = 0; + } + + float tw; + + if (!lower16 && !lower32 && !lower24) + { + tw = TextRender()->TextWidth(0, TitleFontsize, aBuf, -1); + TextRender()->Text(0, x+w-tw-20.0f, y, TitleFontsize, aBuf, -1); + } // calculate measurements x += 10.0f; float LineHeight = 60.0f; float TeeSizeMod = 1.0f; float Spacing = 16.0f; - if(m_pClient->m_Snap.m_aTeamSize[Team] > 12) + float RoundRadius = 15.0f; + if(m_pClient->m_Snap.m_aTeamSize[Team] > 48) + { + LineHeight = 20.0f; + TeeSizeMod = 0.4f; + Spacing = 0.0f; + RoundRadius = 5.0f; + } + else if(m_pClient->m_Snap.m_aTeamSize[Team] > 32) + { + LineHeight = 27.0f; + TeeSizeMod = 0.6f; + Spacing = 0.0f; + RoundRadius = 5.0f; + } + else if(m_pClient->m_Snap.m_aTeamSize[Team] > 12) { LineHeight = 40.0f; TeeSizeMod = 0.8f; Spacing = 0.0f; + RoundRadius = 15.0f; } else if(m_pClient->m_Snap.m_aTeamSize[Team] > 8) { LineHeight = 50.0f; TeeSizeMod = 0.9f; - Spacing = 8.0f; + Spacing = 5.0f; + RoundRadius = 15.0f; } float ScoreOffset = x+10.0f, ScoreLength = TextRender()->TextWidth(0, 22.0f/*HeadlineFontsize*/, "00:00:0", -1); @@ -228,33 +302,123 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch // render player entries y += HeadlineFontsize*2.0f; float FontSize = 24.0f; + if(m_pClient->m_Snap.m_aTeamSize[Team] > 48) + FontSize = 16.0f; + else if(m_pClient->m_Snap.m_aTeamSize[Team] > 32) + FontSize = 20.0f; CTextCursor Cursor; + int rendered = 0; + if (upper16) + rendered = -16; + if (upper32) + rendered = -32; + if (upper24) + rendered = -24; + + int OldDDTeam = -1; + for(int i = 0; i < MAX_CLIENTS; i++) { // make sure that we render the correct team - const CNetObj_PlayerInfo *pInfo = m_pClient->m_Snap.m_paInfoByScore[i]; + const CNetObj_PlayerInfo *pInfo = m_pClient->m_Snap.m_paInfoByDDTeam[i]; if(!pInfo || pInfo->m_Team != Team) continue; + if (rendered++ < 0) continue; + + int DDTeam = ((CGameClient *) m_pClient)->m_Teams.Team(pInfo->m_ClientID); + int NextDDTeam = 0; + + for(int j = i + 1; j < MAX_CLIENTS; j++) + { + const CNetObj_PlayerInfo *pInfo2 = m_pClient->m_Snap.m_paInfoByDDTeam[j]; + + if(!pInfo2 || pInfo2->m_Team != Team) + continue; + + NextDDTeam = ((CGameClient *) m_pClient)->m_Teams.Team(pInfo2->m_ClientID); + break; + } + + if (OldDDTeam == -1) + { + for (int j = i - 1; j >= 0; j--) + { + const CNetObj_PlayerInfo *pInfo2 = m_pClient->m_Snap.m_paInfoByDDTeam[j]; + + if(!pInfo2 || pInfo2->m_Team != Team) + continue; + + OldDDTeam = ((CGameClient *) m_pClient)->m_Teams.Team(pInfo2->m_ClientID); + break; + } + } + + if (DDTeam != TEAM_FLOCK) + { + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + vec3 rgb = HslToRgb(vec3(DDTeam / 64.0f, 1.0f, 0.5f)); + Graphics()->SetColor(rgb.r, rgb.g, rgb.b, 0.5f); + + int Corners = 0; + + if (OldDDTeam != DDTeam) + Corners |= CUI::CORNER_TL | CUI::CORNER_TR; + if (NextDDTeam != DDTeam) + Corners |= CUI::CORNER_BL | CUI::CORNER_BR; + + RenderTools()->DrawRoundRectExt(x - 10.0f, y, w, LineHeight + Spacing, RoundRadius, Corners); + + Graphics()->QuadsEnd(); + + if (NextDDTeam != DDTeam) + { + char aBuf[64]; + if(m_pClient->m_Snap.m_aTeamSize[0] > 8) + { + str_format(aBuf, sizeof(aBuf),"%d", DDTeam); + tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); + TextRender()->SetCursor(&Cursor, x - 10.0f, y + Spacing + FontSize - (FontSize/1.5f), FontSize/1.5f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = NameLength+3; + } + else + { + str_format(aBuf, sizeof(aBuf),"Team %d", DDTeam); + tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); + TextRender()->SetCursor(&Cursor, ScoreOffset+w/2.0f-tw/2.0f, y + LineHeight - Spacing/3.0f, FontSize/1.5f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = NameLength+3; + } + TextRender()->TextEx(&Cursor, aBuf, -1); + } + } + + OldDDTeam = DDTeam; + // background so it's easy to find the local player or the followed one in spectator mode if(pInfo->m_Local || (m_pClient->m_Snap.m_SpecInfo.m_Active && pInfo->m_ClientID == m_pClient->m_Snap.m_SpecInfo.m_SpectatorID)) { Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.25f); - RenderTools()->DrawRoundRect(x, y, w-20.0f, LineHeight, 15.0f); + RenderTools()->DrawRoundRect(x, y, w-20.0f, LineHeight, RoundRadius); Graphics()->QuadsEnd(); } // score if(m_IsGameTypeRace && g_Config.m_ClDDRaceScoreBoard) { - int Time = pInfo->m_Score == -9999 ? 0 : abs(pInfo->m_Score); - str_format(aBuf, sizeof(aBuf), "%02d:%02d", Time/60, Time%60); + if (pInfo->m_Score == -9999) + aBuf[0] = 0; + else + { + int Time = abs(pInfo->m_Score); + str_format(aBuf, sizeof(aBuf), "%02d:%02d", Time/60, Time%60); + } } else - str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Score, -999, 999)); + str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Score, -999, 999)); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->SetCursor(&Cursor, ScoreOffset+ScoreLength-tw, y+Spacing, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = ScoreLength; @@ -317,9 +481,37 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch TextRender()->TextEx(&Cursor, aBuf, -1); y += LineHeight+Spacing; + if (lower32 || upper32) { + if (rendered == 32) break; + } else if (lower24 || upper24) { + if (rendered == 24) break; + } else { + if (rendered == 16) break; + } } } +void CScoreboard::RenderLocalTime(float x) +{ + //draw the box + Graphics()->BlendNormal(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.4f); + RenderTools()->DrawRoundRectExt(x-120.0f, 0.0f, 100.0f, 50.0f, 15.0f, CUI::CORNER_B); + Graphics()->QuadsEnd(); + + time_t rawtime; + struct tm *timeinfo; + time(&rawtime); + timeinfo = localtime(&rawtime); + + //draw the text + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%02d:%02d", timeinfo->tm_hour, timeinfo->tm_min); + TextRender()->Text(0, x-100.0f, 10.0f, 20.0f, aBuf, -1); +} + void CScoreboard::RenderRecordingNotification(float x) { if(!m_pClient->DemoRecorder()->IsRecording()) @@ -366,7 +558,24 @@ void CScoreboard::OnRender() if(m_pClient->m_Snap.m_pGameInfoObj) { if(!(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS)) - RenderScoreboard(Width/2-w/2, 150.0f, w, 0, 0); + { + if(m_pClient->m_Snap.m_aTeamSize[0] > 48) + { + RenderScoreboard(Width/2-w, 150.0f, w, -4, 0); + RenderScoreboard(Width/2, 150.0f, w, -5, ""); + } else if(m_pClient->m_Snap.m_aTeamSize[0] > 32) + { + RenderScoreboard(Width/2-w, 150.0f, w, -7, 0); + RenderScoreboard(Width/2, 150.0f, w, -8, ""); + } else if(m_pClient->m_Snap.m_aTeamSize[0] > 16) + { + RenderScoreboard(Width/2-w, 150.0f, w, -6, 0); + RenderScoreboard(Width/2, 150.0f, w, -3, ""); + } else + { + RenderScoreboard(Width/2-w/2, 150.0f, w, 0, 0); + } + } else { const char *pRedClanName = GetClanName(TEAM_RED); @@ -404,6 +613,7 @@ void CScoreboard::OnRender() RenderGoals(Width/2-w/2, 150+760+10, w); RenderSpectators(Width/2-w/2, 150+760+10+50+10, w); RenderRecordingNotification((Width/7)*4); + RenderLocalTime((Width/7)*3); } bool CScoreboard::Active() diff --git a/src/game/client/components/scoreboard.h b/src/game/client/components/scoreboard.h index ed7b587c2d..cccc9290b4 100644 --- a/src/game/client/components/scoreboard.h +++ b/src/game/client/components/scoreboard.h @@ -10,6 +10,7 @@ class CScoreboard : public CComponent void RenderSpectators(float x, float y, float w); void RenderScoreboard(float x, float y, float w, int Team, const char *pTitle); void RenderRecordingNotification(float x); + void RenderLocalTime(float x); static void ConKeyScoreboard(IConsole::IResult *pResult, void *pUserData); @@ -28,9 +29,12 @@ class CScoreboard : public CComponent // DDRace + virtual void OnMessage(int MsgType, void *pRawMsg); + private: bool m_IsGameTypeRace; + float m_ServerRecord; }; #endif diff --git a/src/game/client/components/spectator.cpp b/src/game/client/components/spectator.cpp index c09b2ee21b..08a5d979d8 100644 --- a/src/game/client/components/spectator.cpp +++ b/src/game/client/components/spectator.cpp @@ -33,38 +33,43 @@ void CSpectator::ConSpectateNext(IConsole::IResult *pResult, void *pUserData) int NewSpectatorID; bool GotNewSpectatorID = false; + int CurPos = -1; + for (int i = 0; i < MAX_CLIENTS; i++) + if (pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i] && pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID == pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID) + CurPos = i; + if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW) { for(int i = 0; i < MAX_CLIENTS; i++) { - if(!pSelf->m_pClient->m_Snap.m_paPlayerInfos[i] || pSelf->m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) + if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_Team == TEAM_SPECTATORS) continue; - NewSpectatorID = i; + NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID; GotNewSpectatorID = true; break; } } else { - for(int i = pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID + 1; i < MAX_CLIENTS; i++) + for(int i = CurPos + 1; i < MAX_CLIENTS; i++) { - if(!pSelf->m_pClient->m_Snap.m_paPlayerInfos[i] || pSelf->m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) + if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_Team == TEAM_SPECTATORS) continue; - NewSpectatorID = i; + NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID; GotNewSpectatorID = true; break; } if(!GotNewSpectatorID) { - for(int i = 0; i < pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID; i++) + for(int i = 0; i < CurPos; i++) { - if(!pSelf->m_pClient->m_Snap.m_paPlayerInfos[i] || pSelf->m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) + if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_Team == TEAM_SPECTATORS) continue; - NewSpectatorID = i; + NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID; GotNewSpectatorID = true; break; } @@ -80,38 +85,43 @@ void CSpectator::ConSpectatePrevious(IConsole::IResult *pResult, void *pUserData int NewSpectatorID; bool GotNewSpectatorID = false; + int CurPos = -1; + for (int i = 0; i < MAX_CLIENTS; i++) + if (pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i] && pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID == pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID) + CurPos = i; + if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW) { for(int i = MAX_CLIENTS -1; i > -1; i--) { - if(!pSelf->m_pClient->m_Snap.m_paPlayerInfos[i] || pSelf->m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) + if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_Team == TEAM_SPECTATORS) continue; - NewSpectatorID = i; + NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID; GotNewSpectatorID = true; break; } } else { - for(int i = pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID - 1; i > -1; i--) + for(int i = CurPos - 1; i > -1; i--) { - if(!pSelf->m_pClient->m_Snap.m_paPlayerInfos[i] || pSelf->m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) + if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_Team == TEAM_SPECTATORS) continue; - NewSpectatorID = i; + NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID; GotNewSpectatorID = true; break; } if(!GotNewSpectatorID) { - for(int i = MAX_CLIENTS - 1; i > pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID; i--) + for(int i = MAX_CLIENTS - 1; i > CurPos; i--) { - if(!pSelf->m_pClient->m_Snap.m_paPlayerInfos[i] || pSelf->m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) + if(!pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i] || pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_Team == TEAM_SPECTATORS) continue; - NewSpectatorID = i; + NewSpectatorID = pSelf->m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID; GotNewSpectatorID = true; break; } @@ -124,6 +134,7 @@ void CSpectator::ConSpectatePrevious(IConsole::IResult *pResult, void *pUserData CSpectator::CSpectator() { OnReset(); + m_OldMouseX = m_OldMouseY = 0.0f; } void CSpectator::OnConsoleInit() @@ -139,8 +150,18 @@ bool CSpectator::OnMouseMove(float x, float y) if(!m_Active) return false; +#if defined(__ANDROID__) // No relative mouse on Android + m_SelectorMouse = vec2(x,y); + if( m_OldMouseX != x || m_OldMouseY != y ) + { + m_OldMouseX = x; + m_OldMouseY = y; + m_SelectorMouse = vec2((x - g_Config.m_GfxScreenWidth/2), (y - g_Config.m_GfxScreenHeight/2)); + } +#else UI()->ConvertMouseMove(&x, &y); m_SelectorMouse += vec2(x,y); +#endif return true; } @@ -175,6 +196,39 @@ void CSpectator::OnRender() // draw background float Width = 400*3.0f*Graphics()->ScreenAspect(); float Height = 400*3.0f; + float ObjWidth = 300.0f; + float FontSize = 20.0f; + float BigFontSize = 20.0f; + float StartY = -190.0f; + float LineHeight = 60.0f; + float TeeSizeMod = 1.0f; + float RoundRadius = 30.0f; + bool Selected = false; + int TotalPlayers = 0; + int PerLine = 8; + float BoxMove = -10.0f; + + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(!m_pClient->m_Snap.m_paInfoByDDTeam[i] || m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_Team == TEAM_SPECTATORS) + continue; + + ++TotalPlayers; + } + + if (TotalPlayers > 32) + { + FontSize = 18.0f; + LineHeight = 30.0f; + TeeSizeMod = 0.7f; + PerLine = 16; + RoundRadius = 10.0f; + BoxMove = 3.0f; + } + if (TotalPlayers > 16) + { + ObjWidth = 600.0f; + } Graphics()->MapScreen(0, 0, Width, Height); @@ -182,78 +236,128 @@ void CSpectator::OnRender() Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.3f); - RenderTools()->DrawRoundRect(Width/2.0f-300.0f, Height/2.0f-300.0f, 600.0f, 600.0f, 20.0f); + RenderTools()->DrawRoundRect(Width/2.0f-ObjWidth, Height/2.0f-300.0f, ObjWidth*2, 600.0f, 20.0f); Graphics()->QuadsEnd(); // clamp mouse position to selector area - m_SelectorMouse.x = clamp(m_SelectorMouse.x, -280.0f, 280.0f); + m_SelectorMouse.x = clamp(m_SelectorMouse.x, -(ObjWidth - 20.0f), ObjWidth - 20.0f); m_SelectorMouse.y = clamp(m_SelectorMouse.y, -280.0f, 280.0f); // draw selections - float FontSize = 20.0f; - float StartY = -190.0f; - float LineHeight = 60.0f; - bool Selected = false; - if(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW) { Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.25f); - RenderTools()->DrawRoundRect(Width/2.0f-280.0f, Height/2.0f-280.0f, 270.0f, 60.0f, 20.0f); + RenderTools()->DrawRoundRect(Width/2.0f-(ObjWidth - 20.0f), Height/2.0f-280.0f, 270.0f, 60.0f, 20.0f); Graphics()->QuadsEnd(); } - if(m_SelectorMouse.x >= -280.0f && m_SelectorMouse.x <= -10.0f && + if(m_SelectorMouse.x >= -(ObjWidth-20.0f) && m_SelectorMouse.x <= -(ObjWidth-300+10.0f) && m_SelectorMouse.y >= -280.0f && m_SelectorMouse.y <= -220.0f) { m_SelectedSpectatorID = SPEC_FREEVIEW; Selected = true; } TextRender()->TextColor(1.0f, 1.0f, 1.0f, Selected?1.0f:0.5f); - TextRender()->Text(0, Width/2.0f-240.0f, Height/2.0f-265.0f, FontSize, Localize("Free-View"), -1); + TextRender()->Text(0, Width/2.0f-(ObjWidth-60.0f), Height/2.0f-265.0f, BigFontSize, Localize("Free-View"), -1); + + float x = -(ObjWidth - 30.0f), y = StartY; + + int OldDDTeam = -1; - float x = -270.0f, y = StartY; for(int i = 0, Count = 0; i < MAX_CLIENTS; ++i) { - if(!m_pClient->m_Snap.m_paPlayerInfos[i] || m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) + if(!m_pClient->m_Snap.m_paInfoByDDTeam[i] || m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_Team == TEAM_SPECTATORS) continue; - if(++Count%9 == 0) + ++Count; + + if(Count == PerLine + 1 || (Count > PerLine + 1 && (Count-1)%PerLine == 0)) { x += 290.0f; y = StartY; } - if(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == i) + const CNetObj_PlayerInfo *pInfo = m_pClient->m_Snap.m_paInfoByDDTeam[i]; + int DDTeam = ((CGameClient *) m_pClient)->m_Teams.Team(pInfo->m_ClientID); + int NextDDTeam = 0; + + for(int j = i + 1; j < MAX_CLIENTS; j++) + { + const CNetObj_PlayerInfo *pInfo2 = m_pClient->m_Snap.m_paInfoByDDTeam[j]; + + if(!pInfo2 || pInfo2->m_Team == TEAM_SPECTATORS) + continue; + + NextDDTeam = ((CGameClient *) m_pClient)->m_Teams.Team(pInfo2->m_ClientID); + break; + } + + if (OldDDTeam == -1) + { + for (int j = i - 1; j >= 0; j--) + { + const CNetObj_PlayerInfo *pInfo2 = m_pClient->m_Snap.m_paInfoByDDTeam[j]; + + if(!pInfo2 || pInfo2->m_Team == TEAM_SPECTATORS) + continue; + + OldDDTeam = ((CGameClient *) m_pClient)->m_Teams.Team(pInfo2->m_ClientID); + break; + } + } + + if (DDTeam != TEAM_FLOCK) + { + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + vec3 rgb = HslToRgb(vec3(DDTeam / 64.0f, 1.0f, 0.5f)); + Graphics()->SetColor(rgb.r, rgb.g, rgb.b, 0.5f); + + int Corners = 0; + + if (OldDDTeam != DDTeam) + Corners |= CUI::CORNER_TL | CUI::CORNER_TR; + if (NextDDTeam != DDTeam) + Corners |= CUI::CORNER_BL | CUI::CORNER_BR; + + RenderTools()->DrawRoundRectExt(Width/2.0f+x-10.0f, Height/2.0f+y+BoxMove, 270.0f, LineHeight, RoundRadius, Corners); + + Graphics()->QuadsEnd(); + } + + OldDDTeam = DDTeam; + + if(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID) { Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.25f); - RenderTools()->DrawRoundRect(Width/2.0f+x-10.0f, Height/2.0f+y-10.0f, 270.0f, 60.0f, 20.0f); + RenderTools()->DrawRoundRect(Width/2.0f+x-10.0f, Height/2.0f+y+BoxMove, 270.0f, LineHeight, RoundRadius); Graphics()->QuadsEnd(); } Selected = false; - if(m_SelectorMouse.x >= x-10.0f && m_SelectorMouse.x <= x+260.0f && - m_SelectorMouse.y >= y-10.0f && m_SelectorMouse.y <= y+50.0f) + if(m_SelectorMouse.x >= x-10.0f && m_SelectorMouse.x < x+260.0f && + m_SelectorMouse.y >= y-(LineHeight/6.0f) && m_SelectorMouse.y < y+(LineHeight*5.0f/6.0f)) { - m_SelectedSpectatorID = i; + m_SelectedSpectatorID = m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID; Selected = true; } TextRender()->TextColor(1.0f, 1.0f, 1.0f, Selected?1.0f:0.5f); - TextRender()->Text(0, Width/2.0f+x+50.0f, Height/2.0f+y+5.0f, FontSize, m_pClient->m_aClients[i].m_aName, 220.0f); + TextRender()->Text(0, Width/2.0f+x+50.0f, Height/2.0f+y+5.0f, FontSize, m_pClient->m_aClients[m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID].m_aName, 220.0f); // flag if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS && - m_pClient->m_Snap.m_pGameDataObj && (m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierRed == m_pClient->m_Snap.m_paPlayerInfos[i]->m_ClientID || - m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierBlue == m_pClient->m_Snap.m_paPlayerInfos[i]->m_ClientID)) + m_pClient->m_Snap.m_pGameDataObj && (m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierRed == m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID || + m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierBlue == m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID)) { Graphics()->BlendNormal(); Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); - RenderTools()->SelectSprite(m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team==TEAM_RED ? SPRITE_FLAG_BLUE : SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); + RenderTools()->SelectSprite(m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_Team==TEAM_RED ? SPRITE_FLAG_BLUE : SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); float Size = LineHeight; IGraphics::CQuadItem QuadItem(Width/2.0f+x-LineHeight/5.0f, Height/2.0f+y-LineHeight/3.0f, Size/2.0f, Size); @@ -261,7 +365,8 @@ void CSpectator::OnRender() Graphics()->QuadsEnd(); } - CTeeRenderInfo TeeInfo = m_pClient->m_aClients[i].m_RenderInfo; + CTeeRenderInfo TeeInfo = m_pClient->m_aClients[m_pClient->m_Snap.m_paInfoByDDTeam[i]->m_ClientID].m_RenderInfo; + TeeInfo.m_Size *= TeeSizeMod; RenderTools()->RenderTee(CAnimState::GetIdle(), &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), vec2(Width/2.0f+x+20.0f, Height/2.0f+y+20.0f)); y += LineHeight; diff --git a/src/game/client/components/spectator.h b/src/game/client/components/spectator.h index 8e775cff9f..11ed42233c 100644 --- a/src/game/client/components/spectator.h +++ b/src/game/client/components/spectator.h @@ -19,6 +19,9 @@ class CSpectator : public CComponent int m_SelectedSpectatorID; vec2 m_SelectorMouse; + float m_OldMouseX; + float m_OldMouseY; + static void ConKeySpectator(IConsole::IResult *pResult, void *pUserData); static void ConSpectate(IConsole::IResult *pResult, void *pUserData); static void ConSpectateNext(IConsole::IResult *pResult, void *pUserData); diff --git a/src/game/client/components/voting.cpp b/src/game/client/components/voting.cpp index 675d677053..adcf7d5bf4 100644 --- a/src/game/client/components/voting.cpp +++ b/src/game/client/components/voting.cpp @@ -113,6 +113,7 @@ void CVoting::AddvoteOption(const char *pDescription, const char *pCommand) void CVoting::Vote(int v) { + m_Voted = v; CNetMsg_Cl_Vote Msg = {v}; Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } @@ -120,7 +121,12 @@ void CVoting::Vote(int v) CVoting::CVoting() { ClearOptions(); - OnReset(); + + m_Closetime = 0; + m_aDescription[0] = 0; + m_aReason[0] = 0; + m_Yes = m_No = m_Pass = m_Total = 0; + m_Voted = 0; } void CVoting::AddOption(const char *pDescription) diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index a60d2da667..0b54e5ef87 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -16,6 +16,11 @@ #include #include +#include +#include + +#include + #include #include #include "render.h" @@ -99,6 +104,11 @@ const char *CGameClient::Version() { return GAME_VERSION; } const char *CGameClient::NetVersion() { return GAME_NETVERSION; } const char *CGameClient::GetItemName(int Type) { return m_NetObjHandler.GetObjName(Type); } +void CGameClient::ResetDummyInput() +{ + m_pControls->ResetDummyInput(); +} + void CGameClient::OnConsoleInit() { m_pEngine = Kernel()->RequestInterface(); @@ -111,6 +121,9 @@ void CGameClient::OnConsoleInit() m_pDemoPlayer = Kernel()->RequestInterface(); m_pDemoRecorder = Kernel()->RequestInterface(); m_pServerBrowser = Kernel()->RequestInterface(); +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + m_pAutoUpdate = Kernel()->RequestInterface(); +#endif m_pEditor = Kernel()->RequestInterface(); m_pFriends = Kernel()->RequestInterface(); @@ -227,6 +240,16 @@ void CGameClient::OnConsoleInit() Console()->Chain("player_color_feet", ConchainSpecialInfoupdate, this); Console()->Chain("player_skin", ConchainSpecialInfoupdate, this); + Console()->Chain("dummy_name", ConchainSpecialDummyInfoupdate, this); + Console()->Chain("dummy_clan", ConchainSpecialDummyInfoupdate, this); + Console()->Chain("dummy_country", ConchainSpecialDummyInfoupdate, this); + Console()->Chain("dummy_use_custom_color", ConchainSpecialDummyInfoupdate, this); + Console()->Chain("dummy_color_body", ConchainSpecialDummyInfoupdate, this); + Console()->Chain("dummy_color_feet", ConchainSpecialDummyInfoupdate, this); + Console()->Chain("dummy_skin", ConchainSpecialDummyInfoupdate, this); + + Console()->Chain("cl_dummy", ConchainSpecialDummy, this); + // m_SuppressEvents = false; } @@ -267,6 +290,8 @@ void CGameClient::OnInit() for(int i = m_All.m_Num-1; i >= 0; --i) m_All.m_paComponents[i]->OnInit(); + char aBuf[256]; + // setup load amount// load textures for(int i = 0; i < g_pData->m_NumImages; i++) { @@ -274,18 +299,23 @@ void CGameClient::OnInit() g_GameClient.m_pMenus->RenderLoading(); } +#if defined(__ANDROID__) + m_pMapimages->OnMapLoad(); // Reload map textures on Android +#endif + for(int i = 0; i < m_All.m_Num; i++) m_All.m_paComponents[i]->OnReset(); int64 End = time_get(); - char aBuf[256]; str_format(aBuf, sizeof(aBuf), "initialisation finished after %.2fms", ((End-Start)*1000)/(float)time_freq()); Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "gameclient", aBuf); m_ServerMode = SERVERMODE_PURE; - m_DDRaceMsgSent = false; - m_ShowOthers = -1; + m_DDRaceMsgSent[0] = false; + m_DDRaceMsgSent[1] = false; + m_ShowOthers[0] = -1; + m_ShowOthers[1] = -1; // Set free binds to DDRace binds if it's active if(!g_Config.m_ClDDRaceBindsSet && g_Config.m_ClDDRaceBinds) @@ -297,7 +327,9 @@ void CGameClient::DispatchInput() // handle mouse movement float x = 0.0f, y = 0.0f; Input()->MouseRelative(&x, &y); +#if !defined(__ANDROID__) // No relative mouse on Android if(x != 0.0f || y != 0.0f) +#endif { for(int h = 0; h < m_Input.m_Num; h++) { @@ -352,12 +384,17 @@ void CGameClient::OnConnected() // send the inital info SendInfo(true); + // we should keep this in for now, because otherwise you can't spectate + // people at start as the other info 64 packet is only sent after the first + // snap + Client()->Rcon("crashmeplx"); } void CGameClient::OnReset() { // clear out the invalid pointers - m_LastNewPredictedTick = -1; + m_LastNewPredictedTick[0] = -1; + m_LastNewPredictedTick[1] = -1; mem_zero(&g_GameClient.m_Snap, sizeof(g_GameClient.m_Snap)); for(int i = 0; i < MAX_CLIENTS; i++) @@ -369,11 +406,13 @@ void CGameClient::OnReset() m_DemoSpecID = SPEC_FREEVIEW; m_FlagDropTick[TEAM_RED] = 0; m_FlagDropTick[TEAM_BLUE] = 0; - m_Tuning = CTuningParams(); + m_Tuning[g_Config.m_ClDummy] = CTuningParams(); m_Teams.Reset(); - m_DDRaceMsgSent = false; - m_ShowOthers = -1; + m_DDRaceMsgSent[0] = false; + m_DDRaceMsgSent[1] = false; + m_ShowOthers[0] = -1; + m_ShowOthers[1] = -1; } @@ -382,12 +421,25 @@ void CGameClient::UpdatePositions() // local character position if(g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) { - if(!m_Snap.m_pLocalCharacter || (m_Snap.m_pGameInfoObj && m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + if (!g_Config.m_ClAntiPing) { - // don't use predicted + if(!m_Snap.m_pLocalCharacter || (m_Snap.m_pGameInfoObj && m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + { + // don't use predicted + } + else + m_LocalCharacterPos = mix(m_PredictedPrevChar.m_Pos, m_PredictedChar.m_Pos, Client()->PredIntraGameTick()); } else - m_LocalCharacterPos = mix(m_PredictedPrevChar.m_Pos, m_PredictedChar.m_Pos, Client()->PredIntraGameTick()); + { + if(!(m_Snap.m_pGameInfoObj && m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + { + if (m_Snap.m_pLocalCharacter) + m_LocalCharacterPos = mix(m_PredictedPrevChar.m_Pos, m_PredictedChar.m_Pos, Client()->PredIntraGameTick()); + } + // else + // m_LocalCharacterPos = mix(m_PredictedPrevChar.m_Pos, m_PredictedChar.m_Pos, Client()->PredIntraGameTick()); + } } else if(m_Snap.m_pLocalCharacter && m_Snap.m_pLocalPrevCharacter) { @@ -395,6 +447,20 @@ void CGameClient::UpdatePositions() vec2(m_Snap.m_pLocalPrevCharacter->m_X, m_Snap.m_pLocalPrevCharacter->m_Y), vec2(m_Snap.m_pLocalCharacter->m_X, m_Snap.m_pLocalCharacter->m_Y), Client()->IntraGameTick()); } + + if (g_Config.m_ClAntiPing) + { + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (!m_Snap.m_aCharacters[i].m_Active) + continue; + + if (m_Snap.m_pLocalCharacter && m_Snap.m_pLocalPrevCharacter && g_Config.m_ClPredict /* && g_Config.m_AntiPing */ && !(m_Snap.m_LocalClientID == -1 || !m_Snap.m_aCharacters[m_Snap.m_LocalClientID].m_Active)) + m_Snap.m_aCharacters[i].m_Position = mix(m_aClients[i].m_PrevPredicted.m_Pos, m_aClients[i].m_Predicted.m_Pos, Client()->PredIntraGameTick()); + else + m_Snap.m_aCharacters[i].m_Position = mix(vec2(m_Snap.m_aCharacters[i].m_Prev.m_X, m_Snap.m_aCharacters[i].m_Prev.m_Y), vec2(m_Snap.m_aCharacters[i].m_Cur.m_X, m_Snap.m_aCharacters[i].m_Cur.m_Y), Client()->IntraGameTick()); + } + } // spectator position if(m_Snap.m_SpecInfo.m_Active) @@ -430,11 +496,12 @@ static void Evolve(CNetObj_Character *pCharacter, int Tick) mem_zero(&TempTeams, sizeof(TempTeams)); TempCore.Init(&TempWorld, g_GameClient.Collision(), &TempTeams); TempCore.Read(pCharacter); + TempCore.m_ActiveWeapon = pCharacter->m_Weapon; while(pCharacter->m_Tick < Tick) { pCharacter->m_Tick++; - TempCore.Tick(false); + TempCore.Tick(false, true); TempCore.Move(); TempCore.Quantize(); } @@ -474,8 +541,11 @@ void CGameClient::OnRender() m_NewTick = false; m_NewPredictedTick = false; + if(g_Config.m_ClDummy && !Client()->DummyConnected()) + g_Config.m_ClDummy = 0; + // check if client info has to be resent - if(m_LastSendInfo && Client()->State() == IClient::STATE_ONLINE && m_Snap.m_LocalClientID >= 0 && !m_pMenus->IsActive() && m_LastSendInfo+time_freq()*5 < time_get()) + if(m_LastSendInfo && Client()->State() == IClient::STATE_ONLINE && m_Snap.m_LocalClientID >= 0 && !m_pMenus->IsActive() && m_LastSendInfo+time_freq()*6 < time_get()) { // resend if client info differs if(str_comp(g_Config.m_PlayerName, m_aClients[m_Snap.m_LocalClientID].m_aName) || @@ -487,12 +557,20 @@ void CGameClient::OnRender() g_Config.m_PlayerColorBody != m_aClients[m_Snap.m_LocalClientID].m_ColorBody || g_Config.m_PlayerColorFeet != m_aClients[m_Snap.m_LocalClientID].m_ColorFeet))) { - SendInfo(false); + if (!g_Config.m_ClDummy) + SendInfo(false); } m_LastSendInfo = 0; } } +void CGameClient::OnDummyDisconnect() +{ + m_DDRaceMsgSent[1] = false; + m_ShowOthers[1] = -1; + m_LastNewPredictedTick[1] = -1; +} + void CGameClient::OnRelease() { // release all systems @@ -500,10 +578,10 @@ void CGameClient::OnRelease() m_All.m_paComponents[i]->OnRelease(); } -void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker) +void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker, bool IsDummy) { // special messages - if(MsgId == NETMSGTYPE_SV_EXTRAPROJECTILE) + if(MsgId == NETMSGTYPE_SV_EXTRAPROJECTILE && !IsDummy) { int Num = pUnpacker->GetInt(); @@ -527,19 +605,21 @@ void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker) CTuningParams NewTuning; int *pParams = (int *)&NewTuning; for(unsigned i = 0; i < sizeof(CTuningParams)/sizeof(int); i++) + { pParams[i] = pUnpacker->GetInt(); - // check for unpacking errors - if(pUnpacker->Error()) - return; + // check for unpacking errors + if(pUnpacker->Error()) + break; + } m_ServerMode = SERVERMODE_PURE; // apply new tuning - m_Tuning = NewTuning; + m_Tuning[IsDummy ? !g_Config.m_ClDummy : g_Config.m_ClDummy] = NewTuning; return; } - + void *pRawMsg = m_NetObjHandler.SecureUnpackMsg(MsgId, pUnpacker); if(!pRawMsg) { @@ -549,6 +629,25 @@ void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker) return; } + if(IsDummy) + { + if(MsgId == NETMSGTYPE_SV_CHAT + && Client()->m_LocalIDs[0] >= 0 + && Client()->m_LocalIDs[1] >= 0) + { + CNetMsg_Sv_Chat *pMsg = (CNetMsg_Sv_Chat *)pRawMsg; + + if((pMsg->m_Team == 1 + && (m_aClients[Client()->m_LocalIDs[0]].m_Team != m_aClients[Client()->m_LocalIDs[1]].m_Team + || m_Teams.Team(Client()->m_LocalIDs[0]) != m_Teams.Team(Client()->m_LocalIDs[1]))) + || pMsg->m_Team > 1) + { + m_pChat->OnMessage(MsgId, pRawMsg); + } + } + return; // no need of all that stuff for the dummy + } + // TODO: this should be done smarter for(int i = 0; i < m_All.m_Num; i++) m_All.m_paComponents[i]->OnMessage(MsgId, pRawMsg); @@ -579,25 +678,32 @@ void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker) else g_GameClient.m_pSounds->Play(CSounds::CHN_GLOBAL, pMsg->m_SoundID, 1.0f); } - else if(MsgId == NETMSGTYPE_CL_TEAMSSTATE) + else if(MsgId == NETMSGTYPE_SV_TEAMSSTATE) { - CNetMsg_Cl_TeamsState *pMsg = (CNetMsg_Cl_TeamsState *)pRawMsg; - m_Teams.Team(0, pMsg->m_Tee0); - m_Teams.Team(1, pMsg->m_Tee1); - m_Teams.Team(2, pMsg->m_Tee2); - m_Teams.Team(3, pMsg->m_Tee3); - m_Teams.Team(4, pMsg->m_Tee4); - m_Teams.Team(5, pMsg->m_Tee5); - m_Teams.Team(6, pMsg->m_Tee6); - m_Teams.Team(7, pMsg->m_Tee7); - m_Teams.Team(8, pMsg->m_Tee8); - m_Teams.Team(9, pMsg->m_Tee9); - m_Teams.Team(10, pMsg->m_Tee10); - m_Teams.Team(11, pMsg->m_Tee11); - m_Teams.Team(12, pMsg->m_Tee12); - m_Teams.Team(13, pMsg->m_Tee13); - m_Teams.Team(14, pMsg->m_Tee14); - m_Teams.Team(15, pMsg->m_Tee15); + unsigned int i; + + for(i = 0; i < MAX_CLIENTS; i++) + { + int Team = pUnpacker->GetInt(); + bool WentWrong = false; + + if(pUnpacker->Error()) + WentWrong = true; + + if(!WentWrong && Team >= 0 && Team < MAX_CLIENTS) + m_Teams.Team(i, Team); + else if (Team != MAX_CLIENTS) + WentWrong = true; + + if(WentWrong) + { + m_Teams.Team(i, 0); + break; + } + } + + if (i <= 16) + m_Teams.m_IsDDRace16 = true; } else if(MsgId == NETMSGTYPE_SV_PLAYERTIME) { @@ -626,7 +732,7 @@ void CGameClient::OnEnterGame() {} void CGameClient::OnGameOver() { - if(Client()->State() != IClient::STATE_DEMOPLAYBACK) + if(Client()->State() != IClient::STATE_DEMOPLAYBACK && g_Config.m_ClEditor == 0) Client()->AutoScreenshot_Start(); } @@ -909,8 +1015,23 @@ void CGameClient::OnNewSnapshot() m_aClients[i].m_Friend = true; } + // sort player infos by name + mem_copy(m_Snap.m_paInfoByName, m_Snap.m_paPlayerInfos, sizeof(m_Snap.m_paInfoByName)); + for(int k = 0; k < MAX_CLIENTS-1; k++) // ffs, bubblesort + { + for(int i = 0; i < MAX_CLIENTS-k-1; i++) + { + if(m_Snap.m_paInfoByName[i+1] && (!m_Snap.m_paInfoByName[i] || str_comp(m_aClients[m_Snap.m_paInfoByName[i]->m_ClientID].m_aName, m_aClients[m_Snap.m_paInfoByName[i+1]->m_ClientID].m_aName) > 0)) + { + const CNetObj_PlayerInfo *pTmp = m_Snap.m_paInfoByName[i]; + m_Snap.m_paInfoByName[i] = m_Snap.m_paInfoByName[i+1]; + m_Snap.m_paInfoByName[i+1] = pTmp; + } + } + } + // sort player infos by score - mem_copy(m_Snap.m_paInfoByScore, m_Snap.m_paPlayerInfos, sizeof(m_Snap.m_paInfoByScore)); + mem_copy(m_Snap.m_paInfoByScore, m_Snap.m_paInfoByName, sizeof(m_Snap.m_paInfoByScore)); for(int k = 0; k < MAX_CLIENTS-1; k++) // ffs, bubblesort { for(int i = 0; i < MAX_CLIENTS-k-1; i++) @@ -923,6 +1044,7 @@ void CGameClient::OnNewSnapshot() } } } + // sort player infos by team int Teams[3] = { TEAM_RED, TEAM_BLUE, TEAM_SPECTATORS }; int Index = 0; @@ -935,6 +1057,17 @@ void CGameClient::OnNewSnapshot() } } + // sort player infos by DDRace Team (and score inbetween) + Index = 0; + for(int Team = 0; Team <= MAX_CLIENTS; ++Team) + { + for(int i = 0; i < MAX_CLIENTS && Index < MAX_CLIENTS; ++i) + { + if(m_Snap.m_paInfoByScore[i] && m_Teams.Team(m_Snap.m_paInfoByScore[i]->m_ClientID) == Team) + m_Snap.m_paInfoByDDTeam[Index++] = m_Snap.m_paInfoByScore[i]; + } + } + CTuningParams StandardTuning; CServerInfo CurrentServerInfo; Client()->GetServerInfo(&CurrentServerInfo); @@ -942,30 +1075,39 @@ void CGameClient::OnNewSnapshot() { if(str_comp(CurrentServerInfo.m_aGameType, "DM") != 0 && str_comp(CurrentServerInfo.m_aGameType, "TDM") != 0 && str_comp(CurrentServerInfo.m_aGameType, "CTF") != 0) m_ServerMode = SERVERMODE_MOD; - else if(mem_comp(&StandardTuning, &m_Tuning, sizeof(CTuningParams)) == 0) + else if(mem_comp(&StandardTuning, &m_Tuning[g_Config.m_ClDummy], 33) == 0) m_ServerMode = SERVERMODE_PURE; else m_ServerMode = SERVERMODE_PUREMOD; } // add tuning to demo - if(DemoRecorder()->IsRecording() && mem_comp(&StandardTuning, &m_Tuning, sizeof(CTuningParams)) != 0) + if(DemoRecorder()->IsRecording() && mem_comp(&StandardTuning, &m_Tuning[g_Config.m_ClDummy], sizeof(CTuningParams)) != 0) { CMsgPacker Msg(NETMSGTYPE_SV_TUNEPARAMS); - int *pParams = (int *)&m_Tuning; - for(unsigned i = 0; i < sizeof(m_Tuning)/sizeof(int); i++) + int *pParams = (int *)&m_Tuning[g_Config.m_ClDummy]; + for(unsigned i = 0; i < sizeof(m_Tuning[0])/sizeof(int); i++) Msg.AddInt(pParams[i]); Client()->SendMsg(&Msg, MSGFLAG_RECORD|MSGFLAG_NOSEND); } - if(!m_DDRaceMsgSent && m_Snap.m_pLocalInfo) + if(!m_DDRaceMsgSent[0] && m_Snap.m_pLocalInfo) + { + CMsgPacker Msg(NETMSGTYPE_CL_ISDDNET); + Msg.AddInt(CLIENT_VERSIONNR); + Client()->SendMsgExY(&Msg, MSGFLAG_VITAL,false, 0); + m_DDRaceMsgSent[0] = true; + } + + if(!m_DDRaceMsgSent[1] && m_Snap.m_pLocalInfo && Client()->DummyConnected()) { - CNetMsg_Cl_IsDDRace Msg; - Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); - m_DDRaceMsgSent = true; + CMsgPacker Msg(NETMSGTYPE_CL_ISDDNET); + Msg.AddInt(CLIENT_VERSIONNR); + Client()->SendMsgExY(&Msg, MSGFLAG_VITAL,false, 1); + m_DDRaceMsgSent[1] = true; } - if(m_ShowOthers == -1 || (m_ShowOthers != -1 && m_ShowOthers != g_Config.m_ClShowOthers)) + if(m_ShowOthers[g_Config.m_ClDummy] == -1 || (m_ShowOthers[g_Config.m_ClDummy] != -1 && m_ShowOthers[g_Config.m_ClDummy] != g_Config.m_ClShowOthers)) { // no need to send, default settings //if(!(m_ShowOthers == -1 && g_Config.m_ClShowOthers)) @@ -976,7 +1118,7 @@ void CGameClient::OnNewSnapshot() } // update state - m_ShowOthers = g_Config.m_ClShowOthers; + m_ShowOthers[g_Config.m_ClDummy] = g_Config.m_ClShowOthers; } } @@ -994,15 +1136,21 @@ void CGameClient::OnPredict() if(m_Snap.m_pGameInfoObj && m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) { if(m_Snap.m_pLocalCharacter) + { m_PredictedChar.Read(m_Snap.m_pLocalCharacter); + m_PredictedChar.m_ActiveWeapon = m_Snap.m_pLocalCharacter->m_Weapon; + } if(m_Snap.m_pLocalPrevCharacter) + { m_PredictedPrevChar.Read(m_Snap.m_pLocalPrevCharacter); + m_PredictedPrevChar.m_ActiveWeapon = m_Snap.m_pLocalPrevCharacter->m_Weapon; + } return; } // repredict character CWorldCore World; - World.m_Tuning = m_Tuning; + World.m_Tuning[g_Config.m_ClDummy] = m_Tuning[g_Config.m_ClDummy]; // search for players for(int i = 0; i < MAX_CLIENTS; i++) @@ -1014,6 +1162,7 @@ void CGameClient::OnPredict() World.m_apCharacters[i] = &g_GameClient.m_aClients[i].m_Predicted; World.m_apCharacters[i]->m_Id = m_Snap.m_paPlayerInfos[i]->m_ClientID; g_GameClient.m_aClients[i].m_Predicted.Read(&m_Snap.m_aCharacters[i].m_Cur); + g_GameClient.m_aClients[i].m_Predicted.m_ActiveWeapon = m_Snap.m_aCharacters[i].m_Cur.m_Weapon; } // predict @@ -1028,6 +1177,9 @@ void CGameClient::OnPredict() { if(!World.m_apCharacters[c]) continue; + + if(g_Config.m_ClAntiPing && Tick == Client()->PredGameTick()) + g_GameClient.m_aClients[c].m_PrevPredicted = *World.m_apCharacters[c]; mem_zero(&World.m_apCharacters[c]->m_Input, sizeof(World.m_apCharacters[c]->m_Input)); if(m_Snap.m_LocalClientID == c) @@ -1036,10 +1188,10 @@ void CGameClient::OnPredict() int *pInput = Client()->GetInput(Tick); if(pInput) World.m_apCharacters[c]->m_Input = *((CNetObj_PlayerInput*)pInput); - World.m_apCharacters[c]->Tick(true); + World.m_apCharacters[c]->Tick(true, true); } else - World.m_apCharacters[c]->Tick(false); + World.m_apCharacters[c]->Tick(false, true); } @@ -1054,9 +1206,9 @@ void CGameClient::OnPredict() } // check if we want to trigger effects - if(Tick > m_LastNewPredictedTick) + if(Tick > m_LastNewPredictedTick[g_Config.m_ClDummy]) { - m_LastNewPredictedTick = Tick; + m_LastNewPredictedTick[g_Config.m_ClDummy] = Tick; m_NewPredictedTick = true; if(m_Snap.m_LocalClientID != -1 && World.m_apCharacters[m_Snap.m_LocalClientID]) @@ -1080,7 +1232,20 @@ void CGameClient::OnPredict() } if(Tick == Client()->PredGameTick() && World.m_apCharacters[m_Snap.m_LocalClientID]) + { m_PredictedChar = *World.m_apCharacters[m_Snap.m_LocalClientID]; + + if (g_Config.m_ClAntiPing) + { + for (int c = 0; c < MAX_CLIENTS; c++) + { + if(!World.m_apCharacters[c]) + continue; + + g_GameClient.m_aClients[c].m_Predicted = *World.m_apCharacters[c]; + } + } + } } if(g_Config.m_Debug && g_Config.m_ClPredict && m_PredictedTick == Client()->PredGameTick()) @@ -1157,6 +1322,9 @@ void CGameClient::SendSwitchTeam(int Team) CNetMsg_Cl_SetTeam Msg; Msg.m_Team = Team; Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); + + if (Team != TEAM_SPECTATORS) + m_pCamera->OnReset(); } void CGameClient::SendInfo(bool Start) @@ -1171,7 +1339,9 @@ void CGameClient::SendInfo(bool Start) Msg.m_UseCustomColor = g_Config.m_PlayerUseCustomColor; Msg.m_ColorBody = g_Config.m_PlayerColorBody; Msg.m_ColorFeet = g_Config.m_PlayerColorFeet; - Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); + CMsgPacker Packer(Msg.MsgID()); + Msg.Pack(&Packer); + Client()->SendMsgExY(&Packer, MSGFLAG_VITAL,false, 0); } else { @@ -1183,7 +1353,9 @@ void CGameClient::SendInfo(bool Start) Msg.m_UseCustomColor = g_Config.m_PlayerUseCustomColor; Msg.m_ColorBody = g_Config.m_PlayerColorBody; Msg.m_ColorFeet = g_Config.m_PlayerColorFeet; - Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); + CMsgPacker Packer(Msg.MsgID()); + Msg.Pack(&Packer); + Client()->SendMsgExY(&Packer, MSGFLAG_VITAL,false, 0); // activate timer to resend the info if it gets filtered if(!m_LastSendInfo || m_LastSendInfo+time_freq()*5 < time_get()) @@ -1191,6 +1363,42 @@ void CGameClient::SendInfo(bool Start) } } +void CGameClient::SendDummyInfo(bool Start) +{ + if(Start) + { + CNetMsg_Cl_StartInfo Msg; + Msg.m_pName = g_Config.m_DummyName; + Msg.m_pClan = g_Config.m_DummyClan; + Msg.m_Country = g_Config.m_DummyCountry; + Msg.m_pSkin = g_Config.m_DummySkin; + Msg.m_UseCustomColor = g_Config.m_DummyUseCustomColor; + Msg.m_ColorBody = g_Config.m_DummyColorBody; + Msg.m_ColorFeet = g_Config.m_DummyColorFeet; + CMsgPacker Packer(Msg.MsgID()); + Msg.Pack(&Packer); + Client()->SendMsgExY(&Packer, MSGFLAG_VITAL,false, 1); + } + else + { + CNetMsg_Cl_ChangeInfo Msg; + Msg.m_pName = g_Config.m_DummyName; + Msg.m_pClan = g_Config.m_DummyClan; + Msg.m_Country = g_Config.m_DummyCountry; + Msg.m_pSkin = g_Config.m_DummySkin; + Msg.m_UseCustomColor = g_Config.m_DummyUseCustomColor; + Msg.m_ColorBody = g_Config.m_DummyColorBody; + Msg.m_ColorFeet = g_Config.m_DummyColorFeet; + CMsgPacker Packer(Msg.MsgID()); + Msg.Pack(&Packer); + Client()->SendMsgExY(&Packer, MSGFLAG_VITAL,false, 1); + + // activate timer to resend the info if it gets filtered + //if(!m_LastSendInfo || m_LastSendInfo+time_freq()*5 < time_get()) + // m_LastSendInfo = time_get(); + } +} + void CGameClient::SendKill(int ClientID) { CNetMsg_Cl_Kill Msg; @@ -1214,7 +1422,57 @@ void CGameClient::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pU ((CGameClient*)pUserData)->SendInfo(false); } +void CGameClient::ConchainSpecialDummyInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments()) + ((CGameClient*)pUserData)->SendDummyInfo(false); +} + +void CGameClient::ConchainSpecialDummy(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments()) + if(g_Config.m_ClDummy && !((CGameClient*)pUserData)->Client()->DummyConnected()) + g_Config.m_ClDummy = 0; +} + IGameClient *CreateGameClient() { return &g_GameClient; } + +int CGameClient::IntersectCharacter(vec2 HookPos, vec2 NewPos, vec2& NewPos2, int ownID) +{ + float PhysSize = 28.0f; + float Distance = 0.0f; + int ClosestID = -1; + + if (!m_Tuning[g_Config.m_ClDummy].m_PlayerHooking) + return ClosestID; + + for (int i=0; iIntraGameTick()); + + if (!cData.m_Active || i == ownID || !m_Teams.SameTeam(i, ownID)) + continue; + + vec2 ClosestPoint = closest_point_on_line(HookPos, NewPos, Position); + if(distance(Position, ClosestPoint) < PhysSize+2.0f) + { + if(ClosestID == -1 || distance(HookPos, Position) < Distance) + { + NewPos2 = ClosestPoint; + ClosestID = i; + Distance = distance(HookPos, Position); + } + } + } + + return ClosestID; +} diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 3a01cc1976..f00e241677 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -12,6 +12,16 @@ #include +#define MIN3(x,y,z) ((y) <= (z) ? \ + ((x) <= (y) ? (x) : (y)) \ + : \ + ((x) <= (z) ? (x) : (z))) + +#define MAX3(x,y,z) ((y) >= (z) ? \ + ((x) >= (y) ? (x) : (y)) \ + : \ + ((x) >= (z) ? (x) : (z))) + class CGameClient : public IGameClient { class CStack @@ -44,6 +54,9 @@ class CGameClient : public IGameClient class IDemoPlayer *m_pDemoPlayer; class IDemoRecorder *m_pDemoRecorder; class IServerBrowser *m_pServerBrowser; +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + class IAutoUpdate *m_pAutoUpdate; +#endif class IEditor *m_pEditor; class IFriends *m_pFriends; @@ -56,7 +69,7 @@ class CGameClient : public IGameClient void UpdatePositions(); int m_PredictedTick; - int m_LastNewPredictedTick; + int m_LastNewPredictedTick[2]; int64 m_LastSendInfo; @@ -64,6 +77,8 @@ class CGameClient : public IGameClient static void ConKill(IConsole::IResult *pResult, void *pUserData); static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + static void ConchainSpecialDummyInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + static void ConchainSpecialDummy(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); public: IKernel *Kernel() { return IInterface::Kernel(); } @@ -79,6 +94,9 @@ class CGameClient : public IGameClient class IDemoPlayer *DemoPlayer() const { return m_pDemoPlayer; } class IDemoRecorder *DemoRecorder() const { return m_pDemoRecorder; } class IServerBrowser *ServerBrowser() const { return m_pServerBrowser; } +#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__) + class IAutoUpdate *AutoUpdate() const { return m_pAutoUpdate; } +#endif class CRenderTools *RenderTools() { return &m_RenderTools; } class CLayers *Layers() { return &m_Layers; }; class CCollision *Collision() { return &m_Collision; }; @@ -94,7 +112,7 @@ class CGameClient : public IGameClient int m_FlagDropTick[2]; // TODO: move this - CTuningParams m_Tuning; + CTuningParams m_Tuning[2]; enum { @@ -127,7 +145,9 @@ class CGameClient : public IGameClient const CNetObj_PlayerInfo *m_paPlayerInfos[MAX_CLIENTS]; const CNetObj_PlayerInfo *m_paInfoByScore[MAX_CLIENTS]; + const CNetObj_PlayerInfo *m_paInfoByName[MAX_CLIENTS]; const CNetObj_PlayerInfo *m_paInfoByTeam[MAX_CLIENTS]; + const CNetObj_PlayerInfo *m_paInfoByDDTeam[MAX_CLIENTS]; int m_LocalClientID; int m_NumPlayers; @@ -177,6 +197,7 @@ class CGameClient : public IGameClient int m_Emoticon; int m_EmoticonStart; CCharacterCore m_Predicted; + CCharacterCore m_PrevPredicted; CTeeRenderInfo m_SkinInfo; // this is what the server reports CTeeRenderInfo m_RenderInfo; // this is what we use @@ -203,11 +224,12 @@ class CGameClient : public IGameClient // hooks virtual void OnConnected(); virtual void OnRender(); + virtual void OnDummyDisconnect(); virtual void OnRelease(); virtual void OnInit(); virtual void OnConsoleInit(); virtual void OnStateChange(int NewState, int OldState); - virtual void OnMessage(int MsgId, CUnpacker *pUnpacker); + virtual void OnMessage(int MsgId, CUnpacker *pUnpacker, bool IsDummy = 0); virtual void OnNewSnapshot(); virtual void OnPredict(); virtual void OnActivateEditor(); @@ -218,6 +240,7 @@ class CGameClient : public IGameClient virtual void OnGameOver(); virtual void OnStartGame(); + virtual void ResetDummyInput(); virtual const char *GetItemName(int Type); virtual const char *Version(); virtual const char *NetVersion(); @@ -227,6 +250,7 @@ class CGameClient : public IGameClient // TODO: move these void SendSwitchTeam(int Team); void SendInfo(bool Start); + virtual void SendDummyInfo(bool Start); void SendKill(int ClientID); // pointers to all systems @@ -255,12 +279,14 @@ class CGameClient : public IGameClient class CRaceDemo *m_pRaceDemo; class CGhost *m_pGhost; + class CTeamsCore m_Teams; + + int IntersectCharacter(vec2 Pos0, vec2 Pos1, vec2& NewPos, int ownID); private: - class CTeamsCore m_Teams; - bool m_DDRaceMsgSent; - int m_ShowOthers; + bool m_DDRaceMsgSent[2]; + int m_ShowOthers[2]; }; @@ -287,6 +313,39 @@ inline vec3 HslToRgb(vec3 HSL) } } +inline vec3 RgbToHsl(vec3 RGB) +{ + vec3 HSL; + float maxColor = MAX3(RGB.r, RGB.g, RGB.b); + float minColor = MIN3(RGB.r, RGB.g, RGB.b); + if (minColor == maxColor) + return vec3(0.0f, 0.0f, RGB.g * 255.0f); + else + { + HSL.l = (minColor + maxColor) / 2; + + if (HSL.l < 0.5) + HSL.s = (maxColor - minColor) / (maxColor + minColor); + else + HSL.s = (maxColor - minColor) / (2.0 - maxColor - minColor); + + if (RGB.r == maxColor) + HSL.h = (RGB.g - RGB.b) / (maxColor - minColor); + else if (RGB.g == maxColor) + HSL.h = 2.0 + (RGB.b - RGB.r) / (maxColor - minColor); + else + HSL.h = 4.0 + (RGB.r - RGB.g) / (maxColor - minColor); + + HSL.h /= 6; //to bring it to a number between 0 and 1 + if (HSL.h < 0) HSL.h++; + } + HSL.h = int(HSL.h * 255.0); + HSL.s = int(HSL.s * 255.0); + HSL.l = int(HSL.l * 255.0); + return HSL; + +} + extern const char *Localize(const char *Str); diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp index 2de85d667f..11fae570b0 100644 --- a/src/game/client/lineinput.cpp +++ b/src/game/client/lineinput.cpp @@ -13,6 +13,7 @@ void CLineInput::Clear() mem_zero(m_Str, sizeof(m_Str)); m_Len = 0; m_CursorPos = 0; + m_NumChars = 0; } void CLineInput::Set(const char *pString) @@ -20,10 +21,18 @@ void CLineInput::Set(const char *pString) str_copy(m_Str, pString, sizeof(m_Str)); m_Len = str_length(m_Str); m_CursorPos = m_Len; + m_NumChars = 0; + int Offset = 0; + while(pString[Offset]) + { + Offset = str_utf8_forward(pString, Offset); + ++m_NumChars; + } } -bool CLineInput::Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *pStrLenPtr, int *pCursorPosPtr) +bool CLineInput::Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int StrMaxChars, int *pStrLenPtr, int *pCursorPosPtr, int *pNumCharsPtr) { + int NumChars = *pNumCharsPtr; int CursorPos = *pCursorPosPtr; int Len = *pStrLenPtr; bool Changes = false; @@ -40,13 +49,15 @@ bool CLineInput::Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *p char Tmp[8]; int CharSize = str_utf8_encode(Tmp, Code); - if (Len < StrMaxSize - CharSize && CursorPos < StrMaxSize - CharSize) + if (Len < StrMaxSize - CharSize && CursorPos < StrMaxSize - CharSize && NumChars < StrMaxChars) { mem_move(pStr + CursorPos + CharSize, pStr + CursorPos, Len-CursorPos+1); // +1 == null term for(int i = 0; i < CharSize; i++) pStr[CursorPos+i] = Tmp[i]; CursorPos += CharSize; Len += CharSize; + if(CharSize > 0) + ++NumChars; Changes = true; } } @@ -60,6 +71,8 @@ bool CLineInput::Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *p mem_move(pStr+NewCursorPos, pStr+CursorPos, Len - NewCursorPos - CharSize + 1); // +1 == null term CursorPos = NewCursorPos; Len -= CharSize; + if(CharSize > 0) + --NumChars; Changes = true; } else if (k == KEY_DELETE && CursorPos < Len) @@ -68,6 +81,8 @@ bool CLineInput::Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *p int CharSize = p-CursorPos; mem_move(pStr + CursorPos, pStr + CursorPos + CharSize, Len - CursorPos - CharSize + 1); // +1 == null term Len -= CharSize; + if(CharSize > 0) + --NumChars; Changes = true; } else if (k == KEY_LEFT && CursorPos > 0) @@ -80,6 +95,7 @@ bool CLineInput::Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *p CursorPos = Len; } + *pNumCharsPtr = NumChars; *pCursorPosPtr = CursorPos; *pStrLenPtr = Len; @@ -88,5 +104,5 @@ bool CLineInput::Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *p void CLineInput::ProcessInput(IInput::CEvent e) { - Manipulate(e, m_Str, sizeof(m_Str), &m_Len, &m_CursorPos); + Manipulate(e, m_Str, MAX_SIZE, MAX_CHARS, &m_Len, &m_CursorPos, &m_NumChars); } diff --git a/src/game/client/lineinput.h b/src/game/client/lineinput.h index 5dfc3e4892..d70805b245 100644 --- a/src/game/client/lineinput.h +++ b/src/game/client/lineinput.h @@ -8,11 +8,17 @@ // line input helter class CLineInput { - char m_Str[256]; + enum + { + MAX_SIZE=512, + MAX_CHARS=MAX_SIZE/2, + }; + char m_Str[MAX_SIZE]; int m_Len; int m_CursorPos; + int m_NumChars; public: - static bool Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *pStrLenPtr, int *pCursorPosPtr); + static bool Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int StrMaxChars, int *pStrLenPtr, int *pCursorPosPtr, int *pNumCharsPtr); class CCallback { diff --git a/src/game/client/render.cpp b/src/game/client/render.cpp index a5adac1255..b3df8b241b 100644 --- a/src/game/client/render.cpp +++ b/src/game/client/render.cpp @@ -307,7 +307,6 @@ void CRenderTools::MapscreenToWorld(float CenterX, float CenterY, float Parallax void CRenderTools::RenderTilemapGenerateSkip(class CLayers *pLayers) { - for(int g = 0; g < pLayers->NumGroups(); g++) { CMapItemGroup *pGroup = pLayers->GetGroup(g); @@ -322,7 +321,7 @@ void CRenderTools::RenderTilemapGenerateSkip(class CLayers *pLayers) CTile *pTiles = (CTile *)pLayers->Map()->GetData(pTmap->m_Data); for(int y = 0; y < pTmap->m_Height; y++) { - for(int x = 1; x < pTmap->m_Width; x++) + for(int x = 1; x < pTmap->m_Width;) { int sx; for(sx = 1; x+sx < pTmap->m_Width && sx < 255; sx++) @@ -332,6 +331,7 @@ void CRenderTools::RenderTilemapGenerateSkip(class CLayers *pLayers) } pTiles[y*pTmap->m_Width+x].m_Skip = sx-1; + x += sx; } } } diff --git a/src/game/client/render.h b/src/game/client/render.h index a9a5d2a7f8..5cd08e2464 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -72,6 +72,7 @@ class CRenderTools // map render methods (gc_render_map.cpp) static void RenderEvalEnvelope(CEnvPoint *pPoints, int NumPoints, int Channels, float Time, float *pResult); void RenderQuads(CQuad *pQuads, int NumQuads, int Flags, ENVELOPE_EVAL pfnEval, void *pUser); + void ForceRenderQuads(CQuad *pQuads, int NumQuads, int Flags, ENVELOPE_EVAL pfnEval, void *pUser, float Alpha = 1.0f); void RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset); // helpers @@ -80,12 +81,14 @@ class CRenderTools // DDRace - void RenderTeleOverlay(CTeleTile *pTele, int w, int h, float Scale); - void RenderSpeedupOverlay(CSpeedupTile *pTele, int w, int h, float Scale); - void RenderSwitchOverlay(CSwitchTile *pSwitch, int w, int h, float Scale); + void RenderTeleOverlay(CTeleTile *pTele, int w, int h, float Scale, float Alpha=1.0f); + void RenderSpeedupOverlay(CSpeedupTile *pTele, int w, int h, float Scale, float Alpha=1.0f); + void RenderSwitchOverlay(CSwitchTile *pSwitch, int w, int h, float Scale, float Alpha=1.0f); + void RenderTuneOverlay(CTuneTile *pTune, int w, int h, float Scale, float Alpha=1.0f); void RenderTelemap(CTeleTile *pTele, int w, int h, float Scale, vec4 Color, int RenderFlags); void RenderSpeedupmap(CSpeedupTile *pTele, int w, int h, float Scale, vec4 Color, int RenderFlags); void RenderSwitchmap(CSwitchTile *pSwitch, int w, int h, float Scale, vec4 Color, int RenderFlags); + void RenderTunemap(CTuneTile *pTune, int w, int h, float Scale, vec4 Color, int RenderFlags); }; #endif diff --git a/src/game/client/render_map.cpp b/src/game/client/render_map.cpp index ebc79e094e..a389d8c3c8 100644 --- a/src/game/client/render_map.cpp +++ b/src/game/client/render_map.cpp @@ -85,9 +85,14 @@ static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation) void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser) { - if(g_Config.m_ClShowEntities && g_Config.m_ClDDRaceCheats) + if(!g_Config.m_ClShowQuads || g_Config.m_ClOverlayEntities == 100) return; + ForceRenderQuads(pQuads, NumQuads, RenderFlags, pfnEval, pUser, (100-g_Config.m_ClOverlayEntities)/100.0f); +} + +void CRenderTools::ForceRenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser, float Alpha) +{ Graphics()->QuadsBegin(); float Conv = 1/255.0f; for(int i = 0; i < NumQuads; i++) @@ -107,9 +112,10 @@ void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENV } bool Opaque = false; + /* TODO: Analyze quadtexture if(a < 0.01f || (q->m_aColors[0].a < 0.01f && q->m_aColors[1].a < 0.01f && q->m_aColors[2].a < 0.01f && q->m_aColors[3].a < 0.01f)) Opaque = true; - + */ if(Opaque && !(RenderFlags&LAYERRENDERFLAG_OPAQUE)) continue; if(!Opaque && !(RenderFlags&LAYERRENDERFLAG_TRANSPARENT)) @@ -137,10 +143,10 @@ void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENV } IGraphics::CColorVertex Array[4] = { - IGraphics::CColorVertex(0, q->m_aColors[0].r*Conv*r, q->m_aColors[0].g*Conv*g, q->m_aColors[0].b*Conv*b, q->m_aColors[0].a*Conv*a), - IGraphics::CColorVertex(1, q->m_aColors[1].r*Conv*r, q->m_aColors[1].g*Conv*g, q->m_aColors[1].b*Conv*b, q->m_aColors[1].a*Conv*a), - IGraphics::CColorVertex(2, q->m_aColors[2].r*Conv*r, q->m_aColors[2].g*Conv*g, q->m_aColors[2].b*Conv*b, q->m_aColors[2].a*Conv*a), - IGraphics::CColorVertex(3, q->m_aColors[3].r*Conv*r, q->m_aColors[3].g*Conv*g, q->m_aColors[3].b*Conv*b, q->m_aColors[3].a*Conv*a)}; + IGraphics::CColorVertex(0, q->m_aColors[0].r*Conv*r, q->m_aColors[0].g*Conv*g, q->m_aColors[0].b*Conv*b, q->m_aColors[0].a*Conv*a*Alpha), + IGraphics::CColorVertex(1, q->m_aColors[1].r*Conv*r, q->m_aColors[1].g*Conv*g, q->m_aColors[1].b*Conv*b, q->m_aColors[1].a*Conv*a*Alpha), + IGraphics::CColorVertex(2, q->m_aColors[2].r*Conv*r, q->m_aColors[2].g*Conv*g, q->m_aColors[2].b*Conv*b, q->m_aColors[2].a*Conv*a*Alpha), + IGraphics::CColorVertex(3, q->m_aColors[3].r*Conv*r, q->m_aColors[3].g*Conv*g, q->m_aColors[3].b*Conv*b, q->m_aColors[3].a*Conv*a*Alpha)}; Graphics()->SetColorVertex(Array, 4); CPoint *pPoints = q->m_aPoints; @@ -244,7 +250,7 @@ void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 unsigned char Flags = pTiles[c].m_Flags; bool Render = false; - if(Flags&TILEFLAG_OPAQUE) + if(Flags&TILEFLAG_OPAQUE && Color.a*a > 254.0f/255.0f) { if(RenderFlags&LAYERRENDERFLAG_OPAQUE) Render = true; @@ -316,7 +322,7 @@ void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } -void CRenderTools::RenderTeleOverlay(CTeleTile *pTele, int w, int h, float Scale) +void CRenderTools::RenderTeleOverlay(CTeleTile *pTele, int w, int h, float Scale, float Alpha) { float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); @@ -325,7 +331,7 @@ void CRenderTools::RenderTeleOverlay(CTeleTile *pTele, int w, int h, float Scale int StartX = (int)(ScreenX0/Scale)-1; int EndY = (int)(ScreenY1/Scale)+1; int EndX = (int)(ScreenX1/Scale)+1; - + for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { @@ -349,14 +355,16 @@ void CRenderTools::RenderTeleOverlay(CTeleTile *pTele, int w, int h, float Scale { char aBuf[16]; str_format(aBuf, sizeof(aBuf), "%d", Index); + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); UI()->TextRender()->Text(0, mx*Scale-2, my*Scale-4, Scale-5, aBuf, -1); + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } } Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } -void CRenderTools::RenderSpeedupOverlay(CSpeedupTile *pSpeedup, int w, int h, float Scale) +void CRenderTools::RenderSpeedupOverlay(CSpeedupTile *pSpeedup, int w, int h, float Scale, float Alpha) { float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); @@ -365,7 +373,7 @@ void CRenderTools::RenderSpeedupOverlay(CSpeedupTile *pSpeedup, int w, int h, fl int StartX = (int)(ScreenX0/Scale)-1; int EndY = (int)(ScreenY1/Scale)+1; int EndX = (int)(ScreenX1/Scale)+1; - + for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { @@ -390,6 +398,7 @@ void CRenderTools::RenderSpeedupOverlay(CSpeedupTile *pSpeedup, int w, int h, fl // draw arrow Graphics()->TextureSet(g_pData->m_aImages[IMAGE_SPEEDUP_ARROW].m_Id); Graphics()->QuadsBegin(); + Graphics()->SetColor(255.0f, 255.0f, 255.0f, Alpha); SelectSprite(SPRITE_SPEEDUP_ARROW); Graphics()->QuadsSetRotation(pSpeedup[c].m_Angle*(3.14159265f/180.0f)); @@ -400,11 +409,15 @@ void CRenderTools::RenderSpeedupOverlay(CSpeedupTile *pSpeedup, int w, int h, fl // draw force char aBuf[16]; str_format(aBuf, sizeof(aBuf), "%d", Force); + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); UI()->TextRender()->Text(0, mx*Scale, my*Scale+16, Scale-20, aBuf, -1); + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); if(MaxSpeed) { str_format(aBuf, sizeof(aBuf), "%d", MaxSpeed); + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); UI()->TextRender()->Text(0, mx*Scale, my*Scale-2, Scale-20, aBuf, -1); + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } } } @@ -412,7 +425,7 @@ void CRenderTools::RenderSpeedupOverlay(CSpeedupTile *pSpeedup, int w, int h, fl Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } -void CRenderTools::RenderSwitchOverlay(CSwitchTile *pSwitch, int w, int h, float Scale) +void CRenderTools::RenderSwitchOverlay(CSwitchTile *pSwitch, int w, int h, float Scale, float Alpha) { float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); @@ -421,7 +434,7 @@ void CRenderTools::RenderSwitchOverlay(CSwitchTile *pSwitch, int w, int h, float int StartX = (int)(ScreenX0/Scale)-1; int EndY = (int)(ScreenY1/Scale)+1; int EndX = (int)(ScreenX1/Scale)+1; - + for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { @@ -445,7 +458,9 @@ void CRenderTools::RenderSwitchOverlay(CSwitchTile *pSwitch, int w, int h, float { char aBuf[16]; str_format(aBuf, sizeof(aBuf), "%d", Index); + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); UI()->TextRender()->Text(0, mx*Scale, my*Scale+16, Scale-20, aBuf, -1); + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } unsigned char Delay = pSwitch[c].m_Delay; @@ -453,7 +468,54 @@ void CRenderTools::RenderSwitchOverlay(CSwitchTile *pSwitch, int w, int h, float { char aBuf[16]; str_format(aBuf, sizeof(aBuf), "%d", Delay); + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); UI()->TextRender()->Text(0, mx*Scale, my*Scale-2, Scale-20, aBuf, -1); + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); + } + } + + Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); +} + +void CRenderTools::RenderTuneOverlay(CTuneTile *pTune, int w, int h, float Scale, float Alpha) +{ + float ScreenX0, ScreenY0, ScreenX1, ScreenY1; + Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); + + int StartY = (int)(ScreenY0/Scale)-1; + int StartX = (int)(ScreenX0/Scale)-1; + int EndY = (int)(ScreenY1/Scale)+1; + int EndX = (int)(ScreenX1/Scale)+1; + + if(EndX - StartX > g_Config.m_GfxScreenWidth / g_Config.m_GfxTuneOverlay || EndY - StartY > g_Config.m_GfxScreenHeight / g_Config.m_GfxTuneOverlay) + return; // its useless to render text at this distance + + for(int y = StartY; y < EndY; y++) + for(int x = StartX; x < EndX; x++) + { + int mx = x; + int my = y; + + + if(mx<0) + continue; // mx = 0; + if(mx>=w) + continue; // mx = w-1; + if(my<0) + continue; // my = 0; + if(my>=h) + continue; // my = h-1; + + int c = mx + my*w; + + unsigned char Index = pTune[c].m_Number; + if(Index) + { + char aBuf[16]; + str_format(aBuf, sizeof(aBuf), "%d", Index); + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); + UI()->TextRender()->Text(0, mx*Scale+11.f, my*Scale+6.f, Scale/1.5f-5.f, aBuf, -1); // numbers shouldnt be too big and in the center of the tile + UI()->TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } } @@ -776,3 +838,95 @@ void CRenderTools::RenderSwitchmap(CSwitchTile *pSwitchTile, int w, int h, float Graphics()->QuadsEnd(); Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } + +void CRenderTools::RenderTunemap(CTuneTile *pTune, int w, int h, float Scale, vec4 Color, int RenderFlags) +{ + float ScreenX0, ScreenY0, ScreenX1, ScreenY1; + Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); + + // calculate the final pixelsize for the tiles + float TilePixelSize = 1024/32.0f; + float FinalTileSize = Scale/(ScreenX1-ScreenX0) * Graphics()->ScreenWidth(); + float FinalTilesetScale = FinalTileSize/TilePixelSize; + + Graphics()->QuadsBegin(); + Graphics()->SetColor(Color.r, Color.g, Color.b, Color.a); + + int StartY = (int)(ScreenY0/Scale)-1; + int StartX = (int)(ScreenX0/Scale)-1; + int EndY = (int)(ScreenY1/Scale)+1; + int EndX = (int)(ScreenX1/Scale)+1; + + // adjust the texture shift according to mipmap level + float TexSize = 1024.0f; + float Frac = (1.25f/TexSize) * (1/FinalTilesetScale); + float Nudge = (0.5f/TexSize) * (1/FinalTilesetScale); + + for(int y = StartY; y < EndY; y++) + for(int x = StartX; x < EndX; x++) + { + int mx = x; + int my = y; + + if(RenderFlags&TILERENDERFLAG_EXTEND) + { + if(mx<0) + mx = 0; + if(mx>=w) + mx = w-1; + if(my<0) + my = 0; + if(my>=h) + my = h-1; + } + else + { + if(mx<0) + continue; // mx = 0; + if(mx>=w) + continue; // mx = w-1; + if(my<0) + continue; // my = 0; + if(my>=h) + continue; // my = h-1; + } + + int c = mx + my*w; + + unsigned char Index = pTune[c].m_Type; + if(Index) + { + bool Render = false; + if(RenderFlags&LAYERRENDERFLAG_TRANSPARENT) + Render = true; + + if(Render) + { + + int tx = Index%16; + int ty = Index/16; + int Px0 = tx*(1024/16); + int Py0 = ty*(1024/16); + int Px1 = Px0+(1024/16)-1; + int Py1 = Py0+(1024/16)-1; + + float x0 = Nudge + Px0/TexSize+Frac; + float y0 = Nudge + Py0/TexSize+Frac; + float x1 = Nudge + Px1/TexSize-Frac; + float y1 = Nudge + Py0/TexSize+Frac; + float x2 = Nudge + Px1/TexSize-Frac; + float y2 = Nudge + Py1/TexSize-Frac; + float x3 = Nudge + Px0/TexSize+Frac; + float y3 = Nudge + Py1/TexSize-Frac; + + Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3); + IGraphics::CQuadItem QuadItem(x*Scale, y*Scale, Scale, Scale); + Graphics()->QuadsDrawTL(&QuadItem, 1); + } + } + } + + Graphics()->QuadsEnd(); + Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); +} + diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index c5219575f7..700b3ac1ea 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -7,6 +7,10 @@ #include #include "ui.h" +#if defined(__ANDROID__) +#include +#endif + /******************************************************** UI *********************************************************/ @@ -55,9 +59,133 @@ int CUI::MouseInside(const CUIRect *r) void CUI::ConvertMouseMove(float *x, float *y) { +#if defined(__ANDROID__) + //*x = *x * 500 / g_Config.m_GfxScreenWidth; + //*y = *y * 500 / g_Config.m_GfxScreenHeight; +#else float Fac = (float)(g_Config.m_UiMousesens)/g_Config.m_InpMousesens; *x = *x*Fac; *y = *y*Fac; +#endif +} + +void CUI::AndroidShowScreenKeys(bool shown) +{ +#if defined(__ANDROID__) + static bool ScreenKeyboardInitialized = false; + static bool ScreenKeyboardShown = true; + static SDL_Rect Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_NUM]; + static SDL_Rect ButtonHidden = { 0, 0, 0, 0 }; + if( !ScreenKeyboardInitialized ) + { + //dbg_msg("dbg", "CUI::AndroidShowScreenKeys: ScreenKeyboardInitialized"); + ScreenKeyboardInitialized = true; + + for( int i = 0; i < SDL_ANDROID_SCREENKEYBOARD_BUTTON_NUM; i++ ) + SDL_ANDROID_GetScreenKeyboardButtonPos( i, &Buttons[i] ); + + if( !SDL_ANDROID_GetScreenKeyboardRedefinedByUser() ) + { + int ScreenW = Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD2].x + + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD2].w; + int ScreenH = Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD2].y + + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD2].h; + // Hide Hook button(it was above Weapnext) + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_0].x = + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_1].x; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_0].y = + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_1].y - + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_0].h; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_0].w = 0; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_0].h = 0; + // Hide Weapprev button + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_2].x = + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_0].x; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_2].y = + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_1].y - + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_2].h; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_2].w = 0; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_2].h = 0; + // Scores button above left joystick + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_3].x = 0; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_3].y = + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD].y - + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_3].h * 2.0f; + // Text input button above scores + //Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_TEXT].w = + // Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_3].w; + //Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_TEXT].h = + // Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_3].h; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_TEXT].x = 0; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_TEXT].y = + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_3].y - + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_TEXT].h; + // Spec next button + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_5].x = + ScreenW - Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_4].w; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_5].y = + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD2].y - + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_5].h; + // Spec prev button + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_4].x = + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_5].x - + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_4].w; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_4].y = + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_5].y; + // Weap next button + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_1].x = + ScreenW - Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_1].w; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_1].y = + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_5].y - + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_1].h; + // Bigger joysticks + /* + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD].w *= 1.25; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD].h *= 1.25; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD].y = + ScreenH - Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD].h; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD2].w *= 1.25; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD2].h *= 1.25; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD2].x = + ScreenW - Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD2].w; + Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD2].y = + ScreenH - Buttons[SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD2].h; + */ + } + } + + if( ScreenKeyboardShown == shown ) + return; + ScreenKeyboardShown = shown; + //dbg_msg("dbg", "CUI::AndroidShowScreenKeys: shown %d", shown); + for( int i = 0; i < SDL_ANDROID_SCREENKEYBOARD_BUTTON_NUM; i++ ) + SDL_ANDROID_SetScreenKeyboardButtonPos( i, shown ? &Buttons[i] : &ButtonHidden ); +#endif +} + +void CUI::AndroidShowTextInput(const char *text, const char *hintText) +{ +#if defined(__ANDROID__) + SDL_ANDROID_SetScreenKeyboardHintMesage(hintText); + SDL_ANDROID_ToggleScreenKeyboardTextInput(text); +#endif +} + +void CUI::AndroidBlockAndGetTextInput(char *text, int textLength, const char *hintText) +{ +#if defined(__ANDROID__) + SDL_ANDROID_SetScreenKeyboardHintMesage(hintText); + SDL_ANDROID_GetScreenKeyboardTextInput(text, textLength); +#endif +} + +bool CUI::AndroidTextInputShown() +{ +#if defined(__ANDROID__) + return SDL_IsScreenKeyboardShown(NULL); +#else + return false; +#endif } CUIRect *CUI::Screen() @@ -370,4 +498,4 @@ void CUI::DoLabel(const CUIRect *r, const char *pText, float Size, int Align, in void CUI::DoLabelScaled(const CUIRect *r, const char *pText, float Size, int Align, int MaxWidth) { DoLabel(r, pText, Size*Scale(), Align, MaxWidth); -} \ No newline at end of file +} diff --git a/src/game/client/ui.h b/src/game/client/ui.h index daba5d514c..a063a8f858 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -95,6 +95,11 @@ class CUI // TODO: Refactor: Remove this? void DoLabel(const CUIRect *pRect, const char *pText, float Size, int Align, int MaxWidth = -1); void DoLabelScaled(const CUIRect *pRect, const char *pText, float Size, int Align, int MaxWidth = -1); + + void AndroidShowScreenKeys(bool shown); + void AndroidShowTextInput(const char *text, const char *hintText); + void AndroidBlockAndGetTextInput(char *text, int textLength, const char *hintText); + bool AndroidTextInputShown(); }; diff --git a/src/game/collision.cpp b/src/game/collision.cpp index fd184a3606..aebcabc7af 100644 --- a/src/game/collision.cpp +++ b/src/game/collision.cpp @@ -27,6 +27,7 @@ CCollision::CCollision() m_pSwitch = 0; m_pDoor = 0; m_pSwitchers = 0; + m_pTune = 0; } void CCollision::Init(class CLayers *pLayers) @@ -40,14 +41,25 @@ void CCollision::Init(class CLayers *pLayers) m_pTiles = static_cast(m_pLayers->Map()->GetData(m_pLayers->GameLayer()->m_Data)); if(m_pLayers->TeleLayer()) - m_pTele = static_cast(m_pLayers->Map()->GetData(m_pLayers->TeleLayer()->m_Tele)); + { + unsigned int Size = m_pLayers->Map()->GetUncompressedDataSize(m_pLayers->TeleLayer()->m_Tele); + if (Size >= m_Width*m_Height*sizeof(CTeleTile)) + m_pTele = static_cast(m_pLayers->Map()->GetData(m_pLayers->TeleLayer()->m_Tele)); + } if(m_pLayers->SpeedupLayer()) - m_pSpeedup = static_cast(m_pLayers->Map()->GetData(m_pLayers->SpeedupLayer()->m_Speedup)); + { + unsigned int Size = m_pLayers->Map()->GetUncompressedDataSize(m_pLayers->SpeedupLayer()->m_Speedup); + if (Size >= m_Width*m_Height*sizeof(CSpeedupTile)) + m_pSpeedup = static_cast(m_pLayers->Map()->GetData(m_pLayers->SpeedupLayer()->m_Speedup)); + } if(m_pLayers->SwitchLayer()) { - m_pSwitch = static_cast(m_pLayers->Map()->GetData(m_pLayers->SwitchLayer()->m_Switch)); + unsigned int Size = m_pLayers->Map()->GetUncompressedDataSize(m_pLayers->SwitchLayer()->m_Switch); + if (Size >= m_Width*m_Height*sizeof(CSwitchTile)) + m_pSwitch = static_cast(m_pLayers->Map()->GetData(m_pLayers->SwitchLayer()->m_Switch)); + m_pDoor = new CDoorTile[m_Width*m_Height]; mem_zero(m_pDoor, m_Width * m_Height * sizeof(CDoorTile)); } @@ -57,8 +69,19 @@ void CCollision::Init(class CLayers *pLayers) m_pSwitchers = 0; } + if(m_pLayers->TuneLayer()) + { + unsigned int Size = m_pLayers->Map()->GetUncompressedDataSize(m_pLayers->TuneLayer()->m_Tune); + if (Size >= m_Width*m_Height*sizeof(CTuneTile)) + m_pTune = static_cast(m_pLayers->Map()->GetData(m_pLayers->TuneLayer()->m_Tune)); + } + if(m_pLayers->FrontLayer()) - m_pFront = static_cast(m_pLayers->Map()->GetData(m_pLayers->FrontLayer()->m_Front)); + { + unsigned int Size = m_pLayers->Map()->GetUncompressedDataSize(m_pLayers->FrontLayer()->m_Front); + if (Size >= m_Width*m_Height*sizeof(CTile)) + m_pFront = static_cast(m_pLayers->Map()->GetData(m_pLayers->FrontLayer()->m_Front)); + } for(int i = 0; i < m_Width*m_Height; i++) { @@ -75,9 +98,9 @@ void CCollision::Init(class CLayers *pLayers) Index = m_pSwitch[i].m_Type; - if(Index <= TILE_NPH) + if(Index <= TILE_NPH_START) { - if(Index >= TILE_FREEZE && Index <= TILE_SWITCHCLOSE) + if(Index >= TILE_JUMP && Index <= TILE_PENALTY) m_pSwitch[i].m_Type = Index; else m_pSwitch[i].m_Type = 0; @@ -87,7 +110,7 @@ void CCollision::Init(class CLayers *pLayers) { Index = m_pFront[i].m_Index; - if(Index <= TILE_NPH) + if(Index <= TILE_NPH_START) { switch(Index) { @@ -108,12 +131,12 @@ void CCollision::Init(class CLayers *pLayers) } // DDRace tiles - if(Index == TILE_THROUGH || (Index >= TILE_FREEZE && Index <= TILE_UNFREEZE) || (Index >= TILE_SWITCHOPEN && Index <= TILE_TELECHECKIN) || (Index >= TILE_BEGIN && Index <= TILE_STOPA) || Index == TILE_CP || Index == TILE_CP_F || (Index >= TILE_OLDLASER && Index <= TILE_NPH) || (Index >= TILE_EHOOK_START && Index <= TILE_SOLO_END) || (Index >= TILE_DFREEZE && Index <= TILE_DUNFREEZE)) + if(Index == TILE_THROUGH || Index == TILE_WALLJUMP || (Index >= TILE_FREEZE && Index <= TILE_UNFREEZE) || (Index >= TILE_SWITCHOPEN && Index <= TILE_TELECHECKIN) || (Index >= TILE_BEGIN && Index <= TILE_STOPA) || Index == TILE_CP || Index == TILE_CP_F || (Index >= TILE_OLDLASER && Index <= TILE_NPH_START) || (Index >= TILE_EHOOK_START && Index <= TILE_SOLO_END) || (Index >= TILE_DFREEZE && Index <= TILE_DUNFREEZE)) m_pFront[i].m_Index = Index; } } Index = m_pTiles[i].m_Index; - if(Index <= TILE_NPH) + if(Index <= TILE_NPH_START) { switch(Index) { @@ -134,7 +157,7 @@ void CCollision::Init(class CLayers *pLayers) } // DDRace tiles - if(Index == TILE_THROUGH || (Index >= TILE_FREEZE && Index <= TILE_UNFREEZE) || (Index >= TILE_SWITCHOPEN && Index <= TILE_TELECHECKIN) || (Index >= TILE_BEGIN && Index <= TILE_STOPA) || Index == TILE_CP || Index == TILE_CP_F || (Index >= TILE_OLDLASER && Index <= TILE_NPH) || (Index >= TILE_EHOOK_START && Index <= TILE_SOLO_END) || (Index >= TILE_DFREEZE && Index <= TILE_DUNFREEZE)) + if(Index == TILE_THROUGH || Index == TILE_WALLJUMP || (Index >= TILE_FREEZE && Index <= TILE_UNFREEZE) || (Index >= TILE_SWITCHOPEN && Index <= TILE_TELECHECKIN) || (Index >= TILE_BEGIN && Index <= TILE_STOPA) || Index == TILE_CP || Index == TILE_CP_F || (Index >= TILE_OLDLASER && Index <= TILE_NPH_START) || (Index >= TILE_EHOOK_START && Index <= TILE_SOLO_END) || (Index >= TILE_DFREEZE && Index <= TILE_DUNFREEZE)) m_pTiles[i].m_Index = Index; } } @@ -144,7 +167,7 @@ void CCollision::Init(class CLayers *pLayers) for (int i = 0; i < m_NumSwitchers+1; ++i) { - for (int j = 0; j < 16; ++j) + for (int j = 0; j < MAX_CLIENTS; ++j) { m_pSwitchers[i].m_Status[j] = true; m_pSwitchers[i].m_EndTick[j] = 0; @@ -186,13 +209,116 @@ int CCollision::IntersectLine(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *p { ThroughOffset(Pos0, Pos1, &dx, &dy); } - for(int i = 0; i < End; i++) + for(int i = 0; i <= End; i++) + { + float a = i/(float)End; + vec2 Pos = mix(Pos0, Pos1, a); + ix = round_to_int(Pos.x); + iy = round_to_int(Pos.y); + + if((CheckPoint(ix, iy) && !(AllowThrough && IsThrough(ix + dx, iy + dy)))) + { + if(pOutCollision) + *pOutCollision = Pos; + if(pOutBeforeCollision) + *pOutBeforeCollision = Last; + return GetCollisionAt(ix, iy); + } + + Last = Pos; + } + if(pOutCollision) + *pOutCollision = Pos1; + if(pOutBeforeCollision) + *pOutBeforeCollision = Pos1; + return 0; +} + +int CCollision::IntersectLineTeleHook(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision, int *pTeleNr, bool AllowThrough) +{ + float Distance = distance(Pos0, Pos1); + int End(Distance+1); + vec2 Last = Pos0; + int ix = 0, iy = 0; // Temporary position for checking collision + int dx = 0, dy = 0; // Offset for checking the "through" tile + if (AllowThrough) + { + ThroughOffset(Pos0, Pos1, &dx, &dy); + } + for(int i = 0; i <= End; i++) + { + float a = i/(float)End; + vec2 Pos = mix(Pos0, Pos1, a); + ix = round_to_int(Pos.x); + iy = round_to_int(Pos.y); + + int Nx = clamp(ix/32, 0, m_Width-1); + int Ny = clamp(iy/32, 0, m_Height-1); + if (g_Config.m_SvOldTeleportHook) + *pTeleNr = IsTeleport(Ny*m_Width+Nx); + else + *pTeleNr = IsTeleportHook(Ny*m_Width+Nx); + if(*pTeleNr) + { + if(pOutCollision) + *pOutCollision = Pos; + if(pOutBeforeCollision) + *pOutBeforeCollision = Last; + return COLFLAG_TELE; + } + + if((CheckPoint(ix, iy) && !(AllowThrough && IsThrough(ix + dx, iy + dy)))) + { + if(pOutCollision) + *pOutCollision = Pos; + if(pOutBeforeCollision) + *pOutBeforeCollision = Last; + return GetCollisionAt(ix, iy); + } + + Last = Pos; + } + if(pOutCollision) + *pOutCollision = Pos1; + if(pOutBeforeCollision) + *pOutBeforeCollision = Pos1; + return 0; +} + +int CCollision::IntersectLineTeleWeapon(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision, int *pTeleNr, bool AllowThrough) +{ + float Distance = distance(Pos0, Pos1); + int End(Distance+1); + vec2 Last = Pos0; + int ix = 0, iy = 0; // Temporary position for checking collision + int dx = 0, dy = 0; // Offset for checking the "through" tile + if (AllowThrough) + { + ThroughOffset(Pos0, Pos1, &dx, &dy); + } + for(int i = 0; i <= End; i++) { - float a = i/Distance; + float a = i/(float)End; vec2 Pos = mix(Pos0, Pos1, a); - ix = round(Pos.x); - iy = round(Pos.y); - if(CheckPoint(ix, iy) && !(AllowThrough && IsThrough(ix + dx, iy + dy))) + ix = round_to_int(Pos.x); + iy = round_to_int(Pos.y); + + int Nx = clamp(ix/32, 0, m_Width-1); + int Ny = clamp(iy/32, 0, m_Height-1); + if (g_Config.m_SvOldTeleportWeapons) + *pTeleNr = IsTeleport(Ny*m_Width+Nx); + else + *pTeleNr = IsTeleportWeapon(Ny*m_Width+Nx); + if(*pTeleNr) + { + if(pOutCollision) + *pOutCollision = Pos; + if(pOutBeforeCollision) + *pOutBeforeCollision = Last; + return COLFLAG_TELE; + } + + if((CheckPoint(ix, iy) && !(AllowThrough && IsThrough(ix + dx, iy + dy)))) { if(pOutCollision) *pOutCollision = Pos; @@ -200,6 +326,7 @@ int CCollision::IntersectLine(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *p *pOutBeforeCollision = Last; return GetCollisionAt(ix, iy); } + Last = Pos; } if(pOutCollision) @@ -336,6 +463,7 @@ void CCollision::Dest() m_pSpeedup = 0; m_pFront = 0; m_pSwitch = 0; + m_pTune = 0; m_pDoor = 0; m_pSwitchers = 0; } @@ -360,6 +488,14 @@ int CCollision::IsThrough(int x, int y) return 0; } +int CCollision::IsWallJump(int Index) +{ + if(Index < 0) + return 0; + + return m_pTiles[Index].m_Index == TILE_WALLJUMP; +} + int CCollision::IsNoLaser(int x, int y) { return (CCollision::GetTile(x,y) & COLFLAG_NOLASER); @@ -407,6 +543,19 @@ int CCollision::IsCheckTeleport(int Index) return 0; } +int CCollision::IsCheckEvilTeleport(int Index) +{ + if(Index < 0) + return 0; + if(!m_pTele) + return 0; + + if(m_pTele[Index].m_Type == TILE_TELECHECKINEVIL) + return m_pTele[Index].m_Number; + + return 0; +} + int CCollision::IsTCheckpoint(int Index) { if(Index < 0) @@ -421,6 +570,29 @@ int CCollision::IsTCheckpoint(int Index) return 0; } +int CCollision::IsTeleportWeapon(int Index) +{ + if(Index < 0 || !m_pTele) + return 0; + + if(m_pTele[Index].m_Type == TILE_TELEINWEAPON) + return m_pTele[Index].m_Number; + + return 0; +} + +int CCollision::IsTeleportHook(int Index) +{ + if(Index < 0 || !m_pTele) + return 0; + + if(m_pTele[Index].m_Type == TILE_TELEINHOOK) + return m_pTele[Index].m_Number; + + return 0; +} + + int CCollision::IsSpeedup(int Index) { if(Index < 0 || !m_pSpeedup) @@ -432,6 +604,17 @@ int CCollision::IsSpeedup(int Index) return 0; } +int CCollision::IsTune(int Index) +{ + if(Index < 0 || !m_pTune) + return 0; + + if(m_pTune[Index].m_Type) + return m_pTune[Index].m_Number; + + return 0; +} + void CCollision::GetSpeedup(int Index, vec2 *Dir, int *Force, int *MaxSpeed) { if(Index < 0 || !m_pSpeedup) @@ -538,11 +721,11 @@ bool CCollision::TileExists(int Index) if(Index < 0) return false; - if(m_pTiles[Index].m_Index >= TILE_FREEZE && m_pTiles[Index].m_Index <= TILE_NPH) + if(m_pTiles[Index].m_Index >= TILE_FREEZE && m_pTiles[Index].m_Index <= TILE_NPH_START) return true; - if(m_pFront && m_pFront[Index].m_Index >= TILE_FREEZE && m_pFront[Index].m_Index <= TILE_NPH) + if(m_pFront && m_pFront[Index].m_Index >= TILE_FREEZE && m_pFront[Index].m_Index <= TILE_NPH_START) return true; - if(m_pTele && (m_pTele[Index].m_Type == TILE_TELEIN || m_pTele[Index].m_Type == TILE_TELEINEVIL || m_pTele[Index].m_Type == TILE_TELECHECK || m_pTele[Index].m_Type == TILE_TELECHECKIN)) + if(m_pTele && (m_pTele[Index].m_Type == TILE_TELEIN || m_pTele[Index].m_Type == TILE_TELEINEVIL || m_pTele[Index].m_Type == TILE_TELECHECKINEVIL ||m_pTele[Index].m_Type == TILE_TELECHECK || m_pTele[Index].m_Type == TILE_TELECHECKIN)) return true; if(m_pSpeedup && m_pSpeedup[Index].m_Force > 0) return true; @@ -550,6 +733,8 @@ bool CCollision::TileExists(int Index) return true; if(m_pSwitch && m_pSwitch[Index].m_Type) return true; + if(m_pTune && m_pTune[Index].m_Type) + return true; return TileExistsNext(Index); } @@ -711,6 +896,51 @@ int CCollision::GetIndex(int Nx, int Ny) return m_pTiles[Ny*m_Width+Nx].m_Index; } +int CCollision::GetIndex(vec2 Pos) +{ + int nx = clamp((int)Pos.x/32, 0, m_Width-1); + int ny = clamp((int)Pos.y/32, 0, m_Height-1); + + return ny*m_Width+nx; +} + +int CCollision::GetIndex(vec2 PrevPos, vec2 Pos) +{ + float Distance = distance(PrevPos, Pos); + + if(!Distance) + { + int Nx = clamp((int)Pos.x/32, 0, m_Width-1); + int Ny = clamp((int)Pos.y/32, 0, m_Height-1); + + if ((m_pTele) || + (m_pSpeedup && m_pSpeedup[Ny*m_Width+Nx].m_Force > 0)) + { + return Ny*m_Width+Nx; + } + } + + float a = 0.0f; + vec2 Tmp = vec2(0, 0); + int Nx = 0; + int Ny = 0; + + for(float f = 0; f < Distance; f++) + { + a = f/Distance; + Tmp = mix(PrevPos, Pos, a); + Nx = clamp((int)Tmp.x/32, 0, m_Width-1); + Ny = clamp((int)Tmp.y/32, 0, m_Height-1); + if ((m_pTele) || + (m_pSpeedup && m_pSpeedup[Ny*m_Width+Nx].m_Force > 0)) + { + return Ny*m_Width+Nx; + } + } + + return -1; +} + int CCollision::GetFIndex(int Nx, int Ny) { if(!m_pFront) return 0; @@ -753,10 +983,13 @@ int CCollision::Entity(int x, int y, int Layer) case LAYER_SPEEDUP: str_format(aBuf,sizeof(aBuf), "Speedup"); break; + case LAYER_TUNE: + str_format(aBuf,sizeof(aBuf), "Tune"); + break; default: str_format(aBuf,sizeof(aBuf), "Unknown"); } - dbg_msg("CCollision::Entity","Something is VERY wrong with the %s layer please report this at http://DDRace.info, you will need to post the map as well and aNy steps that u think may have led to this, Please Also Read the News Section every once and a while", aBuf); + dbg_msg("CCollision::Entity","Something is VERY wrong with the %s layer please report this at http://ddnet.tw, you will need to post the map as well and any steps that u think may have led to this", aBuf); return 0; } switch (Layer) @@ -771,6 +1004,8 @@ int CCollision::Entity(int x, int y, int Layer) return m_pTele[y*m_Width+x].m_Type - ENTITY_OFFSET; case LAYER_SPEEDUP: return m_pSpeedup[y*m_Width+x].m_Type - ENTITY_OFFSET; + case LAYER_TUNE: + return m_pTune[y*m_Width+x].m_Type - ENTITY_OFFSET; default: return 0; break; @@ -779,8 +1014,8 @@ int CCollision::Entity(int x, int y, int Layer) void CCollision::SetCollisionAt(float x, float y, int flag) { - int Nx = clamp(round(x)/32, 0, m_Width-1); - int Ny = clamp(round(y)/32, 0, m_Height-1); + int Nx = clamp(round_to_int(x)/32, 0, m_Width-1); + int Ny = clamp(round_to_int(y)/32, 0, m_Height-1); m_pTiles[Ny * m_Width + Nx].m_Index = flag; } @@ -789,8 +1024,8 @@ void CCollision::SetDCollisionAt(float x, float y, int Type, int Flags, int Numb { if(!m_pDoor) return; - int Nx = clamp(round(x)/32, 0, m_Width-1); - int Ny = clamp(round(y)/32, 0, m_Height-1); + int Nx = clamp(round_to_int(x)/32, 0, m_Width-1); + int Ny = clamp(round_to_int(y)/32, 0, m_Height-1); m_pDoor[Ny * m_Width + Nx].m_Index = Type; m_pDoor[Ny * m_Width + Nx].m_Flags = Flags; @@ -860,8 +1095,8 @@ int CCollision::IntersectNoLaser(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 { float a = f/d; vec2 Pos = mix(Pos0, Pos1, a); - int Nx = clamp(round(Pos.x)/32, 0, m_Width-1); - int Ny = clamp(round(Pos.y)/32, 0, m_Height-1); + int Nx = clamp(round_to_int(Pos.x)/32, 0, m_Width-1); + int Ny = clamp(round_to_int(Pos.y)/32, 0, m_Height-1); if(GetIndex(Nx, Ny) == COLFLAG_SOLID || GetIndex(Nx, Ny) == (COLFLAG_SOLID|COLFLAG_NOHOOK) || GetIndex(Nx, Ny) == COLFLAG_NOLASER @@ -893,13 +1128,13 @@ int CCollision::IntersectNoLaserNW(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, ve { float a = f/d; vec2 Pos = mix(Pos0, Pos1, a); - if(IsNoLaser(round(Pos.x), round(Pos.y)) || IsFNoLaser(round(Pos.x), round(Pos.y))) + if(IsNoLaser(round_to_int(Pos.x), round_to_int(Pos.y)) || IsFNoLaser(round_to_int(Pos.x), round_to_int(Pos.y))) { if(pOutCollision) *pOutCollision = Pos; if(pOutBeforeCollision) *pOutBeforeCollision = Last; - if(IsNoLaser(round(Pos.x), round(Pos.y))) return GetCollisionAt(Pos.x, Pos.y); + if(IsNoLaser(round_to_int(Pos.x), round_to_int(Pos.y))) return GetCollisionAt(Pos.x, Pos.y); else return GetFCollisionAt(Pos.x, Pos.y); } Last = Pos; @@ -920,17 +1155,17 @@ int CCollision::IntersectAir(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pO { float a = f/d; vec2 Pos = mix(Pos0, Pos1, a); - if(IsSolid(round(Pos.x), round(Pos.y)) || (!GetTile(round(Pos.x), round(Pos.y)) && !GetFTile(round(Pos.x), round(Pos.y)))) + if(IsSolid(round_to_int(Pos.x), round_to_int(Pos.y)) || (!GetTile(round_to_int(Pos.x), round_to_int(Pos.y)) && !GetFTile(round_to_int(Pos.x), round_to_int(Pos.y)))) { if(pOutCollision) *pOutCollision = Pos; if(pOutBeforeCollision) *pOutBeforeCollision = Last; - if(!GetTile(round(Pos.x), round(Pos.y)) && !GetFTile(round(Pos.x), round(Pos.y))) + if(!GetTile(round_to_int(Pos.x), round_to_int(Pos.y)) && !GetFTile(round_to_int(Pos.x), round_to_int(Pos.y))) return -1; else - if (!GetTile(round(Pos.x), round(Pos.y))) return GetTile(round(Pos.x), round(Pos.y)); - else return GetFTile(round(Pos.x), round(Pos.y)); + if (!GetTile(round_to_int(Pos.x), round_to_int(Pos.y))) return GetTile(round_to_int(Pos.x), round_to_int(Pos.y)); + else return GetFTile(round_to_int(Pos.x), round_to_int(Pos.y)); } Last = Pos; } diff --git a/src/game/collision.h b/src/game/collision.h index f28e2ad148..d57f2f0c5d 100644 --- a/src/game/collision.h +++ b/src/game/collision.h @@ -4,6 +4,7 @@ #define GAME_COLLISION_H #include +#include #include @@ -25,17 +26,20 @@ class CCollision COLFLAG_NOHOOK=4, // DDRace COLFLAG_NOLASER=8, - COLFLAG_THROUGH=16 + COLFLAG_THROUGH=16, + COLFLAG_TELE=32 }; CCollision(); void Init(class CLayers *pLayers); - bool CheckPoint(float x, float y) { return IsSolid(round(x), round(y)); } + bool CheckPoint(float x, float y) { return IsSolid(round_to_int(x), round_to_int(y)); } bool CheckPoint(vec2 Pos) { return CheckPoint(Pos.x, Pos.y); } - int GetCollisionAt(float x, float y) { return GetTile(round(x), round(y)); } + int GetCollisionAt(float x, float y) { return GetTile(round_to_int(x), round_to_int(y)); } int GetWidth() { return m_Width; }; int GetHeight() { return m_Height; }; int IntersectLine(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision, bool AllowThrough); + int IntersectLineTeleWeapon(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision, int *pTeleNr, bool AllowThrough); + int IntersectLineTeleHook(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision, int *pTeleNr, bool AllowThrough); void MovePoint(vec2 *pInoutPos, vec2 *pInoutVel, float Elasticity, int *pBounces); void MoveBox(vec2 *pInoutPos, vec2 *pInoutVel, vec2 Size, float Elasticity); bool TestBox(vec2 Pos, vec2 Size); @@ -49,11 +53,13 @@ class CCollision int GetDTileIndex(int Index); int GetDTileFlags(int Index); int GetDTileNumber(int Index); - int GetFCollisionAt(float x, float y) { return GetFTile(round(x), round(y)); } + int GetFCollisionAt(float x, float y) { return GetFTile(round_to_int(x), round_to_int(y)); } int IntersectNoLaser(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision); int IntersectNoLaserNW(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision); int IntersectAir(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision); int GetIndex(int x, int y); + int GetIndex(vec2 Pos); + int GetIndex(vec2 PrevPos, vec2 Pos); int GetFIndex(int x, int y); int GetTile(int x, int y); @@ -72,9 +78,13 @@ class CCollision int IsTeleport(int Index); int IsEvilTeleport(int Index); int IsCheckTeleport(int Index); + int IsCheckEvilTeleport(int Index); + int IsTeleportWeapon(int Index); + int IsTeleportHook(int Index); int IsTCheckpoint(int Index); //int IsCheckpoint(int Index); int IsSpeedup(int Index); + int IsTune(int Index); void GetSpeedup(int Index, vec2 *Dir, int *Force, int *MaxSpeed); int IsSwitch(int Index); int GetSwitchNumber(int Index); @@ -82,6 +92,7 @@ class CCollision int IsSolid(int x, int y); int IsThrough(int x, int y); + int IsWallJump(int Index); int IsNoLaser(int x, int y); int IsFNoLaser(int x, int y); @@ -94,6 +105,7 @@ class CCollision class CTeleTile *TeleLayer() { return m_pTele; } class CSwitchTile *SwitchLayer() { return m_pSwitch; } + class CTuneTile *TuneLayer() { return m_pTune; } class CLayers *Layers() { return m_pLayers; } int m_NumSwitchers; @@ -103,12 +115,13 @@ class CCollision class CSpeedupTile *m_pSpeedup; class CTile *m_pFront; class CSwitchTile *m_pSwitch; + class CTuneTile *m_pTune; class CDoorTile *m_pDoor; struct SSwitchers { - bool m_Status[16]; - int m_EndTick[16]; - int m_Type[16]; + bool m_Status[MAX_CLIENTS]; + int m_EndTick[MAX_CLIENTS]; + int m_Type[MAX_CLIENTS]; }; public: diff --git a/src/game/ddracecommands.h b/src/game/ddracecommands.h index 1adf7a0ce9..3fdc6e489f 100644 --- a/src/game/ddracecommands.h +++ b/src/game/ddracecommands.h @@ -7,7 +7,9 @@ #endif CONSOLE_COMMAND("kill_pl", "v", CFGFLAG_SERVER, ConKillPlayer, this, "Kills player v and announces the kill") -CONSOLE_COMMAND("tele", "v", CFGFLAG_SERVER|CMDFLAG_TEST, ConTeleport, this, "Teleports you to player v") +CONSOLE_COMMAND("totele", "i", CFGFLAG_SERVER|CMDFLAG_TEST, ConToTeleporter, this, "Teleports you to teleporter v") +CONSOLE_COMMAND("totelecp", "i", CFGFLAG_SERVER|CMDFLAG_TEST, ConToCheckTeleporter, this, "Teleports you to checkpoint teleporter v") +CONSOLE_COMMAND("tele", "v?i", CFGFLAG_SERVER|CMDFLAG_TEST, ConTeleport, this, "Teleports you (or player v) to player i") CONSOLE_COMMAND("addweapon", "i", CFGFLAG_SERVER|CMDFLAG_TEST, ConAddWeapon, this, "Gives weapon with id i to you (all = -1, hammer = 0, gun = 1, shotgun = 2, grenade = 3, rifle = 4, ninja = 5)") CONSOLE_COMMAND("removeweapon", "i", CFGFLAG_SERVER|CMDFLAG_TEST, ConRemoveWeapon, this, "removes weapon with id i from you (all = -1, hammer = 0, gun = 1, shotgun = 2, grenade = 3, rifle = 4)") CONSOLE_COMMAND("shotgun", "", CFGFLAG_SERVER|CMDFLAG_TEST, ConShotgun, this, "Gives a shotgun to you") @@ -21,20 +23,30 @@ CONSOLE_COMMAND("unweapons", "", CFGFLAG_SERVER|CMDFLAG_TEST, ConUnWeapons, this CONSOLE_COMMAND("ninja", "", CFGFLAG_SERVER|CMDFLAG_TEST, ConNinja, this, "Makes you a ninja") CONSOLE_COMMAND("super", "", CFGFLAG_SERVER|CMDFLAG_TEST, ConSuper, this, "Makes you super") CONSOLE_COMMAND("unsuper", "", CFGFLAG_SERVER|CMDFLAG_TEST, ConUnSuper, this, "Removes super from you") +CONSOLE_COMMAND("unsolo", "", CFGFLAG_SERVER|CMDFLAG_TEST, ConUnSolo, this, "Puts you out of solo part") +CONSOLE_COMMAND("undeep", "", CFGFLAG_SERVER|CMDFLAG_TEST, ConUnDeep, this, "Puts you out of deep freeze") CONSOLE_COMMAND("left", "", CFGFLAG_SERVER|CMDFLAG_TEST, ConGoLeft, this, "Makes you move 1 tile left") CONSOLE_COMMAND("right", "", CFGFLAG_SERVER|CMDFLAG_TEST, ConGoRight, this, "Makes you move 1 tile right") CONSOLE_COMMAND("up", "", CFGFLAG_SERVER|CMDFLAG_TEST, ConGoUp, this, "Makes you move 1 tile up") CONSOLE_COMMAND("down", "", CFGFLAG_SERVER|CMDFLAG_TEST, ConGoDown, this, "Makes you move 1 tile down") -CONSOLE_COMMAND("move", "ii", CFGFLAG_SERVER|CMDFLAG_TEST, ConMove, this, "Moves you relative to your position to the tile with x/y-number ii") -CONSOLE_COMMAND("move_raw", "ii", CFGFLAG_SERVER|CMDFLAG_TEST, ConMoveRaw, this, "Moves you relative to your position to the point with x/y-coordinates ii") -CONSOLE_COMMAND("force_pause", "vi", CFGFLAG_SERVER, ConForcePause, this, "Force v to pause for i seconds") -CONSOLE_COMMAND("force_unpause", "v", CFGFLAG_SERVER, ConForcePause, this, "Set force-pause timer of v to 0.") + +CONSOLE_COMMAND("move", "ii", CFGFLAG_SERVER|CMDFLAG_TEST, ConMove, this, "Moves to the tile with x/y-number ii") +CONSOLE_COMMAND("move_raw", "ii", CFGFLAG_SERVER|CMDFLAG_TEST, ConMoveRaw, this, "Moves to the point with x/y-coordinates ii") +CONSOLE_COMMAND("force_pause", "ii", CFGFLAG_SERVER, ConForcePause, this, "Force i to pause for i seconds") +CONSOLE_COMMAND("force_unpause", "i", CFGFLAG_SERVER, ConForcePause, this, "Set force-pause timer of v to 0.") +CONSOLE_COMMAND("showothers", "?i", CFGFLAG_CHAT, ConShowOthers, this, "Whether to show players from other teams or not (off by default), optional i = 0 for off else for on") +CONSOLE_COMMAND("showall", "?i", CFGFLAG_CHAT, ConShowAll, this, "Whether to show players at any distance (off by default), optional i = 0 for off else for on") + +CONSOLE_COMMAND("list", "?s", CFGFLAG_CHAT, ConList, this, "List connected players with optional case-insensitive substring matching filter") CONSOLE_COMMAND("mute", "", CFGFLAG_SERVER, ConMute, this, ""); CONSOLE_COMMAND("muteid", "vi", CFGFLAG_SERVER, ConMuteID, this, ""); CONSOLE_COMMAND("muteip", "si", CFGFLAG_SERVER, ConMuteIP, this, ""); CONSOLE_COMMAND("unmute", "v", CFGFLAG_SERVER, ConUnmute, this, ""); CONSOLE_COMMAND("mutes", "", CFGFLAG_SERVER, ConMutes, this, ""); + +CONSOLE_COMMAND("freezehammer", "v", CFGFLAG_SERVER, ConFreezeHammer, this, "Gives a player Freeze Hammer") +CONSOLE_COMMAND("unfreezehammer", "v", CFGFLAG_SERVER, ConUnFreezeHammer, this, "Removes Freeze Hammer from a player") #undef CONSOLE_COMMAND #endif diff --git a/src/game/editor/auto_map.cpp b/src/game/editor/auto_map.cpp index 3abcf0f467..c09e8d8bda 100644 --- a/src/game/editor/auto_map.cpp +++ b/src/game/editor/auto_map.cpp @@ -54,8 +54,9 @@ void CAutoMapper::Load(const char* pTileName) // new index int ID = 0; char aFlip[128] = ""; + char aFlip2[128] = ""; - sscanf(pLine, "Index %d %127s", &ID, aFlip); + sscanf(pLine, "Index %d %127s %127s", &ID, aFlip, aFlip2); CIndexRule NewIndexRule; NewIndexRule.m_ID = ID; @@ -66,9 +67,17 @@ void CAutoMapper::Load(const char* pTileName) if(str_length(aFlip) > 0) { if(!str_comp(aFlip, "XFLIP")) - NewIndexRule.m_Flag = TILEFLAG_VFLIP; + NewIndexRule.m_Flag |= TILEFLAG_VFLIP; else if(!str_comp(aFlip, "YFLIP")) - NewIndexRule.m_Flag = TILEFLAG_HFLIP; + NewIndexRule.m_Flag |= TILEFLAG_HFLIP; + } + + if(str_length(aFlip2) > 0) + { + if(!str_comp(aFlip2, "XFLIP")) + NewIndexRule.m_Flag |= TILEFLAG_VFLIP; + else if(!str_comp(aFlip2, "YFLIP")) + NewIndexRule.m_Flag |= TILEFLAG_HFLIP; } // add the index rule object and make it current @@ -107,6 +116,29 @@ void CAutoMapper::Load(const char* pTileName) } } + // add default rule for Pos 0 0 if there is none + for (int h = 0; h < m_lConfigs.size(); ++h) + { + for(int i = 0; i < m_lConfigs[h].m_aIndexRules.size(); ++i) + { + bool Found = false; + for(int j = 0; j < m_lConfigs[h].m_aIndexRules[i].m_aRules.size(); ++j) + { + CPosRule *pRule = &m_lConfigs[h].m_aIndexRules[i].m_aRules[j]; + if(pRule && pRule->m_X == 0 && pRule->m_Y == 0) + { + Found = true; + break; + } + } + if(!Found) + { + CPosRule NewPosRule = {0, 0, CPosRule::FULL, false}; + m_lConfigs[h].m_aIndexRules[i].m_aRules.add(NewPosRule); + } + } + } + io_close(RulesFile); str_format(aBuf, sizeof(aBuf),"loaded %s", aPath); @@ -135,6 +167,17 @@ void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID) int BaseTile = 1; + CLayerTiles newLayer(pLayer->m_Width, pLayer->m_Height); + + for(int y = 0; y < pLayer->m_Height; y++) + for(int x = 0; x < pLayer->m_Width; x++) + { + CTile *in = &pLayer->m_pTiles[y*pLayer->m_Width+x]; + CTile *out = &newLayer.m_pTiles[y*pLayer->m_Width+x]; + out->m_Index = in->m_Index; + out->m_Flags = in->m_Flags; + } + // find base tile if there is one for(int i = 0; i < pConf->m_aIndexRules.size(); ++i) { @@ -150,11 +193,9 @@ void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID) for(int y = 0; y < pLayer->m_Height; y++) for(int x = 0; x < pLayer->m_Width; x++) { - CTile *pTile = &(pLayer->m_pTiles[y*pLayer->m_Width+x]); - if(pTile->m_Index == 0) - continue; - - pTile->m_Index = BaseTile; + CTile *pTile = &(newLayer.m_pTiles[y*pLayer->m_Width+x]); + if(pTile->m_Index != 0) + pTile->m_Index = BaseTile; m_pEditor->m_Map.m_Modified = true; if(y == 0 || y == pLayer->m_Height-1 || x == 0 || x == pLayer->m_Width-1) @@ -199,4 +240,13 @@ void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID) } } } + + for(int y = 0; y < pLayer->m_Height; y++) + for(int x = 0; x < pLayer->m_Width; x++) + { + CTile *in = &newLayer.m_pTiles[y*pLayer->m_Width+x]; + CTile *out = &pLayer->m_pTiles[y*pLayer->m_Width+x]; + out->m_Index = in->m_Index; + out->m_Flags = in->m_Flags; + } } diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 3f2b695c1c..3482645743 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -5,6 +5,10 @@ #include +#if defined(CONF_FAMILY_UNIX) +#include +#endif + #include #include #include @@ -35,6 +39,7 @@ int CEditor::ms_FrontTexture; int CEditor::ms_TeleTexture; int CEditor::ms_SpeedupTexture; int CEditor::ms_SwitchTexture; +int CEditor::ms_TuneTexture; enum { @@ -44,6 +49,11 @@ enum CEditorImage::~CEditorImage() { m_pEditor->Graphics()->UnloadTexture(m_TexID); + if(m_pData) + { + mem_free(m_pData); + m_pData = 0; + } } CLayerGroup::CLayerGroup() @@ -119,7 +129,7 @@ void CLayerGroup::Render() { if(m_lLayers[i]->m_Visible && m_lLayers[i] != m_pMap->m_pGameLayer && m_lLayers[i] != m_pMap->m_pFrontLayer && m_lLayers[i] != m_pMap->m_pTeleLayer - && m_lLayers[i] != m_pMap->m_pSpeedupLayer && m_lLayers[i] != m_pMap->m_pSwitchLayer) + && m_lLayers[i] != m_pMap->m_pSpeedupLayer && m_lLayers[i] != m_pMap->m_pSwitchLayer && m_lLayers[i] != m_pMap->m_pTuneLayer) { if(m_pMap->m_pEditor->m_ShowDetail || !(m_lLayers[i]->m_Flags&LAYERFLAG_DETAIL)) m_lLayers[i]->Render(); @@ -141,6 +151,7 @@ void CLayerGroup::DeleteLayer(int Index) delete m_lLayers[Index]; m_lLayers.remove_index(Index); m_pMap->m_Modified = true; + m_pMap->m_UndoModified++; } void CLayerGroup::GetSize(float *w, float *h) @@ -155,13 +166,13 @@ void CLayerGroup::GetSize(float *w, float *h) } } - int CLayerGroup::SwapLayers(int Index0, int Index1) { if(Index0 < 0 || Index0 >= m_lLayers.size()) return Index0; if(Index1 < 0 || Index1 >= m_lLayers.size()) return Index0; if(Index0 == Index1) return Index0; m_pMap->m_Modified = true; + m_pMap->m_UndoModified++; swap(m_lLayers[Index0], m_lLayers[Index1]); return Index1; } @@ -281,7 +292,8 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str for(int i = 0; i < Input()->NumEvents(); i++) { Len = str_length(pStr); - ReturnValue |= CLineInput::Manipulate(Input()->GetEvent(i), pStr, StrSize, &Len, &s_AtIndex); + int NumChars = Len; + ReturnValue |= CLineInput::Manipulate(Input()->GetEvent(i), pStr, StrSize, StrSize, &Len, &s_AtIndex, &NumChars); } } @@ -440,6 +452,20 @@ vec4 CEditor::GetButtonColor(const void *pID, int Checked) if(Checked < 0) return vec4(0,0,0,0.5f); + if(Checked > 2) + { + if(UI()->HotItem() == pID) + return vec4(1,0.5,0,0.75f); + return vec4(1,0.5,0,0.5f); + } + + if(Checked > 1) + { + if(UI()->HotItem() == pID) + return vec4(0,1,0,0.5f); + return vec4(0,1,0,0.33f); + } + if(Checked > 0) { if(UI()->HotItem() == pID) @@ -482,6 +508,8 @@ int CEditor::DoButton_Editor(const void *pID, const char *pText, int Checked, co TextRender()->SetCursor(&Cursor, NewRect.x + NewRect.w/2-tw/2, NewRect.y - 1.0f, 10.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = NewRect.w; TextRender()->TextEx(&Cursor, pText, -1); + if(Checked > 1) + Checked -= 2; return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } @@ -713,6 +741,13 @@ void CEditor::CallbackOpenMap(const char *pFileName, int StorageType, void *pUse pEditor->SortImages(); pEditor->m_Dialog = DIALOG_NONE; pEditor->m_Map.m_Modified = false; + pEditor->m_Map.m_UndoModified = 0; + pEditor->m_LastUndoUpdateTime = time_get(); + } + else + { + pEditor->Reset(); + pEditor->m_aFileName[0] = 0; } } void CEditor::CallbackAppendMap(const char *pFileName, int StorageType, void *pUser) @@ -744,6 +779,8 @@ void CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUse str_copy(pEditor->m_aFileName, pFileName, sizeof(pEditor->m_aFileName)); pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder; pEditor->m_Map.m_Modified = false; + pEditor->m_Map.m_UndoModified = 0; + pEditor->m_LastUndoUpdateTime = time_get(); } pEditor->m_Dialog = DIALOG_NONE; @@ -971,7 +1008,7 @@ void CEditor::DoToolbar(CUIRect ToolBar) CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES); // no border for tele layer, speedup, front and switch - if(pT && (pT->m_Tele || pT->m_Speedup || pT->m_Switch || pT->m_Front)) + if(pT && (pT->m_Tele || pT->m_Speedup || pT->m_Switch || pT->m_Front || pT->m_Tune)) pT = 0; if(DoButton_Editor(&s_BorderBut, "Border", pT?0:-1, &Button, 0, "Adds border tiles")) @@ -1008,6 +1045,15 @@ void CEditor::DoToolbar(CUIRect ToolBar) static int s_SwitchPopupID = 0; UiInvokePopupMenu(&s_SwitchPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 36, PopupSwitch); } + // do tuning button + TB_Bottom.VSplitLeft(5.0f, &Button, &TB_Bottom); + TB_Bottom.VSplitLeft(60.0f, &Button, &TB_Bottom); + static int s_TuneButton = 0; + if(DoButton_Ex(&s_TuneButton, "Tune", (pS && pS->m_Tune)?0:-1, &Button, 0, "Tune", CUI::CORNER_ALL)) + { + static int s_TunePopupID = 0; + UiInvokePopupMenu(&s_TunePopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 90, PopupTune); + } } TB_Bottom.VSplitLeft(5.0f, 0, &TB_Bottom); @@ -1057,7 +1103,7 @@ void CEditor::DoToolbar(CUIRect ToolBar) } } -static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation) +static void Rotate(const CPoint *pCenter, CPoint *pPoint, float Rotation) { int x = pPoint->x - pCenter->x; int y = pPoint->y - pCenter->y; @@ -1198,6 +1244,8 @@ void CEditor::DoQuad(CQuad *q, int Index) { if(!UI()->MouseButton(1)) { + m_Map.m_UndoModified++; + static int s_QuadPopupID = 0; UiInvokePopupMenu(&s_QuadPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 180, PopupQuad); m_LockMouse = false; @@ -1209,6 +1257,11 @@ void CEditor::DoQuad(CQuad *q, int Index) { if(!UI()->MouseButton(0)) { + if(s_Operation == OP_ROTATE || s_Operation == OP_MOVE_ALL || s_Operation == OP_MOVE_PIVOT) + { + m_Map.m_UndoModified++; + } + m_LockMouse = false; s_Operation = OP_NONE; UI()->SetActiveItem(0); @@ -1372,6 +1425,8 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) { if(!UI()->MouseButton(1)) { + m_Map.m_UndoModified++; + static int s_PointPopupID = 0; UiInvokePopupMenu(&s_PointPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, PopupPoint); UI()->SetActiveItem(0); @@ -1388,6 +1443,10 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) else m_SelectedPoints = 1<SetActiveItem(0); } @@ -1447,99 +1506,126 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) Graphics()->QuadsDraw(&QuadItem, 1); } -void CEditor::DoQuadEnvelopes(CQuad *pQuad, int Index, int TexID) +void CEditor::DoQuadEnvelopes(const array &lQuads, int TexID) { - CEnvelope *pEnvelope = 0x0; - if(pQuad->m_PosEnv >= 0 && pQuad->m_PosEnv < m_Map.m_lEnvelopes.size()) - pEnvelope = m_Map.m_lEnvelopes[pQuad->m_PosEnv]; - if (!pEnvelope) - return; - - //QuadParams - CPoint *pPoints = pQuad->m_aPoints; + int Num = lQuads.size(); + CEnvelope **apEnvelope = new CEnvelope*[Num]; + mem_zero(apEnvelope, sizeof(CEnvelope*)*Num); + for(int i = 0; i < Num; i++) + { + if((m_ShowEnvelopePreview == 1 && lQuads[i].m_PosEnv == m_SelectedEnvelope) || m_ShowEnvelopePreview == 2) + if(lQuads[i].m_PosEnv >= 0 && lQuads[i].m_PosEnv < m_Map.m_lEnvelopes.size()) + apEnvelope[i] = m_Map.m_lEnvelopes[lQuads[i].m_PosEnv]; + } //Draw Lines Graphics()->TextureSet(-1); Graphics()->LinesBegin(); - Graphics()->SetColor(80.0f/255, 150.0f/255, 230.f/255, 0.5f); - for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++) + Graphics()->SetColor(80.0f/255, 150.0f/255, 230.f/255, 0.5f); + for(int j = 0; j < Num; j++) + { + if(!apEnvelope[j]) + continue; + + //QuadParams + const CPoint *pPoints = lQuads[j].m_aPoints; + for(int i = 0; i < apEnvelope[j]->m_lPoints.size()-1; i++) { - float OffsetX = fx2f(pEnvelope->m_lPoints[i].m_aValues[0]); - float OffsetY = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]); + float OffsetX = fx2f(apEnvelope[j]->m_lPoints[i].m_aValues[0]); + float OffsetY = fx2f(apEnvelope[j]->m_lPoints[i].m_aValues[1]); vec2 Pos0 = vec2(fx2f(pPoints[4].x)+OffsetX, fx2f(pPoints[4].y)+OffsetY); - OffsetX = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[0]); - OffsetY = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[1]); + OffsetX = fx2f(apEnvelope[j]->m_lPoints[i+1].m_aValues[0]); + OffsetY = fx2f(apEnvelope[j]->m_lPoints[i+1].m_aValues[1]); vec2 Pos1 = vec2(fx2f(pPoints[4].x)+OffsetX, fx2f(pPoints[4].y)+OffsetY); IGraphics::CLineItem Line = IGraphics::CLineItem(Pos0.x, Pos0.y, Pos1.x, Pos1.y); Graphics()->LinesDraw(&Line, 1); } - Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + } + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); Graphics()->LinesEnd(); //Draw Quads - for(int i = 0; i < pEnvelope->m_lPoints.size(); i++) + Graphics()->TextureSet(TexID); + Graphics()->QuadsBegin(); + + for(int j = 0; j < Num; j++) { - Graphics()->TextureSet(TexID); - Graphics()->QuadsBegin(); - - //Calc Env Position - float OffsetX = fx2f(pEnvelope->m_lPoints[i].m_aValues[0]); - float OffsetY = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]); - float Rot = fx2f(pEnvelope->m_lPoints[i].m_aValues[2])/360.0f*pi*2; - - //Set Colours - float Alpha = (m_SelectedQuadEnvelope == pQuad->m_PosEnv && m_SelectedEnvelopePoint == i) ? 0.65f : 0.35f; - IGraphics::CColorVertex aArray[4] = { - IGraphics::CColorVertex(0, pQuad->m_aColors[0].r, pQuad->m_aColors[0].g, pQuad->m_aColors[0].b, Alpha), - IGraphics::CColorVertex(1, pQuad->m_aColors[1].r, pQuad->m_aColors[1].g, pQuad->m_aColors[1].b, Alpha), - IGraphics::CColorVertex(2, pQuad->m_aColors[2].r, pQuad->m_aColors[2].g, pQuad->m_aColors[2].b, Alpha), - IGraphics::CColorVertex(3, pQuad->m_aColors[3].r, pQuad->m_aColors[3].g, pQuad->m_aColors[3].b, Alpha)}; - Graphics()->SetColorVertex(aArray, 4); - - //Rotation - if(Rot != 0) + if(!apEnvelope[j]) + continue; + + //QuadParams + const CPoint *pPoints = lQuads[j].m_aPoints; + + for(int i = 0; i < apEnvelope[j]->m_lPoints.size(); i++) { - static CPoint aRotated[4]; - aRotated[0] = pQuad->m_aPoints[0]; - aRotated[1] = pQuad->m_aPoints[1]; - aRotated[2] = pQuad->m_aPoints[2]; - aRotated[3] = pQuad->m_aPoints[3]; - pPoints = aRotated; - - Rotate(&pQuad->m_aPoints[4], &aRotated[0], Rot); - Rotate(&pQuad->m_aPoints[4], &aRotated[1], Rot); - Rotate(&pQuad->m_aPoints[4], &aRotated[2], Rot); - Rotate(&pQuad->m_aPoints[4], &aRotated[3], Rot); + //Calc Env Position + float OffsetX = fx2f(apEnvelope[j]->m_lPoints[i].m_aValues[0]); + float OffsetY = fx2f(apEnvelope[j]->m_lPoints[i].m_aValues[1]); + float Rot = fx2f(apEnvelope[j]->m_lPoints[i].m_aValues[2])/360.0f*pi*2; + + //Set Colours + float Alpha = (m_SelectedQuadEnvelope == lQuads[j].m_PosEnv && m_SelectedEnvelopePoint == i) ? 0.65f : 0.35f; + IGraphics::CColorVertex aArray[4] = { + IGraphics::CColorVertex(0, lQuads[j].m_aColors[0].r, lQuads[j].m_aColors[0].g, lQuads[j].m_aColors[0].b, Alpha), + IGraphics::CColorVertex(1, lQuads[j].m_aColors[1].r, lQuads[j].m_aColors[1].g, lQuads[j].m_aColors[1].b, Alpha), + IGraphics::CColorVertex(2, lQuads[j].m_aColors[2].r, lQuads[j].m_aColors[2].g, lQuads[j].m_aColors[2].b, Alpha), + IGraphics::CColorVertex(3, lQuads[j].m_aColors[3].r, lQuads[j].m_aColors[3].g, lQuads[j].m_aColors[3].b, Alpha)}; + Graphics()->SetColorVertex(aArray, 4); + + //Rotation + if(Rot != 0) + { + static CPoint aRotated[4]; + aRotated[0] = lQuads[j].m_aPoints[0]; + aRotated[1] = lQuads[j].m_aPoints[1]; + aRotated[2] = lQuads[j].m_aPoints[2]; + aRotated[3] = lQuads[j].m_aPoints[3]; + pPoints = aRotated; + + Rotate(&lQuads[j].m_aPoints[4], &aRotated[0], Rot); + Rotate(&lQuads[j].m_aPoints[4], &aRotated[1], Rot); + Rotate(&lQuads[j].m_aPoints[4], &aRotated[2], Rot); + Rotate(&lQuads[j].m_aPoints[4], &aRotated[3], Rot); + } + + //Set Texture Coords + Graphics()->QuadsSetSubsetFree( + fx2f(lQuads[j].m_aTexcoords[0].x), fx2f(lQuads[j].m_aTexcoords[0].y), + fx2f(lQuads[j].m_aTexcoords[1].x), fx2f(lQuads[j].m_aTexcoords[1].y), + fx2f(lQuads[j].m_aTexcoords[2].x), fx2f(lQuads[j].m_aTexcoords[2].y), + fx2f(lQuads[j].m_aTexcoords[3].x), fx2f(lQuads[j].m_aTexcoords[3].y) + ); + + //Set Quad Coords & Draw + IGraphics::CFreeformItem Freeform( + fx2f(pPoints[0].x)+OffsetX, fx2f(pPoints[0].y)+OffsetY, + fx2f(pPoints[1].x)+OffsetX, fx2f(pPoints[1].y)+OffsetY, + fx2f(pPoints[2].x)+OffsetX, fx2f(pPoints[2].y)+OffsetY, + fx2f(pPoints[3].x)+OffsetX, fx2f(pPoints[3].y)+OffsetY); + Graphics()->QuadsDrawFreeform(&Freeform, 1); } + } + Graphics()->QuadsEnd(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); - //Set Texture Coords - Graphics()->QuadsSetSubsetFree( - fx2f(pQuad->m_aTexcoords[0].x), fx2f(pQuad->m_aTexcoords[0].y), - fx2f(pQuad->m_aTexcoords[1].x), fx2f(pQuad->m_aTexcoords[1].y), - fx2f(pQuad->m_aTexcoords[2].x), fx2f(pQuad->m_aTexcoords[2].y), - fx2f(pQuad->m_aTexcoords[3].x), fx2f(pQuad->m_aTexcoords[3].y) - ); - - //Set Quad Coords & Draw - IGraphics::CFreeformItem Freeform( - fx2f(pPoints[0].x)+OffsetX, fx2f(pPoints[0].y)+OffsetY, - fx2f(pPoints[1].x)+OffsetX, fx2f(pPoints[1].y)+OffsetY, - fx2f(pPoints[2].x)+OffsetX, fx2f(pPoints[2].y)+OffsetY, - fx2f(pPoints[3].x)+OffsetX, fx2f(pPoints[3].y)+OffsetY); - Graphics()->QuadsDrawFreeform(&Freeform, 1); + // Draw QuadPoints + for(int j = 0; j < Num; j++) + { + if(!apEnvelope[j]) + continue; - Graphics()->QuadsEnd(); - - Graphics()->TextureSet(-1); - Graphics()->QuadsBegin(); - DoQuadEnvPoint(pQuad, Index, i); - Graphics()->QuadsEnd(); + //QuadParams + for(int i = 0; i < apEnvelope[j]->m_lPoints.size()-1; i++) + DoQuadEnvPoint(&lQuads[j], j, i); } + Graphics()->QuadsEnd(); + delete[] apEnvelope; } -void CEditor::DoQuadEnvPoint(CQuad *pQuad, int QIndex, int PIndex) +void CEditor::DoQuadEnvPoint(const CQuad *pQuad, int QIndex, int PIndex) { enum { @@ -1595,8 +1681,8 @@ void CEditor::DoQuadEnvPoint(CQuad *pQuad, int QIndex, int PIndex) else y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); - pEnvelope->m_lPoints[PIndex].m_aValues[0] = f2fx(x); - pEnvelope->m_lPoints[PIndex].m_aValues[1] = f2fx(y); + pEnvelope->m_lPoints[PIndex].m_aValues[0] = f2fx(x)-pQuad->m_aPoints[4].x; + pEnvelope->m_lPoints[PIndex].m_aValues[1] = f2fx(y)-pQuad->m_aPoints[4].y; } else { @@ -1670,7 +1756,8 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) m_Map.m_lGroups[g] == (CLayerGroup *)m_Map.m_pFrontLayer || m_Map.m_lGroups[g] == (CLayerGroup *)m_Map.m_pTeleLayer || m_Map.m_lGroups[g] == (CLayerGroup *)m_Map.m_pSpeedupLayer || - m_Map.m_lGroups[g] == (CLayerGroup *)m_Map.m_pSwitchLayer + m_Map.m_lGroups[g] == (CLayerGroup *)m_Map.m_pSwitchLayer || + m_Map.m_lGroups[g] == (CLayerGroup *)m_Map.m_pTuneLayer ) continue; if(m_Map.m_lGroups[g]->m_Visible) @@ -1678,7 +1765,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) //UI()->ClipEnable(&view); } - // render the game, tele, speedup, front and switch above everything else + // render the game, tele, speedup, front, tune and switch above everything else if(m_Map.m_pGameGroup->m_Visible) { m_Map.m_pGameGroup->MapScreen(); @@ -1692,7 +1779,8 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) m_Map.m_pGameGroup->m_lLayers[i] == m_Map.m_pFrontLayer || m_Map.m_pGameGroup->m_lLayers[i] == m_Map.m_pTeleLayer || m_Map.m_pGameGroup->m_lLayers[i] == m_Map.m_pSpeedupLayer || - m_Map.m_pGameGroup->m_lLayers[i] == m_Map.m_pSwitchLayer + m_Map.m_pGameGroup->m_lLayers[i] == m_Map.m_pSwitchLayer || + m_Map.m_pGameGroup->m_lLayers[i] == m_Map.m_pTuneLayer ) ) m_Map.m_pGameGroup->m_lLayers[i]->Render(); @@ -2020,6 +2108,9 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) // release mouse if(!UI()->MouseButton(0)) { + if(s_Operation == OP_BRUSH_DRAW || s_Operation == OP_BRUSH_PAINT) + m_Map.m_UndoModified++; + s_Operation = OP_NONE; UI()->SetActiveItem(0); } @@ -2153,21 +2244,16 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) if(pLayer->m_Image >= 0 && pLayer->m_Image < m_Map.m_lImages.size()) TexID = m_Map.m_lImages[pLayer->m_Image]->m_TexID; - for(int i = 0; i < pLayer->m_lQuads.size(); i++) - { - if((m_ShowEnvelopePreview == 1 && pLayer->m_lQuads[i].m_PosEnv == m_SelectedEnvelope) || m_ShowEnvelopePreview == 2) - DoQuadEnvelopes(&pLayer->m_lQuads[i], i, TexID); - } - + DoQuadEnvelopes(pLayer->m_lQuads, TexID); m_ShowEnvelopePreview = 0; - } + } Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); //UI()->ClipDisable(); } -int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int *pNewVal) +int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int *pNewVal, vec4 color) { int Change = -1; @@ -2188,7 +2274,7 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int * Shifter.VSplitRight(10.0f, &Shifter, &Inc); Shifter.VSplitLeft(10.0f, &Dec, &Shifter); str_format(aBuf, sizeof(aBuf),"%d", pProps[i].m_Value); - RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f); + RenderTools()->DrawUIRect(&Shifter, color, 0, 0.0f); UI()->DoLabel(&Shifter, aBuf, 10.0f, 0, -1); if(DoButton_ButtonDec(&pIDs[i], 0, 0, &Dec, 0, "Decrease")) @@ -2453,7 +2539,7 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) m_SelectedGroup = g; static int s_LayerPopupID = 0; if(Result == 2) - UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 245, PopupLayer); + UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 260, PopupLayer); } LayerCur += 14.0f; @@ -2488,11 +2574,17 @@ void CEditor::ReplaceImage(const char *pFileName, int StorageType, void *pUser) CEditorImage *pImg = pEditor->m_Map.m_lImages[pEditor->m_SelectedImage]; int External = pImg->m_External; pEditor->Graphics()->UnloadTexture(pImg->m_TexID); + if(pImg->m_pData) + { + mem_free(pImg->m_pData); + pImg->m_pData = 0; + } *pImg = ImgInfo; pImg->m_External = External; pEditor->ExtractName(pFileName, pImg->m_aName, sizeof(pImg->m_aName)); pImg->m_AutoMapper.Load(pImg->m_aName); pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0); + ImgInfo.m_pData = 0; pEditor->SortImages(); for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i) { @@ -2521,6 +2613,7 @@ void CEditor::AddImage(const char *pFileName, int StorageType, void *pUser) CEditorImage *pImg = new CEditorImage(pEditor); *pImg = ImgInfo; pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0); + ImgInfo.m_pData = 0; pImg->m_External = 1; // external by default str_copy(pImg->m_aName, aBuf, sizeof(pImg->m_aName)); pImg->m_AutoMapper.Load(pImg->m_aName); @@ -2718,7 +2811,30 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) str_copy(aBuf, m_Map.m_lImages[i]->m_aName, sizeof(aBuf)); ToolBox.HSplitTop(12.0f, &Slot, &ToolBox); - if(int Result = DoButton_Editor(&m_Map.m_lImages[i], aBuf, m_SelectedImage == i, &Slot, + int Selected = m_SelectedImage == i; + for(int x = 0; x < m_Map.m_lGroups.size(); ++x) + for(int j = 0; j < m_Map.m_lGroups[x]->m_lLayers.size(); ++j) + if(m_Map.m_lGroups[x]->m_lLayers[j]->m_Type == LAYERTYPE_QUADS) + { + CLayerQuads *pLayer = static_cast(m_Map.m_lGroups[x]->m_lLayers[j]); + if(pLayer->m_Image == i) + { + Selected = 2 + Selected; + goto done; + } + } + else if(m_Map.m_lGroups[x]->m_lLayers[j]->m_Type == LAYERTYPE_TILES) + { + CLayerTiles *pLayer = static_cast(m_Map.m_lGroups[x]->m_lLayers[j]); + if(pLayer->m_Image == i) + { + Selected = 2 + Selected; + goto done; + } + } + + done: + if(int Result = DoButton_Editor(&m_Map.m_lImages[i], aBuf, Selected, &Slot, BUTTON_CONTEXT, "Select image")) { m_SelectedImage = i; @@ -2763,8 +2879,8 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) Graphics()->LinesEnd(); } - if(ImageCur + 27.0f > ImageStopAt) - return; + //if(ImageCur + 27.0f > ImageStopAt) + // return; CUIRect Slot; ToolBox.HSplitTop(5.0f, &Slot, &ToolBox); @@ -2827,6 +2943,7 @@ void CEditor::AddFileDialogEntry(int Index, CUIRect *pView) else m_aFileDialogFileName[0] = 0; m_FilesSelectedIndex = Index; + m_FilePreviewImage = 0; if(Input()->MouseDoubleClick()) m_aFileDialogActivate = true; @@ -2838,6 +2955,7 @@ void CEditor::RenderFileDialog() // GUI coordsys Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); CUIRect View = *UI()->Screen(); + CUIRect Preview; float Width = View.w, Height = View.h; RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.25f), 0, 0); @@ -2856,6 +2974,8 @@ void CEditor::RenderFileDialog() View.HSplitBottom(14.0f, &View, &FileBox); FileBox.VSplitLeft(55.0f, &FileBoxLabel, &FileBox); View.HSplitBottom(10.0f, &View, 0); // some spacing + if (m_FileDialogFileType == CEditor::FILETYPE_IMG) + View.VSplitMid(&View, &Preview); View.VSplitRight(15.0f, &View, &Scroll); // title @@ -2931,8 +3051,43 @@ void CEditor::RenderFileDialog() else m_aFileDialogFileName[0] = 0; m_FilesSelectedIndex = NewIndex; + m_FilePreviewImage = 0; + } + } + + if (m_FileDialogFileType == CEditor::FILETYPE_IMG && m_FilePreviewImage == 0 && m_FilesSelectedIndex > -1) + { + char aBuffer[1024]; + str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_pFileDialogPath, m_FileList[m_FilesSelectedIndex].m_aFilename); + + if(Graphics()->LoadPNG(&m_FilePreviewImageInfo, aBuffer, IStorage::TYPE_ALL)) + { + m_FilePreviewImage = Graphics()->LoadTextureRaw(m_FilePreviewImageInfo.m_Width, m_FilePreviewImageInfo.m_Height, m_FilePreviewImageInfo.m_Format, m_FilePreviewImageInfo.m_pData, m_FilePreviewImageInfo.m_Format, IGraphics::TEXLOAD_NORESAMPLE); + mem_free(m_FilePreviewImageInfo.m_pData); } } + if (m_FilePreviewImage) + { + int w = m_FilePreviewImageInfo.m_Width; + int h = m_FilePreviewImageInfo.m_Height; + if (m_FilePreviewImageInfo.m_Width > Preview.w) + { + h = m_FilePreviewImageInfo.m_Height * Preview.w / m_FilePreviewImageInfo.m_Width; + w = Preview.w; + } + if (h > Preview.h) + { + w = w * Preview.h / h, + h = Preview.h; + } + + Graphics()->TextureSet(m_FilePreviewImage); + Graphics()->BlendNormal(); + Graphics()->QuadsBegin(); + IGraphics::CQuadItem QuadItem(Preview.x, Preview.y, w, h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + } } for(int i = 0; i < Input()->NumEvents(); i++) @@ -3077,6 +3232,7 @@ void CEditor::FilelistPopulate(int StorageType) } Storage()->ListDirectory(StorageType, m_pFileDialogPath, EditorListdirCallback, this); m_FilesSelectedIndex = m_FileList.size() ? 0 : -1; + m_FilePreviewImage = 0; m_aFileDialogActivate = false; } @@ -3095,6 +3251,7 @@ void CEditor::InvokeFileDialog(int StorageType, int FileType, const char *pTitle m_pFileDialogPath = m_aFileDialogCurrentFolder; m_FileDialogFileType = FileType; m_FileDialogScrollValue = 0.0f; + m_FilePreviewImage = 0; if(pDefaultName) str_copy(m_aFileDialogFileName, pDefaultName, sizeof(m_aFileDialogFileName)); @@ -3138,6 +3295,15 @@ void CEditor::RenderStatusbar(CUIRect View) if(DoButton_Editor(&s_EnvelopeButton, "Envelopes", m_ShowEnvelopeEditor, &Button, 0, "Toggles the envelope editor.")) m_ShowEnvelopeEditor = (m_ShowEnvelopeEditor+1)%4; + if (g_Config.m_ClEditorUndo) + { + View.VSplitRight(5.0f, &View, &Button); + View.VSplitRight(60.0f, &View, &Button); + static int s_UndolistButton = 0; + if(DoButton_Editor(&s_UndolistButton, "Undolist", m_ShowUndo, &Button, 0, "Toggles the undo list.")) + m_ShowUndo = (m_ShowUndo + 1) % 2; + } + if(m_pTooltip) { if(ms_pUiGotContext && ms_pUiGotContext == UI()->HotItem()) @@ -3151,6 +3317,66 @@ void CEditor::RenderStatusbar(CUIRect View) } } +void CEditor::RenderUndoList(CUIRect View) +{ + CUIRect List, Preview, Scroll, Button; + View.VSplitMid(&List, &Preview); + List.VSplitRight(15.0f, &List, &Scroll); + //int Num = (int)(List.h/17.0f)+1; + static int ScrollBar = 0; + Scroll.HMargin(5.0f, &Scroll); + m_UndoScrollValue = UiDoScrollbarV(&ScrollBar, &Scroll, m_UndoScrollValue); + + float TopY = List.y; + float Height = List.h; + UI()->ClipEnable(&List); + int ClickedIndex = -1; + int HoveredIndex = -1; + int ScrollNum = m_lUndoSteps.size() - List.h / 17.0f; + if (ScrollNum < 0) + ScrollNum = 0; + List.y -= m_UndoScrollValue*ScrollNum*17.0f; + for (int i = 0; i < m_lUndoSteps.size(); i++) + { + List.HSplitTop(17.0f, &Button, &List); + if (List.y < TopY) + continue; + if (List.y - 17.0f > TopY + Height) + break; + if(DoButton_Editor(&m_lUndoSteps[i].m_ButtonId, m_lUndoSteps[i].m_aName, 0, &Button, 0, "Undo to this step")) + ClickedIndex = i; + if (UI()->HotItem() == &m_lUndoSteps[i].m_ButtonId) + HoveredIndex = i; + } + UI()->ClipDisable(); + if (ClickedIndex != -1) + { + char aBuffer[1024]; + str_format(aBuffer, sizeof(aBuffer), "editor/undo_%i", m_lUndoSteps[HoveredIndex].m_FileNum); + m_Map.Load(m_pStorage, aBuffer, IStorage::TYPE_SAVE); + m_Map.m_UndoModified = 0; + m_LastUndoUpdateTime = time_get(); + } + if (HoveredIndex != -1) + { + if (m_lUndoSteps[HoveredIndex].m_PreviewImage == 0) + { + char aBuffer[1024]; + str_format(aBuffer, sizeof(aBuffer), "editor/undo_%i.png", m_lUndoSteps[HoveredIndex].m_FileNum); + m_lUndoSteps[HoveredIndex].m_PreviewImage = Graphics()->LoadTexture(aBuffer, IStorage::TYPE_SAVE, CImageInfo::FORMAT_RGB, IGraphics::TEXLOAD_NORESAMPLE); + } + if (m_lUndoSteps[HoveredIndex].m_PreviewImage) + { + Graphics()->TextureSet(m_lUndoSteps[HoveredIndex].m_PreviewImage); + Graphics()->BlendNormal(); + Graphics()->QuadsBegin(); + IGraphics::CQuadItem QuadItem(Preview.x, Preview.y, Preview.w, Preview.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + } + } +} + void CEditor::RenderEnvelopeEditor(CUIRect View) { if(m_SelectedEnvelope < 0) m_SelectedEnvelope = 0; @@ -3171,24 +3397,12 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) CUIRect Button; CEnvelope *pNewEnv = 0; - // Delete button - if(m_Map.m_lEnvelopes.size()) - { - ToolBar.VSplitRight(5.0f, &ToolBar, &Button); - ToolBar.VSplitRight(50.0f, &ToolBar, &Button); - static int s_DelButton = 0; - if(DoButton_Editor(&s_DelButton, Localize("Delete"), 0, &Button, 0, Localize("Delete this envelope"))) - m_Map.DeleteEnvelope(m_SelectedEnvelope); - - // little space - ToolBar.VSplitRight(10.0f, &ToolBar, &Button); - } - ToolBar.VSplitRight(50.0f, &ToolBar, &Button); static int s_New4dButton = 0; if(DoButton_Editor(&s_New4dButton, "Color+", 0, &Button, 0, "Creates a new color envelope")) { m_Map.m_Modified = true; + m_Map.m_UndoModified++; pNewEnv = m_Map.NewEnvelope(4); } @@ -3198,6 +3412,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) if(DoButton_Editor(&s_New2dButton, "Pos.+", 0, &Button, 0, "Creates a new pos envelope")) { m_Map.m_Modified = true; + m_Map.m_UndoModified++; pNewEnv = m_Map.NewEnvelope(3); } @@ -3210,6 +3425,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) if(DoButton_Editor(&s_DelButton, "Delete", 0, &Button, 0, "Delete this envelope")) { m_Map.m_Modified = true; + m_Map.m_UndoModified++; m_Map.DeleteEnvelope(m_SelectedEnvelope); if(m_SelectedEnvelope >= m_Map.m_lEnvelopes.size()) m_SelectedEnvelope = m_Map.m_lEnvelopes.size()-1; @@ -3258,7 +3474,10 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) static float s_NameBox = 0; if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f, &s_NameBox)) + { m_Map.m_Modified = true; + m_Map.m_UndoModified++; + } } } @@ -3358,6 +3577,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) f2fx(aChannels[0]), f2fx(aChannels[1]), f2fx(aChannels[2]), f2fx(aChannels[3])); m_Map.m_Modified = true; + m_Map.m_UndoModified++; } m_ShowEnvelopePreview = 1; @@ -3528,6 +3748,9 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) pEnvelope->m_lPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY*ValueScale); } + if (m_SelectedEnvelopePoint != i) + m_Map.m_UndoModified++; + m_SelectedQuadEnvelope = m_SelectedEnvelope; m_ShowEnvelopePreview = 1; m_SelectedEnvelopePoint = i; @@ -3551,6 +3774,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) { pEnvelope->m_lPoints.remove_index(i); m_Map.m_Modified = true; + m_Map.m_UndoModified++; } m_ShowEnvelopePreview = 1; @@ -3721,7 +3945,7 @@ void CEditor::Render() // render checker RenderBackground(View, ms_CheckerTexture, 32.0f, 1.0f); - CUIRect MenuBar, CModeBar, ToolBar, StatusBar, EnvelopeEditor, ToolBox; + CUIRect MenuBar, CModeBar, ToolBar, StatusBar, EnvelopeEditor, UndoList, ToolBox; m_ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE; if(m_GuiActive) @@ -3741,6 +3965,10 @@ void CEditor::Render() size *= 3.0f; View.HSplitBottom(size, &View, &EnvelopeEditor); } + if (m_ShowUndo && !m_ShowPicker) + { + View.HSplitBottom(250.0f, &View, &UndoList); + } } // a little hack for now @@ -3794,6 +4022,11 @@ void CEditor::Render() RenderBackground(EnvelopeEditor, ms_BackgroundTexture, 128.0f, Brightness); EnvelopeEditor.Margin(2.0f, &EnvelopeEditor); } + if(m_ShowUndo) + { + RenderBackground(UndoList, ms_BackgroundTexture, 128.0f, Brightness); + UndoList.Margin(2.0f, &UndoList); + } } @@ -3811,6 +4044,8 @@ void CEditor::Render() RenderModebar(CModeBar); if(m_ShowEnvelopeEditor) RenderEnvelopeEditor(EnvelopeEditor); + if(m_ShowUndo) + RenderUndoList(UndoList); } if(m_Dialog == DIALOG_FILE) @@ -3869,10 +4104,29 @@ void CEditor::Render() } } +static int UndoStepsListdirCallback(const char *pName, int IsDir, int StorageType, void *pUser) +{ + IStorage *pStorage = (IStorage *)pUser; + if (str_comp_nocase_num(pName, "undo_", 5) == 0) + { + char aBuffer[1024]; + pStorage->GetCompletePath(IStorage::TYPE_SAVE, "editor/", aBuffer, sizeof(aBuffer)); + str_append(aBuffer, pName, sizeof(aBuffer)); + fs_remove(aBuffer); + } + return 0; +} + void CEditor::Reset(bool CreateDefault) { m_Map.Clean(); + //delete undo file + char aBuffer[1024]; + m_pStorage->GetCompletePath(IStorage::TYPE_SAVE, "editor/", aBuffer, sizeof(aBuffer)); + fs_listdir(aBuffer, UndoStepsListdirCallback, 0, m_pStorage); + m_lUndoSteps.clear(); + // create default layers if(CreateDefault) m_Map.CreateDefault(ms_EntitiesTexture); @@ -3902,8 +4156,12 @@ void CEditor::Reset(bool CreateDefault) m_MouseDeltaWy = 0; m_Map.m_Modified = false; + m_Map.m_UndoModified = 0; + m_LastUndoUpdateTime = time_get(); + m_UndoRunning = false; m_ShowEnvelopePreview = 0; + m_ShiftBy = 1; } int CEditor::GetLineDistance() @@ -3930,6 +4188,7 @@ void CEditorMap::DeleteEnvelope(int Index) return; m_Modified = true; + m_UndoModified++; // fix links between envelopes and quads for(int i = 0; i < m_lGroups.size(); ++i) @@ -3994,6 +4253,7 @@ void CEditorMap::Clean() m_pSpeedupLayer = 0x0; m_pFrontLayer = 0x0; m_pSwitchLayer = 0x0; + m_pTuneLayer = 0x0; } void CEditorMap::CreateDefault(int EntitiesTexture) @@ -4019,7 +4279,7 @@ void CEditorMap::CreateDefault(int EntitiesTexture) pQuad->m_aColors[2].b = pQuad->m_aColors[3].b = 255; pGroup->AddLayer(pLayer); - // add game layer and reset front, tele, speedup and switch layer pointers + // add game layer and reset front, tele, speedup, tune and switch layer pointers MakeGameGroup(NewGroup()); MakeGameLayer(new CLayerGame(50, 50)); m_pGameGroup->AddLayer(m_pGameLayer); @@ -4028,6 +4288,7 @@ void CEditorMap::CreateDefault(int EntitiesTexture) m_pTeleLayer = 0x0; m_pSpeedupLayer = 0x0; m_pSwitchLayer = 0x0; + m_pTuneLayer = 0x0; } void CEditor::Init() @@ -4052,6 +4313,7 @@ void CEditor::Init() ms_TeleTexture = Graphics()->LoadTexture("editor/tele.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0); ms_SpeedupTexture = Graphics()->LoadTexture("editor/speedup.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0); ms_SwitchTexture = Graphics()->LoadTexture("editor/switch.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0); + ms_TuneTexture = Graphics()->LoadTexture("editor/tune.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0); m_TilesetPicker.m_pEditor = this; m_TilesetPicker.MakePalette(); @@ -4061,6 +4323,8 @@ void CEditor::Init() Reset(); m_Map.m_Modified = false; + m_Map.m_UndoModified = 0; + m_LastUndoUpdateTime = time_get(); } void CEditor::DoMapBorder() @@ -4080,6 +4344,37 @@ void CEditor::DoMapBorder() pT->m_pTiles[i].m_Index = 1; } +void CEditor::CreateUndoStep() +{ + void *CreateThread = thread_create(CreateUndoStepThread, this); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)CreateThread); +#endif +} + +void CEditor::CreateUndoStepThread(void *pUser) +{ + CEditor *pEditor = (CEditor *)pUser; + + CUndo NewStep; + str_timestamp(NewStep.m_aName, sizeof(NewStep.m_aName)); + if (pEditor->m_lUndoSteps.size()) + NewStep.m_FileNum = pEditor->m_lUndoSteps[pEditor->m_lUndoSteps.size() - 1].m_FileNum + 1; + else + NewStep.m_FileNum = 0; + NewStep.m_PreviewImage = 0; + + char aBuffer[1024]; + str_format(aBuffer, sizeof(aBuffer), "editor/undo_%i.png", NewStep.m_FileNum); + pEditor->Graphics()->TakeCustomScreenshot(aBuffer); + + str_format(aBuffer, sizeof(aBuffer), "editor/undo_%i", NewStep.m_FileNum); + pEditor->Save(aBuffer); + + pEditor->m_lUndoSteps.add(NewStep); + pEditor->m_UndoRunning = false; +} + void CEditor::UpdateAndRender() { static float s_MouseX = 0.0f; @@ -4146,6 +4441,22 @@ void CEditor::UpdateAndRender() if(Input()->KeyDown(KEY_F10)) m_ShowMousePointer = false; + if (g_Config.m_ClEditorUndo) + { + // Screenshot at most every 5 seconds, at least every 60 + if ((m_LastUndoUpdateTime + time_freq() * 60 < time_get() && m_Map.m_UndoModified) + || (m_LastUndoUpdateTime + time_freq() * 5 < time_get() && m_Map.m_UndoModified >= 10)) + { + m_Map.m_UndoModified = 0; + m_LastUndoUpdateTime = time_get(); + + if (!m_UndoRunning) + { + m_UndoRunning = true; + CreateUndoStep(); + } + } + } Render(); if(Input()->KeyDown(KEY_F10)) @@ -4188,3 +4499,10 @@ void CEditorMap::MakeSwitchLayer(CLayer *pLayer) m_pSwitchLayer->m_pEditor = m_pEditor; m_pSwitchLayer->m_TexID = m_pEditor->ms_SwitchTexture; } + +void CEditorMap::MakeTuneLayer(CLayer *pLayer) +{ + m_pTuneLayer = (CLayerTune *)pLayer; + m_pTuneLayer->m_pEditor = m_pEditor; + m_pTuneLayer->m_TexID = m_pEditor->ms_TuneTexture; +} diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 879d1b798b..a8c0a557d5 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -207,8 +207,43 @@ class CLayerGroup bool IsEmpty() const { - return m_lLayers.size() == 0; + return m_lLayers.size() == 0; // stupid function, since its bad for Fillselection: TODO add a function for Fillselection that returns whether a specific tile is used in the given layer } + + /*bool IsUsedInThisLayer(int Layer, int Index) // <--------- this is what i meant but cause i dont know which Indexes belongs to which layers i cant finish yet + { + switch Layer + { + case LAYERTYPE_GAME: // security + return true; + case LAYERTYPE_FRONT: + return true; + case LAYERTYPE_TELE: + { + if (Index ==) // you could add an 2D array into mapitems.h which defines which Indexes belong to which layer(s) + } + case LAYERTYPE_SPEEDUP: + { + if (Index == TILE_BOOST) + return true; + else + return false; + } + case LAYERTYPE_SWITCH: + { + + } + case LAYERTYPE_TUNE: + { + if (Index == TILE_TUNE1) + return true; + else + return false; + } + default: + return false; + } + }*/ void Clear() { @@ -266,6 +301,7 @@ class CEditorMap public: CEditor *m_pEditor; bool m_Modified; + int m_UndoModified; CEditorMap() { @@ -310,6 +346,7 @@ class CEditorMap CEnvelope *NewEnvelope(int Channels) { m_Modified = true; + m_UndoModified++; CEnvelope *e = new CEnvelope(Channels); m_lEnvelopes.add(e); return e; @@ -320,6 +357,7 @@ class CEditorMap CLayerGroup *NewGroup() { m_Modified = true; + m_UndoModified++; CLayerGroup *g = new CLayerGroup; g->m_pMap = this; m_lGroups.add(g); @@ -332,6 +370,7 @@ class CEditorMap if(Index1 < 0 || Index1 >= m_lGroups.size()) return Index0; if(Index0 == Index1) return Index0; m_Modified = true; + m_UndoModified++; swap(m_lGroups[Index0], m_lGroups[Index1]); return Index1; } @@ -340,6 +379,7 @@ class CEditorMap { if(Index < 0 || Index >= m_lGroups.size()) return; m_Modified = true; + m_UndoModified++; delete m_lGroups[Index]; m_lGroups.remove_index(Index); } @@ -347,6 +387,7 @@ class CEditorMap void ModifyImageIndex(INDEX_MODIFY_FUNC pfnFunc) { m_Modified = true; + m_UndoModified++; for(int i = 0; i < m_lGroups.size(); i++) m_lGroups[i]->ModifyImageIndex(pfnFunc); } @@ -354,6 +395,7 @@ class CEditorMap void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc) { m_Modified = true; + m_UndoModified++; for(int i = 0; i < m_lGroups.size(); i++) m_lGroups[i]->ModifyEnvelopeIndex(pfnFunc); } @@ -371,10 +413,12 @@ class CEditorMap class CLayerSpeedup *m_pSpeedupLayer; class CLayerFront *m_pFrontLayer; class CLayerSwitch *m_pSwitchLayer; + class CLayerTune *m_pTuneLayer; void MakeTeleLayer(CLayer *pLayer); void MakeSpeedupLayer(CLayer *pLayer); void MakeFrontLayer(CLayer *pLayer); void MakeSwitchLayer(CLayer *pLayer); + void MakeTuneLayer(CLayer *pLayer); }; @@ -457,6 +501,7 @@ class CLayerTiles : public CLayer int m_Speedup; int m_Front; int m_Switch; + int m_Tune; char m_aFileName[512]; }; @@ -578,6 +623,8 @@ class CEditor : public IEditor m_AnimateSpeed = 1; m_ShowEnvelopeEditor = 0; + m_ShowUndo = 0; + m_UndoScrollValue = 0.0f; m_ShowEnvelopePreview = 0; m_SelectedQuadEnvelope = -1; @@ -596,8 +643,10 @@ class CEditor : public IEditor ms_TeleTexture = 0; ms_SpeedupTexture = 0; ms_SwitchTexture = 0; + ms_TuneTexture = 0; m_TeleNumber = 1; m_SwitchNum = 1; + m_TuningNum = 1; m_SwitchDelay = 0; m_SpeedupForce = 50; m_SpeedupMaxSpeed = 0; @@ -608,6 +657,22 @@ class CEditor : public IEditor virtual void UpdateAndRender(); virtual bool HasUnsavedData() { return m_Map.m_Modified; } + int64 m_LastUndoUpdateTime; + bool m_UndoRunning; + void CreateUndoStep(); + static void CreateUndoStepThread(void *pUser); + int UndoStep(); + struct CUndo + { + int m_FileNum; + int m_ButtonId; + char m_aName[128]; + int m_PreviewImage; + }; + array m_lUndoSteps; + bool m_Undo; + int m_ShowUndo; + float m_UndoScrollValue; void FilelistPopulate(int StorageType); void InvokeFileDialog(int StorageType, int FileType, const char *pTitle, const char *pButtonText, const char *pBasepath, const char *pDefaultName, @@ -624,7 +689,7 @@ class CEditor : public IEditor CLayer *GetSelectedLayer(int Index); CLayerGroup *GetSelectedGroup(); - int DoProperties(CUIRect *pToolbox, CProperty *pProps, int *pIDs, int *pNewVal); + int DoProperties(CUIRect *pToolbox, CProperty *pProps, int *pIDs, int *pNewVal, vec4 color = vec4(1,1,1,0.5f)); int m_Mode; int m_Dialog; @@ -673,6 +738,9 @@ class CEditor : public IEditor int m_FilesSelectedIndex; char m_FileDialogNewFolderName[64]; char m_FileDialogErrString[64]; + int m_FilePreviewImage; + CImageInfo m_FilePreviewImageInfo; + struct CFilelistItem { @@ -737,6 +805,7 @@ class CEditor : public IEditor static const void *ms_pUiGotContext; CEditorMap m_Map; + int m_ShiftBy; static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser); @@ -793,8 +862,8 @@ class CEditor : public IEditor vec4 ButtonColorMul(const void *pID); - void DoQuadEnvelopes(CQuad *pQuad, int Index, int TexID = -1); - void DoQuadEnvPoint(CQuad *pQuad, int QIndex, int pIndex); + void DoQuadEnvelopes(const array &m_lQuads, int TexID = -1); + void DoQuadEnvPoint(const CQuad *pQuad, int QIndex, int pIndex); void DoQuadPoint(CQuad *pQuad, int QuadIndex, int v); void DoMapEditor(CUIRect View, CUIRect Toolbar); @@ -811,6 +880,7 @@ class CEditor : public IEditor void RenderModebar(CUIRect View); void RenderStatusbar(CUIRect View); void RenderEnvelopeEditor(CUIRect View); + void RenderUndoList(CUIRect View); void RenderMenubar(CUIRect Menubar); void RenderFileDialog(); @@ -841,10 +911,14 @@ class CEditor : public IEditor static int ms_TeleTexture; static int ms_SpeedupTexture; static int ms_SwitchTexture; + static int ms_TuneTexture; static int PopupTele(CEditor *pEditor, CUIRect View); static int PopupSpeedup(CEditor *pEditor, CUIRect View); static int PopupSwitch(CEditor *pEditor, CUIRect View); + static int PopupTune(CEditor *pEditor, CUIRect View); unsigned char m_TeleNumber; + + unsigned char m_TuningNum; unsigned char m_SpeedupForce; unsigned char m_SpeedupMaxSpeed; @@ -924,4 +998,23 @@ class CLayerSwitch : public CLayerTiles virtual void FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect); }; +class CLayerTune : public CLayerTiles +{ +public: + CLayerTune(int w, int h); + ~CLayerTune(); + + CTuneTile *m_pTuneTile; + unsigned char m_TuningNumber; + + virtual void Resize(int NewW, int NewH); + virtual void Shift(int Direction); + virtual void BrushDraw(CLayer *pBrush, float wx, float wy); + virtual void BrushFlipX(); + virtual void BrushFlipY(); + virtual void BrushRotate(float Amount); + virtual void FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect); +}; + + #endif diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index 0daf8ab27a..390c7461de 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -330,6 +330,8 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) Item.m_Flags = TILESLAYERFLAG_FRONT; else if(pLayer->m_Switch) Item.m_Flags = TILESLAYERFLAG_SWITCH; + else if(pLayer->m_Tune) + Item.m_Flags = TILESLAYERFLAG_TUNE; else Item.m_Flags = pLayer->m_Game ? TILESLAYERFLAG_GAME : 0; @@ -366,6 +368,14 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) Item.m_Switch = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CSwitchTile), ((CLayerSwitch *)pLayer)->m_pSwitchTile); delete[] Tiles; } + else if(pLayer->m_Tune) + { + CTile *Tiles = new CTile[pLayer->m_Width*pLayer->m_Height]; + mem_zero(Tiles, pLayer->m_Width*pLayer->m_Height*sizeof(CTile)); + Item.m_Data = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTile), Tiles); + Item.m_Tune = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTuneTile), ((CLayerTune *)pLayer)->m_pTuneTile); + delete[] Tiles; + } else Item.m_Data = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTile), pLayer->m_pTiles); @@ -439,6 +449,7 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) } df.AddItem(MAPITEMTYPE_ENVPOINTS, 0, TotalSize, pPoints); + mem_free(pPoints); // finish the data file df.Finish(); @@ -449,10 +460,18 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) { CServerInfo CurrentServerInfo; m_pEditor->Client()->GetServerInfo(&CurrentServerInfo); - char aMapName[128]; - m_pEditor->ExtractName(pFileName, aMapName, sizeof(aMapName)); - if(!str_comp(aMapName, CurrentServerInfo.m_aMap)) - m_pEditor->Client()->Rcon("reload"); + const unsigned char ipv4Localhost[16] = {127,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0}; + const unsigned char ipv6Localhost[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; + + // and if we're on localhost + if(!mem_comp(CurrentServerInfo.m_NetAddr.ip, ipv4Localhost, sizeof(ipv4Localhost)) + || !mem_comp(CurrentServerInfo.m_NetAddr.ip, ipv6Localhost, sizeof(ipv6Localhost))) + { + char aMapName[128]; + m_pEditor->ExtractName(pFileName, aMapName, sizeof(aMapName)); + if(!str_comp(aMapName, CurrentServerInfo.m_aMap)) + m_pEditor->Client()->Rcon("reload"); + } } return 1; @@ -482,6 +501,7 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag editor->reset(); editor_load_old(df, this); */ + return 0; } else if(pItem->m_Version == 1) { @@ -527,6 +547,7 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag { *pImg = ImgInfo; pImg->m_TexID = m_pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0); + ImgInfo.m_pData = 0; pImg->m_External = 1; } } @@ -641,6 +662,14 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag pTiles = new CLayerSwitch(pTilemapItem->m_Width, pTilemapItem->m_Height); MakeSwitchLayer(pTiles); } + else if(pTilemapItem->m_Flags&TILESLAYERFLAG_TUNE) + { + if(pTilemapItem->m_Version <= 2) + pTilemapItem->m_Tune = *((int*)(pTilemapItem) + 19); + + pTiles = new CLayerTune(pTilemapItem->m_Width, pTilemapItem->m_Height); + MakeTuneLayer(pTiles); + } else { pTiles = new CLayerTiles(pTilemapItem->m_Width, pTilemapItem->m_Height); @@ -654,6 +683,7 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag pGroup->AddLayer(pTiles); void *pData = DataFile.GetData(pTilemapItem->m_Data); + unsigned int Size = DataFile.GetUncompressedDataSize(pTilemapItem->m_Data); pTiles->m_Image = pTilemapItem->m_Image; pTiles->m_Game = pTilemapItem->m_Flags&TILESLAYERFLAG_GAME; @@ -661,14 +691,17 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag if(pTilemapItem->m_Version >= 3) IntsToStr(pTilemapItem->m_aName, sizeof(pTiles->m_aName)/sizeof(int), pTiles->m_aName); - mem_copy(pTiles->m_pTiles, pData, pTiles->m_Width*pTiles->m_Height*sizeof(CTile)); - - if(pTiles->m_Game && pTilemapItem->m_Version == MakeVersion(1, *pTilemapItem)) + if (Size >= pTiles->m_Width*pTiles->m_Height*sizeof(CTile)) { - for(int i = 0; i < pTiles->m_Width*pTiles->m_Height; i++) + mem_copy(pTiles->m_pTiles, pData, pTiles->m_Width*pTiles->m_Height*sizeof(CTile)); + + if(pTiles->m_Game && pTilemapItem->m_Version == MakeVersion(1, *pTilemapItem)) { - if(pTiles->m_pTiles[i].m_Index) - pTiles->m_pTiles[i].m_Index += ENTITY_OFFSET; + for(int i = 0; i < pTiles->m_Width*pTiles->m_Height; i++) + { + if(pTiles->m_pTiles[i].m_Index) + pTiles->m_pTiles[i].m_Index += ENTITY_OFFSET; + } } } @@ -677,38 +710,53 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag if(pTiles->m_Tele) { void *pTeleData = DataFile.GetData(pTilemapItem->m_Tele); - mem_copy(((CLayerTele*)pTiles)->m_pTeleTile, pTeleData, pTiles->m_Width*pTiles->m_Height*sizeof(CTeleTile)); - - for(int i = 0; i < pTiles->m_Width*pTiles->m_Height; i++) + unsigned int Size = DataFile.GetUncompressedDataSize(pTilemapItem->m_Tele); + if (Size >= pTiles->m_Width*pTiles->m_Height*sizeof(CTeleTile)) { - if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELEIN) - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELEIN; - else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELEINEVIL) - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELEINEVIL; - else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELEOUT) - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELEOUT; - else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELECHECK) - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELECHECK; - else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELECHECKIN) - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELECHECKIN; - else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELECHECKOUT) - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELECHECKOUT; - else - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = 0; + mem_copy(((CLayerTele*)pTiles)->m_pTeleTile, pTeleData, pTiles->m_Width*pTiles->m_Height*sizeof(CTeleTile)); + + for(int i = 0; i < pTiles->m_Width*pTiles->m_Height; i++) + { + if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELEIN) + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELEIN; + else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELEINEVIL) + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELEINEVIL; + else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELEOUT) + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELEOUT; + else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELECHECK) + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELECHECK; + else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELECHECKIN) + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELECHECKIN; + else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELECHECKINEVIL) + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELECHECKINEVIL; + else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELECHECKOUT) + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELECHECKOUT; + else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELEINWEAPON) + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELEINWEAPON; + else if(((CLayerTele*)pTiles)->m_pTeleTile[i].m_Type == TILE_TELEINHOOK) + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TELEINHOOK; + else + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = 0; + } } DataFile.UnloadData(pTilemapItem->m_Tele); } else if(pTiles->m_Speedup) { void *pSpeedupData = DataFile.GetData(pTilemapItem->m_Speedup); - mem_copy(((CLayerSpeedup*)pTiles)->m_pSpeedupTile, pSpeedupData, pTiles->m_Width*pTiles->m_Height*sizeof(CSpeedupTile)); + unsigned int Size = DataFile.GetUncompressedDataSize(pTilemapItem->m_Speedup); - for(int i = 0; i < pTiles->m_Width*pTiles->m_Height; i++) + if (Size >= pTiles->m_Width*pTiles->m_Height*sizeof(CSpeedupTile)) { - if(((CLayerSpeedup*)pTiles)->m_pSpeedupTile[i].m_Force > 0) - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_BOOST; - else - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = 0; + mem_copy(((CLayerSpeedup*)pTiles)->m_pSpeedupTile, pSpeedupData, pTiles->m_Width*pTiles->m_Height*sizeof(CSpeedupTile)); + + for(int i = 0; i < pTiles->m_Width*pTiles->m_Height; i++) + { + if(((CLayerSpeedup*)pTiles)->m_pSpeedupTile[i].m_Force > 0) + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_BOOST; + else + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = 0; + } } DataFile.UnloadData(pTilemapItem->m_Speedup); @@ -716,71 +764,105 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag else if(pTiles->m_Front) { void *pFrontData = DataFile.GetData(pTilemapItem->m_Front); - mem_copy(((CLayerFront*)pTiles)->m_pTiles, pFrontData, pTiles->m_Width*pTiles->m_Height*sizeof(CTile)); + unsigned int Size = DataFile.GetUncompressedDataSize(pTilemapItem->m_Front); + if (Size >= pTiles->m_Width*pTiles->m_Height*sizeof(CTile)) + mem_copy(((CLayerFront*)pTiles)->m_pTiles, pFrontData, pTiles->m_Width*pTiles->m_Height*sizeof(CTile)); DataFile.UnloadData(pTilemapItem->m_Front); } else if(pTiles->m_Switch) { void *pSwitchData = DataFile.GetData(pTilemapItem->m_Switch); - mem_copy(((CLayerSwitch*)pTiles)->m_pSwitchTile, pSwitchData, pTiles->m_Width*pTiles->m_Height*sizeof(CSwitchTile)); - - for(int i = 0; i < pTiles->m_Width*pTiles->m_Height; i++) + unsigned int Size = DataFile.GetUncompressedDataSize(pTilemapItem->m_Switch); + if (Size >= pTiles->m_Width*pTiles->m_Height*sizeof(CSwitchTile)) { - if(((((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type > (ENTITY_CRAZY_SHOTGUN + ENTITY_OFFSET) && ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type < (ENTITY_DRAGGER_WEAK + ENTITY_OFFSET)) || ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == (ENTITY_LASER_O_FAST + 1 + ENTITY_OFFSET))) - continue; - if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_SWITCHTIMEDOPEN) - { - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_SWITCHTIMEDOPEN; - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; - } - else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_SWITCHTIMEDCLOSE) - { - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_SWITCHTIMEDCLOSE; - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; - } - else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_SWITCHOPEN) - { - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_SWITCHOPEN; - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; - } - else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_SWITCHCLOSE) - { - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_SWITCHCLOSE; - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; - } - else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_FREEZE) - { - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_FREEZE; - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; - } - else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_DFREEZE) - { - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_DFREEZE; - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; - } - else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_DUNFREEZE) - { - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_DUNFREEZE; - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; - } - else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type >= (ENTITY_ARMOR_1 + ENTITY_OFFSET) && ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type <= (ENTITY_DOOR + ENTITY_OFFSET)) - { - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type; - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; - } - else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_HIT_START) + mem_copy(((CLayerSwitch*)pTiles)->m_pSwitchTile, pSwitchData, pTiles->m_Width*pTiles->m_Height*sizeof(CSwitchTile)); + + for(int i = 0; i < pTiles->m_Width*pTiles->m_Height; i++) { - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_HIT_START; - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + if(((((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type > (ENTITY_CRAZY_SHOTGUN + ENTITY_OFFSET) && ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type < (ENTITY_DRAGGER_WEAK + ENTITY_OFFSET)) || ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == (ENTITY_LASER_O_FAST + 1 + ENTITY_OFFSET))) + continue; + if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_SWITCHTIMEDOPEN) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_SWITCHTIMEDOPEN; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } + else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_SWITCHTIMEDCLOSE) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_SWITCHTIMEDCLOSE; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } + else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_SWITCHOPEN) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_SWITCHOPEN; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } + else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_SWITCHCLOSE) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_SWITCHCLOSE; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } + else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_FREEZE) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_FREEZE; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } + else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_DFREEZE) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_DFREEZE; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } + else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_DUNFREEZE) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_DUNFREEZE; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } + else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type >= (ENTITY_ARMOR_1 + ENTITY_OFFSET) && ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type <= (ENTITY_DOOR + ENTITY_OFFSET)) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } + else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_HIT_START) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_HIT_START; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } + else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_HIT_END) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_HIT_END; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } + else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_JUMP) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_JUMP; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } + else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_PENALTY) + { + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_PENALTY; + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + } } - else if(((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Type == TILE_HIT_END) + } + DataFile.UnloadData(pTilemapItem->m_Switch); + } + else if(pTiles->m_Tune) + { + void *pTuneData = DataFile.GetData(pTilemapItem->m_Tune); + unsigned int Size = DataFile.GetUncompressedDataSize(pTilemapItem->m_Tune); + if (Size >= pTiles->m_Width*pTiles->m_Height*sizeof(CTuneTile)) + { + mem_copy(((CLayerTune*)pTiles)->m_pTuneTile, pTuneData, pTiles->m_Width*pTiles->m_Height*sizeof(CTuneTile)); + + for(int i = 0; i < pTiles->m_Width*pTiles->m_Height; i++) { - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_HIT_END; - ((CLayerTiles*)pTiles)->m_pTiles[i].m_Flags = ((CLayerSwitch*)pTiles)->m_pSwitchTile[i].m_Flags; + if(((CLayerTune*)pTiles)->m_pTuneTile[i].m_Type == TILE_TUNE1) + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = TILE_TUNE1; + else + ((CLayerTiles*)pTiles)->m_pTiles[i].m_Index = 0; } } - DataFile.UnloadData(pTilemapItem->m_Switch); + DataFile.UnloadData(pTilemapItem->m_Tune); } } else if(pLayerItem->m_Type == LAYERTYPE_QUADS) @@ -837,6 +919,8 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag } } } + else + return 0; return 1; } diff --git a/src/game/editor/layer_quads.cpp b/src/game/editor/layer_quads.cpp index d0b66405ce..15c0035b7f 100644 --- a/src/game/editor/layer_quads.cpp +++ b/src/game/editor/layer_quads.cpp @@ -27,7 +27,10 @@ void CLayerQuads::Render() if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size()) Graphics()->TextureSet(m_pEditor->m_Map.m_lImages[m_Image]->m_TexID); - m_pEditor->RenderTools()->RenderQuads(m_lQuads.base_ptr(), m_lQuads.size(), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, m_pEditor->EnvelopeEval, m_pEditor); + Graphics()->BlendNone(); + m_pEditor->RenderTools()->ForceRenderQuads(m_lQuads.base_ptr(), m_lQuads.size(), LAYERRENDERFLAG_OPAQUE, m_pEditor->EnvelopeEval, m_pEditor); + Graphics()->BlendNormal(); + m_pEditor->RenderTools()->ForceRenderQuads(m_lQuads.base_ptr(), m_lQuads.size(), LAYERRENDERFLAG_TRANSPARENT, m_pEditor->EnvelopeEval, m_pEditor); } CQuad *CLayerQuads::NewQuad() @@ -145,12 +148,41 @@ void CLayerQuads::BrushPlace(CLayer *pBrush, float wx, float wy) m_pEditor->m_Map.m_Modified = true; } +void Swap(CPoint& a, CPoint& b) +{ + CPoint tmp; + tmp.x = a.x; + tmp.y = a.y; + + a.x = b.x; + a.y = b.y; + + b.x = tmp.x; + b.y = tmp.y; +} + void CLayerQuads::BrushFlipX() { + for(int i = 0; i < m_lQuads.size(); i++) + { + CQuad *q = &m_lQuads[i]; + + Swap(q->m_aPoints[0], q->m_aPoints[1]); + Swap(q->m_aPoints[2], q->m_aPoints[3]); + } + m_pEditor->m_Map.m_Modified = true; } void CLayerQuads::BrushFlipY() { + for(int i = 0; i < m_lQuads.size(); i++) + { + CQuad *q = &m_lQuads[i]; + + Swap(q->m_aPoints[0], q->m_aPoints[2]); + Swap(q->m_aPoints[1], q->m_aPoints[3]); + } + m_pEditor->m_Map.m_Modified = true; } void Rotate(vec2 *pCenter, vec2 *pPoint, float Rotation) diff --git a/src/game/editor/layer_tiles.cpp b/src/game/editor/layer_tiles.cpp index 507ad83cf7..98e3bd8e28 100644 --- a/src/game/editor/layer_tiles.cpp +++ b/src/game/editor/layer_tiles.cpp @@ -35,6 +35,7 @@ CLayerTiles::CLayerTiles(int w, int h) m_Speedup = 0; m_Front = 0; m_Switch = 0; + m_Tune = 0; m_pTiles = new CTile[m_Width*m_Height]; mem_zero(m_pTiles, m_Width*m_Height*sizeof(CTile)); @@ -72,7 +73,11 @@ void CLayerTiles::Render() m_TexID = m_pEditor->m_Map.m_lImages[m_Image]->m_TexID; Graphics()->TextureSet(m_TexID); vec4 Color = vec4(m_Color.r/255.0f, m_Color.g/255.0f, m_Color.b/255.0f, m_Color.a/255.0f); - m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, + Graphics()->BlendNone(); + m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE, + m_pEditor->EnvelopeEval, m_pEditor, m_ColorEnv, m_ColorEnvOffset); + Graphics()->BlendNormal(); + m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_TRANSPARENT, m_pEditor->EnvelopeEval, m_pEditor, m_ColorEnv, m_ColorEnvOffset); // Render DDRace Layers @@ -82,6 +87,8 @@ void CLayerTiles::Render() m_pEditor->RenderTools()->RenderSpeedupOverlay(((CLayerSpeedup*)this)->m_pSpeedupTile, m_Width, m_Height, 32.0f); if(m_Switch) m_pEditor->RenderTools()->RenderSwitchOverlay(((CLayerSwitch*)this)->m_pSwitchTile, m_Width, m_Height, 32.0f); + if(m_Tune) + m_pEditor->RenderTools()->RenderTuneOverlay(((CLayerTune*)this)->m_pTuneTile, m_Width, m_Height, 32.0f); } int CLayerTiles::ConvertX(float x) const { return (int)(x/32.0f); } @@ -176,7 +183,7 @@ int CLayerTiles::BrushGrab(CLayerGroup *pBrush, CUIRect Rect) for(int x = 0; x < r.w; x++) { pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x] = ((CLayerTele*)this)->m_pTeleTile[(r.y+y)*m_Width+(r.x+x)]; - if(pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEIN || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEOUT || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEINEVIL || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELECHECK || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELECHECKOUT || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELECHECKIN) + if(pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEIN || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEOUT || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEINEVIL || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELECHECKINEVIL || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELECHECK || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELECHECKOUT || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELECHECKIN || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEINWEAPON || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEINHOOK) m_pEditor->m_TeleNumber = pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Number; } pGrabbed->m_TeleNum = m_pEditor->m_TeleNumber; @@ -236,7 +243,7 @@ int CLayerTiles::BrushGrab(CLayerGroup *pBrush, CUIRect Rect) for(int x = 0; x < r.w; x++) { pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x] = ((CLayerSwitch*)this)->m_pSwitchTile[(r.y+y)*m_Width+(r.x+x)]; - if(pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_DOOR + ENTITY_OFFSET || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_HIT_START || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_HIT_END || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHOPEN || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHCLOSE || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHTIMEDOPEN || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHTIMEDCLOSE || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_LASER_LONG + ENTITY_OFFSET || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_LASER_MEDIUM + ENTITY_OFFSET || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_LASER_SHORT + ENTITY_OFFSET) + if(pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_DOOR + ENTITY_OFFSET || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_HIT_START || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_HIT_END || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHOPEN || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHCLOSE || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHTIMEDOPEN || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHTIMEDCLOSE || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_LASER_LONG + ENTITY_OFFSET || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_LASER_MEDIUM + ENTITY_OFFSET || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_LASER_SHORT + ENTITY_OFFSET || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_JUMP || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_PENALTY) { m_pEditor->m_SwitchNum = pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Number; m_pEditor->m_SwitchDelay = pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Delay; @@ -246,6 +253,35 @@ int CLayerTiles::BrushGrab(CLayerGroup *pBrush, CUIRect Rect) pGrabbed->m_SwitchDelay = m_pEditor->m_SwitchDelay; str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName, sizeof(pGrabbed->m_aFileName)); } + + else if(m_pEditor->GetSelectedLayer(0) == m_pEditor->m_Map.m_pTuneLayer) + { + CLayerTune *pGrabbed = new CLayerTune(r.w, r.h); + pGrabbed->m_pEditor = m_pEditor; + pGrabbed->m_TexID = m_TexID; + pGrabbed->m_Image = m_Image; + pGrabbed->m_Game = m_Game; + pBrush->AddLayer(pGrabbed); + + // copy the tiles + for(int y = 0; y < r.h; y++) + for(int x = 0; x < r.w; x++) + pGrabbed->m_pTiles[y*pGrabbed->m_Width+x] = m_pTiles[(r.y+y)*m_Width+(r.x+x)]; + + // copy the tiles + if(!m_pEditor->Input()->KeyPressed(KEY_SPACE)) + for(int y = 0; y < r.h; y++) + for(int x = 0; x < r.w; x++) + { + pGrabbed->m_pTuneTile[y*pGrabbed->m_Width+x] = ((CLayerTune*)this)->m_pTuneTile[(r.y+y)*m_Width+(r.x+x)]; + if(pGrabbed->m_pTuneTile[y*pGrabbed->m_Width+x].m_Type == TILE_TUNE1) + { + m_pEditor->m_TuningNum = pGrabbed->m_pTuneTile[y*pGrabbed->m_Width+x].m_Number; + } + } + pGrabbed->m_TuningNumber = m_pEditor->m_TuningNum; + str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName, sizeof(pGrabbed->m_aFileName)); + } else if(m_pEditor->GetSelectedLayer(0) == m_pEditor->m_Map.m_pFrontLayer) { CLayerFront *pGrabbed = new CLayerFront(r.w, r.h); @@ -346,7 +382,7 @@ void CLayerTiles::BrushFlipX() m_pTiles[y*m_Width+m_Width-1-x] = Tmp; } - if(!m_Game && !m_Tele && !m_Speedup) + if(!m_Game && !m_Tele && !m_Speedup && !m_Tune) for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width; x++) m_pTiles[y*m_Width+x].m_Flags ^= m_pTiles[y*m_Width+x].m_Flags&TILEFLAG_ROTATE ? TILEFLAG_HFLIP : TILEFLAG_VFLIP; @@ -362,7 +398,7 @@ void CLayerTiles::BrushFlipY() m_pTiles[(m_Height-1-y)*m_Width+x] = Tmp; } - if(!m_Game && !m_Tele && !m_Speedup) + if(!m_Game && !m_Tele && !m_Speedup && !m_Tune) for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width; x++) m_pTiles[y*m_Width+x].m_Flags ^= m_pTiles[y*m_Width+x].m_Flags&TILEFLAG_ROTATE ? TILEFLAG_VFLIP : TILEFLAG_HFLIP; @@ -370,13 +406,13 @@ void CLayerTiles::BrushFlipY() void CLayerTiles::BrushRotate(float Amount) { - int Rotation = (round(360.0f*Amount/(pi*2))/90)%4; // 0=0, 1=90, 2=180, 3=270 + int Rotation = (round_to_int(360.0f*Amount/(pi*2))/90)%4; // 0=0°, 1=90°, 2=180°, 3=270° if(Rotation < 0) Rotation +=4; if(Rotation == 1 || Rotation == 3) { - // 90 rotation + // 90° rotation CTile *pTempData = new CTile[m_Width*m_Height]; mem_copy(pTempData, m_pTiles, m_Width*m_Height*sizeof(CTile)); CTile *pDst = m_pTiles; @@ -384,7 +420,7 @@ void CLayerTiles::BrushRotate(float Amount) for(int y = m_Height-1; y >= 0; --y, ++pDst) { *pDst = pTempData[y*m_Width+x]; - if(!m_Game && !m_Tele && !m_Speedup) + if(!m_Game && !m_Tele && !m_Speedup && !m_Tune) { if(pDst->m_Flags&TILEFLAG_ROTATE) pDst->m_Flags ^= (TILEFLAG_HFLIP|TILEFLAG_VFLIP); @@ -435,38 +471,56 @@ void CLayerTiles::Resize(int NewW, int NewH) // resize switch layer if available if(m_Game && m_pEditor->m_Map.m_pSwitchLayer && (m_pEditor->m_Map.m_pSwitchLayer->m_Width != NewW || m_pEditor->m_Map.m_pSwitchLayer->m_Height != NewH)) m_pEditor->m_Map.m_pSwitchLayer->Resize(NewW, NewH); + + // resize tune layer if available + if(m_Game && m_pEditor->m_Map.m_pTuneLayer && (m_pEditor->m_Map.m_pTuneLayer->m_Width != NewW || m_pEditor->m_Map.m_pTuneLayer->m_Height != NewH)) + m_pEditor->m_Map.m_pTuneLayer->Resize(NewW, NewH); } void CLayerTiles::Shift(int Direction) { + int o = m_pEditor->m_ShiftBy; + switch(Direction) { case 1: { // left for(int y = 0; y < m_Height; ++y) - mem_move(&m_pTiles[y*m_Width], &m_pTiles[y*m_Width+1], (m_Width-1)*sizeof(CTile)); + { + mem_move(&m_pTiles[y*m_Width], &m_pTiles[y*m_Width+o], (m_Width-o)*sizeof(CTile)); + mem_zero(&m_pTiles[y*m_Width + (m_Width-o)], o*sizeof(CTile)); + } } break; case 2: { // right for(int y = 0; y < m_Height; ++y) - mem_move(&m_pTiles[y*m_Width+1], &m_pTiles[y*m_Width], (m_Width-1)*sizeof(CTile)); + { + mem_move(&m_pTiles[y*m_Width+o], &m_pTiles[y*m_Width], (m_Width-o)*sizeof(CTile)); + mem_zero(&m_pTiles[y*m_Width], o*sizeof(CTile)); + } } break; case 4: { // up - for(int y = 0; y < m_Height-1; ++y) - mem_copy(&m_pTiles[y*m_Width], &m_pTiles[(y+1)*m_Width], m_Width*sizeof(CTile)); + for(int y = 0; y < m_Height-o; ++y) + { + mem_copy(&m_pTiles[y*m_Width], &m_pTiles[(y+o)*m_Width], m_Width*sizeof(CTile)); + mem_zero(&m_pTiles[(y+o)*m_Width], m_Width*sizeof(CTile)); + } } break; case 8: { // down - for(int y = m_Height-1; y > 0; --y) - mem_copy(&m_pTiles[y*m_Width], &m_pTiles[(y-1)*m_Width], m_Width*sizeof(CTile)); + for(int y = m_Height-1; y >= o; --y) + { + mem_copy(&m_pTiles[y*m_Width], &m_pTiles[(y-o)*m_Width], m_Width*sizeof(CTile)); + mem_zero(&m_pTiles[(y-o)*m_Width], m_Width*sizeof(CTile)); + } } } } @@ -476,6 +530,7 @@ void CLayerTiles::ShowInfo() float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); Graphics()->TextureSet(m_pEditor->Client()->GetDebugFont()); + Graphics()->QuadsBegin(); int StartY = max(0, (int)(ScreenY0/32.0f)-1); int StartX = max(0, (int)(ScreenX0/32.0f)-1); @@ -490,17 +545,18 @@ void CLayerTiles::ShowInfo() { char aBuf[64]; str_format(aBuf, sizeof(aBuf), "%i", m_pTiles[c].m_Index); - m_pEditor->Graphics()->QuadsText(x*32, y*32, 16.0f, 1,1,1,1, aBuf); + m_pEditor->Graphics()->QuadsText(x*32, y*32, 16.0f, aBuf); char aFlags[4] = { m_pTiles[c].m_Flags&TILEFLAG_VFLIP ? 'V' : ' ', m_pTiles[c].m_Flags&TILEFLAG_HFLIP ? 'H' : ' ', m_pTiles[c].m_Flags&TILEFLAG_ROTATE? 'R' : ' ', 0}; - m_pEditor->Graphics()->QuadsText(x*32, y*32+16, 16.0f, 1,1,1,1, aFlags); + m_pEditor->Graphics()->QuadsText(x*32, y*32+16, 16.0f, aFlags); } x += m_pTiles[c].m_Skip; } + Graphics()->QuadsEnd(); Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } @@ -526,7 +582,7 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) } } } - else if(m_pEditor->m_Map.m_pGameLayer == this || m_pEditor->m_Map.m_pTeleLayer == this || m_pEditor->m_Map.m_pSpeedupLayer == this || m_pEditor->m_Map.m_pFrontLayer == this || m_pEditor->m_Map.m_pSwitchLayer == this) + else if(m_pEditor->m_Map.m_pGameLayer == this || m_pEditor->m_Map.m_pTeleLayer == this || m_pEditor->m_Map.m_pSpeedupLayer == this || m_pEditor->m_Map.m_pFrontLayer == this || m_pEditor->m_Map.m_pSwitchLayer == this || m_pEditor->m_Map.m_pTuneLayer == this) InGameGroup = false; if(InGameGroup) @@ -552,18 +608,43 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) case 7: Result = TILE_DUNFREEZE; break; + case 8: + Result = TILE_TELECHECKIN; + break; + case 9: + Result = TILE_TELECHECKINEVIL; + break; default: break; } if(Result > -1) { - CLayerTiles *gl = m_pEditor->m_Map.m_pGameLayer; - int w = min(gl->m_Width, m_Width); - int h = min(gl->m_Height, m_Height); - for(int y = 0; y < h; y++) - for(int x = 0; x < w; x++) - if(m_pTiles[y*m_Width+x].m_Index) - gl->m_pTiles[y*gl->m_Width+x].m_Index = TILE_AIR+Result; + if (Result != TILE_TELECHECKIN && Result != TILE_TELECHECKINEVIL) + { + CLayerTiles *gl = m_pEditor->m_Map.m_pGameLayer; + + int w = min(gl->m_Width, m_Width); + int h = min(gl->m_Height, m_Height); + for(int y = 0; y < h; y++) + for(int x = 0; x < w; x++) + if(m_pTiles[y*m_Width+x].m_Index) + gl->m_pTiles[y*gl->m_Width+x].m_Index = TILE_AIR+Result; + } + else if (m_pEditor->m_Map.m_pTeleLayer) + { + CLayerTele *gl = m_pEditor->m_Map.m_pTeleLayer; + + int w = min(gl->m_Width, m_Width); + int h = min(gl->m_Height, m_Height); + for(int y = 0; y < h; y++) + for(int x = 0; x < w; x++) + if(m_pTiles[y*m_Width+x].m_Index) + { + gl->m_pTiles[y*gl->m_Width+x].m_Index = TILE_AIR+Result; + gl->m_pTeleTile[y*gl->m_Width+x].m_Number = 1; + gl->m_pTeleTile[y*gl->m_Width+x].m_Type = TILE_AIR+Result; + } + } return 1; } @@ -574,6 +655,7 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) PROP_WIDTH=0, PROP_HEIGHT, PROP_SHIFT, + PROP_SHIFT_BY, PROP_IMAGE, PROP_COLOR, PROP_COLOR_ENV, @@ -591,6 +673,7 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) {"Width", m_Width, PROPTYPE_INT_SCROLL, 1, 1000000000}, {"Height", m_Height, PROPTYPE_INT_SCROLL, 1, 1000000000}, {"Shift", 0, PROPTYPE_SHIFT, 0, 0}, + {"Shift by", m_pEditor->m_ShiftBy, PROPTYPE_INT_SCROLL, 1, 1000000000}, {"Image", m_Image, PROPTYPE_IMAGE, 0, 0}, {"Color", Color, PROPTYPE_COLOR, 0, 0}, {"Color Env", m_ColorEnv+1, PROPTYPE_INT_STEP, 0, m_pEditor->m_Map.m_lEnvelopes.size()+1}, @@ -598,10 +681,10 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) {0}, }; - if(m_pEditor->m_Map.m_pGameLayer == this || m_pEditor->m_Map.m_pTeleLayer == this || m_pEditor->m_Map.m_pSpeedupLayer == this || m_pEditor->m_Map.m_pFrontLayer == this || m_pEditor->m_Map.m_pSwitchLayer == this) // remove the image and color properties if this is the game/tele/speedup/front/switch layer + if(m_pEditor->m_Map.m_pGameLayer == this || m_pEditor->m_Map.m_pTeleLayer == this || m_pEditor->m_Map.m_pSpeedupLayer == this || m_pEditor->m_Map.m_pFrontLayer == this || m_pEditor->m_Map.m_pSwitchLayer == this || m_pEditor->m_Map.m_pTuneLayer == this) // remove the image and color properties if this is the game/tele/speedup/front/switch layer { - aProps[3].m_pName = 0; aProps[4].m_pName = 0; + aProps[5].m_pName = 0; } static int s_aIds[NUM_PROPS] = {0}; @@ -616,6 +699,8 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) Resize(m_Width, NewVal); else if(Prop == PROP_SHIFT) Shift(NewVal); + else if(Prop == PROP_SHIFT_BY) + m_pEditor->m_ShiftBy = NewVal; else if(Prop == PROP_IMAGE) { if (NewVal == -1) @@ -704,6 +789,7 @@ void CLayerTele::Resize(int NewW, int NewH) void CLayerTele::Shift(int Direction) { CLayerTiles::Shift(Direction); + int o = m_pEditor->m_ShiftBy; switch(Direction) { @@ -711,28 +797,40 @@ void CLayerTele::Shift(int Direction) { // left for(int y = 0; y < m_Height; ++y) - mem_move(&m_pTeleTile[y*m_Width], &m_pTeleTile[y*m_Width+1], (m_Width-1)*sizeof(CTeleTile)); + { + mem_move(&m_pTeleTile[y*m_Width], &m_pTeleTile[y*m_Width+o], (m_Width-o)*sizeof(CTeleTile)); + mem_zero(&m_pTeleTile[y*m_Width + (m_Width-o)], o*sizeof(CTeleTile)); + } } break; case 2: { // right for(int y = 0; y < m_Height; ++y) - mem_move(&m_pTeleTile[y*m_Width+1], &m_pTeleTile[y*m_Width], (m_Width-1)*sizeof(CTeleTile)); + { + mem_move(&m_pTeleTile[y*m_Width+o], &m_pTeleTile[y*m_Width], (m_Width-o)*sizeof(CTeleTile)); + mem_zero(&m_pTeleTile[y*m_Width], o*sizeof(CTeleTile)); + } } break; case 4: { // up - for(int y = 0; y < m_Height-1; ++y) - mem_copy(&m_pTeleTile[y*m_Width], &m_pTeleTile[(y+1)*m_Width], m_Width*sizeof(CTeleTile)); + for(int y = 0; y < m_Height-o; ++y) + { + mem_copy(&m_pTeleTile[y*m_Width], &m_pTeleTile[(y+o)*m_Width], m_Width*sizeof(CTeleTile)); + mem_zero(&m_pTeleTile[(y+o)*m_Width], m_Width*sizeof(CTeleTile)); + } } break; case 8: { // down - for(int y = m_Height-1; y > 0; --y) - mem_copy(&m_pTeleTile[y*m_Width], &m_pTeleTile[(y-1)*m_Width], m_Width*sizeof(CTeleTile)); + for(int y = m_Height-1; y >= o; --y) + { + mem_copy(&m_pTeleTile[y*m_Width], &m_pTeleTile[(y-o)*m_Width], m_Width*sizeof(CTeleTile)); + mem_zero(&m_pTeleTile[(y-o)*m_Width], m_Width*sizeof(CTeleTile)); + } } } } @@ -756,7 +854,7 @@ void CLayerTele::BrushDraw(CLayer *pBrush, float wx, float wy) if(fx<0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; - if(l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEIN || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEINEVIL || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEOUT || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELECHECK || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELECHECKOUT || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELECHECKIN) + if(l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEIN || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEINEVIL || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELECHECKINEVIL || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEOUT || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELECHECK || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELECHECKOUT || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELECHECKIN || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEINWEAPON || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TELEINHOOK) { if(m_pEditor->m_TeleNumber != l->m_TeleNum) { @@ -818,13 +916,13 @@ void CLayerTele::BrushFlipY() void CLayerTele::BrushRotate(float Amount) { - int Rotation = (round(360.0f*Amount/(pi*2))/90)%4; // 0=0, 1=90, 2=180, 3=270 + int Rotation = (round_to_int(360.0f*Amount/(pi*2))/90)%4; // 0=0°, 1=90°, 2=180°, 3=270° if(Rotation < 0) Rotation +=4; if(Rotation == 1 || Rotation == 3) { - // 90 rotation + // 90° rotation CTeleTile *pTempData1 = new CTeleTile[m_Width*m_Height]; CTile *pTempData2 = new CTile[m_Width*m_Height]; mem_copy(pTempData1, m_pTeleTile, m_Width*m_Height*sizeof(CTeleTile)); @@ -856,6 +954,10 @@ void CLayerTele::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) { if(m_Readonly) return; + + Snap(&Rect); // corrects Rect; no need of <= + + Snap(&Rect); int sx = ConvertX(Rect.x); int sy = ConvertY(Rect.y); @@ -864,9 +966,9 @@ void CLayerTele::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) CLayerTele *pLt = static_cast(pBrush); - for(int y = 0; y <= h; y++) + for(int y = 0; y < h; y++) { - for(int x = 0; x <= w; x++) + for(int x = 0; x < w; x++) { int fx = x+sx; int fy = y+sy; @@ -874,7 +976,7 @@ void CLayerTele::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; - if(Empty) + if(Empty || !(pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]).m_Index) // air chosen: reset { m_pTiles[fy*m_Width+fx].m_Index = 0; m_pTeleTile[fy*m_Width+fx].m_Type = 0; @@ -937,6 +1039,7 @@ void CLayerSpeedup::Resize(int NewW, int NewH) void CLayerSpeedup::Shift(int Direction) { CLayerTiles::Shift(Direction); + int o = m_pEditor->m_ShiftBy; switch(Direction) { @@ -944,28 +1047,40 @@ void CLayerSpeedup::Shift(int Direction) { // left for(int y = 0; y < m_Height; ++y) - mem_move(&m_pSpeedupTile[y*m_Width], &m_pSpeedupTile[y*m_Width+1], (m_Width-1)*sizeof(CSpeedupTile)); + { + mem_move(&m_pSpeedupTile[y*m_Width], &m_pSpeedupTile[y*m_Width+o], (m_Width-o)*sizeof(CSpeedupTile)); + mem_zero(&m_pSpeedupTile[y*m_Width + (m_Width-o)], o*sizeof(CSpeedupTile)); + } } break; case 2: { // right for(int y = 0; y < m_Height; ++y) - mem_move(&m_pSpeedupTile[y*m_Width+1], &m_pSpeedupTile[y*m_Width], (m_Width-1)*sizeof(CSpeedupTile)); + { + mem_move(&m_pSpeedupTile[y*m_Width+o], &m_pSpeedupTile[y*m_Width], (m_Width-o)*sizeof(CSpeedupTile)); + mem_zero(&m_pSpeedupTile[y*m_Width], o*sizeof(CSpeedupTile)); + } } break; case 4: { // up - for(int y = 0; y < m_Height-1; ++y) - mem_copy(&m_pSpeedupTile[y*m_Width], &m_pSpeedupTile[(y+1)*m_Width], m_Width*sizeof(CSpeedupTile)); + for(int y = 0; y < m_Height-o; ++y) + { + mem_copy(&m_pSpeedupTile[y*m_Width], &m_pSpeedupTile[(y+o)*m_Width], m_Width*sizeof(CSpeedupTile)); + mem_zero(&m_pSpeedupTile[(y+o)*m_Width], m_Width*sizeof(CSpeedupTile)); + } } break; case 8: { // down - for(int y = m_Height-1; y > 0; --y) - mem_copy(&m_pSpeedupTile[y*m_Width], &m_pSpeedupTile[(y-1)*m_Width], m_Width*sizeof(CSpeedupTile)); + for(int y = m_Height-1; y >= o; --y) + { + mem_copy(&m_pSpeedupTile[y*m_Width], &m_pSpeedupTile[(y-o)*m_Width], m_Width*sizeof(CSpeedupTile)); + mem_zero(&m_pSpeedupTile[(y-o)*m_Width], m_Width*sizeof(CSpeedupTile)); + } } } } @@ -1066,13 +1181,13 @@ void CLayerSpeedup::BrushFlipY() void CLayerSpeedup::BrushRotate(float Amount) { - int Rotation = (round(360.0f*Amount/(pi*2))/90)%4; // 0=0, 1=90, 2=180, 3=270 + int Rotation = (round_to_int(360.0f*Amount/(pi*2))/90)%4; // 0=0°, 1=90°, 2=180°, 3=270° if(Rotation < 0) Rotation +=4; if(Rotation == 1 || Rotation == 3) { - // 90 rotation + // 90° rotation CSpeedupTile *pTempData1 = new CSpeedupTile[m_Width*m_Height]; CTile *pTempData2 = new CTile[m_Width*m_Height]; mem_copy(pTempData1, m_pSpeedupTile, m_Width*m_Height*sizeof(CSpeedupTile)); @@ -1104,6 +1219,10 @@ void CLayerSpeedup::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) { if(m_Readonly) return; + + Snap(&Rect); // corrects Rect; no need of <= + + Snap(&Rect); int sx = ConvertX(Rect.x); int sy = ConvertY(Rect.y); @@ -1112,9 +1231,9 @@ void CLayerSpeedup::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) CLayerSpeedup *pLt = static_cast(pBrush); - for(int y = 0; y <= h; y++) + for(int y = 0; y < h; y++) { - for(int x = 0; x <= w; x++) + for(int x = 0; x < w; x++) { int fx = x+sx; int fy = y+sy; @@ -1122,7 +1241,7 @@ void CLayerSpeedup::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; - if(Empty) + if(Empty || (pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]).m_Index != TILE_BOOST) // no speed up tile choosen: reset { m_pTiles[fy*m_Width+fx].m_Index = 0; m_pSpeedupTile[fy*m_Width+fx].m_Force = 0; @@ -1173,37 +1292,6 @@ void CLayerFront::Resize(int NewW, int NewH) void CLayerFront::Shift(int Direction) { CLayerTiles::Shift(Direction); - - switch(Direction) - { - case 1: - { - // left - for(int y = 0; y < m_Height; ++y) - mem_move(&m_pTiles[y*m_Width], &m_pTiles[y*m_Width+1], (m_Width-1)*sizeof(CTile)); - } - break; - case 2: - { - // right - for(int y = 0; y < m_Height; ++y) - mem_move(&m_pTiles[y*m_Width+1], &m_pTiles[y*m_Width], (m_Width-1)*sizeof(CTile)); - } - break; - case 4: - { - // up - for(int y = 0; y < m_Height-1; ++y) - mem_copy(&m_pTiles[y*m_Width], &m_pTiles[(y+1)*m_Width], m_Width*sizeof(CTile)); - } - break; - case 8: - { - // down - for(int y = m_Height-1; y > 0; --y) - mem_copy(&m_pTiles[y*m_Width], &m_pTiles[(y-1)*m_Width], m_Width*sizeof(CTile)); - } - } } void CLayerFront::BrushDraw(CLayer *pBrush, float wx, float wy) @@ -1270,6 +1358,7 @@ void CLayerSwitch::Resize(int NewW, int NewH) void CLayerSwitch::Shift(int Direction) { CLayerTiles::Shift(Direction); + int o = m_pEditor->m_ShiftBy; switch(Direction) { @@ -1277,28 +1366,40 @@ void CLayerSwitch::Shift(int Direction) { // left for(int y = 0; y < m_Height; ++y) - mem_move(&m_pSwitchTile[y*m_Width], &m_pSwitchTile[y*m_Width+1], (m_Width-1)*sizeof(CSwitchTile)); + { + mem_move(&m_pSwitchTile[y*m_Width], &m_pSwitchTile[y*m_Width+o], (m_Width-o)*sizeof(CSwitchTile)); + mem_zero(&m_pSwitchTile[y*m_Width + (m_Width-o)], o*sizeof(CSwitchTile)); + } } break; case 2: { // right for(int y = 0; y < m_Height; ++y) - mem_move(&m_pSwitchTile[y*m_Width+1], &m_pSwitchTile[y*m_Width], (m_Width-1)*sizeof(CSwitchTile)); + { + mem_move(&m_pSwitchTile[y*m_Width+o], &m_pSwitchTile[y*m_Width], (m_Width-o)*sizeof(CSwitchTile)); + mem_zero(&m_pSwitchTile[y*m_Width], o*sizeof(CSwitchTile)); + } } break; case 4: { // up - for(int y = 0; y < m_Height-1; ++y) - mem_copy(&m_pSwitchTile[y*m_Width], &m_pSwitchTile[(y+1)*m_Width], m_Width*sizeof(CSwitchTile)); + for(int y = 0; y < m_Height-o; ++y) + { + mem_copy(&m_pSwitchTile[y*m_Width], &m_pSwitchTile[(y+o)*m_Width], m_Width*sizeof(CSwitchTile)); + mem_zero(&m_pSwitchTile[(y+o)*m_Width], m_Width*sizeof(CSwitchTile)); + } } break; case 8: { // down - for(int y = m_Height-1; y > 0; --y) - mem_copy(&m_pSwitchTile[y*m_Width], &m_pSwitchTile[(y-1)*m_Width], m_Width*sizeof(CSwitchTile)); + for(int y = m_Height-1; y >= o; --y) + { + mem_copy(&m_pSwitchTile[y*m_Width], &m_pSwitchTile[(y-o)*m_Width], m_Width*sizeof(CSwitchTile)); + mem_zero(&m_pSwitchTile[(y-o)*m_Width], m_Width*sizeof(CSwitchTile)); + } } } } @@ -1325,7 +1426,7 @@ void CLayerSwitch::BrushDraw(CLayer *pBrush, float wx, float wy) if(fx<0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; - if((l->m_pTiles[y*l->m_Width+x].m_Index >= (ENTITY_ARMOR_1 + ENTITY_OFFSET) && l->m_pTiles[y*l->m_Width+x].m_Index <= (ENTITY_DOOR + ENTITY_OFFSET)) || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_HIT_START || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_HIT_END || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_SWITCHOPEN || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_SWITCHCLOSE || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_SWITCHTIMEDOPEN || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_SWITCHTIMEDCLOSE || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_FREEZE || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_DFREEZE || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_DUNFREEZE) + if((l->m_pTiles[y*l->m_Width+x].m_Index >= (ENTITY_ARMOR_1 + ENTITY_OFFSET) && l->m_pTiles[y*l->m_Width+x].m_Index <= (ENTITY_DOOR + ENTITY_OFFSET)) || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_HIT_START || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_HIT_END || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_SWITCHOPEN || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_SWITCHCLOSE || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_SWITCHTIMEDOPEN || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_SWITCHTIMEDCLOSE || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_FREEZE || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_DFREEZE || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_DUNFREEZE || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_JUMP || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_PENALTY) { if(m_pEditor->m_SwitchNum != l->m_SwitchNumber || m_pEditor->m_SwitchDelay != l->m_SwitchDelay) { @@ -1386,6 +1487,10 @@ void CLayerSwitch::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) { if(m_Readonly) return; + + Snap(&Rect); // corrects Rect; no need of <= + + Snap(&Rect); int sx = ConvertX(Rect.x); int sy = ConvertY(Rect.y); @@ -1394,9 +1499,9 @@ void CLayerSwitch::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) CLayerSwitch *pLt = static_cast(pBrush); - for(int y = 0; y <= h; y++) + for(int y = 0; y < h; y++) { - for(int x = 0; x <= w; x++) + for(int x = 0; x < w; x++) { int fx = x+sx; int fy = y+sy; @@ -1404,11 +1509,12 @@ void CLayerSwitch::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; - if(Empty) + if(Empty || !(pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]).m_Index) // at least reset the tile if air is choosen { m_pTiles[fy*m_Width+fx].m_Index = 0; m_pSwitchTile[fy*m_Width+fx].m_Type = 0; m_pSwitchTile[fy*m_Width+fx].m_Number = 0; + m_pSwitchTile[fy*m_Width+fx].m_Delay = 0; } else { @@ -1430,3 +1536,257 @@ void CLayerSwitch::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) } } } + +//------------------------------------------------------ + +CLayerTune::CLayerTune(int w, int h) +: CLayerTiles(w, h) +{ + //m_Type = LAYERTYPE_SWITCH; + str_copy(m_aName, "Tune", sizeof(m_aName)); + m_Tune = 1; + + m_pTuneTile = new CTuneTile[w*h]; + mem_zero(m_pTuneTile, w*h*sizeof(CTuneTile)); +} + +CLayerTune::~CLayerTune() +{ + delete[] m_pTuneTile; +} + +void CLayerTune::Resize(int NewW, int NewH) +{ + // resize Tune data + CTuneTile *pNewTuneData = new CTuneTile[NewW*NewH]; + mem_zero(pNewTuneData, NewW*NewH*sizeof(CTuneTile)); + + // copy old data + for(int y = 0; y < min(NewH, m_Height); y++) + mem_copy(&pNewTuneData[y*NewW], &m_pTuneTile[y*m_Width], min(m_Width, NewW)*sizeof(CTuneTile)); + + // replace old + delete [] m_pTuneTile; + m_pTuneTile = pNewTuneData; + + // resize tile data + CLayerTiles::Resize(NewW, NewH); + + // resize gamelayer too + if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) + m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); +} + +void CLayerTune::Shift(int Direction) +{ + CLayerTiles::Shift(Direction); + int o = m_pEditor->m_ShiftBy; + + switch(Direction) + { + case 1: + { + // left + for(int y = 0; y < m_Height; ++y) + { + mem_move(&m_pTuneTile[y*m_Width], &m_pTuneTile[y*m_Width+o], (m_Width-o)*sizeof(CTuneTile)); + mem_zero(&m_pTuneTile[y*m_Width + (m_Width-o)], o*sizeof(CTuneTile)); + } + } + break; + case 2: + { + // right + for(int y = 0; y < m_Height; ++y) + { + mem_move(&m_pTuneTile[y*m_Width+o], &m_pTuneTile[y*m_Width], (m_Width-o)*sizeof(CTuneTile)); + mem_zero(&m_pTuneTile[y*m_Width], o*sizeof(CTuneTile)); + } + } + break; + case 4: + { + // up + for(int y = 0; y < m_Height-o; ++y) + { + mem_copy(&m_pTuneTile[y*m_Width], &m_pTuneTile[(y+o)*m_Width], m_Width*sizeof(CTuneTile)); + mem_zero(&m_pTuneTile[(y+o)*m_Width], m_Width*sizeof(CTuneTile)); + } + } + break; + case 8: + { + // down + for(int y = m_Height-1; y >= o; --y) + { + mem_copy(&m_pTuneTile[y*m_Width], &m_pTuneTile[(y-o)*m_Width], m_Width*sizeof(CTuneTile)); + mem_zero(&m_pTuneTile[(y-o)*m_Width], m_Width*sizeof(CTuneTile)); + } + } + } +} + +void CLayerTune::BrushDraw(CLayer *pBrush, float wx, float wy) +{ + if(m_Readonly) + return; + + CLayerTune *l = (CLayerTune *)pBrush; + int sx = ConvertX(wx); + int sy = ConvertY(wy); + if(str_comp(l->m_aFileName, m_pEditor->m_aFileName)) + { + m_pEditor->m_TuningNum = l->m_TuningNumber; + } + + for(int y = 0; y < l->m_Height; y++) + for(int x = 0; x < l->m_Width; x++) + { + int fx = x+sx; + int fy = y+sy; + if(fx<0 || fx >= m_Width || fy < 0 || fy >= m_Height) + continue; + + if(l->m_pTiles[y*l->m_Width+x].m_Index == TILE_TUNE1) + { + if(m_pEditor->m_TuningNum != l->m_TuningNumber) + { + m_pTuneTile[fy*m_Width+fx].m_Number = m_pEditor->m_TuningNum; + } + else if(l->m_pTuneTile[y*l->m_Width+x].m_Number) + m_pTuneTile[fy*m_Width+fx].m_Number = l->m_pTuneTile[y*l->m_Width+x].m_Number; + else + { + if(!m_pEditor->m_TuningNum) + { + m_pTuneTile[fy*m_Width+fx].m_Number = 0; + m_pTuneTile[fy*m_Width+fx].m_Type = 0; + m_pTiles[fy*m_Width+fx].m_Index = 0; + continue; + } + else + m_pTuneTile[fy*m_Width+fx].m_Number = m_pEditor->m_TuningNum; + } + + m_pTuneTile[fy*m_Width+fx].m_Type = l->m_pTiles[y*l->m_Width+x].m_Index; + m_pTiles[fy*m_Width+fx].m_Index = l->m_pTiles[y*l->m_Width+x].m_Index; + } + else + { + m_pTuneTile[fy*m_Width+fx].m_Number = 0; + m_pTuneTile[fy*m_Width+fx].m_Type = 0; + m_pTiles[fy*m_Width+fx].m_Index = 0; + } + } + m_pEditor->m_Map.m_Modified = true; +} + + +void CLayerTune::BrushFlipX() +{ + CLayerTiles::BrushFlipX(); + + for(int y = 0; y < m_Height; y++) + for(int x = 0; x < m_Width/2; x++) + { + CTuneTile Tmp = m_pTuneTile[y*m_Width+x]; + m_pTuneTile[y*m_Width+x] = m_pTuneTile[y*m_Width+m_Width-1-x]; + m_pTuneTile[y*m_Width+m_Width-1-x] = Tmp; + } +} + +void CLayerTune::BrushFlipY() +{ + CLayerTiles::BrushFlipY(); + + for(int y = 0; y < m_Height/2; y++) + for(int x = 0; x < m_Width; x++) + { + CTuneTile Tmp = m_pTuneTile[y*m_Width+x]; + m_pTuneTile[y*m_Width+x] = m_pTuneTile[(m_Height-1-y)*m_Width+x]; + m_pTuneTile[(m_Height-1-y)*m_Width+x] = Tmp; + } +} + +void CLayerTune::BrushRotate(float Amount) +{ + int Rotation = (round_to_int(360.0f*Amount/(pi*2))/90)%4; // 0=0°, 1=90°, 2=180°, 3=270° + if(Rotation < 0) + Rotation +=4; + + if(Rotation == 1 || Rotation == 3) + { + // 90° rotation + CTuneTile *pTempData1 = new CTuneTile[m_Width*m_Height]; + CTile *pTempData2 = new CTile[m_Width*m_Height]; + mem_copy(pTempData1, m_pTuneTile, m_Width*m_Height*sizeof(CTuneTile)); + mem_copy(pTempData2, m_pTiles, m_Width*m_Height*sizeof(CTile)); + CTuneTile *pDst1 = m_pTuneTile; + CTile *pDst2 = m_pTiles; + for(int x = 0; x < m_Width; ++x) + for(int y = m_Height-1; y >= 0; --y, ++pDst1, ++pDst2) + { + *pDst1 = pTempData1[y*m_Width+x]; + *pDst2 = pTempData2[y*m_Width+x]; + } + + int Temp = m_Width; + m_Width = m_Height; + m_Height = Temp; + delete[] pTempData1; + delete[] pTempData2; + } + + if(Rotation == 2 || Rotation == 3) + { + BrushFlipX(); + BrushFlipY(); + } +} + + +void CLayerTune::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) +{ + if(m_Readonly) + return; + + Snap(&Rect); // corrects Rect; no need of <= + + int sx = ConvertX(Rect.x); + int sy = ConvertY(Rect.y); + int w = ConvertX(Rect.w); + int h = ConvertY(Rect.h); + + CLayerTune *pLt = static_cast(pBrush); + + for(int y = 0; y < h; y++) + { + for(int x = 0; x < w; x++) + { + int fx = x+sx; + int fy = y+sy; + + if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) + continue; + + if(Empty || (pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]).m_Index != TILE_TUNE1) // \o/ this fixes editor bug; TODO: use IsUsedInThisLayer here + { + m_pTiles[fy*m_Width+fx].m_Index = 0; + m_pTuneTile[fy*m_Width+fx].m_Type = 0; + m_pTuneTile[fy*m_Width+fx].m_Number = 0; + } + else + { + m_pTiles[fy*m_Width+fx] = pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]; + m_pTuneTile[fy*m_Width+fx].m_Type = m_pTiles[fy*m_Width+fx].m_Index; + if(m_pTiles[fy*m_Width+fx].m_Index > 0) + { + if((!pLt->m_pTuneTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Number && m_pEditor->m_TuningNum) || m_pEditor->m_TuningNum != pLt->m_TuningNumber) + m_pTuneTile[fy*m_Width+fx].m_Number = m_pEditor->m_TuningNum; + else + m_pTuneTile[fy*m_Width+fx].m_Number = pLt->m_pTuneTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Number; + } + } + } + } +} diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index b39165986d..5a2e76e43c 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -174,6 +174,23 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) return 1; } } + + if(pEditor->GetSelectedGroup()->m_GameGroup && !pEditor->m_Map.m_pTuneLayer) + { + // new tune layer + View.HSplitBottom(10.0f, &View, &Button); + View.HSplitBottom(12.0f, &View, &Button); + static int s_NewSwitchLayerButton = 0; + if(pEditor->DoButton_Editor(&s_NewSwitchLayerButton, "Add Tune Layer", 0, &Button, 0, "Creates a new tuning layer")) + { + CLayer *l = new CLayerTune(pEditor->m_Map.m_pGameLayer->m_Width, pEditor->m_Map.m_pGameLayer->m_Height); + pEditor->m_Map.MakeTuneLayer(l); + pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l); + pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1; + pEditor->m_Brush.Clear(); + return 1; + } + } if(pEditor->GetSelectedGroup()->m_GameGroup && !pEditor->m_Map.m_pFrontLayer) { @@ -329,13 +346,15 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View) pEditor->m_Map.m_pSpeedupLayer = 0x0; if(pEditor->GetSelectedLayer(0) == pEditor->m_Map.m_pSwitchLayer) pEditor->m_Map.m_pSwitchLayer = 0x0; + if(pEditor->GetSelectedLayer(0) == pEditor->m_Map.m_pTuneLayer) + pEditor->m_Map.m_pTuneLayer = 0x0; pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->DeleteLayer(pEditor->m_SelectedLayer); return 1; } // layer name // if(pEditor->m_Map.m_pGameLayer != pEditor->GetSelectedLayer(0)) - if(pEditor->m_Map.m_pGameLayer != pEditor->GetSelectedLayer(0) && pEditor->m_Map.m_pTeleLayer != pEditor->GetSelectedLayer(0) && pEditor->m_Map.m_pSpeedupLayer != pEditor->GetSelectedLayer(0) && pEditor->m_Map.m_pFrontLayer != pEditor->GetSelectedLayer(0) && pEditor->m_Map.m_pSwitchLayer != pEditor->GetSelectedLayer(0)) + if(pEditor->m_Map.m_pGameLayer != pEditor->GetSelectedLayer(0) && pEditor->m_Map.m_pTeleLayer != pEditor->GetSelectedLayer(0) && pEditor->m_Map.m_pSpeedupLayer != pEditor->GetSelectedLayer(0) && pEditor->m_Map.m_pFrontLayer != pEditor->GetSelectedLayer(0) && pEditor->m_Map.m_pSwitchLayer != pEditor->GetSelectedLayer(0) && pEditor->m_Map.m_pTuneLayer != pEditor->GetSelectedLayer(0)) { View.HSplitBottom(5.0f, &View, &Button); View.HSplitBottom(12.0f, &View, &Button); @@ -367,7 +386,7 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View) }; // if(pEditor->m_Map.m_pGameLayer == pEditor->GetSelectedLayer(0)) // dont use Group and Detail from the selection if this is the game layer - if(pEditor->m_Map.m_pGameLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pTeleLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pSpeedupLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pFrontLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pSwitchLayer == pEditor->GetSelectedLayer(0)) // dont use Group and Detail from the selection if this is the game layer + if(pEditor->m_Map.m_pGameLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pTeleLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pSpeedupLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pFrontLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pSwitchLayer == pEditor->GetSelectedLayer(0) || pEditor->m_Map.m_pTuneLayer == pEditor->GetSelectedLayer(0)) // dont use Group and Detail from the selection if this is the game layer { aProps[0].m_Type = PROPTYPE_NULL; aProps[2].m_Type = PROPTYPE_NULL; @@ -632,7 +651,6 @@ int CEditor::PopupPoint(CEditor *pEditor, CUIRect View) { if(pEditor->m_SelectedPoints&(1<m_aColors[v].r = (NewVal>>24)&0xff; pQuad->m_aColors[v].g = (NewVal>>16)&0xff; pQuad->m_aColors[v].b = (NewVal>>8)&0xff; @@ -857,11 +875,53 @@ int CEditor::PopupSelectImage(CEditor *pEditor, CUIRect View) int ShowImage = g_SelectImageCurrent; + static int s_ScrollBar = 0; + static float s_ScrollValue = 0; + float ImagesHeight = pEditor->m_Map.m_lImages.size() * 14; + float ScrollDifference = ImagesHeight - ButtonBar.h; + + if(pEditor->m_Map.m_lImages.size() > 20) // Do we need a scrollbar? + { + CUIRect Scroll; + ButtonBar.VSplitRight(15.0f, &ButtonBar, &Scroll); + ButtonBar.VSplitRight(3.0f, &ButtonBar, 0); // extra spacing + Scroll.HMargin(5.0f, &Scroll); + s_ScrollValue = pEditor->UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue); + + if(pEditor->UI()->MouseInside(&Scroll) || pEditor->UI()->MouseInside(&ButtonBar)) + { + int ScrollNum = (int)((ImagesHeight-ButtonBar.h)/14.0f)+1; + if(ScrollNum > 0) + { + if(pEditor->Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f); + if(pEditor->Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f); + } + else + ScrollNum = 0; + } + } + + float ImageStartAt = ScrollDifference * s_ScrollValue; + if(ImageStartAt < 0.0f) + ImageStartAt = 0.0f; + + float ImageStopAt = ImagesHeight - ScrollDifference * (1 - s_ScrollValue); + float ImageCur = 0.0f; for(int i = -1; i < pEditor->m_Map.m_lImages.size(); i++) { + if(ImageCur > ImageStopAt) + break; + if(ImageCur < ImageStartAt) + { + ImageCur += 14.0f; + continue; + } + ImageCur += 14.0f; + CUIRect Button; - ButtonBar.HSplitTop(12.0f, &Button, &ButtonBar); - ButtonBar.HSplitTop(2.0f, 0, &ButtonBar); + ButtonBar.HSplitTop(14.0f, &Button, &ButtonBar); if(pEditor->UI()->MouseInside(&Button)) ShowImage = i; @@ -922,7 +982,7 @@ static int s_GametileOpSelected = -1; int CEditor::PopupSelectGametileOp(CEditor *pEditor, CUIRect View) { - static const char *s_pButtonNames[] = { "Clear", "Collision", "Death", "Unhookable", "Freeze", "Unfreeze", "Deep Freeze", "Deep Unfreeze" }; + static const char *s_pButtonNames[] = { "Clear", "Collision", "Death", "Unhookable", "Freeze", "Unfreeze", "Deep Freeze", "Deep Unfreeze", "Check-Tele From", "Evil Check-Tele From" }; static unsigned s_NumButtons = sizeof(s_pButtonNames) / sizeof(char*); CUIRect Button; @@ -941,7 +1001,7 @@ void CEditor::PopupSelectGametileOpInvoke(float x, float y) { static int s_SelectGametileOpPopupId = 0; s_GametileOpSelected = -1; - UiInvokePopupMenu(&s_SelectGametileOpPopupId, 0, x, y, 120.0f, 120.0f, PopupSelectGametileOp); + UiInvokePopupMenu(&s_SelectGametileOpPopupId, 0, x, y, 120.0f, 150.0f, PopupSelectGametileOp); } int CEditor::PopupSelectGameTileOpResult() @@ -1014,10 +1074,32 @@ int CEditor::PopupTele(CEditor *pEditor, CUIRect View) static int s_aIds[NUM_PROPS] = {0}; int NewVal = 0; - int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); + static vec4 s_color = vec4(1,1,1,0.5f); + + int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal, s_color); if(Prop == PROP_TELE) - pEditor->m_TeleNumber = clamp(NewVal, 0, 255); + { + NewVal = clamp(NewVal, 0, 255); + + CLayerTele *gl = pEditor->m_Map.m_pTeleLayer; + for(int y = 0; y < gl->m_Height; ++y) + { + for(int x = 0; x < gl->m_Width; ++x) + { + if(gl->m_pTeleTile[y*gl->m_Width+x].m_Number == NewVal) + { + s_color = vec4(1,0.5f,0.5f,0.5f); + goto done; + } + } + } + + s_color = vec4(0.5f,1,0.5f,0.5f); + + done: + pEditor->m_TeleNumber = NewVal; + } return 0; } @@ -1076,12 +1158,59 @@ int CEditor::PopupSwitch(CEditor *pEditor, CUIRect View) static int s_aIds[NUM_PROPS] = {0}; int NewVal = 0; - int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); + static vec4 s_color = vec4(1,1,1,0.5f); + int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal, s_color); if(Prop == PROP_SwitchNumber) - pEditor->m_SwitchNum = clamp(NewVal, 0, 255); + { + NewVal = clamp(NewVal, 0, 255); + + CLayerSwitch *gl = pEditor->m_Map.m_pSwitchLayer; + for(int y = 0; y < gl->m_Height; ++y) + { + for(int x = 0; x < gl->m_Width; ++x) + { + if(gl->m_pSwitchTile[y*gl->m_Width+x].m_Number == NewVal) + { + s_color = vec4(1,0.5f,0.5f,0.5f); + goto done; + } + } + } + + s_color = vec4(0.5f,1,0.5f,0.5f); + + done: + pEditor->m_SwitchNum = NewVal; + } if(Prop == PROP_SwitchDelay) - pEditor->m_SwitchDelay = clamp(NewVal, 0, 255); + pEditor->m_SwitchDelay = clamp(NewVal, 0, 255); + + return 0; +} + +int CEditor::PopupTune(CEditor *pEditor, CUIRect View) +{ + CUIRect Button; + View.HSplitBottom(12.0f, &View, &Button); + + enum + { + PROP_TUNE=0, + NUM_PROPS, + }; + + CProperty aProps[] = { + {"Zone", pEditor->m_TuningNum, PROPTYPE_INT_STEP, 1, 255}, + {0}, + }; + + static int s_aIds[NUM_PROPS] = {0}; + int NewVal = 0; + int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); + + if(Prop == PROP_TUNE) + pEditor->m_TuningNum = clamp(NewVal, 1, 255); return 0; } diff --git a/src/game/gamecore.cpp b/src/game/gamecore.cpp index 2ce2ac8904..e4a60033dd 100644 --- a/src/game/gamecore.cpp +++ b/src/game/gamecore.cpp @@ -3,7 +3,7 @@ #include "gamecore.h" #include - +#include const char *CTuningParams::m_apNames[] = { #define MACRO_TUNING_PARAM(Name,ScriptName,Value) #ScriptName, @@ -61,25 +61,49 @@ void CCharacterCore::Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore { m_pWorld = pWorld; m_pCollision = pCollision; + m_pTeleOuts = NULL; + + m_pTeams = pTeams; + m_Id = -1; + m_Hook = true; + m_Collision = true; + m_JumpedTotal = 0; + m_Jumps = 2; +} + +void CCharacterCore::Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore* pTeams, std::map > *pTeleOuts) +{ + m_pWorld = pWorld; + m_pCollision = pCollision; + m_pTeleOuts = pTeleOuts; m_pTeams = pTeams; m_Id = -1; + m_Hook = true; + m_Collision = true; + m_JumpedTotal = 0; + m_Jumps = 2; } void CCharacterCore::Reset() { m_Pos = vec2(0,0); m_Vel = vec2(0,0); + m_NewHook = false; m_HookPos = vec2(0,0); m_HookDir = vec2(0,0); m_HookTick = 0; m_HookState = HOOK_IDLE; m_HookedPlayer = -1; m_Jumped = 0; + m_JumpedTotal = 0; + m_Jumps = 2; m_TriggeredEvents = 0; + m_Hook = true; + m_Collision = true; } -void CCharacterCore::Tick(bool UseInput) +void CCharacterCore::Tick(bool UseInput, bool IsClient) { float PhysSize = 28.0f; int MapIndex = Collision()->GetPureMapIndex(m_Pos);; @@ -120,6 +144,8 @@ void CCharacterCore::Tick(bool UseInput) m_TileSFlagsT = (UseInput && IsRightTeam(MapIndexT))?Collision()->GetDTileFlags(MapIndexT):0; m_TriggeredEvents = 0; + vec2 PrevPos = m_Pos; + // get ground state bool Grounded = false; if(m_pCollision->CheckPoint(m_Pos.x+PhysSize/2, m_Pos.y+PhysSize/2+5)) @@ -129,11 +155,11 @@ void CCharacterCore::Tick(bool UseInput) vec2 TargetDirection = normalize(vec2(m_Input.m_TargetX, m_Input.m_TargetY)); - m_Vel.y += m_pWorld->m_Tuning.m_Gravity; + m_Vel.y += m_pWorld->m_Tuning[g_Config.m_ClDummy].m_Gravity; - float MaxSpeed = Grounded ? m_pWorld->m_Tuning.m_GroundControlSpeed : m_pWorld->m_Tuning.m_AirControlSpeed; - float Accel = Grounded ? m_pWorld->m_Tuning.m_GroundControlAccel : m_pWorld->m_Tuning.m_AirControlAccel; - float Friction = Grounded ? m_pWorld->m_Tuning.m_GroundFriction : m_pWorld->m_Tuning.m_AirFriction; + float MaxSpeed = Grounded ? m_pWorld->m_Tuning[g_Config.m_ClDummy].m_GroundControlSpeed : m_pWorld->m_Tuning[g_Config.m_ClDummy].m_AirControlSpeed; + float Accel = Grounded ? m_pWorld->m_Tuning[g_Config.m_ClDummy].m_GroundControlAccel : m_pWorld->m_Tuning[g_Config.m_ClDummy].m_AirControlAccel; + float Friction = Grounded ? m_pWorld->m_Tuning[g_Config.m_ClDummy].m_GroundFriction : m_pWorld->m_Tuning[g_Config.m_ClDummy].m_AirFriction; // handle input if(UseInput) @@ -160,14 +186,16 @@ void CCharacterCore::Tick(bool UseInput) if(Grounded) { m_TriggeredEvents |= COREEVENT_GROUND_JUMP; - m_Vel.y = -m_pWorld->m_Tuning.m_GroundJumpImpulse; + m_Vel.y = -m_pWorld->m_Tuning[g_Config.m_ClDummy].m_GroundJumpImpulse; m_Jumped |= 1; + m_JumpedTotal = 1; } else if(!(m_Jumped&2)) { m_TriggeredEvents |= COREEVENT_AIR_JUMP; - m_Vel.y = -m_pWorld->m_Tuning.m_AirJumpImpulse; + m_Vel.y = -m_pWorld->m_Tuning[g_Config.m_ClDummy].m_AirJumpImpulse; m_Jumped |= 3; + m_JumpedTotal++; } } } @@ -207,7 +235,10 @@ void CCharacterCore::Tick(bool UseInput) // 1 bit = to keep track if a jump has been made on this input // 2 bit = to keep track if a air-jump has been made if(Grounded) + { m_Jumped &= ~2; + m_JumpedTotal = 0; + } // do hook if(m_HookState == HOOK_IDLE) @@ -228,29 +259,37 @@ void CCharacterCore::Tick(bool UseInput) } else if(m_HookState == HOOK_FLYING) { - vec2 NewPos = m_HookPos+m_HookDir*m_pWorld->m_Tuning.m_HookFireSpeed; - if(distance(m_Pos, NewPos) > m_pWorld->m_Tuning.m_HookLength) + vec2 NewPos = m_HookPos+m_HookDir*m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookFireSpeed; + if((!m_NewHook && distance(m_Pos, NewPos) > m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookLength) + || (m_NewHook && distance(m_HookTeleBase, NewPos) > m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookLength)) { m_HookState = HOOK_RETRACT_START; - NewPos = m_Pos + normalize(NewPos-m_Pos) * m_pWorld->m_Tuning.m_HookLength; + NewPos = m_Pos + normalize(NewPos-m_Pos) * m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookLength; m_pReset = true; } // make sure that the hook doesn't go though the ground bool GoingToHitGround = false; bool GoingToRetract = false; - int Hit = m_pCollision->IntersectLine(m_HookPos, NewPos, &NewPos, 0, true); + bool GoingThroughTele = false; + int teleNr = 0; + int Hit = m_pCollision->IntersectLineTeleHook(m_HookPos, NewPos, &NewPos, 0, &teleNr, true); + + //m_NewHook = false; + if(Hit) { if(Hit&CCollision::COLFLAG_NOHOOK) GoingToRetract = true; + else if (Hit&CCollision::COLFLAG_TELE) + GoingThroughTele = true; else GoingToHitGround = true; m_pReset = true; } // Check against other players first - if(m_pWorld && m_pWorld->m_Tuning.m_PlayerHooking) + if(this->m_Hook && m_pWorld && m_pWorld->m_Tuning[g_Config.m_ClDummy].m_PlayerHooking) { float Distance = 0.0f; for(int i = 0; i < MAX_CLIENTS; i++) @@ -287,7 +326,21 @@ void CCharacterCore::Tick(bool UseInput) m_HookState = HOOK_RETRACT_START; } - m_HookPos = NewPos; + if(GoingThroughTele && m_pTeleOuts && m_pTeleOuts->size() && (*m_pTeleOuts)[teleNr-1].size()) + { + m_TriggeredEvents = 0; + m_HookedPlayer = -1; + + m_NewHook = true; + int Num = (*m_pTeleOuts)[teleNr-1].size(); + m_HookPos = (*m_pTeleOuts)[teleNr-1][(!Num)?Num:rand() % Num]+TargetDirection*PhysSize*1.5f; + m_HookDir = TargetDirection; + m_HookTeleBase = m_HookPos; + } + else + { + m_HookPos = NewPos; + } } } @@ -314,7 +367,7 @@ void CCharacterCore::Tick(bool UseInput) // don't do this hook rutine when we are hook to a player if(m_HookedPlayer == -1 && distance(m_HookPos, m_Pos) > 46.0f) { - vec2 HookVel = normalize(m_HookPos-m_Pos)*m_pWorld->m_Tuning.m_HookDragAccel; + vec2 HookVel = normalize(m_HookPos-m_Pos)*m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookDragAccel; // the hook as more power to drag you up then down. // this makes it easier to get on top of an platform if(HookVel.y > 0) @@ -330,7 +383,7 @@ void CCharacterCore::Tick(bool UseInput) vec2 NewVel = m_Vel+HookVel; // check if we are under the legal limit for the hook - if(length(NewVel) < m_pWorld->m_Tuning.m_HookDragSpeed || length(NewVel) < length(m_Vel)) + if(length(NewVel) < m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookDragSpeed || length(NewVel) < length(m_Vel)) m_Vel = NewVel; // no problem. apply } @@ -362,7 +415,7 @@ void CCharacterCore::Tick(bool UseInput) // handle player <-> player collision float Distance = distance(m_Pos, pCharCore->m_Pos); vec2 Dir = normalize(m_Pos - pCharCore->m_Pos); - if(m_pWorld->m_Tuning.m_PlayerCollision && Distance < PhysSize*1.25f && Distance > 0.0f) + if(pCharCore->m_Collision && this->m_Collision && m_pWorld->m_Tuning[g_Config.m_ClDummy].m_PlayerCollision && Distance < PhysSize*1.25f && Distance > 0.0f) { float a = (PhysSize*1.45f - Distance); float Velocity = 0.5f; @@ -377,12 +430,12 @@ void CCharacterCore::Tick(bool UseInput) } // handle hook influence - if(m_HookedPlayer == i && m_pWorld->m_Tuning.m_PlayerHooking) + if(m_Hook && m_HookedPlayer == i && m_pWorld->m_Tuning[g_Config.m_ClDummy].m_PlayerHooking) { if(Distance > PhysSize*1.50f) // TODO: fix tweakable variable { - float Accel = m_pWorld->m_Tuning.m_HookDragAccel * (Distance/m_pWorld->m_Tuning.m_HookLength); - float DragSpeed = m_pWorld->m_Tuning.m_HookDragSpeed; + float Accel = m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookDragAccel * (Distance/m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookLength); + float DragSpeed = m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookDragSpeed; // add force to the hooked player vec2 Temp = pCharCore->m_Vel; @@ -413,6 +466,120 @@ void CCharacterCore::Tick(bool UseInput) } } } + + if (m_HookState != HOOK_FLYING) + { + m_NewHook = false; + } + + int Index = MapIndex; + if(g_Config.m_ClPredictDDRace && IsClient && m_pCollision->IsSpeedup(Index)) + { + vec2 Direction, MaxVel, TempVel = m_Vel; + int Force, MaxSpeed = 0; + float TeeAngle, SpeederAngle, DiffAngle, SpeedLeft, TeeSpeed; + m_pCollision->GetSpeedup(Index, &Direction, &Force, &MaxSpeed); + if(Force == 255 && MaxSpeed) + { + m_Vel = Direction * (MaxSpeed/5); + } + else + { + if(MaxSpeed > 0 && MaxSpeed < 5) MaxSpeed = 5; + //dbg_msg("speedup tile start","Direction %f %f, Force %d, Max Speed %d", (Direction).x,(Direction).y, Force, MaxSpeed); + if(MaxSpeed > 0) + { + if(Direction.x > 0.0000001f) + SpeederAngle = -atan(Direction.y / Direction.x); + else if(Direction.x < 0.0000001f) + SpeederAngle = atan(Direction.y / Direction.x) + 2.0f * asin(1.0f); + else if(Direction.y > 0.0000001f) + SpeederAngle = asin(1.0f); + else + SpeederAngle = asin(-1.0f); + + if(SpeederAngle < 0) + SpeederAngle = 4.0f * asin(1.0f) + SpeederAngle; + + if(TempVel.x > 0.0000001f) + TeeAngle = -atan(TempVel.y / TempVel.x); + else if(TempVel.x < 0.0000001f) + TeeAngle = atan(TempVel.y / TempVel.x) + 2.0f * asin(1.0f); + else if(TempVel.y > 0.0000001f) + TeeAngle = asin(1.0f); + else + TeeAngle = asin(-1.0f); + + if(TeeAngle < 0) + TeeAngle = 4.0f * asin(1.0f) + TeeAngle; + + TeeSpeed = sqrt(pow(TempVel.x, 2) + pow(TempVel.y, 2)); + + DiffAngle = SpeederAngle - TeeAngle; + SpeedLeft = MaxSpeed / 5.0f - cos(DiffAngle) * TeeSpeed; + //dbg_msg("speedup tile debug","MaxSpeed %i, TeeSpeed %f, SpeedLeft %f, SpeederAngle %f, TeeAngle %f", MaxSpeed, TeeSpeed, SpeedLeft, SpeederAngle, TeeAngle); + if(abs(SpeedLeft) > Force && SpeedLeft > 0.0000001f) + TempVel += Direction * Force; + else if(abs(SpeedLeft) > Force) + TempVel += Direction * -Force; + else + TempVel += Direction * SpeedLeft; + } + else + TempVel += Direction * Force; + + + if(TempVel.x > 0 && ((this->m_TileIndex == TILE_STOP && this->m_TileFlags == ROTATION_270) || (this->m_TileIndexL == TILE_STOP && this->m_TileFlagsL == ROTATION_270) || (this->m_TileIndexL == TILE_STOPS && (this->m_TileFlagsL == ROTATION_90 || this->m_TileFlagsL ==ROTATION_270)) || (this->m_TileIndexL == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_270) || (m_TileFIndexL == TILE_STOP && m_TileFFlagsL == ROTATION_270) || (m_TileFIndexL == TILE_STOPS && (m_TileFFlagsL == ROTATION_90 || m_TileFFlagsL == ROTATION_270)) || (m_TileFIndexL == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_270) || (m_TileSIndexL == TILE_STOP && m_TileSFlagsL == ROTATION_270) || (m_TileSIndexL == TILE_STOPS && (m_TileSFlagsL == ROTATION_90 || m_TileSFlagsL == ROTATION_270)) || (m_TileSIndexL == TILE_STOPA))) + TempVel.x = 0; + if(TempVel.x < 0 && ((this->m_TileIndex == TILE_STOP && this->m_TileFlags == ROTATION_90) || (this->m_TileIndexR == TILE_STOP && this->m_TileFlagsR == ROTATION_90) || (this->m_TileIndexR == TILE_STOPS && (this->m_TileFlagsR == ROTATION_90 || this->m_TileFlagsR == ROTATION_270)) || (this->m_TileIndexR == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_90) || (m_TileFIndexR == TILE_STOP && m_TileFFlagsR == ROTATION_90) || (m_TileFIndexR == TILE_STOPS && (m_TileFFlagsR == ROTATION_90 || m_TileFFlagsR == ROTATION_270)) || (m_TileFIndexR == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_90) || (m_TileSIndexR == TILE_STOP && m_TileSFlagsR == ROTATION_90) || (m_TileSIndexR == TILE_STOPS && (m_TileSFlagsR == ROTATION_90 || m_TileSFlagsR == ROTATION_270)) || (m_TileSIndexR == TILE_STOPA))) + TempVel.x = 0; + if(TempVel.y < 0 && ((this->m_TileIndex == TILE_STOP && this->m_TileFlags == ROTATION_180) || (this->m_TileIndexB == TILE_STOP && this->m_TileFlagsB == ROTATION_180) || (this->m_TileIndexB == TILE_STOPS && (this->m_TileFlagsB == ROTATION_0 || this->m_TileFlagsB == ROTATION_180)) || (this->m_TileIndexB == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_180) || (m_TileFIndexB == TILE_STOP && m_TileFFlagsB == ROTATION_180) || (m_TileFIndexB == TILE_STOPS && (m_TileFFlagsB == ROTATION_0 || m_TileFFlagsB == ROTATION_180)) || (m_TileFIndexB == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_180) || (m_TileSIndexB == TILE_STOP && m_TileSFlagsB == ROTATION_180) || (m_TileSIndexB == TILE_STOPS && (m_TileSFlagsB == ROTATION_0 || m_TileSFlagsB == ROTATION_180)) || (m_TileSIndexB == TILE_STOPA))) + TempVel.y = 0; + if(TempVel.y > 0 && ((this->m_TileIndex == TILE_STOP && this->m_TileFlags == ROTATION_0) || (this->m_TileIndexT == TILE_STOP && this->m_TileFlagsT == ROTATION_0) || (this->m_TileIndexT == TILE_STOPS && (this->m_TileFlagsT == ROTATION_0 || this->m_TileFlagsT == ROTATION_180)) || (this->m_TileIndexT == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_0) || (m_TileFIndexT == TILE_STOP && m_TileFFlagsT == ROTATION_0) || (m_TileFIndexT == TILE_STOPS && (m_TileFFlagsT == ROTATION_0 || m_TileFFlagsT == ROTATION_180)) || (m_TileFIndexT == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_0) || (m_TileSIndexT == TILE_STOP && m_TileSFlagsT == ROTATION_0) || (m_TileSIndexT == TILE_STOPS && (m_TileSFlagsT == ROTATION_0 || m_TileSFlagsT == ROTATION_180)) || (m_TileSIndexT == TILE_STOPA))) + TempVel.y = 0; + + + m_Vel = TempVel; + //dbg_msg("speedup tile end","(Direction*Force) %f %f m_Vel%f %f",(Direction*Force).x,(Direction*Force).y,m_Vel.x,m_Vel.y); + //dbg_msg("speedup tile end","Direction %f %f, Force %d, Max Speed %d", (Direction).x,(Direction).y, Force, MaxSpeed); + } + } + + if(IsClient && UseInput && (m_Input.m_Fire&1) && m_ActiveWeapon == WEAPON_GUN) { + m_Vel += TargetDirection * -1.0f * (m_pWorld->m_Tuning[g_Config.m_ClDummy].m_JetpackStrength / 100.0f / 6.11f); + } + if(g_Config.m_ClPredictDDRace && IsClient) + { + if(((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_270) || (m_TileIndexL == TILE_STOP && m_TileFlagsL == ROTATION_270) || (m_TileIndexL == TILE_STOPS && (m_TileFlagsL == ROTATION_90 || m_TileFlagsL ==ROTATION_270)) || (m_TileIndexL == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_270) || (m_TileFIndexL == TILE_STOP && m_TileFFlagsL == ROTATION_270) || (m_TileFIndexL == TILE_STOPS && (m_TileFFlagsL == ROTATION_90 || m_TileFFlagsL == ROTATION_270)) || (m_TileFIndexL == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_270) || (m_TileSIndexL == TILE_STOP && m_TileSFlagsL == ROTATION_270) || (m_TileSIndexL == TILE_STOPS && (m_TileSFlagsL == ROTATION_90 || m_TileSFlagsL == ROTATION_270)) || (m_TileSIndexL == TILE_STOPA)) && m_Vel.x > 0) + { + if((int)m_pCollision->GetPos(MapIndexL).x < (int)m_Pos.x) + m_Pos = PrevPos; + m_Vel.x = 0; + } + if(((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_90) || (m_TileIndexR == TILE_STOP && m_TileFlagsR == ROTATION_90) || (m_TileIndexR == TILE_STOPS && (m_TileFlagsR == ROTATION_90 || m_TileFlagsR == ROTATION_270)) || (m_TileIndexR == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_90) || (m_TileFIndexR == TILE_STOP && m_TileFFlagsR == ROTATION_90) || (m_TileFIndexR == TILE_STOPS && (m_TileFFlagsR == ROTATION_90 || m_TileFFlagsR == ROTATION_270)) || (m_TileFIndexR == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_90) || (m_TileSIndexR == TILE_STOP && m_TileSFlagsR == ROTATION_90) || (m_TileSIndexR == TILE_STOPS && (m_TileSFlagsR == ROTATION_90 || m_TileSFlagsR == ROTATION_270)) || (m_TileSIndexR == TILE_STOPA)) && m_Vel.x < 0) + { + if((int)m_pCollision->GetPos(MapIndexR).x) + if((int)m_pCollision->GetPos(MapIndexR).x < (int)m_Pos.x) + m_Pos = PrevPos; + m_Vel.x = 0; + } + if(((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_180) || (m_TileIndexB == TILE_STOP && m_TileFlagsB == ROTATION_180) || (m_TileIndexB == TILE_STOPS && (m_TileFlagsB == ROTATION_0 || m_TileFlagsB == ROTATION_180)) || (m_TileIndexB == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_180) || (m_TileFIndexB == TILE_STOP && m_TileFFlagsB == ROTATION_180) || (m_TileFIndexB == TILE_STOPS && (m_TileFFlagsB == ROTATION_0 || m_TileFFlagsB == ROTATION_180)) || (m_TileFIndexB == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_180) || (m_TileSIndexB == TILE_STOP && m_TileSFlagsB == ROTATION_180) || (m_TileSIndexB == TILE_STOPS && (m_TileSFlagsB == ROTATION_0 || m_TileSFlagsB == ROTATION_180)) || (m_TileSIndexB == TILE_STOPA)) && m_Vel.y < 0) + { + if((int)m_pCollision->GetPos(MapIndexB).y) + if((int)m_pCollision->GetPos(MapIndexB).y < (int)m_Pos.y) + m_Pos = PrevPos; + m_Vel.y = 0; + } + if(((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_0) || (m_TileIndexT == TILE_STOP && m_TileFlagsT == ROTATION_0) || (m_TileIndexT == TILE_STOPS && (m_TileFlagsT == ROTATION_0 || m_TileFlagsT == ROTATION_180)) || (m_TileIndexT == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_0) || (m_TileFIndexT == TILE_STOP && m_TileFFlagsT == ROTATION_0) || (m_TileFIndexT == TILE_STOPS && (m_TileFFlagsT == ROTATION_0 || m_TileFFlagsT == ROTATION_180)) || (m_TileFIndexT == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_0) || (m_TileSIndexT == TILE_STOP && m_TileSFlagsT == ROTATION_0) || (m_TileSIndexT == TILE_STOPS && (m_TileSFlagsT == ROTATION_0 || m_TileSFlagsT == ROTATION_180)) || (m_TileSIndexT == TILE_STOPA)) && m_Vel.y > 0) + { + if((int)m_pCollision->GetPos(MapIndexT).y) + if((int)m_pCollision->GetPos(MapIndexT).y < (int)m_Pos.y) + m_Pos = PrevPos; + m_Vel.y = 0; + m_Jumped = 0; + m_JumpedTotal = 0; + } + } } // clamp the velocity to something sane @@ -422,16 +589,29 @@ void CCharacterCore::Tick(bool UseInput) void CCharacterCore::Move() { - float RampValue = VelocityRamp(length(m_Vel)*50, m_pWorld->m_Tuning.m_VelrampStart, m_pWorld->m_Tuning.m_VelrampRange, m_pWorld->m_Tuning.m_VelrampCurvature); + float RampValue = VelocityRamp(length(m_Vel)*50, m_pWorld->m_Tuning[g_Config.m_ClDummy].m_VelrampStart, m_pWorld->m_Tuning[g_Config.m_ClDummy].m_VelrampRange, m_pWorld->m_Tuning[g_Config.m_ClDummy].m_VelrampCurvature); m_Vel.x = m_Vel.x*RampValue; vec2 NewPos = m_Pos; + + vec2 OldVel = m_Vel; m_pCollision->MoveBox(&NewPos, &m_Vel, vec2(28.0f, 28.0f), 0); + m_Colliding = 0; + if(m_Vel.x < 0.001 && m_Vel.x > -0.001) + { + if(OldVel.x > 0) + m_Colliding = 1; + else if(OldVel.x < 0) + m_Colliding = 2; + } + else + m_LeftWall = true; + m_Vel.x = m_Vel.x*(1.0f/RampValue); - if(m_pWorld && m_pWorld->m_Tuning.m_PlayerCollision) + if(m_pWorld && m_pWorld->m_Tuning[g_Config.m_ClDummy].m_PlayerCollision && this->m_Collision) { // check player collision float Distance = distance(m_Pos, NewPos); @@ -444,7 +624,7 @@ void CCharacterCore::Move() for(int p = 0; p < MAX_CLIENTS; p++) { CCharacterCore *pCharCore = m_pWorld->m_apCharacters[p]; - if(!pCharCore || pCharCore == this || (m_Id != -1 && !m_pTeams->CanCollide(m_Id, p))) + if(!pCharCore || pCharCore == this || (m_Id != -1 && !m_pTeams->CanCollide(m_Id, p)) || !pCharCore->m_Collision) continue; float D = distance(Pos, pCharCore->m_Pos); if(D < 28.0f && D > 0.0f) @@ -473,17 +653,17 @@ void CCharacterCore::Move() void CCharacterCore::Write(CNetObj_CharacterCore *pObjCore) { - pObjCore->m_X = round(m_Pos.x); - pObjCore->m_Y = round(m_Pos.y); + pObjCore->m_X = round_to_int(m_Pos.x); + pObjCore->m_Y = round_to_int(m_Pos.y); - pObjCore->m_VelX = round(m_Vel.x*256.0f); - pObjCore->m_VelY = round(m_Vel.y*256.0f); + pObjCore->m_VelX = round_to_int(m_Vel.x*256.0f); + pObjCore->m_VelY = round_to_int(m_Vel.y*256.0f); pObjCore->m_HookState = m_HookState; pObjCore->m_HookTick = m_HookTick; - pObjCore->m_HookX = round(m_HookPos.x); - pObjCore->m_HookY = round(m_HookPos.y); - pObjCore->m_HookDx = round(m_HookDir.x*256.0f); - pObjCore->m_HookDy = round(m_HookDir.y*256.0f); + pObjCore->m_HookX = round_to_int(m_HookPos.x); + pObjCore->m_HookY = round_to_int(m_HookPos.y); + pObjCore->m_HookDx = round_to_int(m_HookDir.x*256.0f); + pObjCore->m_HookDy = round_to_int(m_HookDir.y*256.0f); pObjCore->m_HookedPlayer = m_HookedPlayer; pObjCore->m_Jumped = m_Jumped; pObjCore->m_Direction = m_Direction; @@ -520,7 +700,7 @@ void CCharacterCore::Quantize() bool CCharacterCore::IsRightTeam(int MapIndex) { if(Collision()->m_pSwitchers) - if(m_pTeams->Team(m_Id) != TEAM_SUPER) + if(m_pTeams->Team(m_Id) != (m_pTeams->m_IsDDRace16 ? VANILLA_TEAM_SUPER : TEAM_SUPER)) return Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndex)].m_Status[m_pTeams->Team(m_Id)]; return false; } diff --git a/src/game/gamecore.h b/src/game/gamecore.h index 064da184e2..641e6e3942 100644 --- a/src/game/gamecore.h +++ b/src/game/gamecore.h @@ -6,6 +6,9 @@ #include #include +#include +#include + #include #include "collision.h" #include @@ -161,6 +164,7 @@ enum COREEVENT_HOOK_ATTACH_GROUND=0x10, COREEVENT_HOOK_HIT_NOHOOK=0x20, COREEVENT_HOOK_RETRACT=0x40, + //COREEVENT_HOOK_TELE=0x80, }; class CWorldCore @@ -171,25 +175,37 @@ class CWorldCore mem_zero(m_apCharacters, sizeof(m_apCharacters)); } - CTuningParams m_Tuning; + CTuningParams m_Tuning[2]; class CCharacterCore *m_apCharacters[MAX_CLIENTS]; }; class CCharacterCore { + friend class CCharacter; CWorldCore *m_pWorld; CCollision *m_pCollision; + std::map > *m_pTeleOuts; public: vec2 m_Pos; vec2 m_Vel; + bool m_Hook; + bool m_Collision; vec2 m_HookPos; vec2 m_HookDir; + vec2 m_HookTeleBase; int m_HookTick; int m_HookState; int m_HookedPlayer; + int m_ActiveWeapon; + + bool m_NewHook; + vec2 m_NewHookPos; + vec2 m_NewHookDir; int m_Jumped; + int m_JumpedTotal; + int m_Jumps; int m_Direction; int m_Angle; @@ -198,8 +214,9 @@ class CCharacterCore int m_TriggeredEvents; void Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore* pTeams); + void Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore* pTeams, std::map > *pTeleOuts); void Reset(); - void Tick(bool UseInput); + void Tick(bool UseInput, bool IsClient); void Move(); void Read(const CNetObj_CharacterCore *pObjCore); @@ -212,6 +229,10 @@ class CCharacterCore bool m_pReset; class CCollision *Collision() { return m_pCollision; } + vec2 m_LastVel; + int m_Colliding; + bool m_LeftWall; + private: CTeamsCore* m_pTeams; diff --git a/src/game/generated/createdir.txt b/src/game/generated/createdir.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/game/layers.cpp b/src/game/layers.cpp index 28180b3761..0b3e1641d7 100644 --- a/src/game/layers.cpp +++ b/src/game/layers.cpp @@ -16,6 +16,7 @@ CLayers::CLayers() m_pSpeedupLayer = 0; m_pFrontLayer = 0; m_pSwitchLayer = 0; + m_pTuneLayer = 0; } void CLayers::Init(class IKernel *pKernel) @@ -28,6 +29,7 @@ void CLayers::Init(class IKernel *pKernel) m_pSpeedupLayer = 0; m_pFrontLayer = 0; m_pSwitchLayer = 0; + m_pTuneLayer = 0; for(int g = 0; g < NumGroups(); g++) { @@ -94,6 +96,14 @@ void CLayers::Init(class IKernel *pKernel) } m_pSwitchLayer = pTilemap; } + if(pTilemap->m_Flags&TILESLAYERFLAG_TUNE) + { + if(pTilemap->m_Version <= 2) + { + pTilemap->m_Tune = *((int*)(pTilemap) + 19); + } + m_pTuneLayer = pTilemap; + } } } } @@ -116,5 +126,6 @@ void CLayers::Dest() /*m_pTeleLayer = 0; m_pSpeedupLayer = 0; m_pFrontLayer = 0; - m_pSwitchLayer = 0;*/ + m_pSwitchLayer = 0; + m_pTuneLayer = 0;*/ } diff --git a/src/game/layers.h b/src/game/layers.h index 0ba6727165..33540289c9 100644 --- a/src/game/layers.h +++ b/src/game/layers.h @@ -33,6 +33,7 @@ class CLayers CMapItemLayerTilemap *SpeedupLayer() const { return m_pSpeedupLayer; }; CMapItemLayerTilemap *FrontLayer() const { return m_pFrontLayer; }; CMapItemLayerTilemap *SwitchLayer() const { return m_pSwitchLayer; }; + CMapItemLayerTilemap *TuneLayer() const { return m_pTuneLayer; }; private: @@ -40,6 +41,7 @@ class CLayers CMapItemLayerTilemap *m_pSpeedupLayer; CMapItemLayerTilemap *m_pFrontLayer; CMapItemLayerTilemap *m_pSwitchLayer; + CMapItemLayerTilemap *m_pTuneLayer; }; #endif \ No newline at end of file diff --git a/src/game/mapitems.h b/src/game/mapitems.h index 4b22e82b19..8edfe4c29c 100644 --- a/src/game/mapitems.h +++ b/src/game/mapitems.h @@ -17,6 +17,7 @@ enum LAYERTYPE_TELE, LAYERTYPE_SPEEDUP, LAYERTYPE_SWITCH, + LAYERTYPE_TUNE, MAPITEMTYPE_VERSION=0, MAPITEMTYPE_INFO, @@ -35,6 +36,7 @@ enum NUM_CURVETYPES, // game layer tiles + // TODO define which Layer uses which tiles (needed for mapeditor) ENTITY_NULL=0, ENTITY_SPAWN, ENTITY_SPAWN_RED, @@ -93,12 +95,16 @@ enum TILE_NOHOOK, TILE_NOLASER, TILE_THROUGH = 6, + TILE_JUMP, TILE_FREEZE = 9, TILE_TELEINEVIL, TILE_UNFREEZE, TILE_DFREEZE, TILE_DUNFREEZE, - TILE_EHOOK_START = 17, + TILE_TELEINWEAPON, + TILE_TELEINHOOK, + TILE_WALLJUMP = 16, + TILE_EHOOK_START, TILE_EHOOK_END, TILE_HIT_START, TILE_HIT_END, @@ -120,13 +126,24 @@ enum TILE_STOP = 60, TILE_STOPS, TILE_STOPA, + TILE_TELECHECKINEVIL = 63, TILE_CP = 64, TILE_CP_F, + TILE_TUNE1 = 68, TILE_OLDLASER = 71, TILE_NPC, TILE_EHOOK, TILE_NOHIT, - TILE_NPH,//Remember to change this in collision.cpp if you add anymore tiles + TILE_NPH, + TILE_PENALTY = 79, + TILE_NPC_END = 88, + TILE_SUPER_END, + TILE_JETPACK_END, + TILE_NPH_END, + TILE_NPC_START = 104, + TILE_SUPER_START, + TILE_JETPACK_START, + TILE_NPH_START,//Remember to change this in collision.cpp if you add anymore tiles //End of higher tiles //Layers LAYER_GAME=0, @@ -134,6 +151,7 @@ enum LAYER_TELE, LAYER_SPEEDUP, LAYER_SWITCH, + LAYER_TUNE, NUM_LAYERS, //Flags TILEFLAG_VFLIP=1, @@ -152,6 +170,7 @@ enum TILESLAYERFLAG_SPEEDUP=4, TILESLAYERFLAG_FRONT=8, TILESLAYERFLAG_SWITCH=16, + TILESLAYERFLAG_TUNE=32, ENTITY_OFFSET=255-16*4, }; @@ -264,6 +283,7 @@ struct CMapItemLayerTilemap int m_Speedup; int m_Front; int m_Switch; + int m_Tune; } ; struct CMapItemLayerQuads @@ -343,4 +363,11 @@ class CDoorTile int m_Number; }; +class CTuneTile +{ +public: + unsigned char m_Number; + unsigned char m_Type; +}; + #endif diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 3971ec689c..ed7a4d36bd 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -1,6 +1,7 @@ /* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */ #include "gamecontext.h" #include +#include #include #include #include @@ -11,48 +12,42 @@ #endif bool CheckClientID(int ClientID); -char* TimerType(int TimerType); void CGameContext::ConCredits(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "credit", - "Teeworlds Team takes most of the credits also"); + "DDRaceNetwork is maintained by deen."); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "credit", - "This mod was originally created by \'3DA\'"); + "Many ideas from the great community,"); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "credit", - "Now it is maintained & re-coded by:"); + "64 player support from eeeee's ddrace64."); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "credit", - "\'[Egypt]GreYFoX@GTi\' and \'[BlackTee]den\'"); - pSelf->Console()->Print( - IConsole::OUTPUT_LEVEL_STANDARD, - "credit", - "Others Helping on the code: \'heinrich5991\', \'ravomavain\', \'Trust o_0 Aeeeh ?!\', \'noother\', \'<3 fisted <3\' & \'LemonFace\'"); + "Lots of awesome help and code by HMH and CookiMichal."); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "credit", - "Documentation: Zeta-Hoernchen & Learath2, Entities: Fisico"); + "Based on DDRace by the DDRace developers,"); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "credit", - "Code (in the past): \'3DA\' and \'Fluxid\'"); + "which is a mod of Teeworlds by the Teeworlds developers."); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "credit", - "Please check the changelog on DDRace.info."); - pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "credit", - "Also the commit log on github.com/GreYFoX/teeworlds ."); + "Check the changes on ddnet.tw"); } void CGameContext::ConInfo(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "info", - "DDRace Mod. Version: " GAME_VERSION); + "DDraceNetwork Mod. Version: " GAME_VERSION); #if defined( GIT_SHORTREV_HASH ) pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "info", "Git revision hash: " GIT_SHORTREV_HASH); #endif pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "info", - "Official site: DDRace.info"); + "Official site: ddnet.tw"); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "info", "For more Info /cmdlist"); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "info", - "Or visit DDRace.info"); + "Or visit ddnet.tw"); } void CGameContext::ConHelp(IConsole::IResult *pResult, void *pUserData) @@ -115,7 +110,7 @@ void CGameContext::ConSettings(IConsole::IResult *pResult, void *pUserData) "%s %s", g_Config.m_SvTeam == 1 ? "Teams are available on this server" : - !g_Config.m_SvTeam ? + (g_Config.m_SvTeam == 0 || g_Config.m_SvTeam == 3) ? "Teams are not available on this server" : "You have to be in a team to play on this server", /*g_Config.m_SvTeamStrict ? "and if you die in a team all of you die" : */ "and if you die in a team only you die"); @@ -179,7 +174,7 @@ void CGameContext::ConSettings(IConsole::IResult *pResult, void *pUserData) else if (str_comp(pArg, "timeout") == 0) { str_format(aBuf, sizeof(aBuf), - "The Server Timeout is currently set to %d", + "The Server Timeout is currently set to %d seconds", g_Config.m_ConnTimeout); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "settings", aBuf); @@ -235,15 +230,7 @@ void CGameContext::ConRules(IConsole::IResult *pResult, void *pUserData) if (g_Config.m_SvDDRaceRules) { pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "rules", - "No blocking."); - pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "rules", - "No insulting / spamming."); - pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "rules", - "No fun voting / vote spamming."); - pSelf->Console()->Print( - IConsole::OUTPUT_LEVEL_STANDARD, - "rules", - "Breaking any of these rules will result in a penalty, decided by server admins."); + "Be nice."); Printed = true; } if (g_Config.m_SvRulesLine1[0]) @@ -377,6 +364,37 @@ void CGameContext::ConToggleSpec(IConsole::IResult *pResult, void *pUserData) pPlayer->m_Paused = (pPlayer->m_Paused == CPlayer::PAUSED_SPEC) ? CPlayer::PAUSED_NONE : CPlayer::PAUSED_SPEC; } +void CGameContext::ConTeamTop5(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + +#if defined(CONF_SQL) + if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) + if(pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery + pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) + return; +#endif + + if (g_Config.m_SvHideScore) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "teamtop5", + "Showing the team top 5 is not allowed on this server."); + return; + } + + if (pResult->NumArguments() > 0 && pResult->GetInteger(0) >= 0) + pSelf->Score()->ShowTeamTop5(pResult, pResult->m_ClientID, pUserData, + pResult->GetInteger(0)); + else + pSelf->Score()->ShowTeamTop5(pResult, pResult->m_ClientID, pUserData); + +#if defined(CONF_SQL) + if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) + pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery = pSelf->Server()->Tick(); +#endif +} + void CGameContext::ConTop5(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; @@ -462,22 +480,206 @@ void CGameContext::ConTimes(IConsole::IResult *pResult, void *pUserData) } #endif -void CGameContext::ConRank(IConsole::IResult *pResult, void *pUserData) +void CGameContext::ConDND(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(!CheckClientID(pResult->m_ClientID)) return; + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if(!pPlayer) + return; + + if(pPlayer->m_DND) + { + pPlayer->m_DND = false; + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "dnd", "You will receive global chat and server messages"); + } + else + { + pPlayer->m_DND = true; + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "dnd", "You will not receive any further global chat and server messages"); + } +} + +void CGameContext::ConMap(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; if (!CheckClientID(pResult->m_ClientID)) return; + if (g_Config.m_SvMapVote == 0) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "map", + "Admin has disabled /map"); + return; + } + + if (pResult->NumArguments() <= 0) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "map", "Example: /map adr3 to call vote for Adrenaline 3. This means that the map name must start with 'a' and contain the characters 'd', 'r' and '3' in that order"); + return; + } + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if (!pPlayer) + return; + #if defined(CONF_SQL) - if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) - if(pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery + pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) + if(g_Config.m_SvUseSQL) + if(pPlayer->m_LastSQLQuery + pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) + return; +#endif + + pSelf->Score()->MapVote(pResult->m_ClientID, pResult->GetString(0)); + +#if defined(CONF_SQL) + if(g_Config.m_SvUseSQL) + pPlayer->m_LastSQLQuery = pSelf->Server()->Tick(); +#endif +} + +void CGameContext::ConMapPoints(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if (!pPlayer) + return; + +#if defined(CONF_SQL) + if(g_Config.m_SvUseSQL) + if(pPlayer->m_LastSQLQuery + pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) + return; +#endif + + if (pResult->NumArguments() > 0) + pSelf->Score()->MapPoints(pResult->m_ClientID, pResult->GetString(0)); + else + pSelf->Score()->MapPoints(pResult->m_ClientID, g_Config.m_SvMap); + +#if defined(CONF_SQL) + if(g_Config.m_SvUseSQL) + pPlayer->m_LastSQLQuery = pSelf->Server()->Tick(); +#endif +} + +void CGameContext::ConSave(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if (!pPlayer) + return; + +#if defined(CONF_SQL) + if(!g_Config.m_SvSaveGames) + { + pSelf->SendChatTarget(pResult->m_ClientID, "Save-function is disabled on this server"); + return; + } + + if(g_Config.m_SvUseSQL) + if(pPlayer->m_LastSQLQuery + pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) + return; +#endif + + int Team = ((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.m_Core.Team(pResult->m_ClientID); + pSelf->Score()->SaveTeam(Team, pResult->GetString(0), pResult->m_ClientID); + +#if defined(CONF_SQL) + if(g_Config.m_SvUseSQL) + pPlayer->m_LastSQLQuery = pSelf->Server()->Tick(); +#endif +} + +void CGameContext::ConLoad(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if (!pPlayer) + return; + +#if defined(CONF_SQL) + if(!g_Config.m_SvSaveGames) + { + pSelf->SendChatTarget(pResult->m_ClientID, "Save-function is disabled on this server"); + return; + } + + if(g_Config.m_SvUseSQL) + if(pPlayer->m_LastSQLQuery + pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) return; #endif + if (pResult->NumArguments() > 0) + pSelf->Score()->LoadTeam(pResult->GetString(0), pResult->m_ClientID); + else + return; + +#if defined(CONF_SQL) + if(g_Config.m_SvUseSQL) + pPlayer->m_LastSQLQuery = pSelf->Server()->Tick(); +#endif +} + +void CGameContext::ConTeamRank(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; if (!pPlayer) return; +#if defined(CONF_SQL) + if(g_Config.m_SvUseSQL) + if(pPlayer->m_LastSQLQuery + pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) + return; +#endif + + if (pResult->NumArguments() > 0) + if (!g_Config.m_SvHideScore) + pSelf->Score()->ShowTeamRank(pResult->m_ClientID, pResult->GetString(0), + true); + else + pSelf->Console()->Print( + IConsole::OUTPUT_LEVEL_STANDARD, + "teamrank", + "Showing the team rank of other players is not allowed on this server."); + else + pSelf->Score()->ShowTeamRank(pResult->m_ClientID, + pSelf->Server()->ClientName(pResult->m_ClientID)); + +#if defined(CONF_SQL) + if(g_Config.m_SvUseSQL) + pPlayer->m_LastSQLQuery = pSelf->Server()->Tick(); +#endif +} + +void CGameContext::ConRank(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if (!pPlayer) + return; + +#if defined(CONF_SQL) + if(g_Config.m_SvUseSQL) + if(pPlayer->m_LastSQLQuery + pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) + return; +#endif + if (pResult->NumArguments() > 0) if (!g_Config.m_SvHideScore) pSelf->Score()->ShowRank(pResult->m_ClientID, pResult->GetString(0), @@ -492,11 +694,55 @@ void CGameContext::ConRank(IConsole::IResult *pResult, void *pUserData) pSelf->Server()->ClientName(pResult->m_ClientID)); #if defined(CONF_SQL) - if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) - pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery = pSelf->Server()->Tick(); + if(g_Config.m_SvUseSQL) + pPlayer->m_LastSQLQuery = pSelf->Server()->Tick(); #endif } +void CGameContext::ConLockTeam(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + + int Team = ((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.m_Core.Team(pResult->m_ClientID); + + bool Lock = ((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.TeamLocked(Team); + + if (pResult->NumArguments() > 0) + Lock = !pResult->GetInteger(0); + + if(Team > TEAM_FLOCK && Team < TEAM_SUPER) + { + char aBuf[512]; + if(Lock) + { + ((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.SetTeamLock(Team, false); + + str_format(aBuf, sizeof(aBuf), "'%s' unlocked your team.", pSelf->Server()->ClientName(pResult->m_ClientID)); + + for (int i = 0; i < MAX_CLIENTS; i++) + if (((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.m_Core.Team(i) == Team) + pSelf->SendChatTarget(i, aBuf); + } + else + { + ((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.SetTeamLock(Team, true); + + str_format(aBuf, sizeof(aBuf), "'%s' locked your team. After the race started killing will kill everyone in your team.", pSelf->Server()->ClientName(pResult->m_ClientID)); + + for (int i = 0; i < MAX_CLIENTS; i++) + if (((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.m_Core.Team(i) == Team) + pSelf->SendChatTarget(i, aBuf); + } + } + else + pSelf->Console()->Print( + IConsole::OUTPUT_LEVEL_STANDARD, + "print", + "This team can't be locked"); +} + void CGameContext::ConJoinTeam(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; @@ -504,6 +750,8 @@ void CGameContext::ConJoinTeam(IConsole::IResult *pResult, void *pUserData) return; CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if (!pPlayer) + return; if (pSelf->m_VoteCloseTime && pSelf->m_VoteCreator == pResult->m_ClientID) { @@ -513,13 +761,13 @@ void CGameContext::ConJoinTeam(IConsole::IResult *pResult, void *pUserData) "You are running a vote please try again after the vote is done!"); return; } - else if (g_Config.m_SvTeam == 0) + else if (g_Config.m_SvTeam == 0 || g_Config.m_SvTeam == 3) { pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "join", "Admin has disabled teams"); return; } - else if (g_Config.m_SvTeam == 2 && pResult->GetInteger(0) == 0 && pPlayer->GetCharacter()->m_LastStartWarning < pSelf->Server()->Tick() - 3 * pSelf->Server()->TickSpeed()) + else if (g_Config.m_SvTeam == 2 && pResult->GetInteger(0) == 0 && pPlayer->GetCharacter() && pPlayer->GetCharacter()->m_LastStartWarning < pSelf->Server()->Tick() - 3 * pSelf->Server()->TickSpeed()) { pSelf->Console()->Print( IConsole::OUTPUT_LEVEL_STANDARD, @@ -545,6 +793,17 @@ void CGameContext::ConJoinTeam(IConsole::IResult *pResult, void *pUserData) pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "join", "You can\'t change teams that fast!"); } + else if(pResult->GetInteger(0) > 0 && pResult->GetInteger(0) < MAX_CLIENTS && ((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.TeamLocked(pResult->GetInteger(0))) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "join", + "This team is locked using /lock. Only members of the team can unlock it using /lock."); + } + else if(pResult->GetInteger(0) > 0 && pResult->GetInteger(0) < MAX_CLIENTS && ((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.Count(pResult->GetInteger(0)) >= g_Config.m_SvTeamMaxSize) + { + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "This team already has the maximum allowed size of %d players", g_Config.m_SvTeamMaxSize); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "join", aBuf); + } else if (((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.SetCharacterTeam( pPlayer->GetCID(), pResult->GetInteger(0))) { @@ -606,6 +865,16 @@ void CGameContext::ConMe(IConsole::IResult *pResult, void *pUserData) "/me is disabled on this server, admin can enable it by using sv_slash_me"); } +void CGameContext::ConConverse(IConsole::IResult *pResult, void *pUserData) +{ + // This will never be called +} + +void CGameContext::ConWhisper(IConsole::IResult *pResult, void *pUserData) +{ + // This will never be called +} + void CGameContext::ConSetEyeEmote(IConsole::IResult *pResult, void *pUserData) { @@ -700,6 +969,21 @@ void CGameContext::ConEyeEmote(IConsole::IResult *pResult, void *pUserData) } } +void CGameContext::ConNinjaJetpack(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if (!pPlayer) + return; + if (pResult->NumArguments()) + pPlayer->m_NinjaJetpack = pResult->GetInteger(0); + else + pPlayer->m_NinjaJetpack = !pPlayer->m_NinjaJetpack; +} + void CGameContext::ConShowOthers(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; @@ -711,18 +995,10 @@ void CGameContext::ConShowOthers(IConsole::IResult *pResult, void *pUserData) return; if (g_Config.m_SvShowOthers) { - if (pPlayer->m_IsUsingDDRaceClient) - { - if (pResult->NumArguments()) - pPlayer->m_ShowOthers = pResult->GetInteger(0); - else - pPlayer->m_ShowOthers = !pPlayer->m_ShowOthers; - } + if (pResult->NumArguments()) + pPlayer->m_ShowOthers = pResult->GetInteger(0); else - pSelf->Console()->Print( - IConsole::OUTPUT_LEVEL_STANDARD, - "showotherschat", - "Showing players from other teams is only available with DDRace Client, http://DDRace.info"); + pPlayer->m_ShowOthers = !pPlayer->m_ShowOthers; } else pSelf->Console()->Print( @@ -731,6 +1007,22 @@ void CGameContext::ConShowOthers(IConsole::IResult *pResult, void *pUserData) "Showing players from other teams is disabled by the server admin"); } +void CGameContext::ConShowAll(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if (!pPlayer) + return; + + if (pResult->NumArguments()) + pPlayer->m_ShowAll = pResult->GetInteger(0); + else + pPlayer->m_ShowAll = !pPlayer->m_ShowAll; +} + bool CheckClientID(int ClientID) { dbg_assert(ClientID >= 0 || ClientID < MAX_CLIENTS, @@ -740,18 +1032,33 @@ bool CheckClientID(int ClientID) return true; } -char* TimerType(int TimerType) -{ - char msg[3][128] = {"game/round timer.", "broadcast.", "both game/round timer and broadcast."}; - return msg[TimerType]; -} void CGameContext::ConSayTime(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; if (!CheckClientID(pResult->m_ClientID)) return; - CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + int ClientID; + char aBufname[MAX_NAME_LENGTH]; + + if (pResult->NumArguments() > 0) + { + for(ClientID = 0; ClientID < MAX_CLIENTS; ClientID++) + if (str_comp(pResult->GetString(0), pSelf->Server()->ClientName(ClientID)) == 0) + break; + + if(ClientID == MAX_CLIENTS) + return; + + str_format(aBufname, sizeof(aBufname), "%s's", pSelf->Server()->ClientName(ClientID)); + } + else + { + str_copy(aBufname, "Your", sizeof(aBufname)); + ClientID = pResult->m_ClientID; + } + + CPlayer *pPlayer = pSelf->m_apPlayers[ClientID]; if (!pPlayer) return; CCharacter* pChr = pPlayer->GetCharacter(); @@ -763,7 +1070,8 @@ void CGameContext::ConSayTime(IConsole::IResult *pResult, void *pUserData) char aBuftime[64]; int IntTime = (int) ((float) (pSelf->Server()->Tick() - pChr->m_StartTime) / ((float) pSelf->Server()->TickSpeed())); - str_format(aBuftime, sizeof(aBuftime), "Your Time is %s%d:%s%d", + str_format(aBuftime, sizeof(aBuftime), "%s time is %s%d:%s%d", + aBufname, ((IntTime / 60) > 9) ? "" : "0", IntTime / 60, ((IntTime % 60) > 9) ? "" : "0", IntTime % 60); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "time", aBuftime); @@ -811,7 +1119,7 @@ void CGameContext::ConTime(IConsole::IResult *pResult, void *pUserData) char aBuftime[64]; int IntTime = (int) ((float) (pSelf->Server()->Tick() - pChr->m_StartTime) / ((float) pSelf->Server()->TickSpeed())); - str_format(aBuftime, sizeof(aBuftime), "Your Time is %s%d:%s%d", + str_format(aBuftime, sizeof(aBuftime), "Your time is %s%d:%s%d", ((IntTime / 60) > 9) ? "" : "0", IntTime / 60, ((IntTime % 60) > 9) ? "" : "0", IntTime % 60); pSelf->SendBroadcast(aBuftime, pResult->m_ClientID); @@ -828,9 +1136,10 @@ void CGameContext::ConSetTimerType(IConsole::IResult *pResult, void *pUserData) if (!pPlayer) return; + const char msg[3][128] = {"game/round timer.", "broadcast.", "both game/round timer and broadcast."}; char aBuf[128]; if(pPlayer->m_TimerType <= 2 && pPlayer->m_TimerType >= 0) - str_format(aBuf, sizeof(aBuf), "Timer is displayed in", TimerType(pPlayer->m_TimerType)); + str_format(aBuf, sizeof(aBuf), "Timer is displayed in", msg[pPlayer->m_TimerType]); else if(pPlayer->m_TimerType == 3) str_format(aBuf, sizeof(aBuf), "Timer isn't displayed."); @@ -857,3 +1166,64 @@ void CGameContext::ConSetTimerType(IConsole::IResult *pResult, void *pUserData) pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD,"timer",aBuf); } +#if defined(CONF_SQL) +void CGameContext::ConPoints(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + + if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) + if(pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery + pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) + return; + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if (!pPlayer) + return; + + if (pResult->NumArguments() > 0) + if (!g_Config.m_SvHideScore) + pSelf->Score()->ShowPoints(pResult->m_ClientID, pResult->GetString(0), + true); + else + pSelf->Console()->Print( + IConsole::OUTPUT_LEVEL_STANDARD, + "points", + "Showing the global points of other players is not allowed on this server."); + else + pSelf->Score()->ShowPoints(pResult->m_ClientID, + pSelf->Server()->ClientName(pResult->m_ClientID)); + + if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) + pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery = pSelf->Server()->Tick(); +} +#endif + +#if defined(CONF_SQL) +void CGameContext::ConTopPoints(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + + if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) + if(pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery + pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) + return; + + if (g_Config.m_SvHideScore) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "toppoints", + "Showing the global top points is not allowed on this server."); + return; + } + + if (pResult->NumArguments() > 0 && pResult->GetInteger(0) >= 0) + pSelf->Score()->ShowTopPoints(pResult, pResult->m_ClientID, pUserData, + pResult->GetInteger(0)); + else + pSelf->Score()->ShowTopPoints(pResult, pResult->m_ClientID, pUserData); + + if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) + pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery = pSelf->Server()->Tick(); +} +#endif diff --git a/src/game/server/ddracechat.h b/src/game/server/ddracechat.h index af8a4f54e6..ff8dedea5f 100644 --- a/src/game/server/ddracechat.h +++ b/src/game/server/ddracechat.h @@ -13,20 +13,35 @@ CHAT_COMMAND("settings", "?s", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSettings, this, " CHAT_COMMAND("help", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConHelp, this, "Shows help to command r, general help if left blank") CHAT_COMMAND("info", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConInfo, this, "Shows info about this server") CHAT_COMMAND("me", "r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConMe, this, "Like the famous irc command '/me says hi' will display ' says hi'") +CHAT_COMMAND("w", "sr", CFGFLAG_CHAT|CFGFLAG_SERVER, ConWhisper, this, "Whisper something to someone (private message)") +CHAT_COMMAND("whisper", "sr", CFGFLAG_CHAT|CFGFLAG_SERVER, ConWhisper, this, "Whisper something to someone (private message)") +CHAT_COMMAND("c", "r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConConverse, this, "Converse with the last person you whispered to (private message)"); +CHAT_COMMAND("converse", "r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConConverse, this, "Converse with the last person you whispered to (private message)"); CHAT_COMMAND("pause", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTogglePause, this, "Toggles pause (if not activated on the server, it toggles spec)") CHAT_COMMAND("spec", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConToggleSpec, this, "Toggles spec") +CHAT_COMMAND("dnd", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConDND, this, "Toggle Do Not Disturb (no chat and server messages)") +CHAT_COMMAND("mapinfo", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConMapPoints, this, "Show info about the map with name r gives (current map by default)") +CHAT_COMMAND("save", "s", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSave, this, "Save team with code s") +CHAT_COMMAND("load", "s", CFGFLAG_CHAT|CFGFLAG_SERVER, ConLoad, this, "Load with code s") +CHAT_COMMAND("map", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConMap, this, "Vote a map by name") +CHAT_COMMAND("rankteam", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTeamRank, this, "Shows the team rank of player with name r (your team rank by default)") CHAT_COMMAND("rank", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConRank, this, "Shows the rank of player with name r (your rank by default)") CHAT_COMMAND("rules", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConRules, this, "Shows the server rules") CHAT_COMMAND("team", "?i", CFGFLAG_CHAT|CFGFLAG_SERVER, ConJoinTeam, this, "Lets you join team i (shows your team if left blank)") +CHAT_COMMAND("lock", "?i", CFGFLAG_CHAT|CFGFLAG_SERVER, ConLockTeam, this, "Lock team so noone else can join it") +CHAT_COMMAND("top5team", "?i", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTeamTop5, this, "Shows five team ranks of the ladder beginning with rank i (1 by default)") CHAT_COMMAND("top5", "?i", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTop5, this, "Shows five ranks of the ladder beginning with rank i (1 by default)") CHAT_COMMAND("showothers", "?i", CFGFLAG_CHAT|CFGFLAG_SERVER, ConShowOthers, this, "Whether to showplayers from other teams or not (off by default), optional i = 0 for off else for on") -CHAT_COMMAND("saytime", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSayTime, this, "Privately messages you your current time in this current running race") +CHAT_COMMAND("ninjajetpack", "?i", CFGFLAG_CHAT|CFGFLAG_SERVER, ConNinjaJetpack, this, "Whether to showplayers from other teams or not (off by default), optional i = 0 for off else for on") +CHAT_COMMAND("saytime", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSayTime, this, "Privately messages someone's current time in this current running race (your time by default)") CHAT_COMMAND("saytimeall", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSayTimeAll, this, "Publicly messages everyone your current time in this current running race") CHAT_COMMAND("time", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTime, this, "Privately shows you your current time in this current running race in the broadcast message") CHAT_COMMAND("timer", "?s", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSetTimerType, this, "Personal Setting of showing time in either broadcast or game/round timer, timer s, where s = broadcast for broadcast, gametimer for game/round timer, cycle for cycle, both for both, none for no timer and nothing to show current status") #if defined(CONF_SQL) CHAT_COMMAND("times", "?s?i", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTimes, this, "/times ?s?i shows last 5 times of the server or of a player beginning with name s starting with time i (i = 1 by default)") +CHAT_COMMAND("points", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConPoints, this, "Shows the global points of a player beginning with name r (your rank by default)") +CHAT_COMMAND("top5points", "?i", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTopPoints, this, "Shows five points of the global point ladder beginning with rank i (1 by default)") #endif #undef CHAT_COMMAND diff --git a/src/game/server/ddracecommands.cpp b/src/game/server/ddracecommands.cpp index cfb5ca6d22..40a82ec1ae 100644 --- a/src/game/server/ddracecommands.cpp +++ b/src/game/server/ddracecommands.cpp @@ -128,6 +128,26 @@ void CGameContext::ConUnSuper(IConsole::IResult *pResult, void *pUserData) } } +void CGameContext::ConUnSolo(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + CCharacter* pChr = pSelf->GetPlayerChar(pResult->m_ClientID); + if (pChr) + pChr->Teams()->m_Core.SetSolo(pResult->m_ClientID, false); +} + +void CGameContext::ConUnDeep(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + CCharacter* pChr = pSelf->GetPlayerChar(pResult->m_ClientID); + if (pChr) + pChr->m_DeepFreeze = false; +} + void CGameContext::ConShotgun(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; @@ -248,18 +268,62 @@ void CGameContext::ModifyWeapons(IConsole::IResult *pResult, void *pUserData, pChr->m_DDRaceState = DDRACE_CHEAT; } -void CGameContext::ConTeleport(IConsole::IResult *pResult, void *pUserData) +void CGameContext::ConToTeleporter(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; - if (!CheckClientID(pResult->GetVictim())) - return; - int TeleTo = pResult->GetVictim(); - if (pSelf->m_apPlayers[TeleTo]) + unsigned int TeleTo = pResult->GetInteger(0); + + if (((CGameControllerDDRace*)pSelf->m_pController)->m_TeleOuts[TeleTo-1].size()) + { + int Num = ((CGameControllerDDRace*)pSelf->m_pController)->m_TeleOuts[TeleTo-1].size(); + vec2 TelePos = ((CGameControllerDDRace*)pSelf->m_pController)->m_TeleOuts[TeleTo-1][(!Num)?Num:rand() % Num]; + CCharacter* pChr = pSelf->GetPlayerChar(pResult->m_ClientID); + if (pChr) + { + pChr->Core()->m_Pos = TelePos; + pChr->m_Pos = TelePos; + pChr->m_PrevPos = TelePos; + pChr->m_DDRaceState = DDRACE_CHEAT; + } + } +} + +void CGameContext::ConToCheckTeleporter(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + unsigned int TeleTo = pResult->GetInteger(0); + + if (((CGameControllerDDRace*)pSelf->m_pController)->m_TeleCheckOuts[TeleTo-1].size()) { + int Num = ((CGameControllerDDRace*)pSelf->m_pController)->m_TeleCheckOuts[TeleTo-1].size(); + vec2 TelePos = ((CGameControllerDDRace*)pSelf->m_pController)->m_TeleCheckOuts[TeleTo-1][(!Num)?Num:rand() % Num]; CCharacter* pChr = pSelf->GetPlayerChar(pResult->m_ClientID); if (pChr) + { + pChr->Core()->m_Pos = TelePos; + pChr->m_Pos = TelePos; + pChr->m_PrevPos = TelePos; + pChr->m_DDRaceState = DDRACE_CHEAT; + } + } +} + +void CGameContext::ConTeleport(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + int TeleTo = pResult->GetInteger(0); + int Tele = pResult->m_ClientID; + if (pResult->NumArguments() > 0) + Tele = pResult->GetVictim(); + + if (pSelf->m_apPlayers[TeleTo]) + { + CCharacter* pChr = pSelf->GetPlayerChar(Tele); + if (pChr && pSelf->GetPlayerChar(TeleTo)) { pChr->Core()->m_Pos = pSelf->m_apPlayers[TeleTo]->m_ViewPos; + pChr->m_Pos = pSelf->m_apPlayers[TeleTo]->m_ViewPos; + pChr->m_PrevPos = pSelf->m_apPlayers[TeleTo]->m_ViewPos; pChr->m_DDRaceState = DDRACE_CHEAT; } } @@ -416,3 +480,52 @@ void CGameContext::ConMutes(IConsole::IResult *pResult, void *pUserData) pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "mutes", aBuf); } } + +void CGameContext::ConList(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + int ClientID = pResult->m_ClientID; + if(!CheckClientID(ClientID)) return; + + char zerochar = 0; + if(pResult->NumArguments() > 0) + pSelf->List(ClientID, pResult->GetString(0)); + else + pSelf->List(ClientID, &zerochar); +} + +void CGameContext::ConFreezeHammer(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + int Victim = pResult->GetVictim(); + + CCharacter* pChr = pSelf->GetPlayerChar(Victim); + + if (!pChr) + return; + + char aBuf[128]; + str_format(aBuf, sizeof aBuf, "'%s' got freeze hammer!", + pSelf->Server()->ClientName(Victim)); + pSelf->SendChat(-1, CHAT_ALL, aBuf); + + pChr->m_FreezeHammer = true; +} + +void CGameContext::ConUnFreezeHammer(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + int Victim = pResult->GetVictim(); + + CCharacter* pChr = pSelf->GetPlayerChar(Victim); + + if (!pChr) + return; + + char aBuf[128]; + str_format(aBuf, sizeof aBuf, "'%s' lost freeze hammer!", + pSelf->Server()->ClientName(Victim)); + pSelf->SendChat(-1, CHAT_ALL, aBuf); + + pChr->m_FreezeHammer = false; +} diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index eac8e6a9ca..e56f31b7a8 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -63,15 +63,17 @@ bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos) { m_EmoteStop = -1; m_LastAction = -1; - m_ActiveWeapon = WEAPON_GUN; + m_LastNoAmmoSound = -1; m_LastWeapon = WEAPON_HAMMER; m_QueuedWeapon = -1; - + m_LastPenalty = false; + m_pPlayer = pPlayer; m_Pos = Pos; m_Core.Reset(); - m_Core.Init(&GameServer()->m_World.m_Core, GameServer()->Collision(), &((CGameControllerDDRace*)GameServer()->m_pController)->m_Teams.m_Core); + m_Core.Init(&GameServer()->m_World.m_Core, GameServer()->Collision(), &((CGameControllerDDRace*)GameServer()->m_pController)->m_Teams.m_Core, &((CGameControllerDDRace*)GameServer()->m_pController)->m_TeleOuts); + m_Core.m_ActiveWeapon = WEAPON_GUN; m_Core.m_Pos = m_Pos; GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = &m_Core; @@ -87,6 +89,12 @@ bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos) Teams()->OnCharacterSpawn(GetPlayer()->GetCID()); DDRaceInit(); + + m_TuneZone = GameServer()->Collision()->IsTune(GameServer()->Collision()->GetMapIndex(Pos)); + m_TuneZoneOld = -1; // no zone leave msg on spawn + m_NeededFaketuning = 0; // reset fake tunings on respawn and send the client + SendZoneMsgs(); // we want a entermessage also on spawn + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); return true; } @@ -99,16 +107,16 @@ void CCharacter::Destroy() void CCharacter::SetWeapon(int W) { - if(W == m_ActiveWeapon) + if(W == m_Core.m_ActiveWeapon) return; - m_LastWeapon = m_ActiveWeapon; + m_LastWeapon = m_Core.m_ActiveWeapon; m_QueuedWeapon = -1; - m_ActiveWeapon = W; + m_Core.m_ActiveWeapon = W; GameServer()->CreateSound(m_Pos, SOUND_WEAPON_SWITCH, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID())); - if(m_ActiveWeapon < 0 || m_ActiveWeapon >= NUM_WEAPONS) - m_ActiveWeapon = 0; + if(m_Core.m_ActiveWeapon < 0 || m_Core.m_ActiveWeapon >= NUM_WEAPONS) + m_Core.m_ActiveWeapon = 0; } bool CCharacter::IsGrounded() @@ -120,22 +128,75 @@ bool CCharacter::IsGrounded() return false; } +void CCharacter::HandleJetpack() +{ + vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); + + bool FullAuto = false; + if(m_Core.m_ActiveWeapon == WEAPON_GRENADE || m_Core.m_ActiveWeapon == WEAPON_SHOTGUN || m_Core.m_ActiveWeapon == WEAPON_RIFLE) + FullAuto = true; + if (m_Jetpack && m_Core.m_ActiveWeapon == WEAPON_GUN) + FullAuto = true; + + // check if we gonna fire + bool WillFire = false; + if(CountInput(m_LatestPrevInput.m_Fire, m_LatestInput.m_Fire).m_Presses) + WillFire = true; + + if(FullAuto && (m_LatestInput.m_Fire&1) && m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo) + WillFire = true; + + if(!WillFire) + return; + + // check for ammo + if(!m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo) + { + return; + } + + switch(m_Core.m_ActiveWeapon) + { + case WEAPON_GUN: + { + if (m_Jetpack) + { + float Strength; + if (!m_TuneZone) + Strength = GameServer()->Tuning()->m_JetpackStrength; + else + Strength = GameServer()->TuningList()[m_TuneZone].m_JetpackStrength; + TakeDamage(Direction * -1.0f * (Strength / 100.0f / 6.11f), g_pData->m_Weapons.m_Hammer.m_pBase->m_Damage, m_pPlayer->GetCID(), m_Core.m_ActiveWeapon); + } + } + } +} void CCharacter::HandleNinja() { - if(m_ActiveWeapon != WEAPON_NINJA) + if(m_Core.m_ActiveWeapon != WEAPON_NINJA) return; if ((Server()->Tick() - m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000)) { // time's up, return + m_Ninja.m_CurrentMoveTime = 0; m_aWeapons[WEAPON_NINJA].m_Got = false; - m_ActiveWeapon = m_LastWeapon; + m_Core.m_ActiveWeapon = m_LastWeapon; - SetWeapon(m_ActiveWeapon); + SetWeapon(m_Core.m_ActiveWeapon); return; } + int NinjaTime = m_Ninja.m_ActivationTick + (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000) - Server()->Tick(); + + if (NinjaTime % Server()->TickSpeed() == 0 && NinjaTime / Server()->TickSpeed() <= 5) + { + GameServer()->CreateDamageInd(m_Pos, 0, NinjaTime / Server()->TickSpeed(), Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID())); + } + + m_Armor = 10 - (NinjaTime / 15); + // force ninja Weapon SetWeapon(WEAPON_NINJA); @@ -165,11 +226,23 @@ void CCharacter::HandleNinja() vec2 Center = OldPos + Dir * 0.5f; int Num = GameServer()->m_World.FindEntities(Center, Radius, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); + // check that we're not in solo part + if (Teams()->m_Core.GetSolo(m_pPlayer->GetCID())) + return; + for (int i = 0; i < Num; ++i) { if (aEnts[i] == this) continue; + // Don't hit players in other teams + if (Team() != aEnts[i]->Team()) + continue; + + // Don't hit players in solo parts + if (Teams()->m_Core.GetSolo(aEnts[i]->m_pPlayer->GetCID())) + return; + // make sure we haven't Hit this object before bool bAlreadyHit = false; for (int j = 0; j < m_NumObjectsHit; j++) @@ -213,7 +286,7 @@ void CCharacter::DoWeaponSwitch() void CCharacter::HandleWeaponSwitch() { - int WantedWeapon = m_ActiveWeapon; + int WantedWeapon = m_Core.m_ActiveWeapon; if(m_QueuedWeapon != -1) WantedWeapon = m_QueuedWeapon; @@ -252,7 +325,7 @@ void CCharacter::HandleWeaponSwitch() WantedWeapon = m_Input.m_WantedWeapon-1; // check for insane values - if(WantedWeapon >= 0 && WantedWeapon < NUM_WEAPONS && WantedWeapon != m_ActiveWeapon && m_aWeapons[WantedWeapon].m_Got) + if(WantedWeapon >= 0 && WantedWeapon < NUM_WEAPONS && WantedWeapon != m_Core.m_ActiveWeapon && m_aWeapons[WantedWeapon].m_Got) m_QueuedWeapon = WantedWeapon; DoWeaponSwitch(); @@ -267,23 +340,24 @@ void CCharacter::FireWeapon() vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); bool FullAuto = false; - if(m_ActiveWeapon == WEAPON_GRENADE || m_ActiveWeapon == WEAPON_SHOTGUN || m_ActiveWeapon == WEAPON_RIFLE) + if(m_Core.m_ActiveWeapon == WEAPON_GRENADE || m_Core.m_ActiveWeapon == WEAPON_SHOTGUN || m_Core.m_ActiveWeapon == WEAPON_RIFLE) + FullAuto = true; + if (m_Jetpack && m_Core.m_ActiveWeapon == WEAPON_GUN) FullAuto = true; - // check if we gonna fire bool WillFire = false; if(CountInput(m_LatestPrevInput.m_Fire, m_LatestInput.m_Fire).m_Presses) WillFire = true; - if(FullAuto && (m_LatestInput.m_Fire&1) && m_aWeapons[m_ActiveWeapon].m_Ammo) + if(FullAuto && (m_LatestInput.m_Fire&1) && m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo) WillFire = true; if(!WillFire) return; // check for ammo - if(!m_aWeapons[m_ActiveWeapon].m_Ammo) + if(!m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo) { /*// 125ms is a magical limit of how fast a human can click m_ReloadTimer = 125 * Server()->TickSpeed() / 1000; @@ -299,7 +373,7 @@ void CCharacter::FireWeapon() vec2 ProjStartPos = m_Pos+Direction*m_ProximityRadius*0.75f; - switch(m_ActiveWeapon) + switch(m_Core.m_ActiveWeapon) { case WEAPON_HAMMER: { @@ -334,7 +408,13 @@ void CCharacter::FireWeapon() else Dir = vec2(0.f, -1.f); /*pTarget->TakeDamage(vec2(0.f, -1.f) + normalize(Dir + vec2(0.f, -1.1f)) * 10.0f, g_pData->m_Weapons.m_Hammer.m_pBase->m_Damage, - m_pPlayer->GetCID(), m_ActiveWeapon);*/ + m_pPlayer->GetCID(), m_Core.m_ActiveWeapon);*/ + + float Strength; + if (!m_TuneZone) + Strength = GameServer()->Tuning()->m_HammerStrength; + else + Strength = GameServer()->TuningList()[m_TuneZone].m_HammerStrength; vec2 Temp = pTarget->m_Core.m_Vel + normalize(Dir + vec2(0.f, -1.1f)) * 10.0f; if(Temp.x > 0 && ((pTarget->m_TileIndex == TILE_STOP && pTarget->m_TileFlags == ROTATION_270) || (pTarget->m_TileIndexL == TILE_STOP && pTarget->m_TileFlagsL == ROTATION_270) || (pTarget->m_TileIndexL == TILE_STOPS && (pTarget->m_TileFlagsL == ROTATION_90 || pTarget->m_TileFlagsL ==ROTATION_270)) || (pTarget->m_TileIndexL == TILE_STOPA) || (pTarget->m_TileFIndex == TILE_STOP && pTarget->m_TileFFlags == ROTATION_270) || (pTarget->m_TileFIndexL == TILE_STOP && pTarget->m_TileFFlagsL == ROTATION_270) || (pTarget->m_TileFIndexL == TILE_STOPS && (pTarget->m_TileFFlagsL == ROTATION_90 || pTarget->m_TileFFlagsL == ROTATION_270)) || (pTarget->m_TileFIndexL == TILE_STOPA) || (pTarget->m_TileSIndex == TILE_STOP && pTarget->m_TileSFlags == ROTATION_270) || (pTarget->m_TileSIndexL == TILE_STOP && pTarget->m_TileSFlagsL == ROTATION_270) || (pTarget->m_TileSIndexL == TILE_STOPS && (pTarget->m_TileSFlagsL == ROTATION_90 || pTarget->m_TileSFlagsL == ROTATION_270)) || (pTarget->m_TileSIndexL == TILE_STOPA))) @@ -346,9 +426,13 @@ void CCharacter::FireWeapon() if(Temp.y > 0 && ((pTarget->m_TileIndex == TILE_STOP && pTarget->m_TileFlags == ROTATION_0) || (pTarget->m_TileIndexT == TILE_STOP && pTarget->m_TileFlagsT == ROTATION_0) || (pTarget->m_TileIndexT == TILE_STOPS && (pTarget->m_TileFlagsT == ROTATION_0 || pTarget->m_TileFlagsT == ROTATION_180)) || (pTarget->m_TileIndexT == TILE_STOPA) || (pTarget->m_TileFIndex == TILE_STOP && pTarget->m_TileFFlags == ROTATION_0) || (pTarget->m_TileFIndexT == TILE_STOP && pTarget->m_TileFFlagsT == ROTATION_0) || (pTarget->m_TileFIndexT == TILE_STOPS && (pTarget->m_TileFFlagsT == ROTATION_0 || pTarget->m_TileFFlagsT == ROTATION_180)) || (pTarget->m_TileFIndexT == TILE_STOPA) || (pTarget->m_TileSIndex == TILE_STOP && pTarget->m_TileSFlags == ROTATION_0) || (pTarget->m_TileSIndexT == TILE_STOP && pTarget->m_TileSFlagsT == ROTATION_0) || (pTarget->m_TileSIndexT == TILE_STOPS && (pTarget->m_TileSFlagsT == ROTATION_0 || pTarget->m_TileSFlagsT == ROTATION_180)) || (pTarget->m_TileSIndexT == TILE_STOPA))) Temp.y = 0; Temp -= pTarget->m_Core.m_Vel; - pTarget->TakeDamage(vec2(0.f, -1.f) + Temp, g_pData->m_Weapons.m_Hammer.m_pBase->m_Damage, - m_pPlayer->GetCID(), m_ActiveWeapon); + pTarget->TakeDamage((vec2(0.f, -1.0f) + Temp) * Strength, g_pData->m_Weapons.m_Hammer.m_pBase->m_Damage, + m_pPlayer->GetCID(), m_Core.m_ActiveWeapon); pTarget->UnFreeze(); + + if(m_FreezeHammer) + pTarget->Freeze(); + Hits++; } @@ -360,33 +444,41 @@ void CCharacter::FireWeapon() case WEAPON_GUN: { - CProjectile *pProj = new CProjectile - ( - GameWorld(), - WEAPON_GUN,//Type - m_pPlayer->GetCID(),//Owner - ProjStartPos,//Pos - Direction,//Dir - (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_GunLifetime),//Span - 0,//Freeze - 0,//Explosive - 0,//Force - -1,//SoundImpact - WEAPON_GUN//Weapon - ); - - // pack the Projectile and send it to the client Directly - CNetObj_Projectile p; - pProj->FillInfo(&p); + if (!m_Jetpack || !m_pPlayer->m_NinjaJetpack) + { + int Lifetime; + if (!m_TuneZone) + Lifetime = (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_GunLifetime); + else + Lifetime = (int)(Server()->TickSpeed()*GameServer()->TuningList()[m_TuneZone].m_GunLifetime); + + CProjectile *pProj = new CProjectile + ( + GameWorld(), + WEAPON_GUN,//Type + m_pPlayer->GetCID(),//Owner + ProjStartPos,//Pos + Direction,//Dir + Lifetime,//Span + 0,//Freeze + 0,//Explosive + 0,//Force + -1,//SoundImpact + WEAPON_GUN//Weapon + ); - CMsgPacker Msg(NETMSGTYPE_SV_EXTRAPROJECTILE); - Msg.AddInt(1); - for(unsigned i = 0; i < sizeof(CNetObj_Projectile)/sizeof(int); i++) - Msg.AddInt(((int *)&p)[i]); + // pack the Projectile and send it to the client Directly + CNetObj_Projectile p; + pProj->FillInfo(&p); - Server()->SendMsg(&Msg, 0, m_pPlayer->GetCID()); + CMsgPacker Msg(NETMSGTYPE_SV_EXTRAPROJECTILE); + Msg.AddInt(1); + for(unsigned i = 0; i < sizeof(CNetObj_Projectile)/sizeof(int); i++) + Msg.AddInt(((int *)&p)[i]); - GameServer()->CreateSound(m_Pos, SOUND_GUN_FIRE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID())); + Server()->SendMsg(&Msg, 0, m_pPlayer->GetCID()); + GameServer()->CreateSound(m_Pos, SOUND_GUN_FIRE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID())); + } } break; case WEAPON_SHOTGUN: @@ -421,12 +513,24 @@ void CCharacter::FireWeapon() Server()->SendMsg(&Msg, 0,m_pPlayer->GetCID()); GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE);*/ - new CLaser(&GameServer()->m_World, m_Pos, Direction, GameServer()->Tuning()->m_LaserReach, m_pPlayer->GetCID(), WEAPON_SHOTGUN); + float LaserReach; + if (!m_TuneZone) + LaserReach = GameServer()->Tuning()->m_LaserReach; + else + LaserReach = GameServer()->TuningList()[m_TuneZone].m_LaserReach; + + new CLaser(&GameServer()->m_World, m_Pos, Direction, LaserReach, m_pPlayer->GetCID(), WEAPON_SHOTGUN); GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID())); } break; case WEAPON_GRENADE: { + int Lifetime; + if (!m_TuneZone) + Lifetime = (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_GrenadeLifetime); + else + Lifetime = (int)(Server()->TickSpeed()*GameServer()->TuningList()[m_TuneZone].m_GrenadeLifetime); + CProjectile *pProj = new CProjectile ( GameWorld(), @@ -434,7 +538,7 @@ void CCharacter::FireWeapon() m_pPlayer->GetCID(),//Owner ProjStartPos,//Pos Direction,//Dir - (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_GrenadeLifetime),//Span + Lifetime,//Span 0,//Freeze true,//Explosive 0,//Force @@ -457,7 +561,13 @@ void CCharacter::FireWeapon() case WEAPON_RIFLE: { - new CLaser(GameWorld(), m_Pos, Direction, GameServer()->Tuning()->m_LaserReach, m_pPlayer->GetCID(), WEAPON_RIFLE); + float LaserReach; + if (!m_TuneZone) + LaserReach = GameServer()->Tuning()->m_LaserReach; + else + LaserReach = GameServer()->TuningList()[m_TuneZone].m_LaserReach; + + new CLaser(GameWorld(), m_Pos, Direction, LaserReach, m_pPlayer->GetCID(), WEAPON_RIFLE); GameServer()->CreateSound(m_Pos, SOUND_RIFLE_FIRE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID())); } break; @@ -477,17 +587,18 @@ void CCharacter::FireWeapon() m_AttackTick = Server()->Tick(); - /*if(m_aWeapons[m_ActiveWeapon].m_Ammo > 0) // -1 == unlimited - m_aWeapons[m_ActiveWeapon].m_Ammo--;*/ + /*if(m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo > 0) // -1 == unlimited + m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo--;*/ if(!m_ReloadTimer) - m_ReloadTimer = g_pData->m_Weapons.m_aId[m_ActiveWeapon].m_Firedelay * Server()->TickSpeed() / 1000; + m_ReloadTimer = g_pData->m_Weapons.m_aId[m_Core.m_ActiveWeapon].m_Firedelay * Server()->TickSpeed() / 1000; } void CCharacter::HandleWeapons() { //ninja HandleNinja(); + HandleJetpack(); if(m_PainSoundTimer > 0) m_PainSoundTimer--; @@ -503,25 +614,25 @@ void CCharacter::HandleWeapons() FireWeapon(); /* // ammo regen - int AmmoRegenTime = g_pData->m_Weapons.m_aId[m_ActiveWeapon].m_Ammoregentime; + int AmmoRegenTime = g_pData->m_Weapons.m_aId[m_Core.m_ActiveWeapon].m_Ammoregentime; if(AmmoRegenTime) { // If equipped and not active, regen ammo? if (m_ReloadTimer <= 0) { - if (m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart < 0) - m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = Server()->Tick(); + if (m_aWeapons[m_Core.m_ActiveWeapon].m_AmmoRegenStart < 0) + m_aWeapons[m_Core.m_ActiveWeapon].m_AmmoRegenStart = Server()->Tick(); - if ((Server()->Tick() - m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart) >= AmmoRegenTime * Server()->TickSpeed() / 1000) + if ((Server()->Tick() - m_aWeapons[m_Core.m_ActiveWeapon].m_AmmoRegenStart) >= AmmoRegenTime * Server()->TickSpeed() / 1000) { // Add some ammo - m_aWeapons[m_ActiveWeapon].m_Ammo = min(m_aWeapons[m_ActiveWeapon].m_Ammo + 1, 10); - m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = -1; + m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo = min(m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo + 1, 10); + m_aWeapons[m_Core.m_ActiveWeapon].m_AmmoRegenStart = -1; } } else { - m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = -1; + m_aWeapons[m_Core.m_ActiveWeapon].m_AmmoRegenStart = -1; } }*/ @@ -546,9 +657,9 @@ void CCharacter::GiveNinja() m_aWeapons[WEAPON_NINJA].m_Got = true; if (!m_FreezeTime) m_aWeapons[WEAPON_NINJA].m_Ammo = -1; - if (m_ActiveWeapon != WEAPON_NINJA) - m_LastWeapon = m_ActiveWeapon; - m_ActiveWeapon = WEAPON_NINJA; + if (m_Core.m_ActiveWeapon != WEAPON_NINJA) + m_LastWeapon = m_Core.m_ActiveWeapon; + m_Core.m_ActiveWeapon = WEAPON_NINJA; if(!m_aWeapons[WEAPON_NINJA].m_Got) GameServer()->CreateSound(m_Pos, SOUND_PICKUP_NINJA, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID())); @@ -596,7 +707,7 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput) void CCharacter::ResetInput() { m_Input.m_Direction = 0; - m_Input.m_Hook = 0; + //m_Input.m_Hook = 0; // simulate releasing the fire button if((m_Input.m_Fire&1) != 0) m_Input.m_Fire++; @@ -620,9 +731,9 @@ void CCharacter::Tick() return; DDRaceTick(); - + m_Core.m_Input = m_Input; - m_Core.Tick(true); + m_Core.Tick(true, false); /*// handle death-tiles and leaving gamelayer if(GameServer()->Collision()->GetCollisionAt(m_Pos.x+m_ProximityRadius/3.f, m_Pos.y-m_ProximityRadius/3.f)&CCollision::COLFLAG_DEATH || @@ -651,9 +762,9 @@ void CCharacter::TickDefered() // advance the dummy { CWorldCore TempWorld; - m_ReckoningCore.Init(&TempWorld, GameServer()->Collision(), &((CGameControllerDDRace*)GameServer()->m_pController)->m_Teams.m_Core); + m_ReckoningCore.Init(&TempWorld, GameServer()->Collision(), &((CGameControllerDDRace*)GameServer()->m_pController)->m_Teams.m_Core, &((CGameControllerDDRace*)GameServer()->m_pController)->m_TeleOuts); m_ReckoningCore.m_Id = m_pPlayer->GetCID(); - m_ReckoningCore.Tick(false); + m_ReckoningCore.Tick(false, false); m_ReckoningCore.Move(); m_ReckoningCore.Quantize(); } @@ -740,8 +851,8 @@ void CCharacter::TickPaused() ++m_ReckoningTick; if(m_LastAction != -1) ++m_LastAction; - if(m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart > -1) - ++m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart; + if(m_aWeapons[m_Core.m_ActiveWeapon].m_AmmoRegenStart > -1) + ++m_aWeapons[m_Core.m_ActiveWeapon].m_AmmoRegenStart; if(m_EmoteStop > -1) ++m_EmoteStop; } @@ -792,7 +903,7 @@ void CCharacter::Die(int Killer, int Weapon) GameServer()->m_World.RemoveEntity(this); GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = 0; GameServer()->CreateDeath(m_Pos, m_pPlayer->GetCID(), Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID())); - Teams()->OnCharacterDeath(GetPlayer()->GetCID()); + Teams()->OnCharacterDeath(GetPlayer()->GetCID(), Weapon); } bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) @@ -850,7 +961,7 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) // do damage Hit sound if(From >= 0 && From != m_pPlayer->GetCID() && GameServer()->m_apPlayers[From]) { - int Mask = CmaskOne(From); + int64_t Mask = CmaskOne(From); for(int i = 0; i < MAX_CLIENTS; i++) { if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() == TEAM_SPECTATORS && GameServer()->m_apPlayers[i]->m_SpectatorID == From) @@ -883,8 +994,11 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) else GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_SHORT);*/ - m_EmoteType = EMOTE_PAIN; - m_EmoteStop = Server()->Tick() + 500 * Server()->TickSpeed() / 1000; + if (!m_Jetpack || m_Core.m_ActiveWeapon != WEAPON_GUN) + { + m_EmoteType = EMOTE_PAIN; + m_EmoteStop = Server()->Tick() + 500 * Server()->TickSpeed() / 1000; + } vec2 Temp = m_Core.m_Vel + Force; if(Temp.x > 0 && ((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_270) || (m_TileIndexL == TILE_STOP && m_TileFlagsL == ROTATION_270) || (m_TileIndexL == TILE_STOPS && (m_TileFlagsL == ROTATION_90 || m_TileFlagsL ==ROTATION_270)) || (m_TileIndexL == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_270) || (m_TileFIndexL == TILE_STOP && m_TileFFlagsL == ROTATION_270) || (m_TileFIndexL == TILE_STOPS && (m_TileFFlagsL == ROTATION_90 || m_TileFFlagsL == ROTATION_270)) || (m_TileFIndexL == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_270) || (m_TileSIndexL == TILE_STOP && m_TileSFlagsL == ROTATION_270) || (m_TileSIndexL == TILE_STOPS && (m_TileSFlagsL == ROTATION_90 || m_TileSFlagsL == ROTATION_270)) || (m_TileSIndexL == TILE_STOPA))) @@ -902,6 +1016,11 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) void CCharacter::Snap(int SnappingClient) { + int id = m_pPlayer->GetCID(); + + if (!Server()->Translate(id, SnappingClient)) + return; + if(NetworkClipped(SnappingClient)) return; @@ -919,7 +1038,7 @@ void CCharacter::Snap(int SnappingClient) if (m_Paused) return; - CNetObj_Character *pCharacter = static_cast(Server()->SnapNewItem(NETOBJTYPE_CHARACTER, m_pPlayer->GetCID(), sizeof(CNetObj_Character))); + CNetObj_Character *pCharacter = static_cast(Server()->SnapNewItem(NETOBJTYPE_CHARACTER, id, sizeof(CNetObj_Character))); if(!pCharacter) return; @@ -944,6 +1063,11 @@ void CCharacter::Snap(int SnappingClient) m_EmoteStop = -1; } + if (pCharacter->m_HookedPlayer != -1) + { + if (!Server()->Translate(pCharacter->m_HookedPlayer, SnappingClient)) + pCharacter->m_HookedPlayer = -1; + } pCharacter->m_Emote = m_EmoteType; pCharacter->m_AmmoCount = 0; @@ -964,8 +1088,14 @@ void CCharacter::Snap(int SnappingClient) pCharacter->m_Weapon = WEAPON_NINJA; pCharacter->m_AmmoCount = 0; } + else if (m_pPlayer->m_NinjaJetpack && m_Jetpack && m_Core.m_ActiveWeapon == WEAPON_GUN) + { + pCharacter->m_Emote = EMOTE_HAPPY, + pCharacter->m_Weapon = WEAPON_NINJA; + pCharacter->m_AmmoCount = 10; + } else - pCharacter->m_Weapon = m_ActiveWeapon; + pCharacter->m_Weapon = m_Core.m_ActiveWeapon; pCharacter->m_AttackTick = m_AttackTick; pCharacter->m_Direction = m_Input.m_Direction; @@ -975,11 +1105,14 @@ void CCharacter::Snap(int SnappingClient) { pCharacter->m_Health = m_Health; pCharacter->m_Armor = m_Armor; - if(m_aWeapons[m_ActiveWeapon].m_Ammo > 0) - //pCharacter->m_AmmoCount = m_aWeapons[m_ActiveWeapon].m_Ammo; - pCharacter->m_AmmoCount = (!m_FreezeTime)?m_aWeapons[m_ActiveWeapon].m_Ammo:0; + if(m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo > 0) + //pCharacter->m_AmmoCount = m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo; + pCharacter->m_AmmoCount = (!m_FreezeTime)?m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo:0; } + if(GetPlayer()->m_Afk) + pCharacter->m_Emote = EMOTE_BLINK; + if(pCharacter->m_Emote == EMOTE_NORMAL) { if(250 - ((Server()->Tick() - m_LastAction)%(250)) < 5) @@ -989,6 +1122,27 @@ void CCharacter::Snap(int SnappingClient) pCharacter->m_PlayerFlags = GetPlayer()->m_PlayerFlags; } +int CCharacter::NetworkClipped(int SnappingClient) +{ + return NetworkClipped(SnappingClient, m_Pos); +} + +int CCharacter::NetworkClipped(int SnappingClient, vec2 CheckPos) +{ + if(SnappingClient == -1 || GameServer()->m_apPlayers[SnappingClient]->m_ShowAll) + return 0; + + float dx = GameServer()->m_apPlayers[SnappingClient]->m_ViewPos.x-CheckPos.x; + float dy = GameServer()->m_apPlayers[SnappingClient]->m_ViewPos.y-CheckPos.y; + + if(absolute(dx) > 1000.0f || absolute(dy) > 800.0f) + return 1; + + if(distance(GameServer()->m_apPlayers[SnappingClient]->m_ViewPos, CheckPos) > 4000.0f) + return 1; + return 0; +} + // DDRace bool CCharacter::CanCollide(int ClientID) @@ -1015,7 +1169,7 @@ void CCharacter::HandleBroadcast() CPlayerData *pData = GameServer()->Score()->PlayerData(m_pPlayer->GetCID()); if(m_DDRaceState == DDRACE_STARTED && m_CpLastBroadcast != m_CpActive && - m_CpActive > -1 && m_CpTick > Server()->Tick() && !m_pPlayer->m_IsUsingDDRaceClient && + m_CpActive > -1 && m_CpTick > Server()->Tick() && !m_pPlayer->m_ClientVersion >= VERSION_DDRACE && pData->m_BestTime && pData->m_aBestCpTime[m_CpActive] != 0) { char aBroadcast[128]; @@ -1038,7 +1192,6 @@ void CCharacter::HandleBroadcast() void CCharacter::HandleSkippableTiles(int Index) { - // handle death-tiles and leaving gamelayer if((GameServer()->Collision()->GetCollisionAt(m_Pos.x+m_ProximityRadius/3.f, m_Pos.y-m_ProximityRadius/3.f)&CCollision::COLFLAG_DEATH || GameServer()->Collision()->GetCollisionAt(m_Pos.x+m_ProximityRadius/3.f, m_Pos.y+m_ProximityRadius/3.f)&CCollision::COLFLAG_DEATH || @@ -1046,13 +1199,18 @@ void CCharacter::HandleSkippableTiles(int Index) GameServer()->Collision()->GetFCollisionAt(m_Pos.x+m_ProximityRadius/3.f, m_Pos.y-m_ProximityRadius/3.f)&CCollision::COLFLAG_DEATH || GameServer()->Collision()->GetFCollisionAt(m_Pos.x+m_ProximityRadius/3.f, m_Pos.y+m_ProximityRadius/3.f)&CCollision::COLFLAG_DEATH || GameServer()->Collision()->GetFCollisionAt(m_Pos.x-m_ProximityRadius/3.f, m_Pos.y-m_ProximityRadius/3.f)&CCollision::COLFLAG_DEATH || - GameServer()->Collision()->GetCollisionAt(m_Pos.x-m_ProximityRadius/3.f, m_Pos.y+m_ProximityRadius/3.f)&CCollision::COLFLAG_DEATH || - GameLayerClipped(m_Pos)) && + GameServer()->Collision()->GetCollisionAt(m_Pos.x-m_ProximityRadius/3.f, m_Pos.y+m_ProximityRadius/3.f)&CCollision::COLFLAG_DEATH) && !m_Super && !(Team() && Teams()->TeeFinished(m_pPlayer->GetCID()))) - { - Die(m_pPlayer->GetCID(), WEAPON_WORLD); - return; - } + { + Die(m_pPlayer->GetCID(), WEAPON_WORLD); + return; + } + + if (GameLayerClipped(m_Pos) && !(Team() && Teams()->TeeFinished(m_pPlayer->GetCID()))) + { + Die(m_pPlayer->GetCID(), WEAPON_WORLD); + return; + } if(Index < 0) return; @@ -1186,14 +1344,17 @@ void CCharacter::HandleTiles(int Index) //dbg_msg("","N%d L%d R%d B%d T%d",m_TileIndex,m_TileIndexL,m_TileIndexR,m_TileIndexB,m_TileIndexT); //dbg_msg("","N%d L%d R%d B%d T%d",m_TileFIndex,m_TileFIndexL,m_TileFIndexR,m_TileFIndexB,m_TileFIndexT); if(Index < 0) + { + m_LastPenalty = false; return; + } int cp = GameServer()->Collision()->IsCheckpoint(MapIndex); if(cp != -1 && m_DDRaceState == DDRACE_STARTED && cp > m_CpActive) { m_CpActive = cp; m_CpCurrent[cp] = m_Time; m_CpTick = Server()->Tick() + Server()->TickSpeed() * 2; - if(m_pPlayer->m_IsUsingDDRaceClient) { + if(m_pPlayer->m_ClientVersion >= VERSION_DDRACE) { CPlayerData *pData = GameServer()->Score()->PlayerData(m_pPlayer->GetCID()); CNetMsg_Sv_DDRaceTime Msg; Msg.m_Time = (int)m_Time; @@ -1218,7 +1379,7 @@ void CCharacter::HandleTiles(int Index) m_CpActive = cpf; m_CpCurrent[cpf] = m_Time; m_CpTick = Server()->Tick() + Server()->TickSpeed()*2; - if(m_pPlayer->m_IsUsingDDRaceClient) { + if(m_pPlayer->m_ClientVersion >= VERSION_DDRACE) { CPlayerData *pData = GameServer()->Score()->PlayerData(m_pPlayer->GetCID()); CNetMsg_Sv_DDRaceTime Msg; Msg.m_Time = (int)m_Time; @@ -1248,8 +1409,8 @@ void CCharacter::HandleTiles(int Index) for (int i = WEAPON_SHOTGUN; i < NUM_WEAPONS; ++i) { m_aWeapons[i].m_Got = false; - if(m_ActiveWeapon == i) - m_ActiveWeapon = WEAPON_GUN; + if(m_Core.m_ActiveWeapon == i) + m_Core.m_ActiveWeapon = WEAPON_GUN; } } if(g_Config.m_SvTeam == 2 && (Team() == TEAM_FLOCK || Teams()->Count(Team()) <= 1)) @@ -1270,7 +1431,9 @@ void CCharacter::HandleTiles(int Index) } + } + if(((m_TileIndex == TILE_END) || (m_TileFIndex == TILE_END) || FTile1 == TILE_END || FTile2 == TILE_END || FTile3 == TILE_END || FTile4 == TILE_END || Tile1 == TILE_END || Tile2 == TILE_END || Tile3 == TILE_END || Tile4 == TILE_END) && m_DDRaceState == DDRACE_STARTED) Controller->m_Teams.OnCharacterFinish(m_pPlayer->GetCID()); if(((m_TileIndex == TILE_FREEZE) || (m_TileFIndex == TILE_FREEZE)) && !m_Super && !m_DeepFreeze) @@ -1296,24 +1459,99 @@ void CCharacter::HandleTiles(int Index) GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You can hit others"); m_Hit = HIT_ALL; } + else if(((m_TileIndex == TILE_NPC_START) || (m_TileFIndex == TILE_NPC_START)) && !m_Core.m_Collision) + { + GameServer()->SendChatTarget(GetPlayer()->GetCID(),"You can collide with others"); + m_Core.m_Collision = true; + m_NeededFaketuning &= ~FAKETUNE_NOCOLL; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings + } + else if(((m_TileIndex == TILE_SUPER_START) || (m_TileFIndex == TILE_SUPER_START)) && !m_SuperJump) + { + GameServer()->SendChatTarget(GetPlayer()->GetCID(),"You have infinite air jumps"); + m_SuperJump = true; + if (m_Core.m_Jumps == 0) + { + m_NeededFaketuning &= ~FAKETUNE_NOJUMP; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings + } + } + else if(((m_TileIndex == TILE_JETPACK_START) || (m_TileFIndex == TILE_JETPACK_START)) && !m_Jetpack) + { + GameServer()->SendChatTarget(GetPlayer()->GetCID(),"You have a jetpack gun"); + m_Jetpack = true; + m_NeededFaketuning |= FAKETUNE_JETPACK; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings + } + else if(((m_TileIndex == TILE_NPH_START) || (m_TileFIndex == TILE_NPH_START)) && !m_Core.m_Hook) + { + GameServer()->SendChatTarget(GetPlayer()->GetCID(),"You can hook others"); + m_Core.m_Hook = true; + m_NeededFaketuning &= ~FAKETUNE_NOHOOK; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings + } else if(((m_TileIndex == TILE_HIT_END) || (m_TileFIndex == TILE_HIT_END)) && m_Hit != (DISABLE_HIT_GRENADE|DISABLE_HIT_HAMMER|DISABLE_HIT_RIFLE|DISABLE_HIT_SHOTGUN)) { GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You can't hit others"); m_Hit = DISABLE_HIT_GRENADE|DISABLE_HIT_HAMMER|DISABLE_HIT_RIFLE|DISABLE_HIT_SHOTGUN; } + else if(((m_TileIndex == TILE_NPC_END) || (m_TileFIndex == TILE_NPC_END)) && m_Core.m_Collision) + { + GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You can't collide with others"); + m_Core.m_Collision = false; + m_NeededFaketuning |= FAKETUNE_NOCOLL; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings + } + else if(((m_TileIndex == TILE_SUPER_END) || (m_TileFIndex == TILE_SUPER_END)) && m_SuperJump) + { + GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You don't have infinite air jumps"); + m_SuperJump = false; + if (m_Core.m_Jumps == 0) + { + m_NeededFaketuning |= FAKETUNE_NOJUMP; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings + } + } + else if((m_TileIndex == TILE_WALLJUMP) || (m_TileFIndex == TILE_WALLJUMP)) + { + if(m_Core.m_Vel.y > 0 && m_Core.m_Colliding && m_Core.m_LeftWall) + { + m_Core.m_LeftWall = false; + m_Core.m_JumpedTotal = m_Core.m_Jumps - 1; + m_Core.m_Jumped = 1; + } + } + else if(((m_TileIndex == TILE_JETPACK_END) || (m_TileFIndex == TILE_JETPACK_END)) && m_Jetpack) + { + GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You lost your jetpack gun"); + m_Jetpack = false; + m_NeededFaketuning &= ~FAKETUNE_JETPACK; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings + } + else if(((m_TileIndex == TILE_NPH_END) || (m_TileFIndex == TILE_NPH_END)) && m_Core.m_Hook) + { + GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You can't hook others"); + m_Core.m_Hook = false; + m_NeededFaketuning |= FAKETUNE_NOHOOK; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings + } if(((m_TileIndex == TILE_SOLO_START) || (m_TileFIndex == TILE_SOLO_START)) && !Teams()->m_Core.GetSolo(m_pPlayer->GetCID())) { GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You are now in a solo part."); Teams()->m_Core.SetSolo(m_pPlayer->GetCID(), true); + m_NeededFaketuning |= FAKETUNE_SOLO; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings } else if(((m_TileIndex == TILE_SOLO_END) || (m_TileFIndex == TILE_SOLO_END)) && Teams()->m_Core.GetSolo(m_pPlayer->GetCID())) { GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You are now out of the solo part."); Teams()->m_Core.SetSolo(m_pPlayer->GetCID(), false); + m_NeededFaketuning &= ~FAKETUNE_SOLO; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings } if(((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_270) || (m_TileIndexL == TILE_STOP && m_TileFlagsL == ROTATION_270) || (m_TileIndexL == TILE_STOPS && (m_TileFlagsL == ROTATION_90 || m_TileFlagsL ==ROTATION_270)) || (m_TileIndexL == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_270) || (m_TileFIndexL == TILE_STOP && m_TileFFlagsL == ROTATION_270) || (m_TileFIndexL == TILE_STOPS && (m_TileFFlagsL == ROTATION_90 || m_TileFFlagsL == ROTATION_270)) || (m_TileFIndexL == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_270) || (m_TileSIndexL == TILE_STOP && m_TileSFlagsL == ROTATION_270) || (m_TileSIndexL == TILE_STOPS && (m_TileSFlagsL == ROTATION_90 || m_TileSFlagsL == ROTATION_270)) || (m_TileSIndexL == TILE_STOPA)) && m_Core.m_Vel.x > 0) { - if((int)GameServer()->Collision()->GetPos(MapIndexL).x) + if((int)GameServer()->Collision()->GetPos(MapIndexL).x < (int)m_Core.m_Pos.x) m_Core.m_Pos = m_PrevPos; m_Core.m_Vel.x = 0; @@ -1340,7 +1578,9 @@ void CCharacter::HandleTiles(int Index) m_Core.m_Pos = m_PrevPos; m_Core.m_Vel.y = 0; m_Core.m_Jumped = 0; + m_Core.m_JumpedTotal = 0; } + // handle switch tiles if(GameServer()->Collision()->IsSwitch(MapIndex) == TILE_SWITCHOPEN && Team() != TEAM_SUPER) { @@ -1381,7 +1621,7 @@ void CCharacter::HandleTiles(int Index) else if(GameServer()->Collision()->IsSwitch(MapIndex) == TILE_HIT_START && m_Hit&DISABLE_HIT_HAMMER && GameServer()->Collision()->GetSwitchDelay(MapIndex) == WEAPON_HAMMER) { GameServer()->SendChatTarget(GetPlayer()->GetCID(),"You can hammer hit others"); - m_Hit ^= DISABLE_HIT_HAMMER; + m_Hit &= ~DISABLE_HIT_HAMMER; } else if(GameServer()->Collision()->IsSwitch(MapIndex) == TILE_HIT_END && !(m_Hit&DISABLE_HIT_HAMMER) && GameServer()->Collision()->GetSwitchDelay(MapIndex) == WEAPON_HAMMER) { @@ -1391,7 +1631,7 @@ void CCharacter::HandleTiles(int Index) else if(GameServer()->Collision()->IsSwitch(MapIndex) == TILE_HIT_START && m_Hit&DISABLE_HIT_SHOTGUN && GameServer()->Collision()->GetSwitchDelay(MapIndex) == WEAPON_SHOTGUN) { GameServer()->SendChatTarget(GetPlayer()->GetCID(),"You can shoot others with shotgun"); - m_Hit ^= DISABLE_HIT_SHOTGUN; + m_Hit &= ~DISABLE_HIT_SHOTGUN; } else if(GameServer()->Collision()->IsSwitch(MapIndex) == TILE_HIT_END && !(m_Hit&DISABLE_HIT_SHOTGUN) && GameServer()->Collision()->GetSwitchDelay(MapIndex) == WEAPON_SHOTGUN) { @@ -1401,7 +1641,7 @@ void CCharacter::HandleTiles(int Index) else if(GameServer()->Collision()->IsSwitch(MapIndex) == TILE_HIT_START && m_Hit&DISABLE_HIT_GRENADE && GameServer()->Collision()->GetSwitchDelay(MapIndex) == WEAPON_GRENADE) { GameServer()->SendChatTarget(GetPlayer()->GetCID(),"You can shoot others with grenade"); - m_Hit ^= DISABLE_HIT_GRENADE; + m_Hit &= ~DISABLE_HIT_GRENADE; } else if(GameServer()->Collision()->IsSwitch(MapIndex) == TILE_HIT_END && !(m_Hit&DISABLE_HIT_GRENADE) && GameServer()->Collision()->GetSwitchDelay(MapIndex) == WEAPON_GRENADE) { @@ -1411,41 +1651,150 @@ void CCharacter::HandleTiles(int Index) else if(GameServer()->Collision()->IsSwitch(MapIndex) == TILE_HIT_START && m_Hit&DISABLE_HIT_RIFLE && GameServer()->Collision()->GetSwitchDelay(MapIndex) == WEAPON_RIFLE) { GameServer()->SendChatTarget(GetPlayer()->GetCID(),"You can shoot others with rifle"); - m_Hit ^= DISABLE_HIT_RIFLE; + m_Hit &= ~DISABLE_HIT_RIFLE; } else if(GameServer()->Collision()->IsSwitch(MapIndex) == TILE_HIT_END && !(m_Hit&DISABLE_HIT_RIFLE) && GameServer()->Collision()->GetSwitchDelay(MapIndex) == WEAPON_RIFLE) { GameServer()->SendChatTarget(GetPlayer()->GetCID(),"You can't shoot others with rifle"); m_Hit |= DISABLE_HIT_RIFLE; } + else if(GameServer()->Collision()->IsSwitch(MapIndex) == TILE_JUMP) + { + int newJumps = GameServer()->Collision()->GetSwitchDelay(MapIndex); + + if (newJumps != m_Core.m_Jumps) + { + char aBuf[256]; + if(newJumps == 1) + str_format(aBuf, sizeof(aBuf), "You can jump %d time", newJumps); + else + str_format(aBuf, sizeof(aBuf), "You can jump %d times", newJumps); + GameServer()->SendChatTarget(GetPlayer()->GetCID(),aBuf); + + if (newJumps == 0 && !m_SuperJump) + { + m_NeededFaketuning |= FAKETUNE_NOJUMP; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings + } + else if (m_Core.m_Jumps == 0) + { + m_NeededFaketuning &= ~FAKETUNE_NOJUMP; + GameServer()->SendTuningParams(m_pPlayer->GetCID(), m_TuneZone); // update tunings + } + + m_Core.m_Jumps = newJumps; + } + } + else if(GameServer()->Collision()->IsSwitch(MapIndex) == TILE_PENALTY && !m_LastPenalty) + { + int min = GameServer()->Collision()->GetSwitchDelay(MapIndex); + int sec = GameServer()->Collision()->GetSwitchNumber(MapIndex); + int Team = Teams()->m_Core.Team(m_Core.m_Id); + + m_StartTime -= (min * 60 + sec) * Server()->TickSpeed(); + + if (Team != TEAM_FLOCK && Team != TEAM_SUPER) + { + for (int i = 0; i < MAX_CLIENTS; i++) + { + if(Teams()->m_Core.Team(i) == Team && i != m_Core.m_Id && GameServer()->m_apPlayers[i]) + { + CCharacter* pChar = GameServer()->m_apPlayers[i]->GetCharacter(); + + if (pChar) + pChar->m_StartTime = m_StartTime; + } + } + } + + m_LastPenalty = true; + } + + if(GameServer()->Collision()->IsSwitch(MapIndex) != TILE_PENALTY) + { + m_LastPenalty = false; + } + int z = GameServer()->Collision()->IsTeleport(MapIndex); - if(z && Controller->m_TeleOuts[z-1].size()) + if(!g_Config.m_SvOldTeleportHook && !g_Config.m_SvOldTeleportWeapons && z && Controller->m_TeleOuts[z-1].size()) { - m_Core.m_HookedPlayer = -1; - m_Core.m_HookState = HOOK_RETRACTED; - m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; - m_Core.m_HookState = HOOK_RETRACTED; + if (m_Super) + return; int Num = Controller->m_TeleOuts[z-1].size(); m_Core.m_Pos = Controller->m_TeleOuts[z-1][(!Num)?Num:rand() % Num]; - m_Core.m_HookPos = m_Core.m_Pos; + if(!g_Config.m_SvTeleportHoldHook) + { + m_Core.m_HookedPlayer = -1; + m_Core.m_HookState = HOOK_RETRACTED; + m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; + m_Core.m_HookState = HOOK_RETRACTED; + m_Core.m_HookPos = m_Core.m_Pos; + } return; } int evilz = GameServer()->Collision()->IsEvilTeleport(MapIndex); - if(evilz && !m_Super && Controller->m_TeleOuts[evilz-1].size()) + if(evilz && Controller->m_TeleOuts[evilz-1].size()) { - m_Core.m_HookedPlayer = -1; - m_Core.m_HookState = HOOK_RETRACTED; - m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; - m_Core.m_HookState = HOOK_RETRACTED; - GameWorld()->ReleaseHooked(GetPlayer()->GetCID()); + if (m_Super) + return; int Num = Controller->m_TeleOuts[evilz-1].size(); m_Core.m_Pos = Controller->m_TeleOuts[evilz-1][(!Num)?Num:rand() % Num]; - m_Core.m_HookPos = m_Core.m_Pos; - m_Core.m_Vel = vec2(0,0); + if (!g_Config.m_SvOldTeleportHook && !g_Config.m_SvOldTeleportWeapons) + { + m_Core.m_Vel = vec2(0,0); + + if(!g_Config.m_SvTeleportHoldHook) + { + m_Core.m_HookedPlayer = -1; + m_Core.m_HookState = HOOK_RETRACTED; + m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; + m_Core.m_HookState = HOOK_RETRACTED; + GameWorld()->ReleaseHooked(GetPlayer()->GetCID()); + m_Core.m_HookPos = m_Core.m_Pos; + } + } + return; + } + if(GameServer()->Collision()->IsCheckEvilTeleport(MapIndex)) + { + if (m_Super) + return; + // first check if there is a TeleCheckOut for the current recorded checkpoint, if not check previous checkpoints + for(int k=m_TeleCheckpoint-1; k >= 0; k--) + { + if(Controller->m_TeleCheckOuts[k].size()) + { + m_Core.m_HookedPlayer = -1; + m_Core.m_HookState = HOOK_RETRACTED; + m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; + m_Core.m_HookState = HOOK_RETRACTED; + int Num = Controller->m_TeleCheckOuts[k].size(); + m_Core.m_Pos = Controller->m_TeleCheckOuts[k][(!Num)?Num:rand() % Num]; + GameWorld()->ReleaseHooked(GetPlayer()->GetCID()); + m_Core.m_Vel = vec2(0,0); + m_Core.m_HookPos = m_Core.m_Pos; + return; + } + } + // if no checkpointout have been found (or if there no recorded checkpoint), teleport to start + vec2 SpawnPos; + if(GameServer()->m_pController->CanSpawn(m_pPlayer->GetTeam(), &SpawnPos)) + { + m_Core.m_HookedPlayer = -1; + m_Core.m_HookState = HOOK_RETRACTED; + m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; + m_Core.m_HookState = HOOK_RETRACTED; + m_Core.m_Pos = SpawnPos; + GameWorld()->ReleaseHooked(GetPlayer()->GetCID()); + m_Core.m_Vel = vec2(0,0); + m_Core.m_HookPos = m_Core.m_Pos; + } return; } if(GameServer()->Collision()->IsCheckTeleport(MapIndex)) { + if (m_Super) + return; // first check if there is a TeleCheckOut for the current recorded checkpoint, if not check previous checkpoints for(int k=m_TeleCheckpoint-1; k >= 0; k--) { @@ -1476,6 +1825,59 @@ void CCharacter::HandleTiles(int Index) } } +void CCharacter::HandleTuneLayer() +{ + + m_TuneZoneOld = m_TuneZone; + int CurrentIndex = GameServer()->Collision()->GetMapIndex(m_Pos); + m_TuneZone = GameServer()->Collision()->IsTune(CurrentIndex); + + if(m_TuneZone) + m_Core.m_pWorld->m_Tuning[g_Config.m_ClDummy] = GameServer()->TuningList()[m_TuneZone]; // throw tunings from specific zone into gamecore + else + m_Core.m_pWorld->m_Tuning[g_Config.m_ClDummy] = *GameServer()->Tuning(); + + if (m_TuneZone != m_TuneZoneOld) // dont send tunigs all the time + { + // send zone msgs + SendZoneMsgs(); + } +} + +void CCharacter::SendZoneMsgs() +{ + // send zone leave msg + if (m_TuneZoneOld >= 0 && GameServer()->m_ZoneLeaveMsg[m_TuneZoneOld]) // m_TuneZoneOld >= 0: avoid zone leave msgs on spawn + { + const char* cur = GameServer()->m_ZoneLeaveMsg[m_TuneZoneOld]; + const char* pos; + while ((pos = str_find(cur, "\\n"))) + { + char aBuf[256]; + str_copy(aBuf, cur, pos - cur + 1); + aBuf[pos - cur + 1] = '\0'; + cur = pos + 2; + GameServer()->SendChatTarget(m_pPlayer->GetCID(), aBuf); + } + GameServer()->SendChatTarget(m_pPlayer->GetCID(), cur); + } + // send zone enter msg + if (GameServer()->m_ZoneEnterMsg[m_TuneZone]) + { + const char* cur = GameServer()->m_ZoneEnterMsg[m_TuneZone]; + const char* pos; + while ((pos = str_find(cur, "\\n"))) + { + char aBuf[256]; + str_copy(aBuf, cur, pos - cur + 1); + aBuf[pos - cur + 1] = '\0'; + cur = pos + 2; + GameServer()->SendChatTarget(m_pPlayer->GetCID(), aBuf); + } + GameServer()->SendChatTarget(m_pPlayer->GetCID(), cur); + } +} + void CCharacter::DDRaceTick() { m_Armor=(m_FreezeTime >= 0)?10-(m_FreezeTime/15):0; @@ -1484,9 +1886,9 @@ void CCharacter::DDRaceTick() if(m_FreezeTime > 0 || m_FreezeTime == -1) { - if (m_FreezeTime % Server()->TickSpeed() == 0 || m_FreezeTime == -1) + if (m_FreezeTime % Server()->TickSpeed() == Server()->TickSpeed() - 1 || m_FreezeTime == -1) { - GameServer()->CreateDamageInd(m_Pos, 0, m_FreezeTime / Server()->TickSpeed(), Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID())); + GameServer()->CreateDamageInd(m_Pos, 0, (m_FreezeTime + 1) / Server()->TickSpeed(), Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID())); } if(m_FreezeTime > 0) m_FreezeTime--; @@ -1498,7 +1900,9 @@ void CCharacter::DDRaceTick() if (m_FreezeTime == 1) UnFreeze(); } - + + HandleTuneLayer(); // need this before coretick + m_Core.m_Id = GetPlayer()->GetCID(); } @@ -1508,39 +1912,46 @@ void CCharacter::DDRacePostCoreTick() m_Time = (float)(Server()->Tick() - m_StartTime) / ((float)Server()->TickSpeed()); if (m_pPlayer->m_DefEmoteReset >= 0 && m_pPlayer->m_DefEmoteReset <= Server()->Tick()) - { - m_pPlayer->m_DefEmoteReset = -1; - m_EmoteType = m_pPlayer->m_DefEmote = EMOTE_NORMAL; - m_EmoteStop = -1; - } + { + m_pPlayer->m_DefEmoteReset = -1; + m_EmoteType = m_pPlayer->m_DefEmote = EMOTE_NORMAL; + m_EmoteStop = -1; + } - if (m_EndlessHook || (m_Super && g_Config.m_SvEndlessSuperHook)) - m_Core.m_HookTick = 0; + if (m_EndlessHook || (m_Super && g_Config.m_SvEndlessSuperHook)) + m_Core.m_HookTick = 0; - if (m_DeepFreeze && !m_Super) - Freeze(); + if (m_DeepFreeze && !m_Super) + Freeze(); - if (m_Super && m_Core.m_Jumped > 1) - m_Core.m_Jumped = 1; + if (m_Core.m_Jumps == 0 && !m_Super) + m_Core.m_Jumped = 3; + else if (m_Core.m_Jumps == 1 && m_Core.m_Jumped > 0) + m_Core.m_Jumped = 3; + else if (m_Core.m_JumpedTotal < m_Core.m_Jumps - 1 && m_Core.m_Jumped > 1) + m_Core.m_Jumped = 1; - int CurrentIndex = GameServer()->Collision()->GetMapIndex(m_Pos); - HandleSkippableTiles(CurrentIndex); + if ((m_Super || m_SuperJump) && m_Core.m_Jumped > 1) + m_Core.m_Jumped = 1; - // handle Anti-Skip tiles - std::list < int > Indices = GameServer()->Collision()->GetMapIndices(m_PrevPos, m_Pos); - if(!Indices.empty()) - for(std::list < int >::iterator i = Indices.begin(); i != Indices.end(); i++) - { - HandleTiles(*i); - //dbg_msg("Running","%d", *i); - } - else + int CurrentIndex = GameServer()->Collision()->GetMapIndex(m_Pos); + HandleSkippableTiles(CurrentIndex); + + // handle Anti-Skip tiles + std::list < int > Indices = GameServer()->Collision()->GetMapIndices(m_PrevPos, m_Pos); + if(!Indices.empty()) + for(std::list < int >::iterator i = Indices.begin(); i != Indices.end(); i++) { - HandleTiles(CurrentIndex); - //dbg_msg("Running","%d", CurrentIndex); + HandleTiles(*i); + //dbg_msg("Running","%d", *i); } + else + { + HandleTiles(CurrentIndex); + //dbg_msg("Running","%d", CurrentIndex); + } - HandleBroadcast(); + HandleBroadcast(); } bool CCharacter::Freeze(int Seconds) @@ -1577,12 +1988,12 @@ bool CCharacter::UnFreeze() { m_aWeapons[i].m_Ammo = -1; } - if(!m_aWeapons[m_ActiveWeapon].m_Got) - m_ActiveWeapon = WEAPON_GUN; + if(!m_aWeapons[m_Core.m_ActiveWeapon].m_Got) + m_Core.m_ActiveWeapon = WEAPON_GUN; m_FreezeTime = 0; m_FreezeTick = 0; - if (m_ActiveWeapon==WEAPON_HAMMER) m_ReloadTimer = 0; - return true; + if (m_Core.m_ActiveWeapon==WEAPON_HAMMER) m_ReloadTimer = 0; + return true; } return false; } @@ -1621,7 +2032,6 @@ void CCharacter::DDRaceInit() m_LastBroadcast = 0; m_TeamBeforeSuper = 0; m_Core.m_Id = GetPlayer()->GetCID(); - if(GetPlayer()->m_IsUsingDDRaceClient) ((CGameControllerDDRace*)GameServer()->m_pController)->m_Teams.SendTeamsState(GetPlayer()->GetCID()); if(g_Config.m_SvTeam == 2) { GameServer()->SendChatTarget(GetPlayer()->GetCID(),"Please join a team before you start"); @@ -1630,4 +2040,27 @@ void CCharacter::DDRaceInit() m_TeleCheckpoint = 0; m_EndlessHook = g_Config.m_SvEndlessDrag; m_Hit = g_Config.m_SvHit ? HIT_ALL : DISABLE_HIT_GRENADE|DISABLE_HIT_HAMMER|DISABLE_HIT_RIFLE|DISABLE_HIT_SHOTGUN; + m_SuperJump = false; + m_Jetpack = false; + m_Core.m_Jumps = 2; + m_FreezeHammer = false; + + int Team = Teams()->m_Core.Team(m_Core.m_Id); + + if(Teams()->TeamLocked(Team)) + { + for (int i = 0; i < MAX_CLIENTS; i++) + { + if(Teams()->m_Core.Team(i) == Team && i != m_Core.m_Id && GameServer()->m_apPlayers[i]) + { + CCharacter* pChar = GameServer()->m_apPlayers[i]->GetCharacter(); + + if (pChar) + { + m_DDRaceState = pChar->m_DDRaceState; + m_StartTime = pChar->m_StartTime; + } + } + } + } } diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index 39d2a4c282..4809d348ae 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -18,10 +18,22 @@ enum WEAPON_WORLD = -1, // death tiles etc }; +enum +{ + FAKETUNE_FREEZE = 1, + FAKETUNE_SOLO = 2, + FAKETUNE_NOJUMP = 4, + FAKETUNE_NOCOLL = 8, + FAKETUNE_NOHOOK = 16, + FAKETUNE_JETPACK = 32, +}; + class CCharacter : public CEntity { MACRO_ALLOC_POOL_ID() + friend class CSaveTee; // need to use core + public: //character's size static const int ms_PhysSize = 28; @@ -34,6 +46,8 @@ class CCharacter : public CEntity virtual void TickDefered(); virtual void TickPaused(); virtual void Snap(int SnappingClient); + virtual int NetworkClipped(int SnappingClient); + virtual int NetworkClipped(int SnappingClient, vec2 CheckPos); bool IsGrounded(); @@ -43,6 +57,7 @@ class CCharacter : public CEntity void HandleWeapons(); void HandleNinja(); + void HandleJetpack(); void OnPredictedInput(CNetObj_PlayerInput *pNewInput); void OnDirectInput(CNetObj_PlayerInput *pNewInput); @@ -63,6 +78,7 @@ class CCharacter : public CEntity void SetEmote(int Emote, int Tick); + int NeededFaketuning() {return m_NeededFaketuning;} bool IsAlive() const { return m_Alive; } bool IsPaused() const { return m_Paused; } class CPlayer *GetPlayer() { return m_pPlayer; } @@ -73,6 +89,7 @@ class CCharacter : public CEntity bool m_Alive; bool m_Paused; + int m_NeededFaketuning; // weapon info CEntity *m_apHitObjects[10]; @@ -87,7 +104,6 @@ class CCharacter : public CEntity } m_aWeapons[NUM_WEAPONS]; - int m_ActiveWeapon; int m_LastWeapon; int m_QueuedWeapon; @@ -101,6 +117,7 @@ class CCharacter : public CEntity // last tick that the player took any action ie some input int m_LastAction; + int m_LastNoAmmoSound; // these are non-heldback inputs CNetObj_PlayerInput m_LatestPrevInput; @@ -145,6 +162,8 @@ class CCharacter : public CEntity void DDRaceTick(); void DDRacePostCoreTick(); void HandleBroadcast(); + void HandleTuneLayer(); + void SendZoneMsgs(); public: CGameTeams* Teams(); void Pause(bool Pause); @@ -157,11 +176,15 @@ class CCharacter : public CEntity bool CanCollide(int ClientID); bool SameTeam(int ClientID); bool m_Super; + bool m_SuperJump; + bool m_Jetpack; + bool m_NinjaJetpack; int m_TeamBeforeSuper; int m_FreezeTime; int m_FreezeTick; bool m_DeepFreeze; bool m_EndlessHook; + bool m_FreezeHammer; enum { HIT_ALL=0, @@ -171,6 +194,10 @@ class CCharacter : public CEntity DISABLE_HIT_RIFLE=8 }; int m_Hit; + int m_Collision; + int m_TuneZone; + int m_TuneZoneOld; + int m_Hook; int m_PainSoundTimer; int m_LastMove; int m_StartTime; @@ -212,11 +239,13 @@ class CCharacter : public CEntity int m_TileSFlagsB; vec2 m_Intersection; int64 m_LastStartWarning; + bool m_LastPenalty; + // Setters/Getters because i don't want to modify vanilla vars access modifiers int GetLastWeapon() { return m_LastWeapon; }; void SetLastWeapon(int LastWeap) {m_LastWeapon = LastWeap; }; - int GetActiveWeapon() { return m_ActiveWeapon; }; - void SetActiveWeapon(int ActiveWeap) {m_ActiveWeapon = ActiveWeap; }; + int GetActiveWeapon() { return m_Core.m_ActiveWeapon; }; + void SetActiveWeapon(int ActiveWeap) {m_Core.m_ActiveWeapon = ActiveWeap; }; void SetLastAction(int LastAction) {m_LastAction = LastAction; }; int GetArmor() { return m_Armor; }; void SetArmor(int Armor) {m_Armor = Armor; }; diff --git a/src/game/server/entities/door.cpp b/src/game/server/entities/door.cpp index 4ec622e904..225407d844 100644 --- a/src/game/server/entities/door.cpp +++ b/src/game/server/entities/door.cpp @@ -72,13 +72,24 @@ void CDoor::Snap(int SnappingClient) CNetObj_Laser *pObj = static_cast(Server()->SnapNewItem( NETOBJTYPE_LASER, m_ID, sizeof(CNetObj_Laser))); + + if (!pObj) + return; + pObj->m_X = (int) m_Pos.x; pObj->m_Y = (int) m_Pos.y; CCharacter * Char = GameServer()->GetPlayerChar(SnappingClient); int Tick = (Server()->Tick() % Server()->TickSpeed()) % 11; + + if((GameServer()->m_apPlayers[SnappingClient]->GetTeam() == -1 + || GameServer()->m_apPlayers[SnappingClient]->m_Paused) + && GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID != SPEC_FREEVIEW) + Char = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID); + if (Char == 0) return; + if (Char->IsAlive() && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[Char->Team()] && (!Tick)) diff --git a/src/game/server/entities/dragger.cpp b/src/game/server/entities/dragger.cpp index 77b42582da..9b82ebf692 100644 --- a/src/game/server/entities/dragger.cpp +++ b/src/game/server/entities/dragger.cpp @@ -21,186 +21,233 @@ CDragger::CDragger(CGameWorld *pGameWorld, vec2 Pos, float Strength, bool NW, m_NW = NW; m_CatchedTeam = CatchedTeam; GameWorld()->InsertEntity(this); + + for (int i = 0; i < MAX_CLIENTS; i++) + { + m_SoloIDs[i] = -1; + } } void CDragger::Move() { - if (m_Target && m_Target->IsAlive() + if (m_Target && (!m_Target->IsAlive() || (m_Target->IsAlive() && (m_Target->m_Super || m_Target->IsPaused() || (m_Layer == LAYER_SWITCH - && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[m_Target->Team()]))) + && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[m_Target->Team()]))))) m_Target = 0; - if (m_Target) - return; - CCharacter *Ents[16]; + + mem_zero(m_SoloEnts, sizeof(m_SoloEnts)); + CCharacter *TempEnts[MAX_CLIENTS]; + int Num = GameServer()->m_World.FindEntities(m_Pos, LENGTH, - (CEntity**) Ents, 16, CGameWorld::ENTTYPE_CHARACTER); + (CEntity**) m_SoloEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); + mem_copy(TempEnts, m_SoloEnts, sizeof(TempEnts)); + int Id = -1; int MinLen = 0; + CCharacter *Temp; for (int i = 0; i < Num; i++) { - m_Target = Ents[i]; - if (m_Target->Team() != m_CatchedTeam) + Temp = m_SoloEnts[i]; + if (Temp->Team() != m_CatchedTeam) + { + m_SoloEnts[i] = 0; continue; + } if (m_Layer == LAYER_SWITCH - && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[m_Target->Team()]) + && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[Temp->Team()]) + { + m_SoloEnts[i] = 0; continue; + } int Res = m_NW ? GameServer()->Collision()->IntersectNoLaserNW(m_Pos, - m_Target->m_Pos, 0, 0) : + Temp->m_Pos, 0, 0) : GameServer()->Collision()->IntersectNoLaser(m_Pos, - m_Target->m_Pos, 0, 0); + Temp->m_Pos, 0, 0); if (Res == 0) { - int Len = length(m_Target->m_Pos - m_Pos); + int Len = length(Temp->m_Pos - m_Pos); if (MinLen == 0 || MinLen > Len) { MinLen = Len; Id = i; } + + if (!Temp->Teams()->m_Core.GetSolo(Temp->GetPlayer()->GetCID())) + m_SoloEnts[i] = 0; + } + else + { + m_SoloEnts[i] = 0; + } + } + + if (!m_Target) + m_Target = Id != -1 ? TempEnts[Id] : 0; + + if (m_Target) + { + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (m_SoloEnts[i] == m_Target) + m_SoloEnts[i] = 0; } } - m_Target = Id != -1 ? Ents[Id] : 0; } void CDragger::Drag() { if (m_Target) { + CCharacter *Target = m_Target; - int Res = 0; - if (!m_NW) - Res = GameServer()->Collision()->IntersectNoLaser(m_Pos, - m_Target->m_Pos, 0, 0); - else - Res = GameServer()->Collision()->IntersectNoLaserNW(m_Pos, - m_Target->m_Pos, 0, 0); - if (Res || length(m_Pos - m_Target->m_Pos) > 700) - { - m_Target = 0; - } - else if (length(m_Pos - m_Target->m_Pos) > 28) + for (int i = -1; i < MAX_CLIENTS; i++) { - vec2 Temp = m_Target->Core()->m_Vel - + (normalize(m_Pos - m_Target->m_Pos) * m_Strength); - if (Temp.x > 0 - && ((m_Target->m_TileIndex == TILE_STOP - && m_Target->m_TileFlags == ROTATION_270) - || (m_Target->m_TileIndexL == TILE_STOP - && m_Target->m_TileFlagsL == ROTATION_270) - || (m_Target->m_TileIndexL == TILE_STOPS - && (m_Target->m_TileFlagsL == ROTATION_90 - || m_Target->m_TileFlagsL - == ROTATION_270)) - || (m_Target->m_TileIndexL == TILE_STOPA) - || (m_Target->m_TileFIndex == TILE_STOP - && m_Target->m_TileFFlags == ROTATION_270) - || (m_Target->m_TileFIndexL == TILE_STOP - && m_Target->m_TileFFlagsL == ROTATION_270) - || (m_Target->m_TileFIndexL == TILE_STOPS - && (m_Target->m_TileFFlagsL == ROTATION_90 - || m_Target->m_TileFFlagsL - == ROTATION_270)) - || (m_Target->m_TileFIndexL == TILE_STOPA) - || (m_Target->m_TileSIndex == TILE_STOP - && m_Target->m_TileSFlags == ROTATION_270) - || (m_Target->m_TileSIndexL == TILE_STOP - && m_Target->m_TileSFlagsL == ROTATION_270) - || (m_Target->m_TileSIndexL == TILE_STOPS - && (m_Target->m_TileSFlagsL == ROTATION_90 - || m_Target->m_TileSFlagsL - == ROTATION_270)) - || (m_Target->m_TileSIndexL == TILE_STOPA))) - Temp.x = 0; - if (Temp.x < 0 - && ((m_Target->m_TileIndex == TILE_STOP - && m_Target->m_TileFlags == ROTATION_90) - || (m_Target->m_TileIndexR == TILE_STOP - && m_Target->m_TileFlagsR == ROTATION_90) - || (m_Target->m_TileIndexR == TILE_STOPS - && (m_Target->m_TileFlagsR == ROTATION_90 - || m_Target->m_TileFlagsR - == ROTATION_270)) - || (m_Target->m_TileIndexR == TILE_STOPA) - || (m_Target->m_TileFIndex == TILE_STOP - && m_Target->m_TileFFlags == ROTATION_90) - || (m_Target->m_TileFIndexR == TILE_STOP - && m_Target->m_TileFFlagsR == ROTATION_90) - || (m_Target->m_TileFIndexR == TILE_STOPS - && (m_Target->m_TileFFlagsR == ROTATION_90 - || m_Target->m_TileFFlagsR - == ROTATION_270)) - || (m_Target->m_TileFIndexR == TILE_STOPA) - || (m_Target->m_TileSIndex == TILE_STOP - && m_Target->m_TileSFlags == ROTATION_90) - || (m_Target->m_TileSIndexR == TILE_STOP - && m_Target->m_TileSFlagsR == ROTATION_90) - || (m_Target->m_TileSIndexR == TILE_STOPS - && (m_Target->m_TileSFlagsR == ROTATION_90 - || m_Target->m_TileSFlagsR - == ROTATION_270)) - || (m_Target->m_TileSIndexR == TILE_STOPA))) - Temp.x = 0; - if (Temp.y < 0 - && ((m_Target->m_TileIndex == TILE_STOP - && m_Target->m_TileFlags == ROTATION_180) - || (m_Target->m_TileIndexB == TILE_STOP - && m_Target->m_TileFlagsB == ROTATION_180) - || (m_Target->m_TileIndexB == TILE_STOPS - && (m_Target->m_TileFlagsB == ROTATION_0 - || m_Target->m_TileFlagsB - == ROTATION_180)) - || (m_Target->m_TileIndexB == TILE_STOPA) - || (m_Target->m_TileFIndex == TILE_STOP - && m_Target->m_TileFFlags == ROTATION_180) - || (m_Target->m_TileFIndexB == TILE_STOP - && m_Target->m_TileFFlagsB == ROTATION_180) - || (m_Target->m_TileFIndexB == TILE_STOPS - && (m_Target->m_TileFFlagsB == ROTATION_0 - || m_Target->m_TileFFlagsB - == ROTATION_180)) - || (m_Target->m_TileFIndexB == TILE_STOPA) - || (m_Target->m_TileSIndex == TILE_STOP - && m_Target->m_TileSFlags == ROTATION_180) - || (m_Target->m_TileSIndexB == TILE_STOP - && m_Target->m_TileSFlagsB == ROTATION_180) - || (m_Target->m_TileSIndexB == TILE_STOPS - && (m_Target->m_TileSFlagsB == ROTATION_0 - || m_Target->m_TileSFlagsB - == ROTATION_180)) - || (m_Target->m_TileSIndexB == TILE_STOPA))) - Temp.y = 0; - if (Temp.y > 0 - && ((m_Target->m_TileIndex == TILE_STOP - && m_Target->m_TileFlags == ROTATION_0) - || (m_Target->m_TileIndexT == TILE_STOP - && m_Target->m_TileFlagsT == ROTATION_0) - || (m_Target->m_TileIndexT == TILE_STOPS - && (m_Target->m_TileFlagsT == ROTATION_0 - || m_Target->m_TileFlagsT - == ROTATION_180)) - || (m_Target->m_TileIndexT == TILE_STOPA) - || (m_Target->m_TileFIndex == TILE_STOP - && m_Target->m_TileFFlags == ROTATION_0) - || (m_Target->m_TileFIndexT == TILE_STOP - && m_Target->m_TileFFlagsT == ROTATION_0) - || (m_Target->m_TileFIndexT == TILE_STOPS - && (m_Target->m_TileFFlagsT == ROTATION_0 - || m_Target->m_TileFFlagsT - == ROTATION_180)) - || (m_Target->m_TileFIndexT == TILE_STOPA) - || (m_Target->m_TileSIndex == TILE_STOP - && m_Target->m_TileSFlags == ROTATION_0) - || (m_Target->m_TileSIndexT == TILE_STOP - && m_Target->m_TileSFlagsT == ROTATION_0) - || (m_Target->m_TileSIndexT == TILE_STOPS - && (m_Target->m_TileSFlagsT == ROTATION_0 - || m_Target->m_TileSFlagsT - == ROTATION_180)) - || (m_Target->m_TileSIndexT == TILE_STOPA))) - Temp.y = 0; - m_Target->Core()->m_Vel = Temp; + if (i >= 0) + Target = m_SoloEnts[i]; + + if (!Target) + continue; + + int Res = 0; + if (!m_NW) + Res = GameServer()->Collision()->IntersectNoLaser(m_Pos, + Target->m_Pos, 0, 0); + else + Res = GameServer()->Collision()->IntersectNoLaserNW(m_Pos, + Target->m_Pos, 0, 0); + if (Res || length(m_Pos - Target->m_Pos) > 700) + { + Target = 0; + if (i == -1) + m_Target = 0; + else + m_SoloEnts[i] = 0; + } + else if (length(m_Pos - Target->m_Pos) > 28) + { + vec2 Temp = Target->Core()->m_Vel + + (normalize(m_Pos - Target->m_Pos) * m_Strength); + if (Temp.x > 0 + && ((Target->m_TileIndex == TILE_STOP + && Target->m_TileFlags == ROTATION_270) + || (Target->m_TileIndexL == TILE_STOP + && Target->m_TileFlagsL == ROTATION_270) + || (Target->m_TileIndexL == TILE_STOPS + && (Target->m_TileFlagsL == ROTATION_90 + || Target->m_TileFlagsL + == ROTATION_270)) + || (Target->m_TileIndexL == TILE_STOPA) + || (Target->m_TileFIndex == TILE_STOP + && Target->m_TileFFlags == ROTATION_270) + || (Target->m_TileFIndexL == TILE_STOP + && Target->m_TileFFlagsL == ROTATION_270) + || (Target->m_TileFIndexL == TILE_STOPS + && (Target->m_TileFFlagsL == ROTATION_90 + || Target->m_TileFFlagsL + == ROTATION_270)) + || (Target->m_TileFIndexL == TILE_STOPA) + || (Target->m_TileSIndex == TILE_STOP + && Target->m_TileSFlags == ROTATION_270) + || (Target->m_TileSIndexL == TILE_STOP + && Target->m_TileSFlagsL == ROTATION_270) + || (Target->m_TileSIndexL == TILE_STOPS + && (Target->m_TileSFlagsL == ROTATION_90 + || Target->m_TileSFlagsL + == ROTATION_270)) + || (Target->m_TileSIndexL == TILE_STOPA))) + Temp.x = 0; + if (Temp.x < 0 + && ((Target->m_TileIndex == TILE_STOP + && Target->m_TileFlags == ROTATION_90) + || (Target->m_TileIndexR == TILE_STOP + && Target->m_TileFlagsR == ROTATION_90) + || (Target->m_TileIndexR == TILE_STOPS + && (Target->m_TileFlagsR == ROTATION_90 + || Target->m_TileFlagsR + == ROTATION_270)) + || (Target->m_TileIndexR == TILE_STOPA) + || (Target->m_TileFIndex == TILE_STOP + && Target->m_TileFFlags == ROTATION_90) + || (Target->m_TileFIndexR == TILE_STOP + && Target->m_TileFFlagsR == ROTATION_90) + || (Target->m_TileFIndexR == TILE_STOPS + && (Target->m_TileFFlagsR == ROTATION_90 + || Target->m_TileFFlagsR + == ROTATION_270)) + || (Target->m_TileFIndexR == TILE_STOPA) + || (Target->m_TileSIndex == TILE_STOP + && Target->m_TileSFlags == ROTATION_90) + || (Target->m_TileSIndexR == TILE_STOP + && Target->m_TileSFlagsR == ROTATION_90) + || (Target->m_TileSIndexR == TILE_STOPS + && (Target->m_TileSFlagsR == ROTATION_90 + || Target->m_TileSFlagsR + == ROTATION_270)) + || (Target->m_TileSIndexR == TILE_STOPA))) + Temp.x = 0; + if (Temp.y < 0 + && ((Target->m_TileIndex == TILE_STOP + && Target->m_TileFlags == ROTATION_180) + || (Target->m_TileIndexB == TILE_STOP + && Target->m_TileFlagsB == ROTATION_180) + || (Target->m_TileIndexB == TILE_STOPS + && (Target->m_TileFlagsB == ROTATION_0 + || Target->m_TileFlagsB + == ROTATION_180)) + || (Target->m_TileIndexB == TILE_STOPA) + || (Target->m_TileFIndex == TILE_STOP + && Target->m_TileFFlags == ROTATION_180) + || (Target->m_TileFIndexB == TILE_STOP + && Target->m_TileFFlagsB == ROTATION_180) + || (Target->m_TileFIndexB == TILE_STOPS + && (Target->m_TileFFlagsB == ROTATION_0 + || Target->m_TileFFlagsB + == ROTATION_180)) + || (Target->m_TileFIndexB == TILE_STOPA) + || (Target->m_TileSIndex == TILE_STOP + && Target->m_TileSFlags == ROTATION_180) + || (Target->m_TileSIndexB == TILE_STOP + && Target->m_TileSFlagsB == ROTATION_180) + || (Target->m_TileSIndexB == TILE_STOPS + && (Target->m_TileSFlagsB == ROTATION_0 + || Target->m_TileSFlagsB + == ROTATION_180)) + || (Target->m_TileSIndexB == TILE_STOPA))) + Temp.y = 0; + if (Temp.y > 0 + && ((Target->m_TileIndex == TILE_STOP + && Target->m_TileFlags == ROTATION_0) + || (Target->m_TileIndexT == TILE_STOP + && Target->m_TileFlagsT == ROTATION_0) + || (Target->m_TileIndexT == TILE_STOPS + && (Target->m_TileFlagsT == ROTATION_0 + || Target->m_TileFlagsT + == ROTATION_180)) + || (Target->m_TileIndexT == TILE_STOPA) + || (Target->m_TileFIndex == TILE_STOP + && Target->m_TileFFlags == ROTATION_0) + || (Target->m_TileFIndexT == TILE_STOP + && Target->m_TileFFlagsT == ROTATION_0) + || (Target->m_TileFIndexT == TILE_STOPS + && (Target->m_TileFFlagsT == ROTATION_0 + || Target->m_TileFFlagsT + == ROTATION_180)) + || (Target->m_TileFIndexT == TILE_STOPA) + || (Target->m_TileSIndex == TILE_STOP + && Target->m_TileSFlags == ROTATION_0) + || (Target->m_TileSIndexT == TILE_STOP + && Target->m_TileSFlagsT == ROTATION_0) + || (Target->m_TileSIndexT == TILE_STOPS + && (Target->m_TileSFlagsT == ROTATION_0 + || Target->m_TileSFlagsT + == ROTATION_180)) + || (Target->m_TileSIndexT == TILE_STOPA))) + Temp.y = 0; + Target->Core()->m_Vel = Temp; + } } } } @@ -238,57 +285,107 @@ void CDragger::Snap(int SnappingClient) if (((CGameControllerDDRace*) GameServer()->m_pController)->m_Teams.GetTeamState( m_CatchedTeam) == CGameTeams::TEAMSTATE_EMPTY) return; - if (m_Target) - { - if (NetworkClipped(SnappingClient, m_Pos) - && NetworkClipped(SnappingClient, m_Target->m_Pos)) - return; - } - else if (NetworkClipped(SnappingClient, m_Pos)) - return; - CCharacter * Char = GameServer()->GetPlayerChar(SnappingClient); - int Tick = (Server()->Tick() % Server()->TickSpeed()) % 11; - if (Char && Char->IsAlive() - && (m_Layer == LAYER_SWITCH - && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[Char->Team()] - && (!Tick))) - return; - if (Char && Char->IsAlive()) - { - if (Char->Team() != m_CatchedTeam) - return; - } - else - { - // send to spectators only active draggers and some inactive from team 0 - if (!((m_Target && m_Target->IsAlive()) || m_CatchedTeam == 0)) - return; - } + CCharacter *Target = m_Target; - CNetObj_Laser *obj = static_cast(Server()->SnapNewItem( - NETOBJTYPE_LASER, m_ID, sizeof(CNetObj_Laser))); - if (!obj) - return; - obj->m_X = (int) m_Pos.x; - obj->m_Y = (int) m_Pos.y; - if (m_Target) + for (int i = 0; i < MAX_CLIENTS; i++) { - obj->m_FromX = (int) m_Target->m_Pos.x; - obj->m_FromY = (int) m_Target->m_Pos.y; + if (m_SoloIDs[i] == -1) + break; + + Server()->SnapFreeID(m_SoloIDs[i]); + m_SoloIDs[i] = -1; } - else + + int pos = 0; + + for (int i = -1; i < MAX_CLIENTS; i++) { - obj->m_FromX = (int) m_Pos.x; - obj->m_FromY = (int) m_Pos.y; - } + if (i >= 0) + { + Target = m_SoloEnts[i]; + + if (!Target) + continue; + } - int StartTick = m_EvalTick; - if (StartTick < Server()->Tick() - 4) - StartTick = Server()->Tick() - 4; - else if (StartTick > Server()->Tick()) - StartTick = Server()->Tick(); - obj->m_StartTick = StartTick; + if (Target) + { + if (NetworkClipped(SnappingClient, m_Pos) + && NetworkClipped(SnappingClient, Target->m_Pos)) + continue; + } + else if (NetworkClipped(SnappingClient, m_Pos)) + continue; + + CCharacter * Char = GameServer()->GetPlayerChar(SnappingClient); + + if((GameServer()->m_apPlayers[SnappingClient]->GetTeam() == -1 + || GameServer()->m_apPlayers[SnappingClient]->m_Paused) + && GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID != SPEC_FREEVIEW) + Char = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID); + + int Tick = (Server()->Tick() % Server()->TickSpeed()) % 11; + if (Char && Char->IsAlive() + && (m_Layer == LAYER_SWITCH + && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[Char->Team()] + && (!Tick))) + continue; + if (Char && Char->IsAlive()) + { + if (Char->Team() != m_CatchedTeam) + continue; + } + else + { + // send to spectators only active draggers and some inactive from team 0 + if (!((Target && Target->IsAlive()) || m_CatchedTeam == 0)) + continue; + } + + if (Char && Char->IsAlive() && Target && Target->GetPlayer()->GetCID() != SnappingClient && !Char->GetPlayer()->m_ShowOthers && + (Char->Teams()->m_Core.GetSolo(SnappingClient) || Char->Teams()->m_Core.GetSolo(Target->GetPlayer()->GetCID()))) + { + continue; + } + + CNetObj_Laser *obj; + + if (i == -1) + { + obj = static_cast(Server()->SnapNewItem( + NETOBJTYPE_LASER, m_ID, sizeof(CNetObj_Laser))); + } + else + { + m_SoloIDs[pos] = Server()->SnapNewID(); + obj = static_cast(Server()->SnapNewItem( // TODO: Have to free IDs again? + NETOBJTYPE_LASER, m_SoloIDs[pos], sizeof(CNetObj_Laser))); + pos++; + } + + if (!obj) + continue; + obj->m_X = (int) m_Pos.x; + obj->m_Y = (int) m_Pos.y; + if (Target) + { + obj->m_FromX = (int) Target->m_Pos.x; + obj->m_FromY = (int) Target->m_Pos.y; + } + else + { + obj->m_FromX = (int) m_Pos.x; + obj->m_FromY = (int) m_Pos.y; + } + + int StartTick = m_EvalTick; + if (StartTick < Server()->Tick() - 4) + StartTick = Server()->Tick() - 4; + else if (StartTick > Server()->Tick()) + StartTick = Server()->Tick(); + obj->m_StartTick = StartTick; + } } CDraggerTeam::CDraggerTeam(CGameWorld *pGameWorld, vec2 Pos, float Strength, diff --git a/src/game/server/entities/dragger.h b/src/game/server/entities/dragger.h index 3dea744b99..0bf812d6c3 100644 --- a/src/game/server/entities/dragger.h +++ b/src/game/server/entities/dragger.h @@ -15,6 +15,9 @@ class CDragger: public CEntity CCharacter * m_Target; bool m_NW; int m_CatchedTeam; + + CCharacter * m_SoloEnts[MAX_CLIENTS]; + int m_SoloIDs[MAX_CLIENTS]; public: CDragger(CGameWorld *pGameWorld, vec2 Pos, float Strength, bool NW, diff --git a/src/game/server/entities/gun.cpp b/src/game/server/entities/gun.cpp index 77dbf41c5e..9f9d7ca7a2 100644 --- a/src/game/server/entities/gun.cpp +++ b/src/game/server/entities/gun.cpp @@ -3,6 +3,8 @@ #include #include #include +#include + #include "gun.h" #include "plasma.h" @@ -26,17 +28,17 @@ CGun::CGun(CGameWorld *pGameWorld, vec2 Pos, bool Freeze, bool Explosive, int La void CGun::Fire() { - CCharacter *Ents[16]; - int IdInTeam[16]; - int LenInTeam[16]; - for (int i = 0; i < 16; i++) + CCharacter *Ents[MAX_CLIENTS]; + int IdInTeam[MAX_CLIENTS]; + int LenInTeam[MAX_CLIENTS]; + for (int i = 0; i < MAX_CLIENTS; i++) { IdInTeam[i] = -1; LenInTeam[i] = 0; } int Num = -1; - Num = GameServer()->m_World.FindEntities(m_Pos, g_Config.m_SvPlasmaRange, (CEntity**)Ents, 16, CGameWorld::ENTTYPE_CHARACTER); + Num = GameServer()->m_World.FindEntities(m_Pos, g_Config.m_SvPlasmaRange, (CEntity**)Ents, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); for (int i = 0; i < Num; i++) { @@ -57,7 +59,7 @@ void CGun::Fire() } } } - for (int i = 0; i < 16; i++) + for (int i = 0; i < MAX_CLIENTS; i++) { if(IdInTeam[i] != -1) { @@ -66,6 +68,22 @@ void CGun::Fire() m_LastFire = Server()->Tick(); } } + for (int i = 0; i < Num; i++) + { + CCharacter *Target = Ents[i]; + if (Target->Teams()->m_Core.GetSolo(Target->GetPlayer()->GetCID())) + { + if (IdInTeam[Target->Team()] != i) + { + int res = GameServer()->Collision()->IntersectLine(m_Pos, Target->m_Pos,0,0,false); + if (!res) + { + new CPlasma(&GameServer()->m_World, m_Pos, normalize(Target->m_Pos - m_Pos), m_Freeze, m_Explosive, Target->Team()); + m_LastFire = Server()->Tick(); + } + } + } + } } @@ -97,10 +115,20 @@ void CGun::Snap(int SnappingClient) if(NetworkClipped(SnappingClient)) return; - CCharacter * SnapChar = GameServer()->GetPlayerChar(SnappingClient); + CCharacter *Char = GameServer()->GetPlayerChar(SnappingClient); + + if((GameServer()->m_apPlayers[SnappingClient]->GetTeam() == -1 + || GameServer()->m_apPlayers[SnappingClient]->m_Paused) + && GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID != SPEC_FREEVIEW) + Char = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID); + int Tick = (Server()->Tick()%Server()->TickSpeed())%11; - if (SnapChar && SnapChar->IsAlive() && (m_Layer == LAYER_SWITCH && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[SnapChar->Team()]) && (!Tick)) return; + if (Char && Char->IsAlive() && (m_Layer == LAYER_SWITCH && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[Char->Team()]) && (!Tick)) return; CNetObj_Laser *pObj = static_cast(Server()->SnapNewItem(NETOBJTYPE_LASER, m_ID, sizeof(CNetObj_Laser))); + + if (!pObj) + return; + pObj->m_X = (int)m_Pos.x; pObj->m_Y = (int)m_Pos.y; pObj->m_FromX = (int)m_Pos.x; diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp index 12efc6de26..00c3de1e1d 100644 --- a/src/game/server/entities/laser.cpp +++ b/src/game/server/entities/laser.cpp @@ -2,6 +2,7 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include #include +#include #include "laser.h" #include @@ -16,10 +17,13 @@ CLaser::CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEner m_Dir = Direction; m_Bounces = 0; m_EvalTick = 0; + m_TelePos = vec2(0,0); + m_WasTele = false; m_Type = Type; + m_TuneZone = GameServer()->Collision()->IsTune(GameServer()->Collision()->GetMapIndex(m_Pos)); + m_TeamMask = GameServer()->GetPlayerChar(Owner) ? GameServer()->GetPlayerChar(Owner)->Teams()->TeamMask(GameServer()->GetPlayerChar(Owner)->Team(), -1, m_Owner) : 0; GameWorld()->InsertEntity(this); DoBounce(); - m_TeamMask = GameServer()->GetPlayerChar(Owner) ? GameServer()->GetPlayerChar(Owner)->Teams()->TeamMask(GameServer()->GetPlayerChar(Owner)->Team(), -1, m_Owner) : 0; } @@ -28,11 +32,12 @@ bool CLaser::HitCharacter(vec2 From, vec2 To) vec2 At; CCharacter *pOwnerChar = GameServer()->GetPlayerChar(m_Owner); CCharacter *pHit; + bool pDontHitSelf = g_Config.m_SvOldLaser || (m_Bounces == 0 && !m_WasTele); if(pOwnerChar ? (!(pOwnerChar->m_Hit&CCharacter::DISABLE_HIT_RIFLE) && m_Type == WEAPON_RIFLE) || (!(pOwnerChar->m_Hit&CCharacter::DISABLE_HIT_SHOTGUN) && m_Type == WEAPON_SHOTGUN) : g_Config.m_SvHit) - pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, g_Config.m_SvOldLaser || m_Bounces == 0 ? pOwnerChar : 0, m_Owner); + pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner); else - pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, g_Config.m_SvOldLaser || m_Bounces == 0 ? pOwnerChar : 0, m_Owner, pOwnerChar); + pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner, pOwnerChar); if(!pHit || (pHit == pOwnerChar && g_Config.m_SvOldLaser) || (pHit != pOwnerChar && pOwnerChar ? (pOwnerChar->m_Hit&CCharacter::DISABLE_HIT_RIFLE && m_Type == WEAPON_RIFLE) || (pOwnerChar->m_Hit&CCharacter::DISABLE_HIT_SHOTGUN && m_Type == WEAPON_SHOTGUN) : !g_Config.m_SvHit)) return false; @@ -42,10 +47,17 @@ bool CLaser::HitCharacter(vec2 From, vec2 To) if (m_Type == WEAPON_SHOTGUN) { vec2 Temp; + + float Strength; + if (!m_TuneZone) + Strength = GameServer()->Tuning()->m_ShotgunStrength; + else + Strength = GameServer()->TuningList()[m_TuneZone].m_ShotgunStrength; + if(!g_Config.m_SvOldLaser) - Temp = pHit->Core()->m_Vel + normalize(m_PrevPos - pHit->Core()->m_Pos) * 10; + Temp = pHit->Core()->m_Vel + normalize(m_PrevPos - pHit->Core()->m_Pos) * Strength; else - Temp = pHit->Core()->m_Vel + normalize(pOwnerChar->Core()->m_Pos - pHit->Core()->m_Pos) * 10; + Temp = pHit->Core()->m_Vel + normalize(pOwnerChar->Core()->m_Pos - pHit->Core()->m_Pos) * Strength; if(Temp.x > 0 && ((pHit->m_TileIndex == TILE_STOP && pHit->m_TileFlags == ROTATION_270) || (pHit->m_TileIndexL == TILE_STOP && pHit->m_TileFlagsL == ROTATION_270) || (pHit->m_TileIndexL == TILE_STOPS && (pHit->m_TileFlagsL == ROTATION_90 || pHit->m_TileFlagsL ==ROTATION_270)) || (pHit->m_TileIndexL == TILE_STOPA) || (pHit->m_TileFIndex == TILE_STOP && pHit->m_TileFFlags == ROTATION_270) || (pHit->m_TileFIndexL == TILE_STOP && pHit->m_TileFFlagsL == ROTATION_270) || (pHit->m_TileFIndexL == TILE_STOPS && (pHit->m_TileFFlagsL == ROTATION_90 || pHit->m_TileFFlagsL == ROTATION_270)) || (pHit->m_TileFIndexL == TILE_STOPA) || (pHit->m_TileSIndex == TILE_STOP && pHit->m_TileSFlags == ROTATION_270) || (pHit->m_TileSIndexL == TILE_STOP && pHit->m_TileSFlagsL == ROTATION_270) || (pHit->m_TileSIndexL == TILE_STOPS && (pHit->m_TileSFlagsL == ROTATION_90 || pHit->m_TileSFlagsL == ROTATION_270)) || (pHit->m_TileSIndexL == TILE_STOPA))) Temp.x = 0; if(Temp.x < 0 && ((pHit->m_TileIndex == TILE_STOP && pHit->m_TileFlags == ROTATION_90) || (pHit->m_TileIndexR == TILE_STOP && pHit->m_TileFlagsR == ROTATION_90) || (pHit->m_TileIndexR == TILE_STOPS && (pHit->m_TileFlagsR == ROTATION_90 || pHit->m_TileFlagsR == ROTATION_270)) || (pHit->m_TileIndexR == TILE_STOPA) || (pHit->m_TileFIndex == TILE_STOP && pHit->m_TileFFlags == ROTATION_90) || (pHit->m_TileFIndexR == TILE_STOP && pHit->m_TileFFlagsR == ROTATION_90) || (pHit->m_TileFIndexR == TILE_STOPS && (pHit->m_TileFFlagsR == ROTATION_90 || pHit->m_TileFFlagsR == ROTATION_270)) || (pHit->m_TileFIndexR == TILE_STOPA) || (pHit->m_TileSIndex == TILE_STOP && pHit->m_TileSFlags == ROTATION_90) || (pHit->m_TileSIndexR == TILE_STOP && pHit->m_TileSFlagsR == ROTATION_90) || (pHit->m_TileSIndexR == TILE_STOPS && (pHit->m_TileSFlagsR == ROTATION_90 || pHit->m_TileSFlagsR == ROTATION_270)) || (pHit->m_TileSIndexR == TILE_STOPA))) @@ -73,11 +85,21 @@ void CLaser::DoBounce() return; } m_PrevPos = m_Pos; - vec2 To = m_Pos + m_Dir * m_Energy; vec2 Coltile; int Res; - Res = GameServer()->Collision()->IntersectLine(m_Pos, To, &Coltile, &To,false); + int z; + + if (m_WasTele) + { + m_PrevPos = m_TelePos; + m_Pos = m_TelePos; + m_TelePos = vec2(0,0); + } + + vec2 To = m_Pos + m_Dir * m_Energy; + + Res = GameServer()->Collision()->IntersectLineTeleWeapon(m_Pos, To, &Coltile, &To, &z, false); if(Res) { @@ -93,21 +115,39 @@ void CLaser::DoBounce() int f = 0; if(Res == -1) { - f = GameServer()->Collision()->GetTile(round(Coltile.x), round(Coltile.y)); - GameServer()->Collision()->SetCollisionAt(round(Coltile.x), round(Coltile.y), CCollision::COLFLAG_SOLID); + f = GameServer()->Collision()->GetTile(round_to_int(Coltile.x), round_to_int(Coltile.y)); + GameServer()->Collision()->SetCollisionAt(round_to_int(Coltile.x), round_to_int(Coltile.y), CCollision::COLFLAG_SOLID); } GameServer()->Collision()->MovePoint(&TempPos, &TempDir, 1.0f, 0); if(Res == -1) { - GameServer()->Collision()->SetCollisionAt(round(Coltile.x), round(Coltile.y), f); + GameServer()->Collision()->SetCollisionAt(round_to_int(Coltile.x), round_to_int(Coltile.y), f); } m_Pos = TempPos; m_Dir = normalize(TempDir); - m_Energy -= distance(m_From, m_Pos) + GameServer()->Tuning()->m_LaserBounceCost; - m_Bounces++; + if (!m_TuneZone) + m_Energy -= distance(m_From, m_Pos) + GameServer()->Tuning()->m_LaserBounceCost; + else + m_Energy -= distance(m_From, m_Pos) + GameServer()->TuningList()[m_TuneZone].m_LaserBounceCost; - if(m_Bounces > GameServer()->Tuning()->m_LaserBounceNum) + if (Res&CCollision::COLFLAG_TELE && ((CGameControllerDDRace*)GameServer()->m_pController)->m_TeleOuts[z-1].size()) + { + int Num = ((CGameControllerDDRace*)GameServer()->m_pController)->m_TeleOuts[z-1].size(); + m_TelePos = ((CGameControllerDDRace*)GameServer()->m_pController)->m_TeleOuts[z-1][(!Num)?Num:rand() % Num]; + m_WasTele = true; + } + else + { + m_Bounces++; + m_WasTele = false; + } + + int BounceNum = GameServer()->Tuning()->m_LaserBounceNum; + if (m_TuneZone) + BounceNum = GameServer()->TuningList()[m_TuneZone].m_LaserBounceNum; + + if(m_Bounces > BounceNum) m_Energy = -1; GameServer()->CreateSound(m_Pos, SOUND_RIFLE_BOUNCE, m_TeamMask); @@ -132,8 +172,14 @@ void CLaser::Reset() void CLaser::Tick() { - if(Server()->Tick() > m_EvalTick+(Server()->TickSpeed()*GameServer()->Tuning()->m_LaserBounceDelay)/1000.0f) - DoBounce(); + float Delay; + if (m_TuneZone) + Delay = GameServer()->TuningList()[m_TuneZone].m_LaserBounceDelay; + else + Delay = GameServer()->Tuning()->m_LaserBounceDelay; + + if(Server()->Tick() > m_EvalTick+(Server()->TickSpeed()*Delay/1000.0f)) + DoBounce(); } void CLaser::TickPaused() @@ -145,13 +191,22 @@ void CLaser::Snap(int SnappingClient) { if(NetworkClipped(SnappingClient)) return; - CCharacter * SnappingChar = GameServer()->GetPlayerChar(SnappingClient); CCharacter * OwnerChar = 0; if(m_Owner >= 0) OwnerChar = GameServer()->GetPlayerChar(m_Owner); if(!OwnerChar) return; - if(SnappingChar && !SnappingChar->CanCollide(m_Owner)) + + CCharacter *pOwnerChar = 0; + int64_t TeamMask = -1LL; + + if(m_Owner >= 0) + pOwnerChar = GameServer()->GetPlayerChar(m_Owner); + + if (pOwnerChar && pOwnerChar->IsAlive()) + TeamMask = pOwnerChar->Teams()->TeamMask(pOwnerChar->Team(), -1, m_Owner); + + if(!CmaskIsSet(TeamMask, SnappingClient)) return; CNetObj_Laser *pObj = static_cast(Server()->SnapNewItem(NETOBJTYPE_LASER, m_ID, sizeof(CNetObj_Laser))); if(!pObj) diff --git a/src/game/server/entities/laser.h b/src/game/server/entities/laser.h index db178d34d7..be9aee6d14 100644 --- a/src/game/server/entities/laser.h +++ b/src/game/server/entities/laser.h @@ -22,6 +22,8 @@ class CLaser : public CEntity private: vec2 m_From; vec2 m_Dir; + vec2 m_TelePos; + bool m_WasTele; float m_Energy; int m_Bounces; int m_EvalTick; @@ -32,6 +34,7 @@ class CLaser : public CEntity vec2 m_PrevPos; int m_Type; + int m_TuneZone; }; #endif diff --git a/src/game/server/entities/light.cpp b/src/game/server/entities/light.cpp index 13bad7ffb4..4a3a76cfc2 100644 --- a/src/game/server/entities/light.cpp +++ b/src/game/server/entities/light.cpp @@ -108,28 +108,37 @@ void CLight::Snap(int SnappingClient) && NetworkClipped(SnappingClient, m_To)) return; - CCharacter * pSnappingCharacter = GameServer()->GetPlayerChar( - SnappingClient); + CCharacter *Char = GameServer()->GetPlayerChar(SnappingClient); + + if((GameServer()->m_apPlayers[SnappingClient]->GetTeam() == -1 + || GameServer()->m_apPlayers[SnappingClient]->m_Paused) + && GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID != SPEC_FREEVIEW) + Char = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID); + int Tick = (Server()->Tick() % Server()->TickSpeed()) % 6; - if (pSnappingCharacter && pSnappingCharacter->IsAlive() + if (Char && Char->IsAlive() && m_Layer == LAYER_SWITCH - && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[pSnappingCharacter->Team()] + && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[Char->Team()] && (Tick)) return; CNetObj_Laser *pObj = static_cast(Server()->SnapNewItem( NETOBJTYPE_LASER, m_ID, sizeof(CNetObj_Laser))); + + if (!pObj) + return; + pObj->m_X = (int) m_Pos.x; pObj->m_Y = (int) m_Pos.y; - if (pSnappingCharacter && pSnappingCharacter->Team() == TEAM_SUPER) + if (Char && Char->Team() == TEAM_SUPER) { pObj->m_FromX = (int) m_Pos.x; pObj->m_FromY = (int) m_Pos.y; } - else if (pSnappingCharacter && m_Layer == LAYER_SWITCH - && GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[pSnappingCharacter->Team()]) + else if (Char && m_Layer == LAYER_SWITCH + && GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[Char->Team()]) { pObj->m_FromX = (int) m_To.x; pObj->m_FromY = (int) m_To.y; diff --git a/src/game/server/entities/pickup.cpp b/src/game/server/entities/pickup.cpp index 7e15dcefdc..c0bc2e2775 100644 --- a/src/game/server/entities/pickup.cpp +++ b/src/game/server/entities/pickup.cpp @@ -48,7 +48,7 @@ void CPickup::Tick() }*/ // Check if a player intersected us CCharacter *apEnts[MAX_CLIENTS]; - int Num = GameWorld()->FindEntities(m_Pos, 20.0f, (CEntity**)apEnts, 64, CGameWorld::ENTTYPE_CHARACTER); + int Num = GameWorld()->FindEntities(m_Pos, 20.0f, (CEntity**)apEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); for(int i = 0; i < Num; ++i) { CCharacter * pChr = apEnts[i]; if(pChr && pChr->IsAlive()) @@ -84,7 +84,8 @@ void CPickup::Tick() pChr->SetLastWeapon(WEAPON_GUN); GameServer()->CreateSound(m_Pos, SOUND_PICKUP_ARMOR, pChr->Teams()->TeamMask(pChr->Team())); } - if(!pChr->m_FreezeTime) pChr->SetActiveWeapon(WEAPON_HAMMER); + if(!pChr->m_FreezeTime && pChr->GetActiveWeapon() >= WEAPON_SHOTGUN) + pChr->SetActiveWeapon(WEAPON_HAMMER); break; case POWERUP_WEAPON: @@ -150,11 +151,17 @@ void CPickup::Snap(int SnappingClient) /*if(m_SpawnTick != -1 || NetworkClipped(SnappingClient)) return;*/ - CCharacter * SnapChar = GameServer()->GetPlayerChar(SnappingClient); + CCharacter *Char = GameServer()->GetPlayerChar(SnappingClient); + + if((GameServer()->m_apPlayers[SnappingClient]->GetTeam() == -1 + || GameServer()->m_apPlayers[SnappingClient]->m_Paused) + && GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID != SPEC_FREEVIEW) + Char = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID); + int Tick = (Server()->Tick()%Server()->TickSpeed())%11; - if (SnapChar && SnapChar->IsAlive() && + if (Char && Char->IsAlive() && (m_Layer == LAYER_SWITCH && - !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[SnapChar->Team()]) + !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[Char->Team()]) && (!Tick)) return; diff --git a/src/game/server/entities/plasma.cpp b/src/game/server/entities/plasma.cpp index 76e54512be..b5acad9a5e 100644 --- a/src/game/server/entities/plasma.cpp +++ b/src/game/server/entities/plasma.cpp @@ -109,6 +109,10 @@ void CPlasma::Snap(int SnappingClient) CNetObj_Laser *pObj = static_cast(Server()->SnapNewItem( NETOBJTYPE_LASER, m_ID, sizeof(CNetObj_Laser))); + + if(!pObj) + return; + pObj->m_X = (int) m_Pos.x; pObj->m_Y = (int) m_Pos.y; pObj->m_FromX = (int) m_Pos.x; diff --git a/src/game/server/entities/projectile.cpp b/src/game/server/entities/projectile.cpp index 36d2766221..944a5c8462 100644 --- a/src/game/server/entities/projectile.cpp +++ b/src/game/server/entities/projectile.cpp @@ -2,6 +2,7 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include #include +#include #include "projectile.h" #include @@ -40,6 +41,8 @@ CProjectile::CProjectile m_Layer = Layer; m_Number = Number; m_Freeze = Freeze; + + m_TuneZone = GameServer()->Collision()->IsTune(GameServer()->Collision()->GetMapIndex(m_Pos)); GameWorld()->InsertEntity(this); } @@ -58,18 +61,44 @@ vec2 CProjectile::GetPos(float Time) switch(m_Type) { case WEAPON_GRENADE: - Curvature = GameServer()->Tuning()->m_GrenadeCurvature; - Speed = GameServer()->Tuning()->m_GrenadeSpeed; + if (!m_TuneZone) + { + Curvature = GameServer()->Tuning()->m_GrenadeCurvature; + Speed = GameServer()->Tuning()->m_GrenadeSpeed; + } + else + { + Curvature = GameServer()->TuningList()[m_TuneZone].m_GrenadeCurvature; + Speed = GameServer()->TuningList()[m_TuneZone].m_GrenadeSpeed; + } + break; case WEAPON_SHOTGUN: - Curvature = GameServer()->Tuning()->m_ShotgunCurvature; - Speed = GameServer()->Tuning()->m_ShotgunSpeed; + if (!m_TuneZone) + { + Curvature = GameServer()->Tuning()->m_ShotgunCurvature; + Speed = GameServer()->Tuning()->m_ShotgunSpeed; + } + else + { + Curvature = GameServer()->TuningList()[m_TuneZone].m_ShotgunCurvature; + Speed = GameServer()->TuningList()[m_TuneZone].m_ShotgunSpeed; + } + break; case WEAPON_GUN: - Curvature = GameServer()->Tuning()->m_GunCurvature; - Speed = GameServer()->Tuning()->m_GunSpeed; + if (!m_TuneZone) + { + Curvature = GameServer()->Tuning()->m_GunCurvature; + Speed = GameServer()->Tuning()->m_GunSpeed; + } + else + { + Curvature = GameServer()->TuningList()[m_TuneZone].m_GunCurvature; + Speed = GameServer()->TuningList()[m_TuneZone].m_GunSpeed; + } break; } @@ -88,8 +117,6 @@ void CProjectile::Tick() int Collide = GameServer()->Collision()->IntersectLine(PrevPos, CurPos, &ColPos, &NewPos, false); CCharacter *pOwnerChar = 0; - - if(m_Owner >= 0) pOwnerChar = GameServer()->GetPlayerChar(m_Owner); @@ -98,7 +125,7 @@ void CProjectile::Tick() if(m_LifeSpan > -1) m_LifeSpan--; - int TeamMask = -1; + int64_t TeamMask = -1LL; bool isWeaponCollide = false; if ( @@ -114,16 +141,21 @@ void CProjectile::Tick() } if (pOwnerChar && pOwnerChar->IsAlive()) { - TeamMask = pOwnerChar->Teams()->TeamMask(pOwnerChar->Team(), -1, m_Owner); + TeamMask = pOwnerChar->Teams()->TeamMask(pOwnerChar->Team(), -1, m_Owner); + } + else if (m_Owner >= 0) + { + GameServer()->m_World.DestroyEntity(this); } + if( ((pTargetChr && (pOwnerChar ? !(pOwnerChar->m_Hit&CCharacter::DISABLE_HIT_GRENADE) : g_Config.m_SvHit || m_Owner == -1 || pTargetChr == pOwnerChar)) || Collide || GameLayerClipped(CurPos)) && !isWeaponCollide) { if(m_Explosive/*??*/ && (!pTargetChr || (pTargetChr && !m_Freeze))) { GameServer()->CreateExplosion(ColPos, m_Owner, m_Weapon, m_Owner == -1, (!pTargetChr ? -1 : pTargetChr->Team()), - (m_Owner != -1)? TeamMask : -1); + (m_Owner != -1)? TeamMask : -1LL); GameServer()->CreateSound(ColPos, m_SoundImpact, - (m_Owner != -1)? TeamMask : -1); + (m_Owner != -1)? TeamMask : -1LL); } else if(pTargetChr && m_Freeze && ((m_Layer == LAYER_SWITCH && GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[pTargetChr->Team()]) || m_Layer != LAYER_SWITCH)) pTargetChr->Freeze(); @@ -143,7 +175,7 @@ void CProjectile::Tick() } else if (m_Weapon == WEAPON_GUN) { - GameServer()->CreateDamageInd(CurPos, -atan2(m_Direction.x, m_Direction.y), 10, (m_Owner != -1)? TeamMask : -1); + GameServer()->CreateDamageInd(CurPos, -atan2(m_Direction.x, m_Direction.y), 10, (m_Owner != -1)? TeamMask : -1LL); GameServer()->m_World.DestroyEntity(this); } else @@ -152,8 +184,37 @@ void CProjectile::Tick() } if(m_LifeSpan == -1) { + if(m_Explosive) + { + if(m_Owner >= 0) + pOwnerChar = GameServer()->GetPlayerChar(m_Owner); + + int64_t TeamMask = -1LL; + if (pOwnerChar && pOwnerChar->IsAlive()) + { + TeamMask = pOwnerChar->Teams()->TeamMask(pOwnerChar->Team(), -1, m_Owner); + } + + GameServer()->CreateExplosion(ColPos, m_Owner, m_Weapon, m_Owner == -1, (!pOwnerChar ? -1 : pOwnerChar->Team()), + (m_Owner != -1)? TeamMask : -1LL); + GameServer()->CreateSound(ColPos, m_SoundImpact, + (m_Owner != -1)? TeamMask : -1LL); + } GameServer()->m_World.DestroyEntity(this); } + + int x = GameServer()->Collision()->GetIndex(PrevPos, CurPos); + int z; + if (g_Config.m_SvOldTeleportWeapons) + z = GameServer()->Collision()->IsTeleport(x); + else + z = GameServer()->Collision()->IsTeleportWeapon(x); + if (z && ((CGameControllerDDRace*)GameServer()->m_pController)->m_TeleOuts[z-1].size()) + { + int Num = ((CGameControllerDDRace*)GameServer()->m_pController)->m_TeleOuts[z-1].size(); + m_Pos = ((CGameControllerDDRace*)GameServer()->m_pController)->m_TeleOuts[z-1][(!Num)?Num:rand() % Num]; + m_StartTick = Server()->Tick(); + } } void CProjectile::TickPaused() @@ -183,7 +244,16 @@ void CProjectile::Snap(int SnappingClient) if (pSnapChar && pSnapChar->IsAlive() && (m_Layer == LAYER_SWITCH && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[pSnapChar->Team()] && (!Tick))) return; - if(pSnapChar && m_Owner != -1 && !pSnapChar->CanCollide(m_Owner)) + CCharacter *pOwnerChar = 0; + int64_t TeamMask = -1LL; + + if(m_Owner >= 0) + pOwnerChar = GameServer()->GetPlayerChar(m_Owner); + + if (pOwnerChar && pOwnerChar->IsAlive()) + TeamMask = pOwnerChar->Teams()->TeamMask(pOwnerChar->Team(), -1, m_Owner); + + if(m_Owner != -1 && !CmaskIsSet(TeamMask, SnappingClient)) return; CNetObj_Projectile *pProj = static_cast(Server()->SnapNewItem(NETOBJTYPE_PROJECTILE, m_ID, sizeof(CNetObj_Projectile))); diff --git a/src/game/server/entities/projectile.h b/src/game/server/entities/projectile.h index cfd4e92b97..8eac4c7073 100644 --- a/src/game/server/entities/projectile.h +++ b/src/game/server/entities/projectile.h @@ -48,6 +48,7 @@ class CProjectile : public CEntity int m_Bouncing; bool m_Freeze; bool m_Collised; + int m_TuneZone; public: diff --git a/src/game/server/entity.cpp b/src/game/server/entity.cpp index 788c3f45b9..d288efe1d0 100644 --- a/src/game/server/entity.cpp +++ b/src/game/server/entity.cpp @@ -44,13 +44,13 @@ int CEntity::NetworkClipped(int SnappingClient, vec2 CheckPos) if(absolute(dx) > 1000.0f || absolute(dy) > 800.0f) return 1; - if(distance(GameServer()->m_apPlayers[SnappingClient]->m_ViewPos, CheckPos) > 1100.0f) + if(distance(GameServer()->m_apPlayers[SnappingClient]->m_ViewPos, CheckPos) > 4000.0f) return 1; return 0; } bool CEntity::GameLayerClipped(vec2 CheckPos) { - return round(CheckPos.x)/32 < -200 || round(CheckPos.x)/32 > GameServer()->Collision()->GetWidth()+200 || - round(CheckPos.y)/32 < -200 || round(CheckPos.y)/32 > GameServer()->Collision()->GetHeight()+200 ? true : false; + return round_to_int(CheckPos.x)/32 < -200 || round_to_int(CheckPos.x)/32 > GameServer()->Collision()->GetWidth()+200 || + round_to_int(CheckPos.y)/32 < -200 || round_to_int(CheckPos.y)/32 > GameServer()->Collision()->GetHeight()+200 ? true : false; } diff --git a/src/game/server/entity.h b/src/game/server/entity.h index 7399524e79..2d4ef091a1 100644 --- a/src/game/server/entity.h +++ b/src/game/server/entity.h @@ -62,8 +62,8 @@ class CEntity CEntity *m_pPrevTypeEntity; CEntity *m_pNextTypeEntity; - class CGameWorld *m_pGameWorld; protected: + class CGameWorld *m_pGameWorld; bool m_MarkedForDestroy; int m_ID; int m_ObjType; @@ -138,8 +138,8 @@ class CEntity Returns: Non-zero if the entity doesn't have to be in the snapshot. */ - int NetworkClipped(int SnappingClient); - int NetworkClipped(int SnappingClient, vec2 CheckPos); + virtual int NetworkClipped(int SnappingClient); + virtual int NetworkClipped(int SnappingClient, vec2 CheckPos); bool GameLayerClipped(vec2 CheckPos); diff --git a/src/game/server/eventhandler.cpp b/src/game/server/eventhandler.cpp index e3fa90f110..a8462856e3 100644 --- a/src/game/server/eventhandler.cpp +++ b/src/game/server/eventhandler.cpp @@ -17,7 +17,7 @@ void CEventHandler::SetGameServer(CGameContext *pGameServer) m_pGameServer = pGameServer; } -void *CEventHandler::Create(int Type, int Size, int Mask) +void *CEventHandler::Create(int Type, int Size, int64_t Mask) { if(m_NumEvents == MAX_EVENTS) return 0; @@ -42,8 +42,6 @@ void CEventHandler::Clear() void CEventHandler::Snap(int SnappingClient) { - if (SnappingClient != -1 && GameServer()->m_apPlayers[SnappingClient]->m_Paused) - SnappingClient = GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID; for(int i = 0; i < m_NumEvents; i++) { if(SnappingClient == -1 || CmaskIsSet(m_aClientMasks[i], SnappingClient)) diff --git a/src/game/server/eventhandler.h b/src/game/server/eventhandler.h index 721b59afa3..e3a62c5153 100644 --- a/src/game/server/eventhandler.h +++ b/src/game/server/eventhandler.h @@ -3,6 +3,14 @@ #ifndef GAME_SERVER_EVENTHANDLER_H #define GAME_SERVER_EVENTHANDLER_H +#ifdef _MSC_VER +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif // class CEventHandler { @@ -12,7 +20,7 @@ class CEventHandler int m_aTypes[MAX_EVENTS]; // TODO: remove some of these arrays int m_aOffsets[MAX_EVENTS]; int m_aSizes[MAX_EVENTS]; - int m_aClientMasks[MAX_EVENTS]; + int64_t m_aClientMasks[MAX_EVENTS]; char m_aData[MAX_DATASIZE]; class CGameContext *m_pGameServer; @@ -24,7 +32,7 @@ class CEventHandler void SetGameServer(CGameContext *pGameServer); CEventHandler(); - void *Create(int Type, int Size, int Mask = -1); + void *Create(int Type, int Size, int64_t Mask = -1LL); void Clear(); void Snap(int SnappingClient); }; diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index a41725157e..b85890756b 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -11,8 +11,8 @@ #include "gamecontext.h" #include #include -/*#include -#include "gamemodes/dm.h" +#include +/*#include "gamemodes/dm.h" #include "gamemodes/tdm.h" #include "gamemodes/ctf.h" #include "gamemodes/mod.h"*/ @@ -47,6 +47,7 @@ void CGameContext::Construct(int Resetting) m_pVoteOptionFirst = 0; m_pVoteOptionLast = 0; m_NumVoteOptions = 0; + m_LastMapVote = 0; //m_LockTeams = 0; if(Resetting==NO_RESET) @@ -74,6 +75,9 @@ CGameContext::~CGameContext() delete m_apPlayers[i]; if(!m_Resetting) delete m_pVoteOptionHeap; + + if(m_pScore) + delete m_pScore; } void CGameContext::Clear() @@ -104,7 +108,7 @@ class CCharacter *CGameContext::GetPlayerChar(int ClientID) return m_apPlayers[ClientID]->GetCharacter(); } -void CGameContext::CreateDamageInd(vec2 Pos, float Angle, int Amount, int Mask) +void CGameContext::CreateDamageInd(vec2 Pos, float Angle, int Amount, int64_t Mask) { float a = 3 * 3.14159f / 2 + Angle; //float a = get_angle(dir); @@ -123,7 +127,7 @@ void CGameContext::CreateDamageInd(vec2 Pos, float Angle, int Amount, int Mask) } } -void CGameContext::CreateHammerHit(vec2 Pos, int Mask) +void CGameContext::CreateHammerHit(vec2 Pos, int64_t Mask) { // create the event CNetEvent_HammerHit *pEvent = (CNetEvent_HammerHit *)m_Events.Create(NETEVENTTYPE_HAMMERHIT, sizeof(CNetEvent_HammerHit), Mask); @@ -135,7 +139,7 @@ void CGameContext::CreateHammerHit(vec2 Pos, int Mask) } -void CGameContext::CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamage, int ActivatedTeam, int Mask) +void CGameContext::CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamage, int ActivatedTeam, int64_t Mask) { // create the event CNetEvent_Explosion *pEvent = (CNetEvent_Explosion *)m_Events.Create(NETEVENTTYPE_EXPLOSION, sizeof(CNetEvent_Explosion), Mask); @@ -161,7 +165,13 @@ void CGameContext::CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamag if(l) ForceDir = normalize(Diff); l = 1-clamp((l-InnerRadius)/(Radius-InnerRadius), 0.0f, 1.0f); - float Dmg = 6 * l; + float Strength; + if (Owner == -1 || !m_apPlayers[Owner] || !m_apPlayers[Owner]->m_TuneZone) + Strength = Tuning()->m_ExplosionStrength; + else + Strength = TuningList()[m_apPlayers[Owner]->m_TuneZone].m_ExplosionStrength; + + float Dmg = Strength * l; if((int)Dmg) if((GetPlayerChar(Owner) ? !(GetPlayerChar(Owner)->m_Hit&CCharacter::DISABLE_HIT_GRENADE) : g_Config.m_SvHit || NoDamage) || Owner == apEnts[i]->GetPlayer()->GetCID()) { @@ -186,7 +196,7 @@ void create_smoke(vec2 Pos) } }*/ -void CGameContext::CreatePlayerSpawn(vec2 Pos, int Mask) +void CGameContext::CreatePlayerSpawn(vec2 Pos, int64_t Mask) { // create the event CNetEvent_Spawn *ev = (CNetEvent_Spawn *)m_Events.Create(NETEVENTTYPE_SPAWN, sizeof(CNetEvent_Spawn), Mask); @@ -197,7 +207,7 @@ void CGameContext::CreatePlayerSpawn(vec2 Pos, int Mask) } } -void CGameContext::CreateDeath(vec2 Pos, int ClientID, int Mask) +void CGameContext::CreateDeath(vec2 Pos, int ClientID, int64_t Mask) { // create the event CNetEvent_Death *pEvent = (CNetEvent_Death *)m_Events.Create(NETEVENTTYPE_DEATH, sizeof(CNetEvent_Death), Mask); @@ -209,7 +219,7 @@ void CGameContext::CreateDeath(vec2 Pos, int ClientID, int Mask) } } -void CGameContext::CreateSound(vec2 Pos, int Sound, int Mask) +void CGameContext::CreateSound(vec2 Pos, int Sound, int64_t Mask) { if (Sound < 0) return; @@ -242,6 +252,25 @@ void CGameContext::CreateSoundGlobal(int Sound, int Target) } } +void CGameContext::CallVote(int ClientID, const char *aDesc, const char *aCmd, const char *pReason, const char *aChatmsg) +{ + // check if a vote is already running + if(m_VoteCloseTime) + return; + + int64 Now = Server()->Tick(); + CPlayer *pPlayer = m_apPlayers[ClientID]; + + if(!pPlayer) + return; + + SendChat(-1, CGameContext::CHAT_ALL, aChatmsg); + StartVote(aDesc, aCmd, pReason); + pPlayer->m_Vote = 1; + pPlayer->m_VotePos = m_VotePos = 1; + m_VoteCreator = ClientID; + pPlayer->m_LastVoteCall = Now; +} void CGameContext::SendChatTarget(int To, const char *pText) { @@ -252,6 +281,12 @@ void CGameContext::SendChatTarget(int To, const char *pText) Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, To); } +void CGameContext::SendChatTeam(int Team, const char *pText) +{ + for(int i = 0; im_Teams.m_Core.Team(i) == Team) + SendChatTarget(i, pText); +} void CGameContext::SendChat(int ChatterClientID, int Team, const char *pText, int SpamProtectionClientID) { @@ -259,8 +294,8 @@ void CGameContext::SendChat(int ChatterClientID, int Team, const char *pText, in { if(ProcessSpamProtection(SpamProtectionClientID)) { - SendChatTarget(SpamProtectionClientID, "Muted text:"); - SendChatTarget(SpamProtectionClientID, pText); + //SendChatTarget(SpamProtectionClientID, "Muted text:"); + //SendChatTarget(SpamProtectionClientID, pText); return; } } @@ -285,7 +320,18 @@ void CGameContext::SendChat(int ChatterClientID, int Team, const char *pText, in Msg.m_Team = 0; Msg.m_ClientID = ChatterClientID; Msg.m_pMessage = aText; - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); + + // pack one for the recording only + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_NOSEND, -1); + + // send to the clients + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_apPlayers[i] != 0) { + if(!m_apPlayers[i]->m_DND) + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_NORECORD, i); + } + } } else { @@ -342,10 +388,6 @@ void CGameContext::SendBroadcast(const char *pText, int ClientID) // void CGameContext::StartVote(const char *pDesc, const char *pCommand, const char *pReason) { - // check if a vote is already running - if(m_VoteCloseTime) - return; - // reset votes m_VoteEnforce = VOTE_ENFORCE_UNKNOWN; m_VoteEnforcer = -1; @@ -359,7 +401,7 @@ void CGameContext::StartVote(const char *pDesc, const char *pCommand, const char } // start vote - m_VoteCloseTime = time_get() + time_freq()*25; + m_VoteCloseTime = time_get() + time_freq() * g_Config.m_SvVoteTime; str_copy(m_aVoteDescription, pDesc, sizeof(m_aVoteDescription)); str_copy(m_aVoteCommand, pCommand, sizeof(m_aVoteCommand)); str_copy(m_aVoteReason, pReason, sizeof(m_aVoteReason)); @@ -394,6 +436,13 @@ void CGameContext::SendVoteSet(int ClientID) void CGameContext::SendVoteStatus(int ClientID, int Total, int Yes, int No) { + if (Total > VANILLA_MAX_CLIENTS && m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_ClientVersion <= VERSION_DDRACE) + { + Yes = float(Yes) * VANILLA_MAX_CLIENTS / float(Total); + No = float(No) * VANILLA_MAX_CLIENTS / float(Total); + Total = VANILLA_MAX_CLIENTS; + } + CNetMsg_Sv_VoteStatus Msg = {0}; Msg.m_Total = Total; Msg.m_Yes = Yes; @@ -431,15 +480,76 @@ void CGameContext::CheckPureTuning() } } -void CGameContext::SendTuningParams(int ClientID) +void CGameContext::SendTuningParams(int ClientID, int Zone) { + if (ClientID == -1) + { + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if (m_apPlayers[i]) + { + if(m_apPlayers[i]->GetCharacter()) + { + if (m_apPlayers[i]->GetCharacter()->m_TuneZone == Zone) + SendTuningParams(i, Zone); + } + else if (m_apPlayers[i]->m_TuneZone == Zone) + { + SendTuningParams(i, Zone); + } + } + } + return; + } + CheckPureTuning(); CMsgPacker Msg(NETMSGTYPE_SV_TUNEPARAMS); - int *pParams = (int *)&m_Tuning; - for(unsigned i = 0; i < sizeof(m_Tuning)/sizeof(int); i++) - Msg.AddInt(pParams[i]); - Server()->SendMsg(&Msg, MSGFLAG_VITAL, ClientID); + int *pParams = 0; + if (Zone == 0) + pParams = (int *)&m_Tuning; + else + pParams = (int *)&(m_TuningList[Zone]); + + unsigned int last = sizeof(m_Tuning)/sizeof(int); + if (m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_ClientVersion < VERSION_DDNET_EXTRATUNES) + last = 33; + + for(unsigned i = 0; i < last; i++) + { + if (m_apPlayers[ClientID] && m_apPlayers[ClientID]->GetCharacter()) + { + if((i==31) // collision + && (m_apPlayers[ClientID]->GetCharacter()->NeededFaketuning() & FAKETUNE_SOLO + || m_apPlayers[ClientID]->GetCharacter()->NeededFaketuning() & FAKETUNE_NOCOLL)) + { + Msg.AddInt(0); + } + else if((i==32) // hooking + && (m_apPlayers[ClientID]->GetCharacter()->NeededFaketuning() & FAKETUNE_SOLO + || m_apPlayers[ClientID]->GetCharacter()->NeededFaketuning() & FAKETUNE_NOHOOK)) + { + Msg.AddInt(0); + } + else if((i==3) // ground jump impulse + && m_apPlayers[ClientID]->GetCharacter()->NeededFaketuning() & FAKETUNE_NOJUMP) + { + Msg.AddInt(0); + } + else if((i==33) // jetpack + && !(m_apPlayers[ClientID]->GetCharacter()->NeededFaketuning() & FAKETUNE_JETPACK)) + { + Msg.AddInt(0); + } + else + { + Msg.AddInt(pParams[i]); + } + } + else + Msg.AddInt(pParams[i]); // if everything is normal just send true tunings + } + Server()->SendMsg(&Msg, MSGFLAG_VITAL, ClientID); } /* void CGameContext::SwapTeams() @@ -464,7 +574,7 @@ void CGameContext::OnTick() CheckPureTuning(); // copy tuning - m_World.m_Core.m_Tuning = m_Tuning; + m_World.m_Core.m_Tuning[0] = m_Tuning; m_World.Tick(); //if(world.paused) // make sure that the game object always updates @@ -513,6 +623,9 @@ void CGameContext::OnTick() GetPlayerChar(m_VoteCreator)->Team() != GetPlayerChar(i)->Team()) continue; + if (m_apPlayers[i]->m_Afk) + continue; + int ActVote = m_apPlayers[i]->m_Vote; int ActVotePos = m_apPlayers[i]->m_VotePos; @@ -537,6 +650,9 @@ void CGameContext::OnTick() No++; } + if(g_Config.m_SvVoteMaxTotal && Total > g_Config.m_SvVoteMaxTotal) + Total = g_Config.m_SvVoteMaxTotal; + //if(Yes >= Total/2+1) if(Yes > Total / (100.0 / g_Config.m_SvVoteYesPercentage)) m_VoteEnforce = VOTE_ENFORCE_YES; @@ -585,7 +701,9 @@ void CGameContext::OnTick() else if(m_VoteUpdate) { m_VoteUpdate = false; - SendVoteStatus(-1, Total, Yes, No); + for(int i = 0; i < MAX_CLIENTS; ++i) + if(Server()->ClientIngame(i)) + SendVoteStatus(i, Total, Yes, No); } } } @@ -608,7 +726,7 @@ void CGameContext::OnTick() if(Collision()->m_NumSwitchers > 0) for (int i = 0; i < Collision()->m_NumSwitchers+1; ++i) { - for (int j = 0; j < 16; ++j) + for (int j = 0; j < MAX_CLIENTS; ++j) { if(Collision()->m_pSwitchers[i].m_EndTick[j] <= Server()->Tick() && Collision()->m_pSwitchers[i].m_Type[j] == TILE_SWITCHTIMEDOPEN) { @@ -657,9 +775,11 @@ void CGameContext::OnClientEnter(int ClientID) m_apPlayers[ClientID]->Respawn(); // init the player Score()->PlayerData(ClientID)->Reset(); + m_apPlayers[ClientID]->m_Score = -9999; + + // Can't set score here as LoadScore() is threaded, run it in + // LoadScoreThreaded() instead Score()->LoadScore(ClientID); - Score()->PlayerData(ClientID)->m_CurrentTime = Score()->PlayerData(ClientID)->m_BestTime; - m_apPlayers[ClientID]->m_Score = (Score()->PlayerData(ClientID)->m_BestTime)?Score()->PlayerData(ClientID)->m_BestTime:-9999; if(((CServer *) Server())->m_aPrevStates[ClientID] < CServer::CClient::STATE_INGAME) { @@ -667,8 +787,8 @@ void CGameContext::OnClientEnter(int ClientID) str_format(aBuf, sizeof(aBuf), "'%s' entered and joined the %s", Server()->ClientName(ClientID), m_pController->GetTeamName(m_apPlayers[ClientID]->GetTeam())); SendChat(-1, CGameContext::CHAT_ALL, aBuf); - SendChatTarget(ClientID, "DDRace Mod. Version: " GAME_VERSION); - SendChatTarget(ClientID, "please visit http://DDRace.info or say /info for more info"); + SendChatTarget(ClientID, "DDraceNetwork Mod. Version: " GAME_VERSION); + SendChatTarget(ClientID, "please visit http://ddnet.tw or say /info for more info"); if(g_Config.m_SvWelcome[0]!=0) SendChatTarget(ClientID,g_Config.m_SvWelcome); @@ -676,6 +796,15 @@ void CGameContext::OnClientEnter(int ClientID) Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); + if (g_Config.m_SvShowOthersDefault) + { + if (g_Config.m_SvShowOthers) + SendChatTarget(ClientID, "You can see other players. To disable this use the ddnet client and type /showothers ."); + + m_apPlayers[ClientID]->m_ShowOthers = true; + } + + if (g_Config.m_SvEvents) { @@ -703,9 +832,11 @@ void CGameContext::OnClientEnter(int ClientID) } m_VoteUpdate = true; - m_apPlayers[ClientID]->m_Authed = ((CServer*)Server())->m_aClients[ClientID].m_Authed; - + // send active vote + if(m_VoteCloseTime) + SendVoteSet(ClientID); + m_apPlayers[ClientID]->m_Authed = ((CServer*)Server())->m_aClients[ClientID].m_Authed; } void CGameContext::OnClientConnected(int ClientID) @@ -727,10 +858,6 @@ void CGameContext::OnClientConnected(int ClientID) } #endif - // send active vote - if(m_VoteCloseTime) - SendVoteSet(ClientID); - // send motd CNetMsg_Sv_Motd Msg; Msg.m_pMessage = g_Config.m_SvMotd; @@ -753,6 +880,13 @@ void CGameContext::OnClientDrop(int ClientID, const char *pReason) if(m_apPlayers[i] && m_apPlayers[i]->m_SpectatorID == ClientID) m_apPlayers[i]->m_SpectatorID = SPEC_FREEVIEW; } + + // update conversation targets + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(m_apPlayers[i] && m_apPlayers[i]->m_LastWhisperTo == ClientID) + m_apPlayers[i]->m_LastWhisperTo = -1; + } } void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) @@ -768,280 +902,344 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) return; } - if(MsgID == NETMSGTYPE_CL_SAY) + if(Server()->ClientIngame(ClientID)) { - CNetMsg_Cl_Say *pMsg = (CNetMsg_Cl_Say *)pRawMsg; - int Team = pMsg->m_Team; - /*if(Team) - Team = pPlayer->GetTeam(); - else - Team = CGameContext::CHAT_ALL; - - if(g_Config.m_SvSpamprotection && pPlayer->m_LastChat && pPlayer->m_LastChat+Server()->TickSpeed() > Server()->Tick()) - return; + if(MsgID == NETMSGTYPE_CL_SAY) + { + CNetMsg_Cl_Say *pMsg = (CNetMsg_Cl_Say *)pRawMsg; + int Team = pMsg->m_Team; + + // trim right and set maximum length to 256 utf8-characters + int Length = 0; + const char *p = pMsg->m_pMessage; + const char *pEnd = 0; + while(*p) + { + const char *pStrOld = p; + int Code = str_utf8_decode(&p); + + // check if unicode is not empty + if(Code > 0x20 && Code != 0xA0 && Code != 0x034F && (Code < 0x2000 || Code > 0x200F) && (Code < 0x2028 || Code > 0x202F) && + (Code < 0x205F || Code > 0x2064) && (Code < 0x206A || Code > 0x206F) && (Code < 0xFE00 || Code > 0xFE0F) && + Code != 0xFEFF && (Code < 0xFFF9 || Code > 0xFFFC)) + { + pEnd = 0; + } + else if(pEnd == 0) + pEnd = pStrOld; - pPlayer->m_LastChat = Server()->Tick();*/ + if(++Length >= 256) + { + *(const_cast(p)) = 0; + break; + } + } + if(pEnd != 0) + *(const_cast(pEnd)) = 0; - int GameTeam = ((CGameControllerDDRace*)m_pController)->m_Teams.m_Core.Team(pPlayer->GetCID()); - if(Team) - Team = ((pPlayer->GetTeam() == -1) ? CHAT_SPEC : GameTeam); - else - Team = CHAT_ALL; - /* - if(str_length(pMsg->m_pMessage)>370) - { - SendChatTarget(ClientID, "Your Message is too long"); - return; - } if it's needed someone will report it! :D, i can't check from here so...*/ + // drop empty and autocreated spam messages (more than 32 characters per second) + if(Length == 0 || (pMsg->m_pMessage[0]!='/' && (g_Config.m_SvSpamprotection && pPlayer->m_LastChat && pPlayer->m_LastChat+Server()->TickSpeed()*((31+Length)/32) > Server()->Tick()))) + return; - // check for invalid chars - unsigned char *pMessage = (unsigned char *)pMsg->m_pMessage; - while (*pMessage) - { - if(*pMessage < 32) - *pMessage = ' '; - pMessage++; - } - if(pMsg->m_pMessage[0]=='/') - { - m_ChatResponseTargetID = ClientID; - Console()->SetFlagMask(CFGFLAG_CHAT); + //pPlayer->m_LastChat = Server()->Tick(); - if (pPlayer->m_Authed) - Console()->SetAccessLevel(pPlayer->m_Authed == CServer::AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD); + int GameTeam = ((CGameControllerDDRace*)m_pController)->m_Teams.m_Core.Team(pPlayer->GetCID()); + if(Team) + Team = ((pPlayer->GetTeam() == -1) ? CHAT_SPEC : GameTeam); else - Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_USER); - Console()->SetPrintOutputLevel(m_ChatPrintCBIndex, 0); + Team = CHAT_ALL; - Console()->ExecuteLine(pMsg->m_pMessage + 1, ClientID); - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "%d used %s", ClientID, pMsg->m_pMessage); - Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "chat-command", aBuf); + if(pMsg->m_pMessage[0]=='/') + { + if (str_comp_nocase_num(pMsg->m_pMessage+1, "w ", 2) == 0) + { + char pWhisperMsg[256]; + str_copy(pWhisperMsg, pMsg->m_pMessage + 3, 256); + Whisper(pPlayer->GetCID(), pWhisperMsg); + } + else if (str_comp_nocase_num(pMsg->m_pMessage+1, "whisper ", 8) == 0) + { + char pWhisperMsg[256]; + str_copy(pWhisperMsg, pMsg->m_pMessage + 9, 256); + Whisper(pPlayer->GetCID(), pWhisperMsg); + } + else if (str_comp_nocase_num(pMsg->m_pMessage+1, "c ", 2) == 0) + { + char pWhisperMsg[256]; + str_copy(pWhisperMsg, pMsg->m_pMessage + 3, 256); + Converse(pPlayer->GetCID(), pWhisperMsg); + } + else if (str_comp_nocase_num(pMsg->m_pMessage+1, "converse ", 9) == 0) + { + char pWhisperMsg[256]; + str_copy(pWhisperMsg, pMsg->m_pMessage + 10, 256); + Converse(pPlayer->GetCID(), pWhisperMsg); + } + else + { + if(g_Config.m_SvSpamprotection + && pPlayer->m_LastCommands[0] && pPlayer->m_LastCommands[0]+Server()->TickSpeed() > Server()->Tick() + && pPlayer->m_LastCommands[1] && pPlayer->m_LastCommands[1]+Server()->TickSpeed() > Server()->Tick() + && pPlayer->m_LastCommands[2] && pPlayer->m_LastCommands[2]+Server()->TickSpeed() > Server()->Tick() + && pPlayer->m_LastCommands[3] && pPlayer->m_LastCommands[3]+Server()->TickSpeed() > Server()->Tick() + ) + return; - Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN); - Console()->SetFlagMask(CFGFLAG_SERVER); - m_ChatResponseTargetID = -1; + int64 Now = Server()->Tick(); + pPlayer->m_LastCommands[pPlayer->m_LastCommandPos] = Now; + pPlayer->m_LastCommandPos = (pPlayer->m_LastCommandPos + 1) % 4; + + m_ChatResponseTargetID = ClientID; + Server()->RestrictRconOutput(ClientID); + Console()->SetFlagMask(CFGFLAG_CHAT); + + if (pPlayer->m_Authed) + Console()->SetAccessLevel(pPlayer->m_Authed == CServer::AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD); + else + Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_USER); + Console()->SetPrintOutputLevel(m_ChatPrintCBIndex, 0); + + Console()->ExecuteLine(pMsg->m_pMessage + 1, ClientID); + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "%d used %s", ClientID, pMsg->m_pMessage); + Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "chat-command", aBuf); + + Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN); + Console()->SetFlagMask(CFGFLAG_SERVER); + m_ChatResponseTargetID = -1; + Server()->RestrictRconOutput(-1); + } + } + else + SendChat(ClientID, Team, pMsg->m_pMessage, ClientID); } - else - SendChat(ClientID, Team, pMsg->m_pMessage, ClientID); - } - else if(MsgID == NETMSGTYPE_CL_CALLVOTE) - { - if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry+Server()->TickSpeed()*3 > Server()->Tick()) - return; - - int64 Now = Server()->Tick(); - pPlayer->m_LastVoteTry = Now; - //if(pPlayer->GetTeam() == TEAM_SPECTATORS) - if(g_Config.m_SvSpectatorVotes == 0 && pPlayer->GetTeam() == TEAM_SPECTATORS) + else if(MsgID == NETMSGTYPE_CL_CALLVOTE) { - SendChatTarget(ClientID, "Spectators aren't allowed to start a vote."); - return; - } + if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry+Server()->TickSpeed()*3 > Server()->Tick()) + return; - if(m_VoteCloseTime) - { - SendChatTarget(ClientID, "Wait for current vote to end before calling a new one."); - return; - } + int64 Now = Server()->Tick(); + pPlayer->m_LastVoteTry = Now; + //if(pPlayer->GetTeam() == TEAM_SPECTATORS) + if(g_Config.m_SvSpectatorVotes == 0 && pPlayer->GetTeam() == TEAM_SPECTATORS) + { + SendChatTarget(ClientID, "Spectators aren't allowed to start a vote."); + return; + } - int Timeleft = pPlayer->m_LastVoteCall + Server()->TickSpeed()*60 - Now; - if(pPlayer->m_LastVoteCall && Timeleft > 0) - { - char aChatmsg[512] = {0}; - str_format(aChatmsg, sizeof(aChatmsg), "You must wait %d seconds before making another vote", (Timeleft/Server()->TickSpeed())+1); - SendChatTarget(ClientID, aChatmsg); - return; - } + if(m_VoteCloseTime) + { + SendChatTarget(ClientID, "Wait for current vote to end before calling a new one."); + return; + } + + int Timeleft = pPlayer->m_LastVoteCall + Server()->TickSpeed()*g_Config.m_SvVoteDelay - Now; + if(pPlayer->m_LastVoteCall && Timeleft > 0) + { + char aChatmsg[512] = {0}; + str_format(aChatmsg, sizeof(aChatmsg), "You must wait %d seconds before making another vote", (Timeleft/Server()->TickSpeed())+1); + SendChatTarget(ClientID, aChatmsg); + return; + } - char aChatmsg[512] = {0}; - char aDesc[VOTE_DESC_LENGTH] = {0}; - char aCmd[VOTE_CMD_LENGTH] = {0}; - CNetMsg_Cl_CallVote *pMsg = (CNetMsg_Cl_CallVote *)pRawMsg; - const char *pReason = pMsg->m_Reason[0] ? pMsg->m_Reason : "No reason given"; + char aChatmsg[512] = {0}; + char aDesc[VOTE_DESC_LENGTH] = {0}; + char aCmd[VOTE_CMD_LENGTH] = {0}; + CNetMsg_Cl_CallVote *pMsg = (CNetMsg_Cl_CallVote *)pRawMsg; + const char *pReason = pMsg->m_Reason[0] ? pMsg->m_Reason : "No reason given"; - if(str_comp_nocase(pMsg->m_Type, "option") == 0) - { - CVoteOptionServer *pOption = m_pVoteOptionFirst; - static int64 LastMapVote = 0; - while(pOption) + if(str_comp_nocase(pMsg->m_Type, "option") == 0) { - if(str_comp_nocase(pMsg->m_Value, pOption->m_aDescription) == 0) + CVoteOptionServer *pOption = m_pVoteOptionFirst; + while(pOption) + { + if(str_comp_nocase(pMsg->m_Value, pOption->m_aDescription) == 0) + { + if(!Console()->LineIsValid(pOption->m_aCommand)) + { + SendChatTarget(ClientID, "Invalid option"); + return; + } + if(!m_apPlayers[ClientID]->m_Authed && (strncmp(pOption->m_aCommand, "sv_map ", 7) == 0 || strncmp(pOption->m_aCommand, "change_map ", 11) == 0 || strncmp(pOption->m_aCommand, "random_map", 10) == 0 || strncmp(pOption->m_aCommand, "random_unfinished_map", 10) == 0) && time_get() < m_LastMapVote + (time_freq() * g_Config.m_SvVoteMapTimeDelay)) + { + char chatmsg[512] = {0}; + str_format(chatmsg, sizeof(chatmsg), "There's a %d second delay between map-votes, please wait %d seconds.", g_Config.m_SvVoteMapTimeDelay,((m_LastMapVote+(g_Config.m_SvVoteMapTimeDelay * time_freq()))/time_freq())-(time_get()/time_freq())); + SendChatTarget(ClientID, chatmsg); + + return; + } + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s' (%s)", Server()->ClientName(ClientID), + pOption->m_aDescription, pReason); + str_format(aDesc, sizeof(aDesc), "%s", pOption->m_aDescription); + str_format(aCmd, sizeof(aCmd), "%s", pOption->m_aCommand); + m_LastMapVote = time_get(); + break; + } + + pOption = pOption->m_pNext; + } + + if(!pOption) { - if(!Console()->LineIsValid(pOption->m_aCommand)) + if (pPlayer->m_Authed != CServer::AUTHED_ADMIN) // allow admins to call any vote they want { - SendChatTarget(ClientID, "Invalid option"); + str_format(aChatmsg, sizeof(aChatmsg), "'%s' isn't an option on this server", pMsg->m_Value); + SendChatTarget(ClientID, aChatmsg); return; } - if(!m_apPlayers[ClientID]->m_Authed && (strncmp(pOption->m_aCommand, "sv_map ", 7) == 0 || strncmp(pOption->m_aCommand, "change_map ", 11) == 0) && time_get() < LastMapVote + (time_freq() * g_Config.m_SvVoteMapTimeDelay)) + else { - char chatmsg[512] = {0}; - str_format(chatmsg, sizeof(chatmsg), "There's a %d second delay between map-votes,Please wait %d Second(s)", g_Config.m_SvVoteMapTimeDelay,((LastMapVote+(g_Config.m_SvVoteMapTimeDelay * time_freq()))/time_freq())-(time_get()/time_freq())); - SendChatTarget(ClientID, chatmsg); + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s'", Server()->ClientName(ClientID), pMsg->m_Value); + str_format(aDesc, sizeof(aDesc), "%s", pMsg->m_Value); + str_format(aCmd, sizeof(aCmd), "%s", pMsg->m_Value); + } + } + + m_LastMapVote = time_get(); + m_VoteKick = false; + } + else if(str_comp_nocase(pMsg->m_Type, "kick") == 0) + { + if(!m_apPlayers[ClientID]->m_Authed && time_get() < m_apPlayers[ClientID]->m_Last_KickVote + (time_freq() * 5)) + return; + else if(!m_apPlayers[ClientID]->m_Authed && time_get() < m_apPlayers[ClientID]->m_Last_KickVote + (time_freq() * g_Config.m_SvVoteKickTimeDelay)) + { + char chatmsg[512] = {0}; + str_format(chatmsg, sizeof(chatmsg), "There's a %d second wait time between kick votes for each player please wait %d second(s)", + g_Config.m_SvVoteKickTimeDelay, + ((m_apPlayers[ClientID]->m_Last_KickVote + (m_apPlayers[ClientID]->m_Last_KickVote*time_freq()))/time_freq())-(time_get()/time_freq()) + ); + SendChatTarget(ClientID, chatmsg); + m_apPlayers[ClientID]->m_Last_KickVote = time_get(); + return; + } + //else if(!g_Config.m_SvVoteKick) + else if(!g_Config.m_SvVoteKick && !pPlayer->m_Authed) // allow admins to call kick votes even if they are forbidden + { + SendChatTarget(ClientID, "Server does not allow voting to kick players"); + m_apPlayers[ClientID]->m_Last_KickVote = time_get(); + return; + } + if(g_Config.m_SvVoteKickMin) + { + int PlayerNum = 0; + for(int i = 0; i < MAX_CLIENTS; ++i) + if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + ++PlayerNum; + + if(PlayerNum < g_Config.m_SvVoteKickMin) + { + str_format(aChatmsg, sizeof(aChatmsg), "Kick voting requires %d players on the server", g_Config.m_SvVoteKickMin); + SendChatTarget(ClientID, aChatmsg); return; } - str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s' (%s)", Server()->ClientName(ClientID), - pOption->m_aDescription, pReason); - str_format(aDesc, sizeof(aDesc), "%s", pOption->m_aDescription); - str_format(aCmd, sizeof(aCmd), "%s", pOption->m_aCommand); - LastMapVote = time_get(); - break; } - pOption = pOption->m_pNext; - } + int KickID = str_toint(pMsg->m_Value); + if (!Server()->ReverseTranslate(KickID, ClientID)) + return; - if(!pOption) - { - if (!pPlayer->m_Authed) // allow admins to call any vote they want + if(KickID < 0 || KickID >= MAX_CLIENTS || !m_apPlayers[KickID]) { - str_format(aChatmsg, sizeof(aChatmsg), "'%s' isn't an option on this server", pMsg->m_Value); - SendChatTarget(ClientID, aChatmsg); + SendChatTarget(ClientID, "Invalid client id to kick"); return; } - else + if(KickID == ClientID) { - str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s'", Server()->ClientName(ClientID), pMsg->m_Value); - str_format(aDesc, sizeof(aDesc), "%s", pMsg->m_Value); - str_format(aCmd, sizeof(aCmd), "%s", pMsg->m_Value); + SendChatTarget(ClientID, "You can't kick yourself"); + return; + } + //if(Server()->IsAuthed(KickID)) + if(m_apPlayers[KickID]->m_Authed > 0 && m_apPlayers[KickID]->m_Authed >= pPlayer->m_Authed) + { + SendChatTarget(ClientID, "You can't kick admins"); + m_apPlayers[ClientID]->m_Last_KickVote = time_get(); + char aBufKick[128]; + str_format(aBufKick, sizeof(aBufKick), "'%s' called for vote to kick you", Server()->ClientName(ClientID)); + SendChatTarget(KickID, aBufKick); + return; } - } - LastMapVote = time_get(); - m_VoteKick = false; - } - else if(str_comp_nocase(pMsg->m_Type, "kick") == 0) - { - if(!m_apPlayers[ClientID]->m_Authed && time_get() < m_apPlayers[ClientID]->m_Last_KickVote + (time_freq() * 5)) - return; - else if(!m_apPlayers[ClientID]->m_Authed && time_get() < m_apPlayers[ClientID]->m_Last_KickVote + (time_freq() * g_Config.m_SvVoteKickTimeDelay)) - { - char chatmsg[512] = {0}; - str_format(chatmsg, sizeof(chatmsg), "There's a %d second wait time between kick votes for each player please wait %d second(s)", - g_Config.m_SvVoteKickTimeDelay, - ((m_apPlayers[ClientID]->m_Last_KickVote + (m_apPlayers[ClientID]->m_Last_KickVote*time_freq()))/time_freq())-(time_get()/time_freq()) - ); - SendChatTarget(ClientID, chatmsg); + // Don't allow kicking if a player has no character + if(!GetPlayerChar(ClientID) || !GetPlayerChar(KickID) || GetDDRaceTeam(ClientID) != GetDDRaceTeam(KickID)) + { + SendChatTarget(ClientID, "You can kick only your team member"); + m_apPlayers[ClientID]->m_Last_KickVote = time_get(); + return; + } + + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to kick '%s' (%s)", Server()->ClientName(ClientID), Server()->ClientName(KickID), pReason); + str_format(aDesc, sizeof(aDesc), "Kick '%s'", Server()->ClientName(KickID)); + if (!g_Config.m_SvVoteKickBantime) + str_format(aCmd, sizeof(aCmd), "kick %d Kicked by vote", KickID); + else + { + char aAddrStr[NETADDR_MAXSTRSIZE] = {0}; + Server()->GetClientAddr(KickID, aAddrStr, sizeof(aAddrStr)); + str_format(aCmd, sizeof(aCmd), "ban %s %d Banned by vote", aAddrStr, g_Config.m_SvVoteKickBantime); + } m_apPlayers[ClientID]->m_Last_KickVote = time_get(); - return; + m_VoteKick = true; } - //else if(!g_Config.m_SvVoteKick) - else if(!g_Config.m_SvVoteKick && !pPlayer->m_Authed) // allow admins to call kick votes even if they are forbidden + else if(str_comp_nocase(pMsg->m_Type, "spectate") == 0) { - SendChatTarget(ClientID, "Server does not allow voting to kick players"); - m_apPlayers[ClientID]->m_Last_KickVote = time_get(); - return; - } + if(!g_Config.m_SvVoteSpectate) + { + SendChatTarget(ClientID, "Server does not allow voting to move players to spectators"); + return; + } - if(g_Config.m_SvVoteKickMin) - { - int PlayerNum = 0; - for(int i = 0; i < MAX_CLIENTS; ++i) - if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) - ++PlayerNum; + int SpectateID = str_toint(pMsg->m_Value); + if (!Server()->ReverseTranslate(SpectateID, ClientID)) + return; - if(PlayerNum < g_Config.m_SvVoteKickMin) + if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !m_apPlayers[SpectateID] || m_apPlayers[SpectateID]->GetTeam() == TEAM_SPECTATORS) { - str_format(aChatmsg, sizeof(aChatmsg), "Kick voting requires %d players on the server", g_Config.m_SvVoteKickMin); - SendChatTarget(ClientID, aChatmsg); + SendChatTarget(ClientID, "Invalid client id to move"); + return; + } + if(SpectateID == ClientID) + { + SendChatTarget(ClientID, "You can't move yourself"); return; } - } - int KickID = str_toint(pMsg->m_Value); - if(KickID < 0 || KickID >= MAX_CLIENTS || !m_apPlayers[KickID]) - { - SendChatTarget(ClientID, "Invalid client id to kick"); - return; - } - if(KickID == ClientID) - { - SendChatTarget(ClientID, "You can't kick yourself"); - return; - } - //if(Server()->IsAuthed(KickID)) - if(m_apPlayers[KickID]->m_Authed > 0 && m_apPlayers[KickID]->m_Authed >= pPlayer->m_Authed) - { - SendChatTarget(ClientID, "You can't kick admins"); - m_apPlayers[ClientID]->m_Last_KickVote = time_get(); - char aBufKick[128]; - str_format(aBufKick, sizeof(aBufKick), "'%s' called for vote to kick you", Server()->ClientName(ClientID)); - SendChatTarget(KickID, aBufKick); - return; - } + if(!GetPlayerChar(ClientID) || !GetPlayerChar(SpectateID) || GetDDRaceTeam(ClientID) != GetDDRaceTeam(SpectateID)) + { + SendChatTarget(ClientID, "You can only move your team member to specators"); + return; + } - if(GetPlayerChar(ClientID) && GetPlayerChar(KickID) && GetDDRaceTeam(ClientID) != GetDDRaceTeam(KickID)) - { - SendChatTarget(ClientID, "You can kick only your team member"); - m_apPlayers[ClientID]->m_Last_KickVote = time_get(); - return; + if(g_Config.m_SvPauseable && g_Config.m_SvVotePause) + { + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to pause '%s' for %d seconds (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), g_Config.m_SvVotePauseTime, pReason); + str_format(aDesc, sizeof(aDesc), "Pause '%s' (%ds)", Server()->ClientName(SpectateID), g_Config.m_SvVotePauseTime); + str_format(aCmd, sizeof(aCmd), "force_pause %d %d", SpectateID, g_Config.m_SvVotePauseTime); + } + else + { + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to move '%s' to spectators (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), pReason); + str_format(aDesc, sizeof(aDesc), "move '%s' to spectators", Server()->ClientName(SpectateID)); + str_format(aCmd, sizeof(aCmd), "set_team %d -1 %d", SpectateID, g_Config.m_SvVoteSpectateRejoindelay); + } } - str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to kick '%s' (%s)", Server()->ClientName(ClientID), Server()->ClientName(KickID), pReason); - str_format(aDesc, sizeof(aDesc), "Kick '%s'", Server()->ClientName(KickID)); - if (!g_Config.m_SvVoteKickBantime) - str_format(aCmd, sizeof(aCmd), "kick %d Kicked by vote", KickID); - else - { - char aAddrStr[NETADDR_MAXSTRSIZE] = {0}; - Server()->GetClientAddr(KickID, aAddrStr, sizeof(aAddrStr)); - str_format(aCmd, sizeof(aCmd), "ban %s %d Banned by vote", aAddrStr, g_Config.m_SvVoteKickBantime); - } - m_apPlayers[ClientID]->m_Last_KickVote = time_get(); - m_VoteKick = true; + if(aCmd[0] && str_comp(aCmd,"info")) + CallVote(ClientID, aDesc, aCmd, pReason, aChatmsg); } - else if(str_comp_nocase(pMsg->m_Type, "spectate") == 0) + else if(MsgID == NETMSGTYPE_CL_VOTE) { - if(!g_Config.m_SvVoteSpectate) - { - SendChatTarget(ClientID, "Server does not allow voting to move players to spectators"); + if(!m_VoteCloseTime) return; - } - int SpectateID = str_toint(pMsg->m_Value); - if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !m_apPlayers[SpectateID] || m_apPlayers[SpectateID]->GetTeam() == TEAM_SPECTATORS) - { - SendChatTarget(ClientID, "Invalid client id to move"); - return; - } - if(SpectateID == ClientID) - { - SendChatTarget(ClientID, "You can't move yourself"); + if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry+Server()->TickSpeed()*3 > Server()->Tick()) return; - } - if(g_Config.m_SvPauseable && g_Config.m_SvVotePause) - { - str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to pause '%s' for %d seconds (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), g_Config.m_SvVotePauseTime, pReason); - str_format(aDesc, sizeof(aDesc), "Pause '%s' (%ds)", Server()->ClientName(SpectateID), g_Config.m_SvVotePauseTime); - str_format(aCmd, sizeof(aCmd), "force_pause %d %d", SpectateID, g_Config.m_SvVotePauseTime); - } - else - { - str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to move '%s' to spectators (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), pReason); - str_format(aDesc, sizeof(aDesc), "move '%s' to spectators", Server()->ClientName(SpectateID)); - str_format(aCmd, sizeof(aCmd), "set_team %d -1 %d", SpectateID, g_Config.m_SvVoteSpectateRejoindelay); - } - } + int64 Now = Server()->Tick(); - if(aCmd[0]) - { - SendChat(-1, CGameContext::CHAT_ALL, aChatmsg); - StartVote(aDesc, aCmd, pReason); - pPlayer->m_Vote = 1; - pPlayer->m_VotePos = m_VotePos = 1; - m_VoteCreator = ClientID; - pPlayer->m_LastVoteCall = Now; - } - } - else if(MsgID == NETMSGTYPE_CL_VOTE) - { - if(!m_VoteCloseTime) - return; + pPlayer->m_LastVoteTry = Now; - if(pPlayer->m_Vote == 0) - { CNetMsg_Cl_Vote *pMsg = (CNetMsg_Cl_Vote *)pRawMsg; if(!pMsg->m_Vote) return; @@ -1050,312 +1248,329 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) pPlayer->m_VotePos = ++m_VotePos; m_VoteUpdate = true; } - } - else if (MsgID == NETMSGTYPE_CL_SETTEAM && !m_World.m_Paused) - { - CNetMsg_Cl_SetTeam *pMsg = (CNetMsg_Cl_SetTeam *)pRawMsg; + else if (MsgID == NETMSGTYPE_CL_SETTEAM && !m_World.m_Paused) + { + CNetMsg_Cl_SetTeam *pMsg = (CNetMsg_Cl_SetTeam *)pRawMsg; - //if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam+Server()->TickSpeed()*3 > Server()->Tick())) - if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam + Server()->TickSpeed() * g_Config.m_SvTeamChangeDelay > Server()->Tick())) - return; + //if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam+Server()->TickSpeed()*3 > Server()->Tick())) + if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam + Server()->TickSpeed() * g_Config.m_SvTeamChangeDelay > Server()->Tick())) + return; - /*if(pMsg->m_Team != TEAM_SPECTATORS && m_LockTeams) - { - pPlayer->m_LastSetTeam = Server()->Tick(); - SendBroadcast("Teams are locked", ClientID); - return; - }*/ + /*if(pMsg->m_Team != TEAM_SPECTATORS && m_LockTeams) + { + pPlayer->m_LastSetTeam = Server()->Tick(); + SendBroadcast("Teams are locked", ClientID); + return; + }*/ - if(pPlayer->m_TeamChangeTick > Server()->Tick()) - { - pPlayer->m_LastSetTeam = Server()->Tick(); - int TimeLeft = (pPlayer->m_TeamChangeTick - Server()->Tick())/Server()->TickSpeed(); - char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "Time to wait before changing team: %02d:%02d", TimeLeft/60, TimeLeft%60); - SendBroadcast(aBuf, ClientID); - return; - } + if(pPlayer->m_TeamChangeTick > Server()->Tick()) + { + pPlayer->m_LastSetTeam = Server()->Tick(); + int TimeLeft = (pPlayer->m_TeamChangeTick - Server()->Tick())/Server()->TickSpeed(); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Time to wait before changing team: %02d:%02d", TimeLeft/60, TimeLeft%60); + SendBroadcast(aBuf, ClientID); + return; + } - // Switch team on given client and kill/respawn him - if(m_pController->CanJoinTeam(pMsg->m_Team, ClientID)) - { - //if(m_pController->CanChangeTeam(pPlayer, pMsg->m_Team)) + // Switch team on given client and kill/respawn him + if(m_pController->CanJoinTeam(pMsg->m_Team, ClientID)) + { + //if(m_pController->CanChangeTeam(pPlayer, pMsg->m_Team)) - if(pPlayer->m_Paused) - SendChatTarget(ClientID,"Use /pause first then you can kill"); + if(pPlayer->m_Paused) + SendChatTarget(ClientID,"Use /pause first then you can kill"); + else + { + //pPlayer->m_LastSetTeam = Server()->Tick(); + if(pPlayer->GetTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS) + m_VoteUpdate = true; + pPlayer->SetTeam(pMsg->m_Team); + //(void)m_pController->CheckTeamBalance(); + pPlayer->m_TeamChangeTick = Server()->Tick(); + } + //else + //SendBroadcast("Teams must be balanced, please join other team", ClientID); + } else { - //pPlayer->m_LastSetTeam = Server()->Tick(); - if(pPlayer->GetTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS) - m_VoteUpdate = true; - pPlayer->SetTeam(pMsg->m_Team); - //(void)m_pController->CheckTeamBalance(); - pPlayer->m_TeamChangeTick = Server()->Tick(); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Only %d active players are allowed", Server()->MaxClients()-g_Config.m_SvSpectatorSlots); + SendBroadcast(aBuf, ClientID); } - //else - //SendBroadcast("Teams must be balanced, please join other team", ClientID); } - else + else if (MsgID == NETMSGTYPE_CL_ISDDNET) { - char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "Only %d active players are allowed", Server()->MaxClients()-g_Config.m_SvSpectatorSlots); - SendBroadcast(aBuf, ClientID); - } - } - else if (MsgID == NETMSGTYPE_CL_ISDDRACE) - { - pPlayer->m_IsUsingDDRaceClient = true; + int Version = pUnpacker->GetInt(); - char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "%d use DDRace Client", ClientID); - dbg_msg("DDRace", aBuf); + if (pUnpacker->Error()) + { + if (pPlayer->m_ClientVersion < VERSION_DDRACE) + pPlayer->m_ClientVersion = VERSION_DDRACE; + } + else + pPlayer->m_ClientVersion = Version; - //first update his teams state - ((CGameControllerDDRace*)m_pController)->m_Teams.SendTeamsState(ClientID); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "%d using Custom Client %d", ClientID, pPlayer->m_ClientVersion); + dbg_msg("DDNet", aBuf); - //second give him records - SendRecord(ClientID); + //first update his teams state + ((CGameControllerDDRace*)m_pController)->m_Teams.SendTeamsState(ClientID); + //second give him records + SendRecord(ClientID); - //third give him others current time for table score - if(g_Config.m_SvHideScore) return; - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(m_apPlayers[i] && Score()->PlayerData(i)->m_CurrentTime > 0) + //third give him others current time for table score + if(g_Config.m_SvHideScore) return; + for(int i = 0; i < MAX_CLIENTS; i++) { - CNetMsg_Sv_PlayerTime Msg; - Msg.m_Time = Score()->PlayerData(i)->m_CurrentTime * 100; - Msg.m_ClientID = i; - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID); - //also send its time to others + if(m_apPlayers[i] && Score()->PlayerData(i)->m_CurrentTime > 0) + { + CNetMsg_Sv_PlayerTime Msg; + Msg.m_Time = Score()->PlayerData(i)->m_CurrentTime * 100; + Msg.m_ClientID = i; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID); + //also send its time to others + } + } + //also send its time to others + if(Score()->PlayerData(ClientID)->m_CurrentTime > 0) + { + //TODO: make function for this fucking steps + CNetMsg_Sv_PlayerTime Msg; + Msg.m_Time = Score()->PlayerData(ClientID)->m_CurrentTime * 100; + Msg.m_ClientID = ClientID; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); } - } - //also send its time to others - if(Score()->PlayerData(ClientID)->m_CurrentTime > 0) { - //TODO: make function for this fucking steps - CNetMsg_Sv_PlayerTime Msg; - Msg.m_Time = Score()->PlayerData(ClientID)->m_CurrentTime * 100; - Msg.m_ClientID = ClientID; - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); - } - } - else if (MsgID == NETMSGTYPE_CL_SHOWOTHERS) - { - if(g_Config.m_SvShowOthers); - { - // TODO: prevent spam ? - CNetMsg_Cl_ShowOthers *pMsg = (CNetMsg_Cl_ShowOthers *)pRawMsg; - pPlayer->m_ShowOthers = (bool)pMsg->m_Show; - } - } - else if (MsgID == NETMSGTYPE_CL_SETSPECTATORMODE && !m_World.m_Paused) - { - CNetMsg_Cl_SetSpectatorMode *pMsg = (CNetMsg_Cl_SetSpectatorMode *)pRawMsg; - - if((pPlayer->GetTeam() != TEAM_SPECTATORS && !pPlayer->m_Paused) || pPlayer->m_SpectatorID == pMsg->m_SpectatorID || ClientID == pMsg->m_SpectatorID || - (g_Config.m_SvSpamprotection && pPlayer->m_LastSetSpectatorMode && pPlayer->m_LastSetSpectatorMode+Server()->TickSpeed()*3 > Server()->Tick())) - return; - - pPlayer->m_LastSetSpectatorMode = Server()->Tick(); - if(pMsg->m_SpectatorID != SPEC_FREEVIEW && (!m_apPlayers[pMsg->m_SpectatorID] || m_apPlayers[pMsg->m_SpectatorID]->GetTeam() == TEAM_SPECTATORS)) - SendChatTarget(ClientID, "Invalid spectator id used"); - else - pPlayer->m_SpectatorID = pMsg->m_SpectatorID; - } - else if (MsgID == NETMSGTYPE_CL_STARTINFO) - { - if(pPlayer->m_IsReady) - return; - CNetMsg_Cl_StartInfo *pMsg = (CNetMsg_Cl_StartInfo *)pRawMsg; - pPlayer->m_LastChangeInfo = Server()->Tick(); - - // set start infos - Server()->SetClientName(ClientID, pMsg->m_pName); - Server()->SetClientClan(ClientID, pMsg->m_pClan); - Server()->SetClientCountry(ClientID, pMsg->m_Country); - str_copy(pPlayer->m_TeeInfos.m_SkinName, pMsg->m_pSkin, sizeof(pPlayer->m_TeeInfos.m_SkinName)); - pPlayer->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor; - pPlayer->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody; - pPlayer->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet; - //m_pController->OnPlayerInfoChange(pPlayer); - - // send vote options - CNetMsg_Sv_VoteClearOptions ClearMsg; - Server()->SendPackMsg(&ClearMsg, MSGFLAG_VITAL, ClientID); - - CNetMsg_Sv_VoteOptionListAdd OptionMsg; - int NumOptions = 0; - OptionMsg.m_pDescription0 = ""; - OptionMsg.m_pDescription1 = ""; - OptionMsg.m_pDescription2 = ""; - OptionMsg.m_pDescription3 = ""; - OptionMsg.m_pDescription4 = ""; - OptionMsg.m_pDescription5 = ""; - OptionMsg.m_pDescription6 = ""; - OptionMsg.m_pDescription7 = ""; - OptionMsg.m_pDescription8 = ""; - OptionMsg.m_pDescription9 = ""; - OptionMsg.m_pDescription10 = ""; - OptionMsg.m_pDescription11 = ""; - OptionMsg.m_pDescription12 = ""; - OptionMsg.m_pDescription13 = ""; - OptionMsg.m_pDescription14 = ""; - CVoteOptionServer *pCurrent = m_pVoteOptionFirst; - while(pCurrent) + //and give him correct tunings + if (Version >= VERSION_DDNET_EXTRATUNES) + SendTuningParams(ClientID, pPlayer->m_TuneZone); + } + else if (MsgID == NETMSGTYPE_CL_SHOWOTHERS) { - switch(NumOptions++) + if(g_Config.m_SvShowOthers && !g_Config.m_SvShowOthersDefault) { - case 0: OptionMsg.m_pDescription0 = pCurrent->m_aDescription; break; - case 1: OptionMsg.m_pDescription1 = pCurrent->m_aDescription; break; - case 2: OptionMsg.m_pDescription2 = pCurrent->m_aDescription; break; - case 3: OptionMsg.m_pDescription3 = pCurrent->m_aDescription; break; - case 4: OptionMsg.m_pDescription4 = pCurrent->m_aDescription; break; - case 5: OptionMsg.m_pDescription5 = pCurrent->m_aDescription; break; - case 6: OptionMsg.m_pDescription6 = pCurrent->m_aDescription; break; - case 7: OptionMsg.m_pDescription7 = pCurrent->m_aDescription; break; - case 8: OptionMsg.m_pDescription8 = pCurrent->m_aDescription; break; - case 9: OptionMsg.m_pDescription9 = pCurrent->m_aDescription; break; - case 10: OptionMsg.m_pDescription10 = pCurrent->m_aDescription; break; - case 11: OptionMsg.m_pDescription11 = pCurrent->m_aDescription; break; - case 12: OptionMsg.m_pDescription12 = pCurrent->m_aDescription; break; - case 13: OptionMsg.m_pDescription13 = pCurrent->m_aDescription; break; - case 14: - { - OptionMsg.m_pDescription14 = pCurrent->m_aDescription; - OptionMsg.m_NumOptions = NumOptions; - Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, ClientID); - OptionMsg = CNetMsg_Sv_VoteOptionListAdd(); - NumOptions = 0; - OptionMsg.m_pDescription1 = ""; - OptionMsg.m_pDescription2 = ""; - OptionMsg.m_pDescription3 = ""; - OptionMsg.m_pDescription4 = ""; - OptionMsg.m_pDescription5 = ""; - OptionMsg.m_pDescription6 = ""; - OptionMsg.m_pDescription7 = ""; - OptionMsg.m_pDescription8 = ""; - OptionMsg.m_pDescription9 = ""; - OptionMsg.m_pDescription10 = ""; - OptionMsg.m_pDescription11 = ""; - OptionMsg.m_pDescription12 = ""; - OptionMsg.m_pDescription13 = ""; - OptionMsg.m_pDescription14 = ""; - } + CNetMsg_Cl_ShowOthers *pMsg = (CNetMsg_Cl_ShowOthers *)pRawMsg; + pPlayer->m_ShowOthers = (bool)pMsg->m_Show; } - pCurrent = pCurrent->m_pNext; } - if(NumOptions > 0) + else if (MsgID == NETMSGTYPE_CL_SETSPECTATORMODE && !m_World.m_Paused) { - OptionMsg.m_NumOptions = NumOptions; - Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, ClientID); - NumOptions = 0; - } + CNetMsg_Cl_SetSpectatorMode *pMsg = (CNetMsg_Cl_SetSpectatorMode *)pRawMsg; + + if(pMsg->m_SpectatorID != SPEC_FREEVIEW) + if (!Server()->ReverseTranslate(pMsg->m_SpectatorID, ClientID)) + return; - // send tuning parameters to client - SendTuningParams(ClientID); + if((g_Config.m_SvSpamprotection && pPlayer->m_LastSetSpectatorMode && pPlayer->m_LastSetSpectatorMode+Server()->TickSpeed() > Server()->Tick())) + return; - // client is ready to enter - pPlayer->m_IsReady = true; - CNetMsg_Sv_ReadyToEnter m; - Server()->SendPackMsg(&m, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID); - } - else if (MsgID == NETMSGTYPE_CL_CHANGEINFO) - { - if(g_Config.m_SvSpamprotection && pPlayer->m_LastChangeInfo && pPlayer->m_LastChangeInfo+Server()->TickSpeed()*g_Config.m_SvInfoChangeDelay > Server()->Tick()) - return; + pPlayer->m_LastSetSpectatorMode = Server()->Tick(); + if(pMsg->m_SpectatorID != SPEC_FREEVIEW && (!m_apPlayers[pMsg->m_SpectatorID] || m_apPlayers[pMsg->m_SpectatorID]->GetTeam() == TEAM_SPECTATORS)) + SendChatTarget(ClientID, "Invalid spectator id used"); + else + pPlayer->m_SpectatorID = pMsg->m_SpectatorID; + } + else if (MsgID == NETMSGTYPE_CL_CHANGEINFO) + { + if(g_Config.m_SvSpamprotection && pPlayer->m_LastChangeInfo && pPlayer->m_LastChangeInfo+Server()->TickSpeed()*g_Config.m_SvInfoChangeDelay > Server()->Tick()) + return; - CNetMsg_Cl_ChangeInfo *pMsg = (CNetMsg_Cl_ChangeInfo *)pRawMsg; - pPlayer->m_LastChangeInfo = Server()->Tick(); + CNetMsg_Cl_ChangeInfo *pMsg = (CNetMsg_Cl_ChangeInfo *)pRawMsg; + pPlayer->m_LastChangeInfo = Server()->Tick(); - // set infos - char aOldName[MAX_NAME_LENGTH]; - str_copy(aOldName, Server()->ClientName(ClientID), sizeof(aOldName)); - Server()->SetClientName(ClientID, pMsg->m_pName); - if(str_comp(aOldName, Server()->ClientName(ClientID)) != 0) - { - char aChatText[256]; - str_format(aChatText, sizeof(aChatText), "'%s' changed name to '%s'", aOldName, Server()->ClientName(ClientID)); - SendChat(-1, CGameContext::CHAT_ALL, aChatText); + // set infos + char aOldName[MAX_NAME_LENGTH]; + str_copy(aOldName, Server()->ClientName(ClientID), sizeof(aOldName)); + Server()->SetClientName(ClientID, pMsg->m_pName); + if(str_comp(aOldName, Server()->ClientName(ClientID)) != 0) + { + char aChatText[256]; + str_format(aChatText, sizeof(aChatText), "'%s' changed name to '%s'", aOldName, Server()->ClientName(ClientID)); + SendChat(-1, CGameContext::CHAT_ALL, aChatText); - // reload scores + // reload scores - Score()->PlayerData(ClientID)->Reset(); - Score()->LoadScore(ClientID); - Score()->PlayerData(ClientID)->m_CurrentTime = Score()->PlayerData(ClientID)->m_BestTime; - m_apPlayers[ClientID]->m_Score = (Score()->PlayerData(ClientID)->m_BestTime)?Score()->PlayerData(ClientID)->m_BestTime:-9999; + Score()->PlayerData(ClientID)->Reset(); + Score()->LoadScore(ClientID); + Score()->PlayerData(ClientID)->m_CurrentTime = Score()->PlayerData(ClientID)->m_BestTime; + m_apPlayers[ClientID]->m_Score = (Score()->PlayerData(ClientID)->m_BestTime)?Score()->PlayerData(ClientID)->m_BestTime:-9999; + } + Server()->SetClientClan(ClientID, pMsg->m_pClan); + Server()->SetClientCountry(ClientID, pMsg->m_Country); + str_copy(pPlayer->m_TeeInfos.m_SkinName, pMsg->m_pSkin, sizeof(pPlayer->m_TeeInfos.m_SkinName)); + pPlayer->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor; + pPlayer->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody; + pPlayer->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet; + //m_pController->OnPlayerInfoChange(pPlayer); } - Server()->SetClientClan(ClientID, pMsg->m_pClan); - Server()->SetClientCountry(ClientID, pMsg->m_Country); - str_copy(pPlayer->m_TeeInfos.m_SkinName, pMsg->m_pSkin, sizeof(pPlayer->m_TeeInfos.m_SkinName)); - pPlayer->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor; - pPlayer->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody; - pPlayer->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet; - //m_pController->OnPlayerInfoChange(pPlayer); - } - else if (MsgID == NETMSGTYPE_CL_EMOTICON && !m_World.m_Paused) - { - CNetMsg_Cl_Emoticon *pMsg = (CNetMsg_Cl_Emoticon *)pRawMsg; + else if (MsgID == NETMSGTYPE_CL_EMOTICON && !m_World.m_Paused) + { + CNetMsg_Cl_Emoticon *pMsg = (CNetMsg_Cl_Emoticon *)pRawMsg; - if(g_Config.m_SvSpamprotection && pPlayer->m_LastEmote && pPlayer->m_LastEmote+Server()->TickSpeed()*g_Config.m_SvEmoticonDelay > Server()->Tick()) - return; + if(g_Config.m_SvSpamprotection && pPlayer->m_LastEmote && pPlayer->m_LastEmote+Server()->TickSpeed()*g_Config.m_SvEmoticonDelay > Server()->Tick()) + return; - pPlayer->m_LastEmote = Server()->Tick(); + pPlayer->m_LastEmote = Server()->Tick(); - SendEmoticon(ClientID, pMsg->m_Emoticon); - CCharacter* pChr = pPlayer->GetCharacter(); - if(pChr && g_Config.m_SvEmotionalTees && pPlayer->m_EyeEmote) + SendEmoticon(ClientID, pMsg->m_Emoticon); + CCharacter* pChr = pPlayer->GetCharacter(); + if(pChr && g_Config.m_SvEmotionalTees && pPlayer->m_EyeEmote) + { + switch(pMsg->m_Emoticon) + { + case EMOTICON_EXCLAMATION: + case EMOTICON_GHOST: + case EMOTICON_QUESTION: + case EMOTICON_WTF: + pChr->SetEmoteType(EMOTE_SURPRISE); + break; + case EMOTICON_DOTDOT: + case EMOTICON_DROP: + case EMOTICON_ZZZ: + pChr->SetEmoteType(EMOTE_BLINK); + break; + case EMOTICON_EYES: + case EMOTICON_HEARTS: + case EMOTICON_MUSIC: + pChr->SetEmoteType(EMOTE_HAPPY); + break; + case EMOTICON_OOP: + case EMOTICON_SORRY: + case EMOTICON_SUSHI: + pChr->SetEmoteType(EMOTE_PAIN); + break; + case EMOTICON_DEVILTEE: + case EMOTICON_SPLATTEE: + case EMOTICON_ZOMG: + pChr->SetEmoteType(EMOTE_ANGRY); + break; + default: + pChr->SetEmoteType(EMOTE_NORMAL); + break; + } + pChr->SetEmoteStop(Server()->Tick() + 2 * Server()->TickSpeed()); + } + } + else if (MsgID == NETMSGTYPE_CL_KILL && !m_World.m_Paused) { - switch(pMsg->m_Emoticon) + if(m_VoteCloseTime && m_VoteCreator == ClientID && GetDDRaceTeam(ClientID)) { - case EMOTICON_EXCLAMATION: - case EMOTICON_GHOST: - case EMOTICON_QUESTION: - case EMOTICON_WTF: - pChr->SetEmoteType(EMOTE_SURPRISE); - break; - case EMOTICON_DOTDOT: - case EMOTICON_DROP: - case EMOTICON_ZZZ: - pChr->SetEmoteType(EMOTE_BLINK); - break; - case EMOTICON_EYES: - case EMOTICON_HEARTS: - case EMOTICON_MUSIC: - pChr->SetEmoteType(EMOTE_HAPPY); - break; - case EMOTICON_OOP: - case EMOTICON_SORRY: - case EMOTICON_SUSHI: - pChr->SetEmoteType(EMOTE_PAIN); - break; - case EMOTICON_DEVILTEE: - case EMOTICON_SPLATTEE: - case EMOTICON_ZOMG: - pChr->SetEmoteType(EMOTE_ANGRY); - break; - default: - pChr->SetEmoteType(EMOTE_NORMAL); - break; + SendChatTarget(ClientID, "You are running a vote please try again after the vote is done!"); + return; } - pChr->SetEmoteStop(Server()->Tick() + 2 * Server()->TickSpeed()); + if(pPlayer->m_LastKill && pPlayer->m_LastKill+Server()->TickSpeed()*g_Config.m_SvKillDelay > Server()->Tick()) + return; + if(pPlayer->m_Paused) + return; + + pPlayer->m_LastKill = Server()->Tick(); + pPlayer->KillCharacter(WEAPON_SELF); } } - else if (MsgID == NETMSGTYPE_CL_KILL && !m_World.m_Paused) + else { - if(m_VoteCloseTime && m_VoteCreator == ClientID && GetDDRaceTeam(ClientID)) + if (MsgID == NETMSGTYPE_CL_STARTINFO) { - SendChatTarget(ClientID, "You are running a vote please try again after the vote is done!"); - return; - } - if(pPlayer->m_LastKill && pPlayer->m_LastKill+Server()->TickSpeed()*g_Config.m_SvKillDelay > Server()->Tick()) - return; - if(pPlayer->m_Paused) - return; + if(pPlayer->m_IsReady) + return; + + CNetMsg_Cl_StartInfo *pMsg = (CNetMsg_Cl_StartInfo *)pRawMsg; + pPlayer->m_LastChangeInfo = Server()->Tick(); + + // set start infos + Server()->SetClientName(ClientID, pMsg->m_pName); + Server()->SetClientClan(ClientID, pMsg->m_pClan); + Server()->SetClientCountry(ClientID, pMsg->m_Country); + str_copy(pPlayer->m_TeeInfos.m_SkinName, pMsg->m_pSkin, sizeof(pPlayer->m_TeeInfos.m_SkinName)); + pPlayer->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor; + pPlayer->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody; + pPlayer->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet; + //m_pController->OnPlayerInfoChange(pPlayer); + + // send vote options + CNetMsg_Sv_VoteClearOptions ClearMsg; + Server()->SendPackMsg(&ClearMsg, MSGFLAG_VITAL, ClientID); + + CNetMsg_Sv_VoteOptionListAdd OptionMsg; + int NumOptions = 0; + OptionMsg.m_pDescription0 = ""; + OptionMsg.m_pDescription1 = ""; + OptionMsg.m_pDescription2 = ""; + OptionMsg.m_pDescription3 = ""; + OptionMsg.m_pDescription4 = ""; + OptionMsg.m_pDescription5 = ""; + OptionMsg.m_pDescription6 = ""; + OptionMsg.m_pDescription7 = ""; + OptionMsg.m_pDescription8 = ""; + OptionMsg.m_pDescription9 = ""; + OptionMsg.m_pDescription10 = ""; + OptionMsg.m_pDescription11 = ""; + OptionMsg.m_pDescription12 = ""; + OptionMsg.m_pDescription13 = ""; + OptionMsg.m_pDescription14 = ""; + CVoteOptionServer *pCurrent = m_pVoteOptionFirst; + while(pCurrent) + { + switch(NumOptions++) + { + case 0: OptionMsg.m_pDescription0 = pCurrent->m_aDescription; break; + case 1: OptionMsg.m_pDescription1 = pCurrent->m_aDescription; break; + case 2: OptionMsg.m_pDescription2 = pCurrent->m_aDescription; break; + case 3: OptionMsg.m_pDescription3 = pCurrent->m_aDescription; break; + case 4: OptionMsg.m_pDescription4 = pCurrent->m_aDescription; break; + case 5: OptionMsg.m_pDescription5 = pCurrent->m_aDescription; break; + case 6: OptionMsg.m_pDescription6 = pCurrent->m_aDescription; break; + case 7: OptionMsg.m_pDescription7 = pCurrent->m_aDescription; break; + case 8: OptionMsg.m_pDescription8 = pCurrent->m_aDescription; break; + case 9: OptionMsg.m_pDescription9 = pCurrent->m_aDescription; break; + case 10: OptionMsg.m_pDescription10 = pCurrent->m_aDescription; break; + case 11: OptionMsg.m_pDescription11 = pCurrent->m_aDescription; break; + case 12: OptionMsg.m_pDescription12 = pCurrent->m_aDescription; break; + case 13: OptionMsg.m_pDescription13 = pCurrent->m_aDescription; break; + case 14: + { + OptionMsg.m_pDescription14 = pCurrent->m_aDescription; + OptionMsg.m_NumOptions = NumOptions; + Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, ClientID); + OptionMsg = CNetMsg_Sv_VoteOptionListAdd(); + NumOptions = 0; + OptionMsg.m_pDescription1 = ""; + OptionMsg.m_pDescription2 = ""; + OptionMsg.m_pDescription3 = ""; + OptionMsg.m_pDescription4 = ""; + OptionMsg.m_pDescription5 = ""; + OptionMsg.m_pDescription6 = ""; + OptionMsg.m_pDescription7 = ""; + OptionMsg.m_pDescription8 = ""; + OptionMsg.m_pDescription9 = ""; + OptionMsg.m_pDescription10 = ""; + OptionMsg.m_pDescription11 = ""; + OptionMsg.m_pDescription12 = ""; + OptionMsg.m_pDescription13 = ""; + OptionMsg.m_pDescription14 = ""; + } + } + pCurrent = pCurrent->m_pNext; + } + if(NumOptions > 0) + { + OptionMsg.m_NumOptions = NumOptions; + Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, ClientID); + NumOptions = 0; + } + + // send tuning parameters to client + SendTuningParams(ClientID, pPlayer->m_TuneZone); - pPlayer->m_LastKill = Server()->Tick(); - pPlayer->KillCharacter(WEAPON_SELF); + // client is ready to enter + pPlayer->m_IsReady = true; + CNetMsg_Sv_ReadyToEnter m; + Server()->SendPackMsg(&m, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID); + } } } @@ -1400,6 +1615,97 @@ void CGameContext::ConTuneDump(IConsole::IResult *pResult, void *pUserData) } } +void CGameContext::ConTuneZone(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + int List = pResult->GetInteger(0); + const char *pParamName = pResult->GetString(1); + float NewValue = pResult->GetFloat(2); + + if (List >= 0 && List < 256) + { + if(pSelf->TuningList()[List].Set(pParamName, NewValue)) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "%s in zone %d changed to %.2f", pParamName, List, NewValue); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf); + pSelf->SendTuningParams(-1, List); + } + else + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", "No such tuning parameter"); + } +} + +void CGameContext::ConTuneDumpZone(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + int List = pResult->GetInteger(0); + char aBuf[256]; + if (List >= 0 && List < 256) + { + for(int i = 0; i < pSelf->TuningList()[List].Num(); i++) + { + float v; + pSelf->TuningList()[List].Get(i, &v); + str_format(aBuf, sizeof(aBuf), "zone %d: %s %.2f", List, pSelf->TuningList()[List].m_apNames[i], v); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf); + } + } +} + +void CGameContext::ConTuneResetZone(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + CTuningParams TuningParams; + if (pResult->NumArguments()) + { + int List = pResult->GetInteger(0); + if (List >= 0 && List < 256) + { + pSelf->TuningList()[List] = TuningParams; + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "Tunezone %d resetted", List); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", aBuf); + pSelf->SendTuningParams(-1, List); + } + } + else + { + for (int i = 0; i < 256; i++) + { + *(pSelf->TuningList()+i) = TuningParams; + pSelf->SendTuningParams(-1, i); + } + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "tuning", "All Tunezones resetted"); + } +} + +void CGameContext::ConTuneSetZoneMsgEnter(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if (pResult->NumArguments()) + { + int List = pResult->GetInteger(0); + if (List >= 0 && List < 256) + { + str_format(pSelf->m_ZoneEnterMsg[List], sizeof(pSelf->m_ZoneEnterMsg[List]), pResult->GetString(1)); + } + } +} + +void CGameContext::ConTuneSetZoneMsgLeave(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if (pResult->NumArguments()) + { + int List = pResult->GetInteger(0); + if (List >= 0 && List < 256) + { + str_format(pSelf->m_ZoneLeaveMsg[List], sizeof(pSelf->m_ZoneLeaveMsg[List]), pResult->GetString(1)); + } + } +} + void CGameContext::ConPause(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; @@ -1416,6 +1722,66 @@ void CGameContext::ConChangeMap(IConsole::IResult *pResult, void *pUserData) pSelf->m_pController->ChangeMap(pResult->NumArguments() ? pResult->GetString(0) : ""); } +void CGameContext::ConRandomMap(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + int NumMaps = 0; + int NumVotes = 0; + int OurMap; + char* pMapName; + char pQuotedMapName[64]; + + CVoteOptionServer *pOption = pSelf->m_pVoteOptionFirst; + while(pOption) + { + if(strncmp(pOption->m_aCommand, "change_map ", 11) == 0 + || strncmp(pOption->m_aCommand, "sv_map ", 7) == 0) + NumMaps++; + + NumVotes++; + pOption = pOption->m_pNext; + } + + if(!NumMaps) + { + pSelf->SendChat(-1, CGameContext::CHAT_ALL, "random_map called, but no maps available in votes"); + return; + } + + while(true) + { + OurMap = rand() % NumVotes; + pOption = pSelf->m_pVoteOptionFirst; + + while(OurMap > 0) + { + OurMap--; + pOption = pOption->m_pNext; + } + + if(strncmp(pOption->m_aCommand, "change_map ", 11) == 0) + pMapName = &pOption->m_aCommand[11]; + else if(strncmp(pOption->m_aCommand, "sv_map ", 7) == 0) + pMapName = &pOption->m_aCommand[7]; + else + continue; + + str_format(pQuotedMapName, sizeof(pQuotedMapName), "\"%s\"", g_Config.m_SvMap); + + if(str_comp(pMapName, g_Config.m_SvMap) == 0 || str_comp(pMapName, pQuotedMapName) == 0) + continue; + + pSelf->Console()->ExecuteLine(pOption->m_aCommand); + break; + } +} + +void CGameContext::ConRandomUnfinishedMap(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->m_pScore->RandomUnfinishedMap(pSelf->m_VoteCreator); +} + void CGameContext::ConRestart(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; @@ -1428,7 +1794,26 @@ void CGameContext::ConRestart(IConsole::IResult *pResult, void *pUserData) void CGameContext::ConBroadcast(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; - pSelf->SendBroadcast(pResult->GetString(0), -1); + + char aBuf[1024]; + str_copy(aBuf, pResult->GetString(0), sizeof(aBuf)); + + int i, j; + for(i = 0, j = 0; aBuf[i]; i++, j++) + { + if(aBuf[i] == '\\' && aBuf[i+1] == 'n') + { + aBuf[j] = '\n'; + i++; + } + else if (i != j) + { + aBuf[j] = aBuf[i]; + } + } + aBuf[j] = '\0'; + + pSelf->SendBroadcast(aBuf, -1); } void CGameContext::ConSay(IConsole::IResult *pResult, void *pUserData) @@ -1452,6 +1837,8 @@ void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData) pSelf->m_apPlayers[ClientID]->m_TeamChangeTick = pSelf->Server()->Tick()+pSelf->Server()->TickSpeed()*Delay*60; pSelf->m_apPlayers[ClientID]->SetTeam(Team); + if(Team == TEAM_SPECTATORS) + pSelf->m_apPlayers[ClientID]->m_Paused = CPlayer::PAUSED_NONE; // (void)pSelf->m_pController->CheckTeamBalance(); } @@ -1790,9 +2177,15 @@ void CGameContext::OnConsoleInit() Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, "Tune variable to value"); Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, "Reset tuning"); Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, "Dump tuning"); - + Console()->Register("tune_zone", "isi", CFGFLAG_SERVER, ConTuneZone, this, "Tune in zone a variable to value"); + Console()->Register("tune_zone_dump", "i", CFGFLAG_SERVER, ConTuneDumpZone, this, "Dump zone tuning in zone x"); + Console()->Register("tune_zone_reset", "?i", CFGFLAG_SERVER, ConTuneResetZone, this, "reset zone tuning in zone x or in all zones"); + Console()->Register("tune_zone_enter", "is", CFGFLAG_SERVER, ConTuneSetZoneMsgEnter, this, "which message to display on zone enter; use 0 for normal area"); + Console()->Register("tune_zone_leave", "is", CFGFLAG_SERVER, ConTuneSetZoneMsgLeave, this, "which message to display on zone leave; use 0 for normal area"); Console()->Register("pause_game", "", CFGFLAG_SERVER, ConPause, this, "Pause/unpause game"); Console()->Register("change_map", "?r", CFGFLAG_SERVER|CFGFLAG_STORE, ConChangeMap, this, "Change map"); + Console()->Register("random_map", "", CFGFLAG_SERVER, ConRandomMap, this, "Random map"); + Console()->Register("random_unfinished_map", "", CFGFLAG_SERVER, ConRandomUnfinishedMap, this, "Random unfinished map"); Console()->Register("restart", "?i", CFGFLAG_SERVER|CFGFLAG_STORE, ConRestart, this, "Restart in x seconds (0 = abort)"); Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConBroadcast, this, "Broadcast message"); Console()->Register("say", "r", CFGFLAG_SERVER, ConSay, this, "Say in chat"); @@ -1836,6 +2229,23 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/) //world = new GAMEWORLD; //players = new CPlayer[MAX_CLIENTS]; + // Reset Tunezones + CTuningParams TuningParams; + for (int i = 0; i < 256; i++) + { + TuningList()[i] = TuningParams; + TuningList()[i].Set("gun_curvature", 0); + TuningList()[i].Set("gun_speed", 1400); + TuningList()[i].Set("shotgun_curvature", 0); + TuningList()[i].Set("shotgun_speed", 500); + TuningList()[i].Set("shotgun_speeddiff", 0); + } + + for (int i = 0; i < 256; i++) // decided to send no text on changing Tunezones for now + { + str_format(m_ZoneEnterMsg[i], sizeof(m_ZoneEnterMsg[i]), "", i); + str_format(m_ZoneLeaveMsg[i], sizeof(m_ZoneLeaveMsg[i]), "", i); + } // Reset Tuning if(g_Config.m_SvTuneReset) { @@ -1855,8 +2265,13 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/) g_Config.m_SvHit = 1; g_Config.m_SvEndlessDrag = 0; g_Config.m_SvOldLaser = 0; + g_Config.m_SvOldTeleportHook = 0; + g_Config.m_SvOldTeleportWeapons = 0; + g_Config.m_SvTeleportHoldHook = 0; } + Console()->ExecuteFile(g_Config.m_SvResetFile); + char buf[512]; str_format(buf, sizeof(buf), "data/maps/%s.cfg", g_Config.m_SvMap); Console()->ExecuteFile(buf); @@ -1985,6 +2400,8 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/) if(pSwitch) { Index = pSwitch[y*pTileMap->m_Width + x].m_Type; + // TODO: Add off by default door here + // if (Index == TILE_DOOR_OFF) if(Index >= ENTITY_OFFSET) { vec2 Pos(x*32.0f+16.0f, y*32.0f+16.0f); @@ -2038,6 +2455,8 @@ void CGameContext::OnSnap(int ClientID) if(m_apPlayers[i]) m_apPlayers[i]->Snap(ClientID); } + m_apPlayers[ClientID]->FakeSnap(ClientID); + } void CGameContext::OnPreSnap() {} void CGameContext::OnPostSnap() @@ -2119,6 +2538,13 @@ bool CGameContext::PlayerHooking() return Temp != 0.0; } +float CGameContext::PlayerJetpack() +{ + float Temp; + m_Tuning.Get("player_jetpack", &Temp); + return Temp; +} + void CGameContext::OnSetAuthed(int ClientID, int Level) { CServer* pServ = (CServer*)Server(); @@ -2198,3 +2624,210 @@ void CGameContext::ResetTuning() Tuning()->Set("shotgun_curvature", 0); SendTuningParams(-1); } + +bool CheckClientID2(int ClientID) +{ + dbg_assert(ClientID >= 0 || ClientID < MAX_CLIENTS, + "The Client ID is wrong"); + if (ClientID < 0 || ClientID >= MAX_CLIENTS) + return false; + return true; +} + +void CGameContext::Whisper(int ClientID, char *pStr) +{ + char *pName; + char *pMessage; + int Error = 0; + + if(ProcessSpamProtection(ClientID)) + return; + + pStr = str_skip_whitespaces(pStr); + + int Victim; + + // add token + if(*pStr == '"') + { + pStr++; + + pName = pStr; // we might have to process escape data + while(1) + { + if(pStr[0] == '"') + break; + else if(pStr[0] == '\\') + { + if(pStr[1] == '\\') + pStr++; // skip due to escape + else if(pStr[1] == '"') + pStr++; // skip due to escape + } + else if(pStr[0] == 0) + Error = 1; + + pStr++; + } + + // write null termination + *pStr = 0; + pStr++; + + for(Victim = 0; Victim < MAX_CLIENTS; Victim++) + if (str_comp(pName, Server()->ClientName(Victim)) == 0) + break; + + } + else + { + pName = pStr; + while(1) + { + if(pStr[0] == 0) + { + Error = 1; + break; + } + if(pStr[0] == ' ') + { + pStr[0] = 0; + for(Victim = 0; Victim < MAX_CLIENTS; Victim++) + if (str_comp(pName, Server()->ClientName(Victim)) == 0) + break; + + pStr[0] = ' '; + + if (Victim < MAX_CLIENTS) + break; + } + pStr++; + } + } + + if(pStr[0] != ' ') + { + Error = 1; + } + + *pStr = 0; + pStr++; + + pMessage = pStr; + + char aBuf[256]; + + if (Error) + { + str_format(aBuf, sizeof(aBuf), "Invalid whisper"); + SendChatTarget(ClientID, aBuf); + return; + } + + if (Victim >= MAX_CLIENTS || !CheckClientID2(Victim)) + { + str_format(aBuf, sizeof(aBuf), "No player with name \"%s\" found", pName); + SendChatTarget(ClientID, aBuf); + return; + } + + WhisperID(ClientID, Victim, pMessage); +} + +void CGameContext::WhisperID(int ClientID, int VictimID, char *pMessage) +{ + if (!CheckClientID2(ClientID)) + return; + + if (!CheckClientID2(VictimID)) + return; + + if (m_apPlayers[ClientID]) + m_apPlayers[ClientID]->m_LastWhisperTo = VictimID; + + char aBuf[256]; + + if (m_apPlayers[ClientID] && m_apPlayers[ClientID]->m_ClientVersion >= VERSION_DDNET_WHISPER) + { + CNetMsg_Sv_Chat Msg; + Msg.m_Team = CHAT_WHISPER_SEND; + Msg.m_ClientID = VictimID; + Msg.m_pMessage = pMessage; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID); + } else + { + str_format(aBuf, sizeof(aBuf), "[→ %s] %s", Server()->ClientName(VictimID), pMessage); + SendChatTarget(ClientID, aBuf); + } + + if (m_apPlayers[VictimID] && m_apPlayers[VictimID]->m_ClientVersion >= VERSION_DDNET_WHISPER) + { + CNetMsg_Sv_Chat Msg2; + Msg2.m_Team = CHAT_WHISPER_RECV; + Msg2.m_ClientID = ClientID; + Msg2.m_pMessage = pMessage; + Server()->SendPackMsg(&Msg2, MSGFLAG_VITAL, VictimID); + } else + { + str_format(aBuf, sizeof(aBuf), "[← %s] %s", Server()->ClientName(ClientID), pMessage); + SendChatTarget(VictimID, aBuf); + } +} + +void CGameContext::Converse(int ClientID, char *pStr) +{ + CPlayer *pPlayer = m_apPlayers[ClientID]; + if (!pPlayer) + return; + + if(ProcessSpamProtection(ClientID)) + return; + + if (pPlayer->m_LastWhisperTo < 0) + SendChatTarget(ClientID, "You do not have an ongoing conversation. Whisper to someone to start one"); + else + { + WhisperID(ClientID, pPlayer->m_LastWhisperTo, pStr); + } +} + +void CGameContext::List(int ClientID, const char* filter) +{ + int total = 0; + char buf[256]; + int bufcnt = 0; + if (filter[0]) + str_format(buf, sizeof(buf), "Listing players with \"%s\" in name:", filter); + else + str_format(buf, sizeof(buf), "Listing all players:", filter); + SendChatTarget(ClientID, buf); + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_apPlayers[i]) + { + total++; + const char* name = Server()->ClientName(i); + if (str_find_nocase(name, filter) == NULL) + continue; + if (bufcnt + str_length(name) + 4 > 256) + { + SendChatTarget(ClientID, buf); + bufcnt = 0; + } + if (bufcnt != 0) + { + str_format(&buf[bufcnt], sizeof(buf) - bufcnt, ", %s", name); + bufcnt += 2 + str_length(name); + } + else + { + str_format(&buf[bufcnt], sizeof(buf) - bufcnt, "%s", name); + bufcnt += str_length(name); + } + } + } + if (bufcnt != 0) + SendChatTarget(ClientID, buf); + str_format(buf, sizeof(buf), "%d players online", total); + SendChatTarget(ClientID, buf); +} diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 5a596acf4e..09bb683da0 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -16,6 +16,14 @@ #include "player.h" #include "score.h" +#ifdef _MSC_VER +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif /* Tick Game Context (CGameContext::tick) @@ -45,12 +53,22 @@ class CGameContext : public IGameServer CCollision m_Collision; CNetObjHandler m_NetObjHandler; CTuningParams m_Tuning; + CTuningParams m_TuningList[256]; static void ConTuneParam(IConsole::IResult *pResult, void *pUserData); static void ConTuneReset(IConsole::IResult *pResult, void *pUserData); static void ConTuneDump(IConsole::IResult *pResult, void *pUserData); + static void ConTuneZone(IConsole::IResult *pResult, void *pUserData); + static void ConTuneDumpZone(IConsole::IResult *pResult, void *pUserData); + static void ConTuneResetZone(IConsole::IResult *pResult, void *pUserData); + static void ConTuneSetZoneMsgEnter(IConsole::IResult *pResult, void *pUserData); + static void ConTuneSetZoneMsgLeave(IConsole::IResult *pResult, void *pUserData); static void ConPause(IConsole::IResult *pResult, void *pUserData); static void ConChangeMap(IConsole::IResult *pResult, void *pUserData); + static void ConRandomMap(IConsole::IResult *pResult, void *pUserData); + static void ConRandomUnfinishedMap(IConsole::IResult *pResult, void *pUserData); + static void ConSaveTeam(IConsole::IResult *pResult, void *pUserData); + static void ConLoadTeam(IConsole::IResult *pResult, void *pUserData); static void ConRestart(IConsole::IResult *pResult, void *pUserData); static void ConBroadcast(IConsole::IResult *pResult, void *pUserData); static void ConSay(IConsole::IResult *pResult, void *pUserData); @@ -75,6 +93,7 @@ class CGameContext : public IGameServer class IConsole *Console() { return m_pConsole; } CCollision *Collision() { return &m_Collision; } CTuningParams *Tuning() { return &m_Tuning; } + CTuningParams *TuningList() { return &m_TuningList[0]; } CGameContext(); ~CGameContext(); @@ -108,6 +127,9 @@ class CGameContext : public IGameServer char m_aVoteReason[VOTE_REASON_LENGTH]; int m_NumVoteOptions; int m_VoteEnforce; + char m_ZoneEnterMsg[256][256]; // 0 is used for switching from or to area without tunings + char m_ZoneLeaveMsg[256][256]; + enum { VOTE_ENFORCE_UNKNOWN=0, @@ -119,12 +141,12 @@ class CGameContext : public IGameServer CVoteOptionServer *m_pVoteOptionLast; // helper functions - void CreateDamageInd(vec2 Pos, float AngleMod, int Amount, int Mask=-1); - void CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamage, int ActivatedTeam, int Mask); - void CreateHammerHit(vec2 Pos, int Mask=-1); - void CreatePlayerSpawn(vec2 Pos, int Mask=-1); - void CreateDeath(vec2 Pos, int Who, int Mask=-1); - void CreateSound(vec2 Pos, int Sound, int Mask=-1); + void CreateDamageInd(vec2 Pos, float AngleMod, int Amount, int64_t Mask=-1); + void CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamage, int ActivatedTeam, int64_t Mask); + void CreateHammerHit(vec2 Pos, int64_t Mask=-1); + void CreatePlayerSpawn(vec2 Pos, int64_t Mask=-1); + void CreateDeath(vec2 Pos, int Who, int64_t Mask=-1); + void CreateSound(vec2 Pos, int Sound, int64_t Mask=-1); void CreateSoundGlobal(int Sound, int Target=-1); @@ -133,20 +155,25 @@ class CGameContext : public IGameServer CHAT_ALL=-2, CHAT_SPEC=-1, CHAT_RED=0, - CHAT_BLUE=1 + CHAT_BLUE=1, + CHAT_WHISPER_SEND=2, + CHAT_WHISPER_RECV=3 }; // network + void CallVote(int ClientID, const char *aDesc, const char *aCmd, const char *pReason, const char *aChatmsg); void SendChatTarget(int To, const char *pText); + void SendChatTeam(int Team, const char *pText); void SendChat(int ClientID, int Team, const char *pText, int SpamProtectionClientID = -1); void SendEmoticon(int ClientID, int Emoticon); void SendWeaponPickup(int ClientID, int Weapon); void SendBroadcast(const char *pText, int ClientID); + void List(int ClientID, const char* filter); // void CheckPureTuning(); - void SendTuningParams(int ClientID); + void SendTuningParams(int ClientID, int Zone = 0); // //void SwapTeams(); @@ -180,6 +207,7 @@ class CGameContext : public IGameServer int ProcessSpamProtection(int ClientID); int GetDDRaceTeam(int ClientID); + int64 m_LastMapVote; private: @@ -193,6 +221,8 @@ class CGameContext : public IGameServer static void ConKillPlayer(IConsole::IResult *pResult, void *pUserData); static void ConNinja(IConsole::IResult *pResult, void *pUserData); + static void ConUnSolo(IConsole::IResult *pResult, void *pUserData); + static void ConUnDeep(IConsole::IResult *pResult, void *pUserData); static void ConUnSuper(IConsole::IResult *pResult, void *pUserData); static void ConSuper(IConsole::IResult *pResult, void *pUserData); static void ConShotgun(IConsole::IResult *pResult, void *pUserData); @@ -215,6 +245,8 @@ class CGameContext : public IGameServer static void ConMove(IConsole::IResult *pResult, void *pUserData); static void ConMoveRaw(IConsole::IResult *pResult, void *pUserData); + static void ConToTeleporter(IConsole::IResult *pResult, void *pUserData); + static void ConToCheckTeleporter(IConsole::IResult *pResult, void *pUserData); static void ConTeleport(IConsole::IResult *pResult, void *pUserData); static void ConCredits(IConsole::IResult *pResult, void *pUserData); @@ -226,20 +258,34 @@ class CGameContext : public IGameServer static void ConTogglePause(IConsole::IResult *pResult, void *pUserData); static void ConToggleSpec(IConsole::IResult *pResult, void *pUserData); static void ConForcePause(IConsole::IResult *pResult, void *pUserData); + static void ConTeamTop5(IConsole::IResult *pResult, void *pUserData); static void ConTop5(IConsole::IResult *pResult, void *pUserData); #if defined(CONF_SQL) static void ConTimes(IConsole::IResult *pResult, void *pUserData); + static void ConPoints(IConsole::IResult *pResult, void *pUserData); + static void ConTopPoints(IConsole::IResult *pResult, void *pUserData); #endif static void ConUTF8(IConsole::IResult *pResult, void *pUserData); + static void ConDND(IConsole::IResult *pResult, void *pUserData); + static void ConMapPoints(IConsole::IResult *pResult, void *pUserData); + static void ConSave(IConsole::IResult *pResult, void *pUserData); + static void ConLoad(IConsole::IResult *pResult, void *pUserData); + static void ConMap(IConsole::IResult *pResult, void *pUserData); + static void ConTeamRank(IConsole::IResult *pResult, void *pUserData); static void ConRank(IConsole::IResult *pResult, void *pUserData); static void ConBroadTime(IConsole::IResult *pResult, void *pUserData); static void ConJoinTeam(IConsole::IResult *pResult, void *pUserData); + static void ConLockTeam(IConsole::IResult *pResult, void *pUserData); static void ConMe(IConsole::IResult *pResult, void *pUserData); + static void ConWhisper(IConsole::IResult *pResult, void *pUserData); + static void ConConverse(IConsole::IResult *pResult, void *pUserData); static void ConSetEyeEmote(IConsole::IResult *pResult, void *pUserData); static void ConToggleBroadcast(IConsole::IResult *pResult, void *pUserData); static void ConEyeEmote(IConsole::IResult *pResult, void *pUserData); static void ConShowOthers(IConsole::IResult *pResult, void *pUserData); + static void ConShowAll(IConsole::IResult *pResult, void *pUserData); + static void ConNinjaJetpack(IConsole::IResult *pResult, void *pUserData); static void ConSayTime(IConsole::IResult *pResult, void *pUserData); static void ConSayTimeAll(IConsole::IResult *pResult, void *pUserData); static void ConTime(IConsole::IResult *pResult, void *pUserData); @@ -254,6 +300,10 @@ class CGameContext : public IGameServer static void ConUnmute(IConsole::IResult *pResult, void *pUserData); static void ConMutes(IConsole::IResult *pResult, void *pUserData); + static void ConList(IConsole::IResult *pResult, void *pUserData); + static void ConFreezeHammer(IConsole::IResult *pResult, void *pUserData); + static void ConUnFreezeHammer(IConsole::IResult *pResult, void *pUserData); + enum { MAX_MUTES=32, @@ -267,6 +317,9 @@ class CGameContext : public IGameServer CMute m_aMutes[MAX_MUTES]; int m_NumMutes; void Mute(IConsole::IResult *pResult, NETADDR *Addr, int Secs, const char *pDisplayName); + void Whisper(int ClientID, char *pStr); + void WhisperID(int ClientID, int VictimID, char *pMessage); + void Converse(int ClientID, char *pStr); public: CLayers *Layers() { return &m_Layers; } @@ -284,6 +337,7 @@ class CGameContext : public IGameServer virtual void OnSetAuthed(int ClientID,int Level); virtual bool PlayerCollision(); virtual bool PlayerHooking(); + virtual float PlayerJetpack(); void ResetTuning(); @@ -291,8 +345,8 @@ class CGameContext : public IGameServer int m_ChatPrintCBIndex; }; -inline int CmaskAll() { return -1; } -inline int CmaskOne(int ClientID) { return 1<= ENTITY_DRAGGER_WEAK && Index <= ENTITY_DRAGGER_STRONG) { - new CDraggerTeam(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK + 1, false, Layer, Number); + CDraggerTeam(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK + 1, false, Layer, Number); } else if(Index >= ENTITY_DRAGGER_WEAK_NW && Index <= ENTITY_DRAGGER_STRONG_NW) { - new CDraggerTeam(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK_NW + 1, true, Layer, Number); + CDraggerTeam(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK_NW + 1, true, Layer, Number); } else if(Index == ENTITY_PLASMAE) { @@ -475,8 +475,8 @@ void IGameController::ChangeMap(const char *pToMap) pNextMap = pMapRotation; // cut out the next map - char aBuf[512]; - for(int i = 0; i < 512; i++) + char aBuf[512] = {0}; + for(int i = 0; i < 511; i++) { aBuf[i] = pNextMap[i]; if(IsSeparator(pNextMap[i]) || pNextMap[i] == 0) diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h index 395328eb98..27d3de5321 100644 --- a/src/game/server/gamecontroller.h +++ b/src/game/server/gamecontroller.h @@ -6,6 +6,14 @@ #include class CDoor; +#ifdef _MSC_VER +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif /* Class: Game Controller @@ -14,6 +22,8 @@ class CDoor; */ class IGameController { + friend class CSaveTeam; // need acces to GameServer() and Server() + vec2 m_aaSpawnPoints[3][64]; int m_aNumSpawnPoints[3]; diff --git a/src/game/server/gamemodes/DDRace.cpp b/src/game/server/gamemodes/DDRace.cpp index b8c77c2e4e..5db8356c1d 100644 --- a/src/game/server/gamemodes/DDRace.cpp +++ b/src/game/server/gamemodes/DDRace.cpp @@ -1,6 +1,7 @@ /* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */ /* Based on Race mod stuff and tweaked by GreYFoX@GTi and others to fit our DDRace needs. */ #include +#include #include #include #include @@ -11,7 +12,7 @@ CGameControllerDDRace::CGameControllerDDRace(class CGameContext *pGameServer) : IGameController(pGameServer), m_Teams(pGameServer) { - m_pGameType = GAME_NAME; + m_pGameType = g_Config.m_SvTestingCommands ? TEST_NAME : GAME_NAME; InitTeleporter(); } diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp index b57d2b5c0b..3e691fd485 100644 --- a/src/game/server/gamemodes/ctf.cpp +++ b/src/game/server/gamemodes/ctf.cpp @@ -99,7 +99,7 @@ bool CGameControllerCTF::CanBeMovedOnBalance(int ClientID) for(int fi = 0; fi < 2; fi++) { CFlag *F = m_apFlags[fi]; - if(F->m_pCarryingCharacter == Character) + if(F && F->m_pCarryingCharacter == Character) return false; } } diff --git a/src/game/server/gamemodes/gamemode.h b/src/game/server/gamemodes/gamemode.h index cdfe44cd41..536577fa81 100644 --- a/src/game/server/gamemodes/gamemode.h +++ b/src/game/server/gamemodes/gamemode.h @@ -3,5 +3,6 @@ #ifndef GAME_MODE_H #define GAME_MODE_H -#define GAME_NAME "TestDDRace" +#define GAME_NAME "DDraceNetwork" +#define TEST_NAME "TestDDraceNetwork" #endif diff --git a/src/game/server/gamemodes/gamemode.h.official b/src/game/server/gamemodes/gamemode.h.official deleted file mode 100644 index 31bee87dcf..0000000000 --- a/src/game/server/gamemodes/gamemode.h.official +++ /dev/null @@ -1,7 +0,0 @@ -/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */ -/* This is used for Official Builds of DDRace, if you change the source please don't use the name DDRace */ -#ifndef GAME_MODE_H -#define GAME_MODE_H - -#define GAME_NAME "DDRace" -#endif diff --git a/src/game/server/gamemodes/tdm.cpp b/src/game/server/gamemodes/tdm.cpp index d0bb2d6a05..8ab715b042 100644 --- a/src/game/server/gamemodes/tdm.cpp +++ b/src/game/server/gamemodes/tdm.cpp @@ -18,7 +18,7 @@ int CGameControllerTDM::OnCharacterDeath(class CCharacter *pVictim, class CPlaye IGameController::OnCharacterDeath(pVictim, pKiller, Weapon); - if(Weapon != WEAPON_GAME) + if(pKiller && Weapon != WEAPON_GAME) { // do team scoring if(pKiller == pVictim->GetPlayer() || pKiller->GetTeam() == pVictim->GetPlayer()->GetTeam()) diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index 260c4d00b5..ae2222fa76 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -4,6 +4,9 @@ #include "gameworld.h" #include "entity.h" #include "gamecontext.h" +#include +#include +#include ////////////////////////////////////////////////// // game world @@ -147,6 +150,95 @@ void CGameWorld::RemoveEntities() } } +bool distCompare(std::pair a, std::pair b) +{ + return (a.first < b.first); +} + +void CGameWorld::UpdatePlayerMaps() +{ + if (Server()->Tick() % g_Config.m_SvMapUpdateRate != 0) return; + + std::pair dist[MAX_CLIENTS]; + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (!Server()->ClientIngame(i)) continue; + int* map = Server()->GetIdMap(i); + + // compute distances + for (int j = 0; j < MAX_CLIENTS; j++) + { + dist[j].second = j; + if (!Server()->ClientIngame(j) || !GameServer()->m_apPlayers[j]) + { + dist[j].first = 1e10; + continue; + } + CCharacter* ch = GameServer()->m_apPlayers[j]->GetCharacter(); + if (!ch) + { + dist[j].first = 1e9; + continue; + } + // copypasted chunk from character.cpp Snap() follows + CCharacter* SnapChar = GameServer()->GetPlayerChar(i); + if(SnapChar && !SnapChar->m_Super && + !GameServer()->m_apPlayers[i]->m_Paused && GameServer()->m_apPlayers[i]->GetTeam() != -1 && + !ch->CanCollide(i) && + (!GameServer()->m_apPlayers[i] || + GameServer()->m_apPlayers[i]->m_ClientVersion == VERSION_VANILLA || + (GameServer()->m_apPlayers[i]->m_ClientVersion >= VERSION_DDRACE && + !GameServer()->m_apPlayers[i]->m_ShowOthers + ) + ) + ) + dist[j].first = 1e8; + else + dist[j].first = 0; + + dist[j].first += distance(GameServer()->m_apPlayers[i]->m_ViewPos, GameServer()->m_apPlayers[j]->GetCharacter()->m_Pos); + } + + // always send the player himself + dist[i].first = 0; + + // compute reverse map + int rMap[MAX_CLIENTS]; + for (int j = 0; j < MAX_CLIENTS; j++) + { + rMap[j] = -1; + } + for (int j = 0; j < VANILLA_MAX_CLIENTS; j++) + { + if (map[j] == -1) continue; + if (dist[map[j]].first > 5e9) map[j] = -1; + else rMap[map[j]] = j; + } + + std::nth_element(&dist[0], &dist[VANILLA_MAX_CLIENTS - 1], &dist[MAX_CLIENTS], distCompare); + + int mapc = 0; + int demand = 0; + for (int j = 0; j < VANILLA_MAX_CLIENTS - 1; j++) + { + int k = dist[j].second; + if (rMap[k] != -1 || dist[j].first > 5e9) continue; + while (mapc < VANILLA_MAX_CLIENTS && map[mapc] != -1) mapc++; + if (mapc < VANILLA_MAX_CLIENTS - 1) + map[mapc] = k; + else + demand++; + } + for (int j = MAX_CLIENTS - 1; j > VANILLA_MAX_CLIENTS - 2; j--) + { + int k = dist[j].second; + if (rMap[k] != -1 && demand-- > 0) + map[rMap[k]] = -1; + } + map[VANILLA_MAX_CLIENTS - 1] = -1; // player with empty name to say chat msgs + } +} + void CGameWorld::Tick() { if(m_ResetRequested) @@ -186,8 +278,9 @@ void CGameWorld::Tick() } RemoveEntities(); -} + UpdatePlayerMaps(); +} // TODO: should be more general //CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, vec2& NewPos, CEntity *pNotThis) diff --git a/src/game/server/gameworld.h b/src/game/server/gameworld.h index 492362610e..a433e76404 100644 --- a/src/game/server/gameworld.h +++ b/src/game/server/gameworld.h @@ -38,6 +38,8 @@ class CGameWorld class CGameContext *m_pGameServer; class IServer *m_pServer; + void UpdatePlayerMaps(); + public: class CGameContext *GameServer() { return m_pGameServer; } class IServer *Server() { return m_pServer; } diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 3e257b8e79..917ee9c15b 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -8,6 +8,7 @@ #include #include "gamecontext.h" #include +#include #include "gamemodes/DDRace.h" #include #include @@ -30,17 +31,29 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team) m_LastActionTick = Server()->Tick(); m_TeamChangeTick = Server()->Tick(); + int* idMap = Server()->GetIdMap(ClientID); + for (int i = 1;i < VANILLA_MAX_CLIENTS;i++) + { + idMap[i] = -1; + } + idMap[0] = ClientID; + // DDRace + m_LastCommandPos = 0; m_LastPlaytime = time_get(); - m_LastTarget_x = 0; - m_LastTarget_y = 0; m_Sent1stAfkWarning = 0; m_Sent2ndAfkWarning = 0; m_ChatScore = 0; m_EyeEmote = true; m_TimerType = g_Config.m_SvDefaultTimerType; m_DefEmote = EMOTE_NORMAL; + m_Afk = false; + m_LastWhisperTo = -1; + m_LastSetSpectatorMode = 0; + + m_TuneZone = 0; + m_TuneZoneOld = m_TuneZone; //New Year if (g_Config.m_SvEvents) @@ -48,7 +61,7 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team) time_t rawtime; struct tm* timeinfo; char d[16], m[16], y[16]; - int dd, mm, yy; + int dd, mm; time ( &rawtime ); timeinfo = localtime ( &rawtime ); strftime (d,sizeof(y),"%d",timeinfo); @@ -56,17 +69,19 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team) strftime (y,sizeof(y),"%Y",timeinfo); dd = atoi(d); mm = atoi(m); - yy = atoi(y); m_DefEmote = ((mm == 12 && dd == 31) || (mm == 1 && dd == 1)) ? EMOTE_HAPPY : EMOTE_NORMAL; } m_DefEmoteReset = -1; GameServer()->Score()->PlayerData(ClientID)->Reset(); - m_IsUsingDDRaceClient = false; - m_ShowOthers = false; + m_ClientVersion = VERSION_VANILLA; + m_ShowOthers = g_Config.m_SvShowOthersDefault; + m_ShowAll = g_Config.m_SvShowAllDefault; + m_NinjaJetpack = false; m_Paused = PAUSED_NONE; + m_DND = false; m_NextPauseTick = 0; @@ -166,7 +181,16 @@ void CPlayer::Tick() ++m_ScoreStartTick; ++m_LastActionTick; ++m_TeamChangeTick; - } + } + + m_TuneZoneOld = m_TuneZone; // determine needed tunings with viewpos + int CurrentIndex = GameServer()->Collision()->GetMapIndex(m_ViewPos); + m_TuneZone = GameServer()->Collision()->IsTune(CurrentIndex); + + if (m_TuneZone != m_TuneZoneOld) // dont send tunigs all the time + { + GameServer()->SendTuningParams(m_ClientID, m_TuneZone); + } } void CPlayer::PostTick() @@ -194,25 +218,38 @@ void CPlayer::Snap(int SnappingClient) if(!Server()->ClientIngame(m_ClientID)) return; - CNetObj_ClientInfo *pClientInfo = static_cast(Server()->SnapNewItem(NETOBJTYPE_CLIENTINFO, m_ClientID, sizeof(CNetObj_ClientInfo))); + int id = m_ClientID; + if (!Server()->Translate(id, SnappingClient)) return; + + CNetObj_ClientInfo *pClientInfo = static_cast(Server()->SnapNewItem(NETOBJTYPE_CLIENTINFO, id, sizeof(CNetObj_ClientInfo))); + if(!pClientInfo) return; StrToInts(&pClientInfo->m_Name0, 4, Server()->ClientName(m_ClientID)); StrToInts(&pClientInfo->m_Clan0, 3, Server()->ClientClan(m_ClientID)); pClientInfo->m_Country = Server()->ClientCountry(m_ClientID); - StrToInts(&pClientInfo->m_Skin0, 6, m_TeeInfos.m_SkinName); - pClientInfo->m_UseCustomColor = m_TeeInfos.m_UseCustomColor; - pClientInfo->m_ColorBody = m_TeeInfos.m_ColorBody; - pClientInfo->m_ColorFeet = m_TeeInfos.m_ColorFeet; + if (m_StolenSkin && SnappingClient != m_ClientID && g_Config.m_SvSkinStealAction == 1) + { + StrToInts(&pClientInfo->m_Skin0, 6, "pinky"); + pClientInfo->m_UseCustomColor = 0; + pClientInfo->m_ColorBody = m_TeeInfos.m_ColorBody; + pClientInfo->m_ColorFeet = m_TeeInfos.m_ColorFeet; + } else + { + StrToInts(&pClientInfo->m_Skin0, 6, m_TeeInfos.m_SkinName); + pClientInfo->m_UseCustomColor = m_TeeInfos.m_UseCustomColor; + pClientInfo->m_ColorBody = m_TeeInfos.m_ColorBody; + pClientInfo->m_ColorFeet = m_TeeInfos.m_ColorFeet; + } - CNetObj_PlayerInfo *pPlayerInfo = static_cast(Server()->SnapNewItem(NETOBJTYPE_PLAYERINFO, m_ClientID, sizeof(CNetObj_PlayerInfo))); + CNetObj_PlayerInfo *pPlayerInfo = static_cast(Server()->SnapNewItem(NETOBJTYPE_PLAYERINFO, id, sizeof(CNetObj_PlayerInfo))); if(!pPlayerInfo) return; pPlayerInfo->m_Latency = SnappingClient == -1 ? m_Latency.m_Min : GameServer()->m_apPlayers[SnappingClient]->m_aActLatency[m_ClientID]; pPlayerInfo->m_Local = 0; - pPlayerInfo->m_ClientID = m_ClientID; + pPlayerInfo->m_ClientID = id; pPlayerInfo->m_Score = abs(m_Score) * -1; pPlayerInfo->m_Team = (m_Paused != PAUSED_SPEC || m_ClientID != SnappingClient) && m_Paused < PAUSED_PAUSED ? m_Team : TEAM_SPECTATORS; @@ -237,6 +274,28 @@ void CPlayer::Snap(int SnappingClient) pPlayerInfo->m_Score = abs(m_Score) * -1; } +void CPlayer::FakeSnap(int SnappingClient) +{ + // This is problematic when it's sent before we know whether it's a non-64-player-client + // Then we can't spectate players at the start + IServer::CClientInfo info; + Server()->GetClientInfo(SnappingClient, &info); + CGameContext *GameContext = (CGameContext *) GameServer(); + if (GameContext->m_apPlayers[SnappingClient] && GameContext->m_apPlayers[SnappingClient]->m_ClientVersion >= VERSION_DDNET_OLD) + return; + + int id = VANILLA_MAX_CLIENTS - 1; + + CNetObj_ClientInfo *pClientInfo = static_cast(Server()->SnapNewItem(NETOBJTYPE_CLIENTINFO, id, sizeof(CNetObj_ClientInfo))); + + if(!pClientInfo) + return; + + StrToInts(&pClientInfo->m_Name0, 4, " "); + StrToInts(&pClientInfo->m_Clan0, 3, Server()->ClientClan(m_ClientID)); + StrToInts(&pClientInfo->m_Skin0, 6, m_TeeInfos.m_SkinName); +} + void CPlayer::OnDisconnect(const char *pReason) { KillCharacter(); @@ -255,7 +314,7 @@ void CPlayer::OnDisconnect(const char *pReason) } CGameControllerDDRace* Controller = (CGameControllerDDRace*)GameServer()->m_pController; - Controller->m_Teams.m_Core.Team(m_ClientID, 0); + Controller->m_Teams.SetForceCharacterTeam(m_ClientID, 0); } void CPlayer::OnPredictedInput(CNetObj_PlayerInput *NewInput) @@ -264,6 +323,8 @@ void CPlayer::OnPredictedInput(CNetObj_PlayerInput *NewInput) if((m_PlayerFlags&PLAYERFLAG_CHATTING) && (NewInput->m_PlayerFlags&PLAYERFLAG_CHATTING)) return; + AfkVoteTimer(NewInput); + if(m_pCharacter && !m_Paused) m_pCharacter->OnPredictedInput(NewInput); } @@ -272,6 +333,8 @@ void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) { if (AfkTimer(NewInput->m_TargetX, NewInput->m_TargetY)) return; // we must return if kicked, as player struct is already deleted + AfkVoteTimer(NewInput); + if(NewInput->m_PlayerFlags&PLAYERFLAG_CHATTING) { // skip the input if chat is active @@ -324,7 +387,11 @@ void CPlayer::KillCharacter(int Weapon) { if(m_pCharacter) { + if (m_RespawnTick > Server()->Tick()) + return; + m_pCharacter->Die(m_ClientID, Weapon); + delete m_pCharacter; m_pCharacter = 0; } @@ -336,6 +403,15 @@ void CPlayer::Respawn() m_Spawning = true; } +CCharacter* CPlayer::ForceSpawn(vec2 Pos) +{ + m_Spawning = false; + m_pCharacter = new(m_ClientID) CCharacter(&GameServer()->m_World); + m_pCharacter->Spawn(this, Pos); + m_Team = 0; + return m_pCharacter; +} + void CPlayer::SetTeam(int Team, bool DoChatMsg) { // clamp the team @@ -350,11 +426,18 @@ void CPlayer::SetTeam(int Team, bool DoChatMsg) GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); } + if(Team == TEAM_SPECTATORS) + { + CGameControllerDDRace* Controller = (CGameControllerDDRace*)GameServer()->m_pController; + Controller->m_Teams.SetForceCharacterTeam(m_ClientID, 0); + } + KillCharacter(); m_Team = Team; m_LastSetTeam = Server()->Tick(); m_LastActionTick = Server()->Tick(); + m_SpectatorID = SPEC_FREEVIEW; // we got to wait 0.5 secs before respawning m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2; str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' m_Team=%d", m_ClientID, Server()->ClientName(m_ClientID), m_Team); @@ -380,10 +463,25 @@ void CPlayer::TryRespawn() if(!GameServer()->m_pController->CanSpawn(m_Team, &SpawnPos)) return; + CGameControllerDDRace* Controller = (CGameControllerDDRace*)GameServer()->m_pController; + m_Spawning = false; m_pCharacter = new(m_ClientID) CCharacter(&GameServer()->m_World); m_pCharacter->Spawn(this, SpawnPos); GameServer()->CreatePlayerSpawn(SpawnPos, m_pCharacter->Teams()->TeamMask(m_pCharacter->Team(), -1, m_ClientID)); + + if(g_Config.m_SvTeam == 3) + { + int NewTeam = 0; + for(; NewTeam < TEAM_SUPER; NewTeam++) + if(Controller->m_Teams.Count(NewTeam) == 0) + break; + + if(NewTeam == TEAM_SUPER) + NewTeam = 0; + + Controller->m_Teams.SetForceCharacterTeam(GetCID(), NewTeam); + } } bool CPlayer::AfkTimer(int NewTargetX, int NewTargetY) @@ -449,16 +547,41 @@ bool CPlayer::AfkTimer(int NewTargetX, int NewTargetY) return false; } +void CPlayer::AfkVoteTimer(CNetObj_PlayerInput *NewTarget) +{ + if(g_Config.m_SvMaxAfkVoteTime == 0) + return; + + if(mem_comp(NewTarget, &m_LastTarget, sizeof(CNetObj_PlayerInput)) != 0) + { + m_LastPlaytime = time_get(); + mem_copy(&m_LastTarget, NewTarget, sizeof(CNetObj_PlayerInput)); + } + else if(m_LastPlaytime < time_get()-time_freq()*g_Config.m_SvMaxAfkVoteTime) + { + m_Afk = true; + return; + } + + m_Afk = false; +} + void CPlayer::ProcessPause() { + if(!m_pCharacter) + return; + char aBuf[128]; if(m_Paused >= PAUSED_PAUSED) { if(!m_pCharacter->IsPaused()) { m_pCharacter->Pause(true); - str_format(aBuf, sizeof(aBuf), (m_Paused == PAUSED_PAUSED) ? "'%s' paused" : "'%s' was force-paused for %ds", Server()->ClientName(m_ClientID), m_ForcePauseTime/Server()->TickSpeed()); - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + if(g_Config.m_SvPauseMessages) + { + str_format(aBuf, sizeof(aBuf), (m_Paused == PAUSED_PAUSED) ? "'%s' paused" : "'%s' was force-paused for %ds", Server()->ClientName(m_ClientID), m_ForcePauseTime/Server()->TickSpeed()); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + } GameServer()->CreateDeath(m_pCharacter->m_Pos, m_ClientID, m_pCharacter->Teams()->TeamMask(m_pCharacter->Team(), -1, m_ClientID)); GameServer()->CreateSound(m_pCharacter->m_Pos, SOUND_PLAYER_DIE, m_pCharacter->Teams()->TeamMask(m_pCharacter->Team(), -1, m_ClientID)); m_NextPauseTick = Server()->Tick() + g_Config.m_SvPauseFrequency * Server()->TickSpeed(); @@ -469,8 +592,11 @@ void CPlayer::ProcessPause() if(m_pCharacter->IsPaused()) { m_pCharacter->Pause(false); - str_format(aBuf, sizeof(aBuf), "'%s' resumed", Server()->ClientName(m_ClientID)); - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + if(g_Config.m_SvPauseMessages) + { + str_format(aBuf, sizeof(aBuf), "'%s' resumed", Server()->ClientName(m_ClientID)); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + } GameServer()->CreatePlayerSpawn(m_pCharacter->m_Pos, m_pCharacter->Teams()->TeamMask(m_pCharacter->Team(), -1, m_ClientID)); m_NextPauseTick = Server()->Tick() + g_Config.m_SvPauseFrequency * Server()->TickSpeed(); } @@ -483,3 +609,25 @@ bool CPlayer::IsPlaying() return true; return false; } + +void CPlayer::FindDuplicateSkins() +{ + if (m_TeeInfos.m_UseCustomColor == 0 && !m_StolenSkin) return; + m_StolenSkin = 0; + for (int i = 0; i < MAX_CLIENTS; ++i) + { + if (i == m_ClientID) continue; + if(GameServer()->m_apPlayers[i]) + { + if (GameServer()->m_apPlayers[i]->m_StolenSkin) continue; + if ((GameServer()->m_apPlayers[i]->m_TeeInfos.m_UseCustomColor == m_TeeInfos.m_UseCustomColor) && + (GameServer()->m_apPlayers[i]->m_TeeInfos.m_ColorFeet == m_TeeInfos.m_ColorFeet) && + (GameServer()->m_apPlayers[i]->m_TeeInfos.m_ColorBody == m_TeeInfos.m_ColorBody) && + !str_comp(GameServer()->m_apPlayers[i]->m_TeeInfos.m_SkinName, m_TeeInfos.m_SkinName)) + { + m_StolenSkin = 1; + return; + } + } + } +} diff --git a/src/game/server/player.h b/src/game/server/player.h index 69e6ccf335..9fd7f546ca 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -11,6 +11,8 @@ class CPlayer { MACRO_ALLOC_POOL_ID() + + friend class CSaveTee; public: CPlayer(CGameContext *pGameServer, int ClientID, int Team); @@ -20,6 +22,7 @@ class CPlayer void TryRespawn(); void Respawn(); + CCharacter* ForceSpawn(vec2 Pos); // required for loading savegames void SetTeam(int Team, bool DoChatMsg=true); int GetTeam() const { return m_Team; }; int GetCID() const { return m_ClientID; }; @@ -27,6 +30,7 @@ class CPlayer void Tick(); void PostTick(); void Snap(int SnappingClient); + void FakeSnap(int SnappingClient); void OnDirectInput(CNetObj_PlayerInput *NewInput); void OnPredictedInput(CNetObj_PlayerInput *NewInput); @@ -35,9 +39,13 @@ class CPlayer void KillCharacter(int Weapon = WEAPON_GAME); CCharacter *GetCharacter(); + void FindDuplicateSkins(); + //--------------------------------------------------------- // this is used for snapping so we know how we can clip the view for the player vec2 m_ViewPos; + int m_TuneZone; + int m_TuneZoneOld; // states if the client is chatting, accessing a menu etc. int m_PlayerFlags; @@ -62,6 +70,9 @@ class CPlayer int m_LastChangeInfo; int m_LastEmote; int m_LastKill; + int m_LastCommands[4]; + int m_LastCommandPos; + int m_LastWhisperTo; // TODO: clean this up struct @@ -78,6 +89,7 @@ class CPlayer int m_ScoreStartTick; bool m_ForceBalanced; int m_LastActionTick; + bool m_StolenSkin; int m_TeamChangeTick; struct { @@ -121,6 +133,7 @@ class CPlayer }; int m_Paused; + bool m_DND; int64 m_NextPauseTick; void ProcessPause(); @@ -129,16 +142,21 @@ class CPlayer int64 m_Last_KickVote; int64 m_Last_Team; int m_Authed; - bool m_IsUsingDDRaceClient; + int m_ClientVersion; bool m_ShowOthers; + bool m_ShowAll; + bool m_NinjaJetpack; + bool m_Afk; int m_ChatScore; bool AfkTimer(int new_target_x, int new_target_y); //returns true if kicked + void AfkVoteTimer(CNetObj_PlayerInput *NewTarget); int64 m_LastPlaytime; int64 m_LastEyeEmote; int m_LastTarget_x; int m_LastTarget_y; + CNetObj_PlayerInput m_LastTarget; int m_Sent1stAfkWarning; // afk timer's 1st warning after 50% of sv_max_afk_time int m_Sent2ndAfkWarning; // afk timer's 2nd warning after 90% of sv_max_afk_time char m_pAfkMsg[160]; diff --git a/src/game/server/save.cpp b/src/game/server/save.cpp new file mode 100644 index 0000000000..2fc8a289ea --- /dev/null +++ b/src/game/server/save.cpp @@ -0,0 +1,497 @@ +#include +#include + +#include "save.h" +#include "teams.h" +#include +#include "./gamemodes/DDRace.h" + +CSaveTee::CSaveTee() +{ + ; +} + +CSaveTee::~CSaveTee() +{ + ; +} + +void CSaveTee::save(CCharacter* pchr) +{ + str_copy(m_name, pchr->m_pPlayer->Server()->ClientName(pchr->m_pPlayer->GetCID()), sizeof(m_name)); + + m_Alive = pchr->m_Alive; + m_Paused = pchr->m_pPlayer->m_Paused; + m_NeededFaketuning = pchr->m_NeededFaketuning; + + m_TeeFinished = pchr->Teams()->TeeFinished(pchr->m_pPlayer->GetCID()); + m_IsSolo = pchr->Teams()->m_Core.GetSolo(pchr->m_pPlayer->GetCID()); + + for(int i = 0; i< NUM_WEAPONS; i++) + { + m_aWeapons[i].m_AmmoRegenStart = pchr->m_aWeapons[i].m_AmmoRegenStart; + m_aWeapons[i].m_Ammo = pchr->m_aWeapons[i].m_Ammo; + m_aWeapons[i].m_Ammocost = pchr->m_aWeapons[i].m_Ammocost; + m_aWeapons[i].m_Got = pchr->m_aWeapons[i].m_Got; + } + + m_LastWeapon = pchr->m_LastWeapon; + m_QueuedWeapon = pchr->m_QueuedWeapon; + + m_SuperJump = pchr->m_SuperJump; + m_Jetpack = pchr->m_Jetpack; + m_NinjaJetpack = pchr->m_NinjaJetpack; + m_FreezeTime = pchr->m_FreezeTime; + m_FreezeTick = pchr->Server()->Tick() - pchr->m_FreezeTick; + + m_DeepFreeze = pchr->m_DeepFreeze; + m_EndlessHook = pchr->m_EndlessHook; + m_DDRaceState = pchr->m_DDRaceState; + + m_Hit = pchr->m_Hit; + m_Collision = pchr->m_Collision; + m_TuneZone = pchr->m_TuneZone; + m_TuneZoneOld = pchr->m_TuneZoneOld; + m_Hook = pchr->m_Hook; + + if(pchr->m_StartTime) + m_Time = pchr->Server()->Tick() - pchr->m_StartTime; + + m_Pos = pchr->m_Pos; + m_PrevPos = pchr->m_PrevPos; + m_TeleCheckpoint = pchr->m_TeleCheckpoint; + m_LastPenalty = pchr->m_LastPenalty; + + if(pchr->m_CpTick) + m_CpTime = pchr->Server()->Tick() - pchr->m_CpTick; + + m_CpActive = pchr->m_CpActive; + m_CpLastBroadcast = pchr->m_CpLastBroadcast; + + for(int i = 0; i<=25; i++) + m_CpCurrent[i] = pchr->m_CpCurrent[i]; + + // Core + m_CorePos = pchr->m_Core.m_Pos; + m_Vel = pchr->m_Core.m_Vel; + m_ActiveWeapon = pchr->m_Core.m_ActiveWeapon; + m_Jumped = pchr->m_Core.m_Jumped; + m_JumpedTotal = pchr->m_Core.m_JumpedTotal; + m_Jumps = pchr->m_Core.m_Jumps; + m_HookPos = pchr->m_Core.m_HookPos; + m_HookDir = pchr->m_Core.m_HookDir; + m_HookTeleBase = pchr->m_Core.m_HookTeleBase; + + m_HookTick = pchr->m_Core.m_HookTick; + + m_HookState = pchr->m_Core.m_HookState; +} + +void CSaveTee::load(CCharacter* pchr, int Team) +{ + pchr->m_pPlayer->m_Paused = m_Paused; + pchr->m_pPlayer->ProcessPause(); + + pchr->m_Alive = m_Alive; + pchr->m_NeededFaketuning = m_NeededFaketuning; + + pchr->Teams()->SetForceCharacterTeam(pchr->m_pPlayer->GetCID(), Team); + pchr->Teams()->m_Core.SetSolo(pchr->m_pPlayer->GetCID(), m_IsSolo); + pchr->Teams()->SetFinished(pchr->m_pPlayer->GetCID(), m_TeeFinished); + + for(int i = 0; i< NUM_WEAPONS; i++) + { + pchr->m_aWeapons[i].m_AmmoRegenStart = m_aWeapons[i].m_AmmoRegenStart; + pchr->m_aWeapons[i].m_Ammo = m_aWeapons[i].m_Ammo; + pchr->m_aWeapons[i].m_Ammocost = m_aWeapons[i].m_Ammocost; + pchr->m_aWeapons[i].m_Got = m_aWeapons[i].m_Got; + } + + pchr->m_LastWeapon = m_LastWeapon; + pchr->m_QueuedWeapon = m_QueuedWeapon; + + pchr->m_SuperJump = m_SuperJump; + pchr->m_Jetpack = m_Jetpack; + pchr->m_NinjaJetpack = m_NinjaJetpack; + pchr->m_FreezeTime = m_FreezeTime; + pchr->m_FreezeTick = pchr->Server()->Tick() - m_FreezeTick; + + pchr->m_DeepFreeze = m_DeepFreeze; + pchr->m_EndlessHook = m_EndlessHook; + pchr->m_DDRaceState = m_DDRaceState; + + pchr->m_Hit = m_Hit; + pchr->m_Collision = m_Collision; + pchr->m_TuneZone = m_TuneZone; + pchr->m_TuneZoneOld = m_TuneZoneOld; + pchr->m_Hook = m_Hook; + + if(m_Time) + pchr->m_StartTime = pchr->Server()->Tick() - m_Time; + + pchr->m_Pos = m_Pos; + pchr->m_PrevPos = m_PrevPos; + pchr->m_TeleCheckpoint = m_TeleCheckpoint; + pchr->m_LastPenalty = m_LastPenalty; + + if(m_CpTime) + pchr->m_CpTick = pchr->Server()->Tick() - m_CpTime; + + pchr->m_CpActive = m_CpActive; + pchr->m_CpLastBroadcast = m_CpLastBroadcast; + + for(int i = 0; i<=25; i++) + pchr->m_CpCurrent[i] =m_CpCurrent[i]; + + // Core + pchr->m_Core.m_Pos = m_CorePos; + pchr->m_Core.m_Vel = m_Vel; + pchr->m_Core.m_ActiveWeapon = m_ActiveWeapon; + pchr->m_Core.m_Jumped = m_Jumped; + pchr->m_Core.m_JumpedTotal = m_JumpedTotal; + pchr->m_Core.m_Jumps = m_Jumps; + pchr->m_Core.m_HookPos = m_HookPos; + pchr->m_Core.m_HookDir = m_HookDir; + pchr->m_Core.m_HookTeleBase = m_HookTeleBase; + + pchr->m_Core.m_HookTick = m_HookTick; + + if(m_HookState == HOOK_GRABBED) + { + pchr->m_Core.m_HookState = HOOK_FLYING; + pchr->m_Core.m_HookedPlayer = -1; + } + else + { + pchr->m_Core.m_HookState = m_HookState; + } + + pchr->GameServer()->SendTuningParams(pchr->m_pPlayer->GetCID(), m_TuneZone); +} + +char* CSaveTee::GetString() +{ + str_format(m_String, sizeof(m_String), "%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f", m_name, m_Alive, m_Paused, m_NeededFaketuning, m_TeeFinished, m_IsSolo, m_aWeapons[0].m_AmmoRegenStart, m_aWeapons[0].m_Ammo, m_aWeapons[0].m_Ammocost, m_aWeapons[0].m_Got, m_aWeapons[1].m_AmmoRegenStart, m_aWeapons[1].m_Ammo, m_aWeapons[1].m_Ammocost, m_aWeapons[1].m_Got, m_aWeapons[2].m_AmmoRegenStart, m_aWeapons[2].m_Ammo, m_aWeapons[2].m_Ammocost, m_aWeapons[2].m_Got, m_aWeapons[3].m_AmmoRegenStart, m_aWeapons[3].m_Ammo, m_aWeapons[3].m_Ammocost, m_aWeapons[3].m_Got, m_aWeapons[4].m_AmmoRegenStart, m_aWeapons[4].m_Ammo, m_aWeapons[4].m_Ammocost, m_aWeapons[4].m_Got, m_aWeapons[5].m_AmmoRegenStart, m_aWeapons[5].m_Ammo, m_aWeapons[5].m_Ammocost, m_aWeapons[5].m_Got, m_LastWeapon, m_QueuedWeapon, m_SuperJump, m_Jetpack, m_NinjaJetpack, m_FreezeTime, m_FreezeTick, m_DeepFreeze, m_EndlessHook, m_DDRaceState, m_Hit, m_Collision, m_TuneZone, m_TuneZoneOld, m_Hook, m_Time, (int)m_Pos.x, (int)m_Pos.y, (int)m_PrevPos.x, (int)m_PrevPos.y, m_TeleCheckpoint, m_LastPenalty, (int)m_CorePos.x, (int)m_CorePos.y, m_Vel.x, m_Vel.y, m_ActiveWeapon, m_Jumped, m_JumpedTotal, m_Jumps, (int)m_HookPos.x, (int)m_HookPos.y, m_HookDir.x, m_HookDir.y, (int)m_HookTeleBase.x, (int)m_HookTeleBase.y, m_HookTick, m_HookState, m_CpTime, m_CpActive, m_CpLastBroadcast, m_CpCurrent[0], m_CpCurrent[1], m_CpCurrent[2], m_CpCurrent[3], m_CpCurrent[4], m_CpCurrent[5], m_CpCurrent[6], m_CpCurrent[7], m_CpCurrent[8], m_CpCurrent[9], m_CpCurrent[10], m_CpCurrent[11], m_CpCurrent[12], m_CpCurrent[13], m_CpCurrent[14], m_CpCurrent[15], m_CpCurrent[16], m_CpCurrent[17], m_CpCurrent[18], m_CpCurrent[19], m_CpCurrent[20], m_CpCurrent[21], m_CpCurrent[22], m_CpCurrent[23], m_CpCurrent[24]); + return m_String; +} + +int CSaveTee::LoadString(char* String) +{ + int Num; + Num = sscanf(String, "%[^\t]\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%d\t%d\t%f\t%f\t%f\t%f\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f", m_name, &m_Alive, &m_Paused, &m_NeededFaketuning, &m_TeeFinished, &m_IsSolo, &m_aWeapons[0].m_AmmoRegenStart, &m_aWeapons[0].m_Ammo, &m_aWeapons[0].m_Ammocost, &m_aWeapons[0].m_Got, &m_aWeapons[1].m_AmmoRegenStart, &m_aWeapons[1].m_Ammo, &m_aWeapons[1].m_Ammocost, &m_aWeapons[1].m_Got, &m_aWeapons[2].m_AmmoRegenStart, &m_aWeapons[2].m_Ammo, &m_aWeapons[2].m_Ammocost, &m_aWeapons[2].m_Got, &m_aWeapons[3].m_AmmoRegenStart, &m_aWeapons[3].m_Ammo, &m_aWeapons[3].m_Ammocost, &m_aWeapons[3].m_Got, &m_aWeapons[4].m_AmmoRegenStart, &m_aWeapons[4].m_Ammo, &m_aWeapons[4].m_Ammocost, &m_aWeapons[4].m_Got, &m_aWeapons[5].m_AmmoRegenStart, &m_aWeapons[5].m_Ammo, &m_aWeapons[5].m_Ammocost, &m_aWeapons[5].m_Got, &m_LastWeapon, &m_QueuedWeapon, &m_SuperJump, &m_Jetpack, &m_NinjaJetpack, &m_FreezeTime, &m_FreezeTick, &m_DeepFreeze, &m_EndlessHook, &m_DDRaceState, &m_Hit, &m_Collision, &m_TuneZone, &m_TuneZoneOld, &m_Hook, &m_Time, &m_Pos.x, &m_Pos.y, &m_PrevPos.x, &m_PrevPos.y, &m_TeleCheckpoint, &m_LastPenalty, &m_CorePos.x, &m_CorePos.y, &m_Vel.x, &m_Vel.y, &m_ActiveWeapon, &m_Jumped, &m_JumpedTotal, &m_Jumps, &m_HookPos.x, &m_HookPos.y, &m_HookDir.x, &m_HookDir.y, &m_HookTeleBase.x, &m_HookTeleBase.y, &m_HookTick, &m_HookState, &m_CpTime, &m_CpActive, &m_CpLastBroadcast, &m_CpCurrent[0], &m_CpCurrent[1], &m_CpCurrent[2], &m_CpCurrent[3], &m_CpCurrent[4], &m_CpCurrent[5], &m_CpCurrent[6], &m_CpCurrent[7], &m_CpCurrent[8], &m_CpCurrent[9], &m_CpCurrent[10], &m_CpCurrent[11], &m_CpCurrent[12], &m_CpCurrent[13], &m_CpCurrent[14], &m_CpCurrent[15], &m_CpCurrent[16], &m_CpCurrent[17], &m_CpCurrent[18], &m_CpCurrent[19], &m_CpCurrent[20], &m_CpCurrent[21], &m_CpCurrent[22], &m_CpCurrent[23], &m_CpCurrent[24]); + if (Num == 96) // Don't forget to update this when you save / load more / less. + return 0; + else + { + dbg_msg("Load", "failed to load Tee-string"); + char aBuf[32]; + str_format(aBuf, sizeof(aBuf), "loaded %d vars", Num); + dbg_msg("Load", aBuf); + return Num+1; // never 0 here + } +} + +CSaveTeam::CSaveTeam(IGameController* Controller) +{ + m_pController = Controller; + m_Switchers = 0; + SavedTees = 0; +} + +CSaveTeam::~CSaveTeam() +{ + if(m_Switchers) + delete[] m_Switchers; + if(SavedTees) + delete[] SavedTees; +} + +int CSaveTeam::save(int Team) +{ + if(Team > 0 && Team < 64) + { + CGameTeams* Teams = &(((CGameControllerDDRace*)m_pController)->m_Teams); + + if(Teams->Count(Team) <= 0) + { + return 2; + } + + m_TeamState = Teams->GetTeamState(Team); + m_MembersCount = Teams->Count(Team); + m_NumSwitchers = m_pController->GameServer()->Collision()->m_NumSwitchers; + m_TeamLocked = Teams->TeamLocked(Team); + + SavedTees = new CSaveTee[m_MembersCount]; + int j = 0; + for (int i = 0; i<64; i++) + { + if(Teams->m_Core.Team(i) == Team) + { + if(m_pController->GameServer()->m_apPlayers[i]->GetCharacter()) + SavedTees[j].save(m_pController->GameServer()->m_apPlayers[i]->GetCharacter()); + else + return 3; + j++; + } + } + + if(m_pController->GameServer()->Collision()->m_NumSwitchers) + { + m_Switchers = new SSimpleSwitchers[m_pController->GameServer()->Collision()->m_NumSwitchers+1]; + + for(int i=1; i < m_pController->GameServer()->Collision()->m_NumSwitchers+1; i++) + { + m_Switchers[i].m_Status = m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Status[Team]; + if(m_pController->GameServer()->Collision()->m_pSwitchers[i].m_EndTick[Team]) + m_Switchers[i].m_EndTime = m_pController->Server()->Tick() - m_pController->GameServer()->Collision()->m_pSwitchers[i].m_EndTick[Team]; + else + m_Switchers[i].m_EndTime = 0; + m_Switchers[i].m_Type = m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Type[Team]; + } + } + return 0; + } + else + return 1; +} + +int CSaveTeam::load(int Team) +{ + if(Team > 0 && Team < 64) + { + CGameTeams* Teams = &(((CGameControllerDDRace*)m_pController)->m_Teams); + + Teams->ChangeTeamState(Team, m_TeamState); + Teams->SetTeamLock(Team, m_TeamLocked); + + CCharacter* pchr; + + for (int i = 0; iGameServer()->m_apPlayers[ID]->GetCharacter() && m_pController->GameServer()->m_apPlayers[ID]->GetCharacter()->m_DDRaceState) + { + return i+100; // +100 to let space for other return-values + } + } + + for (int i = 0; iGameServer()->Collision()->m_NumSwitchers) + for(int i=1; i < m_pController->GameServer()->Collision()->m_NumSwitchers+1; i++) + { + m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Status[Team] = m_Switchers[i].m_Status; + if(m_Switchers[i].m_EndTime) + m_pController->GameServer()->Collision()->m_pSwitchers[i].m_EndTick[Team] = m_pController->Server()->Tick() - m_Switchers[i].m_EndTime; + m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Type[Team] = m_Switchers[i].m_Type; + } + return 0; + } + return 1; +} + +int CSaveTeam::MatchPlayer(char name[16]) +{ + for (int i = 0; i<64; i++) + { + if(str_comp(m_pController->Server()->ClientName(i), name) == 0) + { + return i; + } + } + return -1; +} + +CCharacter* CSaveTeam::MatchCharacter(char name[16], int SaveID) +{ + int ID = MatchPlayer(name); + if(ID >= 0) + { + if(m_pController->GameServer()->m_apPlayers[ID]->GetCharacter()) + return m_pController->GameServer()->m_apPlayers[ID]->GetCharacter(); + else + return m_pController->GameServer()->m_apPlayers[ID]->ForceSpawn(SavedTees[SaveID].GetPos()); + } + + return 0; +} + +char* CSaveTeam::GetString() +{ + str_format(m_String, sizeof(m_String), "%d\t%d\t%d\t%d", m_TeamState, m_MembersCount, m_NumSwitchers, m_TeamLocked); + + for (int i = 0; i + +class CSaveTee +{ +public: + CSaveTee(); + ~CSaveTee(); + void save(CCharacter* pchr); + void load(CCharacter* pchr, int Team); + char* GetString(); + int LoadString(char* String); + vec2 GetPos() { return m_Pos; } + char* GetName() { return m_name; } + +private: + + char m_String [2048]; + char m_name [16]; + + int m_Alive; + int m_Paused; + int m_NeededFaketuning; + + // Teamstuff + int m_TeeFinished; + int m_IsSolo; + + struct WeaponStat + { + int m_AmmoRegenStart; + int m_Ammo; + int m_Ammocost; + int m_Got; + + } m_aWeapons[NUM_WEAPONS]; + + int m_LastWeapon; + int m_QueuedWeapon; + + int m_SuperJump; + int m_Jetpack; + int m_NinjaJetpack; + int m_FreezeTime; + int m_FreezeTick; + int m_DeepFreeze; + int m_EndlessHook; + int m_DDRaceState; + + int m_Hit; + int m_Collision; + int m_TuneZone; + int m_TuneZoneOld; + int m_Hook; + int m_Time; + vec2 m_Pos; + vec2 m_PrevPos; + int m_TeleCheckpoint; + int m_LastPenalty; + + int m_CpTime; + int m_CpActive; + int m_CpLastBroadcast; + float m_CpCurrent[25]; + + // Core + vec2 m_CorePos; + vec2 m_Vel; + int m_ActiveWeapon; + int m_Jumped; + int m_JumpedTotal; + int m_Jumps; + vec2 m_HookPos; + vec2 m_HookDir; + vec2 m_HookTeleBase; + int m_HookTick; + int m_HookState; +}; + +class CSaveTeam +{ +public: + CSaveTeam(IGameController* Controller); + ~CSaveTeam(); + char* GetString(); + int GetMembersCount() {return m_MembersCount;} + int LoadString(const char* String); + int save(int Team); + int load(int Team); + CSaveTee* SavedTees; + +private: + int MatchPlayer(char name[16]); + CCharacter* MatchCharacter(char name[16], int SaveID); + + IGameController* m_pController; + + char m_String[65536]; + + struct SSimpleSwitchers + { + int m_Status; + int m_EndTime; + int m_Type; + }; + SSimpleSwitchers* m_Switchers; + + int m_TeamState; + int m_MembersCount; + int m_NumSwitchers; + int m_TeamLocked; +}; + +#endif diff --git a/src/game/server/score.h b/src/game/server/score.h index 974729d450..41c63697f8 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -43,11 +43,26 @@ class IScore CPlayerData *PlayerData(int ID) { return &m_aPlayerData[ID]; } + virtual void MapPoints(int ClientID, const char* MapName) = 0; + virtual void MapVote(int ClientID, const char* MapName) = 0; virtual void LoadScore(int ClientID) = 0; virtual void SaveScore(int ClientID, float Time, float CpTime[NUM_CHECKPOINTS]) = 0; + + virtual void SaveTeamScore(int* ClientIDs, unsigned int Size, float Time) = 0; virtual void ShowTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut=1) = 0; virtual void ShowRank(int ClientID, const char* pName, bool Search=false) = 0; + + virtual void ShowTeamTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut=1) = 0; + virtual void ShowTeamRank(int ClientID, const char* pName, bool Search=false) = 0; + + virtual void ShowTopPoints(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut=1) = 0; + virtual void ShowPoints(int ClientID, const char* pName, bool Search=false) = 0; + + virtual void RandomUnfinishedMap(int ClientID) = 0; + + virtual void SaveTeam(int Team, const char* Code, int ClientID) = 0; + virtual void LoadTeam(const char* Code, int ClientID) = 0; }; #endif diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index cf4e81cebd..bf9eb3e6c6 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -54,6 +54,16 @@ std::string SaveFile() return oss.str(); } +void CFileScore::MapPoints(int ClientID, const char* MapName) +{ + // TODO: implement +} + +void CFileScore::MapVote(int ClientID, const char* MapName) +{ + // TODO: implement +} + void CFileScore::SaveScoreThread(void *pUser) { CFileScore *pSelf = (CFileScore *) pUser; @@ -209,11 +219,16 @@ void CFileScore::LoadScore(int ClientID) PlayerData(ClientID)->Set(pPlayer->m_Score, pPlayer->m_aCpTime); } +void CFileScore::SaveTeamScore(int* ClientIDs, unsigned int Size, float Time) +{ + dbg_msg("FileScore", "SaveTeamScore not implemented for FileScore"); +} + void CFileScore::SaveScore(int ClientID, float Time, float CpTime[NUM_CHECKPOINTS]) { CConsole* pCon = (CConsole*) GameServer()->Console(); - if (!pCon->m_Cheated) + if (!pCon->m_Cheated || g_Config.m_SvRankCheats) UpdatePlayer(ClientID, Time, CpTime); } @@ -277,3 +292,52 @@ void CFileScore::ShowRank(int ClientID, const char* pName, bool Search) GameServer()->SendChatTarget(ClientID, aBuf); } + +void CFileScore::ShowTeamTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut) +{ + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); + GameServer()->SendChatTarget(ClientID, aBuf); +} + +void CFileScore::ShowTeamRank(int ClientID, const char* pName, bool Search) +{ + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); + GameServer()->SendChatTarget(ClientID, aBuf); +} + +void CFileScore::ShowTopPoints(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut) +{ + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); + GameServer()->SendChatTarget(ClientID, aBuf); +} + +void CFileScore::ShowPoints(int ClientID, const char* pName, bool Search) +{ + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "Points not supported in file based servers"); + GameServer()->SendChatTarget(ClientID, aBuf); +} + +void CFileScore::RandomUnfinishedMap(int ClientID) +{ + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "Random unfinished map not supported in file based servers"); + GameServer()->SendChatTarget(ClientID, aBuf); +} + +void CFileScore::SaveTeam(int Team, const char* Code, int ClientID) +{ + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "Save-function not supported in file based servers"); + GameServer()->SendChatTarget(ClientID, aBuf); +} + +void CFileScore::LoadTeam(const char* Code, int ClientID) +{ + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "Save-function not supported in file based servers"); + GameServer()->SendChatTarget(ClientID, aBuf); +} diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index 98f4774298..ec9ef9dc86 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -63,12 +63,25 @@ class CFileScore: public IScore ~CFileScore(); virtual void LoadScore(int ClientID); + virtual void MapPoints(int ClientID, const char* MapName); + virtual void MapVote(int ClientID, const char* MapName); virtual void SaveScore(int ClientID, float Time, float CpTime[NUM_CHECKPOINTS]); + virtual void SaveTeamScore(int* ClientIDs, unsigned int Size, float Time); virtual void ShowTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut = 1); virtual void ShowRank(int ClientID, const char* pName, bool Search = false); + + virtual void ShowTeamTop5(IConsole::IResult *pResult, int ClientID, + void *pUserData, int Debut = 1); + virtual void ShowTeamRank(int ClientID, const char* pName, bool Search = false); + + virtual void ShowTopPoints(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut); + virtual void ShowPoints(int ClientID, const char* pName, bool Search); + virtual void RandomUnfinishedMap(int ClientID); + virtual void SaveTeam(int Team, const char* Code, int ClientID); + virtual void LoadTeam(const char* Code, int ClientID); }; #endif diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index a4ed7d2006..17fab73bde 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -3,12 +3,15 @@ /* CSqlScore class by Sushi */ #if defined(CONF_SQL) #include +#include +#include #include #include "../entities/character.h" #include "../gamemodes/DDRace.h" #include "sql_score.h" #include +#include "../save.h" static LOCK gs_SqlLock = 0; @@ -21,8 +24,9 @@ CSqlScore::CSqlScore(CGameContext *pGameServer) : m_pGameServer(pGameServer), m_pIp(g_Config.m_SvSqlIp), m_Port(g_Config.m_SvSqlPort) { + m_pDriver = NULL; str_copy(m_aMap, g_Config.m_SvMap, sizeof(m_aMap)); - NormalizeMapname(m_aMap); + ClearString(m_aMap); if(gs_SqlLock == 0) gs_SqlLock = lock_create(); @@ -34,17 +38,54 @@ CSqlScore::~CSqlScore() { lock_wait(gs_SqlLock); lock_release(gs_SqlLock); + + try + { + delete m_pStatement; + delete m_pConnection; + dbg_msg("SQL", "SQL connection disconnected"); + } + catch (sql::SQLException &e) + { + dbg_msg("SQL", "ERROR: No SQL connection"); + } } bool CSqlScore::Connect() { + if (m_pDriver != NULL && m_pConnection != NULL) + { + try + { + // Connect to specific database + m_pConnection->setSchema(m_pDatabase); + } + catch (sql::SQLException &e) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf); + + dbg_msg("SQL", "ERROR: SQL connection failed"); + return false; + } + return true; + } + try { + char aBuf[256]; + + sql::ConnectOptionsMap connection_properties; + connection_properties["hostName"] = sql::SQLString(m_pIp); + connection_properties["port"] = m_Port; + connection_properties["userName"] = sql::SQLString(m_pUser); + connection_properties["password"] = sql::SQLString(m_pPass); + connection_properties["OPT_RECONNECT"] = true; + // Create connection m_pDriver = get_driver_instance(); - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "tcp://%s:%d", m_pIp, m_Port); - m_pConnection = m_pDriver->connect(aBuf, m_pUser, m_pPass); + m_pConnection = m_pDriver->connect(connection_properties); // Create Statement m_pStatement = m_pConnection->createStatement(); @@ -108,15 +149,6 @@ bool CSqlScore::Connect() void CSqlScore::Disconnect() { - try - { - delete m_pConnection; - dbg_msg("SQL", "SQL connection disconnected"); - } - catch (sql::SQLException &e) - { - dbg_msg("SQL", "ERROR: No SQL connection"); - } } // create tables... should be done only once @@ -130,24 +162,22 @@ void CSqlScore::Init() // create tables char aBuf[768]; - str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_%s_race (Name VARCHAR(%d) NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP , Time FLOAT DEFAULT 0, cp1 FLOAT DEFAULT 0, cp2 FLOAT DEFAULT 0, cp3 FLOAT DEFAULT 0, cp4 FLOAT DEFAULT 0, cp5 FLOAT DEFAULT 0, cp6 FLOAT DEFAULT 0, cp7 FLOAT DEFAULT 0, cp8 FLOAT DEFAULT 0, cp9 FLOAT DEFAULT 0, cp10 FLOAT DEFAULT 0, cp11 FLOAT DEFAULT 0, cp12 FLOAT DEFAULT 0, cp13 FLOAT DEFAULT 0, cp14 FLOAT DEFAULT 0, cp15 FLOAT DEFAULT 0, cp16 FLOAT DEFAULT 0, cp17 FLOAT DEFAULT 0, cp18 FLOAT DEFAULT 0, cp19 FLOAT DEFAULT 0, cp20 FLOAT DEFAULT 0, cp21 FLOAT DEFAULT 0, cp22 FLOAT DEFAULT 0, cp23 FLOAT DEFAULT 0, cp24 FLOAT DEFAULT 0, cp25 FLOAT DEFAULT 0, KEY Name (Name)) CHARACTER SET utf8 ;", m_pPrefix, m_aMap, MAX_NAME_LENGTH); + str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_race (Map VARCHAR(128) BINARY NOT NULL, Name VARCHAR(%d) BINARY NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP , Time FLOAT DEFAULT 0, cp1 FLOAT DEFAULT 0, cp2 FLOAT DEFAULT 0, cp3 FLOAT DEFAULT 0, cp4 FLOAT DEFAULT 0, cp5 FLOAT DEFAULT 0, cp6 FLOAT DEFAULT 0, cp7 FLOAT DEFAULT 0, cp8 FLOAT DEFAULT 0, cp9 FLOAT DEFAULT 0, cp10 FLOAT DEFAULT 0, cp11 FLOAT DEFAULT 0, cp12 FLOAT DEFAULT 0, cp13 FLOAT DEFAULT 0, cp14 FLOAT DEFAULT 0, cp15 FLOAT DEFAULT 0, cp16 FLOAT DEFAULT 0, cp17 FLOAT DEFAULT 0, cp18 FLOAT DEFAULT 0, cp19 FLOAT DEFAULT 0, cp20 FLOAT DEFAULT 0, cp21 FLOAT DEFAULT 0, cp22 FLOAT DEFAULT 0, cp23 FLOAT DEFAULT 0, cp24 FLOAT DEFAULT 0, cp25 FLOAT DEFAULT 0, KEY (Map, Name)) CHARACTER SET utf8 ;", m_pPrefix, MAX_NAME_LENGTH); m_pStatement->execute(aBuf); - // Check if table has new column with timestamp - str_format(aBuf, sizeof(aBuf), "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '%s_%s_race' AND column_name = 'Timestamp'",m_pPrefix, m_aMap); - m_pResults = m_pStatement->executeQuery(aBuf); + str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_teamrace (Map VARCHAR(128) BINARY NOT NULL, Name VARCHAR(%d) BINARY NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, Time FLOAT DEFAULT 0, ID VARBINARY(16) NOT NULL, KEY Map (Map)) CHARACTER SET utf8 ;", m_pPrefix, MAX_NAME_LENGTH); + m_pStatement->execute(aBuf); - if(m_pResults->rowsCount() < 1) - { - // If not... add the column - str_format(aBuf, sizeof(aBuf), "ALTER TABLE %s_%s_race ADD Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER Name, ADD INDEX(Name);",m_pPrefix, m_aMap); - m_pStatement->execute(aBuf); - } + str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_maps (Map VARCHAR(128) BINARY NOT NULL, Server VARCHAR(32) BINARY NOT NULL, Points INT DEFAULT 0, UNIQUE KEY Map (Map)) CHARACTER SET utf8 ;", m_pPrefix); + m_pStatement->execute(aBuf); + + str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_saves (Savegame TEXT CHARACTER SET utf8 BINARY NOT NULL, Map VARCHAR(128) BINARY NOT NULL, Code VARCHAR(128) BINARY NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY (Map, Code)) CHARACTER SET utf8 ;", m_pPrefix); + m_pStatement->execute(aBuf); dbg_msg("SQL", "Tables were created successfully"); // get the best time - str_format(aBuf, sizeof(aBuf), "SELECT Time FROM %s_%s_race ORDER BY `Time` ASC LIMIT 0, 1;", m_pPrefix, m_aMap); + str_format(aBuf, sizeof(aBuf), "SELECT Time FROM %s_race WHERE Map='%s' ORDER BY `Time` ASC LIMIT 0, 1;", m_pPrefix, m_aMap); m_pResults = m_pStatement->executeQuery(aBuf); if(m_pResults->next()) @@ -155,13 +185,10 @@ void CSqlScore::Init() ((CGameControllerDDRace*)GameServer()->m_pController)->m_CurrentRecord = (float)m_pResults->getDouble("Time"); dbg_msg("SQL", "Getting best time on server done"); - - // delete results - delete m_pResults; } // delete statement - delete m_pStatement; + delete m_pResults; } catch (sql::SQLException &e) { @@ -193,12 +220,17 @@ void CSqlScore::LoadScoreThread(void *pUser) char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "SELECT * FROM %s_%s_race WHERE Name='%s' ORDER BY time ASC LIMIT 1;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName); + str_format(aBuf, sizeof(aBuf), "SELECT * FROM %s_race WHERE Map='%s' AND Name='%s' ORDER BY time ASC LIMIT 1;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName); pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); if(pData->m_pSqlData->m_pResults->next()) { // get the best time - pData->m_pSqlData->PlayerData(pData->m_ClientID)->m_BestTime = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); + float time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); + pData->m_pSqlData->PlayerData(pData->m_ClientID)->m_BestTime = time; + pData->m_pSqlData->PlayerData(pData->m_ClientID)->m_CurrentTime = time; + if(pData->m_pSqlData->m_pGameServer->m_apPlayers[pData->m_ClientID]) + pData->m_pSqlData->m_pGameServer->m_apPlayers[pData->m_ClientID]->m_Score = -time; + char aColumn[8]; if(g_Config.m_SvCheckpointSave) { @@ -213,7 +245,6 @@ void CSqlScore::LoadScoreThread(void *pUser) dbg_msg("SQL", "Getting best time done"); // delete statement and results - delete pData->m_pSqlData->m_pStatement; delete pData->m_pSqlData->m_pResults; } catch (sql::SQLException &e) @@ -237,7 +268,7 @@ void CSqlScore::LoadScore(int ClientID) { CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_ClientID = ClientID; - str_copy(Tmp->m_aName, Server()->ClientName(ClientID), sizeof(Tmp->m_aName)); + str_copy(Tmp->m_aName, Server()->ClientName(ClientID), MAX_NAME_LENGTH); Tmp->m_pSqlData = this; void *LoadThread = thread_create(LoadScoreThread, Tmp); @@ -246,30 +277,110 @@ void CSqlScore::LoadScore(int ClientID) #endif } -void CSqlScore::SaveScoreThread(void *pUser) +void CSqlScore::SaveTeamScoreThread(void *pUser) { lock_wait(gs_SqlLock); - CSqlScoreData *pData = (CSqlScoreData *)pUser; + CSqlTeamScoreData *pData = (CSqlTeamScoreData *)pUser; // Connect to database if(pData->m_pSqlData->Connect()) { try { - char aBuf[768]; + char aBuf[2300]; + char aUpdateID[17]; + aUpdateID[0] = 0; - // check strings - pData->m_pSqlData->ClearString(pData->m_aName); + for(unsigned int i = 0; i < pData->m_Size; i++) + { + pData->m_pSqlData->ClearString(pData->m_aNames[i]); + } - // if no entry found... create a new one - str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %s_%s_race(Name, Timestamp, Time, cp1, cp2, cp3, cp4, cp5, cp6, cp7, cp8, cp9, cp10, cp11, cp12, cp13, cp14, cp15, cp16, cp17, cp18, cp19, cp20, cp21, cp22, cp23, cp24, cp25) VALUES ('%s', CURRENT_TIMESTAMP(), '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f');", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName, pData->m_Time, pData->m_aCpCurrent[0], pData->m_aCpCurrent[1], pData->m_aCpCurrent[2], pData->m_aCpCurrent[3], pData->m_aCpCurrent[4], pData->m_aCpCurrent[5], pData->m_aCpCurrent[6], pData->m_aCpCurrent[7], pData->m_aCpCurrent[8], pData->m_aCpCurrent[9], pData->m_aCpCurrent[10], pData->m_aCpCurrent[11], pData->m_aCpCurrent[12], pData->m_aCpCurrent[13], pData->m_aCpCurrent[14], pData->m_aCpCurrent[15], pData->m_aCpCurrent[16], pData->m_aCpCurrent[17], pData->m_aCpCurrent[18], pData->m_aCpCurrent[19], pData->m_aCpCurrent[20], pData->m_aCpCurrent[21], pData->m_aCpCurrent[22], pData->m_aCpCurrent[23], pData->m_aCpCurrent[24]); - pData->m_pSqlData->m_pStatement->execute(aBuf); + str_format(aBuf, sizeof(aBuf), "SELECT Name, l.ID, Time FROM ((SELECT ID FROM %s_teamrace WHERE Map = '%s' AND Name = '%s') as l) LEFT JOIN %s_teamrace as r ON l.ID = r.ID ORDER BY ID;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aNames[0], pData->m_pSqlData->m_pPrefix); + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + + if (pData->m_pSqlData->m_pResults->rowsCount() > 0) + { + char aID[17]; + char aID2[17]; + char aName[64]; + unsigned int Count = 0; + bool ValidNames = true; + + pData->m_pSqlData->m_pResults->first(); + float Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); + strcpy(aID, pData->m_pSqlData->m_pResults->getString("ID").c_str()); + + do + { + strcpy(aID2, pData->m_pSqlData->m_pResults->getString("ID").c_str()); + strcpy(aName, pData->m_pSqlData->m_pResults->getString("Name").c_str()); + pData->m_pSqlData->ClearString(aName); + if (str_comp(aID, aID2) != 0) + { + if (ValidNames && Count == pData->m_Size) + { + if (pData->m_Time < Time) + strcpy(aUpdateID, aID); + else + goto end; + break; + } + + Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); + ValidNames = true; + Count = 0; + strcpy(aID, aID2); + } + if (!ValidNames) + continue; + + ValidNames = false; + + for(unsigned int i = 0; i < pData->m_Size; i++) + { + if (str_comp(aName, pData->m_aNames[i]) == 0) + { + ValidNames = true; + Count++; + break; + } + } + } while (pData->m_pSqlData->m_pResults->next()); + + if (ValidNames && Count == pData->m_Size) + { + if (pData->m_Time < Time) + strcpy(aUpdateID, aID); + else + goto end; + } + } + + if (aUpdateID[0]) + { + str_format(aBuf, sizeof(aBuf), "UPDATE %s_teamrace SET Time='%.2f' WHERE ID = '%s';", pData->m_pSqlData->m_pPrefix, pData->m_Time, aUpdateID); + pData->m_pSqlData->m_pStatement->execute(aBuf); + } + else + { + pData->m_pSqlData->m_pStatement->execute("SET @id = UUID();"); + + for(unsigned int i = 0; i < pData->m_Size; i++) + { + // if no entry found... create a new one + str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %s_teamrace(Map, Name, Timestamp, Time, ID) VALUES ('%s', '%s', CURRENT_TIMESTAMP(), '%.2f', @id);", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aNames[i], pData->m_Time); + pData->m_pSqlData->m_pStatement->execute(aBuf); + } + } + + end: dbg_msg("SQL", "Updating time done"); // delete results statement - delete pData->m_pSqlData->m_pStatement; + delete pData->m_pSqlData->m_pResults; } catch (sql::SQLException &e) { @@ -280,7 +391,7 @@ void CSqlScore::SaveScoreThread(void *pUser) } // disconnect from database - pData->m_pSqlData->Disconnect();//TODO:Check if an exception is caught will this still execute ? + pData->m_pSqlData->Disconnect(); } delete pData; @@ -288,188 +399,175 @@ void CSqlScore::SaveScoreThread(void *pUser) lock_release(gs_SqlLock); } -void CSqlScore::SaveScore(int ClientID, float Time, float CpTime[NUM_CHECKPOINTS]) +void CSqlScore::MapVote(int ClientID, const char* MapName) { - CConsole* pCon = (CConsole*)GameServer()->Console(); - if(pCon->m_Cheated) - return; - CSqlScoreData *Tmp = new CSqlScoreData(); + CSqlMapData *Tmp = new CSqlMapData(); Tmp->m_ClientID = ClientID; - str_copy(Tmp->m_aName, Server()->ClientName(ClientID), sizeof(Tmp->m_aName)); - Tmp->m_Time = Time; - for(int i = 0; i < NUM_CHECKPOINTS; i++) - Tmp->m_aCpCurrent[i] = CpTime[i]; + str_copy(Tmp->m_aMap, MapName, 128); Tmp->m_pSqlData = this; - void *SaveThread = thread_create(SaveScoreThread, Tmp); + void *VoteThread = thread_create(MapVoteThread, Tmp); #if defined(CONF_FAMILY_UNIX) - pthread_detach((pthread_t)SaveThread); + pthread_detach((pthread_t)VoteThread); #endif } -void CSqlScore::ShowRankThread(void *pUser) +void CSqlScore::MapVoteThread(void *pUser) { lock_wait(gs_SqlLock); - CSqlScoreData *pData = (CSqlScoreData *)pUser; + CSqlMapData *pData = (CSqlMapData *)pUser; // Connect to database if(pData->m_pSqlData->Connect()) { + char originalMap[128]; + strcpy(originalMap,pData->m_aMap); + pData->m_pSqlData->ClearString(pData->m_aMap); + pData->m_pSqlData->FuzzyString(pData->m_aMap); + try { - // check strings - char originalName[MAX_NAME_LENGTH]; - strcpy(originalName,pData->m_aName); - pData->m_pSqlData->ClearString(pData->m_aName); + char aBuf[768]; + str_format(aBuf, sizeof(aBuf), "SELECT Map, Server FROM %s_maps WHERE Map LIKE '%s' COLLATE utf8_general_ci ORDER BY LENGTH(Map), Map LIMIT 1;", pData->m_pSqlData->m_pPrefix, pData->m_aMap); + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); - // check sort methode - char aBuf[600]; + CPlayer *pPlayer = pData->m_pSqlData->m_pGameServer->m_apPlayers[pData->m_ClientID]; - pData->m_pSqlData->m_pStatement->execute("SET @rownum := 0;"); - str_format(aBuf, sizeof(aBuf), "SELECT Rank, one_rank.Name, one_rank.Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(r.Timestamp) as Ago, UNIX_TIMESTAMP(r.Timestamp) as stamp " - "FROM (" - "SELECT * FROM (" - "SELECT @rownum := @rownum + 1 AS RANK, Name, Time " - "FROM (" - "SELECT Name, min(Time) as Time " - "FROM %s_%s_race " - "Group By Name) as all_top_times " - "ORDER BY Time ASC) as all_ranks " - "WHERE all_ranks.Name = '%s') as one_rank " - "LEFT JOIN %s_%s_race as r " - "ON one_rank.Name = r.Name && one_rank.Time = r.Time " - "ORDER BY Ago ASC " - "LIMIT 0,1" - ";", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap,pData->m_aName, pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap); + int64 Now = pData->m_pSqlData->Server()->Tick(); + int Timeleft = 0; - pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + if(!pPlayer) + goto end; + + Timeleft = pPlayer->m_LastVoteCall + pData->m_pSqlData->Server()->TickSpeed()*g_Config.m_SvVoteDelay - Now; if(pData->m_pSqlData->m_pResults->rowsCount() != 1) { - str_format(aBuf, sizeof(aBuf), "%s is not ranked", originalName); + str_format(aBuf, sizeof(aBuf), "No map like \"%s\" found. Try adding a '%%' at the start if you don't know the first character. Example: /map %%castle for \"Out of Castle\"", originalMap); pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); } + else if(pPlayer->m_LastVoteCall && Timeleft > 0) + { + char aChatmsg[512] = {0}; + str_format(aChatmsg, sizeof(aChatmsg), "You must wait %d seconds before making another vote", (Timeleft/pData->m_pSqlData->Server()->TickSpeed())+1); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aChatmsg); + } + else if(time_get() < pData->m_pSqlData->GameServer()->m_LastMapVote + (time_freq() * g_Config.m_SvVoteMapTimeDelay)) + { + char chatmsg[512] = {0}; + str_format(chatmsg, sizeof(chatmsg), "There's a %d second delay between map-votes, please wait %d seconds.", g_Config.m_SvVoteMapTimeDelay,((pData->m_pSqlData->GameServer()->m_LastMapVote+(g_Config.m_SvVoteMapTimeDelay * time_freq()))/time_freq())-(time_get()/time_freq())); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, chatmsg); + } else { pData->m_pSqlData->m_pResults->next(); - int since = (int)pData->m_pSqlData->m_pResults->getInt("Ago"); - char agoString[40]; - mem_zero(agoString, sizeof(agoString)); - agoTimeToString(since,agoString); - - float Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); - int Rank = (int)pData->m_pSqlData->m_pResults->getInt("Rank"); - if(g_Config.m_SvHideScore) - str_format(aBuf, sizeof(aBuf), "Your time: %d minute(s) %5.2f second(s)", (int)(Time/60), Time-((int)Time/60*60)); - else - str_format(aBuf, sizeof(aBuf), "%d. %s Time: %d minute(s) %5.2f second(s), requested by (%s)", Rank, pData->m_pSqlData->m_pResults->getString("Name").c_str(), (int)(Time/60), Time-((int)Time/60*60), pData->m_aRequestingPlayer, agoString); - - if(pData->m_pSqlData->m_pResults->getInt("stamp") != 0) - { - pData->m_pSqlData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID); - str_format(aBuf, sizeof(aBuf), "Finished: %s ago", agoString); - } - if(pData->m_Search) - strcat(aBuf, pData->m_aRequestingPlayer); - pData->m_pSqlData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID); - + char aMap[128]; + strcpy(aMap, pData->m_pSqlData->m_pResults->getString("Map").c_str()); + char aServer[32]; + strcpy(aServer, pData->m_pSqlData->m_pResults->getString("Server").c_str()); + char aCmd[256]; + str_format(aCmd, sizeof(aCmd), "sv_reset_file types/%s/flexreset.cfg; change_map %s", aServer, aMap); + char aChatmsg[512]; + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s' (%s)", pData->m_pSqlData->GameServer()->Server()->ClientName(pData->m_ClientID), aMap, "/map"); + + pData->m_pSqlData->GameServer()->m_VoteKick = false; + pData->m_pSqlData->GameServer()->m_LastMapVote = time_get(); + pData->m_pSqlData->GameServer()->CallVote(pData->m_ClientID, aMap, aCmd, "/map", aChatmsg); } - dbg_msg("SQL", "Showing rank done"); - - // delete results and statement + end: delete pData->m_pSqlData->m_pResults; - delete pData->m_pSqlData->m_pStatement; } catch (sql::SQLException &e) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); dbg_msg("SQL", aBuf); - dbg_msg("SQL", "ERROR: Could not show rank"); + dbg_msg("SQL", "ERROR: Could not update time"); } - // disconnect from database - pData->m_pSqlData->Disconnect();//TODO:Check if an exception is caught will this still execute ? + pData->m_pSqlData->Disconnect(); } delete pData; - lock_release(gs_SqlLock); } -void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search) +void CSqlScore::MapPoints(int ClientID, const char* MapName) { - CSqlScoreData *Tmp = new CSqlScoreData(); + CSqlMapData *Tmp = new CSqlMapData(); Tmp->m_ClientID = ClientID; - str_copy(Tmp->m_aName, pName, sizeof(Tmp->m_aName)); - Tmp->m_Search = Search; - str_format(Tmp->m_aRequestingPlayer, sizeof(Tmp->m_aRequestingPlayer), " (%s)", Server()->ClientName(ClientID)); + str_copy(Tmp->m_aMap, MapName, 128); Tmp->m_pSqlData = this; - void *RankThread = thread_create(ShowRankThread, Tmp); + void *InfoThread = thread_create(MapPointsThread, Tmp); #if defined(CONF_FAMILY_UNIX) - pthread_detach((pthread_t)RankThread); + pthread_detach((pthread_t)InfoThread); #endif } -void CSqlScore::ShowTop5Thread(void *pUser) +void CSqlScore::MapPointsThread(void *pUser) { lock_wait(gs_SqlLock); - CSqlScoreData *pData = (CSqlScoreData *)pUser; + CSqlMapData *pData = (CSqlMapData *)pUser; // Connect to database if(pData->m_pSqlData->Connect()) { + char originalMap[128]; + strcpy(originalMap,pData->m_aMap); + pData->m_pSqlData->ClearString(pData->m_aMap); + pData->m_pSqlData->FuzzyString(pData->m_aMap); + try { - // check sort methode - char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "SELECT Name, min(Time) as Time FROM %s_%s_race Group By Name ORDER BY `Time` ASC LIMIT %d, 5;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_Num-1); + char aBuf[768]; + str_format(aBuf, sizeof(aBuf), "SELECT Map, Server, Points FROM %s_maps WHERE Map LIKE '%s' COLLATE utf8_general_ci ORDER BY LENGTH(Map), Map LIMIT 1;", pData->m_pSqlData->m_pPrefix, pData->m_aMap); pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); - // show top5 - pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "----------- Top 5 -----------"); - - int Rank = pData->m_Num; - float Time = 0; - while(pData->m_pSqlData->m_pResults->next()) + if(pData->m_pSqlData->m_pResults->rowsCount() != 1) { - Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); - str_format(aBuf, sizeof(aBuf), "%d. %s Time: %d minute(s) %.2f second(s)", Rank, pData->m_pSqlData->m_pResults->getString("Name").c_str(), (int)(Time/60), Time-((int)Time/60*60)); - pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - Rank++; + str_format(aBuf, sizeof(aBuf), "No map like \"%s\" found.", originalMap); + } + else + { + pData->m_pSqlData->m_pResults->next(); + int points = (int)pData->m_pSqlData->m_pResults->getInt("Points"); + char aMap[128]; + strcpy(aMap, pData->m_pSqlData->m_pResults->getString("Map").c_str()); + char aServer[32]; + strcpy(aServer, pData->m_pSqlData->m_pResults->getString("Server").c_str()); + aServer[0] = toupper(aServer[0]); + if (points == 1) + str_format(aBuf, sizeof(aBuf), "\"%s\" on %s (%d point)", aMap, aServer, points); + else + str_format(aBuf, sizeof(aBuf), "\"%s\" on %s (%d points)", aMap, aServer, points); } - pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "-------------------------------"); - - dbg_msg("SQL", "Showing top5 done"); - // delete results and statement + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); delete pData->m_pSqlData->m_pResults; - delete pData->m_pSqlData->m_pStatement; } catch (sql::SQLException &e) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); dbg_msg("SQL", aBuf); - dbg_msg("SQL", "ERROR: Could not show top5"); + dbg_msg("SQL", "ERROR: Could not update time"); } - // disconnect from database pData->m_pSqlData->Disconnect(); } delete pData; - lock_release(gs_SqlLock); } -void CSqlScore::ShowTimesThread(void *pUser) +void CSqlScore::SaveScoreThread(void *pUser) { lock_wait(gs_SqlLock); + CSqlScoreData *pData = (CSqlScoreData *)pUser; // Connect to database @@ -477,246 +575,1166 @@ void CSqlScore::ShowTimesThread(void *pUser) { try { - char originalName[MAX_NAME_LENGTH]; - strcpy(originalName,pData->m_aName); - pData->m_pSqlData->ClearString(pData->m_aName); - - char aBuf[512]; + char aBuf[768]; - if(pData->m_Search) // last 5 times of a player - str_format(aBuf, sizeof(aBuf), "SELECT Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago, UNIX_TIMESTAMP(Timestamp) as Stamp FROM %s_%s_race WHERE Name = '%s' ORDER BY Ago ASC LIMIT %d, 5;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName, pData->m_Num-1); - else// last 5 times of server - str_format(aBuf, sizeof(aBuf), "SELECT Name, Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago, UNIX_TIMESTAMP(Timestamp) as Stamp FROM %s_%s_race ORDER BY Ago ASC LIMIT %d, 5;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_Num-1); + // check strings + pData->m_pSqlData->ClearString(pData->m_aName); + str_format(aBuf, sizeof(aBuf), "SELECT * FROM %s_race WHERE Map='%s' AND Name='%s' ORDER BY time ASC LIMIT 1;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName); pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); - - // show top5 - if(pData->m_pSqlData->m_pResults->rowsCount() == 0) - { - pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "There are no times in the specified range"); - goto end; - } - - str_format(aBuf, sizeof(aBuf), "------------ Last Times No %d - %d ------------",pData->m_Num,pData->m_Num + pData->m_pSqlData->m_pResults->rowsCount() - 1); - pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - - float pTime = 0; - int pSince = 0; - int pStamp = 0; - - while(pData->m_pSqlData->m_pResults->next()) + if(!pData->m_pSqlData->m_pResults->next()) { - char pAgoString[40] = "\0"; - pSince = (int)pData->m_pSqlData->m_pResults->getInt("Ago"); - pStamp = (int)pData->m_pSqlData->m_pResults->getInt("Stamp"); - pTime = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); + delete pData->m_pSqlData->m_pResults; - agoTimeToString(pSince,pAgoString); + str_format(aBuf, sizeof(aBuf), "SELECT Points FROM %s_maps WHERE Map ='%s'", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap); + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); - if(pData->m_Search) // last 5 times of a player - { - if(pStamp == 0) // stamp is 00:00:00 cause it's an old entry from old times where there where no stamps yet - str_format(aBuf, sizeof(aBuf), "%d min %.2f sec, don't know how long ago", (int)(pTime/60), pTime-((int)pTime/60*60)); - else - str_format(aBuf, sizeof(aBuf), "%s ago, %d min %.2f sec", pAgoString,(int)(pTime/60), pTime-((int)pTime/60*60)); - } - else // last 5 times of the server + if(pData->m_pSqlData->m_pResults->rowsCount() == 1) { - if(pStamp == 0) // stamp is 00:00:00 cause it's an old entry from old times where there where no stamps yet - str_format(aBuf, sizeof(aBuf), "%s, %d m %.2f s, don't know when", pData->m_pSqlData->m_pResults->getString("Name").c_str(), (int)(pTime/60), pTime-((int)pTime/60*60)); + pData->m_pSqlData->m_pResults->next(); + int points = (int)pData->m_pSqlData->m_pResults->getInt("Points"); + if (points == 1) + str_format(aBuf, sizeof(aBuf), "You earned %d point for finishing this map!", points); else - str_format(aBuf, sizeof(aBuf), "%s, %s ago, %d m %.2f s", pData->m_pSqlData->m_pResults->getString("Name").c_str(), pAgoString, (int)(pTime/60), pTime-((int)pTime/60*60)); + str_format(aBuf, sizeof(aBuf), "You earned %d points for finishing this map!", points); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); } - pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); } - pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "----------------------------------------------------"); - dbg_msg("SQL", "Showing times done"); + delete pData->m_pSqlData->m_pResults; - // delete results and statement - delete pData->m_pSqlData->m_pResults; - delete pData->m_pSqlData->m_pStatement; + // if no entry found... create a new one + str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %s_race(Map, Name, Timestamp, Time, cp1, cp2, cp3, cp4, cp5, cp6, cp7, cp8, cp9, cp10, cp11, cp12, cp13, cp14, cp15, cp16, cp17, cp18, cp19, cp20, cp21, cp22, cp23, cp24, cp25) VALUES ('%s', '%s', CURRENT_TIMESTAMP(), '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f');", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName, pData->m_Time, pData->m_aCpCurrent[0], pData->m_aCpCurrent[1], pData->m_aCpCurrent[2], pData->m_aCpCurrent[3], pData->m_aCpCurrent[4], pData->m_aCpCurrent[5], pData->m_aCpCurrent[6], pData->m_aCpCurrent[7], pData->m_aCpCurrent[8], pData->m_aCpCurrent[9], pData->m_aCpCurrent[10], pData->m_aCpCurrent[11], pData->m_aCpCurrent[12], pData->m_aCpCurrent[13], pData->m_aCpCurrent[14], pData->m_aCpCurrent[15], pData->m_aCpCurrent[16], pData->m_aCpCurrent[17], pData->m_aCpCurrent[18], pData->m_aCpCurrent[19], pData->m_aCpCurrent[20], pData->m_aCpCurrent[21], pData->m_aCpCurrent[22], pData->m_aCpCurrent[23], pData->m_aCpCurrent[24]); + pData->m_pSqlData->m_pStatement->execute(aBuf); + + dbg_msg("SQL", "Updating time done"); } catch (sql::SQLException &e) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); dbg_msg("SQL", aBuf); - dbg_msg("SQL", "ERROR: Could not show times"); + dbg_msg("SQL", "ERROR: Could not update time"); } - end: + // disconnect from database pData->m_pSqlData->Disconnect(); } + delete pData; lock_release(gs_SqlLock); } -void CSqlScore::ShowTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut) +void CSqlScore::SaveScore(int ClientID, float Time, float CpTime[NUM_CHECKPOINTS]) { + CConsole* pCon = (CConsole*)GameServer()->Console(); + if(pCon->m_Cheated) + return; CSqlScoreData *Tmp = new CSqlScoreData(); - Tmp->m_Num = Debut; Tmp->m_ClientID = ClientID; + str_copy(Tmp->m_aName, Server()->ClientName(ClientID), MAX_NAME_LENGTH); + Tmp->m_Time = Time; + for(int i = 0; i < NUM_CHECKPOINTS; i++) + Tmp->m_aCpCurrent[i] = CpTime[i]; Tmp->m_pSqlData = this; - void *Top5Thread = thread_create(ShowTop5Thread, Tmp); + void *SaveThread = thread_create(SaveScoreThread, Tmp); #if defined(CONF_FAMILY_UNIX) - pthread_detach((pthread_t)Top5Thread); + pthread_detach((pthread_t)SaveThread); #endif } -void CSqlScore::ShowTimes(int ClientID, int Debut) +void CSqlScore::SaveTeamScore(int* aClientIDs, unsigned int Size, float Time) { - CSqlScoreData *Tmp = new CSqlScoreData(); - Tmp->m_Num = Debut; - Tmp->m_ClientID = ClientID; + CConsole* pCon = (CConsole*)GameServer()->Console(); + if(pCon->m_Cheated) + return; + CSqlTeamScoreData *Tmp = new CSqlTeamScoreData(); + for(unsigned int i = 0; i < Size; i++) + { + Tmp->m_aClientIDs[i] = aClientIDs[i]; + str_copy(Tmp->m_aNames[i], Server()->ClientName(aClientIDs[i]), MAX_NAME_LENGTH); + } + Tmp->m_Size = Size; + Tmp->m_Time = Time; Tmp->m_pSqlData = this; - Tmp->m_Search = false; - void *TimesThread = thread_create(ShowTimesThread, Tmp); + void *SaveTeamThread = thread_create(SaveTeamScoreThread, Tmp); #if defined(CONF_FAMILY_UNIX) - pthread_detach((pthread_t)TimesThread); + pthread_detach((pthread_t)SaveTeamThread); #endif } -void CSqlScore::ShowTimes(int ClientID, const char* pName, int Debut) +void CSqlScore::ShowTeamRankThread(void *pUser) +{ + lock_wait(gs_SqlLock); + + CSqlScoreData *pData = (CSqlScoreData *)pUser; + + // Connect to database + if(pData->m_pSqlData->Connect()) + { + try + { + // check strings + char originalName[MAX_NAME_LENGTH]; + strcpy(originalName,pData->m_aName); + pData->m_pSqlData->ClearString(pData->m_aName); + + // check sort methode + char aBuf[600]; + char aNames[2300]; + aNames[0] = '\0'; + + pData->m_pSqlData->m_pStatement->execute("SET @prev := NULL;"); + pData->m_pSqlData->m_pStatement->execute("SET @rank := 1;"); + pData->m_pSqlData->m_pStatement->execute("SET @pos := 0;"); + str_format(aBuf, sizeof(aBuf), "SELECT Rank, Name, Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago, UNIX_TIMESTAMP(Timestamp) as stamp FROM (SELECT Rank, l2.ID FROM ((SELECT ID, (@pos := @pos+1) pos, (@rank := IF(@prev = Time,@rank,@pos)) rank, (@prev := Time) Time FROM (SELECT ID, Time FROM %s_teamrace WHERE Map = '%s' GROUP BY ID ORDER BY Time) as ll) as l2) LEFT JOIN %s_teamrace as r2 ON l2.ID = r2.ID WHERE Map = '%s' AND Name = '%s' ORDER BY Rank LIMIT 1) as l LEFT JOIN %s_teamrace as r ON l.ID = r.ID ORDER BY Name;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName, pData->m_pSqlData->m_pPrefix); + + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + + int Rows = pData->m_pSqlData->m_pResults->rowsCount(); + + if(Rows < 1) + { + str_format(aBuf, sizeof(aBuf), "%s has no team ranks", originalName); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + } + else + { + pData->m_pSqlData->m_pResults->first(); + + int since = (int)pData->m_pSqlData->m_pResults->getInt("Ago"); + char agoString[40]; + mem_zero(agoString, sizeof(agoString)); + agoTimeToString(since,agoString); + + float Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); + int Rank = (int)pData->m_pSqlData->m_pResults->getInt("Rank"); + + for(int Row = 0; Row < Rows; Row++) + { + strcat(aNames, pData->m_pSqlData->m_pResults->getString("Name").c_str()); + pData->m_pSqlData->m_pResults->next(); + + if (Row < Rows - 2) + strcat(aNames, ", "); + else if (Row < Rows - 1) + strcat(aNames, " & "); + } + + pData->m_pSqlData->m_pResults->first(); + + if(g_Config.m_SvHideScore) + str_format(aBuf, sizeof(aBuf), "Your team time: %02d:%05.02f", (int)(Time/60), Time-((int)Time/60*60)); + else + str_format(aBuf, sizeof(aBuf), "%d. %s Team time: %02d:%05.02f, requested by %s", Rank, aNames, (int)(Time/60), Time-((int)Time/60*60), pData->m_aRequestingPlayer, agoString); + + if(pData->m_pSqlData->m_pResults->getInt("stamp") != 0) + { + pData->m_pSqlData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID); + str_format(aBuf, sizeof(aBuf), "Finished: %s ago", agoString); + } + pData->m_pSqlData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID); + + } + + dbg_msg("SQL", "Showing teamrank done"); + + // delete results and statement + delete pData->m_pSqlData->m_pResults; + } + catch (sql::SQLException &e) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf); + dbg_msg("SQL", "ERROR: Could not show team rank"); + } + + // disconnect from database + pData->m_pSqlData->Disconnect(); + } + + delete pData; + + lock_release(gs_SqlLock); +} + +void CSqlScore::ShowTeamTop5Thread(void *pUser) +{ + lock_wait(gs_SqlLock); + + CSqlScoreData *pData = (CSqlScoreData *)pUser; + + // Connect to database + if(pData->m_pSqlData->Connect()) + { + try + { + // check sort methode + char aBuf[512]; + + pData->m_pSqlData->m_pStatement->execute("SET @prev := NULL;"); + pData->m_pSqlData->m_pStatement->execute("SET @previd := NULL;"); + pData->m_pSqlData->m_pStatement->execute("SET @rank := 1;"); + pData->m_pSqlData->m_pStatement->execute("SET @pos := 0;"); + str_format(aBuf, sizeof(aBuf), "SELECT ID, Name, Time, rank FROM (SELECT r.ID, Name, rank, l.Time FROM ((SELECT ID, rank, Time FROM (SELECT ID, (@pos := IF(@previd = ID,@pos,@pos+1)) pos, (@previd := ID), (@rank := IF(@prev = Time,@rank,@pos)) rank, (@prev := Time) Time FROM (SELECT ID, MIN(Time) as Time FROM %s_teamrace WHERE Map = '%s' GROUP BY ID ORDER BY `Time` ASC) as all_top_times) as a LIMIT %d, 5) as l) LEFT JOIN %s_teamrace as r ON l.ID = r.ID ORDER BY Time ASC, r.ID, Name ASC) as a;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_Num-1, pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap); + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + + // show teamtop5 + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "------- Team Top 5 -------"); + + int Rows = pData->m_pSqlData->m_pResults->rowsCount(); + + if (Rows >= 1) { + char aID[17]; + char aID2[17]; + char aNames[2300]; + int Rank = 0; + float Time = 0; + int aCuts[Rows]; + int CutPos = 0; + + aNames[0] = '\0'; + aCuts[0] = -1; + + pData->m_pSqlData->m_pResults->first(); + strcpy(aID, pData->m_pSqlData->m_pResults->getString("ID").c_str()); + for(int Row = 0; Row < Rows; Row++) + { + strcpy(aID2, pData->m_pSqlData->m_pResults->getString("ID").c_str()); + if (str_comp(aID, aID2) != 0) + { + strcpy(aID, aID2); + aCuts[CutPos++] = Row - 1; + } + pData->m_pSqlData->m_pResults->next(); + } + aCuts[CutPos] = Rows - 1; + + CutPos = 0; + pData->m_pSqlData->m_pResults->first(); + for(int Row = 0; Row < Rows; Row++) + { + strcat(aNames, pData->m_pSqlData->m_pResults->getString("Name").c_str()); + + if (Row < aCuts[CutPos] - 1) + strcat(aNames, ", "); + else if (Row < aCuts[CutPos]) + strcat(aNames, " & "); + + Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); + Rank = (float)pData->m_pSqlData->m_pResults->getInt("rank"); + + if (Row == aCuts[CutPos]) + { + str_format(aBuf, sizeof(aBuf), "%d. %s Team Time: %02d:%05.2f", Rank, aNames, (int)(Time/60), Time-((int)Time/60*60)); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + CutPos++; + aNames[0] = '\0'; + } + + pData->m_pSqlData->m_pResults->next(); + } + } + + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "-------------------------------"); + + dbg_msg("SQL", "Showing teamtop5 done"); + + // delete results and statement + delete pData->m_pSqlData->m_pResults; + } + catch (sql::SQLException &e) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf); + dbg_msg("SQL", "ERROR: Could not show teamtop5"); + } + + // disconnect from database + pData->m_pSqlData->Disconnect(); + } + + delete pData; + + lock_release(gs_SqlLock); +} + +void CSqlScore::ShowRankThread(void *pUser) +{ + lock_wait(gs_SqlLock); + + CSqlScoreData *pData = (CSqlScoreData *)pUser; + + // Connect to database + if(pData->m_pSqlData->Connect()) + { + try + { + // check strings + char originalName[MAX_NAME_LENGTH]; + strcpy(originalName,pData->m_aName); + pData->m_pSqlData->ClearString(pData->m_aName); + + // check sort methode + char aBuf[600]; + + pData->m_pSqlData->m_pStatement->execute("SET @prev := NULL;"); + pData->m_pSqlData->m_pStatement->execute("SET @rank := 1;"); + pData->m_pSqlData->m_pStatement->execute("SET @pos := 0;"); + str_format(aBuf, sizeof(aBuf), "SELECT Rank, one_rank.Name, one_rank.Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(r.Timestamp) as Ago, UNIX_TIMESTAMP(r.Timestamp) as stamp FROM (SELECT * FROM (SELECT Name, (@pos := @pos+1) pos, (@rank := IF(@prev = Time,@rank, @pos)) rank, (@prev := Time) Time FROM (SELECT Name, min(Time) as Time FROM %s_race WHERE Map = '%s' Group By Name) as all_top_times ORDER BY Time ASC) as all_ranks WHERE all_ranks.Name = '%s') as one_rank LEFT JOIN %s_race as r ON one_rank.Name = r.Name && one_rank.Time = r.Time ORDER BY Ago ASC LIMIT 0,1;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap,pData->m_aName, pData->m_pSqlData->m_pPrefix); + + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + + if(pData->m_pSqlData->m_pResults->rowsCount() != 1) + { + str_format(aBuf, sizeof(aBuf), "%s is not ranked", originalName); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + } + else + { + pData->m_pSqlData->m_pResults->next(); + int since = (int)pData->m_pSqlData->m_pResults->getInt("Ago"); + char agoString[40]; + mem_zero(agoString, sizeof(agoString)); + agoTimeToString(since,agoString); + + float Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); + int Rank = (int)pData->m_pSqlData->m_pResults->getInt("Rank"); + if(g_Config.m_SvHideScore) + str_format(aBuf, sizeof(aBuf), "Your time: %02d:%05.2f", (int)(Time/60), Time-((int)Time/60*60)); + else + str_format(aBuf, sizeof(aBuf), "%d. %s Time: %02d:%05.2f, requested by %s", Rank, pData->m_pSqlData->m_pResults->getString("Name").c_str(), (int)(Time/60), Time-((int)Time/60*60), pData->m_aRequestingPlayer, agoString); + + if(pData->m_pSqlData->m_pResults->getInt("stamp") != 0) + { + pData->m_pSqlData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID); + str_format(aBuf, sizeof(aBuf), "Finished: %s ago", agoString); + } + pData->m_pSqlData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID); + + } + + dbg_msg("SQL", "Showing rank done"); + + // delete results and statement + delete pData->m_pSqlData->m_pResults; + } + catch (sql::SQLException &e) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf); + dbg_msg("SQL", "ERROR: Could not show rank"); + } + + // disconnect from database + pData->m_pSqlData->Disconnect(); + } + + delete pData; + + lock_release(gs_SqlLock); +} + +void CSqlScore::ShowTeamRank(int ClientID, const char* pName, bool Search) +{ + CSqlScoreData *Tmp = new CSqlScoreData(); + Tmp->m_ClientID = ClientID; + str_copy(Tmp->m_aName, pName, MAX_NAME_LENGTH); + Tmp->m_Search = Search; + str_format(Tmp->m_aRequestingPlayer, sizeof(Tmp->m_aRequestingPlayer), "%s", Server()->ClientName(ClientID)); + Tmp->m_pSqlData = this; + + void *TeamRankThread = thread_create(ShowTeamRankThread, Tmp); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)TeamRankThread); +#endif +} + +void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search) +{ + CSqlScoreData *Tmp = new CSqlScoreData(); + Tmp->m_ClientID = ClientID; + str_copy(Tmp->m_aName, pName, MAX_NAME_LENGTH); + Tmp->m_Search = Search; + str_format(Tmp->m_aRequestingPlayer, sizeof(Tmp->m_aRequestingPlayer), "%s", Server()->ClientName(ClientID)); + Tmp->m_pSqlData = this; + + void *RankThread = thread_create(ShowRankThread, Tmp); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)RankThread); +#endif +} + +void CSqlScore::ShowTop5Thread(void *pUser) +{ + lock_wait(gs_SqlLock); + + CSqlScoreData *pData = (CSqlScoreData *)pUser; + + // Connect to database + if(pData->m_pSqlData->Connect()) + { + try + { + // check sort methode + char aBuf[512]; + pData->m_pSqlData->m_pStatement->execute("SET @prev := NULL;"); + pData->m_pSqlData->m_pStatement->execute("SET @rank := 1;"); + pData->m_pSqlData->m_pStatement->execute("SET @pos := 0;"); + str_format(aBuf, sizeof(aBuf), "SELECT Name, Time, rank FROM (SELECT Name, (@pos := @pos+1) pos, (@rank := IF(@prev = Time,@rank, @pos)) rank, (@prev := Time) Time FROM (SELECT Name, min(Time) as Time FROM %s_race WHERE Map = '%s' GROUP BY Name ORDER BY `Time` ASC) as a) as b LIMIT %d, 5;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_Num-1); + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + + // show top5 + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "----------- Top 5 -----------"); + + int Rank = 0; + float Time = 0; + while(pData->m_pSqlData->m_pResults->next()) + { + Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); + Rank = (float)pData->m_pSqlData->m_pResults->getInt("rank"); + str_format(aBuf, sizeof(aBuf), "%d. %s Time: %02d:%05.2f", Rank, pData->m_pSqlData->m_pResults->getString("Name").c_str(), (int)(Time/60), Time-((int)Time/60*60)); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + //Rank++; + } + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "-------------------------------"); + + dbg_msg("SQL", "Showing top5 done"); + + // delete results and statement + delete pData->m_pSqlData->m_pResults; + } + catch (sql::SQLException &e) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf); + dbg_msg("SQL", "ERROR: Could not show top5"); + } + + // disconnect from database + pData->m_pSqlData->Disconnect(); + } + + delete pData; + + lock_release(gs_SqlLock); +} + +void CSqlScore::ShowTimesThread(void *pUser) +{ + lock_wait(gs_SqlLock); + CSqlScoreData *pData = (CSqlScoreData *)pUser; + + // Connect to database + if(pData->m_pSqlData->Connect()) + { + try + { + char originalName[MAX_NAME_LENGTH]; + strcpy(originalName,pData->m_aName); + pData->m_pSqlData->ClearString(pData->m_aName); + + char aBuf[512]; + + if(pData->m_Search) // last 5 times of a player + str_format(aBuf, sizeof(aBuf), "SELECT Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago, UNIX_TIMESTAMP(Timestamp) as Stamp FROM %s_race WHERE Map = '%s' AND Name = '%s' ORDER BY Ago ASC LIMIT %d, 5;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName, pData->m_Num-1); + else// last 5 times of server + str_format(aBuf, sizeof(aBuf), "SELECT Name, Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago, UNIX_TIMESTAMP(Timestamp) as Stamp FROM %s_race WHERE Map = '%s' ORDER BY Ago ASC LIMIT %d, 5;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_Num-1); + + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + + // show top5 + if(pData->m_pSqlData->m_pResults->rowsCount() == 0) + { + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "There are no times in the specified range"); + goto end; + } + + str_format(aBuf, sizeof(aBuf), "------------ Last Times No %d - %d ------------",pData->m_Num,pData->m_Num + pData->m_pSqlData->m_pResults->rowsCount() - 1); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + + float pTime = 0; + int pSince = 0; + int pStamp = 0; + + while(pData->m_pSqlData->m_pResults->next()) + { + char pAgoString[40] = "\0"; + pSince = (int)pData->m_pSqlData->m_pResults->getInt("Ago"); + pStamp = (int)pData->m_pSqlData->m_pResults->getInt("Stamp"); + pTime = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); + + agoTimeToString(pSince,pAgoString); + + if(pData->m_Search) // last 5 times of a player + { + if(pStamp == 0) // stamp is 00:00:00 cause it's an old entry from old times where there where no stamps yet + str_format(aBuf, sizeof(aBuf), "%d min %.2f sec, don't know how long ago", (int)(pTime/60), pTime-((int)pTime/60*60)); + else + str_format(aBuf, sizeof(aBuf), "%s ago, %d min %.2f sec", pAgoString,(int)(pTime/60), pTime-((int)pTime/60*60)); + } + else // last 5 times of the server + { + if(pStamp == 0) // stamp is 00:00:00 cause it's an old entry from old times where there where no stamps yet + str_format(aBuf, sizeof(aBuf), "%s, %02d:%05.02f s, don't know when", pData->m_pSqlData->m_pResults->getString("Name").c_str(), (int)(pTime/60), pTime-((int)pTime/60*60)); + else + str_format(aBuf, sizeof(aBuf), "%s, %s ago, %02d:%05.02f s", pData->m_pSqlData->m_pResults->getString("Name").c_str(), pAgoString, (int)(pTime/60), pTime-((int)pTime/60*60)); + } + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + } + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "----------------------------------------------------"); + + dbg_msg("SQL", "Showing times done"); + + // delete results and statement + delete pData->m_pSqlData->m_pResults; + } + catch (sql::SQLException &e) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf); + dbg_msg("SQL", "ERROR: Could not show times"); + } + end: + // disconnect from database + pData->m_pSqlData->Disconnect(); + } + delete pData; + + lock_release(gs_SqlLock); +} + +void CSqlScore::ShowTeamTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut) +{ + CSqlScoreData *Tmp = new CSqlScoreData(); + Tmp->m_Num = Debut; + Tmp->m_ClientID = ClientID; + Tmp->m_pSqlData = this; + + void *TeamTop5Thread = thread_create(ShowTeamTop5Thread, Tmp); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)TeamTop5Thread); +#endif +} + +void CSqlScore::ShowTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut) +{ + CSqlScoreData *Tmp = new CSqlScoreData(); + Tmp->m_Num = Debut; + Tmp->m_ClientID = ClientID; + Tmp->m_pSqlData = this; + + void *Top5Thread = thread_create(ShowTop5Thread, Tmp); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)Top5Thread); +#endif +} + +void CSqlScore::ShowTimes(int ClientID, int Debut) +{ + CSqlScoreData *Tmp = new CSqlScoreData(); + Tmp->m_Num = Debut; + Tmp->m_ClientID = ClientID; + Tmp->m_pSqlData = this; + Tmp->m_Search = false; + + void *TimesThread = thread_create(ShowTimesThread, Tmp); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)TimesThread); +#endif +} + +void CSqlScore::ShowTimes(int ClientID, const char* pName, int Debut) +{ + CSqlScoreData *Tmp = new CSqlScoreData(); + Tmp->m_Num = Debut; + Tmp->m_ClientID = ClientID; + str_copy(Tmp->m_aName, pName, MAX_NAME_LENGTH); + Tmp->m_pSqlData = this; + Tmp->m_Search = true; + + void *TimesThread = thread_create(ShowTimesThread, Tmp); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)TimesThread); +#endif +} + +void CSqlScore::FuzzyString(char *pString) +{ + char newString[32*4-1]; + int pos = 0; + + for(int i=0;i<64;i++) + { + if(!pString[i]) + break; + + newString[pos++] = pString[i]; + if (pString[i] != '\\') + newString[pos++] = '%'; + } + + newString[pos] = '\0'; + strcpy(pString, newString); +} + +// anti SQL injection +void CSqlScore::ClearString(char *pString, int size) +{ + char newString[size*2-1]; + int pos = 0; + + for(int i=0;im_pSqlData->Connect()) + { + try + { + // check strings + char originalName[MAX_NAME_LENGTH]; + strcpy(originalName,pData->m_aName); + pData->m_pSqlData->ClearString(pData->m_aName); + + pData->m_pSqlData->m_pStatement->execute("SET @prev := NULL;"); + pData->m_pSqlData->m_pStatement->execute("SET @rank := 1;"); + pData->m_pSqlData->m_pStatement->execute("SET @pos := 0;"); + + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "select Rank, Name, Points from (select (@pos := @pos+1) pos, (@rank := IF(@prev = Points,@rank,@pos)) Rank, (@prev := Points) Points, Name from (select Name, sum(Points) as Points from (select distinct Map, Name from %s_race) as l left join %s_maps on l.Map = %s_maps.Map group by Name order by sum(Points) desc) as l2) as l3 where Name = '%s';", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_pPrefix, pData->m_aName); + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + + if(pData->m_pSqlData->m_pResults->rowsCount() != 1) + { + str_format(aBuf, sizeof(aBuf), "%s has not collected any points so far", originalName); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + } + else + { + pData->m_pSqlData->m_pResults->next(); + int count = (int)pData->m_pSqlData->m_pResults->getInt("Points"); + int rank = (int)pData->m_pSqlData->m_pResults->getInt("rank"); + str_format(aBuf, sizeof(aBuf), "%d. %s Points: %d, requested by %s", rank, pData->m_pSqlData->m_pResults->getString("Name").c_str(), count, pData->m_aRequestingPlayer); + pData->m_pSqlData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID); + } + + dbg_msg("SQL", "Showing points done"); + + // delete results and statement + delete pData->m_pSqlData->m_pResults; + } + catch (sql::SQLException &e) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf); + dbg_msg("SQL", "ERROR: Could not show points"); + } + + // disconnect from database + pData->m_pSqlData->Disconnect(); + } + + delete pData; + + lock_release(gs_SqlLock); +} + +void CSqlScore::ShowPoints(int ClientID, const char* pName, bool Search) { CSqlScoreData *Tmp = new CSqlScoreData(); - Tmp->m_Num = Debut; Tmp->m_ClientID = ClientID; - str_copy(Tmp->m_aName, pName, sizeof(Tmp->m_aName)); + str_copy(Tmp->m_aName, pName, MAX_NAME_LENGTH); + Tmp->m_Search = Search; + str_format(Tmp->m_aRequestingPlayer, sizeof(Tmp->m_aRequestingPlayer), "%s", Server()->ClientName(ClientID)); Tmp->m_pSqlData = this; - Tmp->m_Search = true; - void *TimesThread = thread_create(ShowTimesThread, Tmp); + void *PointsThread = thread_create(ShowPointsThread, Tmp); #if defined(CONF_FAMILY_UNIX) - pthread_detach((pthread_t)TimesThread); + pthread_detach((pthread_t)PointsThread); #endif } -// anti SQL injection - -void CSqlScore::ClearString(char *pString) +void CSqlScore::ShowTopPointsThread(void *pUser) { - char newString[MAX_NAME_LENGTH*2-1]; - int pos = 0; + lock_wait(gs_SqlLock); + + CSqlScoreData *pData = (CSqlScoreData *)pUser; - for(int i=0;im_pSqlData->Connect()) { - if(pString[i] == '\\') + try { - newString[pos++] = '\\'; - newString[pos++] = '\\'; + char aBuf[512]; + pData->m_pSqlData->m_pStatement->execute("SET @prev := NULL;"); + pData->m_pSqlData->m_pStatement->execute("SET @rank := 1;"); + pData->m_pSqlData->m_pStatement->execute("SET @pos := 0;"); + str_format(aBuf, sizeof(aBuf), "select Rank, Name, Points from (select (@pos := @pos+1) pos, (@rank := IF(@prev = Points,@rank,@pos)) Rank, (@prev := Points) Points, Name from (select Name, sum(Points) as Points from (select distinct Map, Name from %s_race) as l left join %s_maps on l.Map = %s_maps.Map group by Name order by sum(Points) desc) as l2) as l3 LIMIT %d, 5;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_pPrefix, pData->m_Num-1); + + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + + // show top points + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "-------- Top Points --------"); + + while(pData->m_pSqlData->m_pResults->next()) + { + str_format(aBuf, sizeof(aBuf), "%d. %s Points: %d", pData->m_pSqlData->m_pResults->getInt("rank"), pData->m_pSqlData->m_pResults->getString("Name").c_str(), pData->m_pSqlData->m_pResults->getInt("Points")); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + } + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "-------------------------------"); + + dbg_msg("SQL", "Showing toppoints done"); + + // delete results and statement + delete pData->m_pSqlData->m_pResults; } - else if(pString[i] == '\'') + catch (sql::SQLException &e) { - newString[pos++] = '\\'; - newString[pos++] = '\''; + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf); + dbg_msg("SQL", "ERROR: Could not show toppoints"); } - else if(pString[i] == '"') + + // disconnect from database + pData->m_pSqlData->Disconnect(); + } + + delete pData; + + lock_release(gs_SqlLock); +} + +void CSqlScore::ShowTopPoints(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut) +{ + CSqlScoreData *Tmp = new CSqlScoreData(); + Tmp->m_Num = Debut; + Tmp->m_ClientID = ClientID; + Tmp->m_pSqlData = this; + + void *TopPointsThread = thread_create(ShowTopPointsThread, Tmp); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)TopPointsThread); +#endif +} + +void CSqlScore::RandomUnfinishedMapThread(void *pUser) +{ + lock_wait(gs_SqlLock); + + CSqlScoreData *pData = (CSqlScoreData *)pUser; + + // Connect to database + if(pData->m_pSqlData->Connect()) + { + try { - newString[pos++] = '\\'; - newString[pos++] = '"'; + char originalName[MAX_NAME_LENGTH]; + strcpy(originalName,pData->m_aName); + pData->m_pSqlData->ClearString(pData->m_aName); + + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "select * from %s_maps where Server = \"%s\" and not exists (select * from %s_race where Name = \"%s\" and %s_race.Map = %s_maps.Map) order by RAND() limit 1;", pData->m_pSqlData->m_pPrefix, g_Config.m_SvServerType, pData->m_pSqlData->m_pPrefix, pData->m_aName, pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_pPrefix); + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + + if(pData->m_pSqlData->m_pResults->rowsCount() != 1) + { + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "You have no unfinished maps on this server!"); + } + else + { + pData->m_pSqlData->m_pResults->next(); + char aMap[128]; + strcpy(aMap, pData->m_pSqlData->m_pResults->getString("Map").c_str()); + + str_format(aBuf, sizeof(aBuf), "change_map \"%s\"", aMap); + pData->m_pSqlData->GameServer()->Console()->ExecuteLine(aBuf); + } + + dbg_msg("SQL", "Voting random unfinished map done"); + + // delete results and statement + delete pData->m_pSqlData->m_pResults; } - else + catch (sql::SQLException &e) { - newString[pos++] = pString[i]; + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf); + dbg_msg("SQL", "ERROR: Could not vote random unfinished map"); } + + // disconnect from database + pData->m_pSqlData->Disconnect(); } - newString[pos] = '\0'; + delete pData; - strcpy(pString,newString); + lock_release(gs_SqlLock); +} + +void CSqlScore::RandomUnfinishedMap(int ClientID) +{ + CSqlScoreData *Tmp = new CSqlScoreData(); + Tmp->m_ClientID = ClientID; + str_copy(Tmp->m_aName, GameServer()->Server()->ClientName(ClientID), MAX_NAME_LENGTH); + Tmp->m_pSqlData = this; + + void *RandomUnfinishedThread = thread_create(RandomUnfinishedMapThread, Tmp); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)RandomUnfinishedThread); +#endif +} + +void CSqlScore::SaveTeam(int Team, const char* Code, int ClientID) +{ + CSqlTeamSave *Tmp = new CSqlTeamSave(); + Tmp->m_Team = Team; + Tmp->m_ClientID = ClientID; + str_copy(Tmp->m_Code, Code, 32); + Tmp->m_pSqlData = this; + + void *SaveThread = thread_create(SaveTeamThread, Tmp); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)SaveThread); +#endif } -void CSqlScore::NormalizeMapname(char *pString) +void CSqlScore::SaveTeamThread(void *pUser) { - std::string validChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"); + CSaveTeam* SavedTeam = 0; + CSqlTeamSave *pData = (CSqlTeamSave *)pUser; - for(int i=0;im_Team; + char OriginalCode[32]; + str_copy(OriginalCode, pData->m_Code, sizeof(OriginalCode)); + pData->m_pSqlData->ClearString(pData->m_Code, sizeof(pData->m_Code)); + char Map[128]; + str_copy(Map, g_Config.m_SvMap, 128); + pData->m_pSqlData->ClearString(Map, sizeof(Map)); + + int Num = -1; + + if(Team > 0 && Team < 64 && ((CGameControllerDDRace*)(pData->m_pSqlData->GameServer()->m_pController))->m_Teams.Count(Team) > 0) { - if(validChars.find(pString[i]) == std::string::npos) + SavedTeam = new CSaveTeam(pData->m_pSqlData->GameServer()->m_pController); + Num = SavedTeam->save(Team); + switch (Num) { - pString[i] = '_'; + case 1: + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "You have to be in a Team (from 1-63)"); + case 2: + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "Could not find your Team"); + case 3: + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "Unable to find all Characters"); + default: + ; } + str_copy(TeamString, SavedTeam->GetString(), sizeof(TeamString)); + pData->m_pSqlData->ClearString(TeamString, sizeof(TeamString)); } -} + else + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "You have to be in a Team (from 1-63)"); -void CSqlScore::agoTimeToString(int agoTime, char agoString[]) -{ - char aBuf[20]; - int times[7] = - { - 60 * 60 * 24 * 365 , - 60 * 60 * 24 * 30 , - 60 * 60 * 24 * 7, - 60 * 60 * 24 , - 60 * 60 , - 60 , - 1 - }; - char names[7][6] = + lock_wait(gs_SqlLock); + // Connect to database + if(!Num && pData->m_pSqlData->Connect()) { - "year", - "month", - "week", - "day", - "hour", - "min", - "sec" - }; + try + { + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "select Savegame from %s_saves where Code = '%s' and Map = '%s';", pData->m_pSqlData->m_pPrefix, pData->m_Code, Map); + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); - int seconds = 0; - char name[6]; - int count = 0; - int i = 0; + if (pData->m_pSqlData->m_pResults->rowsCount() == 0) + { + // delete results and statement + delete pData->m_pSqlData->m_pResults; - // finding biggest match - for(i = 0; i<7; i++) - { - seconds = times[i]; - strcpy(name,names[i]); + char aBuf[65536]; + str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %s_saves(Savegame, Map, Code, Timestamp) VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP())", pData->m_pSqlData->m_pPrefix, TeamString, Map, pData->m_Code); + pData->m_pSqlData->m_pStatement->execute(aBuf); - count = floor((float)agoTime/(float)seconds); - if(count != 0) + char aBuf2[256]; + str_format(aBuf2, sizeof(aBuf2), "Team successfully saved. Use '/load %s' to continue", OriginalCode); + pData->m_pSqlData->GameServer()->SendChatTeam(Team, aBuf2); + ((CGameControllerDDRace*)(pData->m_pSqlData->GameServer()->m_pController))->m_Teams.KillTeam(Team); + } + else + { + dbg_msg("SQL", "ERROR: This save-code already exists"); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "This save-code already exists"); + } + } + catch (sql::SQLException &e) { - break; + char aBuf2[256]; + str_format(aBuf2, sizeof(aBuf2), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf2); + dbg_msg("SQL", "ERROR: Could not save the team"); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "MySQL Error: Could not save the team"); } - } - if(count == 1) - { - str_format(aBuf, sizeof(aBuf), "%d %s", 1 , name); + // disconnect from database + pData->m_pSqlData->Disconnect(); } - else + else if(!Num) { - str_format(aBuf, sizeof(aBuf), "%d %ss", count , name); + dbg_msg("SQL", "connection failed"); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "ERROR: Unable to connect to SQL-Server"); } - strcat(agoString,aBuf); - if (i + 1 < 7) - { - // getting second piece now - int seconds2 = times[i+1]; - char name2[6]; - strcpy(name2,names[i+1]); + delete pData; + if(SavedTeam) + delete SavedTeam; - // add second piece if it's greater than 0 - int count2 = floor((float)(agoTime - (seconds * count)) / (float)seconds2); + lock_release(gs_SqlLock); +} - if (count2 != 0) +void CSqlScore::LoadTeam(const char* Code, int ClientID) +{ + CSqlTeamLoad *Tmp = new CSqlTeamLoad(); + str_copy(Tmp->m_Code, Code, 32); + Tmp->m_ClientID = ClientID; + Tmp->m_pSqlData = this; + + void *LoadThread = thread_create(LoadTeamThread, Tmp); + #if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)LoadThread); + #endif +} + +void CSqlScore::LoadTeamThread(void *pUser) +{ + CSaveTeam* SavedTeam; + CSqlTeamLoad *pData = (CSqlTeamLoad *)pUser; + + SavedTeam = new CSaveTeam(pData->m_pSqlData->GameServer()->m_pController); + + pData->m_pSqlData->ClearString(pData->m_Code, sizeof(pData->m_Code)); + char Map[128]; + str_copy(Map, g_Config.m_SvMap, 128); + pData->m_pSqlData->ClearString(Map, sizeof(Map)); + int Num; + + lock_wait(gs_SqlLock); + // Connect to database + if(pData->m_pSqlData->Connect()) + { + try { - if(count2 == 1) + char aBuf[768]; + str_format(aBuf, sizeof(aBuf), "select Savegame from %s_saves where Code = '%s' and Map = '%s';", pData->m_pSqlData->m_pPrefix, pData->m_Code, Map); + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + + if (pData->m_pSqlData->m_pResults->rowsCount() > 0) { - str_format(aBuf, sizeof(aBuf), " and %d %s", 1 , name2); + pData->m_pSqlData->m_pResults->first(); + Num = SavedTeam->LoadString(pData->m_pSqlData->m_pResults->getString("Savegame").c_str()); + + if(Num) + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "Unable to load savegame: data corrupted"); + else + { + + bool found = false; + for (int i = 0; i < SavedTeam->GetMembersCount(); i++) + { + if(str_comp(SavedTeam->SavedTees[i].GetName(), pData->m_pSqlData->Server()->ClientName(pData->m_ClientID)) == 0) + { found = true; break; } + } + if (!found) + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "You don't belong to this team"); + else + { + + int n; + for(n = 1; n<64; n++) + { + if(((CGameControllerDDRace*)(pData->m_pSqlData->GameServer()->m_pController))->m_Teams.Count(n) == 0) + break; + } + + if(((CGameControllerDDRace*)(pData->m_pSqlData->GameServer()->m_pController))->m_Teams.Count(n) > 0) + { + n = ((CGameControllerDDRace*)(pData->m_pSqlData->GameServer()->m_pController))->m_Teams.m_Core.Team(pData->m_ClientID); // if all Teams are full your the only one in your team + } + + Num = SavedTeam->load(n); + + if(Num == 1) + { + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "You have to be in a team (from 1-63)"); + } + else if(Num >= 10 && Num < 100) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "Unable to find player: '%s'", SavedTeam->SavedTees[Num-10].GetName()); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + } + else if(Num >= 100) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "%s is racing right now, Team can't be loaded if a Tee is racing already", SavedTeam->SavedTees[Num-100].GetName()); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + } + else + { + pData->m_pSqlData->GameServer()->SendChatTeam(n, "Loading successfully done"); + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "DELETE from %s_saves where Code='%s' and Map='%s';", pData->m_pSqlData->m_pPrefix, pData->m_Code, Map); + pData->m_pSqlData->m_pStatement->execute(aBuf); + } + } + } } else - { - str_format(aBuf, sizeof(aBuf), " and %d %ss", count2 , name2); - } - strcat(agoString,aBuf); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "No such savegame for this map"); + // delete results and statement + delete pData->m_pSqlData->m_pResults; + } + catch (sql::SQLException &e) + { + char aBuf2[256]; + str_format(aBuf2, sizeof(aBuf2), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf2); + dbg_msg("SQL", "ERROR: Could not load the team"); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "MySQL Error: Could not load the team"); } + + // disconnect from database + pData->m_pSqlData->Disconnect(); } + else + { + dbg_msg("SQL", "connection failed"); + pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "ERROR: Unable to connect to SQL-Server"); + } + + delete pData; + delete SavedTeam; + + lock_release(gs_SqlLock); } + #endif diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index bbd832f0a6..b3423708bf 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -8,7 +8,6 @@ #include #include -#include #include #include "../score.h" @@ -41,19 +40,30 @@ class CSqlScore: public IScore return m_pServer; } + static void MapPointsThread(void *pUser); + static void MapVoteThread(void *pUser); static void LoadScoreThread(void *pUser); static void SaveScoreThread(void *pUser); + static void SaveTeamScoreThread(void *pUser); static void ShowRankThread(void *pUser); static void ShowTop5Thread(void *pUser); + static void ShowTeamRankThread(void *pUser); + static void ShowTeamTop5Thread(void *pUser); static void ShowTimesThread(void *pUser); + static void ShowPointsThread(void *pUser); + static void ShowTopPointsThread(void *pUser); + static void RandomUnfinishedMapThread(void *pUser); + static void SaveTeamThread(void *pUser); + static void LoadTeamThread(void *pUser); void Init(); bool Connect(); void Disconnect(); + void FuzzyString(char *pString); // anti SQL injection - void ClearString(char *pString); + void ClearString(char *pString, int size = 32); void NormalizeMapname(char *pString); @@ -63,14 +73,33 @@ class CSqlScore: public IScore ~CSqlScore(); virtual void LoadScore(int ClientID); + virtual void MapPoints(int ClientID, const char* MapName); + virtual void MapVote(int ClientID, const char* MapName); virtual void SaveScore(int ClientID, float Time, float CpTime[NUM_CHECKPOINTS]); + virtual void SaveTeamScore(int* aClientIDs, unsigned int Size, float Time); virtual void ShowRank(int ClientID, const char* pName, bool Search = false); + virtual void ShowTeamRank(int ClientID, const char* pName, bool Search = false); virtual void ShowTimes(int ClientID, const char* pName, int Debut = 1); virtual void ShowTimes(int ClientID, int Debut = 1); virtual void ShowTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut = 1); - static void agoTimeToString(int agoTime, char agoStrign[]); + virtual void ShowTeamTop5(IConsole::IResult *pResult, int ClientID, + void *pUserData, int Debut = 1); + virtual void ShowPoints(int ClientID, const char* pName, bool Search = false); + virtual void ShowTopPoints(IConsole::IResult *pResult, int ClientID, + void *pUserData, int Debut = 1); + virtual void RandomUnfinishedMap(int ClientID); + virtual void SaveTeam(int Team, const char* Code, int ClientID); + virtual void LoadTeam(const char* Code, int ClientID); + static void agoTimeToString(int agoTime, char agoString[]); +}; + +struct CSqlMapData +{ + CSqlScore *m_pSqlData; + int m_ClientID; + char m_aMap[128]; }; struct CSqlScoreData @@ -90,4 +119,37 @@ struct CSqlScoreData char m_aRequestingPlayer[MAX_NAME_LENGTH]; }; +struct CSqlTeamScoreData +{ + CSqlScore *m_pSqlData; + unsigned int m_Size; + int m_aClientIDs[MAX_CLIENTS]; +#if defined(CONF_FAMILY_WINDOWS) + char m_aNames[16][MAX_CLIENTS]; // Don't edit this, or all your teeth will fall http://bugs.mysql.com/bug.php?id=50046 +#else + char m_aNames[MAX_NAME_LENGTH * 2 - 1][MAX_CLIENTS]; +#endif + + float m_Time; + float m_aCpCurrent[NUM_CHECKPOINTS]; + int m_Num; + bool m_Search; + char m_aRequestingPlayer[MAX_NAME_LENGTH]; +}; + +struct CSqlTeamSave +{ + int m_Team; + int m_ClientID; + char m_Code[128]; + CSqlScore *m_pSqlData; +}; + +struct CSqlTeamLoad +{ + char m_Code[128]; + int m_ClientID; + CSqlScore *m_pSqlData; +}; + #endif diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index ac1e102868..d5530b9e1e 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -1,6 +1,7 @@ /* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */ #include "teams.h" #include +#include CGameTeams::CGameTeams(CGameContext *pGameContext) : m_pGameContext(pGameContext) @@ -17,6 +18,7 @@ void CGameTeams::Reset() m_TeeFinished[i] = false; m_MembersCount[i] = 0; m_LastChat[i] = 0; + m_TeamLocked[i] = false; } } @@ -26,8 +28,8 @@ void CGameTeams::OnCharacterStart(int ClientID) CCharacter* pStartingChar = Character(ClientID); if (!pStartingChar) return; - if (pStartingChar->m_DDRaceState == DDRACE_FINISHED) - pStartingChar->m_DDRaceState = DDRACE_NONE; + if (m_Core.Team(ClientID) != TEAM_FLOCK && pStartingChar->m_DDRaceState == DDRACE_FINISHED) + return; if (m_Core.Team(ClientID) == TEAM_FLOCK || m_Core.Team(ClientID) == TEAM_SUPER) { @@ -47,6 +49,7 @@ void CGameTeams::OnCharacterStart(int ClientID) { Waiting = true; pStartingChar->m_DDRaceState = DDRACE_NONE; + if (m_LastChat[ClientID] + Server()->TickSpeed() + g_Config.m_SvChatDelay < Tick) { @@ -83,7 +86,8 @@ void CGameTeams::OnCharacterStart(int ClientID) if (m_Core.Team(ClientID) == m_Core.Team(i)) { CPlayer* pPlayer = GetPlayer(i); - if (pPlayer && pPlayer->IsPlaying()) + // TODO: THE PROBLEM IS THAT THERE IS NO CHARACTER SO START TIME CAN'T BE SET! + if (pPlayer && (pPlayer->IsPlaying() || TeamLocked(m_Core.Team(ClientID)))) { SetDDRaceState(pPlayer, DDRACE_STARTED); SetStartTime(pPlayer, Tick); @@ -110,6 +114,10 @@ void CGameTeams::OnCharacterFinish(int ClientID) { ChangeTeamState(m_Core.Team(ClientID), TEAMSTATE_FINISHED); //TODO: Make it better //ChangeTeamState(m_Core.Team(ClientID), TEAMSTATE_OPEN); + + CPlayer *TeamPlayers[MAX_CLIENTS]; + unsigned int PlayersCount = 0; + for (int i = 0; i < MAX_CLIENTS; ++i) { if (m_Core.Team(ClientID) == m_Core.Team(i)) @@ -119,10 +127,14 @@ void CGameTeams::OnCharacterFinish(int ClientID) { OnFinish(pPlayer); m_TeeFinished[i] = false; + + TeamPlayers[PlayersCount++] = pPlayer; } } } + OnTeamFinish(TeamPlayers, PlayersCount); + } } } @@ -139,18 +151,15 @@ bool CGameTeams::SetCharacterTeam(int ClientID, int Team) //No need to switch team if you there if (m_Core.Team(ClientID) == Team) return false; + if (!Character(ClientID)) + return false; //You cannot be in TEAM_SUPER if you not super if (Team == TEAM_SUPER && !Character(ClientID)->m_Super) return false; //if you begin race - if (Character(ClientID)->m_DDRaceState != DDRACE_NONE) - { - //you will be killed if you try to join FLOCK - if (Team == TEAM_FLOCK && m_Core.Team(ClientID) != TEAM_FLOCK) - GetPlayer(ClientID)->KillCharacter(WEAPON_GAME); - else if (Team != TEAM_SUPER) - return false; - } + if (Character(ClientID)->m_DDRaceState != DDRACE_NONE && Team != TEAM_SUPER) + return false; + SetForceCharacterTeam(ClientID, Team); //GameServer()->CreatePlayerSpawn(Character(id)->m_Core.m_Pos, TeamMask()); @@ -158,8 +167,40 @@ bool CGameTeams::SetCharacterTeam(int ClientID, int Team) } void CGameTeams::SetForceCharacterTeam(int ClientID, int Team) +{ + int OldTeam = m_Core.Team(ClientID); + + ForceLeaveTeam(ClientID); + + m_Core.Team(ClientID, Team); + + if (m_Core.Team(ClientID) != TEAM_SUPER) + m_MembersCount[m_Core.Team(ClientID)]++; + if (Team != TEAM_SUPER && (m_TeamState[Team] == TEAMSTATE_EMPTY || m_TeamLocked[Team])) + { + if (!m_TeamLocked[Team]) + ChangeTeamState(Team, TEAMSTATE_OPEN); + + if (GameServer()->Collision()->m_NumSwitchers > 0) { + for (int i = 0; i < GameServer()->Collision()->m_NumSwitchers+1; ++i) + { + GameServer()->Collision()->m_pSwitchers[i].m_Status[Team] = true; + GameServer()->Collision()->m_pSwitchers[i].m_EndTick[Team] = 0; + GameServer()->Collision()->m_pSwitchers[i].m_Type[Team] = TILE_SWITCHOPEN; + } + } + } + + if (OldTeam != Team) + for (int LoopClientID = 0; LoopClientID < MAX_CLIENTS; ++LoopClientID) + if (GetPlayer(LoopClientID)) + SendTeamsState(LoopClientID); +} + +void CGameTeams::ForceLeaveTeam(int ClientID) { m_TeeFinished[ClientID] = false; + if (m_Core.Team(ClientID) != TEAM_FLOCK && m_Core.Team(ClientID) != TEAM_SUPER && m_TeamState[m_Core.Team(ClientID)] != TEAMSTATE_EMPTY) @@ -172,22 +213,16 @@ void CGameTeams::SetForceCharacterTeam(int ClientID, int Team) break; } if (NoOneInOldTeam) + { m_TeamState[m_Core.Team(ClientID)] = TEAMSTATE_EMPTY; + + // unlock team when last player leaves + SetTeamLock(m_Core.Team(ClientID), false); + } } + if (Count(m_Core.Team(ClientID)) > 0) m_MembersCount[m_Core.Team(ClientID)]--; - m_Core.Team(ClientID, Team); - if (m_Core.Team(ClientID) != TEAM_SUPER) - m_MembersCount[m_Core.Team(ClientID)]++; - if (Team != TEAM_SUPER && m_TeamState[Team] == TEAMSTATE_EMPTY) - ChangeTeamState(Team, TEAMSTATE_OPEN); - - for (int LoopClientID = 0; LoopClientID < MAX_CLIENTS; ++LoopClientID) - { - if (GetPlayer(LoopClientID) - && GetPlayer(LoopClientID)->m_IsUsingDDRaceClient) - SendTeamsState(LoopClientID); - } } int CGameTeams::Count(int Team) const @@ -224,48 +259,71 @@ bool CGameTeams::TeamFinished(int Team) return true; } -int CGameTeams::TeamMask(int Team, int ExceptID, int Asker) +int64_t CGameTeams::TeamMask(int Team, int ExceptID, int Asker) { - if (Team == TEAM_SUPER) - return -1; - if (m_Core.GetSolo(Asker) && ExceptID == Asker) - return 0; - if (m_Core.GetSolo(Asker)) - return 1 << Asker; - int Mask = 0; + int64_t Mask = 0; + for (int i = 0; i < MAX_CLIENTS; ++i) - if (i != ExceptID) - if ((Asker == i || !m_Core.GetSolo(i)) - && ((Character(i) - && (m_Core.Team(i) == Team - || m_Core.Team(i) == TEAM_SUPER)) - || (GetPlayer(i) && GetPlayer(i)->GetTeam() == -1))) - Mask |= 1 << i; + { + if (i == ExceptID) + continue; // Explicitly excluded + if (!GetPlayer(i)) + continue; // Player doesn't exist + + if (!(GetPlayer(i)->GetTeam() == -1 || GetPlayer(i)->m_Paused)) + { // Not spectator + if (i != Asker) + { // Actions of other players + if (!Character(i)) + continue; // Player is currently dead + if (!GetPlayer(i)->m_ShowOthers) + { + if (m_Core.GetSolo(Asker)) + continue; // When in solo part don't show others + if (m_Core.GetSolo(i)) + continue; // When in solo part don't show others + if (m_Core.Team(i) != Team && m_Core.Team(i) != TEAM_SUPER) + continue; // In different teams + } // ShowOthers + } // See everything of yourself + } + else if (GetPlayer(i)->m_SpectatorID != SPEC_FREEVIEW) + { // Spectating specific player + if (GetPlayer(i)->m_SpectatorID != Asker) + { // Actions of other players + if (!Character(GetPlayer(i)->m_SpectatorID)) + continue; // Player is currently dead + if (!GetPlayer(i)->m_ShowOthers) + { + if (m_Core.GetSolo(Asker)) + continue; // When in solo part don't show others + if (m_Core.GetSolo(GetPlayer(i)->m_SpectatorID)) + continue; // When in solo part don't show others + if (m_Core.Team(GetPlayer(i)->m_SpectatorID) != Team && m_Core.Team(GetPlayer(i)->m_SpectatorID) != TEAM_SUPER) + continue; // In different teams + } // ShowOthers + } // See everything of player you're spectating + } // Freeview sees all + + Mask |= 1LL << i; + } return Mask; } void CGameTeams::SendTeamsState(int ClientID) { - CNetMsg_Cl_TeamsState Msg; - Msg.m_Tee0 = m_Core.Team(0); - Msg.m_Tee1 = m_Core.Team(1); - Msg.m_Tee2 = m_Core.Team(2); - Msg.m_Tee3 = m_Core.Team(3); - Msg.m_Tee4 = m_Core.Team(4); - Msg.m_Tee5 = m_Core.Team(5); - Msg.m_Tee6 = m_Core.Team(6); - Msg.m_Tee7 = m_Core.Team(7); - Msg.m_Tee8 = m_Core.Team(8); - Msg.m_Tee9 = m_Core.Team(9); - Msg.m_Tee10 = m_Core.Team(10); - Msg.m_Tee11 = m_Core.Team(11); - Msg.m_Tee12 = m_Core.Team(12); - Msg.m_Tee13 = m_Core.Team(13); - Msg.m_Tee14 = m_Core.Team(14); - Msg.m_Tee15 = m_Core.Team(15); - - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientID); + if (g_Config.m_SvTeam == 3) + return; + + if (!m_pGameContext->m_apPlayers[ClientID] || m_pGameContext->m_apPlayers[ClientID]->m_ClientVersion <= VERSION_DDRACE) + return; + + CMsgPacker Msg(NETMSGTYPE_SV_TEAMSSTATE); + + for(unsigned i = 0; i < MAX_CLIENTS; i++) + Msg.AddInt(m_Core.Team(i)); + Server()->SendMsg(&Msg, MSGFLAG_VITAL, ClientID); } int CGameTeams::GetDDRaceState(CPlayer* Player) @@ -331,6 +389,33 @@ float *CGameTeams::GetCpCurrent(CPlayer* Player) return NULL; } +void CGameTeams::OnTeamFinish(CPlayer** Players, unsigned int Size) +{ + float time = (float) (Server()->Tick() - GetStartTime(Players[0])) + / ((float) Server()->TickSpeed()); + if (time < 0.000001f) + return; + + bool CallSaveScore = false; + +#if defined(CONF_SQL) + CallSaveScore = g_Config.m_SvUseSQL; +#endif + + int PlayerCIDs[MAX_CLIENTS]; + + for(unsigned int i = 0; i < Size; i++) + { + PlayerCIDs[i] = Players[i]->GetCID(); + + if(g_Config.m_SvRejoinTeam0 && g_Config.m_SvTeam != 3 && (m_Core.Team(Players[i]->GetCID()) >= TEAM_SUPER || !m_TeamLocked[m_Core.Team(Players[i]->GetCID())])) + SetForceCharacterTeam(Players[i]->GetCID(), 0); + } + + if (CallSaveScore && Size >= 2) + GameServer()->Score()->SaveTeamScore(PlayerCIDs, Size, time); +} + void CGameTeams::OnFinish(CPlayer* Player) { if (!Player || !Player->IsPlaying()) @@ -347,40 +432,50 @@ void CGameTeams::OnFinish(CPlayer* Player) "%s finished in: %d minute(s) %5.2f second(s)", Server()->ClientName(Player->GetCID()), (int) time / 60, time - ((int) time / 60 * 60)); - if (g_Config.m_SvHideScore) + if (g_Config.m_SvHideScore || !g_Config.m_SvSaveWorseScores) GameServer()->SendChatTarget(Player->GetCID(), aBuf); else GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + float diff = fabs(time - pData->m_BestTime); + if (time - pData->m_BestTime < 0) { // new record \o/ - str_format(aBuf, sizeof(aBuf), "New record: %5.2f second(s) better.", - fabs(time - pData->m_BestTime)); - if (g_Config.m_SvHideScore) + if (diff >= 60) + str_format(aBuf, sizeof(aBuf), "New record: %d minute(s) %5.2f second(s) better.", + (int) diff / 60, diff - ((int) diff / 60 * 60)); + else + str_format(aBuf, sizeof(aBuf), "New record: %5.2f second(s) better.", + diff); + if (g_Config.m_SvHideScore || !g_Config.m_SvSaveWorseScores) GameServer()->SendChatTarget(Player->GetCID(), aBuf); else GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); } else if (pData->m_BestTime != 0) // tee has already finished? { - if (fabs(time - pData->m_BestTime) <= 0.005) + if (diff <= 0.005) { GameServer()->SendChatTarget(Player->GetCID(), "You finished with your best time."); } else { - str_format(aBuf, sizeof(aBuf), - "%5.2f second(s) worse, better luck next time.", - fabs(pData->m_BestTime - time)); + if (diff >= 60) + str_format(aBuf, sizeof(aBuf), "%d minute(s) %5.2f second(s) worse, better luck next time.", + (int) diff / 60, diff - ((int) diff / 60 * 60)); + else + str_format(aBuf, sizeof(aBuf), + "%5.2f second(s) worse, better luck next time.", + diff); GameServer()->SendChatTarget(Player->GetCID(), aBuf); //this is private, sent only to the tee } } bool CallSaveScore = false; #if defined(CONF_SQL) - CallSaveScore = g_Config.m_SvUseSQL; + CallSaveScore = g_Config.m_SvUseSQL && g_Config.m_SvSaveWorseScores; #endif if (!pData->m_BestTime || time < pData->m_BestTime) @@ -419,7 +514,7 @@ void CGameTeams::OnFinish(CPlayer* Player) NeedToSendNewRecord = true; for (int i = 0; i < MAX_CLIENTS; i++) { - if (GetPlayer(i) && GetPlayer(i)->m_IsUsingDDRaceClient) + if (GetPlayer(i) && GetPlayer(i)->m_ClientVersion >= VERSION_DDRACE) { if (!g_Config.m_SvHideScore || i == Player->GetCID()) { @@ -432,19 +527,19 @@ void CGameTeams::OnFinish(CPlayer* Player) } } - if (NeedToSendNewRecord && Player->m_IsUsingDDRaceClient) + if (NeedToSendNewRecord && Player->m_ClientVersion >= VERSION_DDRACE) { for (int i = 0; i < MAX_CLIENTS; i++) { if (GameServer()->m_apPlayers[i] - && GameServer()->m_apPlayers[i]->m_IsUsingDDRaceClient) + && GameServer()->m_apPlayers[i]->m_ClientVersion >= VERSION_DDRACE) { GameServer()->SendRecord(i); } } } - if (Player->m_IsUsingDDRaceClient) + if (Player->m_ClientVersion >= VERSION_DDRACE) { CNetMsg_Sv_DDRaceTime Msg; Msg.m_Time = (int) (time * 100.0f); @@ -469,11 +564,43 @@ void CGameTeams::OnFinish(CPlayer* Player) void CGameTeams::OnCharacterSpawn(int ClientID) { m_Core.SetSolo(ClientID, false); - SetForceCharacterTeam(ClientID, 0); + + if (m_Core.Team(ClientID) >= TEAM_SUPER || !m_TeamLocked[m_Core.Team(ClientID)]) + SetForceCharacterTeam(ClientID, 0); } -void CGameTeams::OnCharacterDeath(int ClientID) +void CGameTeams::OnCharacterDeath(int ClientID, int Weapon) { m_Core.SetSolo(ClientID, false); - SetForceCharacterTeam(ClientID, 0); + + int Team = m_Core.Team(ClientID); + bool Locked = TeamLocked(Team) && Weapon != WEAPON_GAME; + + if (!Locked) + SetForceCharacterTeam(ClientID, 0); + else + { + SetForceCharacterTeam(ClientID, Team); + + if (GetTeamState(Team) != TEAMSTATE_OPEN) + for (int i = 0; i < MAX_CLIENTS; i++) + if(m_Core.Team(i) == Team && i != ClientID && GameServer()->m_apPlayers[i]) + GameServer()->m_apPlayers[i]->KillCharacter(-2); + + ChangeTeamState(Team, CGameTeams::TEAMSTATE_OPEN); + } +} + +void CGameTeams::SetTeamLock(int Team, bool Lock) +{ + m_TeamLocked[Team] = Lock; +} + +void CGameTeams::KillTeam(int Team) +{ + for (int i = 0; i < MAX_CLIENTS; i++) + if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i]) + GameServer()->m_apPlayers[i]->KillCharacter(-2); + + ChangeTeamState(Team, CGameTeams::TEAMSTATE_OPEN); } diff --git a/src/game/server/teams.h b/src/game/server/teams.h index 8146fcfe44..eaac1d2fd7 100644 --- a/src/game/server/teams.h +++ b/src/game/server/teams.h @@ -10,6 +10,7 @@ class CGameTeams int m_TeamState[MAX_CLIENTS]; int m_MembersCount[MAX_CLIENTS]; bool m_TeeFinished[MAX_CLIENTS]; + bool m_TeamLocked[MAX_CLIENTS]; class CGameContext * m_pGameContext; @@ -45,7 +46,7 @@ class CGameTeams void OnCharacterStart(int ClientID); void OnCharacterFinish(int ClientID); void OnCharacterSpawn(int ClientID); - void OnCharacterDeath(int ClientID); + void OnCharacterDeath(int ClientID, int Weapon); bool SetCharacterTeam(int ClientID, int Team); @@ -54,16 +55,18 @@ class CGameTeams bool TeamFinished(int Team); - int TeamMask(int Team, int ExceptID = -1, int Asker = -1); + int64_t TeamMask(int Team, int ExceptID = -1, int Asker = -1); int Count(int Team) const; //need to be very carefull using this method void SetForceCharacterTeam(int id, int Team); + void ForceLeaveTeam(int id); void Reset(); void SendTeamsState(int Cid); + void SetTeamLock(int Team, bool Lock); int m_LastChat[MAX_CLIENTS]; @@ -74,7 +77,9 @@ class CGameTeams void SetStartTime(CPlayer* Player, int StartTime); void SetRefreshTime(CPlayer* Player, int RefreshTime); void SetCpActive(CPlayer* Player, int CpActive); + void OnTeamFinish(CPlayer** Players, unsigned int Size); void OnFinish(CPlayer* Player); + void KillTeam(int Team); bool TeeFinished(int ClientID) { return m_TeeFinished[ClientID]; @@ -85,6 +90,19 @@ class CGameTeams return m_TeamState[Team]; } ; + bool TeamLocked(int Team) + { + if (Team <= TEAM_FLOCK || Team >= TEAM_SUPER) + return false; + + return m_TeamLocked[Team]; + } + ; + void SetFinished(int ClientID, bool finished) + { + m_TeeFinished[ClientID] = finished; + } + ; }; #endif diff --git a/src/game/teamscore.cpp b/src/game/teamscore.cpp index 8f8c6a48e4..9e0047f0dd 100644 --- a/src/game/teamscore.cpp +++ b/src/game/teamscore.cpp @@ -23,7 +23,7 @@ void CTeamsCore::Team(int ClientID, int Team) bool CTeamsCore::CanCollide(int ClientID1, int ClientID2) { - if (m_Team[ClientID1] == TEAM_SUPER || m_Team[ClientID2] == TEAM_SUPER + if (m_Team[ClientID1] == (m_IsDDRace16 ? VANILLA_TEAM_SUPER : TEAM_SUPER) || m_Team[ClientID2] == (m_IsDDRace16 ? VANILLA_TEAM_SUPER : TEAM_SUPER) || ClientID1 == ClientID2) return true; if (m_IsSolo[ClientID1] || m_IsSolo[ClientID2]) @@ -33,6 +33,8 @@ bool CTeamsCore::CanCollide(int ClientID1, int ClientID2) void CTeamsCore::Reset() { + m_IsDDRace16 = false; + for (int i = 0; i < MAX_CLIENTS; ++i) { m_Team[i] = TEAM_FLOCK; diff --git a/src/game/teamscore.h b/src/game/teamscore.h index 935b589120..bd2587df04 100644 --- a/src/game/teamscore.h +++ b/src/game/teamscore.h @@ -6,7 +6,7 @@ enum { - TEAM_FLOCK = 0, TEAM_SUPER = 16 + TEAM_FLOCK = 0, TEAM_SUPER = MAX_CLIENTS, VANILLA_TEAM_SUPER = VANILLA_MAX_CLIENTS }; class CTeamsCore @@ -15,6 +15,7 @@ class CTeamsCore int m_Team[MAX_CLIENTS]; bool m_IsSolo[MAX_CLIENTS]; public: + bool m_IsDDRace16; CTeamsCore(void); diff --git a/src/game/tuning.h b/src/game/tuning.h index 5bdb576e03..53b8582d65 100644 --- a/src/game/tuning.h +++ b/src/game/tuning.h @@ -39,10 +39,16 @@ MACRO_TUNING_PARAM(GrenadeLifetime, grenade_lifetime, 2.0f) MACRO_TUNING_PARAM(LaserReach, laser_reach, 800.0f) MACRO_TUNING_PARAM(LaserBounceDelay, laser_bounce_delay, 150) -MACRO_TUNING_PARAM(LaserBounceNum, laser_bounce_num, 1) +MACRO_TUNING_PARAM(LaserBounceNum, laser_bounce_num, 1000) MACRO_TUNING_PARAM(LaserBounceCost, laser_bounce_cost, 0) MACRO_TUNING_PARAM(LaserDamage, laser_damage, 5) MACRO_TUNING_PARAM(PlayerCollision, player_collision, 1) MACRO_TUNING_PARAM(PlayerHooking, player_hooking, 1) + +//ddnet tuning +MACRO_TUNING_PARAM(JetpackStrength, jetpack_strength, 400.0f) +MACRO_TUNING_PARAM(ShotgunStrength, shotgun_strength, 10.0f) +MACRO_TUNING_PARAM(ExplosionStrength, explosion_strength, 6.0f) +MACRO_TUNING_PARAM(HammerStrength, hammer_strength, 1.0f) #endif diff --git a/src/game/variables.h b/src/game/variables.h index 88a7c9db0f..249823d413 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -7,14 +7,28 @@ // client MACRO_CONFIG_INT(ClPredict, cl_predict, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Predict client movements") +MACRO_CONFIG_INT(ClAntiPing, cl_antiping, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Antiping (predict other players' movements)") +MACRO_CONFIG_INT(ClAntiPingGrenade, cl_antiping_grenade, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Antiping (predict grenades)") MACRO_CONFIG_INT(ClNameplates, cl_nameplates, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show name plates") MACRO_CONFIG_INT(ClNameplatesAlways, cl_nameplates_always, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Always show name plates disregarding of distance") MACRO_CONFIG_INT(ClNameplatesTeamcolors, cl_nameplates_teamcolors, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Use team colors for name plates") MACRO_CONFIG_INT(ClNameplatesSize, cl_nameplates_size, 50, 0, 100, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Size of the name plates from 0 to 100%") +#if defined(__ANDROID__) +MACRO_CONFIG_INT(ClAutoswitchWeapons, cl_autoswitch_weapons, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Auto switch weapon on pickup") +MACRO_CONFIG_INT(ClAutoswitchWeaponsOutOfAmmo, cl_autoswitch_weapons_out_of_ammo, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Auto switch weapon when out of ammo") +#else MACRO_CONFIG_INT(ClAutoswitchWeapons, cl_autoswitch_weapons, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Auto switch weapon on pickup") +MACRO_CONFIG_INT(ClAutoswitchWeaponsOutOfAmmo, cl_autoswitch_weapons_out_of_ammo, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Auto switch weapon when out of ammo") +#endif MACRO_CONFIG_INT(ClShowhud, cl_showhud, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show ingame HUD") +MACRO_CONFIG_INT(ClShowhudHealthAmmo, cl_showhud_healthammo, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show ingame HUD (Health + Ammo)") +MACRO_CONFIG_INT(ClShowhudScore, cl_showhud_score, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show ingame HUD (Score)") +MACRO_CONFIG_INT(ClShowRecord, cl_showrecord, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show old style DDRace client records") +MACRO_CONFIG_INT(ClShowChat, cl_showchat, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show chat") MACRO_CONFIG_INT(ClShowChatFriends, cl_show_chat_friends, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show only chat messages from friends") +MACRO_CONFIG_INT(ClShowKillMessages, cl_showkillmessages, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show kill messages") +MACRO_CONFIG_INT(ClShowVotesAfterVoting, cl_show_votes_after_voting, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show votes window after voting") MACRO_CONFIG_INT(ClShowfps, cl_showfps, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show ingame FPS counter") MACRO_CONFIG_INT(ClAirjumpindicator, cl_airjumpindicator, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") @@ -22,9 +36,17 @@ MACRO_CONFIG_INT(ClThreadsoundloading, cl_threadsoundloading, 0, 0, 1, CFGFLAG_C MACRO_CONFIG_INT(ClWarningTeambalance, cl_warning_teambalance, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Warn about team balance") +#if defined(__ANDROID__) +MACRO_CONFIG_INT(ClMouseDeadzone, cl_mouse_deadzone, 800, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") // Disable dynamic camera on Android, screen becomes jerky when you tap joystick +#else MACRO_CONFIG_INT(ClMouseDeadzone, cl_mouse_deadzone, 300, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") +#endif MACRO_CONFIG_INT(ClMouseFollowfactor, cl_mouse_followfactor, 60, 0, 200, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") +#if defined(__ANDROID__) +MACRO_CONFIG_INT(ClMouseMaxDistance, cl_mouse_max_distance, 400, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") // Prevent crosshair from moving out of screen on Android +#else MACRO_CONFIG_INT(ClMouseMaxDistance, cl_mouse_max_distance, 800, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") +#endif MACRO_CONFIG_INT(EdShowkeys, ed_showkeys, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") @@ -34,8 +56,11 @@ MACRO_CONFIG_INT(ClShowWelcome, cl_show_welcome, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG 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_STR(ClVersionServer, cl_version_server, 100, "version.teeworlds.com", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Server to use to check for new versions") +MACRO_CONFIG_STR(ClDDNetVersionServer, cl_ddnet_version_server, 100, "version.ddnet.tw", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Server to use to check for new ddnet versions") +MACRO_CONFIG_STR(ClDDNetUpdateServer, cl_ddnet_update_server, 100, "update.ddnet.tw", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Server to use to update new ddnet versions") MACRO_CONFIG_STR(ClLanguagefile, cl_languagefile, 255, "", CFGFLAG_CLIENT|CFGFLAG_SAVE, "What language file to use") +MACRO_CONFIG_INT(ClShowSpecialSkins, cl_show_special_skins, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show special skins in UI") MACRO_CONFIG_INT(PlayerUseCustomColor, player_use_custom_color, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Toggles usage of custom colors") MACRO_CONFIG_INT(PlayerColorBody, player_color_body, 65408, 0, 0xFFFFFF, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Player body color") @@ -53,8 +78,23 @@ MACRO_CONFIG_INT(UiColorSat, ui_color_sat, 70, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SA MACRO_CONFIG_INT(UiColorLht, ui_color_lht, 175, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color lightness") MACRO_CONFIG_INT(UiColorAlpha, ui_color_alpha, 228, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface alpha") +MACRO_CONFIG_INT(UiColorizePing, ui_colorize_ping, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Highlight ping") +MACRO_CONFIG_INT(UiColorizeGametype, ui_colorize_gametype, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Highlight gametype") + MACRO_CONFIG_INT(GfxNoclip, gfx_noclip, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Disable clipping") +// dummy +MACRO_CONFIG_STR(DummyName, dummy_name, 16, "brainless tee", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Name of the Dummy") +MACRO_CONFIG_STR(DummyClan, dummy_clan, 12, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Clan of the Dummy") +MACRO_CONFIG_INT(DummyCountry, dummy_country, -1, -1, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Country of the Dummy") +MACRO_CONFIG_INT(DummyUseCustomColor, dummy_use_custom_color, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Toggles usage of custom colors") +MACRO_CONFIG_INT(DummyColorBody, dummy_color_body, 65408, 0, 0xFFFFFF, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Dummy body color") +MACRO_CONFIG_INT(DummyColorFeet, dummy_color_feet, 65408, 0, 0xFFFFFF, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Dummy feet color") +MACRO_CONFIG_STR(DummySkin, dummy_skin, 24, "default", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Dummy skin") +MACRO_CONFIG_INT(ClDummy, cl_dummy, 0, 0, 1, CFGFLAG_CLIENT, "0 - player / 1 - dummy") +MACRO_CONFIG_INT(ClDummyHammer, cl_dummy_hammer, 0, 0, 1, CFGFLAG_CLIENT, "Whether dummy is hammering for a hammerfly") +MACRO_CONFIG_INT(ClDummyResetOnSwitch, cl_dummy_resetonswitch, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Whether dummy should stop pressing keys when you switch") + // server MACRO_CONFIG_INT(SvWarmup, sv_warmup, 0, 0, 0, CFGFLAG_SERVER, "Number of seconds to do warmup before round starts") MACRO_CONFIG_STR(SvMotd, sv_motd, 900, "", CFGFLAG_SERVER, "Message of the day to display for the clients") @@ -82,6 +122,15 @@ MACRO_CONFIG_INT(SvVoteSpectateRejoindelay, sv_vote_spectate_rejoindelay, 3, 0, MACRO_CONFIG_INT(SvVoteKick, sv_vote_kick, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to kick players") MACRO_CONFIG_INT(SvVoteKickMin, sv_vote_kick_min, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "Minimum number of players required to start a kick vote") MACRO_CONFIG_INT(SvVoteKickBantime, sv_vote_kick_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time to ban a player if kicked by vote. 0 makes it just use kick") +MACRO_CONFIG_INT(SvOldTeleportWeapons, sv_old_teleport_weapons, 0, 0, 1, CFGFLAG_SERVER, "Teleporting of all weapons (deprecated, use special entities instead)"); +MACRO_CONFIG_INT(SvOldTeleportHook, sv_old_teleport_hook, 0, 0, 1, CFGFLAG_SERVER, "Hook through teleporter (deprecated, use special entities instead)"); +MACRO_CONFIG_INT(SvTeleportHoldHook, sv_teleport_hold_hook, 0, 0, 1, CFGFLAG_SERVER, "Hold hook when teleported"); + +MACRO_CONFIG_INT(SvMapUpdateRate, sv_mapupdaterate, 5, 1, 100, CFGFLAG_SERVER, "(Tw32) real id <-> vanilla id players map update rate") + +MACRO_CONFIG_INT(SvSkinStealAction, sv_skinstealaction, 0, 0, 1, CFGFLAG_SERVER, "How to punish skin stealing (currently only 1 = force pinky)") + +MACRO_CONFIG_STR(SvServerType, sv_server_type, 64, "none", CFGFLAG_SERVER, "Type of the server (novice, moderate, ...)") // debug #ifdef CONF_DEBUG // this one can crash the server if not used correctly diff --git a/src/game/version.h b/src/game/version.h index bfbed053da..df08c8e0b5 100644 --- a/src/game/version.h +++ b/src/game/version.h @@ -3,7 +3,8 @@ #ifndef GAME_VERSION_H #define GAME_VERSION_H #include "generated/nethash.cpp" -#define GAME_VERSION "0.6 trunk, 1.15a" +#define GAME_VERSION "0.6.2, 4.6.2" #define GAME_NETVERSION "0.6 626fce9a778df4d4" -static const char GAME_RELEASE_VERSION[8] = {'0', '.', '6', '1', 0}; +static const char GAME_RELEASE_VERSION[8] = "4.6.2"; +#define CLIENT_VERSIONNR 406 #endif diff --git a/src/game/voting.h b/src/game/voting.h index e1706cafb2..61d86143d4 100644 --- a/src/game/voting.h +++ b/src/game/voting.h @@ -9,7 +9,7 @@ enum VOTE_CMD_LENGTH=512, VOTE_REASON_LENGTH=16, - MAX_VOTE_OPTIONS=128, + MAX_VOTE_OPTIONS=8192, }; struct CVoteOptionClient diff --git a/src/mastersrv/mastersrv.cpp b/src/mastersrv/mastersrv.cpp index c88a9e78c4..1098123b4d 100644 --- a/src/mastersrv/mastersrv.cpp +++ b/src/mastersrv/mastersrv.cpp @@ -348,7 +348,11 @@ int main(int argc, const char **argv) // ignore_convention m_pConsole->ParseArguments(argc-1, &argv[1]); // ignore_convention if(g_Config.m_Bindaddr[0] && net_host_lookup(g_Config.m_Bindaddr, &BindAddr, NETTYPE_ALL) == 0) + { + // got bindaddr + BindAddr.type = NETTYPE_ALL; BindAddr.port = MASTERSERVER_PORT; + } else { mem_zero(&BindAddr, sizeof(BindAddr)); @@ -368,6 +372,9 @@ int main(int argc, const char **argv) // ignore_convention return -1; } + // process pending commands + m_pConsole->StoreCommands(false); + dbg_msg("mastersrv", "started"); while(1) diff --git a/src/mastersrv/mastersrv.h b/src/mastersrv/mastersrv.h index 38e5ab2779..ae9a0c2afc 100644 --- a/src/mastersrv/mastersrv.h +++ b/src/mastersrv/mastersrv.h @@ -28,6 +28,9 @@ static const unsigned char SERVERBROWSE_COUNT[] = {255, 255, 255, 255, 's', 'i', static const unsigned char SERVERBROWSE_GETINFO[] = {255, 255, 255, 255, 'g', 'i', 'e', '3'}; static const unsigned char SERVERBROWSE_INFO[] = {255, 255, 255, 255, 'i', 'n', 'f', '3'}; +static const unsigned char SERVERBROWSE_GETINFO64[] = {255, 255, 255, 255, 'f', 's', 't', 'd'}; +static const unsigned char SERVERBROWSE_INFO64[] = {255, 255, 255, 255, 'd', 't', 's', 'f'}; + static const unsigned char SERVERBROWSE_FWCHECK[] = {255, 255, 255, 255, 'f', 'w', '?', '?'}; static const unsigned char SERVERBROWSE_FWRESPONSE[] = {255, 255, 255, 255, 'f', 'w', '!', '!'}; static const unsigned char SERVERBROWSE_FWOK[] = {255, 255, 255, 255, 'f', 'w', 'o', 'k'}; diff --git a/src/osxlaunch/server.m b/src/osxlaunch/server.m index 1aff6bc5d9..e542ff16fe 100644 --- a/src/osxlaunch/server.m +++ b/src/osxlaunch/server.m @@ -83,7 +83,7 @@ void runServer() backing: NSBackingStoreBuffered defer: NO]; - [window setTitle: @"DDRace Server"]; + [window setTitle: @"DDNet Server"]; view = [[[ServerView alloc] initWithFrame: graphicsRect] autorelease]; [view setEditable: NO]; @@ -94,7 +94,7 @@ void runServer() [window makeKeyAndOrderFront: nil]; [view listenTo: task]; - [task setLaunchPath: [mainBundle pathForAuxiliaryExecutable: @"DDRace-Server"]]; + [task setLaunchPath: [mainBundle pathForAuxiliaryExecutable: @"DDNet-Server"]]; [task setArguments: arguments]; [task launch]; [NSApp run]; diff --git a/src/osxlaunch/server_mysql.m b/src/osxlaunch/server_mysql.m index 89430cd931..1a553d7d68 100644 --- a/src/osxlaunch/server_mysql.m +++ b/src/osxlaunch/server_mysql.m @@ -83,7 +83,7 @@ void runServer() backing: NSBackingStoreBuffered defer: NO]; - [window setTitle: @"DDRace Server"]; + [window setTitle: @"DDNet Server"]; view = [[[ServerView alloc] initWithFrame: graphicsRect] autorelease]; [view setEditable: NO]; @@ -94,7 +94,7 @@ void runServer() [window makeKeyAndOrderFront: nil]; [view listenTo: task]; - [task setLaunchPath: [mainBundle pathForAuxiliaryExecutable: @"DDRace-Server_sql"]]; + [task setLaunchPath: [mainBundle pathForAuxiliaryExecutable: @"DDNet-Server_sql"]]; [task setArguments: arguments]; [task launch]; [NSApp run]; diff --git a/src/versionsrv/mapversions.h b/src/versionsrv/mapversions.h new file mode 100644 index 0000000000..fe9e422952 --- /dev/null +++ b/src/versionsrv/mapversions.h @@ -0,0 +1,22 @@ +/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ +/* If you are missing that file, acquire a complete release at teeworlds.com. */ +#ifndef VERSIONSRV_MAPVERSIONS_H +#define VERSIONSRV_MAPVERSIONS_H + +static CMapVersion s_aMapVersionList[] = { + {"ctf1", {0x06, 0xb5, 0xf1, 0x17}, {0x00, 0x00, 0x12, 0x38}}, + {"ctf2", {0x27, 0xbc, 0x5e, 0xac}, {0x00, 0x00, 0x64, 0x1a}}, + {"ctf3", {0xa3, 0x73, 0x9d, 0x41}, {0x00, 0x00, 0x17, 0x0f}}, + {"ctf4", {0xbe, 0x7c, 0x4d, 0xb9}, {0x00, 0x00, 0x2e, 0xfe}}, + {"ctf5", {0xd9, 0x21, 0x29, 0xa0}, {0x00, 0x00, 0x2f, 0x4c}}, + {"ctf6", {0x28, 0xc8, 0x43, 0x51}, {0x00, 0x00, 0x69, 0x2f}}, + {"ctf7", {0x1d, 0x35, 0x98, 0x72}, {0x00, 0x00, 0x15, 0x87}}, + {"dm1", {0xf2, 0x15, 0x9e, 0x6e}, {0x00, 0x00, 0x16, 0xad}}, + {"dm2", {0x71, 0x83, 0x98, 0x78}, {0x00, 0x00, 0x21, 0xdf}}, + {"dm6", {0x47, 0x4d, 0xa2, 0x35}, {0x00, 0x00, 0x1e, 0x95}}, + {"dm7", {0x42, 0x6d, 0xa1, 0x67}, {0x00, 0x00, 0x27, 0x2a}}, + {"dm8", {0x85, 0xf1, 0x1e, 0xd6}, {0x00, 0x00, 0x9e, 0xbd}}, + {"dm9", {0x42, 0xd4, 0x77, 0x7e}, {0x00, 0x00, 0x20, 0x11}}, +}; +static const int s_NumMapVersionItems = sizeof(s_aMapVersionList)/sizeof(CMapVersion); +#endif diff --git a/src/versionsrv/versionsrv.cpp b/src/versionsrv/versionsrv.cpp index da55e71755..183a2397ad 100644 --- a/src/versionsrv/versionsrv.cpp +++ b/src/versionsrv/versionsrv.cpp @@ -7,6 +7,7 @@ #include #include "versionsrv.h" +#include "mapversions.h" enum { MAX_MAPS_PER_PACKET=48, @@ -25,6 +26,7 @@ struct CPacketData CPacketData m_aPackets[MAX_PACKETS]; static int m_NumPackets = 0; +unsigned char m_aNews[NEWS_SIZE]; static CNetClient g_NetOp; // main @@ -56,6 +58,17 @@ void BuildPackets() } } +void ReadNews() +{ + IOHANDLE newsFile = io_open("news", IOFLAG_READ); + if (!newsFile) + return; + + io_read(newsFile, m_aNews, NEWS_SIZE); + + io_close(newsFile); +} + void SendVer(NETADDR *pAddr) { CNetChunk p; @@ -73,6 +86,23 @@ void SendVer(NETADDR *pAddr) g_NetOp.Send(&p); } +void SendNews(NETADDR *pAddr) +{ + CNetChunk p; + unsigned char aData[NEWS_SIZE + sizeof(VERSIONSRV_NEWS)]; + + mem_copy(aData, VERSIONSRV_NEWS, sizeof(VERSIONSRV_NEWS)); + mem_copy(aData + sizeof(VERSIONSRV_NEWS), m_aNews, NEWS_SIZE); + + p.m_ClientID = -1; + p.m_Address = *pAddr; + p.m_Flags = NETSENDFLAG_CONNLESS; + p.m_pData = aData; + p.m_DataSize = sizeof(aData); + + g_NetOp.Send(&p); +} + int main(int argc, char **argv) // ignore_convention { NETADDR BindAddr; @@ -91,6 +121,8 @@ int main(int argc, char **argv) // ignore_convention BuildPackets(); + ReadNews(); + dbg_msg("versionsrv", "started"); while(1) @@ -105,6 +137,24 @@ int main(int argc, char **argv) // ignore_convention mem_comp(Packet.m_pData, VERSIONSRV_GETVERSION, sizeof(VERSIONSRV_GETVERSION)) == 0) { SendVer(&Packet.m_Address); + + char aAddrStr[NETADDR_MAXSTRSIZE]; + net_addr_str(&Packet.m_Address, aAddrStr, sizeof(aAddrStr), false); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "version request by %s", aAddrStr); + dbg_msg("versionsrv", aBuf); + } + + if(Packet.m_DataSize == sizeof(VERSIONSRV_GETNEWS) && + mem_comp(Packet.m_pData, VERSIONSRV_GETNEWS, sizeof(VERSIONSRV_GETNEWS)) == 0) + { + SendNews(&Packet.m_Address); + + char aAddrStr[NETADDR_MAXSTRSIZE]; + net_addr_str(&Packet.m_Address, aAddrStr, sizeof(aAddrStr), false); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "news request by %s", aAddrStr); + dbg_msg("versionsrv", aBuf); } if(Packet.m_DataSize == sizeof(VERSIONSRV_GETMAPLIST) && @@ -124,8 +174,8 @@ int main(int argc, char **argv) // ignore_convention } } - // be nice to the CPU - thread_sleep(1); + // wait for input + net_socket_read_wait(g_NetOp.m_Socket, 1000); } return 0; diff --git a/src/versionsrv/versionsrv.h b/src/versionsrv/versionsrv.h index 383f1ac435..d7840442e6 100644 --- a/src/versionsrv/versionsrv.h +++ b/src/versionsrv/versionsrv.h @@ -3,6 +3,7 @@ #ifndef VERSIONSRV_VERSIONSRV_H #define VERSIONSRV_VERSIONSRV_H static const int VERSIONSRV_PORT = 8302; +static const int NEWS_SIZE = 1380; struct CMapVersion { @@ -11,26 +12,12 @@ struct CMapVersion unsigned char m_aSize[4]; }; -static CMapVersion s_aMapVersionList[] = { - {"ctf1", {0x06, 0xb5, 0xf1, 0x17}, {0x00, 0x00, 0x12, 0x38}}, - {"ctf2", {0x27, 0xbc, 0x5e, 0xac}, {0x00, 0x00, 0x64, 0x1a}}, - {"ctf3", {0xa3, 0x73, 0x9d, 0x41}, {0x00, 0x00, 0x17, 0x0f}}, - {"ctf4", {0xbe, 0x7c, 0x4d, 0xb9}, {0x00, 0x00, 0x2e, 0xfe}}, - {"ctf5", {0xd9, 0x21, 0x29, 0xa0}, {0x00, 0x00, 0x2f, 0x4c}}, - {"ctf6", {0x28, 0xc8, 0x43, 0x51}, {0x00, 0x00, 0x69, 0x2f}}, - {"ctf7", {0x1d, 0x35, 0x98, 0x72}, {0x00, 0x00, 0x15, 0x87}}, - {"dm1", {0xf2, 0x15, 0x9e, 0x6e}, {0x00, 0x00, 0x16, 0xad}}, - {"dm2", {0x71, 0x83, 0x98, 0x78}, {0x00, 0x00, 0x21, 0xdf}}, - {"dm6", {0x47, 0x4d, 0xa2, 0x35}, {0x00, 0x00, 0x1e, 0x95}}, - {"dm7", {0x42, 0x6d, 0xa1, 0x67}, {0x00, 0x00, 0x27, 0x2a}}, - {"dm8", {0x85, 0xf1, 0x1e, 0xd6}, {0x00, 0x00, 0x9e, 0xbd}}, - {"dm9", {0x42, 0xd4, 0x77, 0x7e}, {0x00, 0x00, 0x20, 0x11}}, -}; -static const int s_NumMapVersionItems = sizeof(s_aMapVersionList)/sizeof(CMapVersion); - static const unsigned char VERSIONSRV_GETVERSION[] = {255, 255, 255, 255, 'v', 'e', 'r', 'g'}; static const unsigned char VERSIONSRV_VERSION[] = {255, 255, 255, 255, 'v', 'e', 'r', 's'}; static const unsigned char VERSIONSRV_GETMAPLIST[] = {255, 255, 255, 255, 'v', 'm', 'l', 'g'}; static const unsigned char VERSIONSRV_MAPLIST[] = {255, 255, 255, 255, 'v', 'm', 'l', 's'}; + +static const unsigned char VERSIONSRV_GETNEWS[] = {255, 255, 255, 255, 'n', 'e', 'w', 'g'}; +static const unsigned char VERSIONSRV_NEWS[] = {255, 255, 255, 255, 'n', 'e', 'w', 's'}; #endif