diff --git a/data/countryflags/AD.png b/data/countryflags/AD.png new file mode 100644 index 000000000..9977c0097 Binary files /dev/null and b/data/countryflags/AD.png differ diff --git a/data/countryflags/AE.png b/data/countryflags/AE.png new file mode 100644 index 000000000..7be66c754 Binary files /dev/null and b/data/countryflags/AE.png differ diff --git a/data/countryflags/AF.png b/data/countryflags/AF.png new file mode 100644 index 000000000..e7f20d1c3 Binary files /dev/null and b/data/countryflags/AF.png differ diff --git a/data/countryflags/AG.png b/data/countryflags/AG.png new file mode 100644 index 000000000..22fdded84 Binary files /dev/null and b/data/countryflags/AG.png differ diff --git a/data/countryflags/AI.png b/data/countryflags/AI.png new file mode 100644 index 000000000..9056a2b21 Binary files /dev/null and b/data/countryflags/AI.png differ diff --git a/data/countryflags/AL.png b/data/countryflags/AL.png new file mode 100644 index 000000000..acc746193 Binary files /dev/null and b/data/countryflags/AL.png differ diff --git a/data/countryflags/AM.png b/data/countryflags/AM.png new file mode 100644 index 000000000..139c30d9a Binary files /dev/null and b/data/countryflags/AM.png differ diff --git a/data/countryflags/AO.png b/data/countryflags/AO.png new file mode 100644 index 000000000..5b3ca638b Binary files /dev/null and b/data/countryflags/AO.png differ diff --git a/data/countryflags/AR.png b/data/countryflags/AR.png index 0ce5f92be..878458c70 100644 Binary files a/data/countryflags/AR.png and b/data/countryflags/AR.png differ diff --git a/data/countryflags/AS.png b/data/countryflags/AS.png new file mode 100644 index 000000000..b4add724f Binary files /dev/null and b/data/countryflags/AS.png differ diff --git a/data/countryflags/AT.png b/data/countryflags/AT.png index c1fbe9d40..13c6a5990 100644 Binary files a/data/countryflags/AT.png and b/data/countryflags/AT.png differ diff --git a/data/countryflags/AU.png b/data/countryflags/AU.png index 361f22e4e..6e3346454 100644 Binary files a/data/countryflags/AU.png and b/data/countryflags/AU.png differ diff --git a/data/countryflags/AW.png b/data/countryflags/AW.png new file mode 100644 index 000000000..9c89e68c5 Binary files /dev/null and b/data/countryflags/AW.png differ diff --git a/data/countryflags/AX.png b/data/countryflags/AX.png new file mode 100644 index 000000000..d7fb2236c Binary files /dev/null and b/data/countryflags/AX.png differ diff --git a/data/countryflags/AZ.png b/data/countryflags/AZ.png new file mode 100644 index 000000000..49bcb8353 Binary files /dev/null and b/data/countryflags/AZ.png differ diff --git a/data/countryflags/BA.png b/data/countryflags/BA.png index 3a77a439a..21397f63c 100644 Binary files a/data/countryflags/BA.png and b/data/countryflags/BA.png differ diff --git a/data/countryflags/BB.png b/data/countryflags/BB.png new file mode 100644 index 000000000..051b3032b Binary files /dev/null and b/data/countryflags/BB.png differ diff --git a/data/countryflags/BD.png b/data/countryflags/BD.png new file mode 100644 index 000000000..1488cf270 Binary files /dev/null and b/data/countryflags/BD.png differ diff --git a/data/countryflags/BE.png b/data/countryflags/BE.png index 409e85522..b6135ec63 100644 Binary files a/data/countryflags/BE.png and b/data/countryflags/BE.png differ diff --git a/data/countryflags/BF.png b/data/countryflags/BF.png new file mode 100644 index 000000000..fd1fa109b Binary files /dev/null and b/data/countryflags/BF.png differ diff --git a/data/countryflags/BG.png b/data/countryflags/BG.png index 7f9a65902..f323e5d02 100644 Binary files a/data/countryflags/BG.png and b/data/countryflags/BG.png differ diff --git a/data/countryflags/BH.png b/data/countryflags/BH.png new file mode 100644 index 000000000..59c8a4874 Binary files /dev/null and b/data/countryflags/BH.png differ diff --git a/data/countryflags/BI.png b/data/countryflags/BI.png new file mode 100644 index 000000000..08f0ec47f Binary files /dev/null and b/data/countryflags/BI.png differ diff --git a/data/countryflags/BJ.png b/data/countryflags/BJ.png new file mode 100644 index 000000000..74725ce96 Binary files /dev/null and b/data/countryflags/BJ.png differ diff --git a/data/countryflags/BL.png b/data/countryflags/BL.png new file mode 100644 index 000000000..692401bdd Binary files /dev/null and b/data/countryflags/BL.png differ diff --git a/data/countryflags/BM.png b/data/countryflags/BM.png new file mode 100644 index 000000000..9d7cd8713 Binary files /dev/null and b/data/countryflags/BM.png differ diff --git a/data/countryflags/BN.png b/data/countryflags/BN.png new file mode 100644 index 000000000..38dd8cc95 Binary files /dev/null and b/data/countryflags/BN.png differ diff --git a/data/countryflags/BO.png b/data/countryflags/BO.png new file mode 100644 index 000000000..028e1fe3b Binary files /dev/null and b/data/countryflags/BO.png differ diff --git a/data/countryflags/BR.png b/data/countryflags/BR.png index aada91d5d..5c2ea746a 100644 Binary files a/data/countryflags/BR.png and b/data/countryflags/BR.png differ diff --git a/data/countryflags/BS.png b/data/countryflags/BS.png new file mode 100644 index 000000000..aae921df0 Binary files /dev/null and b/data/countryflags/BS.png differ diff --git a/data/countryflags/BT.png b/data/countryflags/BT.png new file mode 100644 index 000000000..130b90456 Binary files /dev/null and b/data/countryflags/BT.png differ diff --git a/data/countryflags/BW.png b/data/countryflags/BW.png new file mode 100644 index 000000000..d7782c6ee Binary files /dev/null and b/data/countryflags/BW.png differ diff --git a/data/countryflags/BY.png b/data/countryflags/BY.png index 6052eea47..c05a3664e 100644 Binary files a/data/countryflags/BY.png and b/data/countryflags/BY.png differ diff --git a/data/countryflags/BZ.png b/data/countryflags/BZ.png new file mode 100644 index 000000000..93f832e58 Binary files /dev/null and b/data/countryflags/BZ.png differ diff --git a/data/countryflags/CA.png b/data/countryflags/CA.png index 9898ba577..9d9f4a1e6 100644 Binary files a/data/countryflags/CA.png and b/data/countryflags/CA.png differ diff --git a/data/countryflags/CC.png b/data/countryflags/CC.png new file mode 100644 index 000000000..ef1b1dd2c Binary files /dev/null and b/data/countryflags/CC.png differ diff --git a/data/countryflags/CD.png b/data/countryflags/CD.png new file mode 100644 index 000000000..3c5f2f95a Binary files /dev/null and b/data/countryflags/CD.png differ diff --git a/data/countryflags/CF.png b/data/countryflags/CF.png new file mode 100644 index 000000000..e42de4dcf Binary files /dev/null and b/data/countryflags/CF.png differ diff --git a/data/countryflags/CG.png b/data/countryflags/CG.png new file mode 100644 index 000000000..99a97ea13 Binary files /dev/null and b/data/countryflags/CG.png differ diff --git a/data/countryflags/CH.png b/data/countryflags/CH.png index fa8d6482d..27881d0ce 100644 Binary files a/data/countryflags/CH.png and b/data/countryflags/CH.png differ diff --git a/data/countryflags/CI.png b/data/countryflags/CI.png new file mode 100644 index 000000000..fa80f60b8 Binary files /dev/null and b/data/countryflags/CI.png differ diff --git a/data/countryflags/CK.png b/data/countryflags/CK.png new file mode 100644 index 000000000..c7a8d3252 Binary files /dev/null and b/data/countryflags/CK.png differ diff --git a/data/countryflags/CL.png b/data/countryflags/CL.png index 571851c72..327b27f68 100644 Binary files a/data/countryflags/CL.png and b/data/countryflags/CL.png differ diff --git a/data/countryflags/CM.png b/data/countryflags/CM.png new file mode 100644 index 000000000..9e983e2d1 Binary files /dev/null and b/data/countryflags/CM.png differ diff --git a/data/countryflags/CN.png b/data/countryflags/CN.png index 3251bd9a8..1996131e7 100644 Binary files a/data/countryflags/CN.png and b/data/countryflags/CN.png differ diff --git a/data/countryflags/CO.png b/data/countryflags/CO.png index 5fdda4a59..61f8fa550 100644 Binary files a/data/countryflags/CO.png and b/data/countryflags/CO.png differ diff --git a/data/countryflags/CR.png b/data/countryflags/CR.png new file mode 100644 index 000000000..f6a02efd2 Binary files /dev/null and b/data/countryflags/CR.png differ diff --git a/data/countryflags/CU.png b/data/countryflags/CU.png new file mode 100644 index 000000000..0dbee8c25 Binary files /dev/null and b/data/countryflags/CU.png differ diff --git a/data/countryflags/CV.png b/data/countryflags/CV.png new file mode 100644 index 000000000..95afe4b5a Binary files /dev/null and b/data/countryflags/CV.png differ diff --git a/data/countryflags/CW.png b/data/countryflags/CW.png new file mode 100644 index 000000000..079ec8773 Binary files /dev/null and b/data/countryflags/CW.png differ diff --git a/data/countryflags/CX.png b/data/countryflags/CX.png new file mode 100644 index 000000000..120510340 Binary files /dev/null and b/data/countryflags/CX.png differ diff --git a/data/countryflags/CY.png b/data/countryflags/CY.png new file mode 100644 index 000000000..dd34d6c44 Binary files /dev/null and b/data/countryflags/CY.png differ diff --git a/data/countryflags/CZ.png b/data/countryflags/CZ.png index cb7508773..b4df10db0 100644 Binary files a/data/countryflags/CZ.png and b/data/countryflags/CZ.png differ diff --git a/data/countryflags/DE.png b/data/countryflags/DE.png index 699a43d8c..dde74c9ee 100644 Binary files a/data/countryflags/DE.png and b/data/countryflags/DE.png differ diff --git a/data/countryflags/DJ.png b/data/countryflags/DJ.png new file mode 100644 index 000000000..62a8c250c Binary files /dev/null and b/data/countryflags/DJ.png differ diff --git a/data/countryflags/DK.png b/data/countryflags/DK.png index df04cf93d..4125f6040 100644 Binary files a/data/countryflags/DK.png and b/data/countryflags/DK.png differ diff --git a/data/countryflags/DM.png b/data/countryflags/DM.png new file mode 100644 index 000000000..3ea6cf597 Binary files /dev/null and b/data/countryflags/DM.png differ diff --git a/data/countryflags/DO.png b/data/countryflags/DO.png new file mode 100644 index 000000000..f70848f5f Binary files /dev/null and b/data/countryflags/DO.png differ diff --git a/data/countryflags/DZ.png b/data/countryflags/DZ.png new file mode 100644 index 000000000..31df99f39 Binary files /dev/null and b/data/countryflags/DZ.png differ diff --git a/data/countryflags/EC.png b/data/countryflags/EC.png new file mode 100644 index 000000000..80dfbaa23 Binary files /dev/null and b/data/countryflags/EC.png differ diff --git a/data/countryflags/EE.png b/data/countryflags/EE.png index 45ef482f1..6dc9933a4 100644 Binary files a/data/countryflags/EE.png and b/data/countryflags/EE.png differ diff --git a/data/countryflags/EG.png b/data/countryflags/EG.png index 3cd437d3e..b0c3bf52e 100644 Binary files a/data/countryflags/EG.png and b/data/countryflags/EG.png differ diff --git a/data/countryflags/EH.png b/data/countryflags/EH.png new file mode 100644 index 000000000..9af42a3be Binary files /dev/null and b/data/countryflags/EH.png differ diff --git a/data/countryflags/ER.png b/data/countryflags/ER.png new file mode 100644 index 000000000..88dd07066 Binary files /dev/null and b/data/countryflags/ER.png differ diff --git a/data/countryflags/ES.png b/data/countryflags/ES.png index b507a3284..412b21076 100644 Binary files a/data/countryflags/ES.png and b/data/countryflags/ES.png differ diff --git a/data/countryflags/ET.png b/data/countryflags/ET.png new file mode 100644 index 000000000..9588ce9a2 Binary files /dev/null and b/data/countryflags/ET.png differ diff --git a/data/countryflags/FI.png b/data/countryflags/FI.png index 96b395932..0373e03eb 100644 Binary files a/data/countryflags/FI.png and b/data/countryflags/FI.png differ diff --git a/data/countryflags/FJ.png b/data/countryflags/FJ.png new file mode 100644 index 000000000..9d2af6295 Binary files /dev/null and b/data/countryflags/FJ.png differ diff --git a/data/countryflags/FK.png b/data/countryflags/FK.png new file mode 100644 index 000000000..a3f2ad346 Binary files /dev/null and b/data/countryflags/FK.png differ diff --git a/data/countryflags/FM.png b/data/countryflags/FM.png new file mode 100644 index 000000000..c0cd4cf1f Binary files /dev/null and b/data/countryflags/FM.png differ diff --git a/data/countryflags/FO.png b/data/countryflags/FO.png new file mode 100644 index 000000000..e46c2eb32 Binary files /dev/null and b/data/countryflags/FO.png differ diff --git a/data/countryflags/FR.png b/data/countryflags/FR.png index f85cfa499..eb4ec8ab5 100644 Binary files a/data/countryflags/FR.png and b/data/countryflags/FR.png differ diff --git a/data/countryflags/GA.png b/data/countryflags/GA.png new file mode 100644 index 000000000..446d380d1 Binary files /dev/null and b/data/countryflags/GA.png differ diff --git a/data/countryflags/GB.png b/data/countryflags/GB.png index 240adcc7b..4aa460677 100644 Binary files a/data/countryflags/GB.png and b/data/countryflags/GB.png differ diff --git a/data/countryflags/GD.png b/data/countryflags/GD.png new file mode 100644 index 000000000..538e8e095 Binary files /dev/null and b/data/countryflags/GD.png differ diff --git a/data/countryflags/GE.png b/data/countryflags/GE.png new file mode 100644 index 000000000..679538477 Binary files /dev/null and b/data/countryflags/GE.png differ diff --git a/data/countryflags/GF.png b/data/countryflags/GF.png new file mode 100644 index 000000000..2b804f54d Binary files /dev/null and b/data/countryflags/GF.png differ diff --git a/data/countryflags/GG.png b/data/countryflags/GG.png new file mode 100644 index 000000000..472924fb7 Binary files /dev/null and b/data/countryflags/GG.png differ diff --git a/data/countryflags/GH.png b/data/countryflags/GH.png new file mode 100644 index 000000000..2809250f0 Binary files /dev/null and b/data/countryflags/GH.png differ diff --git a/data/countryflags/GI.png b/data/countryflags/GI.png new file mode 100644 index 000000000..9c88bfd4c Binary files /dev/null and b/data/countryflags/GI.png differ diff --git a/data/countryflags/GL.png b/data/countryflags/GL.png new file mode 100644 index 000000000..6191d23c2 Binary files /dev/null and b/data/countryflags/GL.png differ diff --git a/data/countryflags/GM.png b/data/countryflags/GM.png new file mode 100644 index 000000000..ce94adfdf Binary files /dev/null and b/data/countryflags/GM.png differ diff --git a/data/countryflags/GN.png b/data/countryflags/GN.png new file mode 100644 index 000000000..3f6985599 Binary files /dev/null and b/data/countryflags/GN.png differ diff --git a/data/countryflags/GP.png b/data/countryflags/GP.png new file mode 100644 index 000000000..bff1c31f4 Binary files /dev/null and b/data/countryflags/GP.png differ diff --git a/data/countryflags/GQ.png b/data/countryflags/GQ.png new file mode 100644 index 000000000..3a7dc2c50 Binary files /dev/null and b/data/countryflags/GQ.png differ diff --git a/data/countryflags/GR.png b/data/countryflags/GR.png index 18eaeb28f..a9dcd902a 100644 Binary files a/data/countryflags/GR.png and b/data/countryflags/GR.png differ diff --git a/data/countryflags/GS.png b/data/countryflags/GS.png new file mode 100644 index 000000000..bdeda46f6 Binary files /dev/null and b/data/countryflags/GS.png differ diff --git a/data/countryflags/GT.png b/data/countryflags/GT.png new file mode 100644 index 000000000..9ed9c11b9 Binary files /dev/null and b/data/countryflags/GT.png differ diff --git a/data/countryflags/GU.png b/data/countryflags/GU.png new file mode 100644 index 000000000..1a0affcd3 Binary files /dev/null and b/data/countryflags/GU.png differ diff --git a/data/countryflags/GW.png b/data/countryflags/GW.png new file mode 100644 index 000000000..b22b8d0e3 Binary files /dev/null and b/data/countryflags/GW.png differ diff --git a/data/countryflags/GY.png b/data/countryflags/GY.png new file mode 100644 index 000000000..35fd8c8fa Binary files /dev/null and b/data/countryflags/GY.png differ diff --git a/data/countryflags/HK.png b/data/countryflags/HK.png new file mode 100644 index 000000000..6e4b18ced Binary files /dev/null and b/data/countryflags/HK.png differ diff --git a/data/countryflags/HN.png b/data/countryflags/HN.png new file mode 100644 index 000000000..095047184 Binary files /dev/null and b/data/countryflags/HN.png differ diff --git a/data/countryflags/HR.png b/data/countryflags/HR.png index 6097adc7f..c844d4f4e 100644 Binary files a/data/countryflags/HR.png and b/data/countryflags/HR.png differ diff --git a/data/countryflags/HT.png b/data/countryflags/HT.png new file mode 100644 index 000000000..3ab3e79f2 Binary files /dev/null and b/data/countryflags/HT.png differ diff --git a/data/countryflags/HU.png b/data/countryflags/HU.png index 20c21f0e0..8106da42c 100644 Binary files a/data/countryflags/HU.png and b/data/countryflags/HU.png differ diff --git a/data/countryflags/ID.png b/data/countryflags/ID.png index 510bac083..49b52e3c8 100644 Binary files a/data/countryflags/ID.png and b/data/countryflags/ID.png differ diff --git a/data/countryflags/IE.png b/data/countryflags/IE.png new file mode 100644 index 000000000..df9be88fc Binary files /dev/null and b/data/countryflags/IE.png differ diff --git a/data/countryflags/IL.png b/data/countryflags/IL.png index 3852a8f94..ce4acc739 100644 Binary files a/data/countryflags/IL.png and b/data/countryflags/IL.png differ diff --git a/data/countryflags/IM.png b/data/countryflags/IM.png new file mode 100644 index 000000000..efd55fe48 Binary files /dev/null and b/data/countryflags/IM.png differ diff --git a/data/countryflags/IN.png b/data/countryflags/IN.png index 91d34696e..c4f8dbf07 100644 Binary files a/data/countryflags/IN.png and b/data/countryflags/IN.png differ diff --git a/data/countryflags/IO.png b/data/countryflags/IO.png new file mode 100644 index 000000000..e983a3f2d Binary files /dev/null and b/data/countryflags/IO.png differ diff --git a/data/countryflags/IQ.png b/data/countryflags/IQ.png new file mode 100644 index 000000000..81e7a8b45 Binary files /dev/null and b/data/countryflags/IQ.png differ diff --git a/data/countryflags/IR.png b/data/countryflags/IR.png index 141a0e56c..dcb71c3af 100644 Binary files a/data/countryflags/IR.png and b/data/countryflags/IR.png differ diff --git a/data/countryflags/IS.png b/data/countryflags/IS.png new file mode 100644 index 000000000..4307fdfd1 Binary files /dev/null and b/data/countryflags/IS.png differ diff --git a/data/countryflags/IT.png b/data/countryflags/IT.png index 4d4a1b3e9..7372d8124 100644 Binary files a/data/countryflags/IT.png and b/data/countryflags/IT.png differ diff --git a/data/countryflags/JE.png b/data/countryflags/JE.png new file mode 100644 index 000000000..a6b50b0c5 Binary files /dev/null and b/data/countryflags/JE.png differ diff --git a/data/countryflags/JM.png b/data/countryflags/JM.png new file mode 100644 index 000000000..94cf3abf0 Binary files /dev/null and b/data/countryflags/JM.png differ diff --git a/data/countryflags/JO.png b/data/countryflags/JO.png new file mode 100644 index 000000000..0bb6cb65a Binary files /dev/null and b/data/countryflags/JO.png differ diff --git a/data/countryflags/JP.png b/data/countryflags/JP.png new file mode 100644 index 000000000..ddfdfb5cf Binary files /dev/null and b/data/countryflags/JP.png differ diff --git a/data/countryflags/KE.png b/data/countryflags/KE.png new file mode 100644 index 000000000..e44c27d61 Binary files /dev/null and b/data/countryflags/KE.png differ diff --git a/data/countryflags/KG.png b/data/countryflags/KG.png new file mode 100644 index 000000000..1c57fbbcc Binary files /dev/null and b/data/countryflags/KG.png differ diff --git a/data/countryflags/KH.png b/data/countryflags/KH.png new file mode 100644 index 000000000..102bc0240 Binary files /dev/null and b/data/countryflags/KH.png differ diff --git a/data/countryflags/KI.png b/data/countryflags/KI.png new file mode 100644 index 000000000..7f62de607 Binary files /dev/null and b/data/countryflags/KI.png differ diff --git a/data/countryflags/KM.png b/data/countryflags/KM.png new file mode 100644 index 000000000..f6a77ecc8 Binary files /dev/null and b/data/countryflags/KM.png differ diff --git a/data/countryflags/KN.png b/data/countryflags/KN.png new file mode 100644 index 000000000..06f6a9f9a Binary files /dev/null and b/data/countryflags/KN.png differ diff --git a/data/countryflags/KP.png b/data/countryflags/KP.png new file mode 100644 index 000000000..48093061b Binary files /dev/null and b/data/countryflags/KP.png differ diff --git a/data/countryflags/KR.png b/data/countryflags/KR.png new file mode 100644 index 000000000..32a83733f Binary files /dev/null and b/data/countryflags/KR.png differ diff --git a/data/countryflags/KW.png b/data/countryflags/KW.png new file mode 100644 index 000000000..557e38b1c Binary files /dev/null and b/data/countryflags/KW.png differ diff --git a/data/countryflags/KY.png b/data/countryflags/KY.png new file mode 100644 index 000000000..020dbeef1 Binary files /dev/null and b/data/countryflags/KY.png differ diff --git a/data/countryflags/KZ.png b/data/countryflags/KZ.png index e3db4f930..d0b6eede8 100644 Binary files a/data/countryflags/KZ.png and b/data/countryflags/KZ.png differ diff --git a/data/countryflags/LA.png b/data/countryflags/LA.png new file mode 100644 index 000000000..bfe966f6c Binary files /dev/null and b/data/countryflags/LA.png differ diff --git a/data/countryflags/LB.png b/data/countryflags/LB.png new file mode 100644 index 000000000..3c8ef00c3 Binary files /dev/null and b/data/countryflags/LB.png differ diff --git a/data/countryflags/LC.png b/data/countryflags/LC.png new file mode 100644 index 000000000..19b5f78ad Binary files /dev/null and b/data/countryflags/LC.png differ diff --git a/data/countryflags/LI.png b/data/countryflags/LI.png new file mode 100644 index 000000000..06daeeb83 Binary files /dev/null and b/data/countryflags/LI.png differ diff --git a/data/countryflags/LK.png b/data/countryflags/LK.png new file mode 100644 index 000000000..f7710ae99 Binary files /dev/null and b/data/countryflags/LK.png differ diff --git a/data/countryflags/LR.png b/data/countryflags/LR.png new file mode 100644 index 000000000..6d361dff2 Binary files /dev/null and b/data/countryflags/LR.png differ diff --git a/data/countryflags/LS.png b/data/countryflags/LS.png new file mode 100644 index 000000000..02a844fec Binary files /dev/null and b/data/countryflags/LS.png differ diff --git a/data/countryflags/LT.png b/data/countryflags/LT.png index dd1d3d13c..c37e857ff 100644 Binary files a/data/countryflags/LT.png and b/data/countryflags/LT.png differ diff --git a/data/countryflags/LU.png b/data/countryflags/LU.png index 01377c56e..ed89b6161 100644 Binary files a/data/countryflags/LU.png and b/data/countryflags/LU.png differ diff --git a/data/countryflags/LV.png b/data/countryflags/LV.png index 8915e25d9..f5f51afc9 100644 Binary files a/data/countryflags/LV.png and b/data/countryflags/LV.png differ diff --git a/data/countryflags/LY.png b/data/countryflags/LY.png new file mode 100644 index 000000000..1dda0198f Binary files /dev/null and b/data/countryflags/LY.png differ diff --git a/data/countryflags/MA.png b/data/countryflags/MA.png new file mode 100644 index 000000000..0ae330bb5 Binary files /dev/null and b/data/countryflags/MA.png differ diff --git a/data/countryflags/MC.png b/data/countryflags/MC.png new file mode 100644 index 000000000..5a73c3c14 Binary files /dev/null and b/data/countryflags/MC.png differ diff --git a/data/countryflags/MD.png b/data/countryflags/MD.png new file mode 100644 index 000000000..d713b0798 Binary files /dev/null and b/data/countryflags/MD.png differ diff --git a/data/countryflags/ME.png b/data/countryflags/ME.png new file mode 100644 index 000000000..f6348bdab Binary files /dev/null and b/data/countryflags/ME.png differ diff --git a/data/countryflags/MF.png b/data/countryflags/MF.png new file mode 100644 index 000000000..0fa201c98 Binary files /dev/null and b/data/countryflags/MF.png differ diff --git a/data/countryflags/MG.png b/data/countryflags/MG.png new file mode 100644 index 000000000..520308379 Binary files /dev/null and b/data/countryflags/MG.png differ diff --git a/data/countryflags/MH.png b/data/countryflags/MH.png new file mode 100644 index 000000000..40d7bd3d4 Binary files /dev/null and b/data/countryflags/MH.png differ diff --git a/data/countryflags/MK.png b/data/countryflags/MK.png new file mode 100644 index 000000000..3743d81a2 Binary files /dev/null and b/data/countryflags/MK.png differ diff --git a/data/countryflags/ML.png b/data/countryflags/ML.png new file mode 100644 index 000000000..d548a1e20 Binary files /dev/null and b/data/countryflags/ML.png differ diff --git a/data/countryflags/MM.png b/data/countryflags/MM.png new file mode 100644 index 000000000..58c098b80 Binary files /dev/null and b/data/countryflags/MM.png differ diff --git a/data/countryflags/MN.png b/data/countryflags/MN.png new file mode 100644 index 000000000..1d9f5159a Binary files /dev/null and b/data/countryflags/MN.png differ diff --git a/data/countryflags/MO.png b/data/countryflags/MO.png new file mode 100644 index 000000000..6adda2988 Binary files /dev/null and b/data/countryflags/MO.png differ diff --git a/data/countryflags/MP.png b/data/countryflags/MP.png new file mode 100644 index 000000000..6b3086453 Binary files /dev/null and b/data/countryflags/MP.png differ diff --git a/data/countryflags/MQ.png b/data/countryflags/MQ.png new file mode 100644 index 000000000..2f3c556a2 Binary files /dev/null and b/data/countryflags/MQ.png differ diff --git a/data/countryflags/MR.png b/data/countryflags/MR.png new file mode 100644 index 000000000..8c6462ba9 Binary files /dev/null and b/data/countryflags/MR.png differ diff --git a/data/countryflags/MS.png b/data/countryflags/MS.png new file mode 100644 index 000000000..273120235 Binary files /dev/null and b/data/countryflags/MS.png differ diff --git a/data/countryflags/MT.png b/data/countryflags/MT.png new file mode 100644 index 000000000..043ea73eb Binary files /dev/null and b/data/countryflags/MT.png differ diff --git a/data/countryflags/MU.png b/data/countryflags/MU.png new file mode 100644 index 000000000..02a5f3af3 Binary files /dev/null and b/data/countryflags/MU.png differ diff --git a/data/countryflags/MV.png b/data/countryflags/MV.png new file mode 100644 index 000000000..6a3fd9618 Binary files /dev/null and b/data/countryflags/MV.png differ diff --git a/data/countryflags/MW.png b/data/countryflags/MW.png new file mode 100644 index 000000000..5d26e5f78 Binary files /dev/null and b/data/countryflags/MW.png differ diff --git a/data/countryflags/MX.png b/data/countryflags/MX.png index 9505d6234..19c7ddeaf 100644 Binary files a/data/countryflags/MX.png and b/data/countryflags/MX.png differ diff --git a/data/countryflags/MY.png b/data/countryflags/MY.png new file mode 100644 index 000000000..885ba6388 Binary files /dev/null and b/data/countryflags/MY.png differ diff --git a/data/countryflags/MZ.png b/data/countryflags/MZ.png new file mode 100644 index 000000000..caa5cb4d8 Binary files /dev/null and b/data/countryflags/MZ.png differ diff --git a/data/countryflags/NA.png b/data/countryflags/NA.png new file mode 100644 index 000000000..f6936933f Binary files /dev/null and b/data/countryflags/NA.png differ diff --git a/data/countryflags/NC.png b/data/countryflags/NC.png new file mode 100644 index 000000000..5f3b08928 Binary files /dev/null and b/data/countryflags/NC.png differ diff --git a/data/countryflags/NE.png b/data/countryflags/NE.png new file mode 100644 index 000000000..7d73f48e5 Binary files /dev/null and b/data/countryflags/NE.png differ diff --git a/data/countryflags/NF.png b/data/countryflags/NF.png new file mode 100644 index 000000000..5f5401d7c Binary files /dev/null and b/data/countryflags/NF.png differ diff --git a/data/countryflags/NG.png b/data/countryflags/NG.png new file mode 100644 index 000000000..e9030716e Binary files /dev/null and b/data/countryflags/NG.png differ diff --git a/data/countryflags/NI.png b/data/countryflags/NI.png new file mode 100644 index 000000000..19df0e95b Binary files /dev/null and b/data/countryflags/NI.png differ diff --git a/data/countryflags/NL.png b/data/countryflags/NL.png index 5882be49d..ba54b03c8 100644 Binary files a/data/countryflags/NL.png and b/data/countryflags/NL.png differ diff --git a/data/countryflags/NO.png b/data/countryflags/NO.png index 2e9bbec97..6f085323f 100644 Binary files a/data/countryflags/NO.png and b/data/countryflags/NO.png differ diff --git a/data/countryflags/NP.png b/data/countryflags/NP.png new file mode 100644 index 000000000..53c7510a4 Binary files /dev/null and b/data/countryflags/NP.png differ diff --git a/data/countryflags/NR.png b/data/countryflags/NR.png new file mode 100644 index 000000000..562be4fc4 Binary files /dev/null and b/data/countryflags/NR.png differ diff --git a/data/countryflags/NU.png b/data/countryflags/NU.png new file mode 100644 index 000000000..1928bab68 Binary files /dev/null and b/data/countryflags/NU.png differ diff --git a/data/countryflags/NZ.png b/data/countryflags/NZ.png new file mode 100644 index 000000000..4e210491a Binary files /dev/null and b/data/countryflags/NZ.png differ diff --git a/data/countryflags/OM.png b/data/countryflags/OM.png new file mode 100644 index 000000000..8b6aba47c Binary files /dev/null and b/data/countryflags/OM.png differ diff --git a/data/countryflags/PA.png b/data/countryflags/PA.png new file mode 100644 index 000000000..ef051c61e Binary files /dev/null and b/data/countryflags/PA.png differ diff --git a/data/countryflags/PE.png b/data/countryflags/PE.png new file mode 100644 index 000000000..f61886507 Binary files /dev/null and b/data/countryflags/PE.png differ diff --git a/data/countryflags/PF.png b/data/countryflags/PF.png new file mode 100644 index 000000000..bf04554d4 Binary files /dev/null and b/data/countryflags/PF.png differ diff --git a/data/countryflags/PG.png b/data/countryflags/PG.png new file mode 100644 index 000000000..04b36a38a Binary files /dev/null and b/data/countryflags/PG.png differ diff --git a/data/countryflags/PH.png b/data/countryflags/PH.png index ebcec1f2b..edb506c0f 100644 Binary files a/data/countryflags/PH.png and b/data/countryflags/PH.png differ diff --git a/data/countryflags/PK.png b/data/countryflags/PK.png index 6e4e49500..6030b86b2 100644 Binary files a/data/countryflags/PK.png and b/data/countryflags/PK.png differ diff --git a/data/countryflags/PL.png b/data/countryflags/PL.png index 6e6a5c954..03b9e71fa 100644 Binary files a/data/countryflags/PL.png and b/data/countryflags/PL.png differ diff --git a/data/countryflags/PM.png b/data/countryflags/PM.png new file mode 100644 index 000000000..dd0dce939 Binary files /dev/null and b/data/countryflags/PM.png differ diff --git a/data/countryflags/PN.png b/data/countryflags/PN.png new file mode 100644 index 000000000..a14628bbe Binary files /dev/null and b/data/countryflags/PN.png differ diff --git a/data/countryflags/PR.png b/data/countryflags/PR.png new file mode 100644 index 000000000..6a3f60148 Binary files /dev/null and b/data/countryflags/PR.png differ diff --git a/data/countryflags/PT.png b/data/countryflags/PT.png index b3be09731..ca90e6e9d 100644 Binary files a/data/countryflags/PT.png and b/data/countryflags/PT.png differ diff --git a/data/countryflags/PW.png b/data/countryflags/PW.png new file mode 100644 index 000000000..49ee4480e Binary files /dev/null and b/data/countryflags/PW.png differ diff --git a/data/countryflags/PY.png b/data/countryflags/PY.png new file mode 100644 index 000000000..948f71516 Binary files /dev/null and b/data/countryflags/PY.png differ diff --git a/data/countryflags/QA.png b/data/countryflags/QA.png new file mode 100644 index 000000000..cae22d2ba Binary files /dev/null and b/data/countryflags/QA.png differ diff --git a/data/countryflags/RE.png b/data/countryflags/RE.png new file mode 100644 index 000000000..5efe1da4a Binary files /dev/null and b/data/countryflags/RE.png differ diff --git a/data/countryflags/RO.png b/data/countryflags/RO.png index 4eae6d222..6ea76a886 100644 Binary files a/data/countryflags/RO.png and b/data/countryflags/RO.png differ diff --git a/data/countryflags/RS.png b/data/countryflags/RS.png index 7cc06394b..24e067b4c 100644 Binary files a/data/countryflags/RS.png and b/data/countryflags/RS.png differ diff --git a/data/countryflags/RU.png b/data/countryflags/RU.png index ed2938106..74a8528fc 100644 Binary files a/data/countryflags/RU.png and b/data/countryflags/RU.png differ diff --git a/data/countryflags/RW.png b/data/countryflags/RW.png new file mode 100644 index 000000000..57d3ac5f2 Binary files /dev/null and b/data/countryflags/RW.png differ diff --git a/data/countryflags/SA.png b/data/countryflags/SA.png index fbfb6cf58..93598245b 100644 Binary files a/data/countryflags/SA.png and b/data/countryflags/SA.png differ diff --git a/data/countryflags/SB.png b/data/countryflags/SB.png new file mode 100644 index 000000000..7c4c468d2 Binary files /dev/null and b/data/countryflags/SB.png differ diff --git a/data/countryflags/SC.png b/data/countryflags/SC.png new file mode 100644 index 000000000..5237f2e8e Binary files /dev/null and b/data/countryflags/SC.png differ diff --git a/data/countryflags/SD.png b/data/countryflags/SD.png new file mode 100644 index 000000000..6adab5ad9 Binary files /dev/null and b/data/countryflags/SD.png differ diff --git a/data/countryflags/SE.png b/data/countryflags/SE.png index c5c9cda9a..47dcef3f8 100644 Binary files a/data/countryflags/SE.png and b/data/countryflags/SE.png differ diff --git a/data/countryflags/SG.png b/data/countryflags/SG.png new file mode 100644 index 000000000..c54a76083 Binary files /dev/null and b/data/countryflags/SG.png differ diff --git a/data/countryflags/SH.png b/data/countryflags/SH.png new file mode 100644 index 000000000..a9b6fc9fe Binary files /dev/null and b/data/countryflags/SH.png differ diff --git a/data/countryflags/SI.png b/data/countryflags/SI.png new file mode 100644 index 000000000..1e553fd55 Binary files /dev/null and b/data/countryflags/SI.png differ diff --git a/data/countryflags/SK.png b/data/countryflags/SK.png index 5bd535a18..9c927e8f2 100644 Binary files a/data/countryflags/SK.png and b/data/countryflags/SK.png differ diff --git a/data/countryflags/SL.png b/data/countryflags/SL.png new file mode 100644 index 000000000..5d1d42fe0 Binary files /dev/null and b/data/countryflags/SL.png differ diff --git a/data/countryflags/SM.png b/data/countryflags/SM.png new file mode 100644 index 000000000..967263052 Binary files /dev/null and b/data/countryflags/SM.png differ diff --git a/data/countryflags/SN.png b/data/countryflags/SN.png new file mode 100644 index 000000000..42a57326b Binary files /dev/null and b/data/countryflags/SN.png differ diff --git a/data/countryflags/SO.png b/data/countryflags/SO.png new file mode 100644 index 000000000..93815d278 Binary files /dev/null and b/data/countryflags/SO.png differ diff --git a/data/countryflags/SR.png b/data/countryflags/SR.png new file mode 100644 index 000000000..06fe40f30 Binary files /dev/null and b/data/countryflags/SR.png differ diff --git a/data/countryflags/SS.png b/data/countryflags/SS.png new file mode 100644 index 000000000..203c51da0 Binary files /dev/null and b/data/countryflags/SS.png differ diff --git a/data/countryflags/ST.png b/data/countryflags/ST.png new file mode 100644 index 000000000..748030a02 Binary files /dev/null and b/data/countryflags/ST.png differ diff --git a/data/countryflags/SV.png b/data/countryflags/SV.png index d313b0c18..d03b8be4a 100644 Binary files a/data/countryflags/SV.png and b/data/countryflags/SV.png differ diff --git a/data/countryflags/SX.png b/data/countryflags/SX.png new file mode 100644 index 000000000..61606935c Binary files /dev/null and b/data/countryflags/SX.png differ diff --git a/data/countryflags/SY.png b/data/countryflags/SY.png new file mode 100644 index 000000000..04342871c Binary files /dev/null and b/data/countryflags/SY.png differ diff --git a/data/countryflags/SZ.png b/data/countryflags/SZ.png new file mode 100644 index 000000000..bea99b2cb Binary files /dev/null and b/data/countryflags/SZ.png differ diff --git a/data/countryflags/TC.png b/data/countryflags/TC.png new file mode 100644 index 000000000..0abdc8456 Binary files /dev/null and b/data/countryflags/TC.png differ diff --git a/data/countryflags/TD.png b/data/countryflags/TD.png new file mode 100644 index 000000000..4ffcc6703 Binary files /dev/null and b/data/countryflags/TD.png differ diff --git a/data/countryflags/TF.png b/data/countryflags/TF.png new file mode 100644 index 000000000..d3095b292 Binary files /dev/null and b/data/countryflags/TF.png differ diff --git a/data/countryflags/TG.png b/data/countryflags/TG.png new file mode 100644 index 000000000..797f48cd0 Binary files /dev/null and b/data/countryflags/TG.png differ diff --git a/data/countryflags/TH.png b/data/countryflags/TH.png new file mode 100644 index 000000000..97e22df3d Binary files /dev/null and b/data/countryflags/TH.png differ diff --git a/data/countryflags/TJ.png b/data/countryflags/TJ.png new file mode 100644 index 000000000..54d9830fe Binary files /dev/null and b/data/countryflags/TJ.png differ diff --git a/data/countryflags/TK.png b/data/countryflags/TK.png new file mode 100644 index 000000000..623f69e0b Binary files /dev/null and b/data/countryflags/TK.png differ diff --git a/data/countryflags/TL.png b/data/countryflags/TL.png new file mode 100644 index 000000000..5c6f1ff73 Binary files /dev/null and b/data/countryflags/TL.png differ diff --git a/data/countryflags/TM.png b/data/countryflags/TM.png new file mode 100644 index 000000000..407675896 Binary files /dev/null and b/data/countryflags/TM.png differ diff --git a/data/countryflags/TN.png b/data/countryflags/TN.png new file mode 100644 index 000000000..10f9f696e Binary files /dev/null and b/data/countryflags/TN.png differ diff --git a/data/countryflags/TO.png b/data/countryflags/TO.png new file mode 100644 index 000000000..a7a703c98 Binary files /dev/null and b/data/countryflags/TO.png differ diff --git a/data/countryflags/TR.png b/data/countryflags/TR.png index 8bc78a722..2df563de2 100644 Binary files a/data/countryflags/TR.png and b/data/countryflags/TR.png differ diff --git a/data/countryflags/TT.png b/data/countryflags/TT.png new file mode 100644 index 000000000..c3f21b2b3 Binary files /dev/null and b/data/countryflags/TT.png differ diff --git a/data/countryflags/TV.png b/data/countryflags/TV.png new file mode 100644 index 000000000..ede2dbcaf Binary files /dev/null and b/data/countryflags/TV.png differ diff --git a/data/countryflags/TW.png b/data/countryflags/TW.png new file mode 100644 index 000000000..e3c48eb21 Binary files /dev/null and b/data/countryflags/TW.png differ diff --git a/data/countryflags/TZ.png b/data/countryflags/TZ.png new file mode 100644 index 000000000..38ad129d6 Binary files /dev/null and b/data/countryflags/TZ.png differ diff --git a/data/countryflags/UA.png b/data/countryflags/UA.png index 1356e7cb8..f9fc86cd2 100644 Binary files a/data/countryflags/UA.png and b/data/countryflags/UA.png differ diff --git a/data/countryflags/UG.png b/data/countryflags/UG.png new file mode 100644 index 000000000..92537e9b2 Binary files /dev/null and b/data/countryflags/UG.png differ diff --git a/data/countryflags/US.png b/data/countryflags/US.png index 96dfca89b..448930c28 100644 Binary files a/data/countryflags/US.png and b/data/countryflags/US.png differ diff --git a/data/countryflags/UY.png b/data/countryflags/UY.png new file mode 100644 index 000000000..4c950068f Binary files /dev/null and b/data/countryflags/UY.png differ diff --git a/data/countryflags/UZ.png b/data/countryflags/UZ.png new file mode 100644 index 000000000..0689f2178 Binary files /dev/null and b/data/countryflags/UZ.png differ diff --git a/data/countryflags/VA.png b/data/countryflags/VA.png new file mode 100644 index 000000000..e957e06d5 Binary files /dev/null and b/data/countryflags/VA.png differ diff --git a/data/countryflags/VC.png b/data/countryflags/VC.png new file mode 100644 index 000000000..60f90b55d Binary files /dev/null and b/data/countryflags/VC.png differ diff --git a/data/countryflags/VE.png b/data/countryflags/VE.png new file mode 100644 index 000000000..e36f7a4b0 Binary files /dev/null and b/data/countryflags/VE.png differ diff --git a/data/countryflags/VG.png b/data/countryflags/VG.png new file mode 100644 index 000000000..99069e251 Binary files /dev/null and b/data/countryflags/VG.png differ diff --git a/data/countryflags/VI.png b/data/countryflags/VI.png new file mode 100644 index 000000000..05fa911ad Binary files /dev/null and b/data/countryflags/VI.png differ diff --git a/data/countryflags/VN.png b/data/countryflags/VN.png new file mode 100644 index 000000000..c4e19d792 Binary files /dev/null and b/data/countryflags/VN.png differ diff --git a/data/countryflags/VU.png b/data/countryflags/VU.png new file mode 100644 index 000000000..228083ba4 Binary files /dev/null and b/data/countryflags/VU.png differ diff --git a/data/countryflags/WF.png b/data/countryflags/WF.png new file mode 100644 index 000000000..aeeba249d Binary files /dev/null and b/data/countryflags/WF.png differ diff --git a/data/countryflags/WS.png b/data/countryflags/WS.png new file mode 100644 index 000000000..5d16a5d9d Binary files /dev/null and b/data/countryflags/WS.png differ diff --git a/data/countryflags/XEN.png b/data/countryflags/XEN.png index 8388316c7..08e3ef29d 100644 Binary files a/data/countryflags/XEN.png and b/data/countryflags/XEN.png differ diff --git a/data/countryflags/XNI.png b/data/countryflags/XNI.png index 410615b0a..18083db03 100644 Binary files a/data/countryflags/XNI.png and b/data/countryflags/XNI.png differ diff --git a/data/countryflags/XSC.png b/data/countryflags/XSC.png index f3b9ee034..3002fd50f 100644 Binary files a/data/countryflags/XSC.png and b/data/countryflags/XSC.png differ diff --git a/data/countryflags/XWA.png b/data/countryflags/XWA.png index bae3809e9..3b6b86be5 100644 Binary files a/data/countryflags/XWA.png and b/data/countryflags/XWA.png differ diff --git a/data/countryflags/YE.png b/data/countryflags/YE.png new file mode 100644 index 000000000..736860073 Binary files /dev/null and b/data/countryflags/YE.png differ diff --git a/data/countryflags/ZA.png b/data/countryflags/ZA.png index c7e30de01..21aec1ed5 100644 Binary files a/data/countryflags/ZA.png and b/data/countryflags/ZA.png differ diff --git a/data/countryflags/ZM.png b/data/countryflags/ZM.png new file mode 100644 index 000000000..2160d5271 Binary files /dev/null and b/data/countryflags/ZM.png differ diff --git a/data/countryflags/ZW.png b/data/countryflags/ZW.png new file mode 100644 index 000000000..b6c870ca7 Binary files /dev/null and b/data/countryflags/ZW.png differ diff --git a/data/countryflags/index.txt b/data/countryflags/index.txt index f841c194b..3b60f1b29 100644 --- a/data/countryflags/index.txt +++ b/data/countryflags/index.txt @@ -1,765 +1,769 @@ - -##### country codes ##### - -##### custom ##### - -default -== -1 - -XEN -== 901 - -XNI -== 902 - -XSC -== 903 - -XWA -== 904 - -##### ISO 3166-1 based ##### - -#AF -#== 4 - -#AX -#== 248 - -#AL -#== 8 - -#DZ -#== 12 - -#AS -#== 16 - -#AD -#== 20 - -#AO -#== 24 - -#AI -#== 660 - -#AQ -#== 10 - -#AG -#== 28 - -AR -== 32 - -#AM -#== 51 - -#AW -#== 533 - -AU -== 36 - -AT -== 40 - -#AZ -#== 31 - -#BS -#== 44 - -#BH -#== 48 - -#BD -#== 50 - -#BB -#== 52 - -BY -== 112 - -BE -== 56 - -#BZ -#== 84 - -#BJ -#== 204 - -#BM -#== 60 - -#BT -#== 64 - -#BO -#== 68 - -#BQ -#== 535 - -BA -== 70 - -#BW -#== 72 - -#BV -#== 74 - -BR -== 76 - -#IO -#== 86 - -#BN -#== 96 - -BG -== 100 - -#BF -#== 854 - -#BI -#== 108 - -#KH -#== 116 - -#CM -#== 120 - -CA -== 124 - -#CV -#== 132 - -#KY -#== 136 - -#CF -#== 140 - -#TD -#== 148 - -CL -== 152 - -CN -== 156 - -#CX -#== 162 - -#CC -#== 166 - -CO -== 170 - -#KM -#== 174 - -#CG -#== 178 - -#CD -#== 180 - -#CK -#== 184 - -#CR -#== 188 - -#CI -#== 384 - -HR -== 191 - -#CU -#== 192 - -#CW -#== 531 - -#CY -#== 196 - -CZ -== 203 - -DK -== 208 - -#DJ -#== 262 - -#DM -#== 212 - -#DO -#== 214 - -#EC -#== 218 - -EG -== 818 - -SV -== 222 - -#GQ -#== 226 - -#ER -#== 232 - -EE -== 233 - -#ET -#== 231 - -#FK -#== 238 - -#FO -#== 234 - -#FJ -#== 242 - -FI -== 246 - -FR -== 250 - -#GF -#== 254 - -#PF -#== 258 - -#TF -#== 260 - -#GA -#== 266 - -#GM -#== 270 - -#GE -#== 268 - -DE -== 276 - -#GH -#== 288 - -#GI -#== 292 - -GR -== 300 - -#GL -#== 304 - -#GD -#== 308 - -#GP -#== 312 - -#GU -#== 316 - -#GT -#== 320 - -#GG -#== 831 - -#GN -#== 324 - -#GW -#== 624 - -#GY -#== 328 - -#HT -#== 332 - -#HM -#== 334 - -#VA -#== 336 - -#HN -#== 340 - -#HK -#== 344 - -HU -== 348 - -#IS -#== 352 - -IN -== 356 - -ID -== 360 - -IR -== 364 - -#IQ -#== 368 - -#IE -#== 372 - -#IM -#== 833 - -IL -== 376 - -IT -== 380 - -#JM -#== 388 - -#JP -#== 392 - -#JE -#== 832 - -#JO -#== 400 - -KZ -== 398 - -#KE -#== 404 - -#KI -#== 296 - -#KP -#== 408 - -#KR -#== 410 - -#KW -#== 414 - -#KG -#== 417 - -#LA -#== 418 - -LV -== 428 - -#LB -#== 422 - -#LS -#== 426 - -#LR -#== 430 - -#LY -#== 434 - -#LI -#== 438 - -LT -== 440 - -LU -== 442 - -#MO -#== 446 - -#MK -#== 807 - -#MG -#== 450 - -#MW -#== 454 - -#MY -#== 458 - -#MV -#== 462 - -#ML -#== 466 - -#MT -#== 470 - -#MH -#== 584 - -#MQ -#== 474 - -#MR -#== 478 - -#MU -#== 480 - -#YT -#== 175 - -MX -== 484 - -#FM -#== 583 - -#MD -#== 498 - -#MC -#== 492 - -#MN -#== 496 - -#ME -#== 499 - -#MS -#== 500 - -#MA -#== 504 - -#MZ -#== 508 - -#MM -#== 104 - -#NA -#== 516 - -#NR -#== 520 - -#NP -#== 524 - -NL -== 528 - -#NC -#== 540 - -#NZ -#== 554 - -#NI -#== 558 - -#NE -#== 562 - -#NG -#== 566 - -#NU -#== 570 - -#NF -#== 574 - -#MP -#== 580 - -NO -== 578 - -#OM -#== 512 - -PK -== 586 - -#PW -#== 585 - -#PS -#== 275 - -#PA -#== 591 - -#PG -#== 598 - -#PY -#== 600 - -#PE -#== 604 - -PH -== 608 - -#PN -#== 612 - -PL -== 616 - -PT -== 620 - -#PR -#== 630 - -#QA -#== 634 - -#RE -#== 638 - -RO -== 642 - -RU -== 643 - -#RW -#== 646 - -#BL -#== 652 - -#SH -#== 654 - -#KN -#== 659 - -#LC -#== 662 - -#MF -#== 663 - -#PM -#== 666 - -#VC -#== 670 - -#WS -#== 882 - -#SM -#== 674 - -#ST -#== 678 - -SA -== 682 - -#SN -#== 686 - -RS -== 688 - -#SC -#== 690 - -#SL -#== 694 - -#SG -#== 702 - -#SX -#== 534 - -SK -== 703 - -#SI -#== 705 - -#SB -#== 90 - -#SO -#== 706 - -ZA -== 710 - -#GS -#== 239 - -ES -== 724 - -#LK -#== 144 - -#SD -#== 736 - -#SR -#== 740 - -#SJ -#== 744 - -#SZ -#== 748 - -SE -== 752 - -CH -== 756 - -#SY -#== 760 - -#TW -#== 158 - -#TJ -#== 762 - -#TZ -#== 834 - -#TH -#== 764 - -#TL -#== 626 - -#TG -#== 768 - -#TK -#== 772 - -#TO -#== 776 - -#TT -#== 780 - -#TN -#== 788 - -TR -== 792 - -#TM -#== 795 - -#TC -#== 796 - -#TV -#== 798 - -#UG -#== 800 - -UA -== 804 - -#AE -#== 784 - -GB -== 826 - -US -== 840 - -#UM -#== 581 - -#UY -#== 858 - -#UZ -#== 860 - -#VU -#== 548 - -#VE -#== 862 - -#VN -#== 704 - -#VG -#== 92 - -#VI -#== 850 - -#WF -#== 876 - -#EH -#== 732 - -#YE -#== 887 - -#ZM -#== 894 - -#ZW -#== 716 + +##### country codes ##### + +##### custom ##### + +default +== -1 + +XEN +== 901 + +XNI +== 902 + +XSC +== 903 + +XWA +== 904 + +#south sudan, non official code# +SS +== 737 + +##### ISO 3166-1 based ##### + +AF +== 4 + +AX +== 248 + +AL +== 8 + +DZ +== 12 + +AS +== 16 + +AD +== 20 + +AO +== 24 + +AI +== 660 + +#AQ +#== 10 + +AG +== 28 + +AR +== 32 + +AM +== 51 + +AW +== 533 + +AU +== 36 + +AT +== 40 + +AZ +== 31 + +BS +== 44 + +BH +== 48 + +BD +== 50 + +BB +== 52 + +BY +== 112 + +BE +== 56 + +BZ +== 84 + +BJ +== 204 + +BM +== 60 + +BT +== 64 + +BO +== 68 + +#BQ +#== 535 + +BA +== 70 + +BW +== 72 + +#BV +#== 74 + +BR +== 76 + +IO +== 86 + +BN +== 96 + +BG +== 100 + +BF +== 854 + +BI +== 108 + +KH +== 116 + +CM +== 120 + +CA +== 124 + +CV +== 132 + +KY +== 136 + +CF +== 140 + +TD +== 148 + +CL +== 152 + +CN +== 156 + +CX +== 162 + +CC +== 166 + +CO +== 170 + +KM +== 174 + +CG +== 178 + +CD +== 180 + +CK +== 184 + +CR +== 188 + +CI +== 384 + +HR +== 191 + +CU +== 192 + +CW +== 531 + +CY +== 196 + +CZ +== 203 + +DK +== 208 + +DJ +== 262 + +DM +== 212 + +DO +== 214 + +EC +== 218 + +EG +== 818 + +SV +== 222 + +GQ +== 226 + +ER +== 232 + +EE +== 233 + +ET +== 231 + +FK +== 238 + +FO +== 234 + +FJ +== 242 + +FI +== 246 + +FR +== 250 + +GF +== 254 + +PF +== 258 + +TF +== 260 + +GA +== 266 + +GM +== 270 + +GE +== 268 + +DE +== 276 + +GH +== 288 + +GI +== 292 + +GR +== 300 + +GL +== 304 + +GD +== 308 + +GP +== 312 + +GU +== 316 + +GT +== 320 + +GG +== 831 + +GN +== 324 + +GW +== 624 + +GY +== 328 + +HT +== 332 + +#HM +#== 334 + +VA +== 336 + +HN +== 340 + +HK +== 344 + +HU +== 348 + +IS +== 352 + +IN +== 356 + +ID +== 360 + +IR +== 364 + +IQ +== 368 + +IE +== 372 + +IM +== 833 + +IL +== 376 + +IT +== 380 + +JM +== 388 + +JP +== 392 + +JE +== 832 + +JO +== 400 + +KZ +== 398 + +KE +== 404 + +KI +== 296 + +KP +== 408 + +KR +== 410 + +KW +== 414 + +KG +== 417 + +LA +== 418 + +LV +== 428 + +LB +== 422 + +LS +== 426 + +LR +== 430 + +LY +== 434 + +LI +== 438 + +LT +== 440 + +LU +== 442 + +MO +== 446 + +MK +== 807 + +MG +== 450 + +MW +== 454 + +MY +== 458 + +MV +== 462 + +ML +== 466 + +MT +== 470 + +MH +== 584 + +MQ +== 474 + +MR +== 478 + +MU +== 480 + +#YT +#== 175 + +MX +== 484 + +FM +== 583 + +MD +== 498 + +MC +== 492 + +MN +== 496 + +ME +== 499 + +MS +== 500 + +MA +== 504 + +MZ +== 508 + +MM +== 104 + +NA +== 516 + +NR +== 520 + +NP +== 524 + +NL +== 528 + +NC +== 540 + +NZ +== 554 + +NI +== 558 + +NE +== 562 + +NG +== 566 + +NU +== 570 + +NF +== 574 + +MP +== 580 + +NO +== 578 + +OM +== 512 + +PK +== 586 + +PW +== 585 + +#PS +#== 275 + +PA +== 591 + +PG +== 598 + +PY +== 600 + +PE +== 604 + +PH +== 608 + +PN +== 612 + +PL +== 616 + +PT +== 620 + +PR +== 630 + +QA +== 634 + +RE +== 638 + +RO +== 642 + +RU +== 643 + +RW +== 646 + +BL +== 652 + +SH +== 654 + +KN +== 659 + +LC +== 662 + +MF +== 663 + +PM +== 666 + +VC +== 670 + +WS +== 882 + +SM +== 674 + +ST +== 678 + +SA +== 682 + +SN +== 686 + +RS +== 688 + +SC +== 690 + +SL +== 694 + +SG +== 702 + +SX +== 534 + +SK +== 703 + +SI +== 705 + +SB +== 90 + +SO +== 706 + +ZA +== 710 + +GS +== 239 + +ES +== 724 + +LK +== 144 + +SD +== 736 + +SR +== 740 + +#SJ +#== 744 + +SZ +== 748 + +SE +== 752 + +CH +== 756 + +SY +== 760 + +TW +== 158 + +TJ +== 762 + +TZ +== 834 + +TH +== 764 + +TL +== 626 + +TG +== 768 + +TK +== 772 + +TO +== 776 + +TT +== 780 + +TN +== 788 + +TR +== 792 + +TM +== 795 + +TC +== 796 + +TV +== 798 + +UG +== 800 + +UA +== 804 + +AE +== 784 + +GB +== 826 + +US +== 840 + +#UM +#== 581 + +UY +== 858 + +UZ +== 860 + +VU +== 548 + +VE +== 862 + +VN +== 704 + +VG +== 92 + +VI +== 850 + +WF +== 876 + +EH +== 732 + +YE +== 887 + +ZM +== 894 + +ZW +== 716 diff --git a/data/editor/desert_main.rules b/data/editor/desert_main.rules new file mode 100644 index 000000000..0102a1973 --- /dev/null +++ b/data/editor/desert_main.rules @@ -0,0 +1,227 @@ +[Desert] +Index 1 +BaseTile + +#random +Index 2 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 3 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +#top +Index 16 +Pos 0 -1 EMPTY + +#right +Index 17 +Pos 1 0 EMPTY + +#bottom +Index 18 +Pos 0 1 EMPTY + +#left +Index 19 +Pos -1 0 EMPTY + +#corner top-right +Index 33 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +#corner top-left +Index 32 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-left +Index 35 +Pos 0 1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-right +Index 34 +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +#inside corner top-right +Index 51 +Pos -1 1 EMPTY +Pos -1 0 FULL +Pos 0 1 FULL + +#inside corner top-left +Index 50 +Pos 1 1 EMPTY +Pos 1 0 FULL +Pos 0 1 FULL + +#inside corner bottom-left +Index 49 +Pos 1 -1 EMPTY +Pos 1 0 FULL +Pos 0 -1 FULL + +#inside corner bottom-right +Index 48 +Pos -1 -1 EMPTY +Pos -1 0 FULL +Pos 0 -1 FULL + +[Mine] +Index 81 +BaseTile + +#random +Index 82 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +Index 83 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +Index 84 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +Index 85 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +Index 86 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +Index 100 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +Index 101 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +Index 102 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +Index 117 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +Index 118 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +Index 133 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +Index 134 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 500 + +#top +Index 96 +Pos 0 -1 EMPTY + +#right +Index 97 +Pos 1 0 EMPTY + +#bottom +Index 98 +Pos 0 1 EMPTY + +#left +Index 99 +Pos -1 0 EMPTY + +#corner top-right +Index 113 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +#corner top-left +Index 112 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-left +Index 115 +Pos 0 1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-right +Index 114 +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +#inside corner top-right +Index 131 +Pos -1 1 EMPTY +Pos -1 0 FULL +Pos 0 1 FULL + +#inside corner top-left +Index 130 +Pos 1 1 EMPTY +Pos 1 0 FULL +Pos 0 1 FULL + +#inside corner bottom-left +Index 129 +Pos 1 -1 EMPTY +Pos 1 0 FULL +Pos 0 -1 FULL + +#inside corner bottom-right +Index 128 +Pos -1 -1 EMPTY +Pos -1 0 FULL +Pos 0 -1 FULL diff --git a/data/editor/grass_main.rules b/data/editor/grass_main.rules new file mode 100644 index 000000000..b909eb0e8 --- /dev/null +++ b/data/editor/grass_main.rules @@ -0,0 +1,225 @@ +[Grass] +Index 1 +BaseTile + +#random bones +Index 2 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 3 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 66 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 67 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 68 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 +#--------- + +#top +Index 16 +Pos 0 -1 EMPTY + +#right +Index 21 +Pos 1 0 EMPTY + +#bottom +Index 52 +Pos 0 1 EMPTY + +#left +Index 20 +Pos -1 0 EMPTY + +#corner top-right +Index 5 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +#corner top-left +Index 4 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-left +Index 36 +Pos 0 1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-right +Index 37 +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +#inside corner top-right +Index 54 +Pos -1 1 EMPTY +Pos -1 0 FULL +Pos 0 1 FULL + +#inside corner top-left +Index 53 +Pos 1 1 EMPTY +Pos 1 0 FULL +Pos 0 1 FULL + +#inside corner bottom-left +Index 49 +Pos 1 -1 EMPTY +Pos 1 0 FULL +Pos 0 -1 FULL + +#inside corner bottom-right +Index 48 +Pos -1 -1 EMPTY +Pos -1 0 FULL +Pos 0 -1 FULL + +#right bottom +Index 22 +Pos -1 0 EMPTY +Pos -1 1 FULL +Pos 0 1 FULL + +#left bottom +Index 38 +Pos 1 0 EMPTY +Pos 1 1 FULL +Pos 0 1 FULL + +#top corner right 2 +Index 33 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY +Pos 1 1 FULL + +#top corner left 2 +Index 32 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos -1 1 FULL + +[Cave] +Index 13 +BaseTile + +#random bones +Index 29 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 42 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 43 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 44 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 45 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 +#--------- + +#top +Index 26 +Pos 0 -1 EMPTY + +#right +Index 25 +Pos 1 0 EMPTY + +#bottom +Index 10 +Pos 0 1 EMPTY + +#left +Index 24 +Pos -1 0 EMPTY + +#corner top-right +Index 9 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +#corner top-left +Index 8 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-left +Index 40 +Pos 0 1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-right +Index 41 +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +#inside corner top-right +Index 12 +Pos -1 1 EMPTY +Pos -1 0 FULL +Pos 0 1 FULL + +#inside corner top-left +Index 11 +Pos 1 1 EMPTY +Pos 1 0 FULL +Pos 0 1 FULL + +#inside corner bottom-left +Index 27 +Pos 1 -1 EMPTY +Pos 1 0 FULL +Pos 0 -1 FULL + +#inside corner bottom-right +Index 28 +Pos -1 -1 EMPTY +Pos -1 0 FULL +Pos 0 -1 FULL diff --git a/data/editor/jungle_main.rules b/data/editor/jungle_main.rules new file mode 100644 index 000000000..ada1f3ac4 --- /dev/null +++ b/data/editor/jungle_main.rules @@ -0,0 +1,266 @@ +[Jungle] +Index 1 +BaseTile + +#random bricks +Index 66 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 200 + +Index 67 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 200 + +#top +Index 16 +Pos 0 -1 EMPTY + +Index 96 +Pos 0 -1 EMPTY +Random 15 + +Index 97 +Pos 0 -1 EMPTY +Random 15 + +Index 98 +Pos 0 -1 EMPTY +Random 15 + +#right +Index 21 +Pos 1 0 EMPTY + +#bottom +Index 52 +Pos 0 1 EMPTY + +Index 99 +Pos 0 1 EMPTY +Random 10 + +Index 100 +Pos 0 1 EMPTY +Random 10 + +Index 101 +Pos 0 1 EMPTY +Random 10 + +#left +Index 21 XFLIP +Pos -1 0 EMPTY + +#corner top-right +Index 5 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +#corner top-left +Index 5 XFLIP +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-left +Index 37 XFLIP +Pos 0 1 EMPTY +Pos -1 0 EMPTY + +Index 39 XFLIP +Pos 0 1 EMPTY +Pos -1 0 EMPTY +Random 2 + +#corner bottom-right +Index 37 +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +Index 39 +Pos 0 1 EMPTY +Pos 1 0 EMPTY +Random 2 + +#inside corner top-right +Index 54 +Pos -1 1 EMPTY +Pos -1 0 FULL +Pos 0 1 FULL + +Index 53 XFLIP +Pos -1 1 EMPTY +Pos -1 0 FULL +Pos 0 1 FULL +Random 2 + +#inside corner top-left +Index 54 XFLIP +Pos 1 1 EMPTY +Pos 1 0 FULL +Pos 0 1 FULL + +Index 53 +Pos 1 1 EMPTY +Pos 1 0 FULL +Pos 0 1 FULL +Random 2 + +#inside corner bottom-left +Index 48 XFLIP +Pos 1 -1 EMPTY +Pos 1 0 FULL +Pos 0 -1 FULL + +Index 49 +Pos 1 -1 EMPTY +Pos 1 0 FULL +Pos 0 -1 FULL +Random 3 + +Index 50 YFLIP +Pos 1 -1 EMPTY +Pos 1 0 FULL +Pos 0 -1 FULL +Random 3 + +#inside corner bottom-right +Index 48 +Pos -1 -1 EMPTY +Pos -1 0 FULL +Pos 0 -1 FULL + +Index 49 XFLIP +Pos -1 -1 EMPTY +Pos -1 0 FULL +Pos 0 -1 FULL +Random 3 + +Index 51 YFLIP +Pos -1 -1 EMPTY +Pos -1 0 FULL +Pos 0 -1 FULL +Random 3 + +#right bottom +Index 22 +Pos -1 0 EMPTY +Pos -1 1 FULL +Pos 0 1 FULL + +#left bottom +Index 22 XFLIP +Pos 1 0 EMPTY +Pos 1 1 FULL +Pos 0 1 FULL + +#top corner right 2 +Index 33 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY +Pos 1 1 FULL + +#top corner left 2 +Index 32 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY +Pos -1 1 FULL + +[Jungle dark] +Index 13 +BaseTile + +#random bricks +Index 42 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 43 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 44 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 + +Index 45 +Pos 0 1 FULL +Pos 0 -1 FULL +Pos 1 0 FULL +Pos -1 0 FULL +Random 150 +#--------- + +#top +Index 26 +Pos 0 -1 EMPTY + +#right +Index 25 +Pos 1 0 EMPTY + +#bottom +Index 10 +Pos 0 1 EMPTY + +#left +Index 24 +Pos -1 0 EMPTY + +#corner top-right +Index 9 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +#corner top-left +Index 8 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-left +Index 40 +Pos 0 1 EMPTY +Pos -1 0 EMPTY + +#corner bottom-right +Index 41 +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +#inside corner top-right +Index 12 +Pos -1 1 EMPTY +Pos -1 0 FULL +Pos 0 1 FULL + +#inside corner top-left +Index 11 +Pos 1 1 EMPTY +Pos 1 0 FULL +Pos 0 1 FULL + +#inside corner bottom-left +Index 27 +Pos 1 -1 EMPTY +Pos 1 0 FULL +Pos 0 -1 FULL + +#inside corner bottom-right +Index 28 +Pos -1 -1 EMPTY +Pos -1 0 FULL +Pos 0 -1 FULL diff --git a/data/editor/winter_main.rules b/data/editor/winter_main.rules new file mode 100644 index 000000000..eaeaec8e3 --- /dev/null +++ b/data/editor/winter_main.rules @@ -0,0 +1,177 @@ +[Winter] +Index 1 +BaseTile + +#top +Index 17 +Pos 0 -1 EMPTY + +Index 18 +Pos 0 -1 EMPTY +Pos -1 0 INDEX 17 + +Index 19 +Pos 0 -1 EMPTY +Pos -1 0 INDEX 18 + +#bottom +Index 97 +Pos 0 1 EMPTY + +Index 98 +Pos 0 1 EMPTY +Pos -1 0 INDEX 97 + +Index 99 +Pos 0 1 EMPTY +Pos -1 0 INDEX 98 + +#right +Index 2 XFLIP +Pos 1 0 EMPTY + +#left +Index 2 +Pos -1 0 EMPTY + +#corner top right +Index 20 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +Index 24 +Pos 0 -1 EMPTY +Pos 1 0 EMPTY +Pos -1 0 INDEX 17 + +#corner top left +Index 16 +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +#corner bottom right +Index 100 +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +#corner bottom left +Index 96 +Pos 0 1 EMPTY +Pos -1 0 EMPTY + +#inside corner top right +Index 8 +Pos 0 1 FULL +Pos -1 0 FULL +Pos -1 1 EMPTY + +#inside corner top left +Index 7 +Pos 0 1 FULL +Pos 1 0 FULL +Pos 1 1 EMPTY + +#inside corner bottom right +Index 4 +Pos 0 -1 FULL +Pos -1 0 FULL +Pos -1 -1 EMPTY + +#inside corner bottom left +Index 3 +Pos 0 -1 FULL +Pos 1 0 FULL +Pos 1 -1 EMPTY + +#one tile height +Index 113 +Pos 0 1 EMPTY +Pos 0 -1 EMPTY + +Index 114 +Pos 0 1 EMPTY +Pos 0 -1 EMPTY +Pos -1 0 INDEX 113 + +Index 115 +Pos 0 1 EMPTY +Pos 0 -1 EMPTY +Pos -1 0 INDEX 114 + +#one tile height right +Index 116 +Pos 0 1 EMPTY +Pos 0 -1 EMPTY +Pos 1 0 EMPTY + +Index 120 +Pos 0 1 EMPTY +Pos 0 -1 EMPTY +Pos 1 0 EMPTY +Pos -1 0 INDEX 113 + +#one tile height left +Index 112 +Pos 0 1 EMPTY +Pos 0 -1 EMPTY +Pos -1 0 EMPTY + +#one tile height link right +Index 6 +Pos -1 -1 EMPTY +Pos -1 0 FULL +Pos 0 -1 FULL +Pos -1 1 EMPTY + +#one tile height link left +Index 5 +Pos 1 -1 EMPTY +Pos 1 0 FULL +Pos 0 -1 FULL +Pos 1 1 EMPTY + +[Winter dark] +Index 177 +BaseTile + +#bottom +Index 194 +Pos 0 1 EMPTY + +Index 195 +Pos 0 1 EMPTY +Pos -1 0 INDEX 194 + +Index 196 +Pos 0 1 EMPTY +Pos -1 0 INDEX 195 + +#right +Index 178 XFLIP +Pos 1 0 EMPTY + +#left +Index 178 +Pos -1 0 EMPTY + +#corner bottom right +Index 197 +Pos 0 1 EMPTY +Pos 1 0 EMPTY + +#corner bottom left +Index 193 +Pos 0 1 EMPTY +Pos -1 0 EMPTY + +#inside corner top right +Index 180 +Pos 0 1 FULL +Pos -1 0 FULL +Pos -1 1 EMPTY + +#inside corner top left +Index 179 +Pos 0 1 FULL +Pos 1 0 FULL +Pos 1 1 EMPTY diff --git a/data/languages/belarusian.txt b/data/languages/belarusian.txt new file mode 100644 index 000000000..672995c0f --- /dev/null +++ b/data/languages/belarusian.txt @@ -0,0 +1,679 @@ + +##### 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 +== 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 +== REC %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 +== Без ліміту + +##### needs translation ##### + +##### old translations ##### + diff --git a/data/languages/bosnian.txt b/data/languages/bosnian.txt index 0c87d4ba5..4888db6ab 100644 --- a/data/languages/bosnian.txt +++ b/data/languages/bosnian.txt @@ -145,6 +145,9 @@ Delete demo Demo details == Podaci o demo-snimku +Demofile: %s +== Demo-snimak: %s + Demos == Demo @@ -388,9 +391,15 @@ Pistol Play == Pogledaj +Play background music +== Pozadinska muzika + Player == Igrač +Player country: +== Država + Player options == Postavke igrača @@ -508,7 +517,7 @@ Shotgun Show chat == Prikaži chat -Show friends +Show friends only == Prikaži prijatelje Show ingame HUD @@ -538,6 +547,12 @@ Sound volume Spectate == Posmatraj +Spectate next +== Posmatraj narednog + +Spectate previous +== Posmatraj prethodnog + Spectator mode == Posmatrački mod @@ -553,6 +568,9 @@ Standard map Stop record == Prekini snimanje +Strict gametype filter +== Striktan filter tipa igre + Sudden Death == Iznenadna smrt @@ -657,20 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/brazilian_portuguese.txt b/data/languages/brazilian_portuguese.txt new file mode 100644 index 000000000..a83a0d057 --- /dev/null +++ b/data/languages/brazilian_portuguese.txt @@ -0,0 +1,679 @@ + +##### translated strings ##### + +%d Bytes +== %d Bytes + +%d of %d servers, %d players +== %d de %d servidores, %d jogadores + +%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 + +Alpha +== Alpha + +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 + +Crc: +== Crc: + +Created: +== Criado: + +Current +== Atual + +Current version: %s +== Versão atual: %s + +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 + +Display Modes +== Modos de exibição + +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 + +FSAA samples +== Amostras FSAA + +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 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 + +Host address +== Endereço do servidor + +Hue +== Matiz + +Info +== Informações + +Internet +== Internet + +Invalid Demo +== Demo inválido + +Join blue +== Azul + +Join game +== Entrar no jogo + +Join red +== Vermelho + +Jump +== Pular + +Kick player +== Expulsar jogador + +LAN +== LAN + +Language +== Idioma + +Length: +== Duração: + +Lht. +== Luminos. + +Loading +== Carregando + +MOTD +== MOTD + +Map +== Mapa + +Map: +== Mapa: + +Max Screenshots +== Máximo de capturas de tela + +Max demos +== Máximo de demos + +Maximum ping: +== Ping máximo: + +Miscellaneous +== Diversos + +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 + +Name plates size +== Tamanho dos apelidos + +Netversion: +== Netversion + +New name: +== Novo nome: + +News +== Novidades + +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 + +Quick search: +== Pesquisa rápida: + +Quit +== Sair + +Quit anyway? +== Sair mesmo assim? + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Motivo: + +Record demo +== Gravar demo + +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 + +Reset to defaults +== Resetar configurações + +Rifle +== Laser + +Round +== Rodada + +Sample rate +== Frequência do som + +Sat. +== Saturação + +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 supported +== Mostrar apenas modos suportados + +Size: +== Tamanho: + +Skins +== Skins + +Sound +== Som + +Sound error +== Erro de som + +Sound volume +== Volume do 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 + +Type: +== Tipo: + +UI Color +== Cor do menu + +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 + +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. + +Your skin +== Sua skin + +no limit +== sem limite + +##### needs translation ##### + +##### old translations ##### + diff --git a/data/languages/bulgarian.txt b/data/languages/bulgarian.txt index 89b95af72..38df78916 100644 --- a/data/languages/bulgarian.txt +++ b/data/languages/bulgarian.txt @@ -53,16 +53,16 @@ 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 == Записвай демота автоматично @@ -145,6 +145,9 @@ Delete demo Demo details == Детайли за демото +Demofile: %s +== Демофайл: %s + Demos == Демота @@ -347,7 +350,7 @@ News == Новини Next weapon -== Следващо оръжие +== След. оръжие Nickname == Ник @@ -362,7 +365,7 @@ No servers found == Няма намерени сървъри No servers match your filter criteria -== Няма сървъри съответстващи вашите настройки +== Няма сървъри съответстващи твоите настройки Ok == Добре @@ -388,9 +391,15 @@ Pistol Play == Възпроизведи +Play background music +== Пусни музика за фон + Player == Играч +Player country: +== Страна на играча: + Player options == Настройки на Играча @@ -508,7 +517,7 @@ Shotgun Show chat == Показвай чата -Show friends +Show friends only == Покажи приятели Show ingame HUD @@ -536,7 +545,10 @@ Sound volume == Сила на звука Spectate -== Наблюдавайте +== Наблюдавай + +Spectate next +== Наблюдавай след. Spectator mode == Наблюдател @@ -553,6 +565,9 @@ Standard map Stop record == Спри записа +Strict gametype filter +== Стриктен геймтайп филтър + Sudden Death == Внезапна Смърт @@ -566,13 +581,13 @@ Team chat == Отборен чат Teeworlds %s is out! Download it at www.teeworlds.com! -== Излезна Teeworlds %s! Свалете новата версия от 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. == Този сървър използва нестандартен тунинг на стандартен тип ига. @@ -587,7 +602,7 @@ Time limit: %d min == Лимит на Времето: %d мин Try again -== Опитайте Отново +== Опитай пак Type == Тип @@ -620,10 +635,10 @@ Version: == Версия: Vote command: -== Гласувай за команда: +== Вот команда: Vote description: -== Гласувай за обяснение: +== Вот дефиниция: Vote no == Против @@ -647,39 +662,18 @@ Yes == Да You must restart the game for all settings to take effect. -== Трябва да рестартирате играта за да поемат ефект новите настройки. +== Трябва да рестартираш играта за да поемат ефект новите настройки. Your skin -== Вашият модел +== Твоят модел no limit == Без лимит ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Spectate next -== - Spectate previous == -Strict gametype filter -== - ##### old translations ##### -Page %d of %d -== Страница %d/%d - -Next -== Нататък - -Prev -== Назад - diff --git a/data/languages/czech.txt b/data/languages/czech.txt index 33aca462b..5d50f47ae 100644 --- a/data/languages/czech.txt +++ b/data/languages/czech.txt @@ -5,7 +5,7 @@ == %d Bajtů %d of %d servers, %d players -== %d z %d serverů, %d hráčů +== Serverů: %d z %d, Hráčů: %d %d%% loaded == %d%% načteno @@ -26,7 +26,7 @@ == Zbývá sekund: %i %s wins! -== %s zvítězil! +== Vítězí %s! -Page %d- == -Strana %d- @@ -53,7 +53,7 @@ Always show name plates == Vždy zobrazovat Are you sure that you want to delete the demo? -== Opravdu chcete smazat toto demo? +== Opravdu chcete smazat tento záznam? Are you sure that you want to quit? == Opravdu chcete ukončit Teeworlds? @@ -65,7 +65,7 @@ As this is the first time you launch the game, please enter your nick name below == 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 -== Nahrávat dema automaticky +== Automaticky nahrávat záznamy Automatically take game over screenshot == Na konci kola vyfotit obrazovku @@ -116,7 +116,7 @@ Controls == Ovládání Count players only -== Započítat hrajíci hráče +== Započítat hrající hráče Country == Národnost @@ -140,13 +140,16 @@ Delete == Smazat Delete demo -== Smazat demo +== Smazat záznam Demo details == Detaily záznamu: +Demofile: %s +== Soubor záznamu: %s + Demos -== Dema +== Záznamy Disconnect == Odpojit @@ -176,7 +179,7 @@ Error == Chyba Error loading demo -== Chyba načítání dema +== Chyba načítání záznamu FSAA samples == FSAA vzorkování @@ -218,7 +221,7 @@ Game info == Hra Game over -== Ukončit hru +== Konec hry Game type == Herní mód @@ -260,7 +263,7 @@ Internet == Internet Invalid Demo -== Neplatné demo +== Neplatný záznam Join blue == Modří @@ -305,7 +308,7 @@ Max Screenshots == Maximální počet vyfocených obrazovek Max demos -== Maximální počet dem +== Maximální počet záznamů Maximum ping: == Maximální ping: @@ -329,7 +332,7 @@ Movement == Pohyb Mute when not active -== Při nečinosti ztlumit zvuk +== Při nečinnosti ztlumit zvuk Name == Jméno @@ -359,7 +362,7 @@ No password == Bez hesla No servers found -== Žádny server nenalezen +== Žádný server nenalezen No servers match your filter criteria == Žádný server neodpovídá požadavkům filtru @@ -388,9 +391,15 @@ Pistol Play == Přehrát +Play background music +== Přehrávat hudbu na pozadí + Player == Hráč +Player country: +== Národnost: + Player options == Nastavení hráčů @@ -422,7 +431,7 @@ Reason: == Důvod: Record demo -== Záznam dema +== Zaznamenat Red team == Červení @@ -491,13 +500,13 @@ Server details == Detaily serveru Server filter -== Filtr serverů: +== Filtr serverů Server info == Informace Server not full -== Možnost připojení +== Možnost připojit se Settings == Nastavení @@ -508,7 +517,7 @@ Shotgun Show chat == Zobrazit chat -Show friends +Show friends only == Zobrazit přátele Show ingame HUD @@ -538,6 +547,12 @@ Sound volume Spectate == Pozorovat +Spectate next +== Pozorovat dalšího + +Spectate previous +== Pozorovat minulého + Spectator mode == Nastavit pozorování @@ -551,13 +566,16 @@ Standard map == Základní mapy Stop record -== Zastavit nahránání +== Ukončit záznam + +Strict gametype filter +== Pouze tento herní mód Sudden Death == Náhlá smrt Switch weapon on pickup -== Automaticky přepínat zbraň na nově získanou +== Přehazovat zbraň na nově sebranou Team == Týmu @@ -566,7 +584,7 @@ Team chat == Týmový chat Teeworlds %s is out! Download it at www.teeworlds.com! -== Nejnovější verze Teeworlds je %s! Stáhněte si ji na www.teeworlds.com! +== Stáhněte si nové Teeworlds %s na www.teeworlds.com! Texture Compression == Komprimované textury @@ -599,7 +617,7 @@ UI Color == Barvy uživatelského rozhraní Unable to delete the demo -== Demo se nepodařilo smazat +== Záznam se nepodařilo smazat Unable to rename the demo == Záznam nelze přejmenovat @@ -608,7 +626,7 @@ Use sounds == Povolit zvuk Use team colors for name plates -== Obarvit jména hráčů podle týmů +== Obarvit jména hráčů podle týmu V-Sync == Vertikální synchronizace @@ -641,7 +659,7 @@ Weapon == Zbraně Welcome to Teeworlds -== Vítejte v Teeworls +== Vítejte v Teeworlds Yes == Ano @@ -657,20 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/danish.txt b/data/languages/danish.txt index db69e06c2..d8d0e7066 100644 --- a/data/languages/danish.txt +++ b/data/languages/danish.txt @@ -1,4 +1,3 @@ - ##### translated strings ##### %d Bytes @@ -398,7 +397,7 @@ Players == Spillere Please balance teams! -== Venligst balancerer hold! +== Balancer venligst holdet! Prev. weapon == Foregående våben @@ -482,7 +481,7 @@ Scoreboard == Resultatliste Screenshot -== Screenshot +== Skærmbillede Server address: == Serveradresse @@ -508,7 +507,7 @@ Shotgun Show chat == Vis chat -Show friends +Show friends only == Vis venner Show ingame HUD @@ -548,7 +547,7 @@ Standard gametype == Standard spilletype Standard map -== Standard +== Standard map Stop record == Stop optagelse @@ -596,13 +595,13 @@ Type: == Type: UI Color -== Brugergrænsefladefarve +== Brugergrænseflade farve Unable to delete the demo == Kunne ikke slette demoen Unable to rename the demo -== Kunne omdøbe demoen +== Kunne ikke omdøbe demoen Use sounds == Anvend lydeffekter @@ -655,25 +654,25 @@ Your skin no limit == Ingen grænse -##### needs translation ##### - Demofile: %s -== +== Demo fil: %s Play background music -== +== Spil baggrunds musik + +Player country: +== Spillernes land: Spectate next -== +== Kig på næste Spectate previous -== +== Kig på forrige Strict gametype filter -== +== Streng spiltype filter + +##### needs translation ##### ##### old translations ##### -##### translated strings ##### -== - diff --git a/data/languages/dutch.txt b/data/languages/dutch.txt index fbd488a1d..4375f7847 100644 --- a/data/languages/dutch.txt +++ b/data/languages/dutch.txt @@ -62,7 +62,7 @@ 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 nicknaam kiezen. Doe dat hieronder. Het is aanbevolen om de instellingen te controleren, voordat je een spel start. +== 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 @@ -145,6 +145,9 @@ Delete demo Demo details == Demo details +Demofile: %s +== Demobestand: %s + Demos == Demo's @@ -350,7 +353,7 @@ Next weapon == Volgend wapen Nickname -== Nicknaam +== Bijnaam No == Nee @@ -394,6 +397,9 @@ Play background music Player == Speler +Player country: +== Speler land: + Player options == Speler opties @@ -428,10 +434,10 @@ Record demo == Neem demo op Red team -== Rood +== Rode team Red team wins! -== Rood wint! +== Rode team wint! Refresh == Vernieuwen @@ -511,7 +517,7 @@ Shotgun Show chat == Laat chat zien -Show friends +Show friends only == Laat vrienden zien Show ingame HUD @@ -562,6 +568,9 @@ Standard map Stop record == Stop met opnemen +Strict gametype filter +== Strikt speltype filter + Sudden Death == Sudden Death @@ -590,7 +599,7 @@ There's an unsaved map in the editor, you might want to save it before you quit == Er is een onopgeslagen kaart in de editor, misschien wil je deze opslaan voordat je stopt. Time limit -== Tijdlimiet +== Tijdslimiet Time limit: %d min == Tijdslimiet: %d minuten @@ -666,11 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/finnish.txt b/data/languages/finnish.txt index 6f418c4bd..77ce6cf79 100644 --- a/data/languages/finnish.txt +++ b/data/languages/finnish.txt @@ -145,6 +145,9 @@ Delete demo Demo details == Demon tiedot +Demofile: %s +== Demotiedosto: %s + Demos == Demot @@ -388,9 +391,15 @@ Pistol Play == Toista +Play background music +== Soita taustamusiikki + Player == Pelaaja +Player country: +== Pelaajan maa: + Player options == Pelaajavalinnat @@ -508,8 +517,8 @@ Shotgun Show chat == Näytä chatti -Show friends -== Näytä ystävät +Show friends only +== Näytä vain ystävät Show ingame HUD == Näytä peli-HUD @@ -538,6 +547,12 @@ Sound volume Spectate == Katso +Spectate next +== Katso seuraavaa + +Spectate previous +== Katso edellistä + Spectator mode == Katsojatila @@ -553,6 +568,9 @@ Standard map Stop record == Lopeta nauhoit. +Strict gametype filter +== Tarkka pelityyppisuodin + Sudden Death == Äkkikuolema @@ -657,20 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/french.txt b/data/languages/french.txt index 137e36820..5ea3b935a 100644 --- a/data/languages/french.txt +++ b/data/languages/french.txt @@ -145,6 +145,9 @@ Delete demo Demo details == Détails de la démo +Demofile: %s +== Démo : %s + Demos == Démos @@ -224,7 +227,7 @@ Game type == Type de jeu Game types: -== Types de jeu: +== Types de jeu : General == Général @@ -308,7 +311,7 @@ Max demos == Nombre maximum de démos Maximum ping: -== Ping maximum: +== Ping maximum : Miscellaneous == Divers @@ -388,9 +391,15 @@ Pistol Play == Jouer +Play background music +== Jouer la musique de fond + Player == Joueur +Player country: +== Nation joueurs : + Player options == Joueurs @@ -508,7 +517,7 @@ Shotgun Show chat == Montrer le chat -Show friends +Show friends only == Montrer les amis Show ingame HUD @@ -538,6 +547,12 @@ Sound volume Spectate == Regarder +Spectate next +== Suivre suivant + +Spectate previous +== Suivre précédent + Spectator mode == Menu spectateur @@ -553,6 +568,9 @@ Standard map Stop record == Arrêter +Strict gametype filter +== Types de jeu exacts + Sudden Death == Mort Subite @@ -657,20 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/german.txt b/data/languages/german.txt index c3e74bbf4..17720d60c 100644 --- a/data/languages/german.txt +++ b/data/languages/german.txt @@ -397,6 +397,9 @@ Play background music Player == Spieler +Player country: +== Spielerland: + Player options == Spieleroptionen @@ -514,7 +517,7 @@ Shotgun Show chat == Chat anzeigen -Show friends +Show friends only == Nur Freunde zeigen Show ingame HUD diff --git a/data/languages/hungarian.txt b/data/languages/hungarian.txt new file mode 100644 index 000000000..49f3116ac --- /dev/null +++ b/data/languages/hungarian.txt @@ -0,0 +1,679 @@ + +##### translated strings ##### + +%d Bytes +== %d Bit + +%d of %d servers, %d players +== %d Szerver, %d Játékos + +%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 + +Alpha +== Alpha + +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 + +Crc: +== Crc: + +Created: +== Készítette: + +Current +== Aktuális + +Current version: %s +== Aktuális verzió: %s + +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 + +Display Modes +== Kijelző módok + +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 + +FSAA samples +== FSAA mintáku + +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 + +Host address +== Szerver címe + +Hue +== Színárnyalat + +Info +== Infó + +Internet +== Internet + +Invalid Demo +== Érvénytelen demó + +Join blue +== Kékhez lépés + +Join game +== Csatlakozás a játékhoz + +Join red +== Piroshoz lépés + +Jump +== Ugrás + +Kick player +== Játékos kirúgása + +LAN +== Helyi + +Language +== Nyelv + +Length: +== Hossza: + +Lht. +== Lht. + +Loading +== Betöltés + +MOTD +== Napi üzenet + +Map +== Pálya + +Map: +== Pálya: + +Max Screenshots +== Maximum Fotó + +Max demos +== Maximum Demó + +Maximum ping: +== Maximum Ping: + +Miscellaneous +== Vegyes + +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 + +Name plates size +== Névtáblák mérete + +Netversion: +== Netes verzió: + +New name: +== Új Név: + +News +== Hírek + +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 + +Quick search: +== Gyors keresés: + +Quit +== Kilépés + +Quit anyway? +== Mindenképpen kilép? + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Indok: + +Record demo +== Demó felvétele + +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 + +Reset to defaults +== Visszaállítás az alapértelmezettre + +Rifle +== Lézer + +Round +== Menet + +Sample rate +== Mintavételi frekvencia + +Sat. +== Sat. + +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 + +Size: +== Méret: + +Skins +== Skinek + +Sound +== Hang + +Sound error +== Hang hiba + +Sound volume +== Hangerő + +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 + +Type: +== Típus: + +UI Color +== UI Szín + +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ó + +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. + +Your skin +== Te skined + +no limit +== Nincs korlát + +##### needs translation ##### + +Demofile: %s +== + +Play background music +== + +Player country: +== + +Spectate next +== + +Spectate previous +== + +Strict gametype filter +== + +##### old translations ##### + diff --git a/data/languages/index.txt b/data/languages/index.txt index fa5862c9f..b7a6179b3 100644 --- a/data/languages/index.txt +++ b/data/languages/index.txt @@ -1,62 +1,94 @@ ##### 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 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 index 6ee030544..d566da491 100644 --- a/data/languages/italian.txt +++ b/data/languages/italian.txt @@ -2,31 +2,31 @@ ##### translated strings ##### %d Bytes -== %d Bytes +== %d byte %d of %d servers, %d players -== %d di %d servers, %d giocatori +== %d di %d server, %d giocatori %d%% loaded == %d%% caricato %ds left -== %ds rimanenti +== %ds sec. %i minute left -== %i minuto residuo +== %i minuto rimanente %i minutes left -== %i minuti residui +== %i minuti rimanenti %i second left -== %i secondo residuo +== %i secondo rimanente %i seconds left -== %i secondi residui +== %i secondi rimanenti %s wins! -== %s vittorie! +== %s ha vinto! -Page %d- == -Pagina %d- @@ -38,7 +38,7 @@ Add == Aggiungi Add Friend -== Aggiungi Amico +== Aggiungi amico Address == Indirizzo @@ -50,25 +50,25 @@ Alpha == Alpha Always show name plates -== Mostra sempre nomi +== 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 uscire? +== 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 degli amici? +== 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. -== E' la prima volta che avvii il gioco, inserisci il tuo nickname. E' bene che tu controlli le opzioni prima di giocare per avere una migliore esperienza di gioco. +== 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 automaticamente la schermata 'game over' +== Cattura schermata alla fine di ogni partita Blue team == Squadra blu @@ -80,10 +80,10 @@ Body == Corpo Call vote -== Chiama voto +== Vota Change settings -== Cambia configurazione +== Cambia opzioni Chat == Chat @@ -113,7 +113,7 @@ Console == Console Controls -== Controlli +== Comandi Count players only == Conta solo giocatori @@ -125,7 +125,7 @@ Crc: == Crc: Created: -== Creato: +== Creata: Current == Attuale @@ -145,6 +145,9 @@ Delete demo Demo details == Dettagli demo +Demofile: %s +== Demo: %s + Demos == Demo @@ -155,13 +158,13 @@ Disconnected == Disconnesso Display Modes -== Modalità Display +== Risoluzioni schermo Downloading map -== Scaricamento mappa +== Scaricamento mappa in corso Draw! -== Patta! +== Pareggio! Dynamic Camera == Camera dinamica @@ -203,58 +206,58 @@ Force vote == Forza voto Free-View -== Camera libera +== Visione libera Friends == Amici Fullscreen -== A tutto schermo +== Schermo intero Game == Partita Game info -== Informazioni partita +== Info partita Game over -== Fine partita +== Partita finita Game type -== Modalità di gioco +== Tipo Game types: -== Modalità di gioco: +== Tipo di gioco: General == Generale Graphics -== Gráfica +== Aspetto Grenade -== Granata +== Lanciagranate Hammer == Martello Has people playing -== Contiene giocatori +== Con giocatori High Detail -== Alta Risoluzione +== Alta qualità Hook == Rampino Host address -== Indirizzo Host +== Indirizzo host Hue == Tinta Info -== Informazioni +== Info Internet == Internet @@ -263,19 +266,19 @@ Invalid Demo == Demo non valida Join blue -== Unisciti ai blu +== Vai ai blu Join game -== Unisciti alla partita +== Entra Join red -== Unisciti ai rossi +== Vai ai rossi Jump == Salta Kick player -== Espelli giocatore +== Caccia giocatore LAN == LAN @@ -284,7 +287,7 @@ Language == Lingua Length: -== Lunghezza: +== Durata: Lht. == Luminosità @@ -302,10 +305,10 @@ Map: == Mappa: Max Screenshots -== Screenshot Massimi +== Numero massimo di catture Max demos -== Demo massime +== Numero massimo di demo Maximum ping: == Ping massimo: @@ -314,13 +317,13 @@ Miscellaneous == Altro Mouse sens. -== Sensività del mouse +== Sensibilità Move left == Sinistra Move player to spectators -== Muovi giocatore tra gli spettatori +== Fai osservare il giocatore Move right == Destra @@ -329,13 +332,13 @@ Movement == Movimento Mute when not active -== Silenzioso se inattivo +== Silenzioso quanto inattivo Name == Nome Name plates size -== Lunghezza nomi +== Dimensione nomi Netversion: == Versione net @@ -344,10 +347,10 @@ New name: == Nuovo nome: News -== Notizie +== Novità Next weapon -== Prossima arma +== Arma seguente Nickname == Nickname @@ -362,7 +365,7 @@ No servers found == Nessun server trovato No servers match your filter criteria -== Nessun server rispecchia i tuoi criteri +== Nessun server corrisponde ai tuoi criteri di ricerca Ok == Ok @@ -386,11 +389,17 @@ Pistol == Pistola Play -== Gioca +== Riproduci + +Play background music +== Riproduci musica di sottofondo Player == Giocatore +Player country: +== Filtra per paese: + Player options == Opzioni giocatore @@ -398,22 +407,22 @@ Players == Giocatori Please balance teams! -== Bilancia le squadre! +== Equilibra le squadre! Prev. weapon == Arma precedente Quality Textures -== Qualità Textures +== Texture di qualità Quick search: == Ricerca rapida: Quit -== Esci +== Chiudi Quit anyway? -== Vuoi uscire comunque? +== Vuoi chiudere comunque? REC %3d:%02d == REC %3d:%02d @@ -428,7 +437,7 @@ Red team == Squadra rossa Red team wins! -== La squadra rossa vince! +== La squadra rossa ha vinto! Refresh == Aggiorna @@ -440,13 +449,13 @@ Remote console == Console remota Remove -== Rimuovi +== Elimina Remove friend -== Rimuovi amico +== Elimina amico Rename -== Renomina +== Rinomina Rename demo == Rinomina demo @@ -455,34 +464,34 @@ Reset filter == Azzera filtri Reset to defaults -== Ripristina impostazioni iniziali +== Reimposta Rifle -== Mitra +== Laser Round -== Round +== Turno Sample rate -== Frequenza di campionamento +== Frequenza Sat. -== Sat. +== Saturazione Score -== Punteggi +== Punti Score board -== Tabella dei Punteggi +== Punteggio Score limit -== Punteggio Max. +== Limite punti Scoreboard -== Tabella dei Punteggi +== Punteggi Screenshot -== Schermata +== Cattura schermata Server address: == Indirizzo server: @@ -491,7 +500,7 @@ Server details == Dettagli server Server filter -== Filtri server +== Filtro server Server info == Info server @@ -500,7 +509,7 @@ Server not full == Server non pieno Settings -== Configurazioni +== Opzioni Shotgun == Fucile @@ -508,8 +517,8 @@ Shotgun Show chat == Mostra chat -Show friends -== Mostra amici +Show friends only +== Mostra solo amici Show ingame HUD == Mostra HUD in gioco @@ -521,43 +530,52 @@ Show only supported == Mostra solo supportati Size: -== Dimensioni: +== Dimensione: Skins -== Skins +== Skin Sound == Suono Sound error -== Suono errore +== Errore suono Sound volume == Volume suono Spectate -== Spettatore +== Osserva + +Spectate next +== Osserva succ. + +Spectate previous +== Osserva prec. Spectator mode -== Modalità spettatore +== Menu osservazione Spectators == Spettatori Standard gametype -== Tipo di gioco standard +== Tipo di gioco normale Standard map -== Mappa standard +== Mappa normale Stop record -== Ferma registrazione +== Ferma reg. + +Strict gametype filter +== Tipo di gioco preciso Sudden Death -== Morte istantanea +== Tempo supplementare Switch weapon on pickup -== Cambia arma automaticamente +== Cambia arma ad ogni raccolta Team == Squadra @@ -566,37 +584,37 @@ Team chat == Chat di squadra Teeworlds %s is out! Download it at www.teeworlds.com! -== Teeworlds %s e' stato rilasciato! Scaricalo da www.teeworlds.com! +== È uscito Teeworld %s! Scaricalo su www.teeworlds.com! Texture Compression -== Compressione textures +== 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 è attivo con una configurazione non standard in una modalità di gioco pura. +== 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, sicuramente vorrai salvarla prima di uscire. +== C'è una mappa non salvata nell'editor, probabilmente vuoi salvarla prima di uscire. Time limit -== Limite tempo. +== Limite di tempo Time limit: %d min -== Limite tempo: %d min +== Limite di tempo: %d min Try again -== Riprova +== Ritenta Type -== Tipologia +== Tipo Type: -== Tipologia: +== Tipo: UI Color -== Colore UI +== Color interfaccia Unable to delete the demo == Impossibile eliminare la demo @@ -608,10 +626,10 @@ Use sounds == Attiva suoni Use team colors for name plates -== Utilizza i colori del team per i nomi +== Usa i colori della squadra nei nomi V-Sync -== V-Sync +== Sincronizzazione verticale Version == Versione @@ -620,7 +638,7 @@ Version: == Versione: Vote command: -== Comando voto: +== Comando di voto: Vote description: == Descrizione voto: @@ -629,10 +647,10 @@ Vote no == Vota no Vote yes -== Vota si +== Vota sì Voting -== Votazione in corso +== Votazione Warmup == Riscaldamento @@ -641,36 +659,24 @@ Weapon == Arma Welcome to Teeworlds -== Benvenuto su Teeworlds! +== Benvenuto su Teeworlds Yes -== Si +== Sì You must restart the game for all settings to take effect. -== E' necessario riavviare il gioco per impostare i cambiamenti. +== Devi riavviare il gioco per rendere effettive le modifiche. Your skin -== Tua skin +== La tua skin no limit == senza limiti ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### + +== ## translated strings ##### + diff --git a/data/languages/norwegian.txt b/data/languages/norwegian.txt index a17365d56..db362e93d 100644 --- a/data/languages/norwegian.txt +++ b/data/languages/norwegian.txt @@ -145,6 +145,9 @@ Delete demo Demo details == Demo detaljer +Demofile: %s +== Demo fil: %s + Demos == Demoer @@ -388,9 +391,15 @@ Pistol Play == Spill +Play background music +== Spill bakrunds musikk + Player == Spiller +Player country: +== spiller landet + Player options == Spillerinstillinger @@ -508,7 +517,7 @@ Shotgun Show chat == Vis samtale -Show friends +Show friends only == Vis venner Show ingame HUD @@ -538,6 +547,12 @@ Sound volume Spectate == Se på +Spectate next +== Se på neste + +Spectate previous +== Se på forige + Spectator mode == Tilhenger modus @@ -545,7 +560,7 @@ Spectators == Tilskuere: Standard gametype -== Standard spilltyper +== Standard spilletyper Standard map == Standard bane @@ -553,6 +568,9 @@ Standard map Stop record == Stopp inspilling +Strict gametype filter +== spilltype-filter + Sudden Death == Brå død @@ -657,20 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/polish.txt b/data/languages/polish.txt index c3223a9ce..e955e1e68 100644 --- a/data/languages/polish.txt +++ b/data/languages/polish.txt @@ -1,15 +1,45 @@ ##### translated strings ##### +%d Bytes +== %d Bajtów + %d of %d servers, %d players == %d z %d serwerów, %d graczy +%d%% loaded +== Załadowano %d%% + %ds left -== Jeszcze %d +== 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 @@ -20,19 +50,31 @@ Alpha == Alfa Always show name plates -== Zawsze pokazuj nicki +== 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 siebie przed dołączeniem do gry. +== 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 -== Drużyna niebieskich +== Niebiescy Blue team wins! -== Drużyna niebieskich wygrała! +== Niebiescy wygrali! Body == Ciało @@ -40,9 +82,18 @@ Body Call vote == Głosowanie +Change settings +== Zmień ustawienia + Chat == Chat +Clan +== Klan + +Client +== Klient + Close == Zamknij @@ -64,6 +115,18 @@ Console Controls == Sterowanie +Count players only +== Licz tylko graczy + +Country +== Kraj + +Crc: +== Crc: + +Created: +== Utworzono: + Current == Aktualnie @@ -71,11 +134,20 @@ Current version: %s == Aktualna wersja: %s Custom colors -== Własne kolory +== Dostosuj kolory Delete == Usuń +Delete demo +== Usuń demo + +Demo details +== Szczegóły dema + +Demofile: %s +== Plik dema: %s + Demos == Dema @@ -98,7 +170,7 @@ Dynamic Camera == Dynamiczna kamera Emoticon -== Emotikonka +== Emotikona Enter == Wejdź @@ -107,10 +179,10 @@ Error == Błąd Error loading demo -== Błąd przy ładowaniu demo +== Błąd przy ładowaniu dema FSAA samples -== próbki FSAA (anti-aliasing) +== Próbkowanie FSAA (Antyaliasing) Favorite == Ulubiony @@ -127,9 +199,18 @@ Filter Fire == Strzał +Folder +== Katalog + Force vote == Wymuś głosowanie +Free-View +== Wolna kamera + +Friends +== Znajomi + Fullscreen == Pełny ekran @@ -137,7 +218,7 @@ Game == Gra Game info -== Informacje o grze +== Info o grze Game over == Koniec gry @@ -164,7 +245,7 @@ Has people playing == Nie pokazuj pustych High Detail -== Wysoka jakość obrazu +== Wysoka jakość Hook == Hak @@ -181,26 +262,35 @@ Info Internet == Internet +Invalid Demo +== Nieprawidłowe demo + Join blue -== Dołącz do niebieskich +== Do niebieskich Join game == Dołącz Join red -== Dołącz do czerwonych +== Do czerwonych Jump == Skok +Kick player +== Wyrzuć gracza + LAN == LAN Language == Język +Length: +== Długość: + Lht. -== Jasn. +== Jasność Loading == Ładowanie @@ -211,6 +301,15 @@ MOTD Map == Mapa +Map: +== Mapa: + +Max Screenshots +== Maksymalnie screenshotów + +Max demos +== Maksymalnie dem + Maximum ping: == Maksymalny ping: @@ -221,22 +320,34 @@ Mouse sens. == Czułość myszy Move left -== Lewo +== W lewo + +Move player to spectators +== Przesuń gracza do obserwatorów Move right -== Prawo +== W prawo Movement == Ruch Mute when not active -== Wycisz kiedy gra nieaktywna +== Wycisz, kiedy gra nieaktywna Name -== Nick +== Nazwa + +Name plates size +== Wielkość wyświetlanych nicków + +Netversion: +== Wersja sieciowa: + +New name: +== Nowa nazwa: News -== News +== Wiadomości Next weapon == Następna broń @@ -257,11 +368,14 @@ No servers match your filter criteria == Nie znaleziono serwerów spełniających twoje kryteria Ok -== OK +== Ok Open == Otwórz +Parent Folder +== Nadrzędny katalog + Password == Hasło @@ -275,16 +389,25 @@ Pistol == Pistolet Play -== Start +== Graj + +Play background music +== Odtwarzaj muzykę w tle Player == Gracz +Player country: +== Narodowość: + +Player options +== Opcje gracza + Players == Gracze Please balance teams! -== Konieczne wyrównanie drużyn! +== Proszę wyrównać szanse drużyn! Prev. weapon == Poprzednia broń @@ -298,11 +421,23 @@ Quick search: Quit == Wyjście +Quit anyway? +== Wyjść mimo wszystko? + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Powód: + +Record demo +== Nagraj demo + Red team -== Drużyna czerwonych +== Czerwoni Red team wins! -== Drużyna czerwonych wygrała! +== Czerwoni wygrali! Refresh == Odśwież @@ -313,6 +448,18 @@ Refreshing master servers Remote console == Zdalna konsola +Remove +== Usuń + +Remove friend +== Usuń znajomego + +Rename +== Zmień nazwę + +Rename demo +== Zmień nazwę dema + Reset filter == Domyślne filtry @@ -326,10 +473,10 @@ Round == Runda Sample rate -== Próbkowanie +== Częstotliwość próbkowania Sat. -== Nasyc. +== Nasycenie Score == Wynik @@ -346,11 +493,17 @@ Scoreboard Screenshot == Screenshot +Server address: +== Adres serwera: + Server details == Szczegóły serwera +Server filter +== Filtr serwerów + Server info -== Server info +== Info serwera Server not full == Nie pokazuj pełnych @@ -364,24 +517,45 @@ 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 +Size: +== Rozmiar: + Skins == Motywy Sound == Dźwięk +Sound error +== Błąd dźwięku + Sound volume == Głośność Spectate == Obserwuj +Spectate next +== Obserwuj następnego + +Spectate previous +== Obserwuj poprzedniego + +Spectator mode +== Tryb obserwatora + Spectators == Obserwatorzy @@ -391,6 +565,12 @@ Standard gametype Standard map == Standardowa mapa +Stop record +== Zakończ REC + +Strict gametype filter +== Szczegółowy filtr typu gry + Sudden Death == Nagła śmierć @@ -404,35 +584,65 @@ Team chat == Chat drużynowy Teeworlds %s is out! Download it at www.teeworlds.com! -== Wydano Teeworlds %s! Ściągnij je z 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 nie korzysta ze standardowych ustawień. +== 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 +Type: +== Typ: + UI Color == Kolor menu +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 -== V-Sync +== Synchronizacja pionowa (V-Sync) Version == Wersja +Version: +== Wersja: + +Vote command: +== Polecenie głosowania: + +Vote description: +== Opis głosowania: + Vote no == Nie @@ -455,222 +665,18 @@ Yes == Tak You must restart the game for all settings to take effect. -== Gra musi zostać uruchomiona ponownie, żeby nowe ustawienia weszły w życie. +== Uruchom ponownie grę, aby użyć nowych ustawieńn Your skin == Twój wygląd -##### needs translation ##### - -%d Bytes -== - -%d%% loaded -== - -%i minute left -== - -%i minutes left -== - -%i second left -== - -%i seconds left -== - -%s wins! -== - --Page %d- -== - -Add -== - -Add Friend -== - -Are you sure that you want to delete the demo? -== - -Are you sure that you want to remove the player from your friends list? -== - -Automatically record demos -== - -Automatically take game over screenshot -== - -Change settings -== - -Clan -== - -Client -== - -Count players only -== - -Country -== - -Crc: -== - -Created: -== - -Delete demo -== - -Demo details -== - -Demofile: %s -== - -Folder -== - -Free-View -== - -Friends -== - -Invalid Demo -== - -Kick player -== - -Length: -== - -Map: -== - -Max Screenshots -== - -Max demos -== - -Move player to spectators -== - -Name plates size -== - -Netversion: -== - -New name: -== - -Parent Folder -== - -Play background music -== - -Player options -== - -Quit anyway? -== - -REC %3d:%02d -== - -Reason: -== - -Record demo -== - -Remove -== - -Remove friend -== - -Rename -== - -Rename demo -== - -Server address: -== - -Server filter -== - -Show friends -== - -Show ingame HUD -== - -Size: -== - -Sound error -== - -Spectate next -== - -Spectate previous -== - -Spectator mode -== - -Stop record -== - -Strict gametype filter -== - -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. -== - -Time limit: %d min -== - -Type: -== - -Unable to delete the demo -== - -Unable to rename the demo -== - -Use team colors for name plates -== - -Version: -== - -Vote command: -== - -Vote description: -== - no limit -== +== bez limitu + +##### needs translation ##### ##### old translations ##### +utracić swojej pracy! +== + diff --git a/data/languages/portuguese.txt b/data/languages/portuguese.txt index 144862475..e3c7c6cbf 100644 --- a/data/languages/portuguese.txt +++ b/data/languages/portuguese.txt @@ -1,4 +1,4 @@ - + ##### translated strings ##### %d Bytes @@ -8,7 +8,7 @@ == %d de %d servidores, %d jogadores %d%% loaded -== %d%% carregado +== %d%% A Carregar %ds left == faltam %ds @@ -26,7 +26,7 @@ == faltam %i segundos %s wins! -== %s vence! +== %s ganhou! -Page %d- == -Página %d- @@ -50,31 +50,31 @@ Alpha == Alpha Always show name plates -== Sempre mostrar apelidos +== Mostrar sempre os nicks Are you sure that you want to delete the demo? -== Tem certeza que deseja deletar o demo? +== Tens a certeza que queres Eliminar a demo? Are you sure that you want to quit? -== Você tem certeza que deseja sair? +== Queres mesmo sair? Are you sure that you want to remove the player from your friends list? -== Tem certeza que deseja remover o jogador da sua lista de amigos? +== Queres mesmo apaga-lo 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. -== 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. +== Olá! Pelos vistos é a primeira vez que inicias o jogo, por isso escolhe um Nick Name para ti! Automatically record demos -== Gravar demos automaticamente +== Gravar demos Automaticamente Automatically take game over screenshot -== Capturar tela final de jogo automaticamente +== Tirar screenshot's Automaticamente Blue team -== Time azul +== Equipa azul Blue team wins! -== Time azul vence! +== A Equipa azul ganhou! Body == Corpo @@ -104,19 +104,19 @@ Connect == Conectar Connecting to -== Conectando a +== A Conectar a Connection Problems... -== Problemas de Conexão... +== Problemas na conecção! Console == Console Controls -== Controles +== Controlos Count players only -== Contar apenas jogadores +== Só contar jogadores Country == País @@ -128,22 +128,22 @@ Created: == Criado: Current -== Atualmente +== Actualmente Current version: %s -== Versão atual : %s +== Versão actual : %s Custom colors == Cores personalizadas Delete -== Deletar +== Eliminar Delete demo -== Deletar demo +== Eliminar demo Demo details -== Detalhes do demo +== Detalhes da demo Demofile: %s == Demo: %s @@ -161,7 +161,7 @@ Display Modes == Modos de exibição Downloading map -== Baixando mapa +== A fazer download do mapa... Draw! == Empate! @@ -179,7 +179,7 @@ Error == Erro Error loading demo -== Erro ao carregar demo +== Erro a carregar a demo FSAA samples == Amostras FSAA @@ -197,7 +197,7 @@ Filter == Filtro Fire -== Atirar +== Disparar Folder == Pasta @@ -206,13 +206,13 @@ Force vote == Forçar Free-View -== Visão Livre +== Vista Livre Friends == Amigos Fullscreen -== Tela cheia +== Fullscreen (Ecrã inteiro) Game == Jogo @@ -242,7 +242,7 @@ Hammer == Martelo Has people playing -== Tem gente jogando +== Há gente a jogar High Detail == Mais detalhes (HD) @@ -251,7 +251,7 @@ Hook == Gancho Host address -== Endereço do host +== Direcção do Host Hue == Matiz @@ -263,37 +263,37 @@ Internet == Internet Invalid Demo -== Demo inválido +== Demo inválida Join blue -== Azul +== Juntar - Azuis Join game -== Entre no jogo +== Entrar no jogo Join red -== Vermelho +== J. Vermelhos Jump -== Pular +== Saltar Kick player -== Kickar jogador +== Expulsar jogador LAN == LAN Language -== Idioma +== Língua Length: -== Duração: +== Longitude: Lht. == Luz Loading -== Carregando +== Aguarda por favor MOTD == MOTD @@ -308,7 +308,7 @@ Max Screenshots == Máx. de Capturas de Tela Max demos -== Máx. de demos +== Demos maximas Maximum ping: == Ping máximo: @@ -317,13 +317,13 @@ Miscellaneous == Diversos Mouse sens. -== Sens. do mouse +== Sens. do rato Move left == Esquerda Move player to spectators -== Mover jogador para observadores +== Juntar jogador aos espectadores Move right == Direita @@ -332,13 +332,13 @@ Movement == Movimento Mute when not active -== Silenciar quando inativo +== Silencíar os jogadores inactivos Name == Nome Name plates size -== Tamanho dos apelidos +== Caracteres maximos nos nicks Netversion: == Netversion @@ -353,34 +353,34 @@ Next weapon == Próxima arma Nickname -== Apelido +== Nick No == Não No password -== Sem senha +== Sem password No servers found -== Nenhum servidor encontrado +== Nenhum servidor encontrado. No servers match your filter criteria -== Nenhum servidor corresponde aos critérios do filtro +== Não há servidores que correspondam às definições de procura. Ok -== Ok +== Aceitar Open == Abrir Parent Folder -== Diretório pai +== Pasta superior Password -== Senha +== Password Password incorrect -== Senha incorreta +== Password errada! Ping == Ping @@ -389,14 +389,17 @@ Pistol == Pistola Play -== Assistir +== Ver Play background music -== Tocar música de fundo +== Tocar a música de fundo Player == Jogador +Player country: +== País do jogador + Player options == Opções do jogador @@ -404,7 +407,7 @@ Players == Jogadores Please balance teams! -== Por favor, balanceie os times! +== Equilibrem as equipas! Prev. weapon == Arma anterior @@ -413,13 +416,13 @@ Quality Textures == Texturas de Qualidade Quick search: -== Pesquisa rápida: +== Busca rápida: Quit == Sair Quit anyway? -== Sair mesmo assim? +== Sair na mesma? REC %3d:%02d == REC %3d:%02d @@ -428,28 +431,28 @@ Reason: == Motivo: Record demo -== Gravar demo +== Gravar uma demo Red team -== Time vermelho +== Equipa vermelha Red team wins! -== Time vermelho vence! +== Ganhou a equipa vermelha! Refresh -== Atualizar +== Actualizar Refreshing master servers -== Atualizando servidores mestres +== A Actualizar servidores Remote console -== Console remoto +== Remote console Remove -== Deletar +== Eliminar Remove friend -== Deletar amigo +== Deixar de ser amigo Rename == Renomear @@ -458,19 +461,19 @@ Rename demo == Renomear demo Reset filter -== Resetar filtro +== Reiniciar Fitro Reset to defaults -== Resetar para padrão +== Por como defeito Rifle == Laser Round -== Rodada +== Ronda Sample rate -== Taxa de som +== Frequencia de rate Sat. == Sat. @@ -479,52 +482,52 @@ Score == Pontos Score board -== Placar +== Tabela de Pontos Score limit -== Placar máx. +== Pontuação Máx. Scoreboard -== Placar +== Pontuação Screenshot -== Captura de tela +== Screenshot Server address: -== End. do servidor: +== Direção do servidor: Server details -== Detalhes do server +== Detalhes do servidor Server filter -== Filtro de server +== Filtro de servidores Server info -== Info do server +== Info de servidor Server not full -== Servidor não cheio +== Não está cheio Settings -== Configurações +== Config. Shotgun == Espingarda Show chat -== Mostrar conversa +== Mostrar chat -Show friends +Show friends only == Mostrar amigos Show ingame HUD == Mostrar HUD do jogo Show name plates -== Mostrar apelidos +== Mostrar nick's Show only supported -== Mostrar apenas suportados +== Mostrar apenas suportado Size: == Tamanho: @@ -536,10 +539,10 @@ Sound == Som Sound error -== Erro de som +== Erro no som! Sound volume -== Volume do som +== Volume Spectate == Observar @@ -551,49 +554,49 @@ Spectate previous == Observar anterior Spectator mode -== Modo Observador +== Modo espectador Spectators -== Observadores +== Espectadores Standard gametype -== Tipo de jogo padrão +== Tipo de jogo normal Standard map -== Mapa padrão +== Mapa normal Stop record == Parar de gravar Strict gametype filter -== Tipo de jogo exato +== Tipo de jogo especifico Sudden Death -== Morte Súbita +== Morte súbita Switch weapon on pickup -== Trocar arma ao pegar +== Mudar de arma ao agarrar Team -== Time +== Equipa Team chat -== Conv. de equipe +== Chat de equipa Teeworlds %s is out! Download it at www.teeworlds.com! -== Teeworlds %s foi lançado! Baixe-o em www.teeworlds.com! +== Teeworlds %s já saiu! Download em www.teeworlds.com! Texture Compression == Compressão de Textura The audio device couldn't be initialised. -== O aparelho de áudio não pode ser inicializado. +== 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á rodando uma modificação não padrão em um tipo de jogo puro. +== O servidor está a usar um tipo de jogo não 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. +== O mapa que editas-te não foi gravado. Queres gravar antes de sair? Time limit == Tempo máx. @@ -602,7 +605,7 @@ Time limit: %d min == Limite de tempo: %d min Try again -== Tente de novo +== Tenta outra vez Type == Tipo @@ -614,16 +617,16 @@ UI Color == Cor do menu Unable to delete the demo -== Incapaz de deletar demo +== Não podes eliminar a demo Unable to rename the demo -== Incapaz de renomear demo +== Impossivel de renomear a demo Use sounds == Usar sons Use team colors for name plates -== Usar cores do time para apelidos +== Usar cores dos nicks com a cor da equipa V-Sync == V-Sync @@ -638,7 +641,7 @@ Vote command: == Comando: Vote description: -== Descrição da votação: +== Descrição de votação: Vote no == Votar não @@ -647,7 +650,7 @@ Vote yes == Votar sim Voting -== Votação +== A votar Warmup == Aquecimento @@ -662,19 +665,15 @@ 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. +== Para que as configurações sejam efectuadas deves Reiniciar o jogo. Your skin -== Sua skin +== A tua skin no limit == sem limite ##### needs translation ##### - ##### old translations ##### - -== ## translated strings ##### - diff --git a/data/languages/romanian.txt b/data/languages/romanian.txt index c7bbb6950..e38faadb4 100644 --- a/data/languages/romanian.txt +++ b/data/languages/romanian.txt @@ -1,3 +1,4 @@ + ##### translated strings ##### %d Bytes @@ -141,12 +142,12 @@ Delete Delete demo == Șterge demonstrația -Demofile: %s -== Fișier demo: %s - Demo details == Detalii demo +Demofile: %s +== Fișier demo: %s + Demos == Demo @@ -331,7 +332,7 @@ Movement == Mișcare Mute when not active -== Mută la inactivate +== Opreşte sunetul la inactivate Name == Nume @@ -396,6 +397,9 @@ Play background music Player == Jucător +Player country: +== Țara jucătorului: + Player options == Opțiuni jucător @@ -513,7 +517,7 @@ Shotgun Show chat == Afișare chat -Show friends +Show friends only == Arată prietenii Show ingame HUD diff --git a/data/languages/russian.txt b/data/languages/russian.txt index 87cf31688..93a329126 100644 --- a/data/languages/russian.txt +++ b/data/languages/russian.txt @@ -1,8 +1,7 @@ - ##### translated strings ##### %d Bytes -== %d Байтов +== %d байт %d of %d servers, %d players == %d из %d серверов, %d игроков @@ -14,19 +13,19 @@ == осталось %d сек. %i minute left -== %i минута осталась +== Осталась %i минута! %i minutes left -== %i минут осталось +== Осталось %i минут! %i second left -== %i секунда осталась +== Осталась %i секунда! %i seconds left -== %i секунд осталось +== Осталось %i секунд! %s wins! -== %s - победа! +== %s победил! -Page %d- == -Страница %d- @@ -47,34 +46,34 @@ 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 == Тело @@ -83,7 +82,7 @@ Call vote == Голосовать Change settings -== Настройки +== Изменить настройки Chat == Чат @@ -95,19 +94,19 @@ Client == Клиент Close -== Закрыть +== Выход Compatible version == Совместимая версия Connect -== Играть +== Подключиться Connecting to -== Соединяемся с +== Подключение к Connection Problems... -== Проблемы с соединением... +== Проблемы со связью... Console == Консоль @@ -119,10 +118,10 @@ Count players only == Считать только игроков Country -== Страна +== Флаг вашей страны Crc: -== Crc-сумма: +== Crc: Created: == Создан: @@ -134,7 +133,7 @@ Current version: %s == Текущая версия: %s Custom colors -== Произвольный цвет +== Свои цвета Delete == Удалить @@ -143,79 +142,82 @@ 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 сэмплы +== Сэмплов FSAA Favorite -== Избранное +== Избранный Favorites -== Избранное +== Избранные Feet -== Нога +== Ноги Filter == Фильтр Fire -== Стрельба +== Выстрел Folder == Папка Force vote -== Форсировать голосование +== Форсировать Free-View -== Свободный просмотр +== Свободный обзор Friends -== Приятели +== Друзья Fullscreen -== На весь экран +== Полноэкранный режим Game == Игра Game info -== Информация о игре +== Инфо об игре Game over == Игра окончена @@ -224,7 +226,7 @@ Game type == Тип игры Game types: -== Типы игры: +== Тип игры: General == Основные @@ -233,19 +235,19 @@ Graphics == Графика Grenade -== Граната +== Гранатомёт Hammer -== Молоток +== Молот Has people playing -== Есть игроки на сервере +== Не пустой сервер High Detail -== Высокая Детализация +== Высокая детализация Hook -== Цепь +== Крюк Host address == Адрес сервера @@ -260,22 +262,22 @@ Internet == Интернет Invalid Demo -== Невалидное демо +== Недопустимое демо Join blue -== К синим +== За синих Join game -== Войти в игру +== Играть Join red -== К красным +== За красных Jump == Прыжок Kick player -== Кикнуть игрока +== Забанить игрока LAN == LAN @@ -284,7 +286,7 @@ Language == Язык Length: -== Длина: +== Длина Lht. == Яркость @@ -302,10 +304,10 @@ Map: == Карта: Max Screenshots -== Максимум Скриншотов +== Максимальное количество снимков Max demos -== Максимум Демо +== Максимальное количество демо Maximum ping: == Макс. пинг: @@ -314,43 +316,43 @@ 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 == Нет @@ -362,22 +364,22 @@ No servers found == Сервера не найдены No servers match your filter criteria -== Нет серверов, подходящих под Ваш фильтр +== Нет серверов, подходящих под ваш фильтр Ok -== Ok +== ОК Open == Открыть Parent Folder -== Корневая папка +== Родительский каталог Password == Пароль Password incorrect -== Неверный пароль +== Пароль Ping == Пинг @@ -386,40 +388,43 @@ Pistol == Пистолет Play -== Воспроизвести +== Просмотр Play background music -== Воспроизвести фоновую музыку +== Играть фоновую музыку Player == Игрок +Player country: +== Страна: + Player options -== Настройки игрока +== Опции игрока Players == Игроки Please balance teams! -== Пожалуйста cбалансируйте команды! +== Сбалансируйте команды! Prev. weapon == Пред. оружие Quality Textures -== Качественные Текстуры +== Качественные текстуры Quick search: -== Быстрый поиск +== Быстрый поиск: Quit == Выход Quit anyway? -== Всё равно выйти? +== Выйти? REC %3d:%02d -== Записано %3d:%02d +== REC %3d:%02d Reason: == Причина: @@ -428,40 +433,40 @@ 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 == Раунд @@ -476,28 +481,28 @@ Score == Очки Score board -== Результат +== Табло Score limit -== Лимит на очки +== Лимит очков Scoreboard -== Результат +== Табло Screenshot -== Скриншот +== Снимок Server address: -== Адрес сервера: +== Адрес сервера Server details -== Информация о сервере +== Детали сервера Server filter -== Фильтр сервера +== Фильтр серверов Server info -== Инфо +== Информация Server not full == Сервер не заполнен @@ -511,23 +516,23 @@ Shotgun Show chat == Показать чат -Show friends -== Показывать друзей +Show friends only +== Только с друзьями Show ingame HUD -== Показывать HUD +== Показывать внутриигровой HUD Show name plates -== Показывать имена над игроками +== Показывать имена игроков Show only supported -== Показывать только поддерживаемые +== Показывать только поддерживаемые разрешения экрана Size: -== Размер +== Размер: Skins -== Модели +== Скины Sound == Звук @@ -536,16 +541,16 @@ Sound error == Звуковая ошибка Sound volume -== Громкость +== Громкость звука Spectate == Наблюдать Spectate next -== Наблюдать следующего +== Наблюдать след. Spectate previous -== Наблюдать предыдущего +== Наблюдать пред. Spectator mode == Наблюдатель @@ -560,13 +565,16 @@ Standard map == Стандартная карта Stop record -== Остановить запись +== Стоп + +Strict gametype filter +== Строгий фильтр р-мов Sudden Death -== Внезапная смерть +== Быстрая смерть Switch weapon on pickup -== Сменить оружие на подобранное +== Переключать оружие при подборе Team == Команда @@ -575,28 +583,28 @@ Team chat == Командный чат Teeworlds %s is out! Download it at www.teeworlds.com! -== Teeworlds %s в сети! Скачайте его на 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 мин +== Лимит времени: %d Try again -== Попробовать ещё раз +== ОК Type == Тип @@ -614,10 +622,10 @@ Unable to rename the demo == Невозможно переименовать демо Use sounds -== Звук +== Использовать звуки Use team colors for name plates -== Использовать цвет команды для имён игроков +== Командные цвета для имен игроков V-Sync == Вертикальная синхронизация @@ -629,10 +637,10 @@ Version: == Версия: Vote command: -== Голосование: +== Комманда голосования: Vote description: -== Причина голосования: +== Описание голосования: Vote no == Против @@ -644,7 +652,7 @@ Voting == Голосование Warmup -== Разогрев +== Разминка Weapon == Оружие @@ -656,7 +664,7 @@ Yes == Да You must restart the game for all settings to take effect. -== Вы должны перезапустить игру, чтобы настройки применились. +== Перезапустите игру для применения изменений. Your skin == Ваш скин @@ -666,11 +674,4 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Strict gametype filter -== - ##### old translations ##### - diff --git a/data/languages/serbian.txt b/data/languages/serbian.txt index aaabeabdd..e4ec95c2a 100644 --- a/data/languages/serbian.txt +++ b/data/languages/serbian.txt @@ -1,15 +1,45 @@ ##### translated strings ##### +%d Bytes +== %d Bytes + %d of %d servers, %d players == %d od %d server(a), %d igrač(a) +%d%% loaded +== Učitano %d%% + %ds left == Još %ds +%i minute left +== Još %ds + +%i minutes left +== Preostalo: %i min. + +%i second left +== Preostalo: %i min. + +%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 @@ -22,12 +52,24 @@ Alpha 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 napravi screenshot + Blue team == Plavi tim @@ -40,9 +82,18 @@ Body Call vote == Glasanje +Change settings +== Izmeni podešavanja + Chat == Chat +Clan +== Klan + +Client +== Klijent + Close == Zatvori @@ -64,6 +115,18 @@ Console Controls == Kontrole +Count players only +== Izbroj samo igrače + +Country +== Država + +Crc: +== Crc: + +Created: +== Kreirano: + Current == Trenutno @@ -76,6 +139,15 @@ Custom colors Delete == Izbriši +Delete demo +== Obriši demo-snimak + +Demo details +== Detalji demo-a + +Demofile: %s +== Demo-snimak: %s + Demos == Demoi @@ -127,9 +199,18 @@ Filter Fire == Pucanje +Folder +== Direktorijum + Force vote == Obavezno glasanje +Free-View +== Slobodan pregled + +Friends +== Prijatelji + Fullscreen == Čitav ekran @@ -181,6 +262,9 @@ Info Internet == Internet +Invalid Demo +== Neispravan demo-snimak + Join blue == U plavi tim @@ -193,12 +277,18 @@ Join red Jump == Skok +Kick player +== Izbaci igrača iz igre + LAN == LAN Language == Jezik +Length: +== Dužina: + Lht. == Svetl. @@ -211,6 +301,15 @@ MOTD Map == Mapa +Map: +== Mapa: + +Max Screenshots +== Maksimalan broj screenshot-ova + +Max demos +== Maksimalan broj demo-a + Maximum ping: == Maksimalan ping: @@ -223,6 +322,9 @@ Mouse sens. Move left == Nalevo +Move player to spectators +== Prebaci igrača u posmatrače + Move right == Nadesno @@ -235,6 +337,15 @@ Mute when not active Name == Ime +Name plates size +== Veličina pozadine za ime + +Netversion: +== Net-verzija: + +New name: +== Novo ime: + News == Novosti @@ -259,6 +370,12 @@ No servers match your filter criteria Ok == OK +Open +== Otvori + +Parent Folder +== Prethodni direktorijum + Password == Lozinka @@ -274,9 +391,18 @@ Pistol Play == Pokreni +Play background music +== Pozadinska muzika + Player == Igrač +Player country: +== Država + +Player options +== Podešavanja igrača + Players == Igrači @@ -295,6 +421,18 @@ Quick search: Quit == Izlaz +Quit anyway? +== Izlaz? + +REC %3d:%02d +== REC %3d:%02d + +Reason: +== Razlog: + +Record demo +== Snimi demo + Red team == Crveni tim @@ -310,6 +448,18 @@ Refreshing master servers Remote console == Udaljena konzola +Remove +== Ukloni + +Remove friend +== Ukloni prijatelja + +Rename +== Preimenuj + +Rename demo +== Preimenuj demo-snimak + Reset filter == Poništi filter @@ -343,9 +493,15 @@ Scoreboard Screenshot == Screenshot +Server address: +== Adresa servera: + Server details == Podaci o serveru +Server filter +== Filter servera + Server info == O Serveru @@ -361,24 +517,45 @@ Shotgun Show chat == Prikaži chat +Show friends only +== Prikaži isključivo prijatelje + +Show ingame HUD +== Prikaži HUD + Show name plates == Prikaži imena igrača Show only supported == Prikaži samo podržane +Size: +== Veličina: + Skins == Izgled Sound == Zvuk +Sound error +== Problem sa zvukom + Sound volume == Jačina zvuka Spectate == Posmatraj +Spectate next +== Posmatraj narednog + +Spectate previous +== Posmatraj prethodnog + +Spectator mode +== Posmatrački mod + Spectators == Posmatrači @@ -388,6 +565,12 @@ Standard gametype Standard map == Standardna mapa +Stop record +== Prekini snimanje + +Strict gametype filter +== Striktan filter tipa igre + Sudden Death == Iznenadna smrt @@ -406,30 +589,60 @@ Teeworlds %s is out! Download it at 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 +Type: +== Tip: + UI Color == Boja menija +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 +Version: +== Verzija: + +Vote command: +== Komanda za glasanje: + +Vote description: +== Opis glasanja: + Vote no == Ne @@ -457,220 +670,10 @@ You must restart the game for all settings to take effect. Your skin == Vaš izgled -##### needs translation ##### - -%d Bytes -== - -%d%% loaded -== - -%i minute left -== - -%i minutes left -== - -%i second left -== - -%i seconds left -== - -%s wins! -== - --Page %d- -== - -Add -== - -Add Friend -== - -Are you sure that you want to delete the demo? -== - -Are you sure that you want to remove the player from your friends list? -== - -Automatically record demos -== - -Automatically take game over screenshot -== - -Change settings -== - -Clan -== - -Client -== - -Count players only -== - -Country -== - -Crc: -== - -Created: -== - -Delete demo -== - -Demo details -== - -Demofile: %s -== - -Folder -== - -Free-View -== - -Friends -== - -Invalid Demo -== - -Kick player -== - -Length: -== - -Map: -== - -Max Screenshots -== - -Max demos -== - -Move player to spectators -== - -Name plates size -== - -Netversion: -== - -New name: -== - -Open -== - -Parent Folder -== - -Play background music -== - -Player options -== - -Quit anyway? -== - -REC %3d:%02d -== - -Reason: -== - -Record demo -== - -Remove -== - -Remove friend -== - -Rename -== - -Rename demo -== - -Server address: -== - -Server filter -== - -Show friends -== - -Show ingame HUD -== - -Size: -== - -Sound error -== - -Spectate next -== - -Spectate previous -== - -Spectator mode -== - -Stop record -== - -Strict gametype filter -== - -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. -== - -Time limit: %d min -== - -Type: -== - -Unable to delete the demo -== - -Unable to rename the demo -== - -Use team colors for name plates -== - -Version: -== - -Vote command: -== - -Vote description: -== - no limit -== +== bez ograničenja + +##### needs translation ##### ##### old translations ##### diff --git a/data/languages/slovak.txt b/data/languages/slovak.txt index 203940967..48816234e 100644 --- a/data/languages/slovak.txt +++ b/data/languages/slovak.txt @@ -14,16 +14,16 @@ == Zostáva %ds %i minute left -== Zostáva %i minúta +== Zostávajúce minúty: %i %i minutes left -== Zostáva %i minút +== Zostávajúce minúty: %i %i second left -== Zostáva %i sekunda +== Zostávajúce sekundy: %i %i seconds left -== Zostáva %i sekúnd +== Zostávajúce sekundy: %i %s wins! == %s vyhráva! @@ -62,7 +62,7 @@ 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. Pred tý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. +== 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 @@ -116,7 +116,7 @@ Controls == Ovládanie Count players only -== Rátať len hráčov +== Nepočítať divákov Country == Krajina @@ -145,6 +145,9 @@ Delete demo Demo details == Detaily nahrávky +Demofile: %s +== Nahrávka: %s + Demos == Záznamy @@ -254,7 +257,7 @@ Hue == Hue Info -== Informácie +== Info Internet == Internet @@ -320,7 +323,7 @@ Move left == Pohyb vľavo Move player to spectators -== Presunúť hráča do skupiny divákov +== Poslať hráča pozorovať Move right == Pohyb vpravo @@ -365,7 +368,7 @@ No servers match your filter criteria == Žiadny server nezodpovedá zadaným kritériám Ok -== Ok +== OK Open == Otvoriť @@ -388,9 +391,15 @@ Pistol Play == Prehrať +Play background music +== Prehrať hudbu na pozadí + Player == Hráč +Player country: +== Filter krajín: + Player options == Nastavenia hráča @@ -407,7 +416,7 @@ Quality Textures == Kvalitné textúry Quick search: -== Rýchle hľadanie: +== Hľadanie: Quit == Ukončiť @@ -473,13 +482,13 @@ Score == Skóre Score board -== Prehľad skóre +== Tabuľka výsledkov Score limit == Limit skóre Scoreboard -== Prehľad skóre +== Tabuľka výsledkov Screenshot == Screenshot @@ -508,7 +517,7 @@ Shotgun Show chat == Ukázať chat -Show friends +Show friends only == Ukázať priateľov Show ingame HUD @@ -527,7 +536,7 @@ Skins == Skiny Sound -== Zvuky +== Zvuk Sound error == Zvuková chyba @@ -538,6 +547,12 @@ Sound volume Spectate == Pozorovať +Spectate next +== Pozorovať ďalšieho + +Spectate previous +== Pozorovať predch. + Spectator mode == Mód diváka @@ -545,14 +560,17 @@ Spectators == Diváci Standard gametype -== Štandartný herný typ +== Štandardný herný typ Standard map -== Štandartná mapa +== Štandardná mapa Stop record == Nenahrávať +Strict gametype filter +== Striktný filter módov + Sudden Death == Rýchla Smrť @@ -560,7 +578,7 @@ Switch weapon on pickup == Nastavovať zdvíhanú zbraň ako aktuálnu Team -== Tým +== Týmu Team chat == Týmový chat @@ -575,10 +593,10 @@ 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. -== Na serveri je nastavený neštandartný tuning. +== 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, mali by ste si ju uložiť predtým než skončíte hru. +== V editore máte neuloženú mapu, možno si ju chcete pred skončením hry uložiť. Time limit == Časový limit @@ -602,7 +620,7 @@ Unable to delete the demo == Nemôžem vymazať záznam Unable to rename the demo -== Nedá sa premenovať nahrávka +== Nahrávka sa nedá premenovať Use sounds == Povoliť zvuky @@ -626,10 +644,10 @@ Vote description: == Popis hlasu: Vote no -== Hlasovať proti +== Nie Vote yes -== Hlasovať pre +== Áno Voting == Hlasovanie @@ -638,7 +656,7 @@ Warmup == Rozohrávka Weapon -== Zbraň +== Zbrane Welcome to Teeworlds == Vitajte v hre Teeworlds! @@ -657,20 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/spanish.txt b/data/languages/spanish.txt index d587cf026..5348d16ab 100644 --- a/data/languages/spanish.txt +++ b/data/languages/spanish.txt @@ -2,7 +2,7 @@ ##### translated strings ##### %d Bytes -== %d Bytes +== %d bytes %d of %d servers, %d players == %d de %d servidores, %d jugadores @@ -11,22 +11,22 @@ == %d%% cargado %ds left -== faltan %ds +== %ds restantes %i minute left -== falta %i minuto +== %i minuto restante %i minutes left -== faltan %i minutos +== %i minutos restantes %i second left -== falta %i segundo +== %i segundo restante %i seconds left -== faltan %i segundos +== %i segundos restantes %s wins! -== %s gana! +== ¡%s gana! -Page %d- == -Página %d- @@ -38,7 +38,7 @@ Add == Añadir Add Friend -== Añadir Amigo +== Añadir amigo Address == Dirección @@ -53,40 +53,40 @@ Always show name plates == Mostrar siempre los apodos Are you sure that you want to delete the demo? -== ¿Seguro que quiere eliminar la demo? +== ¿Estás seguro de que quieres eliminar la demo? Are you sure that you want to quit? -== ¿Seguro que quiere salir? +== ¿Estás seguro de que quieres salir? Are you sure that you want to remove the player from your friends list? -== ¿Estas seguro de que quiere eliminar este jugador de su lista de amigos? +== ¿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 abre el juego, por favor, introduzca su apodo. Es recomendable que verifique su configuración y ajuste las preferencias antes de entrar en un servidor. +== 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 -== Pantallazo 'game over' automáticamente +== Captura de pantalla al final de la partida Blue team == Equipo azul Blue team wins! -== Equipo azul gana! +== ¡El equipo azul gana! Body == Cuerpo Call vote -== Votación +== Votar Change settings == Cambiar configuración Chat -== Charla +== Conversación Clan == Clan @@ -107,7 +107,7 @@ Connecting to == Conectando con Connection Problems... -== Problemas de Conexión... +== Problemas de conexión... Console == Consola @@ -119,16 +119,16 @@ Count players only == Solo contar jugadores Country -== Pais +== País Crc: -== Crc: +== CRC: Created: == Creado: Current -== Actualmente +== Actual Current version: %s == Versión actual: %s @@ -143,7 +143,10 @@ Delete demo == Borrar demo Demo details -== Detellades demo +== Detalles de la demo + +Demofile: %s +== Archivo: %s Demos == Demos @@ -155,13 +158,13 @@ Disconnected == Desconectado Display Modes -== Modos de exibición +== Modos de video Downloading map -== Bajando mapa +== Descargando mapa Draw! -== Empate! +== ¡Empate! Dynamic Camera == Cámara dinámica @@ -176,7 +179,7 @@ Error == Error Error loading demo -== Error al cargar demo +== Error al cargar la demo FSAA samples == Muestras FSAA @@ -203,25 +206,25 @@ Force vote == Forzar Free-View -== Vista Libre +== Vista libre Friends == Amigos Fullscreen -== Pantalla Completa +== Pantalla completa Game == Juego Game info -== Info. sobre el juego +== Información del juego Game over -== Fin del Juego +== Fin de la partida Game type -== Tipo de juego +== Modo Game types: == Tipos de juego: @@ -248,13 +251,13 @@ Hook == Gancho Host address -== Dirección de host +== Dirección del host Hue == Matiz Info -== Info. +== Información Internet == Internet @@ -263,13 +266,13 @@ Invalid Demo == Demo inválida Join blue -== Azul +== Unirse a azul Join game -== Entrar al juego +== Unirse Join red -== Rojo +== Unirse a rojo Jump == Saltar @@ -287,7 +290,7 @@ Length: == Longitud: Lht. -== Luz +== Luminosidad Loading == Cargando @@ -302,10 +305,10 @@ Map: == Mapa: Max Screenshots -== Max Pantallazos +== Número máximo de capturas Max demos -== Max demos +== Número máximo de demos Maximum ping: == Ping máximo: @@ -314,40 +317,40 @@ Miscellaneous == Miscelánea Mouse sens. -== Sens. del ratón +== Sensibilidad ratón Move left -== Izquierda +== Mover a la izquierda Move player to spectators == Mover jugador a espectadores Move right -== Derecha +== Mover a la derecha Movement == Movimiento Mute when not active -== Silenciar en inactivo +== Silenciar si no está activo Name == Nombre Name plates size -== Máx. Caracteres para los Apodos +== Tamaño de la fuente de los apodos Netversion: == Versión Net New name: -== Nuevo Nombre: +== Nuevo nombre: News -== Notícia +== Noticias Next weapon -== Próxima arma +== Arma siguiente Nickname == Apodo @@ -362,7 +365,7 @@ No servers found == Ningún servidor encontrado No servers match your filter criteria -== Ningún servidor corresponde a los critérios de filtrado +== Ningún servidor corresponde a los criterios de filtrado Ok == Aceptar @@ -371,7 +374,7 @@ Open == Abrir Parent Folder -== Carpeta superior +== Directorio padre Password == Contraseña @@ -386,19 +389,25 @@ Pistol == Pistola Play -== Asistir +== Reproducir + +Play background music +== Reproducir música de fondo Player == Jugador +Player country: +== País del jugador + Player options -== Opciones de Jugador +== Opciones de jugador Players == Jugadores Please balance teams! -== Por favor, equilibre los equipos! +== Por favor, ¡equilibrad los equipos! Prev. weapon == Arma anterior @@ -407,7 +416,7 @@ Quality Textures == Texturas de calidad Quick search: -== Busqueda rápida: +== Búsqueda rápida: Quit == Salir @@ -428,7 +437,7 @@ Red team == Equipo rojo Red team wins! -== Ganó el Equipo rojo! +== ¡El equipo rojo gana! Refresh == Actualizar @@ -458,7 +467,7 @@ Reset to defaults == Resetar por defecto Rifle -== Laser +== Láser Round == Ronda @@ -467,40 +476,40 @@ Sample rate == Frecuencia de muestreo Sat. -== Sat. +== Saturación Score == Puntos Score board -== Tabla de puntos +== Puntuación Score limit -== Puntuación Máx. +== Límite puntos Scoreboard == Puntuación Screenshot -== Pantallazo +== Captura de pantalla Server address: -== Dirección d. servidor: +== IP del servidor: Server details -== Detalles del server +== Detalles del servidor Server filter == Filtro del servidor Server info -== Info del server +== Servidor Server not full -== Servidor incompleto +== Servidor sin llenar Settings -== Config. +== Configuración Shotgun == Escopeta @@ -508,17 +517,17 @@ Shotgun Show chat == Mostrar chat -Show friends -== Mostrar amigos +Show friends only +== Solo mostrar amigos Show ingame HUD -== Mostar HUD en juego +== Mostar HUD durante el juego Show name plates == Mostrar apodos Show only supported -== Mostrar solo soportados +== Mostrar únicamente modos soportados Size: == Tamaño: @@ -536,58 +545,67 @@ Sound volume == Volumen de sonido Spectate -== Observar +== Asistir + +Spectate next +== Observar siguiente + +Spectate previous +== Observar anterior Spectator mode == Modo espectador Spectators -== Observadores +== Espectadores Standard gametype -== Tipo de juego normal +== Tipo de juego estándar Standard map -== Mapa normal +== Mapa estándar Stop record -== Parar grabación +== Detener grabación + +Strict gametype filter +== Tipo de juego del filtro Sudden Death -== Muerte Súbita +== Muerte súbita Switch weapon on pickup -== Cambiar de arma +== Cambiar al arma recogida Team == Equipo Team chat -== Conv. de equipo +== En equipo Teeworlds %s is out! Download it at www.teeworlds.com! -== Teeworlds %s ya salió! Descárgalo desde www.teeworlds.com! +== ¡Teeworlds %s ha salido! ¡Descárgalo desde www.teeworlds.com! Texture Compression -== Compresión de Textura +== 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 afinación no estándar en un tipo de juego puro. +== 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, ¿quiere guardarlo antes de salir? +== Tienes un mapa sin guardar en el editor, quizá quieras guardarlo antes de salir. Time limit -== Tiempo máx. +== Tiempo límite Time limit: %d min -== Tiempo limite: %d min +== Tiempo límite: %d minutos Try again -== Probar de nuevo +== Intentar de nuevo Type == Tipo @@ -596,19 +614,19 @@ Type: == Tipo: UI Color -== Color de menu +== Color de menú Unable to delete the demo -== No se puede eliminar la demo +== No se pudo eliminar la demo Unable to rename the demo -== Imposible renombrar la demo +== No se pudo renombrar la demo Use sounds == Usar sonidos Use team colors for name plates -== Usar el color del equipo en los apodos +== Usar el color de equipo en los apodos V-Sync == V-Sync @@ -620,7 +638,7 @@ Version: == Versión: Vote command: -== Votar comando: +== Comando de votación: Vote description: == Descripción de la votación: @@ -629,10 +647,10 @@ Vote no == Votar no Vote yes -== Votar si +== Votar sí Voting -== Votando +== Votación Warmup == Calentamiento @@ -641,10 +659,10 @@ Weapon == Arma Welcome to Teeworlds -== Bienvenido a Teeworlds! +== ¡Bienvenido/a a Teeworlds! Yes -== Si +== Sí You must restart the game for all settings to take effect. == Debes reiniciar el juego para que los cambios tengan efecto. @@ -657,29 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### -Page %d of %d -== Página %d de %d - -Next -== Siguiente - -Prev -== Anterior - diff --git a/data/languages/swedish.txt b/data/languages/swedish.txt index 6dea5ed4b..7f48d95d3 100644 --- a/data/languages/swedish.txt +++ b/data/languages/swedish.txt @@ -145,6 +145,9 @@ Delete demo Demo details == Demoinformation +Demofile: %s +== Demofil: %s + Demos == Demon @@ -179,7 +182,7 @@ Error loading demo == Kunde inte ladda demot FSAA samples -== FSAA samplingar +== FSAA-samplingar Favorite == Favorit @@ -239,7 +242,7 @@ Hammer == Hammare Has people playing -== Har folk som spelar +== Inte tom High Detail == Extra detaljer @@ -296,7 +299,7 @@ MOTD == Meddelande Map -== Karta +== Bana Map: == Bana @@ -388,9 +391,15 @@ Pistol Play == Spela +Play background music +== Aktivera bakgrundsmusik + Player == Spelare +Player country: +== Land + Player options == Spelaralternativ @@ -497,7 +506,7 @@ Server info == Serverinfo Server not full -== Inte full server +== Lediga platser Settings == Inställningar @@ -506,9 +515,9 @@ Shotgun == Hagelgevär Show chat -== Visa chatt +== Visa chatten -Show friends +Show friends only == Visa kompisar Show ingame HUD @@ -533,11 +542,17 @@ Sound error == Ljudfel Sound volume -== Ljud volym +== Volym Spectate == Åskåda +Spectate next +== Se på nästa + +Spectate previous +== Se på föregående + Spectator mode == Åskådarläge @@ -553,6 +568,9 @@ Standard map Stop record == Sluta spela in +Strict gametype filter +== Strikt speltypsfilter + Sudden Death == Plötslig död @@ -566,7 +584,7 @@ Team chat == Lagchatt Teeworlds %s is out! Download it at www.teeworlds.com! -== Teeworld %s är släppt! Ladda ner det på www.teeworlds.com! +== Teeworlds %s är ute nu! Ladda ner det från www.teeworlds.com. Texture Compression == Texturkompression @@ -575,7 +593,7 @@ 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å standard speltyp. +== 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. @@ -605,13 +623,13 @@ Unable to rename the demo == Kunde inte byta namn på demot Use sounds -== Använd ljudeffekter +== Aktivera ljudeffekter Use team colors for name plates == Använd lagfärger i namnskyltar V-Sync -== V-Sync +== V-Synk Version == Version @@ -647,7 +665,7 @@ Yes == Ja You must restart the game for all settings to take effect. -== Du måste starta om spelet för att ändringarna skall gälla. +== Du måste starta om spelet för att ändringarna skall verkställas. Your skin == Ditt skin @@ -657,20 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/turkish.txt b/data/languages/turkish.txt index a62e6073f..d91c72bf7 100644 --- a/data/languages/turkish.txt +++ b/data/languages/turkish.txt @@ -508,8 +508,8 @@ Shotgun Show chat == Sohbeti göster -Show friends -== Arkadaşları göster +Show friends only +== Sadece arkadaşları göster Show ingame HUD == Oyun içi HUD ı göster @@ -663,6 +663,9 @@ Demofile: %s Play background music == +Player country: +== + Spectate next == diff --git a/data/languages/ukrainian.txt b/data/languages/ukrainian.txt index 5019714bf..935668298 100644 --- a/data/languages/ukrainian.txt +++ b/data/languages/ukrainian.txt @@ -50,7 +50,7 @@ Chat == Чат Close -== Закрити +== Зачинити Compatible version == Сумісна версія @@ -86,10 +86,10 @@ Demos == Демо Disconnect -== # Відключитись +== Від'єднатись Disconnected -== Відключено +== Від'єднанно Display Modes == Режими дисплея @@ -155,7 +155,7 @@ Game type == Тип гри Game types: -== Тип гри: +== Типи гри: General == Основні @@ -164,7 +164,7 @@ Graphics == Графіка Grenade -== Граната +== Ракетниця Hammer == Молоток @@ -215,7 +215,7 @@ Lht. == Яскравість Loading -== Завантиження +== Завантаження MOTD == MOTD @@ -245,7 +245,7 @@ Mute when not active == Глушити звуки, коли гра неактивна Name -== Імя +== Ім'я News == Новини @@ -332,7 +332,7 @@ Remote console == Консоль сервера Reset filter -== Сикнути фільтри +== Скинути фільтри Reset to defaults == Скинути налаштування @@ -591,6 +591,9 @@ Parent Folder Play background music == +Player country: +== + Player options == @@ -618,7 +621,7 @@ Server address: Server filter == -Show friends +Show friends only == Show ingame HUD diff --git a/docs/tool/NaturalDocs b/docs/tool/NaturalDocs old mode 100755 new mode 100644 diff --git a/other/sdl/vc2005libs/SDL.dll b/other/sdl/vc2005libs/SDL.dll index 628cdfcf0..429ae5458 100644 Binary files a/other/sdl/vc2005libs/SDL.dll and b/other/sdl/vc2005libs/SDL.dll differ diff --git a/other/sdl/vc2005libs/SDL.lib b/other/sdl/vc2005libs/SDL.lib index 5b3f17c58..f4e860f8c 100644 Binary files a/other/sdl/vc2005libs/SDL.lib and b/other/sdl/vc2005libs/SDL.lib differ diff --git a/other/sdl/vc2005libs/SDLmain.lib b/other/sdl/vc2005libs/SDLmain.lib index 945b9ad8e..825c03b91 100644 Binary files a/other/sdl/vc2005libs/SDLmain.lib and b/other/sdl/vc2005libs/SDLmain.lib differ diff --git a/scripts/cmd5.py b/scripts/cmd5.py index 18f33d6b7..f21f7bb83 100644 --- a/scripts/cmd5.py +++ b/scripts/cmd5.py @@ -30,7 +30,7 @@ for filename in sys.argv[1:]: hash = hashlib.md5(f).hexdigest().lower()[16:] #TODO 0.7: improve nethash creation -if hash == "6e28a475de43adfd": +if hash == "3dc531e4296de555": hash = "626fce9a778df4d4" print('#define GAME_NETVERSION_HASH "%s"' % hash) print('#define GIT_SHORTREV_HASH "%s"' % os.popen('git rev-parse HEAD').readline(8)) diff --git a/scripts/compiler.py b/scripts/compiler.py old mode 100755 new mode 100644 diff --git a/scripts/copyright.py b/scripts/copyright.py old mode 100755 new mode 100644 diff --git a/scripts/font_installer.sh b/scripts/font_installer.sh old mode 100755 new mode 100644 diff --git a/scripts/linecount.sh b/scripts/linecount.sh old mode 100755 new mode 100644 diff --git a/scripts/make_docs.sh b/scripts/make_docs.sh old mode 100755 new mode 100644 diff --git a/scripts/tw_api.py b/scripts/tw_api.py index 054a4fa20..36cc5f5e4 100644 --- a/scripts/tw_api.py +++ b/scripts/tw_api.py @@ -2,18 +2,57 @@ from socket import * import struct import sys +import threading +import time -def get_server_info(address, port): + + +NUM_MASTERSERVERS = 4 +MASTERSERVER_PORT = 8300 + +TIMEOUT = 2 + +SERVERTYPE_NORMAL = 0 +SERVERTYPE_LEGACY = 1 + +PACKET_GETLIST = "\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreqt" +PACKET_GETLIST2 = "\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreq2" +PACKET_GETINFO = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief" +PACKET_GETINFO2 = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgie2" + "\x00" +PACKET_GETINFO3 = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgie3" + "\x00" + + + +class Server_Info(threading.Thread): + + def __init__(self, address, type): + self.address = address + self.type = type + self.finished = False + threading.Thread.__init__(self, target = self.run) + + def run(self): + self.info = None + if self.type == SERVERTYPE_NORMAL: + self.info = get_server_info3(self.address) + elif self.type == SERVERTYPE_LEGACY: + self.info = get_server_info(self.address) + if self.info: + self.info = get_server_info2(self.address) + self.finished = True + + +def get_server_info(address): try: - sock = socket(AF_INET, SOCK_DGRAM) - sock.settimeout(1.5); - sock.sendto("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief", (address, port)) - data, addr = sock.recvfrom(1024) - sock.close() - + sock = socket(AF_INET, SOCK_DGRAM) + sock.settimeout(TIMEOUT); + sock.sendto(PACKET_GETINFO, address) + data, addr = sock.recvfrom(1024) + sock.close() + data = data[14:] # skip header - slots = data.split("\x00") + server_info = {} server_info["version"] = slots[0] server_info["name"] = slots[1] @@ -24,108 +63,208 @@ def get_server_info(address, port): server_info["num_players"] = int(slots[6]) server_info["max_players"] = int(slots[7]) server_info["players"] = [] - + for i in xrange(0, server_info["num_players"]): player = {} - player["name"] = slots[8+i*2+1] - player["score"] = slots[8+i*2] - server_info["players"] += [player] - - gametypes = ["dm", "tdm", "ctf"] - try: server_info["gametype_name"] = gametypes[server_info["gametype_id"]] - except: server_info["gametype_name"] = "unknown" - - return server_info - except: - return None - -def get_server_count(address, port): - try: - sock = socket(AF_INET, SOCK_DGRAM) - sock.settimeout(1.5); - sock.sendto("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffcoun", (address, port)) - data, addr = sock.recvfrom(1024) - sock.close() - - data = data[14:] # skip header - return struct.unpack(">H", data)[0] - except: - return -1 + player["name"] = slots[8+i*2] + player["score"] = int(slots[8+i*2+1]) + server_info["players"].append(player) -def get_servers(address): - counter = 0 - master_port = 8300 - servers = [] - + return server_info + + except: + sock.close() + return None + + +def get_server_info2(address): try: - sock = socket(AF_INET, SOCK_DGRAM) - sock.settimeout(1.5) - sock.sendto("\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreqt", (address, master_port)) - + sock = socket(AF_INET, SOCK_DGRAM) + sock.settimeout(TIMEOUT); + sock.sendto(PACKET_GETINFO2, address) + data, addr = sock.recvfrom(1024) + sock.close() + + data = data[14:] # skip header + slots = data.split("\x00") + + server_info = {} + server_info["token"] = slots[0] + server_info["version"] = slots[1] + server_info["name"] = slots[2] + server_info["map"] = slots[3] + server_info["gametype"] = slots[4] + server_info["flags"] = int(slots[5]) + server_info["progression"] = int(slots[6]) + server_info["num_players"] = int(slots[7]) + server_info["max_players"] = int(slots[8]) + server_info["players"] = [] + + for i in xrange(0, server_info["num_players"]): + player = {} + player["name"] = slots[9+i*2] + player["score"] = int(slots[9+i*2+1]) + server_info["players"].append(player) + + return server_info + + except: + sock.close() + return None + + +def get_server_info3(address): + try: + sock = socket(AF_INET, SOCK_DGRAM) + sock.settimeout(TIMEOUT); + sock.sendto(PACKET_GETINFO3, address) + data, addr = sock.recvfrom(1400) + sock.close() + + data = data[14:] # skip header + slots = data.split("\x00") + + server_info = {} + server_info["token"] = slots[0] + server_info["version"] = slots[1] + server_info["name"] = slots[2] + server_info["map"] = slots[3] + server_info["gametype"] = slots[4] + server_info["flags"] = int(slots[5]) + server_info["num_players"] = int(slots[6]) + server_info["max_players"] = int(slots[7]) + server_info["num_clients"] = int(slots[8]) + server_info["max_clients"] = int(slots[9]) + server_info["players"] = [] + + for i in xrange(0, server_info["num_clients"]): + player = {} + player["name"] = slots[10+i*5] + player["clan"] = slots[10+i*5+1] + player["country"] = int(slots[10+i*5+2]) + player["score"] = int(slots[10+i*5+3]) + if int(slots[10+i*5+4]): + player["player"] = True + else: + player["player"] = False + server_info["players"].append(player) + + return server_info + + except: + sock.close() + return None + + + +class Master_Server_Info(threading.Thread): + + def __init__(self, address): + self.address = address + self.finished = False + threading.Thread.__init__(self, target = self.run) + + def run(self): + self.servers = get_list(self.address) + get_list2(self.address) + self.finished = True + + +def get_list(address): + servers = [] + + try: + sock = socket(AF_INET, SOCK_DGRAM) + sock.settimeout(TIMEOUT) + sock.sendto(PACKET_GETLIST, address) + while 1: data, addr = sock.recvfrom(1024) - - data = data[14:] - num_servers = len(data) / 6 - for n in range(0, num_servers): - ip = ".".join(map(str, map(ord, data[n*6:n*6+4]))) - port = ord(data[n*6+5]) * 256 + ord(data[n*6+4]) - servers += [[ip, port]] + data = data[14:] + num_servers = len(data) / 6 - except: # timeout + for n in range(0, num_servers): + ip = ".".join(map(str, map(ord, data[n*6:n*6+4]))) + port = ord(data[n*6+5]) * 256 + ord(data[n*6+4]) + servers += [[(ip, port), SERVERTYPE_LEGACY]] + + except: sock.close() - return servers -def get_all_servers(): - servers = [] - for i in range(1, 16): - addr = "master%d.teeworlds.com"%i - list = get_servers(addr) - if list: - #print addr, "had", len(list), "servers" - servers += list return servers -servers = get_all_servers() -total_players = 0 -players_per_versions = {} -versions = {} -gametypes = {} -if 1: - for server in servers: - #print "checking server", server[0], server[1] - info = get_server_info(server[0], server[1]) - if info: - total_players += len(info["players"]) - if info["version"] in versions: - versions[info["version"]] += 1 + +def get_list2(address): + servers = [] + + try: + sock = socket(AF_INET, SOCK_DGRAM) + sock.settimeout(TIMEOUT) + sock.sendto(PACKET_GETLIST2, address) + + while 1: + data, addr = sock.recvfrom(1400) + + data = data[14:] + num_servers = len(data) / 18 + + for n in range(0, num_servers): + if data[n*18:n*18+12] == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff": + ip = ".".join(map(str, map(ord, data[n*18+12:n*18+16]))) + else: + ip = ":".join(map(str, map(ord, data[n*18:n*18+16]))) + port = (ord(data[n*18+16])<<8) + ord(data[n*18+17]) + servers += [[(ip, port), SERVERTYPE_NORMAL]] + + except: + sock.close() + + return servers + + + +master_servers = [] + +for i in range(1, NUM_MASTERSERVERS+1): + m = Master_Server_Info(("master%d.teeworlds.com"%i, MASTERSERVER_PORT)) + master_servers.append(m) + m.start() + time.sleep(0.001) # avoid issues + +servers = [] + +while len(master_servers) != 0: + if master_servers[0].finished == True: + if master_servers[0].servers: + servers += master_servers[0].servers + del master_servers[0] + time.sleep(0.001) # be nice + +servers_info = [] + +print str(len(servers)) + " servers" + +for server in servers: + s = Server_Info(server[0], server[1]) + servers_info.append(s) + s.start() + time.sleep(0.001) # avoid issues + +num_players = 0 +num_clients = 0 + +while len(servers_info) != 0: + if servers_info[0].finished == True: + + if servers_info[0].info: + num_players += servers_info[0].info["num_players"] + if servers_info[0].type == SERVERTYPE_NORMAL: + num_clients += servers_info[0].info["num_clients"] else: - versions[info["version"]] = 1 + num_clients += servers_info[0].info["num_players"] - if info["version"] in players_per_versions: - players_per_versions[info["version"]] += len(info["players"]) - else: - players_per_versions[info["version"]] = len(info["players"]) + del servers_info[0] - if info["gametype"] in gametypes: - gametypes[info["gametype"]] += 1 - else: - gametypes[info["gametype"]] = 1 - -print total_players - -if 0: - print total_players, "on", len(servers), 'servers' - print "versions:" - for v in versions: - print "\t",v, versions[v] - - print "players per version:" - for v in players_per_versions: - print "\t",v, players_per_versions[v] - - print "gametypes:" - for v in gametypes: - print "\t",v, gametypes[v] + time.sleep(0.001) # be nice +print str(num_players) + " players and " + str(num_clients-num_players) + " spectators" diff --git a/src/base/detect.h b/src/base/detect.h index 0b66acefc..f9ca57791 100644 --- a/src/base/detect.h +++ b/src/base/detect.h @@ -25,7 +25,7 @@ #endif /* unix family */ -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #define CONF_FAMILY_UNIX 1 #define CONF_FAMILY_STRING "unix" #define CONF_PLATFORM_FREEBSD 1 @@ -46,6 +46,13 @@ #define CONF_PLATFORM_STRING "linux" #endif +#if defined(__GNU__) || defined(__gnu__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_HURD 1 + #define CONF_PLATFORM_STRING "gnu" +#endif + #if defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__) #define CONF_FAMILY_UNIX 1 #define CONF_FAMILY_STRING "unix" @@ -69,35 +76,61 @@ #endif +/* use gcc endianness definitions when available */ +#if defined(__GNUC__) && !defined(__APPLE__) + #if defined(__FreeBSD__) || defined(__OpenBSD__) + #include + #else + #include + #endif + + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define CONF_ARCH_ENDIAN_LITTLE 1 + #elif __BYTE_ORDER == __BIG_ENDIAN + #define CONF_ARCH_ENDIAN_BIG 1 + #endif +#endif + + /* architectures */ #if defined(i386) || defined(__i386__) || defined(__x86__) || defined(CONF_PLATFORM_WIN32) #define CONF_ARCH_IA32 1 #define CONF_ARCH_STRING "ia32" - #define CONF_ARCH_ENDIAN_LITTLE 1 + #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) + #define CONF_ARCH_ENDIAN_LITTLE 1 + #endif #endif #if defined(__ia64__) #define CONF_ARCH_IA64 1 #define CONF_ARCH_STRING "ia64" - #define CONF_ARCH_ENDIAN_LITTLE 1 + #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) + #define CONF_ARCH_ENDIAN_LITTLE 1 + #endif #endif #if defined(__amd64__) || defined(__x86_64__) #define CONF_ARCH_AMD64 1 #define CONF_ARCH_STRING "amd64" - #define CONF_ARCH_ENDIAN_LITTLE 1 + #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) + #define CONF_ARCH_ENDIAN_LITTLE 1 + #endif #endif #if defined(__powerpc__) || defined(__ppc__) #define CONF_ARCH_PPC 1 #define CONF_ARCH_STRING "ppc" - #define CONF_ARCH_ENDIAN_BIG 1 + #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) + #define CONF_ARCH_ENDIAN_BIG 1 + #endif #endif #if defined(__sparc__) #define CONF_ARCH_SPARC 1 #define CONF_ARCH_STRING "sparc" - #define CONF_ARCH_ENDIAN_BIG 1 + #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) + #define CONF_ARCH_ENDIAN_BIG 1 + #endif #endif diff --git a/src/base/system.c b/src/base/system.c index 26503d67b..f93e7c02e 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -42,10 +42,6 @@ #include #include #include - - #ifndef EWOULDBLOCK - #define EWOULDBLOCK WSAEWOULDBLOCK - #endif #else #error NOT IMPLEMENTED #endif @@ -833,8 +829,6 @@ static int priv_net_close_all_sockets(NETSOCKET sock) static int priv_net_create_socket(int domain, int type, struct sockaddr *addr, int sockaddrlen) { int sock, e; - unsigned long mode = 1; - int broadcast = 1; /* create socket */ sock = socket(domain, type, 0); @@ -845,11 +839,13 @@ static int priv_net_create_socket(int domain, int type, struct sockaddr *addr, i } /* set to IPv6 only if thats what we are creating */ +#if defined(IPV6_V6ONLY) /* windows sdk 6.1 and higher */ if(domain == AF_INET6) { int ipv6only = 1; setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&ipv6only, sizeof(ipv6only)); } +#endif /* bind the socket */ e = bind(sock, addr, sockaddrlen); @@ -860,16 +856,6 @@ static int priv_net_create_socket(int domain, int type, struct sockaddr *addr, i return -1; } - /* set non-blocking */ -#if defined(CONF_FAMILY_WINDOWS) - ioctlsocket(sock, FIONBIO, &mode); -#else - ioctl(sock, FIONBIO, &mode); -#endif - - /* set boardcast */ - setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); - /* return the newly created socket */ return sock; } @@ -878,6 +864,7 @@ NETSOCKET net_udp_create(NETADDR bindaddr) { NETSOCKET sock = invalid_socket; NETADDR tmpbindaddr = bindaddr; + int broadcast = 1; if(bindaddr.type&NETTYPE_IPV4) { @@ -893,6 +880,12 @@ 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)); } if(bindaddr.type&NETTYPE_IPV6) @@ -909,6 +902,12 @@ 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)); } /* return */ @@ -1018,32 +1017,48 @@ int net_udp_close(NETSOCKET sock) return priv_net_close_all_sockets(sock); } -// TODO: make TCP stuff work again -NETSOCKET net_tcp_create(const NETADDR *a) +NETSOCKET net_tcp_create(NETADDR bindaddr) { - /* TODO: IPv6 support */ NETSOCKET sock = invalid_socket; + NETADDR tmpbindaddr = bindaddr; - if(a->type&NETTYPE_IPV4) + if(bindaddr.type&NETTYPE_IPV4) { struct sockaddr_in addr; - - /* create socket */ - sock.type |= NETTYPE_IPV4; - sock.ipv4sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock.ipv4sock < 0) - return invalid_socket; + int socket = -1; /* bind, we should check for error */ - netaddr_to_sockaddr_in(a, &addr); - bind(sock.ipv4sock, (struct sockaddr *)&addr, sizeof(addr)); + tmpbindaddr.type = NETTYPE_IPV4; + netaddr_to_sockaddr_in(&tmpbindaddr, &addr); + socket = priv_net_create_socket(AF_INET, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr)); + if(socket >= 0) + { + sock.type |= NETTYPE_IPV4; + sock.ipv4sock = socket; + } + } + + if(bindaddr.type&NETTYPE_IPV6) + { + struct sockaddr_in6 addr; + int socket = -1; + + /* bind, we should check for error */ + tmpbindaddr.type = NETTYPE_IPV6; + netaddr_to_sockaddr_in6(&tmpbindaddr, &addr); + socket = priv_net_create_socket(AF_INET6, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr)); + if(socket >= 0) + { + sock.type |= NETTYPE_IPV6; + sock.ipv6sock = socket; + } } /* return */ return sock; } -int net_tcp_set_non_blocking(NETSOCKET sock) +int net_set_non_blocking(NETSOCKET sock) { unsigned long mode = 1; if(sock.ipv4sock >= 0) @@ -1067,7 +1082,7 @@ int net_tcp_set_non_blocking(NETSOCKET sock) return 0; } -int net_tcp_set_blocking(NETSOCKET sock) +int net_set_blocking(NETSOCKET sock) { unsigned long mode = 0; if(sock.ipv4sock >= 0) @@ -1093,30 +1108,31 @@ int net_tcp_set_blocking(NETSOCKET sock) int net_tcp_listen(NETSOCKET sock, int backlog) { + int err = -1; if(sock.ipv4sock >= 0) - listen(sock.ipv4sock, backlog); + err = listen(sock.ipv4sock, backlog); if(sock.ipv6sock >= 0) - listen(sock.ipv6sock, backlog); - return 0; + err = listen(sock.ipv6sock, backlog); + return err; } int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *a) { int s; socklen_t sockaddr_len; - struct sockaddr addr; *new_sock = invalid_socket; - sockaddr_len = sizeof(addr); - if(sock.ipv4sock >= 0) { - s = accept(sock.ipv4sock, &addr, &sockaddr_len); + struct sockaddr_in addr; + sockaddr_len = sizeof(addr); + s = accept(sock.ipv4sock, (struct sockaddr *)&addr, &sockaddr_len); + if (s != -1) { - sockaddr_to_netaddr(&addr, a); + sockaddr_to_netaddr((const struct sockaddr *)&addr, a); new_sock->type = NETTYPE_IPV4; new_sock->ipv4sock = s; return s; @@ -1125,55 +1141,74 @@ int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *a) if(sock.ipv6sock >= 0) { - s = accept(sock.ipv6sock, &addr, &sockaddr_len); + struct sockaddr_in6 addr; + sockaddr_len = sizeof(addr); + s = accept(sock.ipv6sock, (struct sockaddr *)&addr, &sockaddr_len); + if (s != -1) { - sockaddr_to_netaddr(&addr, a); + sockaddr_to_netaddr((const struct sockaddr *)&addr, a); new_sock->type = NETTYPE_IPV6; new_sock->ipv6sock = s; return s; } } - return 0; + return -1; } int net_tcp_connect(NETSOCKET sock, const NETADDR *a) { - /*struct sockaddr addr; - netaddr_to_sockaddr(a, &addr); - return connect(sock, &addr, sizeof(addr)); - */ - return 0; + if(a->type&NETTYPE_IPV4) + { + struct sockaddr_in addr; + netaddr_to_sockaddr_in(a, &addr); + return connect(sock.ipv4sock, (struct sockaddr *)&addr, sizeof(addr)); + } + + if(a->type&NETTYPE_IPV6) + { + struct sockaddr_in6 addr; + netaddr_to_sockaddr_in6(a, &addr); + return connect(sock.ipv6sock, (struct sockaddr *)&addr, sizeof(addr)); + } + + return -1; } -int net_tcp_connect_non_blocking(NETSOCKET sock, const NETADDR *a) +int net_tcp_connect_non_blocking(NETSOCKET sock, NETADDR bindaddr) { - /* struct sockaddr addr; */ int res = 0; - /* - netaddr_to_sockaddr(a, &addr); - net_tcp_set_non_blocking(sock); - res = connect(sock, &addr, sizeof(addr)); - net_tcp_set_blocking(sock); - */ + net_set_non_blocking(sock); + res = net_tcp_connect(sock, &bindaddr); + net_set_blocking(sock); return res; } int net_tcp_send(NETSOCKET sock, const void *data, int size) { - int bytes = 0; - /* bytes = send((int)sock, (const char*)data, size, 0); */ + int bytes = -1; + + if(sock.ipv4sock >= 0) + bytes = send((int)sock.ipv4sock, (const char*)data, size, 0); + if(sock.ipv6sock >= 0) + bytes = send((int)sock.ipv6sock, (const char*)data, size, 0); + return bytes; } int net_tcp_recv(NETSOCKET sock, void *data, int maxsize) { - int bytes = 0; - /* bytes = recv((int)sock, (char*)data, maxsize, 0); */ + int bytes = -1; + + if(sock.ipv4sock >= 0) + bytes = recv((int)sock.ipv4sock, (char*)data, maxsize, 0); + if(sock.ipv6sock >= 0) + bytes = recv((int)sock.ipv6sock, (char*)data, maxsize, 0); + return bytes; } @@ -1184,12 +1219,20 @@ int net_tcp_close(NETSOCKET sock) int net_errno() { +#if defined(CONF_FAMILY_WINDOWS) + return WSAGetLastError(); +#else return errno; +#endif } int net_would_block() { +#if defined(CONF_FAMILY_WINDOWS) + return net_errno() == WSAEWOULDBLOCK; +#else return net_errno() == EWOULDBLOCK; +#endif } int net_init() @@ -1556,6 +1599,15 @@ int str_comp_nocase(const char *a, const char *b) #endif } +int str_comp_nocase_num(const char *a, const char *b, const int num) +{ +#if defined(CONF_FAMILY_WINDOWS) + return _strnicmp(a, b, num); +#else + return strncasecmp(a, b, num); +#endif +} + int str_comp(const char *a, const char *b) { return strcmp(a, b); diff --git a/src/base/system.h b/src/base/system.h index aab71bfff..aaa5b43f2 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -591,7 +591,7 @@ int net_udp_close(NETSOCKET sock); Returns: On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID. */ -NETSOCKET net_tcp_create(const NETADDR *a); +NETSOCKET net_tcp_create(NETADDR bindaddr); /* Function: net_tcp_listen @@ -825,6 +825,25 @@ char *str_skip_whitespaces(char *str); */ int str_comp_nocase(const char *a, const char *b); +/* + Function: str_comp_nocase_num + Compares up to num characters of two strings case insensitive. + + Parameters: + a - String to compare. + b - String to compare. + num - Maximum characters to compare + + Returns: + <0 - String a is lesser than string b + 0 - String a is equal to string b + >0 - String a is greater than string b + + Remarks: + - Only garanted to work with a-z/A-Z. + - The strings are treated as zero-termineted strings. +*/ +int str_comp_nocase_num(const char *a, const char *b, const int num); /* Function: str_comp @@ -1075,21 +1094,21 @@ int fs_rename(const char *oldname, const char *newname); DOCTODO: serp */ -int net_tcp_connect_non_blocking(NETSOCKET sock, const NETADDR *a); +int net_tcp_connect_non_blocking(NETSOCKET sock, NETADDR bindaddr); /* - Function: net_tcp_set_non_blocking + Function: net_set_non_blocking DOCTODO: serp */ -int net_tcp_set_non_blocking(NETSOCKET sock); +int net_set_non_blocking(NETSOCKET sock); /* - Function: net_tcp_set_non_blocking + Function: net_set_non_blocking DOCTODO: serp */ -int net_tcp_set_blocking(NETSOCKET sock); +int net_set_blocking(NETSOCKET sock); /* Function: net_errno diff --git a/src/engine/client.h b/src/engine/client.h index 222fa7a9c..bf1474ef9 100644 --- a/src/engine/client.h +++ b/src/engine/client.h @@ -95,6 +95,7 @@ public: // remote console virtual void RconAuth(const char *pUsername, const char *pPassword) = 0; virtual bool RconAuthed() = 0; + virtual bool UseTempRconCommands() = 0; virtual void Rcon(const char *pLine) = 0; // server info @@ -167,7 +168,6 @@ public: virtual int OnSnapInput(int *pData) = 0; virtual const char *GetItemName(int Type) = 0; - virtual int GetCountryIndex(int Code) = 0; virtual const char *Version() = 0; virtual const char *NetVersion() = 0; diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 4e6527249..50c6cf193 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -1,5 +1,6 @@ /* (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 // qsort #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -231,185 +233,6 @@ void CSmoothTime::Update(CGraph *pGraph, int64 Target, int TimeLeft, int AdjustD } -bool CFileCollection::IsFilenameValid(const char *pFilename) -{ - if(str_length(pFilename) != m_FileDescLength+TIMESTAMP_LENGTH+m_FileExtLength || - str_comp_num(pFilename, m_aFileDesc, m_FileDescLength) || - str_comp(pFilename+m_FileDescLength+TIMESTAMP_LENGTH, m_aFileExt)) - return false; - - pFilename += m_FileDescLength; - if(pFilename[0] == '_' && - pFilename[1] >= '0' && pFilename[1] <= '9' && - pFilename[2] >= '0' && pFilename[2] <= '9' && - pFilename[3] >= '0' && pFilename[3] <= '9' && - pFilename[4] >= '0' && pFilename[4] <= '9' && - pFilename[5] == '-' && - pFilename[6] >= '0' && pFilename[6] <= '9' && - pFilename[7] >= '0' && pFilename[7] <= '9' && - pFilename[8] == '-' && - pFilename[9] >= '0' && pFilename[9] <= '9' && - pFilename[10] >= '0' && pFilename[10] <= '9' && - pFilename[11] == '_' && - pFilename[12] >= '0' && pFilename[12] <= '9' && - pFilename[13] >= '0' && pFilename[13] <= '9' && - pFilename[14] == '-' && - pFilename[15] >= '0' && pFilename[15] <= '9' && - pFilename[16] >= '0' && pFilename[16] <= '9' && - pFilename[17] == '-' && - pFilename[18] >= '0' && pFilename[18] <= '9' && - pFilename[19] >= '0' && pFilename[19] <= '9') - return true; - - return false; -} - -int64 CFileCollection::ExtractTimestamp(const char *pTimestring) -{ - int64 Timestamp = pTimestring[0]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[1]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[2]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[3]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[5]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[6]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[8]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[9]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[11]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[12]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[14]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[15]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[17]-'0'; Timestamp <<= 4; - Timestamp += pTimestring[18]-'0'; - - return Timestamp; -} - -void CFileCollection::BuildTimestring(int64 Timestamp, char *pTimestring) -{ - pTimestring[19] = 0; - pTimestring[18] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[17] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[16] = '-'; - pTimestring[15] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[14] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[13] = '-'; - pTimestring[12] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[11] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[10] = '_'; - pTimestring[9] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[8] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[7] = '-'; - pTimestring[6] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[5] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[4] = '-'; - pTimestring[3] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[2] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[1] = (Timestamp&0xF)+'0'; Timestamp >>= 4; - pTimestring[0] = (Timestamp&0xF)+'0'; -} - -void CFileCollection::Init(IStorage *pStorage, const char *pPath, const char *pFileDesc, const char *pFileExt, int MaxEntries) -{ - mem_zero(m_aTimestamps, sizeof(m_aTimestamps)); - m_NumTimestamps = 0; - m_MaxEntries = clamp(MaxEntries, 1, static_cast(MAX_ENTRIES)); - str_copy(m_aFileDesc, pFileDesc, sizeof(m_aFileDesc)); - m_FileDescLength = str_length(m_aFileDesc); - str_copy(m_aFileExt, pFileExt, sizeof(m_aFileExt)); - m_FileExtLength = str_length(m_aFileExt); - str_copy(m_aPath, pPath, sizeof(m_aPath)); - m_pStorage = pStorage; - - m_pStorage->ListDirectory(IStorage::TYPE_SAVE, m_aPath, FilelistCallback, this); -} - -void CFileCollection::AddEntry(int64 Timestamp) -{ - if(m_NumTimestamps == 0) - { - // empty list - m_aTimestamps[m_NumTimestamps++] = Timestamp; - } - else - { - // remove old file - if(m_NumTimestamps == m_MaxEntries) - { - char aBuf[512]; - char aTimestring[TIMESTAMP_LENGTH]; - BuildTimestring(m_aTimestamps[0], aTimestring); - str_format(aBuf, sizeof(aBuf), "%s/%s_%s%s", m_aPath, m_aFileDesc, aTimestring, m_aFileExt); - m_pStorage->RemoveFile(aBuf, IStorage::TYPE_SAVE); - } - - // add entry to the sorted list - if(m_aTimestamps[0] > Timestamp) - { - // first entry - if(m_NumTimestamps < m_MaxEntries) - { - mem_move(m_aTimestamps+1, m_aTimestamps, m_NumTimestamps*sizeof(int64)); - m_aTimestamps[0] = Timestamp; - ++m_NumTimestamps; - } - } - else if(m_aTimestamps[m_NumTimestamps-1] <= Timestamp) - { - // last entry - if(m_NumTimestamps == m_MaxEntries) - { - mem_move(m_aTimestamps, m_aTimestamps+1, (m_NumTimestamps-1)*sizeof(int64)); - m_aTimestamps[m_NumTimestamps-1] = Timestamp; - } - else - m_aTimestamps[m_NumTimestamps++] = Timestamp; - } - else - { - // middle entry - int Left = 0, Right = m_NumTimestamps-1; - while(Right-Left > 1) - { - int Mid = (Left+Right)/2; - if(m_aTimestamps[Mid] > Timestamp) - Right = Mid; - else - Left = Mid; - } - - if(m_NumTimestamps == m_MaxEntries) - { - mem_move(m_aTimestamps, m_aTimestamps+1, (Right-1)*sizeof(int64)); - m_aTimestamps[Right-1] = Timestamp; - } - else - { - mem_move(m_aTimestamps+Right+1, m_aTimestamps+Right, (m_NumTimestamps-Right)*sizeof(int64)); - m_aTimestamps[Right] = Timestamp; - ++m_NumTimestamps; - } - } - } -} - -int CFileCollection::FilelistCallback(const char *pFilename, int IsDir, int StorageType, void *pUser) -{ - CFileCollection *pThis = static_cast(pUser); - - // check for valid file name format - if(IsDir || !pThis->IsFilenameValid(pFilename)) - return 0; - - // extract the timestamp - int64 Timestamp = pThis->ExtractTimestamp(pFilename+pThis->m_FileDescLength+1); - - // add the entry - pThis->AddEntry(Timestamp); - - return 0; -} - - CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotDelta) { m_pEditor = 0; @@ -467,6 +290,7 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotD m_aServerAddressStr[0] = 0; mem_zero(m_aSnapshots, sizeof(m_aSnapshots)); + m_SnapshotStorage.Init(); m_RecivedSnapshots = 0; m_VersionInfo.m_State = CVersionInfo::STATE_INIT; @@ -536,11 +360,6 @@ void CClient::SendReady() SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH); } -bool CClient::RconAuthed() -{ - return m_RconAuthed; -} - void CClient::RconAuth(const char *pName, const char *pPassword) { if(RconAuthed()) @@ -549,6 +368,7 @@ void CClient::RconAuth(const char *pName, const char *pPassword) CMsgPacker Msg(NETMSG_RCON_AUTH); Msg.AddString(pName, 32); Msg.AddString(pPassword, 32); + Msg.AddInt(1); SendMsgEx(&Msg, MSGFLAG_VITAL); } @@ -693,12 +513,12 @@ void CClient::Connect(const char *pAddress) ServerInfoRequest(); - if(net_host_lookup(m_aServerAddressStr, &m_ServerAddress, NETTYPE_ALL) != 0) + if(net_host_lookup(m_aServerAddressStr, &m_ServerAddress, m_NetClient.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, NETTYPE_ALL); + net_host_lookup("localhost", &m_ServerAddress, m_NetClient.NetType()); } m_RconAuthed = 0; @@ -726,6 +546,7 @@ void CClient::DisconnectWithReason(const char *pReason) // m_RconAuthed = 0; + m_pConsole->DeregisterTempAll(); m_NetClient.Disconnect(pReason); SetState(IClient::STATE_OFFLINE); m_pMap->Unload(); @@ -1152,7 +973,7 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket) { 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 = GameClient()->GetCountryIndex(str_toint(Up.GetString())); + 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; } @@ -1316,11 +1137,28 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) CMsgPacker Msg(NETMSG_PING_REPLY); SendMsgEx(&Msg, 0); } + else if(Msg == NETMSG_RCON_CMD_ADD) + { + const char *pName = Unpacker.GetString(CUnpacker::SANITIZE_CC); + const char *pHelp = Unpacker.GetString(CUnpacker::SANITIZE_CC); + const char *pParams = Unpacker.GetString(CUnpacker::SANITIZE_CC); + if(Unpacker.Error() == 0) + m_pConsole->RegisterTemp(pName, pParams, CFGFLAG_SERVER, pHelp); + } + else if(Msg == NETMSG_RCON_CMD_REM) + { + const char *pName = Unpacker.GetString(CUnpacker::SANITIZE_CC); + if(Unpacker.Error() == 0) + m_pConsole->DeregisterTemp(pName); + } else if(Msg == NETMSG_RCON_AUTH_STATUS) { int Result = Unpacker.GetInt(); if(Unpacker.Error() == 0) m_RconAuthed = Result; + m_UseTempRconCommands = Unpacker.GetInt(); + if(Unpacker.Error() != 0) + m_UseTempRconCommands = 0; } else if(Msg == NETMSG_RCON_LINE) { @@ -1802,7 +1640,7 @@ void CClient::VersionUpdate() { if(m_VersionInfo.m_State == CVersionInfo::STATE_INIT) { - Engine()->HostLookup(&m_VersionInfo.m_VersionServeraddr, g_Config.m_ClVersionServer, m_BindAddr.type); + Engine()->HostLookup(&m_VersionInfo.m_VersionServeraddr, g_Config.m_ClVersionServer, m_NetClient.NetType()); m_VersionInfo.m_State = CVersionInfo::STATE_START; } else if(m_VersionInfo.m_State == CVersionInfo::STATE_START) @@ -1884,7 +1722,7 @@ void CClient::Run() Input()->Init(); // start refreshing addresses while we load - MasterServer()->RefreshAddresses(m_BindAddr.type); + MasterServer()->RefreshAddresses(m_NetClient.NetType()); // init the editor m_pEditor->Init(); @@ -2341,7 +2179,12 @@ void CClient::RegisterCommands() #include } -static CClient m_Client; +static CClient *CreateClient() +{ + CClient *pClient = static_cast(mem_alloc(sizeof(CClient), 1)); + mem_zero(pClient, sizeof(CClient)); + return new(pClient) CClient; +} /* Server Time @@ -2372,9 +2215,10 @@ int main(int argc, const char **argv) // ignore_convention } #endif + CClient *pClient = CreateClient(); IKernel *pKernel = IKernel::Create(); - pKernel->RegisterInterface(&m_Client); - m_Client.RegisterInterfaces(); + pKernel->RegisterInterface(pClient); + pClient->RegisterInterfaces(); // create the components IEngine *pEngine = CreateEngine("Teeworlds"); @@ -2427,12 +2271,12 @@ int main(int argc, const char **argv) // ignore_convention pEngineMasterServer->Load(); // register all console commands - m_Client.RegisterCommands(); + pClient->RegisterCommands(); pKernel->RequestInterface()->OnConsoleInit(); // init client's interfaces - m_Client.InitInterfaces(); + pClient->InitInterfaces(); // execute config file pConsole->ExecuteFile("settings.cfg", -1, IConsole::CONSOLELEVEL_CONFIG, 0, 0); @@ -2447,11 +2291,11 @@ int main(int argc, const char **argv) // ignore_convention // restore empty config strings to their defaults pConfig->RestoreStrings(); - m_Client.Engine()->InitLogfile(); + pClient->Engine()->InitLogfile(); // run the client dbg_msg("client", "starting..."); - m_Client.Run(); + pClient->Run(); // write down the config and quit pConfig->Save(); diff --git a/src/engine/client/client.h b/src/engine/client/client.h index b3af13c4c..919cc8efc 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -51,36 +51,6 @@ public: }; -class CFileCollection -{ - enum - { - MAX_ENTRIES=1000, - TIMESTAMP_LENGTH=20, // _YYYY-MM-DD_HH-MM-SS - }; - - int64 m_aTimestamps[MAX_ENTRIES]; - int m_NumTimestamps; - int m_MaxEntries; - char m_aFileDesc[128]; - int m_FileDescLength; - char m_aFileExt[32]; - int m_FileExtLength; - char m_aPath[512]; - IStorage *m_pStorage; - - bool IsFilenameValid(const char *pFilename); - int64 ExtractTimestamp(const char *pTimestring); - void BuildTimestring(int64 Timestamp, char *pTimestring); - -public: - void Init(IStorage *pStorage, const char *pPath, const char *pFileDesc, const char *pFileExt, int MaxEntries); - void AddEntry(int64 Timestamp); - - static int FilelistCallback(const char *pFilename, int IsDir, int StorageType, void *pUser); -}; - - class CClient : public IClient, public CDemoPlayer::IListner { // needed interfaces @@ -118,7 +88,6 @@ class CClient : public IClient, public CDemoPlayer::IListner float m_FrameTimeHigh; int m_Frames; NETADDR m_ServerAddress; - NETADDR m_BindAddr; int m_WindowMustRefocus; int m_SnapCrcErrors; bool m_AutoScreenshotRecycle; @@ -129,6 +98,7 @@ class CClient : public IClient, public CDemoPlayer::IListner int m_AckGameTick; int m_CurrentRecvTick; int m_RconAuthed; + int m_UseTempRconCommands; // version-checking char m_aVersionStr[10]; @@ -221,7 +191,8 @@ public: void SendEnterGame(); void SendReady(); - virtual bool RconAuthed(); + virtual bool RconAuthed() { return m_RconAuthed != 0; } + virtual bool UseTempRconCommands() { return m_UseTempRconCommands != 0; } void RconAuth(const char *pName, const char *pPassword); virtual void Rcon(const char *pCmd); diff --git a/src/engine/client/friends.cpp b/src/engine/client/friends.cpp index 0545d9f41..aad0577fc 100644 --- a/src/engine/client/friends.cpp +++ b/src/engine/client/friends.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 @@ -11,6 +12,7 @@ CFriends::CFriends() { mem_zero(m_aFriends, sizeof(m_aFriends)); + m_NumFriends = 0; } void CFriends::ConAddFriend(IConsole::IResult *pResult, void *pUserData, int ClientID) @@ -44,12 +46,35 @@ const CFriendInfo *CFriends::GetFriend(int Index) const return &m_aFriends[max(0, Index%m_NumFriends)]; } -bool CFriends::IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const +int CFriends::GetFriendState(const char *pName, const char *pClan) const { + int Result = FRIEND_NO; + unsigned NameHash = str_quickhash(pName); + unsigned ClanHash = str_quickhash(pClan); for(int i = 0; i < m_NumFriends; ++i) { - if(!str_comp(m_aFriends[i].m_aClan, pClan) && - ((!PlayersOnly && m_aFriends[i].m_aName[0] == 0) || !str_comp(m_aFriends[i].m_aName, pName))) + if(m_aFriends[i].m_ClanHash == ClanHash) + { + if(m_aFriends[i].m_aName[0] == 0) + Result = FRIEND_CLAN; + else if(m_aFriends[i].m_NameHash == NameHash) + { + Result = FRIEND_PLAYER; + break; + } + } + } + return Result; +} + +bool CFriends::IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const +{ + unsigned NameHash = str_quickhash(pName); + unsigned ClanHash = str_quickhash(pClan); + for(int i = 0; i < m_NumFriends; ++i) + { + if(m_aFriends[i].m_ClanHash == ClanHash && + ((!PlayersOnly && m_aFriends[i].m_aName[0] == 0) || m_aFriends[i].m_NameHash == NameHash)) return true; } return false; @@ -61,22 +86,28 @@ void CFriends::AddFriend(const char *pName, const char *pClan) return; // make sure we don't have the friend already + unsigned NameHash = str_quickhash(pName); + unsigned ClanHash = str_quickhash(pClan); for(int i = 0; i < m_NumFriends; ++i) { - if(!str_comp(m_aFriends[i].m_aName, pName) && !str_comp(m_aFriends[i].m_aClan, pClan)) + if(m_aFriends[i].m_NameHash == NameHash && m_aFriends[i].m_ClanHash == ClanHash) return; } str_copy(m_aFriends[m_NumFriends].m_aName, pName, sizeof(m_aFriends[m_NumFriends].m_aName)); str_copy(m_aFriends[m_NumFriends].m_aClan, pClan, sizeof(m_aFriends[m_NumFriends].m_aClan)); + m_aFriends[m_NumFriends].m_NameHash = NameHash; + m_aFriends[m_NumFriends].m_ClanHash = ClanHash; ++m_NumFriends; } void CFriends::RemoveFriend(const char *pName, const char *pClan) { + unsigned NameHash = str_quickhash(pName); + unsigned ClanHash = str_quickhash(pClan); for(int i = 0; i < m_NumFriends; ++i) { - if(!str_comp(m_aFriends[i].m_aName, pName) && !str_comp(m_aFriends[i].m_aClan, pClan)) + if(m_aFriends[i].m_NameHash == NameHash && m_aFriends[i].m_ClanHash == ClanHash) { RemoveFriend(i); return; diff --git a/src/engine/client/friends.h b/src/engine/client/friends.h index 3f6daf9d1..1b3a4d4fd 100644 --- a/src/engine/client/friends.h +++ b/src/engine/client/friends.h @@ -22,6 +22,7 @@ public: int NumFriends() const { return m_NumFriends; } const CFriendInfo *GetFriend(int Index) const; + int GetFriendState(const char *pName, const char *pClan) const; bool IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const; void AddFriend(const char *pName, const char *pClan); diff --git a/src/engine/client/graphics.cpp b/src/engine/client/graphics.cpp index 641f9dfb8..d1f0b8a84 100644 --- a/src/engine/client/graphics.cpp +++ b/src/engine/client/graphics.cpp @@ -2,6 +2,7 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include +#include #include "SDL.h" @@ -119,12 +120,39 @@ void CGraphics_OpenGL::Rotate4(const CPoint &rCenter, CVertex *pPoints) } } -unsigned char CGraphics_OpenGL::Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset) +unsigned char CGraphics_OpenGL::Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp) { - return (pData[(v*w+u)*4+Offset]+ - pData[(v*w+u+1)*4+Offset]+ - pData[((v+1)*w+u)*4+Offset]+ - pData[((v+1)*w+u+1)*4+Offset])/4; + 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_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 == 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_OpenGL::CGraphics_OpenGL() @@ -151,7 +179,16 @@ CGraphics_OpenGL::CGraphics_OpenGL() void CGraphics_OpenGL::ClipEnable(int x, int y, int w, int h) { - //if(no_gfx) return; + if(x < 0) + w += x; + if(y < 0) + h += y; + + x = clamp(x, 0, ScreenWidth()); + y = clamp(y, 0, ScreenHeight()); + w = clamp(w, 0, ScreenWidth()-x); + h = clamp(h, 0, ScreenHeight()-y); + glScissor(x, ScreenHeight()-(y+h), w, h); glEnable(GL_SCISSOR_TEST); } @@ -205,21 +242,21 @@ void CGraphics_OpenGL::GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBot void CGraphics_OpenGL::LinesBegin() { - dbg_assert(m_Drawing == 0, "called begin twice"); + dbg_assert(m_Drawing == 0, "called Graphics()->LinesBegin twice"); m_Drawing = DRAWING_LINES; SetColor(1,1,1,1); } void CGraphics_OpenGL::LinesEnd() { - dbg_assert(m_Drawing == DRAWING_LINES, "called end without begin"); + dbg_assert(m_Drawing == DRAWING_LINES, "called Graphics()->LinesEnd without begin"); Flush(); m_Drawing = 0; } void CGraphics_OpenGL::LinesDraw(const CLineItem *pArray, int Num) { - dbg_assert(m_Drawing == DRAWING_LINES, "called draw without begin"); + dbg_assert(m_Drawing == DRAWING_LINES, "called Graphics()->LinesDraw without begin"); for(int i = 0; i < Num; ++i) { @@ -272,28 +309,23 @@ int CGraphics_OpenGL::LoadTextureRaw(int Width, int Height, int Format, const vo m_aTextures[Tex].m_Next = -1; // resample if needed - if(!(Flags&TEXLOAD_NORESAMPLE) && g_Config.m_GfxTextureQuality==0) + if(!(Flags&TEXLOAD_NORESAMPLE) && (Format == CImageInfo::FORMAT_RGBA || Format == CImageInfo::FORMAT_RGB)) { - if(Width > 16 && Height > 16 && Format == CImageInfo::FORMAT_RGBA) + if(Width > GL_MAX_TEXTURE_SIZE || Height > GL_MAX_TEXTURE_SIZE) { - unsigned char *pTmpData; - int c = 0; - int x, y; - - pTmpData = (unsigned char *)mem_alloc(Width*Height*4, 1); - - Width/=2; - Height/=2; - - for(y = 0; y < Height; y++) - for(x = 0; x < Width; x++, c++) - { - pTmpData[c*4] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 0); - pTmpData[c*4+1] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 1); - pTmpData[c*4+2] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 2); - pTmpData[c*4+3] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 3); - } + int NewWidth = min(Width, GL_MAX_TEXTURE_SIZE); + int NewHeight = min(Height, GL_MAX_TEXTURE_SIZE); + pTmpData = Rescale(Width, Height, NewWidth, NewHeight, Format, pTexData); pTexData = pTmpData; + Width = NewWidth; + Height = NewHeight; + } + else if(Width > 16 && Height > 16 && g_Config.m_GfxTextureQuality == 0) + { + pTmpData = Rescale(Width, Height, Width/2, Height/2, Format, pTexData); + pTexData = pTmpData; + Width /= 2; + Height /= 2; } } @@ -488,7 +520,7 @@ void CGraphics_OpenGL::Clear(float r, float g, float b) void CGraphics_OpenGL::QuadsBegin() { - dbg_assert(m_Drawing == 0, "called quads_begin twice"); + dbg_assert(m_Drawing == 0, "called Graphics()->QuadsBegin twice"); m_Drawing = DRAWING_QUADS; QuadsSetSubset(0,0,1,1); @@ -498,7 +530,7 @@ void CGraphics_OpenGL::QuadsBegin() void CGraphics_OpenGL::QuadsEnd() { - dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_end without begin"); + dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsEnd without begin"); Flush(); m_Drawing = 0; } @@ -511,7 +543,7 @@ void CGraphics_OpenGL::QuadsSetRotation(float Angle) void CGraphics_OpenGL::SetColorVertex(const CColorVertex *pArray, int Num) { - dbg_assert(m_Drawing != 0, "called gfx_quads_setcolorvertex without begin"); + dbg_assert(m_Drawing != 0, "called Graphics()->SetColorVertex without begin"); for(int i = 0; i < Num; ++i) { @@ -524,7 +556,7 @@ void CGraphics_OpenGL::SetColorVertex(const CColorVertex *pArray, int Num) void CGraphics_OpenGL::SetColor(float r, float g, float b, float a) { - dbg_assert(m_Drawing != 0, "called gfx_quads_setcolor without begin"); + dbg_assert(m_Drawing != 0, "called Graphics()->SetColor without begin"); CColorVertex Array[4] = { CColorVertex(0, r, g, b, a), CColorVertex(1, r, g, b, a), @@ -570,7 +602,7 @@ void CGraphics_OpenGL::QuadsDrawTL(const CQuadItem *pArray, int Num) CPoint Center; Center.z = 0; - dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw without begin"); + dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsDrawTL without begin"); for(int i = 0; i < Num; ++i) { @@ -608,7 +640,7 @@ void CGraphics_OpenGL::QuadsDrawTL(const CQuadItem *pArray, int Num) void CGraphics_OpenGL::QuadsDrawFreeform(const CFreeformItem *pArray, int Num) { - dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw_freeform without begin"); + dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsDrawFreeform without begin"); for(int i = 0; i < Num; ++i) { diff --git a/src/engine/client/graphics.h b/src/engine/client/graphics.h index 4367ccfca..95e9769ad 100644 --- a/src/engine/client/graphics.h +++ b/src/engine/client/graphics.h @@ -66,7 +66,8 @@ protected: void AddVertices(int Count); void Rotate4(const CPoint &rCenter, CVertex *pPoints); - static unsigned char Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset); + 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); public: CGraphics_OpenGL(); diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index 424acb22c..ddfc75972 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -144,63 +144,65 @@ void CServerBrowser::Filter() { int Filtered = 0; - if(g_Config.m_BrFilterFriends) + if(g_Config.m_BrFilterEmpty && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == 0) || m_ppServerlist[i]->m_Info.m_NumClients == 0)) + Filtered = 1; + else if(g_Config.m_BrFilterFull && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers) || + m_ppServerlist[i]->m_Info.m_NumClients == m_ppServerlist[i]->m_Info.m_MaxClients)) + Filtered = 1; + else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD) + Filtered = 1; + else if(g_Config.m_BrFilterPure && + (str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 && + str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 && + str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0)) { Filtered = 1; - for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++) - { - if(m_pFriends->IsFriend(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, false)) - { - Filtered = 0; - break; - } - } } + else if(g_Config.m_BrFilterPureMap && + !(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf6") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf7") == 0) + ) + { + Filtered = 1; + } + else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency) + Filtered = 1; + else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0) + Filtered = 1; + else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress)) + Filtered = 1; + else if(g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && str_comp_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype)) + Filtered = 1; + else if(!g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype)) + Filtered = 1; else { - if(g_Config.m_BrFilterEmpty && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == 0) || m_ppServerlist[i]->m_Info.m_NumClients == 0)) - Filtered = 1; - else if(g_Config.m_BrFilterFull && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers) || - m_ppServerlist[i]->m_Info.m_NumClients == m_ppServerlist[i]->m_Info.m_MaxClients)) - Filtered = 1; - else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD) - Filtered = 1; - else if(g_Config.m_BrFilterPure && - (str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 && - str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 && - str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0)) + if(g_Config.m_BrFilterCountry) { Filtered = 1; + // match against player country + for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++) + { + if(m_ppServerlist[i]->m_Info.m_aClients[p].m_Country == g_Config.m_BrFilterCountryIndex) + { + Filtered = 0; + break; + } + } } - else if(g_Config.m_BrFilterPureMap && - !(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf6") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf7") == 0) - ) - { - Filtered = 1; - } - else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency) - Filtered = 1; - else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0) - Filtered = 1; - else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress)) - Filtered = 1; - else if(g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && str_comp_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype)) - Filtered = 1; - else if(!g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype)) - Filtered = 1; - else if(g_Config.m_BrFilterString[0] != 0) + + if(!Filtered && g_Config.m_BrFilterString[0] != 0) { int MatchFound = 0; @@ -238,7 +240,19 @@ void CServerBrowser::Filter() } if(Filtered == 0) - m_pSortedServerlist[m_NumSortedServers++] = i; + { + // check for friend + m_ppServerlist[i]->m_Info.m_FriendState = IFriends::FRIEND_NO; + for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++) + { + m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState = m_pFriends->GetFriendState(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, + m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan); + m_ppServerlist[i]->m_Info.m_FriendState = max(m_ppServerlist[i]->m_Info.m_FriendState, m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState); + } + + if(!g_Config.m_BrFilterFriends || m_ppServerlist[i]->m_Info.m_FriendState != IFriends::FRIEND_NO) + m_pSortedServerlist[m_NumSortedServers++] = i; + } } } @@ -255,7 +269,8 @@ int CServerBrowser::SortHash() const i |= g_Config.m_BrFilterPure<<11; i |= g_Config.m_BrFilterPureMap<<12; i |= g_Config.m_BrFilterGametypeStrict<<13; - i |= g_Config.m_BrFilterPing<<18; + i |= g_Config.m_BrFilterCountry<<14; + i |= g_Config.m_BrFilterPing<<15; return i; } diff --git a/src/engine/client/sound.cpp b/src/engine/client/sound.cpp index 4678bb8aa..aae52e32c 100644 --- a/src/engine/client/sound.cpp +++ b/src/engine/client/sound.cpp @@ -31,6 +31,7 @@ struct CSample int m_Channels; int m_LoopStart; int m_LoopEnd; + int m_PausedAt; }; struct CChannel @@ -54,7 +55,6 @@ static CVoice m_aVoices[NUM_VOICES] = { {0} }; static CChannel m_aChannels[NUM_CHANNELS] = { {255, 0} }; static LOCK m_SoundLock = 0; -static int m_SoundEnabled = 0; static int m_CenterX = 0; static int m_CenterY = 0; @@ -126,7 +126,7 @@ static void Mix(short *pFinalOut, unsigned Frames) int dy = v->m_Y - m_CenterY; int Dist = (int)sqrtf((float)dx*dx+dy*dy); // float here. nasty int p = IntAbs(dx); - if(Dist < Range) + if(Dist >= 0 && Dist < Range) { // panning if(dx > 0) @@ -198,6 +198,7 @@ static void SdlCallback(void *pUnused, Uint8 *pStream, int Len) int CSound::Init() { + m_SoundEnabled = 0; m_pGraphics = Kernel()->RequestInterface(); m_pStorage = Kernel()->RequestInterface(); @@ -393,6 +394,7 @@ int CSound::LoadWV(const char *pFilename) pSample->m_NumFrames = m_aSamples; pSample->m_LoopStart = -1; pSample->m_LoopEnd = -1; + pSample->m_PausedAt = 0; } else { @@ -446,7 +448,10 @@ int CSound::Play(int ChannelID, int SampleID, int Flags, float x, float y) { m_aVoices[VoiceID].m_pSample = &m_aSamples[SampleID]; m_aVoices[VoiceID].m_pChannel = &m_aChannels[ChannelID]; - m_aVoices[VoiceID].m_Tick = 0; + if(Flags & FLAG_LOOP) + m_aVoices[VoiceID].m_Tick = m_aSamples[SampleID].m_PausedAt; + else + m_aVoices[VoiceID].m_Tick = 0; m_aVoices[VoiceID].m_Vol = 255; m_aVoices[VoiceID].m_Flags = Flags; m_aVoices[VoiceID].m_X = (int)x; @@ -475,7 +480,13 @@ void CSound::Stop(int SampleID) for(int i = 0; i < NUM_VOICES; i++) { if(m_aVoices[i].m_pSample == pSample) + { + if(m_aVoices[i].m_Flags & FLAG_LOOP) + m_aVoices[i].m_pSample->m_PausedAt = m_aVoices[i].m_Tick; + else + m_aVoices[i].m_pSample->m_PausedAt = 0; m_aVoices[i].m_pSample = 0; + } } lock_release(m_SoundLock); } @@ -486,6 +497,13 @@ void CSound::StopAll() lock_wait(m_SoundLock); for(int i = 0; i < NUM_VOICES; i++) { + if(m_aVoices[i].m_pSample) + { + if(m_aVoices[i].m_Flags & FLAG_LOOP) + m_aVoices[i].m_pSample->m_PausedAt = m_aVoices[i].m_Tick; + else + m_aVoices[i].m_pSample->m_PausedAt = 0; + } m_aVoices[i].m_pSample = 0; } lock_release(m_SoundLock); diff --git a/src/engine/client/sound.h b/src/engine/client/sound.h index 3cc84d4da..8112427c3 100644 --- a/src/engine/client/sound.h +++ b/src/engine/client/sound.h @@ -7,6 +7,8 @@ class CSound : public IEngineSound { + int m_SoundEnabled; + public: IEngineGraphics *m_pGraphics; IStorage *m_pStorage; @@ -23,6 +25,8 @@ public: static IOHANDLE ms_File; static int ReadData(void *pBuffer, int Size); + virtual bool IsSoundEnabled() { return m_SoundEnabled != 0; } + virtual int LoadWV(const char *pFilename); virtual void SetListenerPos(float x, float y); diff --git a/src/engine/console.h b/src/engine/console.h index afd507b53..e44e82cf8 100644 --- a/src/engine/console.h +++ b/src/engine/console.h @@ -10,11 +10,21 @@ class IConsole : public IInterface MACRO_INTERFACE("console", 0) public: + // TODO: rework/cleanup enum { OUTPUT_LEVEL_STANDARD=0, OUTPUT_LEVEL_ADDINFO, - OUTPUT_LEVEL_DEBUG + OUTPUT_LEVEL_DEBUG, + + ACCESS_LEVEL_ADMIN=0, + ACCESS_LEVEL_MOD, + + TEMPCMD_NAME_LENGTH=32, + TEMPCMD_HELP_LENGTH=96, + TEMPCMD_PARAMS_LENGTH=16, + + MAX_PRINT_CB=4, }; // TODO: rework this interface to reduce the amount of virtual calls @@ -40,14 +50,24 @@ public: class CCommandInfo { + protected: + int m_AccessLevel; public: + CCommandInfo() { m_AccessLevel = ACCESS_LEVEL_ADMIN; } + virtual ~CCommandInfo() {} const char *m_pName; const char *m_pHelp; const char *m_pParams; +<<<<<<< HEAD // DDRace int m_Level; +======= + virtual const CCommandInfo *NextCommandInfo(int AccessLevel, int FlagMask) const = 0; + + int GetAccessLevel() const { return m_AccessLevel; } +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe }; typedef void (*FPrintCallback)(const char *pStr, void *pUser); @@ -55,12 +75,20 @@ public: typedef void (*FCommandCallback)(IResult *pResult, void *pUserData, int ClientID); typedef void (*FChainCommandCallback)(IResult *pResult, void *pUserData, FCommandCallback pfnCallback, void *pCallbackUserData); - virtual CCommandInfo *GetCommandInfo(const char *pName, int FlagMask) = 0; - virtual void PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser) = 0; + virtual const CCommandInfo *FirstCommandInfo(int AccessLevel, int Flagmask) const = 0; + virtual const CCommandInfo *GetCommandInfo(const char *pName, int FlagMask, bool Temp) = 0; + virtual void PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser) = 0; virtual void ParseArguments(int NumArgs, const char **ppArguments) = 0; +<<<<<<< HEAD virtual void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp, const int Level) = 0; +======= + virtual void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp) = 0; + virtual void RegisterTemp(const char *pName, const char *pParams, int Flags, const char *pHelp) = 0; + virtual void DeregisterTemp(const char *pName) = 0; + virtual void DeregisterTempAll() = 0; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser) = 0; virtual void StoreCommands(bool Store, int ClientID) = 0; @@ -69,9 +97,11 @@ public: //virtual void ExecuteLineStroked(int Stroke, const char *pStr) = 0; //virtual void ExecuteFile(const char *pFilename) = 0; - virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData) = 0; + virtual int RegisterPrintCallback(int OutputLevel, FPrintCallback pfnPrintCallback, void *pUserData) = 0; + virtual void SetPrintOutputLevel(int Index, int OutputLevel) = 0; virtual void Print(int Level, const char *pFrom, const char *pStr) = 0; +<<<<<<< HEAD //DDRace @@ -105,6 +135,9 @@ public: CONSOLELEVEL_ADMIN, CONSOLELEVEL_CONFIG }; +======= + virtual void SetAccessLevel(int AccessLevel) = 0; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe }; extern IConsole *CreateConsole(int FlagMask); diff --git a/src/engine/demo.h b/src/engine/demo.h index 891c78d61..a9e4f700f 100644 --- a/src/engine/demo.h +++ b/src/engine/demo.h @@ -46,7 +46,7 @@ public: virtual void Pause() = 0; virtual void Unpause() = 0; virtual const CInfo *BaseInfo() const = 0; - virtual char *GetDemoName() = 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; virtual int GetDemoType() const = 0; }; diff --git a/src/engine/friends.h b/src/engine/friends.h index bf9df904a..164e3461a 100644 --- a/src/engine/friends.h +++ b/src/engine/friends.h @@ -11,6 +11,8 @@ struct CFriendInfo { char m_aName[MAX_NAME_LENGTH]; char m_aClan[MAX_CLAN_LENGTH]; + unsigned m_NameHash; + unsigned m_ClanHash; }; class IFriends : public IInterface @@ -19,6 +21,10 @@ class IFriends : public IInterface public: enum { + FRIEND_NO=0, + FRIEND_CLAN, + FRIEND_PLAYER, + MAX_FRIENDS=128, }; @@ -26,11 +32,11 @@ public: virtual int NumFriends() const = 0; virtual const CFriendInfo *GetFriend(int Index) const = 0; + virtual int GetFriendState(const char *pName, const char *pClan) const = 0; virtual bool IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const = 0; virtual void AddFriend(const char *pName, const char *pClan) = 0; virtual void RemoveFriend(const char *pName, const char *pClan) = 0; - virtual void RemoveFriend(int Index) = 0; }; #endif diff --git a/src/engine/graphics.h b/src/engine/graphics.h index 0912bfb5a..e1652cbe8 100644 --- a/src/engine/graphics.h +++ b/src/engine/graphics.h @@ -76,7 +76,7 @@ public: virtual void BlendAdditive() = 0; virtual int MemoryUsage() const = 0; - virtual int LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType) =0; + virtual int LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType) = 0; virtual int UnloadTexture(int Index) = 0; virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) = 0; virtual int LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags) = 0; diff --git a/src/engine/masterserver.h b/src/engine/masterserver.h index 74a394dc0..57433993d 100644 --- a/src/engine/masterserver.h +++ b/src/engine/masterserver.h @@ -23,7 +23,6 @@ public: virtual int RefreshAddresses(int Nettype) = 0; virtual void Update() = 0; virtual int IsRefreshing() = 0; - virtual void DumpServers() = 0; virtual NETADDR GetAddr(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 b4c498f00..1fcf70560 100644 --- a/src/engine/server.h +++ b/src/engine/server.h @@ -57,11 +57,15 @@ public: virtual int IsAuthed(int ClientID) = 0; virtual void Kick(int ClientID, const char *pReason) = 0; +<<<<<<< HEAD // DDRace virtual void GetClientAddr(int ClientID, NETADDR *pAddr) = 0; virtual void SetRconLevel(int ClientID, int Level) = 0; virtual void SetClientAuthed(int ClientID, int Authed) = 0; +======= + virtual void DemoRecorder_HandleAutoStart() = 0; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe }; class IGameServer : public IInterface diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index ff2bde2bb..948d6ba96 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.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 @@ -15,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -191,6 +194,7 @@ CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta) m_MapReload = 0; m_RconClientID = -1; + m_RconAuthLevel = AUTHED_ADMIN; Init(); } @@ -290,6 +294,11 @@ void CServer::Kick(int ClientID, const char *pReason) Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't kick yourself"); return; } + else if(m_aClients[ClientID].m_Authed > m_RconAuthLevel) + { + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "kick command denied"); + return; + } m_NetServer.Drop(ClientID, pReason); } @@ -584,9 +593,13 @@ int CServer::NewClientCallback(int ClientID, void *pUser) pThis->m_aClients[ClientID].m_aName[0] = 0; pThis->m_aClients[ClientID].m_aClan[0] = 0; pThis->m_aClients[ClientID].m_Country = -1; - pThis->m_aClients[ClientID].m_Authed = 0; + pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; pThis->m_aClients[ClientID].m_AuthTries = 0; +<<<<<<< HEAD memset(&pThis->m_aClients[ClientID].m_Addr, 0, sizeof(NETADDR)); +======= + pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe pThis->m_aClients[ClientID].Reset(); return 0; } @@ -610,10 +623,14 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser) pThis->m_aClients[ClientID].m_aName[0] = 0; pThis->m_aClients[ClientID].m_aClan[0] = 0; pThis->m_aClients[ClientID].m_Country = -1; - pThis->m_aClients[ClientID].m_Authed = 0; + pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; pThis->m_aClients[ClientID].m_AuthTries = 0; +<<<<<<< HEAD pThis->m_aPrevStates[ClientID] = CClient::STATE_EMPTY; memset(&pThis->m_aClients[ClientID].m_Addr, 0, sizeof(NETADDR)); +======= + pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe pThis->m_aClients[ClientID].m_Snapshots.PurgeAll(); return 0; } @@ -651,13 +668,44 @@ 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) + if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed >= pThis->m_RconAuthLevel) pThis->SendRconLine(i, pLine); } ReentryGuard--; } +void CServer::SendRconCmdAdd(const IConsole::CCommandInfo *pCommandInfo, int ClientID) +{ + CMsgPacker Msg(NETMSG_RCON_CMD_ADD); + Msg.AddString(pCommandInfo->m_pName, IConsole::TEMPCMD_NAME_LENGTH); + Msg.AddString(pCommandInfo->m_pHelp, IConsole::TEMPCMD_HELP_LENGTH); + Msg.AddString(pCommandInfo->m_pParams, IConsole::TEMPCMD_PARAMS_LENGTH); + SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true); +} + +void CServer::SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int ClientID) +{ + CMsgPacker Msg(NETMSG_RCON_CMD_REM); + Msg.AddString(pCommandInfo->m_pName, 256); + SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true); +} + +void CServer::UpdateClientRconCommands() +{ + int ClientID = Tick() % MAX_CLIENTS; + + if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY && m_aClients[ClientID].m_Authed) + { + int ConsoleAccessLevel = m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD; + for(int i = 0; i < MAX_RCONCMD_SEND && m_aClients[ClientID].m_pRconCmdToSend; ++i) + { + SendRconCmdAdd(m_aClients[ClientID].m_pRconCmdToSend, ClientID); + m_aClients[ClientID].m_pRconCmdToSend = m_aClients[ClientID].m_pRconCmdToSend->NextCommandInfo(ConsoleAccessLevel, CFGFLAG_SERVER); + } + } +} + void CServer::ProcessClientPacket(CNetChunk *pPacket) { int ClientID = pPacket->m_ClientID; @@ -838,8 +886,12 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) str_format(aBuf, sizeof(aBuf), "ClientID=%d rcon='%s'", ClientID, pCmd); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf); m_RconClientID = ClientID; + m_RconAuthLevel = m_aClients[ClientID].m_Authed; + Console()->SetAccessLevel(m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD); Console()->ExecuteLine(pCmd); + Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN); m_RconClientID = -1; +<<<<<<< HEAD }*/ if(Unpacker.Error() == 0) { @@ -856,6 +908,9 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) Console()->ExecuteLine(pCmd, ClientID, m_aClients[ClientID].m_Authed, SendRconResponse, &Info); m_RconClientID = -1; } +======= + m_RconAuthLevel = AUTHED_ADMIN; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } } else if(Msg == NETMSG_RCON_AUTH) @@ -869,20 +924,40 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) /*if(Unpacker.Error() == 0) { - if(g_Config.m_SvRconPassword[0] == 0) + if(g_Config.m_SvRconPassword[0] == 0 && g_Config.m_SvRconModPassword[0] == 0) { - SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password to enable the remote console."); + SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password and/or sv_rcon_mod_password to enable the remote console."); } - else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0) + else if(g_Config.m_SvRconPassword[0] && str_comp(pPw, g_Config.m_SvRconPassword) == 0) { CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS); - Msg.AddInt(1); + Msg.AddInt(1); //authed + Msg.AddInt(1); //cmdlist SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true); - m_aClients[ClientID].m_Authed = 1; - SendRconLine(ClientID, "Authentication successful. Remote console access granted."); + m_aClients[ClientID].m_Authed = AUTHED_ADMIN; + int SendRconCmds = Unpacker.GetInt(); + if(Unpacker.Error() == 0 && SendRconCmds) + m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_ADMIN, CFGFLAG_SERVER); + SendRconLine(ClientID, "Admin authentication successful. Full remote console access granted."); char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "ClientID=%d authed", ClientID); + str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (admin)", ClientID); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); + } + else if(g_Config.m_SvRconModPassword[0] && str_comp(pPw, g_Config.m_SvRconModPassword) == 0) + { + CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS); + Msg.AddInt(1); //authed + Msg.AddInt(1); //cmdlist + SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true); + + m_aClients[ClientID].m_Authed = AUTHED_MOD; + int SendRconCmds = Unpacker.GetInt(); + if(Unpacker.Error() == 0 && SendRconCmds) + m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_MOD, CFGFLAG_SERVER); + SendRconLine(ClientID, "Moderator authentication successful. Limited remote console access granted."); + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (moderator)", ClientID); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); } else if(g_Config.m_SvRconMaxTries) @@ -1096,6 +1171,8 @@ void CServer::PumpNetwork() else ProcessClientPacket(&Packet); } + + m_Econ.Update(); } char *CServer::GetMapName() @@ -1145,7 +1222,11 @@ int CServer::LoadMap(const char *pMapName) str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap)); //map_set(df); +<<<<<<< HEAD // load compelate map into memory for download // Todo: GreYFoX fix this for vanilla (compelate -> complete) +======= + // load complete map into memory for download +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe { IOHANDLE File = Storage()->OpenFile(aBuf, IOFLAG_READ, IStorage::TYPE_ALL); m_CurrentMapSize = (int)io_length(File); @@ -1174,9 +1255,13 @@ int CServer::Run() m_pStorage = Kernel()->RequestInterface(); // +<<<<<<< HEAD Console()->RegisterPrintCallback(SendRconLineAuthed, this); Console()->RegisterClientOnlineCallback(ClientOnline, this); Console()->RegisterCompareClientsCallback(CompareClients, this); +======= + m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, SendRconLineAuthed, this); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe // load map if(!LoadMap(g_Config.m_SvMap)) @@ -1199,7 +1284,6 @@ int CServer::Run() BindAddr.port = g_Config.m_SvPort; } - if(!m_NetServer.Open(BindAddr, g_Config.m_SvMaxClients, g_Config.m_SvMaxClientsPerIP, 0)) { dbg_msg("server", "couldn't open socket. port might already be in use"); @@ -1208,8 +1292,13 @@ int CServer::Run() m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this); +<<<<<<< HEAD Console()->ExecuteFile(SERVER_BANMASTERFILE, -1, IConsole::CONSOLELEVEL_CONFIG, 0, 0); +======= + m_Econ.Init(Console()); + +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe char aBuf[256]; str_format(aBuf, sizeof(aBuf), "server name is '%s'", g_Config.m_SvName); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); @@ -1304,10 +1393,12 @@ int CServer::Run() { if(g_Config.m_SvHighBandwidth || (m_CurrentGameTick%2) == 0) DoSnapshot(); + + UpdateClientRconCommands(); } // master server stuff - m_Register.RegisterUpdate(BindAddr.type); + m_Register.RegisterUpdate(m_NetServer.NetType()); PumpNetwork(); @@ -1345,6 +1436,8 @@ int CServer::Run() { if(m_aClients[i].m_State != CClient::STATE_EMPTY) m_NetServer.Drop(i, "Server shutdown"); + + m_Econ.Shutdown(); } GameServer()->OnShutdown(); @@ -1377,7 +1470,7 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser, int ClientID) const char *pReason = "No reason given"; if(pResult->NumArguments() > 1) - Minutes = pResult->GetInteger(1); + Minutes = max(0, pResult->GetInteger(1)); if(pResult->NumArguments() > 2) pReason = pResult->GetString(2); @@ -1393,6 +1486,20 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser, int ClientID) pResult->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself"); return; } + + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(i == pServer->m_RconClientID) + continue; + + AddrCheck = pServer->m_NetServer.ClientAddr(i); + AddrCheck.port = 0; + if(net_addr_comp(&Addr, &AddrCheck) == 0 && pServer->m_aClients[i].m_Authed > pServer->m_RconAuthLevel) + { + pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied"); + return; + } + } } pServer->BanAdd(Addr, Minutes*60, pReason); } @@ -1410,8 +1517,16 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser, int ClientID) pResult->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself"); return; } +<<<<<<< HEAD if (ClientID != -1 && ((CServer *)pUser)->m_aClients[ClientID].m_Authed <= ((CServer *)pUser)->m_aClients[Victim].m_Authed) return; +======= + else if(pServer->m_aClients[ClientID].m_Authed > pServer->m_RconAuthLevel) + { + pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied"); + return; + } +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe Addr = pServer->m_NetServer.ClientAddr(Victim); pServer->BanAdd(Addr, Minutes*60, pReason); @@ -1517,7 +1632,30 @@ void CServer::ConShutdown(IConsole::IResult *pResult, void *pUser, int ClientID) ((CServer *)pUser)->m_RunServer = 0; } +<<<<<<< HEAD void CServer::ConRecord(IConsole::IResult *pResult, void *pUser, int ClientID) +======= +void CServer::DemoRecorder_HandleAutoStart() +{ + if(g_Config.m_SvAutoDemoRecord) + { + m_DemoRecorder.Stop(); + char aFilename[128]; + char aDate[20]; + str_timestamp(aDate, sizeof(aDate)); + str_format(aFilename, sizeof(aFilename), "demos/%s_%s.demo", "auto/autorecord", aDate); + m_DemoRecorder.Start(Storage(), m_pConsole, aFilename, GameServer()->NetVersion(), m_aCurrentMap, m_CurrentMapCrc, "server"); + if(g_Config.m_SvAutoDemoMax) + { + // clean up auto recorded demos + CFileCollection AutoDemos; + AutoDemos.Init(Storage(), "demos/server", "autorecord", ".demo", g_Config.m_SvAutoDemoMax); + } + } +} + +void CServer::ConRecord(IConsole::IResult *pResult, void *pUser) +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe { CServer* pServer = (CServer *)pUser; char aFilename[128]; @@ -1557,10 +1695,50 @@ void CServer::ConchainMaxclientsperipUpdate(IConsole::IResult *pResult, void *pU ((CServer *)pUserData)->m_NetServer.SetMaxClientsPerIP(pResult->GetInteger(0)); } +void CServer::ConchainModCommandUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + if(pResult->NumArguments() == 2) + { + CServer *pThis = static_cast(pUserData); + const IConsole::CCommandInfo *pInfo = pThis->Console()->GetCommandInfo(pResult->GetString(0), CFGFLAG_SERVER, false); + int OldAccessLevel = 0; + if(pInfo) + OldAccessLevel = pInfo->GetAccessLevel(); + pfnCallback(pResult, pCallbackUserData); + if(pInfo && OldAccessLevel != pInfo->GetAccessLevel()) + { + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(pThis->m_aClients[i].m_State == CServer::CClient::STATE_EMPTY || pThis->m_aClients[i].m_Authed != CServer::AUTHED_MOD || + (pThis->m_aClients[i].m_pRconCmdToSend && str_comp(pResult->GetString(0), pThis->m_aClients[i].m_pRconCmdToSend->m_pName) >= 0)) + continue; + + if(OldAccessLevel == IConsole::ACCESS_LEVEL_ADMIN) + pThis->SendRconCmdAdd(pInfo, i); + else + pThis->SendRconCmdRem(pInfo, i); + } + } + } + else + pfnCallback(pResult, pCallbackUserData); +} + +void CServer::ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments() == 1) + { + CServer *pThis = static_cast(pUserData); + pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0)); + } +} + void CServer::RegisterCommands() { m_pConsole = Kernel()->RequestInterface(); +<<<<<<< HEAD Console()->Register("kick", "v?r", CFGFLAG_SERVER, ConKick, this, "", IConsole::CONSOLELEVEL_MODERATOR); Console()->Register("ban", "s?ir", CFGFLAG_SERVER|CFGFLAG_STORE, ConBan, this, "", IConsole::CONSOLELEVEL_MODERATOR); Console()->Register("unban", "s", CFGFLAG_SERVER|CFGFLAG_STORE, ConUnban, this, "", IConsole::CONSOLELEVEL_MODERATOR); @@ -1577,15 +1755,33 @@ void CServer::RegisterCommands() Console()->Register("add_banmaster", "s", CFGFLAG_SERVER, ConAddBanmaster, this, "", IConsole::CONSOLELEVEL_ADMIN); Console()->Register("banmasters", "", CFGFLAG_SERVER, ConBanmasters, this, "", IConsole::CONSOLELEVEL_ADMIN); Console()->Register("clear_banmasters", "", CFGFLAG_SERVER, ConClearBanmasters, this, "", IConsole::CONSOLELEVEL_ADMIN); +======= + Console()->Register("kick", "i?r", CFGFLAG_SERVER, ConKick, this, "Kick player with specified id for any reason"); + Console()->Register("ban", "s?ir", CFGFLAG_SERVER|CFGFLAG_STORE, ConBan, this, "Ban player with ip/id for x minutes for any reason"); + Console()->Register("unban", "s", CFGFLAG_SERVER|CFGFLAG_STORE, ConUnban, this, "Unban ip"); + Console()->Register("bans", "", CFGFLAG_SERVER|CFGFLAG_STORE, ConBans, this, "Show banlist"); + Console()->Register("status", "", CFGFLAG_SERVER, ConStatus, this, "List players"); + Console()->Register("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, "Shut down"); + + Console()->Register("record", "?s", CFGFLAG_SERVER|CFGFLAG_STORE, ConRecord, this, "Record to a file"); + Console()->Register("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, "Stop recording"); + + Console()->Register("reload", "", CFGFLAG_SERVER, ConMapReload, this, "Reload the map"); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe Console()->Chain("sv_name", ConchainSpecialInfoupdate, this); Console()->Chain("password", ConchainSpecialInfoupdate, this); Console()->Chain("sv_max_clients_per_ip", ConchainMaxclientsperipUpdate, this); +<<<<<<< HEAD Console()->Register("login", "?s", CFGFLAG_SERVER, ConLogin, this, "Allows you access to rcon if no password is given, or changes your level if a password is given", IConsole::CONSOLELEVEL_USER); Console()->Register("auth", "?s", CFGFLAG_SERVER, ConLogin, this, "Allows you access to rcon if no password is given, or changes your level if a password is given", IConsole::CONSOLELEVEL_USER); Console()->Register("cmdlist", "?i", CFGFLAG_SERVER, ConCmdList, this, "Shows you the commands available for your remote console access. Specify the level if you want to see other level's commands", IConsole::CONSOLELEVEL_USER); +======= + Console()->Chain("mod_command", ConchainModCommandUpdate, this); + Console()->Chain("console_output_level", ConchainConsoleOutputLevelUpdate, this); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 7092f6391..fd1ef257b 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -59,6 +59,15 @@ public: class IConsole *Console() { return m_pConsole; } class IStorage *Storage() { return m_pStorage; } + enum + { + AUTHED_NO=0, + AUTHED_MOD, + AUTHED_ADMIN, + + MAX_RCONCMD_SEND=16, + }; + class CClient { public: @@ -103,6 +112,8 @@ public: int m_Authed; int m_AuthTries; + const IConsole::CCommandInfo *m_pRconCmdToSend; + void Reset(); // DDRace @@ -116,6 +127,7 @@ public: CSnapshotBuilder m_SnapshotBuilder; CSnapIDPool m_IDPool; CNetServer m_NetServer; + CEcon m_Econ; IEngineMap *m_pMap; @@ -124,6 +136,8 @@ public: int m_RunServer; int m_MapReload; int m_RconClientID; + int m_RconAuthLevel; + int m_PrintCBIndex; int64 m_Lastheartbeat; //static NETADDR4 master_server; @@ -148,6 +162,8 @@ public: void Kick(int ClientID, const char *pReason); + void DemoRecorder_HandleAutoStart(); + //int Tick() int64 TickStartTime(int Tick); //int TickSpeed() @@ -175,6 +191,10 @@ public: void SendRconLine(int ClientID, const char *pLine); static void SendRconLineAuthed(const char *pLine, void *pUser); + void SendRconCmdAdd(const IConsole::CCommandInfo *pCommandInfo, int ClientID); + void SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int ClientID); + void UpdateClientRconCommands(); + void ProcessClientPacket(CNetChunk *pPacket); void SendServerInfo(NETADDR *pAddr, int Token); @@ -183,7 +203,6 @@ public: int BanAdd(NETADDR Addr, int Seconds, const char *pReason); int BanRemove(NETADDR Addr); - void PumpNetwork(); char *GetMapName(); @@ -203,6 +222,8 @@ public: static void ConMapReload(IConsole::IResult *pResult, void *pUser, int ClientID); static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); static void ConchainMaxclientsperipUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + static void ConchainModCommandUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + static void ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); void RegisterCommands(); diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index 3ca59f9cf..1a49eaf0e 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -24,7 +24,9 @@ public: int m_Country; int m_Score; bool m_Player; - } ; + + int m_FriendState; + }; int m_SortedIndex; int m_ServerIndex; @@ -32,6 +34,7 @@ public: NETADDR m_NetAddr; int m_QuickSearchHit; + int m_FriendState; int m_MaxClients; int m_NumClients; diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index fea9c4741..70aeff377 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -8,6 +8,7 @@ #include "././game/variables.h" +<<<<<<< HEAD MACRO_CONFIG_STR(PlayerName, player_name, 16, "nameless tee", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Name of the player", IConsole::CONSOLELEVEL_USER) MACRO_CONFIG_STR(PlayerClan, player_clan, 12, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Clan of the player", IConsole::CONSOLELEVEL_USER) MACRO_CONFIG_INT(PlayerCountry, player_country, -1, -1, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Country of the player", IConsole::CONSOLELEVEL_USER) @@ -193,4 +194,102 @@ MACRO_CONFIG_INT(SvSpamMuteDuration, sv_spam_mute_duration, 60, 0, 3600 , CFGFLA // 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", IConsole::CONSOLELEVEL_ADMIN) +======= +MACRO_CONFIG_STR(PlayerName, player_name, 16, "nameless tee", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Name of the player") +MACRO_CONFIG_STR(PlayerClan, player_clan, 12, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Clan of the player") +MACRO_CONFIG_INT(PlayerCountry, player_country, -1, -1, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Country of the player") +MACRO_CONFIG_STR(Password, password, 32, "", CFGFLAG_CLIENT|CFGFLAG_SERVER, "Password to the server") +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(ClEditor, cl_editor, 0, 0, 1, CFGFLAG_CLIENT, "") + +MACRO_CONFIG_INT(ClAutoDemoRecord, cl_auto_demo_record, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Automatically record demos") +MACRO_CONFIG_INT(ClAutoDemoMax, cl_auto_demo_max, 10, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Maximum number of automatically recorded demos (0 = no limit)") +MACRO_CONFIG_INT(ClAutoScreenshot, cl_auto_screenshot, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Automatically take game over screenshot") +MACRO_CONFIG_INT(ClAutoScreenshotMax, cl_auto_screenshot_max, 10, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Maximum number of automatically created screenshots (0 = no limit)") + +MACRO_CONFIG_INT(ClEventthread, cl_eventthread, 0, 0, 1, CFGFLAG_CLIENT, "Enables the usage of a thread to pump the events") + +MACRO_CONFIG_INT(InpGrab, inp_grab, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use forceful input grabbing method") + +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(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") +MACRO_CONFIG_INT(BrFilterCountryIndex, br_filter_country_index, -1, -1, 999, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Player country to filter by in the server browser") +MACRO_CONFIG_INT(BrFilterPw, br_filter_pw, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out password protected servers in browser") +MACRO_CONFIG_INT(BrFilterPing, br_filter_ping, 999, 0, 999, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Ping to filter by in the server browser") +MACRO_CONFIG_STR(BrFilterGametype, br_filter_gametype, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Game types to filter") +MACRO_CONFIG_INT(BrFilterGametypeStrict, br_filter_gametype_strict, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Strict gametype filter") +MACRO_CONFIG_STR(BrFilterServerAddress, br_filter_serveraddress, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Server address to filter") +MACRO_CONFIG_INT(BrFilterPure, br_filter_pure, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard servers in browser") +MACRO_CONFIG_INT(BrFilterPureMap, br_filter_pure_map, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard maps in browser") +MACRO_CONFIG_INT(BrFilterCompatversion, br_filter_compatversion, 1, 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(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, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound buffer size") +MACRO_CONFIG_INT(SndRate, snd_rate, 48000, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound mixing rate") +MACRO_CONFIG_INT(SndEnable, snd_enable, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound enable") +MACRO_CONFIG_INT(SndMusic, snd_enable_music, 1, 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(GfxFullscreen, gfx_fullscreen, 1, 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)") +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(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") +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, "") +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(InpMousesens, inp_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity") + +MACRO_CONFIG_STR(SvName, sv_name, 128, "unnamed server", CFGFLAG_SERVER, "Server name") +MACRO_CONFIG_STR(SvBindaddr, sv_bindaddr, 128, "", CFGFLAG_SERVER, "Address to bind the server to") +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, 8, 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)") +MACRO_CONFIG_STR(SvRconModPassword, sv_rcon_mod_password, 32, "", CFGFLAG_SERVER, "Remote console password for moderators (limited access)") +MACRO_CONFIG_INT(SvRconMaxTries, sv_rcon_max_tries, 3, 0, 100, CFGFLAG_SERVER, "Maximum number of tries for remote console authentication") +MACRO_CONFIG_INT(SvRconBantime, sv_rcon_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if remote console authentication fails. 0 makes it just use kick") +MACRO_CONFIG_INT(SvAutoDemoRecord, sv_auto_demo_record, 0, 0, 1, CFGFLAG_SERVER, "Automatically record demos") +MACRO_CONFIG_INT(SvAutoDemoMax, sv_auto_demo_max, 10, 0, 1000, CFGFLAG_SERVER, "Maximum number of automatically recorded demos (0 = no limit)") + +MACRO_CONFIG_STR(EcBindaddr, ec_bindaddr, 128, "localhost", CFGFLAG_SERVER, "Address to bind the external console to. Anything but 'localhost' is dangerous") +MACRO_CONFIG_INT(EcPort, ec_port, 0, 0, 0, CFGFLAG_SERVER, "Port to use for the external console") +MACRO_CONFIG_STR(EcPassword, ec_password, 32, "", CFGFLAG_SERVER, "External console password") +MACRO_CONFIG_INT(EcBantime, ec_bantime, 0, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if econ authentication fails. 0 just closes the connection") +MACRO_CONFIG_INT(EcAuthTimeout, ec_auth_timeout, 30, 1, 120, CFGFLAG_SERVER, "Time in seconds before the the econ authentification times out") +MACRO_CONFIG_INT(EcOutputLevel, ec_output_level, 1, 0, 2, CFGFLAG_SERVER, "Adjusts the amount of information in the external console") + +MACRO_CONFIG_INT(Debug, debug, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Debug mode") +MACRO_CONFIG_INT(DbgStress, dbg_stress, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress systems") +MACRO_CONFIG_INT(DbgStressNetwork, dbg_stress_network, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress network") +MACRO_CONFIG_INT(DbgPref, dbg_pref, 0, 0, 1, CFGFLAG_SERVER, "Performance outputs") +MACRO_CONFIG_INT(DbgGraphs, dbg_graphs, 0, 0, 1, CFGFLAG_CLIENT, "Performance graphs") +MACRO_CONFIG_INT(DbgHitch, dbg_hitch, 0, 0, 0, CFGFLAG_SERVER, "Hitch warnings") +MACRO_CONFIG_STR(DbgStressServer, dbg_stress_server, 32, "localhost", CFGFLAG_CLIENT, "Server to stress") +MACRO_CONFIG_INT(DbgResizable, dbg_resizable, 0, 0, 0, CFGFLAG_CLIENT, "Enables window resizing") +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe #endif diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp index 4cff5d59e..078f54f2a 100644 --- a/src/engine/shared/console.cpp +++ b/src/engine/shared/console.cpp @@ -1,11 +1,15 @@ /* (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 -#include "console.h" +#include + #include "config.h" +#include "console.h" #include "linereader.h" #include @@ -31,6 +35,7 @@ float CConsole::CResult::GetFloat(unsigned Index) return str_tofloat(m_apArgs[Index]); } +<<<<<<< HEAD void CConsole::CResult::Print(int Level, const char *pFrom, const char *pStr) { dbg_msg(pFrom ,"%s", pStr); @@ -40,6 +45,29 @@ void CConsole::CResult::Print(int Level, const char *pFrom, const char *pStr) str_format(aBuf, sizeof(aBuf), "[%s]: %s", pFrom, pStr); (*m_pfnPrintCallback)(aBuf, m_pPrintCallbackUserData); } +======= +const IConsole::CCommandInfo *CConsole::CCommand::NextCommandInfo(int AccessLevel, int FlagMask) const +{ + const CCommand *pInfo = m_pNext; + while(pInfo) + { + if(pInfo->m_Flags&FlagMask && pInfo->m_AccessLevel >= AccessLevel) + break; + pInfo = pInfo->m_pNext; + } + return pInfo; +} + +const IConsole::CCommandInfo *CConsole::FirstCommandInfo(int AccessLevel, int FlagMask) const +{ + for(const CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext) + { + if(pCommand->m_Flags&FlagMask && pCommand->GetAccessLevel() >= AccessLevel) + return pCommand; + } + + return 0; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } // the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces @@ -184,21 +212,46 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat) return Error; } -void CConsole::RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData) +int CConsole::RegisterPrintCallback(int OutputLevel, FPrintCallback pfnPrintCallback, void *pUserData) { + if(m_NumPrintCB == MAX_PRINT_CB) + return -1; + + m_aPrintCB[m_NumPrintCB].m_OutputLevel = clamp(OutputLevel, (int)(OUTPUT_LEVEL_STANDARD), (int)(OUTPUT_LEVEL_DEBUG)); + m_aPrintCB[m_NumPrintCB].m_pfnPrintCallback = pfnPrintCallback; + m_aPrintCB[m_NumPrintCB].m_pPrintCallbackUserdata = pUserData; + return m_NumPrintCB++; +} + +void CConsole::SetPrintOutputLevel(int Index, int OutputLevel) +{ +<<<<<<< HEAD m_pfnPrintCallback = pfnPrintCallback; m_pPrintCallbackUserData = pUserData; +======= + if(Index >= 0 && Index < MAX_PRINT_CB) + m_aPrintCB[Index].m_OutputLevel = clamp(OutputLevel, (int)(OUTPUT_LEVEL_STANDARD), (int)(OUTPUT_LEVEL_DEBUG)); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } void CConsole::Print(int Level, const char *pFrom, const char *pStr) { dbg_msg(pFrom ,"%s", pStr); - if(Level <= g_Config.m_ConsoleOutputLevel && m_pfnPrintCallback) + for(int i = 0; i < m_NumPrintCB; ++i) { +<<<<<<< HEAD char aBuf[1024]; str_format(aBuf, sizeof(aBuf), "[%s]: %s", pFrom, pStr); (*m_pfnPrintCallback)(aBuf, m_pPrintCallbackUserData); +======= + if(Level <= m_aPrintCB[i].m_OutputLevel && m_aPrintCB[i].m_pfnPrintCallback) + { + char aBuf[1024]; + str_format(aBuf, sizeof(aBuf), "[%s]: %s", pFrom, pStr); + m_aPrintCB[i].m_pfnPrintCallback(aBuf, m_aPrintCB[i].m_pPrintCallbackUserdata); + } +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } } @@ -296,33 +349,48 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientID, in if(ParseStart(&Result, pStr, (pEnd-pStr) + 1) != 0) return; + if(!*Result.m_pCommand) + return; + CCommand *pCommand = FindCommand(Result.m_pCommand, m_FlagMask); if(pCommand) { - int IsStrokeCommand = 0; - if(Result.m_pCommand[0] == '+') + if(pCommand->GetAccessLevel() >= m_AccessLevel) { - // insert the stroke direction token - Result.AddArgument(m_paStrokeStr[Stroke]); - IsStrokeCommand = 1; - } - - if(Stroke || IsStrokeCommand) - { - if(ParseArgs(&Result, pCommand->m_pParams)) + int IsStrokeCommand = 0; + if(Result.m_pCommand[0] == '+') { +<<<<<<< HEAD char aBuf[256]; str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams); Result.Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf); +======= + // insert the stroke direction token + Result.AddArgument(m_paStrokeStr[Stroke]); + IsStrokeCommand = 1; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } - else if(m_StoreCommands && pCommand->m_Flags&CFGFLAG_STORE) + + if(Stroke || IsStrokeCommand) { - m_ExecutionQueue.AddEntry(); - m_ExecutionQueue.m_pLast->m_pfnCommandCallback = pCommand->m_pfnCallback; - m_ExecutionQueue.m_pLast->m_pCommandUserData = pCommand->m_pUserData; - m_ExecutionQueue.m_pLast->m_Result = Result; + if(ParseArgs(&Result, pCommand->m_pParams)) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams); + Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf); + } + else if(m_StoreCommands && pCommand->m_Flags&CFGFLAG_STORE) + { + m_ExecutionQueue.AddEntry(); + m_ExecutionQueue.m_pLast->m_pfnCommandCallback = pCommand->m_pfnCallback; + m_ExecutionQueue.m_pLast->m_pCommandUserData = pCommand->m_pUserData; + m_ExecutionQueue.m_pLast->m_Result = Result; + } + else + pCommand->m_pfnCallback(&Result, pCommand->m_pUserData); } +<<<<<<< HEAD else { if(Result.GetVictim() == CResult::VICTIM_ME) @@ -386,6 +454,14 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientID, in pCommand->m_pfnCallback(&Result, pCommand->m_pUserData, ClientID); } } +======= + } + else if(Stroke) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "Access for command %s denied.", Result.m_pCommand); + Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } } else if(Stroke) @@ -399,12 +475,11 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientID, in } } -void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser) +void CConsole::PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser) { - CCommand *pCommand; - for(pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext) + for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext) { - if(pCommand->m_Flags&FlagMask) + if(pCommand->m_Flags&FlagMask && pCommand->m_Temp == Temp) { if(str_find_nocase(pCommand->m_pName, pStr)) pfnCallback(pCommand->m_pName, pUser); @@ -414,8 +489,7 @@ void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallbac CConsole::CCommand *CConsole::FindCommand(const char *pName, int FlagMask) { - CCommand *pCommand; - for (pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext) + for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext) { if(pCommand->m_Flags&FlagMask) { @@ -502,6 +576,62 @@ void CConsole::Con_Exec(IResult *pResult, void *pUserData, int ClientID) ((CConsole *)pUserData)->ExecuteFile(pResult->GetString(0), ClientID, 3, pResult); } +void CConsole::ConModCommandAccess(IResult *pResult, void *pUser) +{ + CConsole* pConsole = static_cast(pUser); + char aBuf[128]; + CCommand *pCommand = pConsole->FindCommand(pResult->GetString(0), CFGFLAG_SERVER); + if(pCommand) + { + if(pResult->NumArguments() == 2) + { + pCommand->SetAccessLevel(pResult->GetInteger(1)); + str_format(aBuf, sizeof(aBuf), "moderator access for '%s' is now %s", pResult->GetString(0), pCommand->GetAccessLevel() ? "enabled" : "disabled"); + } + else + str_format(aBuf, sizeof(aBuf), "moderator access for '%s' is %s", pResult->GetString(0), pCommand->GetAccessLevel() ? "enabled" : "disabled"); + } + else + str_format(aBuf, sizeof(aBuf), "No such command: '%s'.", pResult->GetString(0)); + + pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf); +} + +void CConsole::ConModCommandStatus(IResult *pResult, void *pUser) +{ + CConsole* pConsole = static_cast(pUser); + char aBuf[240]; + mem_zero(aBuf, sizeof(aBuf)); + int Used = 0; + + for(CCommand *pCommand = pConsole->m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext) + { + if(pCommand->m_Flags&pConsole->m_FlagMask && pCommand->GetAccessLevel() == ACCESS_LEVEL_MOD) + { + int Length = str_length(pCommand->m_pName); + if(Used + Length + 2 < (int)(sizeof(aBuf))) + { + if(Used > 0) + { + Used += 2; + str_append(aBuf, ", ", sizeof(aBuf)); + } + str_append(aBuf, pCommand->m_pName, sizeof(aBuf)); + Used += Length; + } + else + { + pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf); + mem_zero(aBuf, sizeof(aBuf)); + str_copy(aBuf, pCommand->m_pName, sizeof(aBuf)); + Used = Length; + } + } + } + if(Used > 0) + pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf); +} + struct CIntVariableData { IConsole *m_pConsole; @@ -588,12 +718,16 @@ static void StrVariableCommand(IConsole::IResult *pResult, void *pUserData, int CConsole::CConsole(int FlagMask) { m_FlagMask = FlagMask; + m_AccessLevel = ACCESS_LEVEL_ADMIN; + m_pRecycleList = 0; + m_TempCommands.Reset(); m_StoreCommands = true; m_paStrokeStr[0] = "0"; m_paStrokeStr[1] = "1"; m_ExecutionQueue.Reset(); m_pFirstCommand = 0; m_pFirstExec = 0; +<<<<<<< HEAD for (int i = 0; i < 5; ++i) { m_aCommandCount[i] = 0; @@ -606,6 +740,10 @@ CConsole::CConsole(int FlagMask) m_pfnCompareClientsCallback = 0; m_pClientOnlineUserdata = 0; m_pCompareClientsUserdata = 0; +======= + mem_zero(m_aPrintCB, sizeof(m_aPrintCB)); + m_NumPrintCB = 0; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe m_pStorage = 0; @@ -613,6 +751,9 @@ CConsole::CConsole(int FlagMask) Register("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Echo, this, "Echo the text", IConsole::CONSOLELEVEL_ADMIN); Register("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Exec, this, "Execute the specified file", IConsole::CONSOLELEVEL_ADMIN); + Register("mod_command", "s?i", CFGFLAG_SERVER, ConModCommandAccess, this, "Specify command accessibility for moderators"); + Register("mod_status", "", CFGFLAG_SERVER, ConModCommandStatus, this, "List all commands which are accessible for moderators"); + // TODO: this should disappear #define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Flags,Desc,Level) \ { \ @@ -658,21 +799,139 @@ void CConsole::ParseArguments(int NumArgs, const char **ppArguments) } } +void CConsole::AddCommandSorted(CCommand *pCommand) +{ + if(!m_pFirstCommand || str_comp(pCommand->m_pName, m_pFirstCommand->m_pName) < 0) + { + if(m_pFirstCommand && m_pFirstCommand->m_pNext) + pCommand->m_pNext = m_pFirstCommand; + else + pCommand->m_pNext = 0; + m_pFirstCommand = pCommand; + } + else + { + for(CCommand *p = m_pFirstCommand; p; p = p->m_pNext) + { + if(!p->m_pNext || str_comp(pCommand->m_pName, p->m_pNext->m_pName) < 0) + { + pCommand->m_pNext = p->m_pNext; + p->m_pNext = pCommand; + break; + } + } + } +} + void CConsole::Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp, const int Level) { - CCommand *pCommand = (CCommand *)mem_alloc(sizeof(CCommand), sizeof(void*)); + CCommand *pCommand = new(mem_alloc(sizeof(CCommand), sizeof(void*))) CCommand; pCommand->m_pfnCallback = pfnFunc; pCommand->m_pUserData = pUser; - pCommand->m_pHelp = pHelp; + pCommand->m_pName = pName; + pCommand->m_pHelp = pHelp; pCommand->m_pParams = pParams; + pCommand->m_Flags = Flags; +<<<<<<< HEAD pCommand->m_Level = Level; pCommand->m_pNext = m_pFirstCommand; m_pFirstCommand = pCommand; m_aCommandCount[pCommand->m_Level]++; +======= + pCommand->m_Temp = false; + + AddCommandSorted(pCommand); +} + +void CConsole::RegisterTemp(const char *pName, const char *pParams, int Flags, const char *pHelp) +{ + CCommand *pCommand; + if(m_pRecycleList) + { + pCommand = m_pRecycleList; + str_copy(const_cast(pCommand->m_pName), pName, TEMPCMD_NAME_LENGTH); + str_copy(const_cast(pCommand->m_pHelp), pHelp, TEMPCMD_HELP_LENGTH); + str_copy(const_cast(pCommand->m_pParams), pParams, TEMPCMD_PARAMS_LENGTH); + + m_pRecycleList = m_pRecycleList->m_pNext; + } + else + { + pCommand = new(m_TempCommands.Allocate(sizeof(CCommand))) CCommand; + char *pMem = static_cast(m_TempCommands.Allocate(TEMPCMD_NAME_LENGTH)); + str_copy(pMem, pName, TEMPCMD_NAME_LENGTH); + pCommand->m_pName = pMem; + pMem = static_cast(m_TempCommands.Allocate(TEMPCMD_HELP_LENGTH)); + str_copy(pMem, pHelp, TEMPCMD_HELP_LENGTH); + pCommand->m_pHelp = pMem; + pMem = static_cast(m_TempCommands.Allocate(TEMPCMD_PARAMS_LENGTH)); + str_copy(pMem, pParams, TEMPCMD_PARAMS_LENGTH); + pCommand->m_pParams = pMem; + } + + pCommand->m_pfnCallback = 0; + pCommand->m_pUserData = 0; + pCommand->m_Flags = Flags; + pCommand->m_Temp = true; + + AddCommandSorted(pCommand); +} + +void CConsole::DeregisterTemp(const char *pName) +{ + if(!m_pFirstCommand) + return; + + CCommand *pRemoved = 0; + + // remove temp entry from command list + if(m_pFirstCommand->m_Temp && str_comp(m_pFirstCommand->m_pName, pName) == 0) + { + pRemoved = m_pFirstCommand; + m_pFirstCommand = m_pFirstCommand->m_pNext; + } + else + { + for(CCommand *pCommand = m_pFirstCommand; pCommand->m_pNext; pCommand = pCommand->m_pNext) + if(pCommand->m_pNext->m_Temp && str_comp(pCommand->m_pNext->m_pName, pName) == 0) + { + pRemoved = pCommand->m_pNext; + pCommand->m_pNext = pCommand->m_pNext->m_pNext; + break; + } + } + + // add to recycle list + if(pRemoved) + { + pRemoved->m_pNext = m_pRecycleList; + m_pRecycleList = pRemoved; + } +} + +void CConsole::DeregisterTempAll() +{ + // set non temp as first one + for(; m_pFirstCommand && m_pFirstCommand->m_Temp; m_pFirstCommand = m_pFirstCommand->m_pNext); + + // remove temp entries from command list + for(CCommand *pCommand = m_pFirstCommand; pCommand && pCommand->m_pNext; pCommand = pCommand->m_pNext) + { + CCommand *pNext = pCommand->m_pNext; + if(pNext->m_Temp) + { + for(; pNext && pNext->m_Temp; pNext = pNext->m_pNext); + pCommand->m_pNext = pNext; + } + } + + m_TempCommands.Reset(); + m_pRecycleList = 0; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } void CConsole::Con_Chain(IResult *pResult, void *pUserData, int ClientID) @@ -716,9 +975,18 @@ void CConsole::StoreCommands(bool Store, int ClientID) } -IConsole::CCommandInfo *CConsole::GetCommandInfo(const char *pName, int FlagMask) +const IConsole::CCommandInfo *CConsole::GetCommandInfo(const char *pName, int FlagMask, bool Temp) { - return FindCommand(pName, FlagMask); + for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext) + { + if(pCommand->m_Flags&FlagMask && pCommand->m_Temp == Temp) + { + if(str_comp_nocase(pCommand->m_pName, pName) == 0) + return pCommand; + } + } + + return 0; } diff --git a/src/engine/shared/console.h b/src/engine/shared/console.h index e6630c8a2..de0c1c1f5 100644 --- a/src/engine/shared/console.h +++ b/src/engine/shared/console.h @@ -15,8 +15,13 @@ class CConsole : public IConsole public: CCommand *m_pNext; int m_Flags; + bool m_Temp; FCommandCallback m_pfnCallback; void *m_pUserData; + + virtual const CCommandInfo *NextCommandInfo(int AccessLevel, int FlagMask) const; + + void SetAccessLevel(int AccessLevel) { m_AccessLevel = clamp(AccessLevel, (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_MOD)); } }; @@ -43,16 +48,38 @@ class CConsole : public IConsole CExecFile *m_pFirstExec; class IStorage *m_pStorage; + int m_AccessLevel; + CCommand *m_pRecycleList; + CHeap m_TempCommands; + +<<<<<<< HEAD static void Con_Chain(IResult *pResult, void *pUserData, int ClientID); static void Con_Echo(IResult *pResult, void *pUserData, int ClientID); static void Con_Exec(IResult *pResult, void *pUserData, int ClientID); +======= + static void Con_Chain(IResult *pResult, void *pUserData); + static void Con_Echo(IResult *pResult, void *pUserData); + static void Con_Exec(IResult *pResult, void *pUserData); + static void ConModCommandAccess(IResult *pResult, void *pUser); + static void ConModCommandStatus(IConsole::IResult *pResult, void *pUser); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe //void ExecuteFileRecurse(const char *pFilename); //void ExecuteLineStroked(int Stroke, const char *pStr); +<<<<<<< HEAD FPrintCallback m_pfnPrintCallback; void *m_pPrintCallbackUserData; +======= + struct + { + int m_OutputLevel; + FPrintCallback m_pfnPrintCallback; + void *m_pPrintCallbackUserdata; + } m_aPrintCB[MAX_PRINT_CB]; + int m_NumPrintCB; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe enum { @@ -157,16 +184,25 @@ class CConsole : public IConsole } } m_ExecutionQueue; + void AddCommandSorted(CCommand *pCommand); CCommand *FindCommand(const char *pName, int FlagMask); public: CConsole(int FlagMask); - virtual CCommandInfo *GetCommandInfo(const char *pName, int FlagMask); - virtual void PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser) ; + virtual const CCommandInfo *FirstCommandInfo(int AccessLevel, int Flagmask) const; + virtual const CCommandInfo *GetCommandInfo(const char *pName, int FlagMask, bool Temp); + virtual void PossibleCommands(const char *pStr, int FlagMask, bool Temp, FPossibleCallback pfnCallback, void *pUser); virtual void ParseArguments(int NumArgs, const char **ppArguments); +<<<<<<< HEAD virtual void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp, const int Level); +======= + virtual void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp); + virtual void RegisterTemp(const char *pName, const char *pParams, int Flags, const char *pHelp); + virtual void DeregisterTemp(const char *pName); + virtual void DeregisterTempAll(); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser); virtual void StoreCommands(bool Store, int ClientID); @@ -174,9 +210,11 @@ public: //virtual void ExecuteLine(const char *pStr); //virtual void ExecuteFile(const char *pFilename); - virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData); + virtual int RegisterPrintCallback(int OutputLevel, FPrintCallback pfnPrintCallback, void *pUserData); + virtual void SetPrintOutputLevel(int Index, int OutputLevel); virtual void Print(int Level, const char *pFrom, const char *pStr); +<<<<<<< HEAD // DDRace virtual void List(IConsole::IResult *pResult, int Level, int Flags); @@ -218,6 +256,9 @@ private: FClientOnlineCallback m_pfnClientOnlineCallback; void *m_pClientOnlineUserdata; int m_aCommandCount[5]; +======= + void SetAccessLevel(int AccessLevel) { m_AccessLevel = clamp(AccessLevel, (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_MOD)); } +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe }; #endif diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp index 004100384..e22156352 100644 --- a/src/engine/shared/datafile.cpp +++ b/src/engine/shared/datafile.cpp @@ -422,6 +422,25 @@ unsigned CDataFileReader::Crc() return m_pDataFile->m_Crc; } + +CDataFileWriter::CDataFileWriter() +{ + m_File = 0; + m_pItemTypes = static_cast(mem_alloc(sizeof(CItemTypeInfo) * MAX_ITEM_TYPES, 1)); + m_pItems = static_cast(mem_alloc(sizeof(CItemInfo) * MAX_ITEMS, 1)); + m_pDatas = static_cast(mem_alloc(sizeof(CDataInfo) * MAX_DATAS, 1)); +} + +CDataFileWriter::~CDataFileWriter() +{ + mem_free(m_pItemTypes); + m_pItemTypes = 0; + mem_free(m_pItems); + m_pItems = 0; + mem_free(m_pDatas); + m_pDatas = 0; +} + bool CDataFileWriter::Open(class IStorage *pStorage, const char *pFilename) { dbg_assert(!m_File, "a file already exists"); @@ -432,12 +451,12 @@ bool CDataFileWriter::Open(class IStorage *pStorage, const char *pFilename) m_NumItems = 0; m_NumDatas = 0; m_NumItemTypes = 0; - mem_zero(&m_aItemTypes, sizeof(m_aItemTypes)); + mem_zero(m_pItemTypes, sizeof(CItemTypeInfo) * MAX_ITEM_TYPES); - for(int i = 0; i < 0xffff; i++) + for(int i = 0; i < MAX_ITEM_TYPES; i++) { - m_aItemTypes[i].m_First = -1; - m_aItemTypes[i].m_Last = -1; + m_pItemTypes[i].m_First = -1; + m_pItemTypes[i].m_Last = -1; } return true; @@ -451,29 +470,29 @@ int CDataFileWriter::AddItem(int Type, int ID, int Size, void *pData) dbg_assert(m_NumItems < 1024, "too many items"); dbg_assert(Size%sizeof(int) == 0, "incorrect boundary"); - m_aItems[m_NumItems].m_Type = Type; - m_aItems[m_NumItems].m_ID = ID; - m_aItems[m_NumItems].m_Size = Size; + m_pItems[m_NumItems].m_Type = Type; + m_pItems[m_NumItems].m_ID = ID; + m_pItems[m_NumItems].m_Size = Size; // copy data - m_aItems[m_NumItems].m_pData = mem_alloc(Size, 1); - mem_copy(m_aItems[m_NumItems].m_pData, pData, Size); + m_pItems[m_NumItems].m_pData = mem_alloc(Size, 1); + mem_copy(m_pItems[m_NumItems].m_pData, pData, Size); - if(!m_aItemTypes[Type].m_Num) // count item types + if(!m_pItemTypes[Type].m_Num) // count item types m_NumItemTypes++; // link - m_aItems[m_NumItems].m_Prev = m_aItemTypes[Type].m_Last; - m_aItems[m_NumItems].m_Next = -1; + m_pItems[m_NumItems].m_Prev = m_pItemTypes[Type].m_Last; + m_pItems[m_NumItems].m_Next = -1; - if(m_aItemTypes[Type].m_Last != -1) - m_aItems[m_aItemTypes[Type].m_Last].m_Next = m_NumItems; - m_aItemTypes[Type].m_Last = m_NumItems; + if(m_pItemTypes[Type].m_Last != -1) + m_pItems[m_pItemTypes[Type].m_Last].m_Next = m_NumItems; + m_pItemTypes[Type].m_Last = m_NumItems; - if(m_aItemTypes[Type].m_First == -1) - m_aItemTypes[Type].m_First = m_NumItems; + if(m_pItemTypes[Type].m_First == -1) + m_pItemTypes[Type].m_First = m_NumItems; - m_aItemTypes[Type].m_Num++; + m_pItemTypes[Type].m_Num++; m_NumItems++; return m_NumItems-1; @@ -485,7 +504,7 @@ int CDataFileWriter::AddData(int Size, void *pData) dbg_assert(m_NumDatas < 1024, "too much data"); - CDataInfo *pInfo = &m_aDatas[m_NumDatas]; + CDataInfo *pInfo = &m_pDatas[m_NumDatas]; unsigned long s = compressBound(Size); void *pCompData = mem_alloc(s, 1); // temporary buffer that we use during compression @@ -540,13 +559,13 @@ int CDataFileWriter::Finish() for(int i = 0; i < m_NumItems; i++) { if(DEBUG) - dbg_msg("datafile", "item=%d size=%d (%d)", i, m_aItems[i].m_Size, m_aItems[i].m_Size+sizeof(CDatafileItem)); - ItemSize += m_aItems[i].m_Size + sizeof(CDatafileItem); + dbg_msg("datafile", "item=%d size=%d (%d)", i, m_pItems[i].m_Size, m_pItems[i].m_Size+sizeof(CDatafileItem)); + ItemSize += m_pItems[i].m_Size + sizeof(CDatafileItem); } for(int i = 0; i < m_NumDatas; i++) - DataSize += m_aDatas[i].m_CompressedSize; + DataSize += m_pDatas[i].m_CompressedSize; // calculate the complete size TypesSize = m_NumItemTypes*sizeof(CDatafileItemType); @@ -587,30 +606,30 @@ int CDataFileWriter::Finish() // write types for(int i = 0, Count = 0; i < 0xffff; i++) { - if(m_aItemTypes[i].m_Num) + if(m_pItemTypes[i].m_Num) { // write info CDatafileItemType Info; Info.m_Type = i; Info.m_Start = Count; - Info.m_Num = m_aItemTypes[i].m_Num; + Info.m_Num = m_pItemTypes[i].m_Num; if(DEBUG) dbg_msg("datafile", "writing type=%x start=%d num=%d", Info.m_Type, Info.m_Start, Info.m_Num); #if defined(CONF_ARCH_ENDIAN_BIG) swap_endian(&Info, sizeof(int), sizeof(CDatafileItemType)/sizeof(int)); #endif io_write(m_File, &Info, sizeof(Info)); - Count += m_aItemTypes[i].m_Num; + Count += m_pItemTypes[i].m_Num; } } // write item offsets for(int i = 0, Offset = 0; i < 0xffff; i++) { - if(m_aItemTypes[i].m_Num) + if(m_pItemTypes[i].m_Num) { - // write all m_aItems in of this type - int k = m_aItemTypes[i].m_First; + // write all m_pItems in of this type + int k = m_pItemTypes[i].m_First; while(k != -1) { if(DEBUG) @@ -620,10 +639,10 @@ int CDataFileWriter::Finish() swap_endian(&Temp, sizeof(int), sizeof(Temp)/sizeof(int)); #endif io_write(m_File, &Temp, sizeof(Temp)); - Offset += m_aItems[k].m_Size + sizeof(CDatafileItem); + Offset += m_pItems[k].m_Size + sizeof(CDatafileItem); // next - k = m_aItems[k].m_Next; + k = m_pItems[k].m_Next; } } } @@ -638,45 +657,45 @@ int CDataFileWriter::Finish() swap_endian(&Temp, sizeof(int), sizeof(Temp)/sizeof(int)); #endif io_write(m_File, &Temp, sizeof(Temp)); - Offset += m_aDatas[i].m_CompressedSize; + Offset += m_pDatas[i].m_CompressedSize; } // write data uncompressed sizes for(int i = 0; i < m_NumDatas; i++) { if(DEBUG) - dbg_msg("datafile", "writing data uncompressed size num=%d size=%d", i, m_aDatas[i].m_UncompressedSize); - int UncompressedSize = m_aDatas[i].m_UncompressedSize; + dbg_msg("datafile", "writing data uncompressed size num=%d size=%d", i, m_pDatas[i].m_UncompressedSize); + int UncompressedSize = m_pDatas[i].m_UncompressedSize; #if defined(CONF_ARCH_ENDIAN_BIG) swap_endian(&UncompressedSize, sizeof(int), sizeof(UncompressedSize)/sizeof(int)); #endif io_write(m_File, &UncompressedSize, sizeof(UncompressedSize)); } - // write m_aItems + // write m_pItems for(int i = 0; i < 0xffff; i++) { - if(m_aItemTypes[i].m_Num) + if(m_pItemTypes[i].m_Num) { - // write all m_aItems in of this type - int k = m_aItemTypes[i].m_First; + // write all m_pItems in of this type + int k = m_pItemTypes[i].m_First; while(k != -1) { CDatafileItem Item; - Item.m_TypeAndID = (i<<16)|m_aItems[k].m_ID; - Item.m_Size = m_aItems[k].m_Size; + Item.m_TypeAndID = (i<<16)|m_pItems[k].m_ID; + Item.m_Size = m_pItems[k].m_Size; if(DEBUG) - dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, m_aItems[k].m_ID, m_aItems[k].m_Size); + dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, m_pItems[k].m_ID, m_pItems[k].m_Size); #if defined(CONF_ARCH_ENDIAN_BIG) swap_endian(&Item, sizeof(int), sizeof(Item)/sizeof(int)); - swap_endian(m_aItems[k].m_pData, sizeof(int), m_aItems[k].m_Size/sizeof(int)); + swap_endian(m_pItems[k].m_pData, sizeof(int), m_pItems[k].m_Size/sizeof(int)); #endif io_write(m_File, &Item, sizeof(Item)); - io_write(m_File, m_aItems[k].m_pData, m_aItems[k].m_Size); + io_write(m_File, m_pItems[k].m_pData, m_pItems[k].m_Size); // next - k = m_aItems[k].m_Next; + k = m_pItems[k].m_Next; } } } @@ -685,15 +704,15 @@ int CDataFileWriter::Finish() for(int i = 0; i < m_NumDatas; i++) { if(DEBUG) - dbg_msg("datafile", "writing data id=%d size=%d", i, m_aDatas[i].m_CompressedSize); - io_write(m_File, m_aDatas[i].m_pCompressedData, m_aDatas[i].m_CompressedSize); + dbg_msg("datafile", "writing data id=%d size=%d", i, m_pDatas[i].m_CompressedSize); + io_write(m_File, m_pDatas[i].m_pCompressedData, m_pDatas[i].m_CompressedSize); } // free data for(int i = 0; i < m_NumItems; i++) - mem_free(m_aItems[i].m_pData); + mem_free(m_pItems[i].m_pData); for(int i = 0; i < m_NumDatas; ++i) - mem_free(m_aDatas[i].m_pCompressedData); + mem_free(m_pDatas[i].m_pCompressedData); io_close(m_File); m_File = 0; diff --git a/src/engine/shared/datafile.h b/src/engine/shared/datafile.h index 9f27f9684..cafce20e9 100644 --- a/src/engine/shared/datafile.h +++ b/src/engine/shared/datafile.h @@ -61,16 +61,24 @@ class CDataFileWriter int m_Last; }; + enum + { + MAX_ITEM_TYPES=0xffff, + MAX_ITEMS=1024, + MAX_DATAS=1024, + }; + IOHANDLE m_File; int m_NumItems; int m_NumDatas; int m_NumItemTypes; - CItemTypeInfo m_aItemTypes[0xffff]; - CItemInfo m_aItems[1024]; - CDataInfo m_aDatas[1024]; + CItemTypeInfo *m_pItemTypes; + CItemInfo *m_pItems; + CDataInfo *m_pDatas; public: - CDataFileWriter() : m_File(0) {} + CDataFileWriter(); + ~CDataFileWriter(); bool Open(class IStorage *pStorage, const char *Filename); int AddData(int Size, void *pData); int AddDataSwapped(int Size, void *pData); diff --git a/src/engine/shared/demo.cpp b/src/engine/shared/demo.cpp index e48c2f1e4..51dd51d9a 100644 --- a/src/engine/shared/demo.cpp +++ b/src/engine/shared/demo.cpp @@ -1,13 +1,16 @@ /* (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 "compression.h" #include "demo.h" #include "memheap.h" -#include "snapshot.h" -#include "compression.h" #include "network.h" +#include "snapshot.h" static const unsigned char gs_aHeaderMarker[7] = {'T', 'W', 'D', 'E', 'M', 'O', 0}; static const unsigned char gs_ActVersion = 3; @@ -760,16 +763,21 @@ int CDemoPlayer::Stop() return 0; } -char *CDemoPlayer::GetDemoName() +void CDemoPlayer::GetDemoName(char *pBuffer, int BufferSize) const { - // get the name of the demo without its path - char *pDemoShortName = &m_aFilename[0]; - for(int i = 0; i < str_length(m_aFilename)-1; i++) + const char *pFileName = m_aFilename; + const char *pExtractedName = pFileName; + const char *pEnd = 0; + for(; *pFileName; ++pFileName) { - if(m_aFilename[i] == '/' || m_aFilename[i] == '\\') - pDemoShortName = &m_aFilename[i+1]; + if(*pFileName == '/' || *pFileName == '\\') + pExtractedName = pFileName+1; + else if(*pFileName == '.') + pEnd = pFileName; } - return pDemoShortName; + + int Length = pEnd > pExtractedName ? min(BufferSize, (int)(pEnd-pExtractedName+1)) : BufferSize; + str_copy(pBuffer, pExtractedName, Length); } bool CDemoPlayer::GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader) const diff --git a/src/engine/shared/demo.h b/src/engine/shared/demo.h index f1897f21e..f4ac5685a 100644 --- a/src/engine/shared/demo.h +++ b/src/engine/shared/demo.h @@ -110,7 +110,7 @@ public: void SetSpeed(float Speed); int SetPos(float Precent); const CInfo *BaseInfo() const { return &m_Info.m_Info; } - char *GetDemoName(); + void GetDemoName(char *pBuffer, int BufferSize) const; bool GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader) const; int GetDemoType() const; diff --git a/src/engine/shared/econ.cpp b/src/engine/shared/econ.cpp new file mode 100644 index 000000000..617cdbd61 --- /dev/null +++ b/src/engine/shared/econ.cpp @@ -0,0 +1,173 @@ +#include +#include + +#include "econ.h" + +int CEcon::NewClientCallback(int ClientID, void *pUser) +{ + CEcon *pThis = (CEcon *)pUser; + + NETADDR Addr = pThis->m_NetConsole.ClientAddr(ClientID); + char aAddrStr[NETADDR_MAXSTRSIZE]; + net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr)); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "client accepted. cid=%d addr=%s'", ClientID, aAddrStr); + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "econ", aBuf); + + pThis->m_aClients[ClientID].m_State = CClient::STATE_CONNECTED; + pThis->m_aClients[ClientID].m_TimeConnected = time_get(); + pThis->m_aClients[ClientID].m_AuthTries = 0; + + pThis->m_NetConsole.Send(ClientID, "Enter password:"); + return 0; +} + +int CEcon::DelClientCallback(int ClientID, const char *pReason, void *pUser) +{ + CEcon *pThis = (CEcon *)pUser; + + NETADDR Addr = pThis->m_NetConsole.ClientAddr(ClientID); + char aAddrStr[NETADDR_MAXSTRSIZE]; + net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr)); + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "client dropped. cid=%d addr=%s reason='%s'", ClientID, aAddrStr, pReason); + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "econ", aBuf); + + pThis->m_aClients[ClientID].m_State = CClient::STATE_EMPTY; + return 0; +} + +void CEcon::SendLineCB(const char *pLine, void *pUserData) +{ + static_cast(pUserData)->Send(-1, pLine); +} + +void CEcon::ConchainEconOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments() == 1) + { + CEcon *pThis = static_cast(pUserData); + pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0)); + } +} + +void CEcon::Init(IConsole *pConsole) +{ + m_pConsole = pConsole; + + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + m_aClients[i].m_State = CClient::STATE_EMPTY; + + m_Ready = false; + + if(g_Config.m_EcPort == 0 || g_Config.m_EcPassword[0] == 0) + return; + + NETADDR BindAddr; + if(g_Config.m_EcBindaddr[0] && net_host_lookup(g_Config.m_EcBindaddr, &BindAddr, NETTYPE_ALL) == 0) + BindAddr.port = g_Config.m_EcPort; + else + { + mem_zero(&BindAddr, sizeof(BindAddr)); + BindAddr.type = NETTYPE_ALL; + BindAddr.port = g_Config.m_EcPort; + } + + if(m_NetConsole.Open(BindAddr, 0)) + { + m_NetConsole.SetCallbacks(NewClientCallback, DelClientCallback, this); + m_Ready = true; + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "bound to %s:%d", g_Config.m_EcBindaddr, g_Config.m_EcPort); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD,"econ", aBuf); + + Console()->Chain("ec_output_level", ConchainEconOutputLevelUpdate, this); + m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_EcOutputLevel, SendLineCB, this); + } + else + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD,"econ", "couldn't open socket. port might already be in use"); +} + +void CEcon::Update() +{ + if(!m_Ready) + return; + + m_NetConsole.Update(); + + char aBuf[NET_MAX_PACKETSIZE]; + int ClientID; + + while(m_NetConsole.Recv(aBuf, (int)(sizeof(aBuf))-1, &ClientID)) + { + dbg_assert(m_aClients[ClientID].m_State != CClient::STATE_EMPTY, "got message from empty slot"); + if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTED) + { + if(str_comp(aBuf, g_Config.m_EcPassword) == 0) + { + m_aClients[ClientID].m_State = CClient::STATE_AUTHED; + m_NetConsole.Send(ClientID, "Authentication successful. External console access granted."); + + str_format(aBuf, sizeof(aBuf), "cid=%d authed", ClientID); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "econ", aBuf); + } + else + { + m_aClients[ClientID].m_AuthTries++; + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Wrong password %d/%d.", m_aClients[ClientID].m_AuthTries, MAX_AUTH_TRIES); + m_NetConsole.Send(ClientID, aBuf); + if(m_aClients[ClientID].m_AuthTries >= MAX_AUTH_TRIES) + { + if(!g_Config.m_EcBantime) + m_NetConsole.Drop(ClientID, "Too many authentication tries"); + else + { + NETADDR Addr = m_NetConsole.ClientAddr(ClientID); + m_NetConsole.AddBan(Addr, g_Config.m_EcBantime*60); + } + } + } + } + else if(m_aClients[ClientID].m_State == CClient::STATE_AUTHED) + { + char aFormatted[256]; + str_format(aFormatted, sizeof(aBuf), "cid=%d cmd='%s'", ClientID, aBuf); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aFormatted); + Console()->ExecuteLine(aBuf); + } + } + + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; ++i) + { + if(m_aClients[i].m_State == CClient::STATE_CONNECTED && + time_get() > m_aClients[i].m_TimeConnected + g_Config.m_EcAuthTimeout * time_freq()) + m_NetConsole.Drop(i, "authentication timeout"); + } +} + +void CEcon::Send(int ClientID, const char *pLine) +{ + if(!m_Ready) + return; + + if(ClientID == -1) + { + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + { + if(m_aClients[i].m_State == CClient::STATE_AUTHED) + m_NetConsole.Send(i, pLine); + } + } + else if(ClientID >= 0 && ClientID < NET_MAX_CONSOLE_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_AUTHED) + m_NetConsole.Send(ClientID, pLine); +} + +void CEcon::Shutdown() +{ + if(!m_Ready) + return; + + m_NetConsole.Close(); +} diff --git a/src/engine/shared/econ.h b/src/engine/shared/econ.h new file mode 100644 index 000000000..daec34c45 --- /dev/null +++ b/src/engine/shared/econ.h @@ -0,0 +1,50 @@ +#ifndef ENGINE_SHARED_ECON_H +#define ENGINE_SHARED_ECON_H + +#include "network.h" + +class CEcon +{ + enum + { + MAX_AUTH_TRIES=3, + }; + + class CClient + { + public: + enum + { + STATE_EMPTY=0, + STATE_CONNECTED, + STATE_AUTHED, + }; + + int m_State; + int64 m_TimeConnected; + int m_AuthTries; + }; + CClient m_aClients[NET_MAX_CONSOLE_CLIENTS]; + + IConsole *m_pConsole; + CNetConsole m_NetConsole; + + bool m_Ready; + int m_PrintCBIndex; + + static void SendLineCB(const char *pLine, void *pUserData); + static void ConchainEconOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + + static int NewClientCallback(int ClientID, void *pUser); + static int DelClientCallback(int ClientID, const char *pReason, void *pUser); + +public: + IConsole *Console() { return m_pConsole; } + + void Init(IConsole *pConsole); + void Update(); + void Send(int ClientID, const char *pLine); + void Shutdown(); +}; + +#endif diff --git a/src/engine/shared/filecollection.cpp b/src/engine/shared/filecollection.cpp new file mode 100644 index 000000000..622534f2d --- /dev/null +++ b/src/engine/shared/filecollection.cpp @@ -0,0 +1,186 @@ +/* (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 "filecollection.h" + +bool CFileCollection::IsFilenameValid(const char *pFilename) +{ + if(str_length(pFilename) != m_FileDescLength+TIMESTAMP_LENGTH+m_FileExtLength || + str_comp_num(pFilename, m_aFileDesc, m_FileDescLength) || + str_comp(pFilename+m_FileDescLength+TIMESTAMP_LENGTH, m_aFileExt)) + return false; + + pFilename += m_FileDescLength; + if(pFilename[0] == '_' && + pFilename[1] >= '0' && pFilename[1] <= '9' && + pFilename[2] >= '0' && pFilename[2] <= '9' && + pFilename[3] >= '0' && pFilename[3] <= '9' && + pFilename[4] >= '0' && pFilename[4] <= '9' && + pFilename[5] == '-' && + pFilename[6] >= '0' && pFilename[6] <= '9' && + pFilename[7] >= '0' && pFilename[7] <= '9' && + pFilename[8] == '-' && + pFilename[9] >= '0' && pFilename[9] <= '9' && + pFilename[10] >= '0' && pFilename[10] <= '9' && + pFilename[11] == '_' && + pFilename[12] >= '0' && pFilename[12] <= '9' && + pFilename[13] >= '0' && pFilename[13] <= '9' && + pFilename[14] == '-' && + pFilename[15] >= '0' && pFilename[15] <= '9' && + pFilename[16] >= '0' && pFilename[16] <= '9' && + pFilename[17] == '-' && + pFilename[18] >= '0' && pFilename[18] <= '9' && + pFilename[19] >= '0' && pFilename[19] <= '9') + return true; + + return false; +} + +int64 CFileCollection::ExtractTimestamp(const char *pTimestring) +{ + int64 Timestamp = pTimestring[0]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[1]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[2]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[3]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[5]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[6]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[8]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[9]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[11]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[12]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[14]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[15]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[17]-'0'; Timestamp <<= 4; + Timestamp += pTimestring[18]-'0'; + + return Timestamp; +} + +void CFileCollection::BuildTimestring(int64 Timestamp, char *pTimestring) +{ + pTimestring[19] = 0; + pTimestring[18] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[17] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[16] = '-'; + pTimestring[15] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[14] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[13] = '-'; + pTimestring[12] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[11] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[10] = '_'; + pTimestring[9] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[8] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[7] = '-'; + pTimestring[6] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[5] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[4] = '-'; + pTimestring[3] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[2] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[1] = (Timestamp&0xF)+'0'; Timestamp >>= 4; + pTimestring[0] = (Timestamp&0xF)+'0'; +} + +void CFileCollection::Init(IStorage *pStorage, const char *pPath, const char *pFileDesc, const char *pFileExt, int MaxEntries) +{ + mem_zero(m_aTimestamps, sizeof(m_aTimestamps)); + m_NumTimestamps = 0; + m_MaxEntries = clamp(MaxEntries, 1, static_cast(MAX_ENTRIES)); + str_copy(m_aFileDesc, pFileDesc, sizeof(m_aFileDesc)); + m_FileDescLength = str_length(m_aFileDesc); + str_copy(m_aFileExt, pFileExt, sizeof(m_aFileExt)); + m_FileExtLength = str_length(m_aFileExt); + str_copy(m_aPath, pPath, sizeof(m_aPath)); + m_pStorage = pStorage; + + m_pStorage->ListDirectory(IStorage::TYPE_SAVE, m_aPath, FilelistCallback, this); +} + +void CFileCollection::AddEntry(int64 Timestamp) +{ + if(m_NumTimestamps == 0) + { + // empty list + m_aTimestamps[m_NumTimestamps++] = Timestamp; + } + else + { + // remove old file + if(m_NumTimestamps == m_MaxEntries) + { + char aBuf[512]; + char aTimestring[TIMESTAMP_LENGTH]; + BuildTimestring(m_aTimestamps[0], aTimestring); + str_format(aBuf, sizeof(aBuf), "%s/%s_%s%s", m_aPath, m_aFileDesc, aTimestring, m_aFileExt); + m_pStorage->RemoveFile(aBuf, IStorage::TYPE_SAVE); + } + + // add entry to the sorted list + if(m_aTimestamps[0] > Timestamp) + { + // first entry + if(m_NumTimestamps < m_MaxEntries) + { + mem_move(m_aTimestamps+1, m_aTimestamps, m_NumTimestamps*sizeof(int64)); + m_aTimestamps[0] = Timestamp; + ++m_NumTimestamps; + } + } + else if(m_aTimestamps[m_NumTimestamps-1] <= Timestamp) + { + // last entry + if(m_NumTimestamps == m_MaxEntries) + { + mem_move(m_aTimestamps, m_aTimestamps+1, (m_NumTimestamps-1)*sizeof(int64)); + m_aTimestamps[m_NumTimestamps-1] = Timestamp; + } + else + m_aTimestamps[m_NumTimestamps++] = Timestamp; + } + else + { + // middle entry + int Left = 0, Right = m_NumTimestamps-1; + while(Right-Left > 1) + { + int Mid = (Left+Right)/2; + if(m_aTimestamps[Mid] > Timestamp) + Right = Mid; + else + Left = Mid; + } + + if(m_NumTimestamps == m_MaxEntries) + { + mem_move(m_aTimestamps, m_aTimestamps+1, (Right-1)*sizeof(int64)); + m_aTimestamps[Right-1] = Timestamp; + } + else + { + mem_move(m_aTimestamps+Right+1, m_aTimestamps+Right, (m_NumTimestamps-Right)*sizeof(int64)); + m_aTimestamps[Right] = Timestamp; + ++m_NumTimestamps; + } + } + } +} + +int CFileCollection::FilelistCallback(const char *pFilename, int IsDir, int StorageType, void *pUser) +{ + CFileCollection *pThis = static_cast(pUser); + + // check for valid file name format + if(IsDir || !pThis->IsFilenameValid(pFilename)) + return 0; + + // extract the timestamp + int64 Timestamp = pThis->ExtractTimestamp(pFilename+pThis->m_FileDescLength+1); + + // add the entry + pThis->AddEntry(Timestamp); + + return 0; +} diff --git a/src/engine/shared/filecollection.h b/src/engine/shared/filecollection.h new file mode 100644 index 000000000..ac6338922 --- /dev/null +++ b/src/engine/shared/filecollection.h @@ -0,0 +1,35 @@ +/* (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 ENGINE_SHARED_FILECOLLECTION_H +#define ENGINE_SHARED_FILECOLLECTION_H + +class CFileCollection +{ + enum + { + MAX_ENTRIES=1000, + TIMESTAMP_LENGTH=20, // _YYYY-MM-DD_HH-MM-SS + }; + + int64 m_aTimestamps[MAX_ENTRIES]; + int m_NumTimestamps; + int m_MaxEntries; + char m_aFileDesc[128]; + int m_FileDescLength; + char m_aFileExt[32]; + int m_FileExtLength; + char m_aPath[512]; + IStorage *m_pStorage; + + bool IsFilenameValid(const char *pFilename); + int64 ExtractTimestamp(const char *pTimestring); + void BuildTimestring(int64 Timestamp, char *pTimestring); + +public: + void Init(IStorage *pStorage, const char *pPath, const char *pFileDesc, const char *pFileExt, int MaxEntries); + void AddEntry(int64 Timestamp); + + static int FilelistCallback(const char *pFilename, int IsDir, int StorageType, void *pUser); +}; + +#endif diff --git a/src/engine/shared/masterserver.cpp b/src/engine/shared/masterserver.cpp index 1bf402cab..1271eeaf4 100644 --- a/src/engine/shared/masterserver.cpp +++ b/src/engine/shared/masterserver.cpp @@ -21,51 +21,57 @@ public: bool m_Valid; CHostLookup m_Lookup; - } ; + }; + + enum + { + STATE_INIT, + STATE_UPDATE, + STATE_READY, + }; CMasterInfo m_aMasterServers[MAX_MASTERSERVERS]; - int m_NeedsUpdate; + int m_State; IEngine *m_pEngine; IStorage *m_pStorage; CMasterServer() { SetDefault(); - m_NeedsUpdate = -1; + m_State = STATE_INIT; m_pEngine = 0; + m_pStorage = 0; } virtual int RefreshAddresses(int Nettype) { - int i; - - if(m_NeedsUpdate != -1) - return 0; + if(m_State != STATE_INIT) + return -1; dbg_msg("engine/mastersrv", "refreshing master server addresses"); // add lookup jobs - for(i = 0; i < MAX_MASTERSERVERS; i++) + for(int i = 0; i < MAX_MASTERSERVERS; i++) { m_pEngine->HostLookup(&m_aMasterServers[i].m_Lookup, m_aMasterServers[i].m_aHostname, Nettype); m_aMasterServers[i].m_Valid = false; } - m_NeedsUpdate = 1; + m_State = STATE_UPDATE; return 0; } virtual void Update() { // check if we need to update - if(m_NeedsUpdate != 1) + if(m_State != STATE_UPDATE) return; - m_NeedsUpdate = 0; + m_State = STATE_READY; for(int i = 0; i < MAX_MASTERSERVERS; i++) { if(m_aMasterServers[i].m_Lookup.m_Job.Status() != CJob::STATE_DONE) - m_NeedsUpdate = 1; + m_State = STATE_UPDATE; else { if(m_aMasterServers[i].m_Lookup.m_Job.Result() == 0) @@ -79,7 +85,7 @@ public: } } - if(!m_NeedsUpdate) + if(m_State == STATE_READY) { dbg_msg("engine/mastersrv", "saving addresses"); Save(); @@ -88,7 +94,7 @@ public: virtual int IsRefreshing() { - return m_NeedsUpdate; + return m_State != STATE_READY; } virtual NETADDR GetAddr(int Index) @@ -106,16 +112,6 @@ public: return m_aMasterServers[Index].m_Valid; } - virtual void DumpServers() - { - for(int i = 0; i < MAX_MASTERSERVERS; i++) - { - char aAddrStr[NETADDR_MAXSTRSIZE]; - net_addr_str(&m_aMasterServers[i].m_Addr, aAddrStr, sizeof(aAddrStr)); - dbg_msg("mastersrv", "#%d = %s", i, aAddrStr); - } - } - virtual void Init() { m_pEngine = Kernel()->RequestInterface(); @@ -131,17 +127,15 @@ public: virtual int Load() { - CLineReader LineReader; - IOHANDLE File; - int Count = 0; if(!m_pStorage) return -1; // try to open file - File = m_pStorage->OpenFile("masters.cfg", IOFLAG_READ, IStorage::TYPE_SAVE); + IOHANDLE File = m_pStorage->OpenFile("masters.cfg", IOFLAG_READ, IStorage::TYPE_SAVE); if(!File) return -1; + CLineReader LineReader; LineReader.Init(File); while(1) { @@ -152,19 +146,32 @@ public: // parse line char aAddrStr[NETADDR_MAXSTRSIZE]; - if(sscanf(pLine, "%s %s", Info.m_aHostname, aAddrStr) == 2 && net_addr_from_str(&Info.m_Addr, aAddrStr) == 0) + if(sscanf(pLine, "%127s %47s", Info.m_aHostname, aAddrStr) == 2 && net_addr_from_str(&Info.m_Addr, aAddrStr) == 0) { Info.m_Addr.port = 8300; - if(Count != MAX_MASTERSERVERS) + bool Added = false; + for(int i = 0; i < MAX_MASTERSERVERS; ++i) + if(str_comp(m_aMasterServers[i].m_aHostname, Info.m_aHostname) == 0) + { + m_aMasterServers[i] = Info; + Added = true; + break; + } + + if(!Added) { - m_aMasterServers[Count] = Info; - Count++; + for(int i = 0; i < MAX_MASTERSERVERS; ++i) + if(m_aMasterServers[i].m_Addr.type == NETTYPE_INVALID) + { + m_aMasterServers[i] = Info; + Added = true; + break; + } } - //else - // dbg_msg("engine/mastersrv", "warning: skipped master server '%s' due to limit of %d", pLine, MAX_MASTERSERVERS); + + if(!Added) + break; } - //else - // dbg_msg("engine/mastersrv", "warning: couldn't parse master server '%s'", pLine); } io_close(File); @@ -173,21 +180,22 @@ public: virtual int Save() { - IOHANDLE File; - if(!m_pStorage) return -1; // try to open file - File = m_pStorage->OpenFile("masters.cfg", IOFLAG_WRITE, IStorage::TYPE_SAVE); + IOHANDLE File = m_pStorage->OpenFile("masters.cfg", IOFLAG_WRITE, IStorage::TYPE_SAVE); if(!File) return -1; for(int i = 0; i < MAX_MASTERSERVERS; i++) { char aAddrStr[NETADDR_MAXSTRSIZE]; - net_addr_str(&m_aMasterServers[i].m_Addr, aAddrStr, sizeof(aAddrStr)); - char aBuf[1024]; + if(m_aMasterServers[i].m_Addr.type != NETTYPE_INVALID) + net_addr_str(&m_aMasterServers[i].m_Addr, aAddrStr, sizeof(aAddrStr)); + else + aAddrStr[0] = 0; + char aBuf[256]; str_format(aBuf, sizeof(aBuf), "%s %s\n", m_aMasterServers[i].m_aHostname, aAddrStr); io_write(File, aBuf, str_length(aBuf)); diff --git a/src/engine/shared/network.h b/src/engine/shared/network.h index 606e6270b..1611a9888 100644 --- a/src/engine/shared/network.h +++ b/src/engine/shared/network.h @@ -49,6 +49,7 @@ enum NET_MAX_CHUNKHEADERSIZE = 5, NET_PACKETHEADERSIZE = 3, NET_MAX_CLIENTS = 16, + NET_MAX_CONSOLE_CLIENTS = 4, NET_MAX_SEQUENCE = 1<<10, NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1, @@ -192,6 +193,36 @@ public: int AckSequence() const { return m_Ack; } }; +class CConsoleNetConnection +{ +private: + int m_State; + + NETADDR m_PeerAddr; + NETSOCKET m_Socket; + + char m_aBuffer[NET_MAX_PACKETSIZE]; + int m_BufferOffset; + + char m_aErrorString[256]; + + bool m_LineEndingDetected; + char m_aLineEnding[3]; + +public: + void Init(NETSOCKET Socket, const NETADDR *pAddr); + void Disconnect(const char *pReason); + + int State() const { return m_State; } + NETADDR PeerAddress() const { return m_PeerAddr; } + const char *ErrorString() const { return m_aErrorString; } + + void Reset(); + int Update(); + int Send(const char *pLine); + int Recv(char *pLine, int MaxLength); +}; + class CNetRecvUnpacker { public: @@ -285,6 +316,7 @@ public: // status requests NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } NETSOCKET Socket() const { return m_Socket; } + int NetType() { return m_Socket.type; } int MaxClients() const { return m_MaxClients; } // @@ -307,6 +339,59 @@ private: }; +class CNetConsole +{ + enum + { + MAX_BANS=128, + }; + + int FindBan(NETADDR Addr); + void UpdateBans(); + + struct CBanEntry + { + NETADDR m_Addr; + int m_Expires; + } m_aBans[MAX_BANS]; + int m_NumBans; + + struct CSlot + { + CConsoleNetConnection m_Connection; + }; + + NETSOCKET m_Socket; + CSlot m_aSlots[NET_MAX_CONSOLE_CLIENTS]; + + NETFUNC_NEWCLIENT m_pfnNewClient; + NETFUNC_DELCLIENT m_pfnDelClient; + void *m_UserPtr; + + CNetRecvUnpacker m_RecvUnpacker; + +public: + void SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); + + // + bool Open(NETADDR BindAddr, int Flags); + int Close(); + + // + int Recv(char *pLine, int MaxLength, int *pClientID = 0); + int Send(int ClientID, const char *pLine); + int Update(); + + // + int AcceptClient(NETSOCKET Socket, const NETADDR *pAddr); + int Drop(int ClientID, const char *pReason); + + bool AddBan(NETADDR Addr, int Seconds); + + // status requests + NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } +}; + // client side @@ -336,6 +421,7 @@ public: int ResetErrorString(); // error and state + int NetType() { return m_Socket.type; } int State(); int GotProblems(); const char *ErrorString(); diff --git a/src/engine/shared/network_console.cpp b/src/engine/shared/network_console.cpp new file mode 100644 index 000000000..13ed37516 --- /dev/null +++ b/src/engine/shared/network_console.cpp @@ -0,0 +1,219 @@ +/* (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 "network.h" + +bool CNetConsole::Open(NETADDR BindAddr, int Flags) +{ + // zero out the whole structure + mem_zero(this, sizeof(*this)); + m_Socket.type = NETTYPE_INVALID; + m_Socket.ipv4sock = -1; + m_Socket.ipv6sock = -1; + + // open socket + m_Socket = net_tcp_create(BindAddr); + if(!m_Socket.type) + return false; + if(net_tcp_listen(m_Socket, NET_MAX_CONSOLE_CLIENTS)) + return false; + net_set_non_blocking(m_Socket); + + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + m_aSlots[i].m_Connection.Reset(); + + return true; +} + +void CNetConsole::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser) +{ + m_pfnNewClient = pfnNewClient; + m_pfnDelClient = pfnDelClient; + m_UserPtr = pUser; +} + +int CNetConsole::Close() +{ + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + m_aSlots[i].m_Connection.Disconnect("closing console"); + + net_tcp_close(m_Socket); + + return 0; +} + +int CNetConsole::Drop(int ClientID, const char *pReason) +{ + if(m_pfnDelClient) + m_pfnDelClient(ClientID, pReason, m_UserPtr); + + m_aSlots[ClientID].m_Connection.Disconnect(pReason); + + return 0; +} + +int CNetConsole::AcceptClient(NETSOCKET Socket, const NETADDR *pAddr) +{ + char aError[256] = { 0 }; + int FreeSlot = -1; + + // look for free slot or multiple client + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + { + if(FreeSlot == -1 && m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) + FreeSlot = i; + if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE) + { + NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); + if(net_addr_comp(pAddr, &PeerAddr) == 0) + { + str_copy(aError, "only one client per IP allowed", sizeof(aError)); + break; + } + } + } + + // accept client + if(!aError[0] && FreeSlot != -1) + { + m_aSlots[FreeSlot].m_Connection.Init(Socket, pAddr); + if(m_pfnNewClient) + m_pfnNewClient(FreeSlot, m_UserPtr); + return 0; + } + + // reject client + if(!aError[0]) + str_copy(aError, "no free slot available", sizeof(aError)); + + net_tcp_send(Socket, aError, str_length(aError)); + net_tcp_close(Socket); + + return -1; +} + +int CNetConsole::Update() +{ + NETSOCKET Socket; + NETADDR Addr; + + if(net_tcp_accept(m_Socket, &Socket, &Addr) > 0) + { + int Index = FindBan(Addr); + if(Index == -1) + AcceptClient(Socket, &Addr); + else + { + char aBuf[128]; + if(m_aBans[Index].m_Expires > -1) + { + int Mins = (m_aBans[Index].m_Expires-time_timestamp()+ 59) / 60; + if(Mins <= 1) + str_format(aBuf, sizeof(aBuf), "You have been banned for 1 minute"); + else + str_format(aBuf, sizeof(aBuf), "You have been banned for %d minutes", Mins); + } + else + str_format(aBuf, sizeof(aBuf), "You have been banned for life"); + + net_tcp_send(Socket, aBuf, str_length(aBuf)); + net_tcp_close(Socket); + } + } + + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + { + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE) + m_aSlots[i].m_Connection.Update(); + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) + Drop(i, m_aSlots[i].m_Connection.ErrorString()); + } + + UpdateBans(); + + return 0; +} + +int CNetConsole::Recv(char *pLine, int MaxLength, int *pClientID) +{ + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + { + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE && m_aSlots[i].m_Connection.Recv(pLine, MaxLength)) + { + if(pClientID) + *pClientID = i; + return 1; + } + } + return 0; +} + +int CNetConsole::Send(int ClientID, const char *pLine) +{ + if(m_aSlots[ClientID].m_Connection.State() == NET_CONNSTATE_ONLINE) + return m_aSlots[ClientID].m_Connection.Send(pLine); + else + return -1; +} + +int CNetConsole::FindBan(NETADDR Addr) +{ + Addr.port = 0; + for(int i = 0; i < m_NumBans; i++) + if(net_addr_comp(&m_aBans[i].m_Addr, &Addr) == 0) + return i; + + return -1; +} + +bool CNetConsole::AddBan(NETADDR Addr, int Seconds) +{ + if(m_NumBans == MAX_BANS) + return false; + + Addr.port = 0; + int Index = FindBan(Addr); + if(Index == -1) + { + Index = m_NumBans++; + m_aBans[Index].m_Addr = Addr; + } + m_aBans[Index].m_Expires = Seconds>0 ? time_timestamp()+Seconds : -1; + + for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) + { + if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE) + { + NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); + PeerAddr.port = 0; + if(net_addr_comp(&Addr, &PeerAddr) == 0) + { + char aBuf[128]; + if(Seconds>0) + { + int Mins = (Seconds + 59) / 60; + if(Mins <= 1) + str_format(aBuf, sizeof(aBuf), "You have been banned for 1 minute"); + else + str_format(aBuf, sizeof(aBuf), "You have been banned for %d minutes", Mins); + } + else + str_format(aBuf, sizeof(aBuf), "You have been banned for life"); + Drop(i, aBuf); + } + } + } + return true; +} + +void CNetConsole::UpdateBans() +{ + int Now = time_timestamp(); + for(int i = 0; i < m_NumBans; ++i) + if(m_aBans[i].m_Expires > 0 && m_aBans[i].m_Expires < Now) + { + m_aBans[i] = m_aBans[m_NumBans-1]; + --m_NumBans; + break; + } +} diff --git a/src/engine/shared/network_console_conn.cpp b/src/engine/shared/network_console_conn.cpp new file mode 100644 index 000000000..75b581fa2 --- /dev/null +++ b/src/engine/shared/network_console_conn.cpp @@ -0,0 +1,186 @@ +/* (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 "network.h" + +void CConsoleNetConnection::Reset() +{ + m_State = NET_CONNSTATE_OFFLINE; + mem_zero(&m_PeerAddr, sizeof(m_PeerAddr)); + m_aErrorString[0] = 0; + + m_Socket.type = NETTYPE_INVALID; + m_Socket.ipv4sock = -1; + m_Socket.ipv6sock = -1; + m_aBuffer[0] = 0; + m_BufferOffset = 0; + + m_LineEndingDetected = false; + #if defined(CONF_FAMILY_WINDOWS) + m_aLineEnding[0] = '\r'; + m_aLineEnding[1] = '\n'; + m_aLineEnding[2] = 0; + #else + m_aLineEnding[0] = '\n'; + m_aLineEnding[1] = 0; + m_aLineEnding[2] = 0; + #endif +} + +void CConsoleNetConnection::Init(NETSOCKET Socket, const NETADDR *pAddr) +{ + Reset(); + + m_Socket = Socket; + net_set_non_blocking(m_Socket); + + m_PeerAddr = *pAddr; + m_State = NET_CONNSTATE_ONLINE; +} + +void CConsoleNetConnection::Disconnect(const char *pReason) +{ + if(State() == NET_CONNSTATE_OFFLINE) + return; + + if(pReason && pReason[0]) + Send(pReason); + + net_tcp_close(m_Socket); + + Reset(); +} + +int CConsoleNetConnection::Update() +{ + if(State() == NET_CONNSTATE_ONLINE) + { + if((int)(sizeof(m_aBuffer)) <= m_BufferOffset) + { + m_State = NET_CONNSTATE_ERROR; + str_copy(m_aErrorString, "too weak connection (out of buffer)", sizeof(m_aErrorString)); + return -1; + } + + int Bytes = net_tcp_recv(m_Socket, m_aBuffer+m_BufferOffset, (int)(sizeof(m_aBuffer))-m_BufferOffset); + + if(Bytes > 0) + { + m_BufferOffset += Bytes; + } + else if(Bytes < 0) + { + if(net_would_block()) // no data received + return 0; + + m_State = NET_CONNSTATE_ERROR; // error + str_copy(m_aErrorString, "connection failure", sizeof(m_aErrorString)); + return -1; + } + else + { + m_State = NET_CONNSTATE_ERROR; + str_copy(m_aErrorString, "remote end closed the connection", sizeof(m_aErrorString)); + return -1; + } + } + + return 0; +} + +int CConsoleNetConnection::Recv(char *pLine, int MaxLength) +{ + if(State() == NET_CONNSTATE_ONLINE) + { + if(m_BufferOffset) + { + // find message start + int StartOffset = 0; + while(m_aBuffer[StartOffset] == '\r' || m_aBuffer[StartOffset] == '\n') + { + // detect clients line ending format + if(!m_LineEndingDetected) + { + m_aLineEnding[0] = m_aBuffer[StartOffset]; + if(StartOffset+1 < m_BufferOffset && (m_aBuffer[StartOffset+1] == '\r' || m_aBuffer[StartOffset+1] == '\n') && + m_aBuffer[StartOffset] != m_aBuffer[StartOffset+1]) + m_aLineEnding[1] = m_aBuffer[StartOffset+1]; + m_LineEndingDetected = true; + } + + if(++StartOffset >= m_BufferOffset) + { + m_BufferOffset = 0; + return 0; + } + } + + // find message end + int EndOffset = StartOffset; + while(m_aBuffer[EndOffset] != '\r' && m_aBuffer[EndOffset] != '\n') + { + if(++EndOffset >= m_BufferOffset) + { + if(StartOffset > 0) + { + mem_move(m_aBuffer, m_aBuffer+StartOffset, m_BufferOffset-StartOffset); + m_BufferOffset -= StartOffset; + } + return 0; + } + } + + // extract message and update buffer + if(MaxLength-1 < EndOffset-StartOffset) + { + if(StartOffset > 0) + { + mem_move(m_aBuffer, m_aBuffer+StartOffset, m_BufferOffset-StartOffset); + m_BufferOffset -= StartOffset; + } + return 0; + } + mem_copy(pLine, m_aBuffer+StartOffset, EndOffset-StartOffset); + pLine[EndOffset-StartOffset] = 0; + str_sanitize_cc(pLine); + mem_move(m_aBuffer, m_aBuffer+EndOffset, m_BufferOffset-EndOffset); + m_BufferOffset -= EndOffset; + return 1; + } + } + return 0; +} + +int CConsoleNetConnection::Send(const char *pLine) +{ + if(State() != NET_CONNSTATE_ONLINE) + return -1; + + char aBuf[1024]; + str_copy(aBuf, pLine, (int)(sizeof(aBuf))-2); + int Length = str_length(aBuf); + aBuf[Length] = m_aLineEnding[0]; + aBuf[Length+1] = m_aLineEnding[1]; + aBuf[Length+2] = m_aLineEnding[2]; + Length += 3; + const char *pData = aBuf; + + while(true) + { + int Send = net_tcp_send(m_Socket, pData, Length); + if(Send < 0) + { + m_State = NET_CONNSTATE_ERROR; + str_copy(m_aErrorString, "failed to send packet", sizeof(m_aErrorString)); + return -1; + } + + if(Send >= Length) + break; + + pData += Send; + Length -= Send; + } + + return 0; +} diff --git a/src/engine/shared/network_server.cpp b/src/engine/shared/network_server.cpp index 361d93c42..6506b5167 100644 --- a/src/engine/shared/network_server.cpp +++ b/src/engine/shared/network_server.cpp @@ -223,11 +223,16 @@ int CNetServer::BanAdd(NETADDR Addr, int Seconds, const char *pReason) char Buf[128]; NETADDR BanAddr; +<<<<<<< HEAD int Mins = (Seconds + 59) / 60; if(Mins && Seconds) +======= + if(Stamp > -1) +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe { - if(Mins == 1) + int Mins = (Seconds + 59) / 60; + if(Mins <= 1) str_format(Buf, sizeof(Buf), "You have been banned for 1 minute (%s)", pReason); else str_format(Buf, sizeof(Buf), "You have been banned for %d minutes (%s)", Mins, pReason); @@ -258,7 +263,11 @@ int CNetServer::Update() } // remove expired bans +<<<<<<< HEAD while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires < Now && m_BanPool_FirstUsed->m_Info.m_Expires != -1) +======= + while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires > -1 && m_BanPool_FirstUsed->m_Info.m_Expires < Now) +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe { CBan *pBan = m_BanPool_FirstUsed; BanRemoveByObject(pBan); @@ -310,10 +319,14 @@ int CNetServer::Recv(CNetChunk *pChunk) { // banned, reply with a message char BanStr[128]; +<<<<<<< HEAD if(pBan->m_Info.m_Expires && pBan->m_Info.m_Expires != -1) +======= + if(pBan->m_Info.m_Expires > -1) +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe { int Mins = ((pBan->m_Info.m_Expires - Now)+59)/60; - if(Mins == 1) + if(Mins <= 1) str_format(BanStr, sizeof(BanStr), "Banned for 1 minute (%s)", pBan->m_Info.m_Reason); else str_format(BanStr, sizeof(BanStr), "Banned for %d minutes (%s)", Mins, pBan->m_Info.m_Reason); diff --git a/src/engine/shared/protocol.h b/src/engine/shared/protocol.h index 4a4895ad8..ba04da8af 100644 --- a/src/engine/shared/protocol.h +++ b/src/engine/shared/protocol.h @@ -65,7 +65,11 @@ enum // sent by both NETMSG_PING, NETMSG_PING_REPLY, - NETMSG_ERROR + NETMSG_ERROR, + + // sent by server (todo: move it up) + NETMSG_RCON_CMD_ADD, + NETMSG_RCON_CMD_REM, }; // this should be revised diff --git a/src/engine/sound.h b/src/engine/sound.h index ac4aeaf50..f55a978d5 100644 --- a/src/engine/sound.h +++ b/src/engine/sound.h @@ -16,6 +16,8 @@ public: FLAG_ALL=3 }; + virtual bool IsSoundEnabled() = 0; + virtual int LoadWV(const char *pFilename) = 0; virtual void SetChannel(int ChannelID, float Volume, float Panning) = 0; diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index 8566bc519..3d299911c 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -133,17 +133,28 @@ bool CChat::OnInput(IInput::CEvent Event) // find next possible name const char *pCompletionString = 0; - m_CompletionChosen = (m_CompletionChosen+1)%MAX_CLIENTS; - for(int i = 0; i < MAX_CLIENTS; ++i) + m_CompletionChosen = (m_CompletionChosen+1)%(2*MAX_CLIENTS); + for(int i = 0; i < 2*MAX_CLIENTS; ++i) { + int SearchType = ((m_CompletionChosen+i)%(2*MAX_CLIENTS))/MAX_CLIENTS; int Index = (m_CompletionChosen+i)%MAX_CLIENTS; if(!m_pClient->m_Snap.m_paPlayerInfos[Index]) continue; - if(str_find_nocase(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer)) + bool Found = false; + if(SearchType == 1) + { + if(str_comp_nocase_num(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer)) && + str_find_nocase(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer)) + Found = true; + } + else if(!str_comp_nocase_num(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer))) + Found = true; + + if(Found) { pCompletionString = m_pClient->m_aClients[Index].m_aName; - m_CompletionChosen = Index; + m_CompletionChosen = Index+SearchType*MAX_CLIENTS; break; } } @@ -152,10 +163,25 @@ bool CChat::OnInput(IInput::CEvent Event) if(pCompletionString) { char aBuf[256]; + // add part before the name str_copy(aBuf, m_Input.GetString(), min(static_cast(sizeof(aBuf)), m_PlaceholderOffset+1)); + + // add the name str_append(aBuf, pCompletionString, sizeof(aBuf)); + + // add seperator + const char *pSeparator = ""; + if(*(m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength) != ' ') + pSeparator = m_PlaceholderOffset == 0 ? ": " : " "; + else if(m_PlaceholderOffset == 0) + pSeparator = ":"; + if(*pSeparator) + str_append(aBuf, pSeparator, sizeof(aBuf)); + + // add part after the name str_append(aBuf, m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength, sizeof(aBuf)); - m_PlaceholderLength = str_length(pCompletionString); + + m_PlaceholderLength = str_length(pSeparator)+str_length(pCompletionString); m_OldChatStringLength = m_Input.GetLength(); m_Input.Set(aBuf); m_Input.SetCursorOffset(m_PlaceholderOffset+m_PlaceholderLength); @@ -185,11 +211,7 @@ bool CChat::OnInput(IInput::CEvent Event) m_pHistoryEntry = m_History.Last(); if (m_pHistoryEntry) - { - unsigned int Len = str_length(m_pHistoryEntry); - if (Len < sizeof(m_Input) - 1) // TODO: WTF? - m_Input.Set(m_pHistoryEntry); - } + m_Input.Set(m_pHistoryEntry); } else if (Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_DOWN) { @@ -197,11 +219,7 @@ bool CChat::OnInput(IInput::CEvent Event) m_pHistoryEntry = m_History.Next(m_pHistoryEntry); if (m_pHistoryEntry) - { - unsigned int Len = str_length(m_pHistoryEntry); - if (Len < sizeof(m_Input) - 1) // TODO: WTF? - m_Input.Set(m_pHistoryEntry); - } + m_Input.Set(m_pHistoryEntry); else m_Input.Clear(); } @@ -247,6 +265,7 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine) char *p = const_cast(pLine); while(*p) { + Highlighted = false; pLine = p; // find line seperator and strip multiline while(*p) @@ -265,9 +284,16 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine) m_aLines[m_CurrentLine].m_ClientID = ClientID; m_aLines[m_CurrentLine].m_Team = Team; m_aLines[m_CurrentLine].m_NameColor = -2; - m_aLines[m_CurrentLine].m_Highlighted = str_find_nocase(pLine, m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName) != 0; - if(m_aLines[m_CurrentLine].m_Highlighted) - Highlighted = true; + + // check for highlighted name + const char *pHL = str_find_nocase(pLine, m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].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] == ' '))) + Highlighted = true; + } + m_aLines[m_CurrentLine].m_Highlighted = Highlighted; if(ClientID == -1) // server message { @@ -355,7 +381,9 @@ void CChat::OnRender() } TextRender()->TextEx(&Cursor, m_Input.GetString()+m_ChatStringOffset, m_Input.GetCursorOffset()-m_ChatStringOffset); + static float MarkerOffset = TextRender()->TextWidth(0, 8.0f, "|", -1)/3; CTextCursor Marker = Cursor; + Marker.m_X -= MarkerOffset; TextRender()->TextEx(&Marker, "|", -1); TextRender()->TextEx(&Cursor, m_Input.GetString()+m_Input.GetCursorOffset(), -1); } diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index c2cc8c614..57facc4e2 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -54,7 +54,7 @@ CGameConsole::CInstance::CInstance(int Type) m_CompletionChosen = -1; m_CompletionRenderOffset = 0.0f; - m_pCommand = 0x0; + m_IsCommand = false; } void CGameConsole::CInstance::Init(CGameConsole *pGameConsole) @@ -130,11 +130,7 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event) m_pHistoryEntry = m_History.Last(); if (m_pHistoryEntry) - { - unsigned int Len = str_length(m_pHistoryEntry); - if (Len < sizeof(m_Input) - 1) // TODO: WTF? - m_Input.Set(m_pHistoryEntry); - } + m_Input.Set(m_pHistoryEntry); Handled = true; } else if (Event.m_Key == KEY_DOWN) @@ -143,11 +139,7 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event) m_pHistoryEntry = m_History.Next(m_pHistoryEntry); if (m_pHistoryEntry) - { - unsigned int Len = str_length(m_pHistoryEntry); - if (Len < sizeof(m_Input) - 1) // TODO: WTF? - m_Input.Set(m_pHistoryEntry); - } + m_Input.Set(m_pHistoryEntry); else m_Input.Clear(); Handled = true; @@ -158,14 +150,16 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event) { m_CompletionChosen++; m_CompletionEnumerationCount = 0; - m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, PossibleCommandsCompleteCallback, this); + m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, m_Type != CGameConsole::CONSOLETYPE_LOCAL && + m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands(), PossibleCommandsCompleteCallback, this); // handle wrapping if(m_CompletionEnumerationCount && m_CompletionChosen >= m_CompletionEnumerationCount) { m_CompletionChosen %= m_CompletionEnumerationCount; m_CompletionEnumerationCount = 0; - m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, PossibleCommandsCompleteCallback, this); + m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, m_Type != CGameConsole::CONSOLETYPE_LOCAL && + m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands(), PossibleCommandsCompleteCallback, this); } } } @@ -201,7 +195,17 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event) aBuf[i] = *pSrc; aBuf[i] = 0; - m_pCommand = m_pGameConsole->m_pConsole->GetCommandInfo(aBuf, m_CompletionFlagmask); + const IConsole::CCommandInfo *pCommand = m_pGameConsole->m_pConsole->GetCommandInfo(aBuf, m_CompletionFlagmask, + m_Type != CGameConsole::CONSOLETYPE_LOCAL && m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands()); + if(pCommand) + { + m_IsCommand = true; + str_copy(m_aCommandName, pCommand->m_pName, IConsole::TEMPCMD_NAME_LENGTH); + str_copy(m_aCommandHelp, pCommand->m_pHelp, IConsole::TEMPCMD_HELP_LENGTH); + str_copy(m_aCommandParams, pCommand->m_pParams, IConsole::TEMPCMD_PARAMS_LENGTH); + } + else + m_IsCommand = false; } } } @@ -460,19 +464,19 @@ void CGameConsole::OnRender() { if(pConsole->m_Input.GetString()[0] != 0) { - m_pConsole->PossibleCommands(pConsole->m_aCompletionBuffer, pConsole->m_CompletionFlagmask, PossibleCommandsRenderCallback, &Info); + m_pConsole->PossibleCommands(pConsole->m_aCompletionBuffer, pConsole->m_CompletionFlagmask, m_ConsoleType != CGameConsole::CONSOLETYPE_LOCAL && + Client()->RconAuthed() && Client()->UseTempRconCommands(), PossibleCommandsRenderCallback, &Info); pConsole->m_CompletionRenderOffset = Info.m_Offset; if(Info.m_EnumCount <= 0) { - if(pConsole->m_pCommand) + if(pConsole->m_IsCommand) { - char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_pCommand->m_pHelp); + str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_aCommandHelp); TextRender()->TextEx(&Info.m_Cursor, aBuf, -1); TextRender()->TextColor(0.75f, 0.75f, 0.75f, 1); - str_format(aBuf, sizeof(aBuf), "Syntax: %s %s", pConsole->m_pCommand->m_pName, pConsole->m_pCommand->m_pParams); + str_format(aBuf, sizeof(aBuf), "Syntax: %s %s", pConsole->m_aCommandName, pConsole->m_aCommandParams); TextRender()->TextEx(&Info.m_Cursor, aBuf, -1); } } @@ -661,6 +665,16 @@ void CGameConsole::ClientConsolePrintCallback(const char *pStr, void *pUserData) ((CGameConsole *)pUserData)->m_LocalConsole.PrintLine(pStr); } +void CGameConsole::ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments() == 1) + { + CGameConsole *pThis = static_cast(pUserData); + pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0)); + } +} + void CGameConsole::PrintLine(int Type, const char *pLine) { if(Type == CONSOLETYPE_LOCAL) @@ -678,14 +692,25 @@ void CGameConsole::OnConsoleInit() m_pConsole = Kernel()->RequestInterface(); // - Console()->RegisterPrintCallback(ClientConsolePrintCallback, this); + m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, ClientConsolePrintCallback, this); +<<<<<<< HEAD Console()->Register("toggle_local_console", "", CFGFLAG_CLIENT, ConToggleLocalConsole, this, "Toggle local console", IConsole::CONSOLELEVEL_USER); Console()->Register("toggle_remote_console", "", CFGFLAG_CLIENT, ConToggleRemoteConsole, this, "Toggle remote console", IConsole::CONSOLELEVEL_USER); Console()->Register("clear_local_console", "", CFGFLAG_CLIENT, ConClearLocalConsole, this, "Clear local console", IConsole::CONSOLELEVEL_USER); Console()->Register("clear_remote_console", "", CFGFLAG_CLIENT, ConClearRemoteConsole, this, "Clear remote console", IConsole::CONSOLELEVEL_USER); Console()->Register("dump_local_console", "", CFGFLAG_CLIENT, ConDumpLocalConsole, this, "Dump local console", IConsole::CONSOLELEVEL_USER); Console()->Register("dump_remote_console", "", CFGFLAG_CLIENT, ConDumpRemoteConsole, this, "Dump remote console", IConsole::CONSOLELEVEL_USER); +======= + Console()->Register("toggle_local_console", "", CFGFLAG_CLIENT, ConToggleLocalConsole, this, "Toggle local console"); + Console()->Register("toggle_remote_console", "", CFGFLAG_CLIENT, ConToggleRemoteConsole, this, "Toggle remote console"); + Console()->Register("clear_local_console", "", CFGFLAG_CLIENT, ConClearLocalConsole, this, "Clear local console"); + Console()->Register("clear_remote_console", "", CFGFLAG_CLIENT, ConClearRemoteConsole, this, "Clear remote console"); + Console()->Register("dump_local_console", "", CFGFLAG_CLIENT, ConDumpLocalConsole, this, "Dump local console"); + Console()->Register("dump_remote_console", "", CFGFLAG_CLIENT, ConDumpRemoteConsole, this, "Dump remote console"); + + Console()->Chain("console_output_level", ConchainConsoleOutputLevelUpdate, this); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } void CGameConsole::OnStateChange(int NewState, int OldState) diff --git a/src/game/client/components/console.h b/src/game/client/components/console.h index 1e1e7dd44..3a8711a96 100644 --- a/src/game/client/components/console.h +++ b/src/game/client/components/console.h @@ -33,7 +33,10 @@ class CGameConsole : public CComponent int m_CompletionFlagmask; float m_CompletionRenderOffset; - IConsole::CCommandInfo *m_pCommand; + bool m_IsCommand; + char m_aCommandName[IConsole::TEMPCMD_NAME_LENGTH]; + char m_aCommandHelp[IConsole::TEMPCMD_HELP_LENGTH]; + char m_aCommandParams[IConsole::TEMPCMD_PARAMS_LENGTH]; CInstance(int t); void Init(CGameConsole *pGameConsole); @@ -57,6 +60,7 @@ class CGameConsole : public CComponent CInstance *CurrentConsole(); float TimeNow(); + int m_PrintCBIndex; int m_ConsoleType; int m_ConsoleState; @@ -68,12 +72,22 @@ class CGameConsole : public CComponent static void PossibleCommandsRenderCallback(const char *pStr, void *pUser); static void ClientConsolePrintCallback(const char *pStr, void *pUserData); +<<<<<<< HEAD static void ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData, int ClientID); static void ConToggleRemoteConsole(IConsole::IResult *pResult, void *pUserData, int ClientID); static void ConClearLocalConsole(IConsole::IResult *pResult, void *pUserData, int ClientID); static void ConClearRemoteConsole(IConsole::IResult *pResult, void *pUserData, int ClientID); static void ConDumpLocalConsole(IConsole::IResult *pResult, void *pUserData, int ClientID); static void ConDumpRemoteConsole(IConsole::IResult *pResult, void *pUserData, int ClientID); +======= + static void ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData); + static void ConToggleRemoteConsole(IConsole::IResult *pResult, void *pUserData); + static void ConClearLocalConsole(IConsole::IResult *pResult, void *pUserData); + static void ConClearRemoteConsole(IConsole::IResult *pResult, void *pUserData); + static void ConDumpLocalConsole(IConsole::IResult *pResult, void *pUserData); + static void ConDumpRemoteConsole(IConsole::IResult *pResult, void *pUserData); + static void ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe public: enum diff --git a/src/game/client/components/countryflags.cpp b/src/game/client/components/countryflags.cpp index 2429ad3fa..6daf3c2ec 100644 --- a/src/game/client/components/countryflags.cpp +++ b/src/game/client/components/countryflags.cpp @@ -45,6 +45,15 @@ void CCountryFlags::LoadCountryflagsIndexfile() continue; } + int CountryCode = str_toint(pReplacement+3); + if(CountryCode < CODE_LB || CountryCode > CODE_UB) + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "country code '%i' not within valid code range [%i..%i]", CountryCode, CODE_LB, CODE_UB); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aBuf); + continue; + } + // load the graphic file char aBuf[128]; str_format(aBuf, sizeof(aBuf), "countryflags/%s.png", aOrigin); @@ -59,7 +68,8 @@ void CCountryFlags::LoadCountryflagsIndexfile() // add entry CCountryFlag CountryFlag; - CountryFlag.m_CountryCode = str_toint(pReplacement+3); + CountryFlag.m_CountryCode = CountryCode; + str_copy(CountryFlag.m_aCountryCodeString, aOrigin, sizeof(CountryFlag.m_aCountryCodeString)); CountryFlag.m_Texture = Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); mem_free(Info.m_pData); str_format(aBuf, sizeof(aBuf), "loaded country flag '%s'", aOrigin); @@ -67,6 +77,10 @@ void CCountryFlags::LoadCountryflagsIndexfile() m_aCountryFlags.add(CountryFlag); } io_close(File); + + mem_zero(m_CodeIndexLUT, sizeof(m_CodeIndexLUT)); + for(int i = 0; i < m_aCountryFlags.size(); ++i) + m_CodeIndexLUT[max(0, (m_aCountryFlags[i].m_CountryCode-CODE_LB)%CODE_RANGE)] = i+1; } void CCountryFlags::OnInit() @@ -80,6 +94,7 @@ void CCountryFlags::OnInit() CCountryFlag DummyEntry; DummyEntry.m_CountryCode = -1; DummyEntry.m_Texture = -1; + mem_zero(DummyEntry.m_aCountryCodeString, sizeof(DummyEntry.m_aCountryCodeString)); m_aCountryFlags.add(DummyEntry); } } @@ -89,17 +104,12 @@ int CCountryFlags::Num() const return m_aCountryFlags.size(); } -const CCountryFlags::CCountryFlag *CCountryFlags::Get(int Index) const +const CCountryFlags::CCountryFlag *CCountryFlags::GetByCountryCode(int CountryCode) const +{ + return GetByIndex(m_CodeIndexLUT[max(0, (CountryCode-CODE_LB)%CODE_RANGE)]-1); +} + +const CCountryFlags::CCountryFlag *CCountryFlags::GetByIndex(int Index) const { return &m_aCountryFlags[max(0, Index%m_aCountryFlags.size())]; } - -int CCountryFlags::Find(int CountryCode) const -{ - for(int i = 0; i < m_aCountryFlags.size(); ++i) - { - if(m_aCountryFlags[i].m_CountryCode == CountryCode) - return i; - } - return -1; -} diff --git a/src/game/client/components/countryflags.h b/src/game/client/components/countryflags.h index cd6290943..ad24a7627 100644 --- a/src/game/client/components/countryflags.h +++ b/src/game/client/components/countryflags.h @@ -12,19 +12,28 @@ public: struct CCountryFlag { int m_CountryCode; + char m_aCountryCodeString[8]; int m_Texture; - bool operator<(const CCountryFlag &Other) { return m_CountryCode < Other.m_CountryCode; } + bool operator<(const CCountryFlag &Other) { return str_comp(m_aCountryCodeString, Other.m_aCountryCodeString) < 0; } }; void OnInit(); int Num() const; - const CCountryFlag *Get(int Index) const; - int Find(int CountryCode) const; + const CCountryFlag *GetByCountryCode(int CountryCode) const; + const CCountryFlag *GetByIndex(int Index) const; + //int Find(int CountryCode) const; private: + enum + { + CODE_LB=-1, + CODE_UB=999, + CODE_RANGE=CODE_UB-CODE_LB+1, + }; sorted_array m_aCountryFlags; + int m_CodeIndexLUT[CODE_RANGE]; void LoadCountryflagsIndexfile(); }; diff --git a/src/game/client/components/emoticon.cpp b/src/game/client/components/emoticon.cpp index 488729cd7..aace9d8b3 100644 --- a/src/game/client/components/emoticon.cpp +++ b/src/game/client/components/emoticon.cpp @@ -54,6 +54,7 @@ bool CEmoticon::OnMouseMove(float x, float y) if(!m_Active) return false; + UI()->ConvertMouseMove(&x, &y); m_SelectorMouse += vec2(x,y); return true; } @@ -101,6 +102,13 @@ void CEmoticon::OnRender() return; } + if(m_pClient->m_Snap.m_SpecInfo.m_Active) + { + m_Active = false; + m_WasActive = false; + return; + } + m_WasActive = true; if (length(m_SelectorMouse) > 140) diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 5c6d9ee77..17c552848 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -45,7 +45,7 @@ void CHud::RenderGameTimer() { char Buf[32]; int Time = 0; - if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit) + if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && !m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer) { Time = m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit*60 - ((Client()->GameTick()-m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick)/Client()->GameTickSpeed()); @@ -62,7 +62,7 @@ void CHud::RenderGameTimer() float FontSize = 10.0f; float w = TextRender()->TextWidth(0, 12,"00:00.0",-1); // last 60 sec red, last 10 sec blink - if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && Time <= 60) + if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && Time <= 60 && !m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer) { float Alpha = Time <= 10 && (2*time_get()/time_freq()) % 2 ? 0.5f : 1.0f; TextRender()->TextColor(1.0f, 0.25f, 0.25f, Alpha); @@ -121,7 +121,9 @@ void CHud::RenderScoreHud() if(GameFlags&GAMEFLAG_FLAGS) { - if(FlagCarrier[t] == FLAG_ATSTAND || (FlagCarrier[t] == FLAG_TAKEN && ((Client()->GameTick()/10)&1))) + int BlinkTimer = (m_pClient->m_FlagDropTick[t] != 0 && + (Client()->GameTick()-m_pClient->m_FlagDropTick[t])/Client()->GameTickSpeed() >= 25) ? 10 : 20; + if(FlagCarrier[t] == FLAG_ATSTAND || (FlagCarrier[t] == FLAG_TAKEN && ((Client()->GameTick()/BlinkTimer)&1))) { // draw flag Graphics()->BlendNormal(); diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index e05746eaa..d2b8c5075 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -219,9 +219,11 @@ void CMapLayers::OnRender() 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); + 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); + 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) { diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index ac7a5115c..929301fba 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -30,6 +30,7 @@ #include #include +#include "countryflags.h" #include "menus.h" #include "skins.h" @@ -214,7 +215,7 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS for(int i = 1; i <= Len; i++) { - if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset + 10 > MxRel) + if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset > MxRel) { s_AtIndex = i - 1; break; @@ -256,6 +257,7 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS { if(!UI()->MouseButton(0)) { + s_AtIndex = min(s_AtIndex, str_length(pStr)); s_DoScroll = false; UI()->SetActiveItem(0); } @@ -745,6 +747,8 @@ void CMenus::OnInit() Console()->Chain("add_favorite", ConchainServerbrowserUpdate, this); Console()->Chain("remove_favorite", ConchainServerbrowserUpdate, this); + Console()->Chain("add_friend", ConchainFriendlistUpdate, this); + Console()->Chain("remove_friend", ConchainFriendlistUpdate, this); // setup load amount m_LoadCurrent = 0; @@ -1072,7 +1076,7 @@ int CMenus::Render() // time left const char *pTimeLeftString; - int TimeLeft = m_DownloadSpeed > 0.0f ? (Client()->MapDownloadTotalsize()-Client()->MapDownloadAmount())/m_DownloadSpeed : 0.0f; + int TimeLeft = max(1, m_DownloadSpeed > 0.0f ? static_cast((Client()->MapDownloadTotalsize()-Client()->MapDownloadAmount())/m_DownloadSpeed) : 1); if(TimeLeft >= 60) { TimeLeft /= 60; @@ -1111,6 +1115,69 @@ int CMenus::Render() if(DoButton_Menu(&s_Button, Localize("Ok"), 0, &Part) || m_EscapePressed || m_EnterPressed) m_Popup = POPUP_FIRST_LAUNCH; } + else if(m_Popup == POPUP_COUNTRY) + { + Box = Screen; + Box.VMargin(150.0f, &Box); + Box.HMargin(150.0f, &Box); + Box.HSplitTop(20.f, &Part, &Box); + Box.HSplitBottom(20.f, &Box, &Part); + Box.HSplitBottom(24.f, &Box, &Part); + Box.HSplitBottom(20.f, &Box, 0); + Box.VMargin(20.0f, &Box); + + static int ActSelection = -2; + if(ActSelection == -2) + ActSelection = g_Config.m_BrFilterCountryIndex; + static float s_ScrollValue = 0.0f; + int OldSelected = -1; + UiDoListboxStart(&s_ScrollValue, &Box, 50.0f, Localize("Country"), "", m_pClient->m_pCountryFlags->Num(), 6, OldSelected, s_ScrollValue); + + 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 == ActSelection) + OldSelected = i; + + CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected == i); + if(Item.m_Visible) + { + CUIRect Label; + Item.m_Rect.Margin(5.0f, &Item.m_Rect); + Item.m_Rect.HSplitBottom(10.0f, &Item.m_Rect, &Label); + float OldWidth = Item.m_Rect.w; + Item.m_Rect.w = Item.m_Rect.h*2; + Item.m_Rect.x += (OldWidth-Item.m_Rect.w)/ 2.0f; + Graphics()->TextureSet(pEntry->m_Texture); + Graphics()->QuadsBegin(); + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + IGraphics::CQuadItem QuadItem(Item.m_Rect.x, Item.m_Rect.y, Item.m_Rect.w, Item.m_Rect.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + UI()->DoLabel(&Label, pEntry->m_aCountryCodeString, 10.0f, 0); + } + } + + const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0); + if(OldSelected != NewSelected) + ActSelection = m_pClient->m_pCountryFlags->GetByIndex(NewSelected)->m_CountryCode; + + Part.VMargin(120.0f, &Part); + + static int s_Button = 0; + if(DoButton_Menu(&s_Button, Localize("Ok"), 0, &Part) || m_EnterPressed) + { + g_Config.m_BrFilterCountryIndex = ActSelection; + Client()->ServerBrowserUpdate(); + m_Popup = POPUP_NONE; + } + + if(m_EscapePressed) + { + ActSelection = g_Config.m_BrFilterCountryIndex; + m_Popup = POPUP_NONE; + } + } else if(m_Popup == POPUP_DELETE_DEMO) { CUIRect Yes, No; @@ -1222,7 +1289,9 @@ int CMenus::Render() // remove friend if(m_FriendlistSelectedIndex >= 0) { - m_pClient->Friends()->RemoveFriend(m_FriendlistSelectedIndex); + m_pClient->Friends()->RemoveFriend(m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aName, + m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aClan); + FriendlistOnUpdate(); Client()->ServerBrowserUpdate(); } } @@ -1299,6 +1368,7 @@ bool CMenus::OnMouseMove(float x, float y) if(!m_MenuActive) return false; + UI()->ConvertMouseMove(&x, &y); m_MousePos.x += x; m_MousePos.y += y; if(m_MousePos.x < 0) m_MousePos.x = 0; @@ -1349,7 +1419,8 @@ void CMenus::OnStateChange(int NewState, int OldState) if(NewState == IClient::STATE_OFFLINE) { - m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0)); + if(OldState >= IClient::STATE_ONLINE) + m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0)); m_Popup = POPUP_NONE; if(Client()->ErrorString() && Client()->ErrorString()[0] != 0) { diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 8988f6110..2f8ca1740 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -100,6 +101,7 @@ class CMenus : public CComponent POPUP_DISCONNECTED, POPUP_PURE, POPUP_LANGUAGE, + POPUP_COUNTRY, POPUP_DELETE_DEMO, POPUP_RENAME_DEMO, POPUP_REMOVE_FRIEND, @@ -205,8 +207,34 @@ class CMenus : public CComponent //void DemolistPopulate(); static int DemolistFetchCallback(const char *pName, int IsDir, int StorageType, void *pUser); + // friends + struct CFriendItem + { + const CFriendInfo *m_pFriendInfo; + int m_NumFound; + + bool operator<(const CFriendItem &Other) + { + if(m_NumFound && !Other.m_NumFound) + return true; + else if(!m_NumFound && Other.m_NumFound) + return false; + else + { + int Result = str_comp(m_pFriendInfo->m_aName, Other.m_pFriendInfo->m_aName); + if(Result) + return Result < 0; + else + return str_comp(m_pFriendInfo->m_aClan, Other.m_pFriendInfo->m_aClan) < 0; + } + } + }; + + sorted_array m_lFriends; int m_FriendlistSelectedIndex; + void FriendlistOnUpdate(); + // found in menus.cpp int Render(); //void render_background(); @@ -228,11 +256,13 @@ class CMenus : public CComponent // found in menus_browser.cpp int m_SelectedIndex; + int m_ScrollOffset; void RenderServerbrowserServerList(CUIRect View); void RenderServerbrowserServerDetail(CUIRect View); void RenderServerbrowserFilters(CUIRect View); void RenderServerbrowserFriends(CUIRect View); void RenderServerbrowser(CUIRect MainView); + static void ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); static void ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); // found in menus_settings.cpp diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index b793fc213..1581eea7d 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -154,6 +154,11 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) int ScrollNum = NumServers-Num+1; if(ScrollNum > 0) { + if(m_ScrollOffset) + { + s_ScrollValue = (float)(m_ScrollOffset)/ScrollNum; + m_ScrollOffset = 0; + } if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && UI()->MouseInside(&View)) s_ScrollValue -= 3.0f/ScrollNum; if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && UI()->MouseInside(&View)) @@ -213,16 +218,14 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) m_SelectedIndex = -1; - for (int i = 0; i < NumServers; i++) - { - const CServerInfo *pItem = ServerBrowser()->SortedGet(i); - NumPlayers += pItem->m_NumPlayers; - } + // reset friend counter + for(int i = 0; i < m_lFriends.size(); m_lFriends[i++].m_NumFound = 0); for (int i = 0; i < NumServers; i++) { int ItemIndex = i; const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex); + NumPlayers += pItem->m_NumPlayers; CUIRect Row; CUIRect SelectHitBox; @@ -234,6 +237,29 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) if(Selected) m_SelectedIndex = i; + // update friend counter + if(pItem->m_FriendState != IFriends::FRIEND_NO) + { + for(int j = 0; j < pItem->m_NumClients; ++j) + { + if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO) + { + unsigned NameHash = str_quickhash(pItem->m_aClients[j].m_aName); + unsigned ClanHash = str_quickhash(pItem->m_aClients[j].m_aClan); + for(int f = 0; f < m_lFriends.size(); ++f) + { + if(ClanHash == m_lFriends[f].m_pFriendInfo->m_ClanHash && + (!m_lFriends[f].m_pFriendInfo->m_aName[0] || NameHash == m_lFriends[f].m_pFriendInfo->m_NameHash)) + { + m_lFriends[f].m_NumFound++; + if(m_lFriends[f].m_pFriendInfo->m_aName[0]) + break; + } + } + } + } + } + // make sure that only those in view can be selected if(Row.y+Row.h > OriginalView.y && Row.y < OriginalView.y+OriginalView.h) { @@ -353,6 +379,15 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } else if(ID == COL_PLAYERS) { + CUIRect Icon; + Button.VMargin(4.0f, &Button); + if(pItem->m_FriendState != IFriends::FRIEND_NO) + { + Button.VSplitLeft(Button.h, &Icon, &Button); + Icon.Margin(2.0f, &Icon); + DoButton_Icon(IMAGE_BROWSEICONS, SPRITE_BROWSE_HEART, &Icon); + } + if(g_Config.m_BrFilterSpectators) str_format(aTemp, sizeof(aTemp), "%i/%i", pItem->m_NumPlayers, pItem->m_MaxPlayers); else @@ -463,7 +498,7 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) g_Config.m_BrFilterFull ^= 1; ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); - if (DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends"), g_Config.m_BrFilterFriends, &Button)) + if (DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends only"), g_Config.m_BrFilterFriends, &Button)) g_Config.m_BrFilterFriends ^= 1; ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); @@ -519,22 +554,58 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) if(DoEditBox(&g_Config.m_BrFilterServerAddress, &Button, g_Config.m_BrFilterServerAddress, sizeof(g_Config.m_BrFilterServerAddress), FontSize, &OffsetAddr)) Client()->ServerBrowserUpdate(); + // player country + { + CUIRect Rect; + ServerFilter.HSplitTop(3.0f, 0, &ServerFilter); + ServerFilter.HSplitTop(26.0f, &Button, &ServerFilter); + Button.VSplitRight(60.0f, &Button, &Rect); + Button.HMargin(3.0f, &Button); + if(DoButton_CheckBox(&g_Config.m_BrFilterCountry, Localize("Player country:"), g_Config.m_BrFilterCountry, &Button)) + g_Config.m_BrFilterCountry ^= 1; + + float OldWidth = Rect.w; + Rect.w = Rect.h*2; + Rect.x += (OldWidth-Rect.w)/2.0f; + Graphics()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(g_Config.m_BrFilterCountryIndex)->m_Texture); + Graphics()->QuadsBegin(); + Graphics()->SetColor(1.0f, 1.0f, 1.0f, g_Config.m_BrFilterCountry?1.0f: 0.5f); + IGraphics::CQuadItem QuadItem(Rect.x, Rect.y, Rect.w, Rect.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + + if(g_Config.m_BrFilterCountry && UI()->DoButtonLogic(&g_Config.m_BrFilterCountryIndex, "", 0, &Rect)) + m_Popup = POPUP_COUNTRY; + } + ServerFilter.HSplitBottom(5.0f, &ServerFilter, 0); ServerFilter.HSplitBottom(ms_ButtonHeight-2.0f, &ServerFilter, &Button); static int s_ClearButton = 0; if(DoButton_Menu(&s_ClearButton, Localize("Reset filter"), 0, &Button)) { + g_Config.m_BrFilterString[0] = 0; g_Config.m_BrFilterFull = 0; g_Config.m_BrFilterEmpty = 0; + g_Config.m_BrFilterSpectators = 0; + g_Config.m_BrFilterFriends = 0; + g_Config.m_BrFilterCountry = 0; + g_Config.m_BrFilterCountryIndex = -1; g_Config.m_BrFilterPw = 0; g_Config.m_BrFilterPing = 999; g_Config.m_BrFilterGametype[0] = 0; + g_Config.m_BrFilterGametypeStrict = 0; g_Config.m_BrFilterServerAddress[0] = 0; +<<<<<<< HEAD g_Config.m_BrFilterCompatversion = 0; g_Config.m_BrFilterString[0] = 0; g_Config.m_BrFilterPure = 0; g_Config.m_BrFilterPureMap = 0; g_Config.m_BrFilterGametypeStrict = 0; +======= + g_Config.m_BrFilterPure = 1; + g_Config.m_BrFilterPureMap = 1; + g_Config.m_BrFilterCompatversion = 1; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe Client()->ServerBrowserUpdate(); } } @@ -622,14 +693,26 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) RenderTools()->DrawUIRect(&ServerScoreBoard, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f); UI()->DoLabelScaled(&ServerHeader, Localize("Scoreboard"), FontSize+2.0f, 0); - if (pSelectedServer) + if(pSelectedServer) { ServerScoreBoard.Margin(3.0f, &ServerScoreBoard); for (int i = 0; i < pSelectedServer->m_NumClients; i++) { CUIRect Name, Clan, Score, Flag; ServerScoreBoard.HSplitTop(25.0f, &Name, &ServerScoreBoard); - RenderTools()->DrawUIRect(&Name, vec4(1,1,1,(i%2+1)*0.05f), CUI::CORNER_ALL, 4.0f); + if(UI()->DoButtonLogic(&pSelectedServer->m_aClients[i], "", 0, &Name)) + { + if(pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_PLAYER) + m_pClient->Friends()->RemoveFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan); + else + m_pClient->Friends()->AddFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan); + FriendlistOnUpdate(); + Client()->ServerBrowserUpdate(); + } + + vec4 Colour = pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_NO ? vec4(1.0f, 1.0f, 1.0f, (i%2+1)*0.05f) : + 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.VSplitRight(34.0f, &Name, &Flag); @@ -691,7 +774,7 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) TextRender()->TextEx(&Cursor, pClan, -1); // flag - Graphics()->TextureSet(m_pClient->m_pCountryFlags->Get(pSelectedServer->m_aClients[i].m_Country)->m_Texture); + Graphics()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(pSelectedServer->m_aClients[i].m_Country)->m_Texture); Graphics()->QuadsBegin(); Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f); IGraphics::CQuadItem QuadItem(Flag.x, Flag.y, Flag.w, Flag.h); @@ -701,45 +784,102 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) } } +void CMenus::FriendlistOnUpdate() +{ + m_lFriends.clear(); + for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i) + { + CFriendItem Item; + Item.m_pFriendInfo = m_pClient->Friends()->GetFriend(i); + Item.m_NumFound = 0; + m_lFriends.add_unsorted(Item); + } + m_lFriends.sort_range(); +} + void CMenus::RenderServerbrowserFriends(CUIRect View) { + static int s_Inited = 0; + if(!s_Inited) + { + FriendlistOnUpdate(); + s_Inited = 1; + } + CUIRect ServerFriends = View, FilterHeader; - const float FontSize = 12.0f; + const float FontSize = 10.0f; // header ServerFriends.HSplitTop(ms_ListheaderHeight, &FilterHeader, &ServerFriends); RenderTools()->DrawUIRect(&FilterHeader, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f); RenderTools()->DrawUIRect(&ServerFriends, vec4(0,0,0,0.15f), 0, 4.0f); - UI()->DoLabelScaled(&FilterHeader, Localize("Friends"), FontSize+2.0f, 0); + UI()->DoLabelScaled(&FilterHeader, Localize("Friends"), FontSize+4.0f, 0); CUIRect Button, List; - ServerFriends.VSplitLeft(5.0f, 0, &ServerFriends); ServerFriends.Margin(3.0f, &ServerFriends); - ServerFriends.VMargin(5.0f, &ServerFriends); + ServerFriends.VMargin(3.0f, &ServerFriends); ServerFriends.HSplitBottom(100.0f, &List, &ServerFriends); // friends list(remove friend) - static int s_FriendList = 0; static float s_ScrollValue = 0; - UiDoListboxStart(&s_FriendList, &List, 40.0f, "", "", m_pClient->Friends()->NumFriends(), 1, m_FriendlistSelectedIndex, s_ScrollValue); + UiDoListboxStart(&m_lFriends, &List, 30.0f, "", "", m_lFriends.size(), 1, m_FriendlistSelectedIndex, s_ScrollValue); - for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i) + m_lFriends.sort_range(); + for(int i = 0; i < m_lFriends.size(); ++i) { - const CFriendInfo *pFriend = m_pClient->Friends()->GetFriend(i); - CListboxItem Item = UiDoListboxNextItem(pFriend); + CListboxItem Item = UiDoListboxNextItem(&m_lFriends[i]); if(Item.m_Visible) { - Item.m_Rect.Margin(2.5f, &Item.m_Rect); - RenderTools()->DrawUIRect(&Item.m_Rect, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_ALL, 4.0f); - Item.m_Rect.Margin(2.5f, &Item.m_Rect); - Item.m_Rect.HSplitTop(14.0f, &Item.m_Rect, &Button); - UI()->DoLabelScaled(&Item.m_Rect, pFriend->m_aName, FontSize, -1); - UI()->DoLabelScaled(&Button, pFriend->m_aClan, FontSize, -1); + Item.m_Rect.Margin(1.5f, &Item.m_Rect); + CUIRect OnState; + Item.m_Rect.VSplitRight(30.0f, &Item.m_Rect, &OnState); + RenderTools()->DrawUIRect(&Item.m_Rect, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_L, 4.0f); + + Item.m_Rect.VMargin(2.5f, &Item.m_Rect); + Item.m_Rect.HSplitTop(12.0f, &Item.m_Rect, &Button); + UI()->DoLabelScaled(&Item.m_Rect, m_lFriends[i].m_pFriendInfo->m_aName, FontSize, -1); + UI()->DoLabelScaled(&Button, m_lFriends[i].m_pFriendInfo->m_aClan, FontSize, -1); + + RenderTools()->DrawUIRect(&OnState, m_lFriends[i].m_NumFound ? vec4(0.0f, 1.0f, 0.0f, 0.25f) : vec4(1.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_R, 4.0f); + OnState.HMargin((OnState.h-FontSize)/3, &OnState); + OnState.VMargin(5.0f, &OnState); + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%i", m_lFriends[i].m_NumFound); + UI()->DoLabelScaled(&OnState, aBuf, FontSize+2, 1); } } - m_FriendlistSelectedIndex = UiDoListboxEnd(&s_ScrollValue, 0); + bool Activated = false; + m_FriendlistSelectedIndex = UiDoListboxEnd(&s_ScrollValue, &Activated); + + // activate found server with friend + if(Activated && !m_EnterPressed && m_lFriends[m_FriendlistSelectedIndex].m_NumFound) + { + bool Found = false; + int NumServers = ServerBrowser()->NumSortedServers(); + for (int i = 0; i < NumServers && !Found; i++) + { + int ItemIndex = m_SelectedIndex != -1 ? (m_SelectedIndex+i+1)%NumServers : i; + const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex); + if(pItem->m_FriendState != IFriends::FRIEND_NO) + { + for(int j = 0; j < pItem->m_NumClients && !Found; ++j) + { + if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO && + str_quickhash(pItem->m_aClients[j].m_aClan) == m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_ClanHash && + (!m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aName[0] || + str_quickhash(pItem->m_aClients[j].m_aName) == m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_NameHash)) + { + str_copy(g_Config.m_UiServerAddress, pItem->m_aAddress, sizeof(g_Config.m_UiServerAddress)); + m_ScrollOffset = ItemIndex; + m_SelectedIndex = ItemIndex; + Found = true; + } + } + } + } + } ServerFriends.HSplitTop(2.5f, 0, &ServerFriends); ServerFriends.HSplitTop(20.0f, &Button, &ServerFriends); @@ -774,10 +914,11 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) ServerFriends.HSplitTop(3.0f, 0, &ServerFriends); ServerFriends.HSplitTop(20.0f, &Button, &ServerFriends); - static int s_RemoveButton = 0; - if(DoButton_Menu(&s_RemoveButton, Localize("Add Friend"), 0, &Button)) + static int s_AddButton = 0; + if(DoButton_Menu(&s_AddButton, Localize("Add Friend"), 0, &Button)) { m_pClient->Friends()->AddFriend(s_aName, s_aClan); + FriendlistOnUpdate(); Client()->ServerBrowserUpdate(); } } @@ -916,6 +1057,16 @@ void CMenus::RenderServerbrowser(CUIRect MainView) } } +void CMenus::ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments() == 2 && ((CMenus *)pUserData)->Client()->State() == IClient::STATE_OFFLINE) + { + ((CMenus *)pUserData)->FriendlistOnUpdate(); + ((CMenus *)pUserData)->Client()->ServerBrowserUpdate(); + } +} + void CMenus::ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) { pfnCallback(pResult, pCallbackUserData, -1); diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index abc9d7d59..682ec0740 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -202,8 +202,10 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) Client()->Disconnect(); // demo name + char aDemoName[64] = {0}; + DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName)); char aBuf[128]; - str_format(aBuf, sizeof(aBuf), Localize("Demofile: %s"), DemoPlayer()->GetDemoName()); + 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; diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index a5af8509a..ed314574d 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -155,7 +155,11 @@ void CMenus::RenderPlayers(CUIRect MainView) static int s_aPlayerIDs[MAX_CLIENTS][2] = {{0}}; for(int i = 0, Count = 0; i < MAX_CLIENTS; ++i) { - if(!m_pClient->m_Snap.m_paPlayerInfos[i] || i == m_pClient->m_Snap.m_LocalClientID) + 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); @@ -165,7 +169,7 @@ void CMenus::RenderPlayers(CUIRect MainView) // player info Player.VSplitLeft(28.0f, &Button, &Player); - CTeeRenderInfo Info = m_pClient->m_aClients[i].m_RenderInfo; + CTeeRenderInfo Info = m_pClient->m_aClients[Index].m_RenderInfo; Info.m_Size = Button.h; RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, EMOTE_NORMAL, vec2(1.0f, 0.0f), vec2(Button.x+Button.h/2, Button.y+Button.h/2)); @@ -174,31 +178,31 @@ void CMenus::RenderPlayers(CUIRect MainView) CTextCursor Cursor; TextRender()->SetCursor(&Cursor, Player.x, Player.y, 14.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = Player.w; - TextRender()->TextEx(&Cursor, m_pClient->m_aClients[i].m_aName, -1); + TextRender()->TextEx(&Cursor, m_pClient->m_aClients[Index].m_aName, -1); TextRender()->SetCursor(&Cursor, Button.x,Button.y, 14.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = Button.w; - TextRender()->TextEx(&Cursor, m_pClient->m_aClients[i].m_aClan, -1); + TextRender()->TextEx(&Cursor, m_pClient->m_aClients[Index].m_aClan, -1); // ignore button ButtonBar.HMargin(2.0f, &ButtonBar); ButtonBar.VSplitLeft(Width, &Button, &ButtonBar); Button.VSplitLeft((Width-Button.h)/4.0f, 0, &Button); Button.VSplitLeft(Button.h, &Button, 0); - if(DoButton_Toggle(&s_aPlayerIDs[i][0], m_pClient->m_aClients[i].m_ChatIgnore, &Button)) - m_pClient->m_aClients[i].m_ChatIgnore ^= 1; + if(DoButton_Toggle(&s_aPlayerIDs[Index][0], m_pClient->m_aClients[Index].m_ChatIgnore, &Button)) + m_pClient->m_aClients[Index].m_ChatIgnore ^= 1; // friend button ButtonBar.VSplitLeft(20.0f, &Button, &ButtonBar); ButtonBar.VSplitLeft(Width, &Button, &ButtonBar); Button.VSplitLeft((Width-Button.h)/4.0f, 0, &Button); Button.VSplitLeft(Button.h, &Button, 0); - if(DoButton_Toggle(&s_aPlayerIDs[i][1], m_pClient->m_aClients[i].m_Friend, &Button)) + if(DoButton_Toggle(&s_aPlayerIDs[Index][1], m_pClient->m_aClients[Index].m_Friend, &Button)) { - if(m_pClient->m_aClients[i].m_Friend) - m_pClient->Friends()->RemoveFriend(m_pClient->m_aClients[i].m_aName, m_pClient->m_aClients[i].m_aClan); + if(m_pClient->m_aClients[Index].m_Friend) + m_pClient->Friends()->RemoveFriend(m_pClient->m_aClients[Index].m_aName, m_pClient->m_aClients[Index].m_aClan); else - m_pClient->Friends()->AddFriend(m_pClient->m_aClients[i].m_aName, m_pClient->m_aClients[i].m_aClan); + m_pClient->Friends()->AddFriend(m_pClient->m_aClients[Index].m_aName, m_pClient->m_aClients[Index].m_aClan); } } @@ -391,13 +395,15 @@ void CMenus::RenderServerControlKick(CUIRect MainView, bool FilterSpectators) static int aPlayerIDs[MAX_CLIENTS]; for(int i = 0; i < MAX_CLIENTS; i++) { - if(!m_pClient->m_Snap.m_paPlayerInfos[i] || i == m_pClient->m_Snap.m_LocalClientID) + if(!m_pClient->m_Snap.m_paInfoByTeam[i]) continue; - if(FilterSpectators && m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) + + int Index = m_pClient->m_Snap.m_paInfoByTeam[i]->m_ClientID; + if(Index == m_pClient->m_Snap.m_LocalClientID || FilterSpectators && m_pClient->m_Snap.m_paInfoByTeam[i]->m_Team == TEAM_SPECTATORS) continue; - if(m_CallvoteSelectedPlayer == i) + if(m_CallvoteSelectedPlayer == Index) Selected = NumOptions; - aPlayerIDs[NumOptions++] = i; + aPlayerIDs[NumOptions++] = Index; } static int s_VoteList = 0; diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index afd63c6c4..c5a7b5d1a 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -210,17 +210,16 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) for(int i = 0; i < m_pClient->m_pCountryFlags->Num(); ++i) { - const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_pCountryFlags->Get(i); - if(pEntry == 0) - continue; - + const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_pCountryFlags->GetByIndex(i); if(pEntry->m_CountryCode == g_Config.m_PlayerCountry) OldSelected = i; CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected == i); if(Item.m_Visible) { - Item.m_Rect.Margin(10.0f, &Item.m_Rect); + CUIRect Label; + Item.m_Rect.Margin(5.0f, &Item.m_Rect); + Item.m_Rect.HSplitBottom(10.0f, &Item.m_Rect, &Label); float OldWidth = Item.m_Rect.w; Item.m_Rect.w = Item.m_Rect.h*2; Item.m_Rect.x += (OldWidth-Item.m_Rect.w)/ 2.0f; @@ -230,13 +229,14 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) IGraphics::CQuadItem QuadItem(Item.m_Rect.x, Item.m_Rect.y, Item.m_Rect.w, Item.m_Rect.h); Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); + UI()->DoLabel(&Label, pEntry->m_aCountryCodeString, 10.0f, 0); } } const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0); if(OldSelected != NewSelected) { - g_Config.m_PlayerCountry = m_pClient->m_pCountryFlags->Get(NewSelected)->m_CountryCode; + g_Config.m_PlayerCountry = m_pClient->m_pCountryFlags->GetByIndex(NewSelected)->m_CountryCode; m_NeedSendinfo = true; } } @@ -761,7 +761,10 @@ void CMenus::RenderSettingsSound(CUIRect MainView) { g_Config.m_SndEnable ^= 1; if(g_Config.m_SndEnable) - m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0)); + { + if(g_Config.m_SndMusic) + m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0)); + } else m_pClient->m_pSounds->Stop(SOUND_MENU); m_NeedRestartSound = g_Config.m_SndEnable && (!s_SndEnable || s_SndRate != g_Config.m_SndRate); @@ -814,10 +817,11 @@ class CLanguage { public: CLanguage() {} - CLanguage(const char *n, const char *f) : m_Name(n), m_FileName(f) {} + CLanguage(const char *n, const char *f, int Code) : m_Name(n), m_FileName(f), m_CountryCode(Code) {} string m_Name; string m_FileName; + int m_CountryCode; bool operator<(const CLanguage &Other) { return m_Name < Other.m_Name; } }; @@ -832,6 +836,7 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array< } char aOrigin[128]; + char aReplacement[128]; CLineReader LineReader; LineReader.Init(File); char *pLine; @@ -841,14 +846,32 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array< continue; str_copy(aOrigin, pLine, sizeof(aOrigin)); - char *pReplacement = LineReader.Get(); - if(!pReplacement) + + pLine = LineReader.Get(); + if(!pLine) { pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", "unexpected end of index file"); break; } - if(pReplacement[0] != '=' || pReplacement[1] != '=' || pReplacement[2] != ' ') + if(pLine[0] != '=' || pLine[1] != '=' || pLine[2] != ' ') + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "malform replacement for index '%s'", aOrigin); + pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf); + (void)LineReader.Get(); + continue; + } + str_copy(aReplacement, pLine+3, sizeof(aReplacement)); + + pLine = LineReader.Get(); + if(!pLine) + { + pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", "unexpected end of index file"); + break; + } + + if(pLine[0] != '=' || pLine[1] != '=' || pLine[2] != ' ') { char aBuf[128]; str_format(aBuf, sizeof(aBuf), "malform replacement for index '%s'", aOrigin); @@ -858,7 +881,7 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array< char aFileName[128]; str_format(aFileName, sizeof(aFileName), "languages/%s.txt", aOrigin); - pLanguages->add(CLanguage(pReplacement+3, aFileName)); + pLanguages->add(CLanguage(aReplacement, aFileName, str_toint(pLine+3))); } io_close(File); } @@ -872,7 +895,7 @@ void CMenus::RenderLanguageSelection(CUIRect MainView) if(s_Languages.size() == 0) { - s_Languages.add(CLanguage("English", "")); + s_Languages.add(CLanguage("English", "", 826)); LoadLanguageIndexfile(Storage(), Console(), &s_Languages); for(int i = 0; i < s_Languages.size(); i++) if(str_comp(s_Languages[i].m_FileName, g_Config.m_ClLanguagefile) == 0) @@ -891,7 +914,19 @@ void CMenus::RenderLanguageSelection(CUIRect MainView) CListboxItem Item = UiDoListboxNextItem(&r.front()); if(Item.m_Visible) - UI()->DoLabelScaled(&Item.m_Rect, r.front().m_Name, 16.0f, -1); + { + CUIRect Rect; + Item.m_Rect.VSplitLeft(Item.m_Rect.h*2.0f, &Rect, &Item.m_Rect); + Rect.VMargin(6.0f, &Rect); + Rect.HMargin(3.0f, &Rect); + Graphics()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(r.front().m_CountryCode)->m_Texture); + Graphics()->QuadsBegin(); + IGraphics::CQuadItem QuadItem(Rect.x, Rect.y, Rect.w, Rect.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + Item.m_Rect.HSplitTop(2.0f, 0, &Item.m_Rect); + UI()->DoLabelScaled(&Item.m_Rect, r.front().m_Name, 16.0f, -1); + } } s_SelectedLanguage = UiDoListboxEnd(&s_ScrollValue, 0); diff --git a/src/game/client/components/scoreboard.cpp b/src/game/client/components/scoreboard.cpp index 1ef6fa70b..7c6fc5365 100644 --- a/src/game/client/components/scoreboard.cpp +++ b/src/game/client/components/scoreboard.cpp @@ -299,7 +299,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch TextRender()->TextEx(&Cursor, m_pClient->m_aClients[pInfo->m_ClientID].m_aClan, -1); // country flag - Graphics()->TextureSet(m_pClient->m_pCountryFlags->Get(m_pClient->m_aClients[pInfo->m_ClientID].m_Country)->m_Texture); + Graphics()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(m_pClient->m_aClients[pInfo->m_ClientID].m_Country)->m_Texture); Graphics()->QuadsBegin(); Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f); IGraphics::CQuadItem QuadItem(CountryOffset, y+(Spacing+TeeSizeMod*5.0f)/2.0f, CountryLength, LineHeight-Spacing-TeeSizeMod*5.0f); diff --git a/src/game/client/components/sounds.cpp b/src/game/client/components/sounds.cpp index 0b91d1cea..2fb3871bc 100644 --- a/src/game/client/components/sounds.cpp +++ b/src/game/client/components/sounds.cpp @@ -68,8 +68,17 @@ void CSounds::OnInit() void CSounds::OnReset() { - Sound()->StopAll(); - ClearQueue(); + if(Client()->State() >= IClient::STATE_ONLINE) + { + Sound()->StopAll(); + ClearQueue(); + } +} + +void CSounds::OnStateChange(int NewState, int OldState) +{ + if(NewState == IClient::STATE_ONLINE || NewState == IClient::STATE_DEMOPLAYBACK) + OnReset(); } void CSounds::OnRender() @@ -131,7 +140,7 @@ void CSounds::PlayAndRecord(int Chn, int SetId, float Vol, vec2 Pos) void CSounds::Play(int Chn, int SetId, float Vol, vec2 Pos) { - if(!g_Config.m_SndEnable || (Chn == CHN_MUSIC && !g_Config.m_SndMusic) || m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds) + if(!g_Config.m_SndEnable || !Sound()->IsSoundEnabled() || (Chn == CHN_MUSIC && !g_Config.m_SndMusic) || m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds) return; CDataSoundset *pSet = &g_pData->m_aSounds[SetId]; diff --git a/src/game/client/components/sounds.h b/src/game/client/components/sounds.h index 2670f793c..ab9cc8e6a 100644 --- a/src/game/client/components/sounds.h +++ b/src/game/client/components/sounds.h @@ -32,6 +32,7 @@ public: virtual void OnInit(); virtual void OnReset(); + virtual void OnStateChange(int NewState, int OldState); virtual void OnRender(); void ClearQueue(); diff --git a/src/game/client/components/spectator.cpp b/src/game/client/components/spectator.cpp index e078e5b65..231df309c 100644 --- a/src/game/client/components/spectator.cpp +++ b/src/game/client/components/spectator.cpp @@ -139,6 +139,7 @@ bool CSpectator::OnMouseMove(float x, float y) if(!m_Active) return false; + UI()->ConvertMouseMove(&x, &y); m_SelectorMouse += vec2(x,y); return true; } @@ -161,6 +162,13 @@ void CSpectator::OnRender() return; } + if(!m_pClient->m_Snap.m_SpecInfo.m_Active) + { + m_Active = false; + m_WasActive = false; + return; + } + m_WasActive = true; m_SelectedSpectatorID = NO_SELECTION; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 2c7ec06cd..8394ffb26 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -98,17 +98,6 @@ void CGameClient::CStack::Add(class CComponent *pComponent) { m_paComponents[m_N 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); } -int CGameClient::GetCountryIndex(int Code) -{ - int Index = g_GameClient.m_pCountryFlags->Find(Code); - if(Index < 0) - { - Index = g_GameClient.m_pCountryFlags->Find(-1); - if(Index < 0) - Index = 0; - } - return Index; -} void CGameClient::OnConsoleInit() { @@ -202,6 +191,7 @@ void CGameClient::OnConsoleInit() Console()->Register("kill", "", CFGFLAG_CLIENT, ConKill, this, "Kill yourself", IConsole::CONSOLELEVEL_USER); // register server dummy commands for tab completion +<<<<<<< HEAD Console()->Register("tune", "si", CFGFLAG_SERVER, ConServerDummy, 0, "Tune variable to value", IConsole::CONSOLELEVEL_USER); Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConServerDummy, 0, "Reset tuning", IConsole::CONSOLELEVEL_USER); Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConServerDummy, 0, "Dump tuning", IConsole::CONSOLELEVEL_USER); @@ -216,6 +206,22 @@ void CGameClient::OnConsoleInit() Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, ConServerDummy, 0, "Force a voting option", IConsole::CONSOLELEVEL_USER); Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConServerDummy, 0, "Clears the voting options", IConsole::CONSOLELEVEL_USER); Console()->Register("vote", "r", CFGFLAG_SERVER, ConServerDummy, 0, "Force a vote to yes/no", IConsole::CONSOLELEVEL_USER); +======= + Console()->Register("tune", "si", CFGFLAG_SERVER, 0, 0, "Tune variable to value"); + Console()->Register("tune_reset", "", CFGFLAG_SERVER, 0, 0, "Reset tuning"); + Console()->Register("tune_dump", "", CFGFLAG_SERVER, 0, 0, "Dump tuning"); + Console()->Register("change_map", "?r", CFGFLAG_SERVER, 0, 0, "Change map"); + Console()->Register("restart", "?i", CFGFLAG_SERVER, 0, 0, "Restart in x seconds"); + Console()->Register("broadcast", "r", CFGFLAG_SERVER, 0, 0, "Broadcast message"); + Console()->Register("say", "r", CFGFLAG_SERVER, 0, 0, "Say in chat"); + Console()->Register("set_team", "ii?i", CFGFLAG_SERVER, 0, 0, "Set team of player to team"); + Console()->Register("set_team_all", "i", CFGFLAG_SERVER, 0, 0, "Set team of all players to team"); + Console()->Register("add_vote", "sr", CFGFLAG_SERVER, 0, 0, "Add a voting option"); + Console()->Register("remove_vote", "s", CFGFLAG_SERVER, 0, 0, "remove a voting option"); + Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, 0, 0, "Force a voting option"); + Console()->Register("clear_votes", "", CFGFLAG_SERVER, 0, 0, "Clears the voting options"); + Console()->Register("vote", "r", CFGFLAG_SERVER, 0, 0, "Force a vote to yes/no"); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe // propagate pointers @@ -371,9 +377,15 @@ void CGameClient::OnReset() m_All.m_paComponents[i]->OnReset(); m_DemoSpecID = SPEC_FREEVIEW; +<<<<<<< HEAD m_Teams.Reset(); m_DDRaceMsgSent = false; +======= + m_FlagDropTick[TEAM_RED] = 0; + m_FlagDropTick[TEAM_BLUE] = 0; + m_Tuning = CTuningParams(); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } @@ -749,7 +761,7 @@ void CGameClient::OnNewSnapshot() int ClientID = Item.m_ID; IntsToStr(&pInfo->m_Name0, 4, m_aClients[ClientID].m_aName); IntsToStr(&pInfo->m_Clan0, 3, m_aClients[ClientID].m_aClan); - m_aClients[ClientID].m_Country = GetCountryIndex(pInfo->m_Country); + m_aClients[ClientID].m_Country = pInfo->m_Country; IntsToStr(&pInfo->m_Skin0, 6, m_aClients[ClientID].m_aSkinName); m_aClients[ClientID].m_UseCustomColor = pInfo->m_UseCustomColor; @@ -847,6 +859,20 @@ void CGameClient::OnNewSnapshot() { m_Snap.m_pGameDataObj = (const CNetObj_GameData *)pData; m_Snap.m_GameDataSnapID = Item.m_ID; + if(m_Snap.m_pGameDataObj->m_FlagCarrierRed == FLAG_TAKEN) + { + if(m_FlagDropTick[TEAM_RED] == 0) + m_FlagDropTick[TEAM_RED] = Client()->GameTick(); + } + else if(m_FlagDropTick[TEAM_RED] != 0) + m_FlagDropTick[TEAM_RED] = 0; + if(m_Snap.m_pGameDataObj->m_FlagCarrierBlue == FLAG_TAKEN) + { + if(m_FlagDropTick[TEAM_BLUE] == 0) + m_FlagDropTick[TEAM_BLUE] = Client()->GameTick(); + } + else if(m_FlagDropTick[TEAM_BLUE] != 0) + m_FlagDropTick[TEAM_BLUE] = 0; } else if(Item.m_Type == NETOBJTYPE_FLAG) m_Snap.m_paFlags[Item.m_ID%2] = (const CNetObj_Flag *)pData; @@ -909,6 +935,17 @@ void CGameClient::OnNewSnapshot() } } } + // sort player infos by team + int Teams[3] = { TEAM_RED, TEAM_BLUE, TEAM_SPECTATORS }; + int Index = 0; + for(int Team = 0; Team < 3; ++Team) + { + for(int i = 0; i < MAX_CLIENTS && Index < MAX_CLIENTS; ++i) + { + if(m_Snap.m_paPlayerInfos[i] && m_Snap.m_paPlayerInfos[i]->m_Team == Teams[Team]) + m_Snap.m_paInfoByTeam[Index++] = m_Snap.m_paPlayerInfos[i]; + } + } CTuningParams StandardTuning; CServerInfo CurrentServerInfo; diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 65b5c790b..19c93d48c 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -91,6 +91,7 @@ public: bool m_SuppressEvents; bool m_NewTick; bool m_NewPredictedTick; + int m_FlagDropTick[2]; // TODO: move this CTuningParams m_Tuning; @@ -126,6 +127,7 @@ public: const CNetObj_PlayerInfo *m_paPlayerInfos[MAX_CLIENTS]; const CNetObj_PlayerInfo *m_paInfoByScore[MAX_CLIENTS]; + const CNetObj_PlayerInfo *m_paInfoByTeam[MAX_CLIENTS]; int m_LocalClientID; int m_NumPlayers; @@ -217,7 +219,6 @@ public: virtual void OnStartGame(); virtual const char *GetItemName(int Type); - virtual int GetCountryIndex(int Code); virtual const char *Version(); virtual const char *NetVersion(); diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp index 29b891c21..2de85d667 100644 --- a/src/game/client/lineinput.cpp +++ b/src/game/client/lineinput.cpp @@ -42,7 +42,7 @@ bool CLineInput::Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *p if (Len < StrMaxSize - CharSize && CursorPos < StrMaxSize - CharSize) { - mem_move(pStr + CursorPos + CharSize, pStr + CursorPos, Len - CursorPos + CharSize); + 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; diff --git a/src/game/client/render.h b/src/game/client/render.h index 5ebee9aac..a9a5d2a7f 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -39,6 +39,7 @@ enum TILERENDERFLAG_EXTEND=4, }; +typedef void (*ENVELOPE_EVAL)(float TimeOffset, int Env, float *pChannels, void *pUser); class CRenderTools { @@ -70,8 +71,8 @@ public: // 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, void (*pfnEval)(float TimeOffset, int Env, float *pChannels, void *pUser), void *pUser); - void RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int Flags); + void RenderQuads(CQuad *pQuads, int NumQuads, int Flags, ENVELOPE_EVAL pfnEval, void *pUser); + void RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset); // helpers void MapscreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY, diff --git a/src/game/client/render_map.cpp b/src/game/client/render_map.cpp index 052c60e02..ebc79e094 100644 --- a/src/game/client/render_map.cpp +++ b/src/game/client/render_map.cpp @@ -83,7 +83,7 @@ static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation) pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y); } -void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, void (*pfnEval)(float TimeOffset, int Env, float *pChannels, void *pUser), void *pUser) +void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser) { if(g_Config.m_ClShowEntities && g_Config.m_ClDDRaceCheats) return; @@ -170,7 +170,8 @@ void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, voi Graphics()->QuadsEnd(); } -void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int RenderFlags) +void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int RenderFlags, + ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset) { //Graphics()->TextureSet(img_get(tmap->image)); float ScreenX0, ScreenY0, ScreenX1, ScreenY1; @@ -182,8 +183,19 @@ void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 float FinalTileSize = Scale/(ScreenX1-ScreenX0) * Graphics()->ScreenWidth(); float FinalTilesetScale = FinalTileSize/TilePixelSize; + float r=1, g=1, b=1, a=1; + if(ColorEnv >= 0) + { + float aChannels[4]; + pfnEval(ColorEnvOffset/1000.0f, ColorEnv, aChannels, pUser); + r = aChannels[0]; + g = aChannels[1]; + b = aChannels[2]; + a = aChannels[3]; + } + Graphics()->QuadsBegin(); - Graphics()->SetColor(Color.r, Color.g, Color.b, Color.a); + Graphics()->SetColor(Color.r*r, Color.g*g, Color.b*b, Color.a*a); int StartY = (int)(ScreenY0/Scale)-1; int StartX = (int)(ScreenX0/Scale)-1; diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 2161bc771..00a30c150 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -53,6 +53,13 @@ int CUI::MouseInside(const CUIRect *r) return 0; } +void CUI::ConvertMouseMove(float *x, float *y) +{ + float Fac = (float)(g_Config.m_UiMousesens)/g_Config.m_InpMousesens; + *x = *x*Fac; + *y = *y*Fac; +} + CUIRect *CUI::Screen() { float Aspect = Graphics()->ScreenAspect(); diff --git a/src/game/client/ui.h b/src/game/client/ui.h index 017abf7c4..7cd78d6f9 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -79,6 +79,7 @@ public: const void *LastActiveItem() const { return m_pLastActiveItem; } int MouseInside(const CUIRect *pRect); + void ConvertMouseMove(float *x, float *y); CUIRect *Screen(); void ClipEnable(const CUIRect *pRect); diff --git a/src/game/editor/auto_map.cpp b/src/game/editor/auto_map.cpp new file mode 100644 index 000000000..528e459bf --- /dev/null +++ b/src/game/editor/auto_map.cpp @@ -0,0 +1,202 @@ +#include // sscanf + +#include +#include +#include + +#include "auto_map.h" +#include "editor.h" + +CAutoMapper::CAutoMapper(CEditor *pEditor) +{ + m_pEditor = pEditor; + m_FileLoaded = false; +} + +void CAutoMapper::Load(const char* pTileName) +{ + char aPath[256]; + str_format(aPath, sizeof(aPath), "editor/%s.rules", pTileName); + IOHANDLE RulesFile = m_pEditor->Storage()->OpenFile(aPath, IOFLAG_READ, IStorage::TYPE_ALL); + if(!RulesFile) + return; + + CLineReader LineReader; + LineReader.Init(RulesFile); + + CConfiguration *pCurrentConf = 0; + CIndexRule *pCurrentIndex = 0; + + char aBuf[256]; + + // read each line + while(char *pLine = LineReader.Get()) + { + // skip blank/empty lines as well as comments + if(str_length(pLine) > 0 && pLine[0] != '#' && pLine[0] != '\n' && pLine[0] != '\r' + && pLine[0] != '\t' && pLine[0] != '\v' && pLine[0] != ' ') + { + if(pLine[0]== '[') + { + // new configuration, get the name + pLine++; + + CConfiguration NewConf; + int ID = m_lConfigs.add(NewConf); + pCurrentConf = &m_lConfigs[ID]; + + str_copy(pCurrentConf->m_aName, pLine, str_length(pLine)); + } + else + { + if(!str_comp_num(pLine, "Index", 5)) + { + // new index + int ID = 0; + char aFlip[128] = ""; + + sscanf(pLine, "Index %d %127s", &ID, aFlip); + + CIndexRule NewIndexRule; + NewIndexRule.m_ID = ID; + NewIndexRule.m_Flag = 0; + NewIndexRule.m_RandomValue = 0; + NewIndexRule.m_BaseTile = false; + + if(str_length(aFlip) > 0) + { + if(!str_comp(aFlip, "XFLIP")) + NewIndexRule.m_Flag = TILEFLAG_VFLIP; + else if(!str_comp(aFlip, "YFLIP")) + NewIndexRule.m_Flag = TILEFLAG_HFLIP; + } + + // add the index rule object and make it current + int ArrayID = pCurrentConf->m_aIndexRules.add(NewIndexRule); + pCurrentIndex = &pCurrentConf->m_aIndexRules[ArrayID]; + } + else if(!str_comp_num(pLine, "BaseTile", 8) && pCurrentIndex) + { + pCurrentIndex->m_BaseTile = true; + } + else if(!str_comp_num(pLine, "Pos", 3) && pCurrentIndex) + { + int x = 0, y = 0; + char aValue[128]; + int Value = CPosRule::EMPTY; + bool IndexValue = false; + + sscanf(pLine, "Pos %d %d %127s", &x, &y, aValue); + + if(!str_comp(aValue, "FULL")) + Value = CPosRule::FULL; + else if(!str_comp_num(aValue, "INDEX", 5)) + { + sscanf(pLine, "Pos %*d %*d INDEX %d", &Value); + IndexValue = true; + } + + CPosRule NewPosRule = {x, y, Value, IndexValue}; + pCurrentIndex->m_aRules.add(NewPosRule); + } + else if(!str_comp_num(pLine, "Random", 6) && pCurrentIndex) + { + sscanf(pLine, "Random %d", &pCurrentIndex->m_RandomValue); + } + } + } + } + + io_close(RulesFile); + + str_format(aBuf, sizeof(aBuf),"loaded %s", aPath); + m_pEditor->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor", aBuf); + + m_FileLoaded = true; +} + +const char* CAutoMapper::GetConfigName(int Index) +{ + if(Index < 0 || Index >= m_lConfigs.size()) + return ""; + + return m_lConfigs[Index].m_aName; +} + +void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID) +{ + if(!m_FileLoaded || pLayer->m_Readonly || ConfigID < 0 || ConfigID >= m_lConfigs.size()) + return; + + CConfiguration *pConf = &m_lConfigs[ConfigID]; + + if(!pConf->m_aIndexRules.size()) + return; + + int BaseTile = 1; + + // find base tile if there is one + for(int i = 0; i < pConf->m_aIndexRules.size(); ++i) + { + if(pConf->m_aIndexRules[i].m_BaseTile) + { + BaseTile = pConf->m_aIndexRules[i].m_ID; + break; + } + } + + // auto map ! + int MaxIndex = 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 *pTile = &(pLayer->m_pTiles[y*pLayer->m_Width+x]); + if(pTile->m_Index == 0) + continue; + + 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) + continue; + + for(int i = 0; i < pConf->m_aIndexRules.size(); ++i) + { + if(pConf->m_aIndexRules[i].m_BaseTile) + continue; + + bool RespectRules = true; + for(int j = 0; j < pConf->m_aIndexRules[i].m_aRules.size() && RespectRules; ++j) + { + CPosRule *pRule = &pConf->m_aIndexRules[i].m_aRules[j]; + int CheckIndex = (y+pRule->m_Y)*pLayer->m_Width+(x+pRule->m_X); + + if(CheckIndex < 0 || CheckIndex >= MaxIndex) + RespectRules = false; + else + { + if(pRule->m_IndexValue) + { + if(pLayer->m_pTiles[CheckIndex].m_Index != pRule->m_Value) + RespectRules = false; + } + else + { + if(pLayer->m_pTiles[CheckIndex].m_Index > 0 && pRule->m_Value == CPosRule::EMPTY) + RespectRules = false; + + if(pLayer->m_pTiles[CheckIndex].m_Index == 0 && pRule->m_Value == CPosRule::FULL) + RespectRules = false; + } + } + } + + if(RespectRules && + (pConf->m_aIndexRules[i].m_RandomValue <= 1 || (int)((float)rand() / ((float)RAND_MAX + 1) * pConf->m_aIndexRules[i].m_RandomValue) == 1)) + { + pTile->m_Index = pConf->m_aIndexRules[i].m_ID; + pTile->m_Flags = pConf->m_aIndexRules[i].m_Flag; + } + } + } +} diff --git a/src/game/editor/auto_map.h b/src/game/editor/auto_map.h new file mode 100644 index 000000000..ee5703784 --- /dev/null +++ b/src/game/editor/auto_map.h @@ -0,0 +1,54 @@ +#ifndef GAME_EDITOR_AUTO_MAP_H +#define GAME_EDITOR_AUTO_MAP_H + +#include + +class CAutoMapper +{ + struct CPosRule + { + int m_X; + int m_Y; + int m_Value; + bool m_IndexValue; + + enum + { + EMPTY=0, + FULL + }; + }; + + struct CIndexRule + { + int m_ID; + array m_aRules; + int m_Flag; + int m_RandomValue; + bool m_BaseTile; + }; + + struct CConfiguration + { + array m_aIndexRules; + char m_aName[128]; + }; + +public: + CAutoMapper(class CEditor *pEditor); + + void Load(const char* pTileName); + void Proceed(class CLayerTiles *pLayer, int ConfigID); + + int ConfigNamesNum() { return m_lConfigs.size(); } + const char* GetConfigName(int Index); + + const bool IsLoaded() { return m_FileLoaded; } +private: + array m_lConfigs; + class CEditor *m_pEditor; + bool m_FileLoaded; +}; + + +#endif diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index d4b459fc6..adf330381 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -10,20 +10,20 @@ #include #include #include -#include #include #include #include +#include -#include #include +#include +#include #include +#include #include +#include "auto_map.h" #include "editor.h" -#include - -#include int CEditor::ms_CheckerTexture; int CEditor::ms_BackgroundTexture; @@ -48,9 +48,10 @@ CEditorImage::~CEditorImage() CLayerGroup::CLayerGroup() { - m_pName = ""; + m_aName[0] = 0; m_Visible = true; m_SaveToMap = true; + m_Collapse = false; m_GameGroup = false; m_OffsetX = 0; m_OffsetY = 0; @@ -196,6 +197,24 @@ void CEditorImage::AnalyseTileFlags() } +void CEditor::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser) +{ + CEditor *pThis = (CEditor *)pUser; + if(Env < 0 || Env >= pThis->m_Map.m_lEnvelopes.size()) + { + pChannels[0] = 0; + pChannels[1] = 0; + pChannels[2] = 0; + pChannels[3] = 0; + return; + } + + CEnvelope *e = pThis->m_Map.m_lEnvelopes[Env]; + float t = pThis->m_AnimateTime+TimeOffset; + t *= pThis->m_AnimateSpeed; + e->Eval(t, pChannels); +} + /******************************************************** OTHER *********************************************************/ @@ -203,32 +222,59 @@ void CEditorImage::AnalyseTileFlags() // copied from gc_menu.cpp, should be more generalized //extern int ui_do_edit_box(void *id, const CUIRect *rect, char *str, int str_size, float font_size, bool hidden=false); -int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden) +int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *Offset, bool Hidden, int Corners) { - int Inside = UI()->MouseInside(pRect); + int Inside = UI()->MouseInside(pRect); bool ReturnValue = false; + bool UpdateOffset = false; static int s_AtIndex = 0; + static bool s_DoScroll = false; + static float s_ScrollStart = 0.0f; + + FontSize *= UI()->Scale(); if(UI()->LastActiveItem() == pID) { int Len = str_length(pStr); + if(Len == 0) + s_AtIndex = 0; if(Inside && UI()->MouseButton(0)) { + s_DoScroll = true; + s_ScrollStart = UI()->MouseX(); int MxRel = (int)(UI()->MouseX() - pRect->x); - for (int i = 1; i <= Len; i++) + for(int i = 1; i <= Len; i++) { - if (TextRender()->TextWidth(0, FontSize, pStr, i) + 10 > MxRel) + if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset > MxRel) { s_AtIndex = i - 1; break; } - if (i == Len) + if(i == Len) s_AtIndex = Len; } } + else if(!UI()->MouseButton(0)) + s_DoScroll = false; + else if(s_DoScroll) + { + // do scrolling + if(UI()->MouseX() < pRect->x && s_ScrollStart-UI()->MouseX() > 10.0f) + { + s_AtIndex = max(0, s_AtIndex-1); + s_ScrollStart = UI()->MouseX(); + UpdateOffset = true; + } + else if(UI()->MouseX() > pRect->x+pRect->w && UI()->MouseX()-s_ScrollStart > 10.0f) + { + s_AtIndex = min(Len, s_AtIndex+1); + s_ScrollStart = UI()->MouseX(); + UpdateOffset = true; + } + } for(int i = 0; i < Input()->NumEvents(); i++) { @@ -242,7 +288,11 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str if(UI()->ActiveItem() == pID) { if(!UI()->MouseButton(0)) + { + s_AtIndex = min(s_AtIndex, str_length(pStr)); + s_DoScroll = false; UI()->SetActiveItem(0); + } } else if(UI()->HotItem() == pID) { @@ -258,8 +308,8 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str UI()->SetHotItem(pID); CUIRect Textbox = *pRect; - RenderTools()->DrawUIRect(&Textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 3.0f); - Textbox.VMargin(3.0f, &Textbox); + RenderTools()->DrawUIRect(&Textbox, vec4(1, 1, 1, 0.5f), Corners, 3.0f); + Textbox.VMargin(2.0f, &Textbox); const char *pDisplayStr = pStr; char aStars[128]; @@ -275,19 +325,47 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str pDisplayStr = aStars; } + // check if the text has to be moved + if(UI()->LastActiveItem() == pID && !JustGotActive && (UpdateOffset || Input()->NumEvents())) + { + float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex); + if(w-*Offset > Textbox.w) + { + // move to the left + float wt = TextRender()->TextWidth(0, FontSize, pDisplayStr, -1); + do + { + *Offset += min(wt-*Offset-Textbox.w, Textbox.w/3); + } + while(w-*Offset > Textbox.w); + } + else if(w-*Offset < 0.0f) + { + // move to the right + do + { + *Offset = max(0.0f, *Offset-Textbox.w/3); + } + while(w-*Offset < 0.0f); + } + } + UI()->ClipEnable(pRect); + Textbox.x -= *Offset; + UI()->DoLabel(&Textbox, pDisplayStr, FontSize, -1); - - //TODO: make it blink + + // render the cursor if(UI()->LastActiveItem() == pID && !JustGotActive) { float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex); Textbox = *pRect; Textbox.VSplitLeft(2.0f, 0, &Textbox); - Textbox.x += w*UI()->Scale(); - Textbox.y -= FontSize/10.f; - - UI()->DoLabel(&Textbox, "|", FontSize*1.1f, -1); + Textbox.x += (w-*Offset-TextRender()->TextWidth(0, FontSize, "|", -1)/2); + + if((2*time_get()/time_freq()) % 2) // make it blink + UI()->DoLabel(&Textbox, "|", FontSize, -1); } + UI()->ClipDisable(); return ReturnValue; } @@ -450,12 +528,12 @@ int CEditor::DoButton_Tab(const void *pID, const char *pText, int Checked, const return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners) +int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize) { RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), Corners, 3.0f); CUIRect NewRect = *pRect; - NewRect.y += NewRect.h/2.0f-7.0f; - UI()->DoLabel(&NewRect, pText, 10, 0, -1); + NewRect.HMargin(NewRect.h/2.0f-FontSize/2.0f-1.0f, &NewRect); + UI()->DoLabel(&NewRect, pText, FontSize, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } @@ -473,6 +551,49 @@ int CEditor::DoButton_ButtonDec(const void *pID, const char *pText, int Checked, return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } +void CEditor::RenderGrid(CLayerGroup *pGroup) +{ + if(!m_GridActive) + return; + + float aGroupPoints[4]; + pGroup->Mapping(aGroupPoints); + + float w = UI()->Screen()->w; + float h = UI()->Screen()->h; + + int LineDistance = GetLineDistance(); + + int XOffset = aGroupPoints[0]/LineDistance; + int YOffset = aGroupPoints[1]/LineDistance; + int XGridOffset = XOffset % m_GridFactor; + int YGridOffset = YOffset % m_GridFactor; + + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); + + for(int i = 0; i < (int)w; i++) + { + if((i+YGridOffset) % m_GridFactor == 0) + Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f); + else + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f); + + IGraphics::CLineItem Line = IGraphics::CLineItem(LineDistance*XOffset, LineDistance*i+LineDistance*YOffset, w+aGroupPoints[2], LineDistance*i+LineDistance*YOffset); + Graphics()->LinesDraw(&Line, 1); + + if((i+XGridOffset) % m_GridFactor == 0) + Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f); + else + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f); + + Line = IGraphics::CLineItem(LineDistance*i+LineDistance*XOffset, LineDistance*YOffset, LineDistance*i+LineDistance*XOffset, h+aGroupPoints[3]); + Graphics()->LinesDraw(&Line, 1); + } + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + Graphics()->LinesEnd(); +} + void CEditor::RenderBackground(CUIRect View, int Texture, float Size, float Brightness) { Graphics()->TextureSet(Texture); @@ -747,13 +868,6 @@ void CEditor::DoToolbar(CUIRect ToolBar) m_AnimateSpeed -= 0.5f; } - if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && m_Dialog == DIALOG_NONE) - m_ZoomLevel -= 20; - - if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && m_Dialog == DIALOG_NONE) - m_ZoomLevel += 20; - - m_ZoomLevel = clamp(m_ZoomLevel, 50, 2000); m_WorldZoom = m_ZoomLevel/100.0f; TB_Top.VSplitLeft(10.0f, &Button, &TB_Top); @@ -857,6 +971,7 @@ void CEditor::DoToolbar(CUIRect ToolBar) { if(pT) DoMapBorder(); +<<<<<<< HEAD } // do tele button TB_Bottom.VSplitLeft(5.0f, &Button, &TB_Bottom); @@ -886,6 +1001,8 @@ void CEditor::DoToolbar(CUIRect ToolBar) { static int s_SwitchPopupID = 0; UiInvokePopupMenu(&s_SwitchPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 36, PopupSwitch); +======= +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } } @@ -899,6 +1016,41 @@ void CEditor::DoToolbar(CUIRect ToolBar) m_WorldOffsetX = 0; m_WorldOffsetY = 0; } + + TB_Bottom.VSplitLeft(5.0f, 0, &TB_Bottom); + + // grid button + TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom); + static int s_GridButton = 0; + if(DoButton_Editor(&s_GridButton, "Grid", m_GridActive, &Button, 0, "Toggle Grid")) + { + m_GridActive = !m_GridActive; + } + + TB_Bottom.VSplitLeft(30.0f, 0, &TB_Bottom); + + // grid zoom + TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom); + static int s_GridIncreaseButton = 0; + if(DoButton_Ex(&s_GridIncreaseButton, "G-", 0, &Button, 0, "Decrease grid", CUI::CORNER_L)) + { + if(m_GridFactor > 1) + m_GridFactor--; + } + + TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom); + static int s_GridNormalButton = 0; + if(DoButton_Ex(&s_GridNormalButton, "1", 0, &Button, 0, "Normal grid", 0)) + m_GridFactor = 1; + + TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom); + + static int s_GridDecreaseButton = 0; + if(DoButton_Ex(&s_GridDecreaseButton, "G+", 0, &Button, 0, "Increase grid", CUI::CORNER_R)) + { + if(m_GridFactor < 15) + m_GridFactor++; + } } static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation) @@ -949,27 +1101,82 @@ void CEditor::DoQuad(CQuad *q, int Index) if(UI()->ActiveItem() == pID) { - // check if we only should move pivot - if(s_Operation == OP_MOVE_PIVOT) + if(m_MouseDeltaWx*m_MouseDeltaWx+m_MouseDeltaWy*m_MouseDeltaWy > 0.5f) { - q->m_aPoints[4].x += f2fx(wx-s_LastWx); - q->m_aPoints[4].y += f2fx(wy-s_LastWy); - } - else if(s_Operation == OP_MOVE_ALL) - { - // move all points including pivot - for(int v = 0; v < 5; v++) + // check if we only should move pivot + if(s_Operation == OP_MOVE_PIVOT) { - q->m_aPoints[v].x += f2fx(wx-s_LastWx); - q->m_aPoints[v].y += f2fx(wy-s_LastWy); + if(m_GridActive) + { + int LineDistance = GetLineDistance(); + + float x = 0.0f; + float y = 0.0f; + if(wx >= 0) + x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + else + x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + if(wy >= 0) + y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + else + y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + + q->m_aPoints[4].x = f2fx(x); + q->m_aPoints[4].y = f2fx(y); + } + else + { + q->m_aPoints[4].x += f2fx(wx-s_LastWx); + q->m_aPoints[4].y += f2fx(wy-s_LastWy); + } } - } - else if(s_Operation == OP_ROTATE) - { - for(int v = 0; v < 4; v++) + else if(s_Operation == OP_MOVE_ALL) { - q->m_aPoints[v] = s_RotatePoints[v]; - Rotate(&q->m_aPoints[4], &q->m_aPoints[v], s_RotateAngle); + // move all points including pivot + if(m_GridActive) + { + int LineDistance = GetLineDistance(); + + float x = 0.0f; + float y = 0.0f; + if(wx >= 0) + x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + else + x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + if(wy >= 0) + y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + else + y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + + int OldX = q->m_aPoints[4].x; + int OldY = q->m_aPoints[4].y; + q->m_aPoints[4].x = f2fx(x); + q->m_aPoints[4].y = f2fx(y); + int DiffX = q->m_aPoints[4].x - OldX; + int DiffY = q->m_aPoints[4].y - OldY; + + for(int v = 0; v < 4; v++) + { + q->m_aPoints[v].x += DiffX; + q->m_aPoints[v].y += DiffY; + } + } + else + { + for(int v = 0; v < 5; v++) + { + q->m_aPoints[v].x += f2fx(wx-s_LastWx); + q->m_aPoints[v].y += f2fx(wy-s_LastWy); + } + } + } + else if(s_Operation == OP_ROTATE) + { + for(int v = 0; v < 4; v++) + { + q->m_aPoints[v] = s_RotatePoints[v]; + Rotate(&q->m_aPoints[4], &q->m_aPoints[v], s_RotateAngle); + } } } @@ -1025,6 +1232,8 @@ void CEditor::DoQuad(CQuad *q, int Index) s_Operation = OP_MOVE_ALL; UI()->SetActiveItem(pID); + if(m_SelectedQuad != Index) + m_SelectedPoints = 0; m_SelectedQuad = Index; s_LastWx = wx; s_LastWy = wy; @@ -1032,7 +1241,9 @@ void CEditor::DoQuad(CQuad *q, int Index) if(UI()->MouseButton(1)) { - m_SelectedQuad = Index; + if(m_SelectedQuad != Index) + m_SelectedPoints = 0; + m_SelectedQuad = Index; s_Operation = OP_CONTEXT_MENU; UI()->SetActiveItem(pID); } @@ -1092,12 +1303,37 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) { if(s_Operation == OP_MOVEPOINT) { - for(int m = 0; m < 4; m++) - if(m_SelectedPoints&(1<m_aPoints[m].x += f2fx(dx); - pQuad->m_aPoints[m].y += f2fx(dy); - } + if(m_GridActive) + { + for(int m = 0; m < 4; m++) + if(m_SelectedPoints&(1<= 0) + x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + else + x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + if(wy >= 0) + y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + else + y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + + pQuad->m_aPoints[m].x = f2fx(x); + pQuad->m_aPoints[m].y = f2fx(y); + } + } + else + { + for(int m = 0; m < 4; m++) + if(m_SelectedPoints&(1<m_aPoints[m].x += f2fx(dx); + pQuad->m_aPoints[m].y += f2fx(dy); + } + } } else if(s_Operation == OP_MOVEUV) { @@ -1168,7 +1404,6 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) m_SelectedPoints |= 1<(GetSelectedLayerType(0, LAYERTYPE_TILES)); if(m_ShowTileInfo && pT && pT->m_Visible && m_ZoomLevel <= 300) + { + GetSelectedGroup()->MapScreen(); pT->ShowInfo(); + } } static void *s_pEditorID = (void *)&s_pEditorID; @@ -1308,6 +1546,8 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) { g->MapScreen(); + RenderGrid(g); + for(int i = 0; i < NumEditLayers; i++) { if(pEditLayers[i]->m_Type != LAYERTYPE_TILES) @@ -1828,9 +2068,13 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) static float s_ScrollValue = 0; for(int g = 0; g < m_Map.m_lGroups.size(); g++) + { // Each group is 19.0f // Each layer is 14.0f - LayersHeight += 19.0f + m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f; + LayersHeight += 19.0f; + if(!m_Map.m_lGroups[g]->m_Collapse) + LayersHeight += m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f; + } float ScrollDifference = LayersHeight - LayersBox.h; @@ -1841,6 +2085,20 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) LayersBox.VSplitRight(3.0f, &LayersBox, 0); // extra spacing Scroll.HMargin(5.0f, &Scroll); s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue); + + if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&LayersBox)) + { + int ScrollNum = (int)((LayersHeight-LayersBox.h)/15.0f)+1; + if(ScrollNum > 0) + { + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f); + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f); + } + else + ScrollNum = 0; + } } float LayerStartAt = ScrollDifference * s_ScrollValue; @@ -1867,7 +2125,7 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) { LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); Slot.VSplitLeft(12, &VisibleToggle, &Slot); - if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", 0, &VisibleToggle, 0, "Toggle group visibility", CUI::CORNER_L)) + if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", m_Map.m_lGroups[g]->m_Collapse ? 1 : 0, &VisibleToggle, 0, "Toggle group visibility", CUI::CORNER_L)) m_Map.m_lGroups[g]->m_Visible = !m_Map.m_lGroups[g]->m_Visible; Slot.VSplitRight(12.0f, &Slot, &SaveCheck); @@ -1875,16 +2133,22 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) if(!m_Map.m_lGroups[g]->m_GameGroup) m_Map.m_lGroups[g]->m_SaveToMap = !m_Map.m_lGroups[g]->m_SaveToMap; - str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_pName); + str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_aName); + float FontSize = 10.0f; + while(TextRender()->TextWidth(0, FontSize, aBuf, -1) > Slot.w) + FontSize--; if(int Result = DoButton_Ex(&m_Map.m_lGroups[g], aBuf, g==m_SelectedGroup, &Slot, - BUTTON_CONTEXT, "Select group. Right click for properties.", 0)) + BUTTON_CONTEXT, m_Map.m_lGroups[g]->m_Collapse ? "Select group. Double click to expand." : "Select group. Double click to collapse.", 0, FontSize)) { m_SelectedGroup = g; m_SelectedLayer = 0; static int s_GroupPopupId = 0; if(Result == 2) - UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 200, PopupGroup); + UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 220, PopupGroup); + + if(m_Map.m_lGroups[g]->m_lLayers.size() && Input()->MouseDoubleClick()) + m_Map.m_lGroups[g]->m_Collapse ^= 1; } LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); } @@ -1900,6 +2164,9 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) continue; } + if(m_Map.m_lGroups[g]->m_Collapse) + continue; + //visible LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); Slot.VSplitLeft(12.0f, 0, &Button); @@ -1913,15 +2180,24 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) if(m_Map.m_lGroups[g]->m_lLayers[i] != m_Map.m_pGameLayer) m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap = !m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap; - str_format(aBuf, sizeof(aBuf),"#%d %s ", i, m_Map.m_lGroups[g]->m_lLayers[i]->m_pTypeName); + if(m_Map.m_lGroups[g]->m_lLayers[i]->m_aName[0]) + str_format(aBuf, sizeof(aBuf), "%s", m_Map.m_lGroups[g]->m_lLayers[i]->m_aName); + else if(m_Map.m_lGroups[g]->m_lLayers[i]->m_Type == LAYERTYPE_TILES) + str_copy(aBuf, "Tiles", sizeof(aBuf)); + else + str_copy(aBuf, "Quads", sizeof(aBuf)); + + float FontSize = 10.0f; + while(TextRender()->TextWidth(0, FontSize, aBuf, -1) > Button.w) + FontSize--; if(int Result = DoButton_Ex(m_Map.m_lGroups[g]->m_lLayers[i], aBuf, g==m_SelectedGroup&&i==m_SelectedLayer, &Button, - BUTTON_CONTEXT, "Select layer. Right click for properties.", 0)) + BUTTON_CONTEXT, "Select layer.", 0, FontSize)) { m_SelectedLayer = i; m_SelectedGroup = g; static int s_LayerPopupID = 0; if(Result == 2) - UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 220, PopupLayer); + UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 245, PopupLayer); } LayerCur += 14.0f; @@ -1959,6 +2235,7 @@ void CEditor::ReplaceImage(const char *pFileName, int StorageType, void *pUser) *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); pEditor->SortImages(); for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i) @@ -1990,6 +2267,7 @@ void CEditor::AddImage(const char *pFileName, int StorageType, void *pUser) pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 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); pEditor->m_Map.m_lImages.add(pImg); pEditor->SortImages(); if(pEditor->m_SelectedImage > -1 && pEditor->m_SelectedImage < pEditor->m_Map.m_lImages.size()) @@ -2123,6 +2401,20 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) ToolBox.VSplitRight(3.0f, &ToolBox, 0); // extra spacing Scroll.HMargin(5.0f, &Scroll); s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue); + + if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&ToolBox)) + { + int ScrollNum = (int)((ImagesHeight-ToolBox.h)/14.0f)+1; + if(ScrollNum > 0) + { + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f); + if(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; @@ -2191,6 +2483,9 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) r.w = r.h; else r.h = r.w; + float Max = (float)(max(m_Map.m_lImages[i]->m_Width, m_Map.m_lImages[i]->m_Height)); + r.w *= m_Map.m_lImages[i]->m_Width/Max; + r.h *= m_Map.m_lImages[i]->m_Height/Max; Graphics()->TextureSet(m_Map.m_lImages[i]->m_TexID); Graphics()->BlendNormal(); Graphics()->QuadsBegin(); @@ -2200,6 +2495,15 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) } } + + // separator + ToolBox.HSplitTop(5.0f, &Slot, &ToolBox); + ImageCur += 5.0f; + IGraphics::CLineItem LineItem(Slot.x, Slot.y+Slot.h/2, Slot.x+Slot.w, Slot.y+Slot.h/2); + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); + Graphics()->LinesDraw(&LineItem, 1); + Graphics()->LinesEnd(); } if(ImageCur + 27.0f > ImageStopAt) @@ -2304,9 +2608,9 @@ void CEditor::RenderFileDialog() // filebox if(m_FileDialogStorageType == IStorage::TYPE_SAVE) { - static int s_FileBoxID = 0; + static float s_FileBoxID = 0; UI()->DoLabel(&FileBoxLabel, "Filename:", 10.0f, -1, -1); - if(DoEditBox(&s_FileBoxID, &FileBox, m_aFileDialogFileName, sizeof(m_aFileDialogFileName), 10.0f)) + if(DoEditBox(&s_FileBoxID, &FileBox, m_aFileDialogFileName, sizeof(m_aFileDialogFileName), 10.0f, &s_FileBoxID)) { // remove '/' and '\' for(int i = 0; m_aFileDialogFileName[i]; ++i) @@ -2668,8 +2972,8 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) ToolBar.VSplitLeft(80.0f, &Button, &ToolBar); - static int s_NameBox = 0; - if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f)) + 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; } } @@ -3128,6 +3432,18 @@ void CEditor::Render() if(m_Mode == MODE_LAYERS) DoMapEditor(View, ToolBar); + // do the scrolling + if(m_Dialog == DIALOG_NONE && UI()->MouseInside(&View)) + { + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + m_ZoomLevel -= 20; + + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + m_ZoomLevel += 20; + + m_ZoomLevel = clamp(m_ZoomLevel, 50, 2000); + } + if(m_GuiActive) { float Brightness = 0.25f; @@ -3225,7 +3541,6 @@ void CEditor::Render() Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } - } void CEditor::Reset(bool CreateDefault) @@ -3263,6 +3578,24 @@ void CEditor::Reset(bool CreateDefault) m_Map.m_Modified = false; } +int CEditor::GetLineDistance() +{ + int LineDistance = 512; + + if(m_ZoomLevel <= 100) + LineDistance = 16; + else if(m_ZoomLevel <= 250) + LineDistance = 32; + else if(m_ZoomLevel <= 450) + LineDistance = 64; + else if(m_ZoomLevel <= 850) + LineDistance = 128; + else if(m_ZoomLevel <= 1550) + LineDistance = 256; + + return LineDistance; +} + void CEditorMap::DeleteEnvelope(int Index) { if(Index < 0 || Index >= m_lEnvelopes.size()) @@ -3303,7 +3636,7 @@ void CEditorMap::MakeGameGroup(CLayerGroup *pGroup) { m_pGameGroup = pGroup; m_pGameGroup->m_GameGroup = true; - m_pGameGroup->m_pName = "Game"; + str_copy(m_pGameGroup->m_aName, "Game", sizeof(m_pGameGroup->m_aName)); } @@ -3425,6 +3758,7 @@ void CEditor::UpdateAndRender() float rx, ry; { Input()->MouseRelative(&rx, &ry); + UI()->ConvertMouseMove(&rx, &ry); m_MouseDeltaX = rx; m_MouseDeltaY = ry; diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 9f34a4f23..f22191937 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -3,23 +3,26 @@ #ifndef GAME_EDITOR_EDITOR_H #define GAME_EDITOR_EDITOR_H -#include +#include + #include -#include +#include + #include +#include #include #include -#include +#include #include #include -#include #include +#include #include #include -#include +#include "auto_map.h" typedef void (*INDEX_MODIFY_FUNC)(int *pIndex); @@ -123,7 +126,7 @@ public: CLayer() { m_Type = LAYERTYPE_INVALID; - m_pTypeName = "(invalid)"; + str_copy(m_aName, "(invalid)", sizeof(m_aName)); m_Visible = true; m_Readonly = false; m_SaveToMap = true; @@ -153,7 +156,7 @@ public: virtual void GetSize(float *w, float *h) { *w = 0; *h = 0;} - const char *m_pTypeName; + char m_aName[12]; int m_Type; int m_Flags; @@ -181,10 +184,11 @@ public: int m_ClipW; int m_ClipH; - const char *m_pName; + char m_aName[12]; bool m_GameGroup; bool m_Visible; bool m_SaveToMap; + bool m_Collapse; CLayerGroup(); ~CLayerGroup(); @@ -230,6 +234,7 @@ public: CEditor *m_pEditor; CEditorImage(CEditor *pEditor) + : m_AutoMapper(pEditor) { m_pEditor = pEditor; m_TexID = -1; @@ -249,6 +254,7 @@ public: int m_External; char m_aName[128]; unsigned char m_aTileFlags[256]; + class CAutoMapper m_AutoMapper; }; class CEditorMap @@ -411,6 +417,8 @@ public: int m_Width; int m_Height; CColor m_Color; + int m_ColorEnv; + int m_ColorEnvOffset; CTile *m_pTiles; // DDRace @@ -489,6 +497,9 @@ public: m_Dialog = 0; m_pTooltip = 0; + m_GridActive = false; + m_GridFactor = 1; + m_aFileName[0] = 0; m_aFileSaveName[0] = 0; m_ValidSaveFilename = false; @@ -583,6 +594,9 @@ public: int m_Dialog; const char *m_pTooltip; + bool m_GridActive; + int m_GridFactor; + char m_aFileName[512]; char m_aFileSaveName[512]; bool m_ValidSaveFilename; @@ -683,12 +697,14 @@ public: CEditorMap m_Map; + static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser); + void DoMapBorder(); int DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); int DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); int DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); - int DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners); + int DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize=10.0f); int DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); int DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); @@ -697,10 +713,12 @@ public: int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); int DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags=0, const char *pToolTip=0); - int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden=false); + int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *Offset, bool Hidden=false, int Corners=CUI::CORNER_ALL); void RenderBackground(CUIRect View, int Texture, float Size, float Brightness); + void RenderGrid(CLayerGroup *pGroup); + void UiInvokePopupMenu(void *pID, int Flags, float X, float Y, float W, float H, int (*pfnFunc)(CEditor *pEditor, CUIRect Rect), void *pExtra=0); void UiDoPopupMenu(); @@ -716,6 +734,7 @@ public: static int PopupSelectGametileOp(CEditor *pEditor, CUIRect View); static int PopupImage(CEditor *pEditor, CUIRect View); static int PopupMenuFile(CEditor *pEditor, CUIRect View); + static int PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View); static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser); static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUser); @@ -726,6 +745,9 @@ public: void PopupSelectGametileOpInvoke(float x, float y); int PopupSelectGameTileOpResult(); + + void PopupSelectConfigAutoMapInvoke(float x, float y); + int PopupSelectConfigAutoMapResult(); vec4 ButtonColorMul(const void *pID); @@ -766,6 +788,7 @@ public: str_copy(pName, pExtractedName, Length); } +<<<<<<< HEAD // DDRace static int ms_FrontTexture; @@ -783,6 +806,9 @@ public: unsigned char m_SwitchNum; unsigned char m_SwitchDelay; +======= + int GetLineDistance(); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe }; // make sure to inline this function diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index 2f41fcbf4..06b85a7b5 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -269,6 +269,9 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) GItem.m_StartLayer = LayerCount; GItem.m_NumLayers = 0; + // save group name + StrToInts(GItem.m_aName, sizeof(GItem.m_aName)/sizeof(int), pGroup->m_aName); + for(int l = 0; l < pGroup->m_lLayers.size(); l++) { if(!pGroup->m_lLayers[l]->m_SaveToMap) @@ -281,20 +284,18 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) pLayer->PrepareForSave(); CMapItemLayerTilemap Item; - Item.m_Version = 2; + Item.m_Version = 3; Item.m_Layer.m_Flags = pLayer->m_Flags; Item.m_Layer.m_Type = pLayer->m_Type; - Item.m_Color.r = pLayer->m_Color.r; - Item.m_Color.g = pLayer->m_Color.g; - Item.m_Color.b = pLayer->m_Color.b; - Item.m_Color.a = pLayer->m_Color.a; - Item.m_ColorEnv = -1; // not in use right now - Item.m_ColorEnvOffset = 0; + Item.m_Color = pLayer->m_Color; + Item.m_ColorEnv = pLayer->m_ColorEnv; + Item.m_ColorEnvOffset = pLayer->m_ColorEnvOffset; Item.m_Width = pLayer->m_Width; Item.m_Height = pLayer->m_Height; +<<<<<<< HEAD //Item.m_Flags = pLayer->m_Game; if(pLayer->m_Tele) @@ -343,6 +344,15 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) } else Item.m_Data = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTile), pLayer->m_pTiles); +======= + Item.m_Flags = pLayer->m_Game ? TILESLAYERFLAG_GAME : 0; + Item.m_Image = pLayer->m_Image; + Item.m_Data = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTile), pLayer->m_pTiles); + + // save layer name + StrToInts(Item.m_aName, sizeof(Item.m_aName)/sizeof(int), pLayer->m_aName); + +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item); GItem.m_NumLayers++; @@ -355,7 +365,7 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) if(pLayer->m_lQuads.size()) { CMapItemLayerQuads Item; - Item.m_Version = 1; + Item.m_Version = 2; Item.m_Layer.m_Flags = pLayer->m_Flags; Item.m_Layer.m_Type = pLayer->m_Type; Item.m_Image = pLayer->m_Image; @@ -363,6 +373,10 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) // add the data Item.m_NumQuads = pLayer->m_lQuads.size(); Item.m_Data = df.AddDataSwapped(pLayer->m_lQuads.size()*sizeof(CQuad), pLayer->m_lQuads.base_ptr()); + + // save layer name + StrToInts(Item.m_aName, sizeof(Item.m_aName)/sizeof(int), pLayer->m_aName); + df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item); // clean up @@ -497,6 +511,9 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag if(pName) str_copy(pImg->m_aName, pName, 128); + // load auto mapper file + pImg->m_AutoMapper.Load(pImg->m_aName); + m_lImages.add(pImg); // unload image @@ -534,6 +551,10 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag pGroup->m_ClipH = pGItem->m_ClipH; } + // load group name + if(pGItem->m_Version >= 3) + IntsToStr(pGItem->m_aName, sizeof(pGroup->m_aName)/sizeof(int), pGroup->m_aName); + for(int l = 0; l < pGItem->m_NumLayers; l++) { CLayer *pLayer = 0; @@ -546,7 +567,7 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag CMapItemLayerTilemap *pTilemapItem = (CMapItemLayerTilemap *)pLayerItem; CLayerTiles *pTiles = 0; - if(pTilemapItem->m_Flags&1) + if(pTilemapItem->m_Flags&TILESLAYERFLAG_GAME) { pTiles = new CLayerGame(pTilemapItem->m_Width, pTilemapItem->m_Height); MakeGameLayer(pTiles); @@ -576,10 +597,9 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag { pTiles = new CLayerTiles(pTilemapItem->m_Width, pTilemapItem->m_Height); pTiles->m_pEditor = m_pEditor; - pTiles->m_Color.r = pTilemapItem->m_Color.r; - pTiles->m_Color.g = pTilemapItem->m_Color.g; - pTiles->m_Color.b = pTilemapItem->m_Color.b; - pTiles->m_Color.a = pTilemapItem->m_Color.a; + pTiles->m_Color = pTilemapItem->m_Color; + pTiles->m_ColorEnv = pTilemapItem->m_ColorEnv; + pTiles->m_ColorEnvOffset = pTilemapItem->m_ColorEnvOffset; } pLayer = pTiles; @@ -587,7 +607,11 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag pGroup->AddLayer(pTiles); void *pData = DataFile.GetData(pTilemapItem->m_Data); pTiles->m_Image = pTilemapItem->m_Image; - pTiles->m_Game = pTilemapItem->m_Flags&1; + pTiles->m_Game = pTilemapItem->m_Flags&TILESLAYERFLAG_GAME; + + // load layer name + 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)); @@ -720,6 +744,11 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag pQuads->m_Image = pQuadsItem->m_Image; if(pQuads->m_Image < -1 || pQuads->m_Image >= m_lImages.size()) pQuads->m_Image = -1; + + // load layer name + if(pQuadsItem->m_Version >= 2) + IntsToStr(pQuadsItem->m_aName, sizeof(pQuads->m_aName)/sizeof(int), pQuads->m_aName); + void *pData = DataFile.GetDataSwapped(pQuadsItem->m_Data); pGroup->AddLayer(pQuads); pQuads->m_lQuads.set_size(pQuadsItem->m_NumQuads); diff --git a/src/game/editor/layer_game.cpp b/src/game/editor/layer_game.cpp index f4a5fb76d..7e879c3ee 100644 --- a/src/game/editor/layer_game.cpp +++ b/src/game/editor/layer_game.cpp @@ -6,7 +6,7 @@ CLayerGame::CLayerGame(int w, int h) : CLayerTiles(w, h) { - m_pTypeName = "Game"; + str_copy(m_aName, "Game", sizeof(m_aName)); m_Game = 1; } diff --git a/src/game/editor/layer_quads.cpp b/src/game/editor/layer_quads.cpp index d2a8a1e5a..d0b66405c 100644 --- a/src/game/editor/layer_quads.cpp +++ b/src/game/editor/layer_quads.cpp @@ -13,7 +13,7 @@ CLayerQuads::CLayerQuads() { m_Type = LAYERTYPE_QUADS; - m_pTypeName = "Quads"; + str_copy(m_aName, "Quads", sizeof(m_aName)); m_Image = -1; } @@ -21,31 +21,13 @@ CLayerQuads::~CLayerQuads() { } -static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser) -{ - CEditor *pEditor = (CEditor *)pUser; - if(Env < 0 || Env > pEditor->m_Map.m_lEnvelopes.size()) - { - pChannels[0] = 0; - pChannels[1] = 0; - pChannels[2] = 0; - pChannels[3] = 0; - return; - } - - CEnvelope *e = pEditor->m_Map.m_lEnvelopes[Env]; - float t = pEditor->m_AnimateTime+TimeOffset; - t *= pEditor->m_AnimateSpeed; - e->Eval(t, pChannels); -} - void CLayerQuads::Render() { Graphics()->TextureSet(-1); 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, EnvelopeEval, m_pEditor); + m_pEditor->RenderTools()->RenderQuads(m_lQuads.base_ptr(), m_lQuads.size(), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, m_pEditor->EnvelopeEval, m_pEditor); } CQuad *CLayerQuads::NewQuad() diff --git a/src/game/editor/layer_tiles.cpp b/src/game/editor/layer_tiles.cpp index a6ed0eb37..9e831a1b9 100644 --- a/src/game/editor/layer_tiles.cpp +++ b/src/game/editor/layer_tiles.cpp @@ -18,7 +18,7 @@ CLayerTiles::CLayerTiles(int w, int h) { m_Type = LAYERTYPE_TILES; - m_pTypeName = "Tiles"; + str_copy(m_aName, "Tiles", sizeof(m_aName)); m_Width = w; m_Height = h; m_Image = -1; @@ -28,6 +28,8 @@ CLayerTiles::CLayerTiles(int w, int h) m_Color.g = 255; m_Color.b = 255; m_Color.a = 255; + m_ColorEnv = -1; + m_ColorEnvOffset = 0; m_Tele = 0; m_Speedup = 0; @@ -70,6 +72,7 @@ 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); +<<<<<<< HEAD m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT); // Render DDRace Layers @@ -79,6 +82,10 @@ 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); +======= + m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, + m_pEditor->EnvelopeEval, m_pEditor, m_ColorEnv, m_ColorEnvOffset); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } int CLayerTiles::ConvertX(float x) const { return (int)(x/32.0f); } @@ -485,19 +492,43 @@ void CLayerTiles::ShowInfo() } x += m_pTiles[c].m_Skip; } + + Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } int CLayerTiles::RenderProperties(CUIRect *pToolBox) { CUIRect Button; - pToolBox->HSplitBottom(12.0f, pToolBox, &Button); - + bool InGameGroup = !find_linear(m_pEditor->m_Map.m_pGameGroup->m_lLayers.all(), this).empty(); +<<<<<<< HEAD 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) +======= + if(m_pEditor->m_Map.m_pGameLayer != this) + { + if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size() && m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.IsLoaded()) + { + static int s_AutoMapperButton = 0; + pToolBox->HSplitBottom(12.0f, pToolBox, &Button); + if(m_pEditor->DoButton_Editor(&s_AutoMapperButton, "Auto map", 0, &Button, 0, "")) + m_pEditor->PopupSelectConfigAutoMapInvoke(m_pEditor->UI()->MouseX(), m_pEditor->UI()->MouseY()); + + int Result = m_pEditor->PopupSelectConfigAutoMapResult(); + if(Result > -1) + { + m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.Proceed(this, Result); + return 1; + } + } + } + else +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe InGameGroup = false; if(InGameGroup) { + pToolBox->HSplitBottom(2.0f, pToolBox, 0); + pToolBox->HSplitBottom(12.0f, pToolBox, &Button); static int s_ColclButton = 0; if(m_pEditor->DoButton_Editor(&s_ColclButton, "Game tiles", 0, &Button, 0, "Constructs game tiles from this layer")) m_pEditor->PopupSelectGametileOpInvoke(m_pEditor->UI()->MouseX(), m_pEditor->UI()->MouseY()); @@ -541,6 +572,8 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) PROP_SHIFT, PROP_IMAGE, PROP_COLOR, + PROP_COLOR_ENV, + PROP_COLOR_ENV_OFFSET, NUM_PROPS, }; @@ -556,6 +589,8 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) {"Shift", 0, PROPTYPE_SHIFT, 0, 0}, {"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}, + {"Color TO", m_ColorEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {0}, }; @@ -594,6 +629,22 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) m_Color.b = (NewVal>>8)&0xff; m_Color.a = NewVal&0xff; } + if(Prop == PROP_COLOR_ENV) + { + int Index = clamp(NewVal-1, -1, m_pEditor->m_Map.m_lEnvelopes.size()-1); + int Step = (Index-m_ColorEnv)%2; + if(Step != 0) + { + for(; Index >= -1 && Index < m_pEditor->m_Map.m_lEnvelopes.size(); Index += Step) + if(Index == -1 || m_pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 4) + { + m_ColorEnv = Index; + break; + } + } + } + if(Prop == PROP_COLOR_ENV_OFFSET) + m_ColorEnvOffset = NewVal; return 0; } diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index d0cffd1cc..cd3ec5e15 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -1,10 +1,14 @@ /* (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 #include + #include "editor.h" @@ -215,6 +219,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) l->m_pEditor = pEditor; 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_Map.m_lGroups[pEditor->m_SelectedGroup]->m_Collapse = false; return 1; } @@ -228,9 +233,22 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) l->m_pEditor = pEditor; 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_Map.m_lGroups[pEditor->m_SelectedGroup]->m_Collapse = false; return 1; } + // group name + if(!pEditor->GetSelectedGroup()->m_GameGroup) + { + View.HSplitBottom(5.0f, &View, &Button); + View.HSplitBottom(12.0f, &View, &Button); + static float s_Name = 0; + pEditor->UI()->DoLabel(&Button, "Name:", 10.0f, -1, -1); + Button.VSplitLeft(40.0f, 0, &Button); + if(pEditor->DoEditBox(&s_Name, &Button, pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_aName, sizeof(pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_aName), 10.0f, &s_Name)) + pEditor->m_Map.m_Modified = true; + } + enum { PROP_ORDER=0, @@ -315,6 +333,18 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View) return 1; } + // layer name + if(pEditor->m_Map.m_pGameLayer != pEditor->GetSelectedLayer(0)) + { + View.HSplitBottom(5.0f, &View, &Button); + View.HSplitBottom(12.0f, &View, &Button); + static float s_Name = 0; + pEditor->UI()->DoLabel(&Button, "Name:", 10.0f, -1, -1); + Button.VSplitLeft(40.0f, 0, &Button); + if(pEditor->DoEditBox(&s_Name, &Button, pEditor->GetSelectedLayer(0)->m_aName, sizeof(pEditor->GetSelectedLayer(0)->m_aName), 10.0f, &s_Name)) + pEditor->m_Map.m_Modified = true; + } + View.HSplitBottom(10.0f, &View, 0); CLayerGroup *pCurrentGroup = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]; @@ -505,9 +535,35 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) for(int k = 0; k < 5; ++k) pQuad->m_aPoints[k].y += Offset; } - if(Prop == PROP_POS_ENV) pQuad->m_PosEnv = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1); + if(Prop == PROP_POS_ENV) + { + int Index = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1); + int Step = (Index-pQuad->m_PosEnv)%2; + if(Step != 0) + { + for(; Index >= -1 && Index < pEditor->m_Map.m_lEnvelopes.size(); Index += Step) + if(Index == -1 || pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 3) + { + pQuad->m_PosEnv = Index; + break; + } + } + } if(Prop == PROP_POS_ENV_OFFSET) pQuad->m_PosEnvOffset = NewVal; - if(Prop == PROP_COLOR_ENV) pQuad->m_ColorEnv = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1); + if(Prop == PROP_COLOR_ENV) + { + int Index = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1); + int Step = (Index-pQuad->m_ColorEnv)%2; + if(Step != 0) + { + for(; Index >= -1 && Index < pEditor->m_Map.m_lEnvelopes.size(); Index += Step) + if(Index == -1 || pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 4) + { + pQuad->m_ColorEnv = Index; + break; + } + } + } if(Prop == PROP_COLOR_ENV_OFFSET) pQuad->m_ColorEnvOffset = NewVal; return 0; @@ -605,8 +661,8 @@ int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View) View.HSplitBottom(40.0f, &View, 0); View.VMargin(40.0f, &View); View.HSplitBottom(20.0f, &View, &Label); - static int s_FolderBox = 0; - pEditor->DoEditBox(&s_FolderBox, &Label, pEditor->m_FileDialogNewFolderName, sizeof(pEditor->m_FileDialogNewFolderName), 15.0f); + static float s_FolderBox = 0; + pEditor->DoEditBox(&s_FolderBox, &Label, pEditor->m_FileDialogNewFolderName, sizeof(pEditor->m_FileDialogNewFolderName), 15.0f, &s_FolderBox); View.HSplitBottom(20.0f, &View, &Label); pEditor->UI()->DoLabel(&Label, "Name:", 10.0f, -1); @@ -750,7 +806,16 @@ int CEditor::PopupSelectImage(CEditor *pEditor, CUIRect View) } if(ShowImage >= 0 && ShowImage < pEditor->m_Map.m_lImages.size()) + { + if(ImageView.h < ImageView.w) + ImageView.w = ImageView.h; + else + ImageView.h = ImageView.w; + float Max = (float)(max(pEditor->m_Map.m_lImages[ShowImage]->m_Width, pEditor->m_Map.m_lImages[ShowImage]->m_Height)); + ImageView.w *= pEditor->m_Map.m_lImages[ShowImage]->m_Width/Max; + ImageView.h *= pEditor->m_Map.m_lImages[ShowImage]->m_Height/Max; pEditor->Graphics()->TextureSet(pEditor->m_Map.m_lImages[ShowImage]->m_TexID); + } else pEditor->Graphics()->TextureSet(-1); pEditor->Graphics()->QuadsBegin(); @@ -815,6 +880,7 @@ int CEditor::PopupSelectGameTileOpResult() return Result; } +<<<<<<< HEAD // DDRace int CEditor::PopupTele(CEditor *pEditor, CUIRect View) @@ -839,10 +905,29 @@ int CEditor::PopupTele(CEditor *pEditor, CUIRect View) if(Prop == PROP_TELE) pEditor->m_TeleNumber = clamp(NewVal, 0, 255); +======= +static int s_AutoMapConfigSelected = -1; + +int CEditor::PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View) +{ + CLayerTiles *pLayer = static_cast(pEditor->GetSelectedLayer(0)); + CUIRect Button; + static int s_AutoMapperConfigButtons[256]; + CAutoMapper *pAutoMapper = &pEditor->m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper; + + for(int i = 0; i < pAutoMapper->ConfigNamesNum(); ++i) + { + View.HSplitTop(2.0f, 0, &View); + View.HSplitTop(12.0f, &Button, &View); + if(pEditor->DoButton_Editor(&s_AutoMapperConfigButtons[i], pAutoMapper->GetConfigName(i), 0, &Button, 0, 0)) + s_AutoMapConfigSelected = i; + } +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe return 0; } +<<<<<<< HEAD int CEditor::PopupSpeedup(CEditor *pEditor, CUIRect View) { CUIRect Button; @@ -905,4 +990,24 @@ int CEditor::PopupSwitch(CEditor *pEditor, CUIRect View) pEditor->m_SwitchDelay = clamp(NewVal, 0, 255); return 0; +======= +void CEditor::PopupSelectConfigAutoMapInvoke(float x, float y) +{ + static int s_AutoMapConfigSelectID = 0; + s_AutoMapConfigSelected = -1; + CLayerTiles *pLayer = static_cast(GetSelectedLayer(0)); + if(pLayer && pLayer->m_Image >= 0 && pLayer->m_Image < m_Map.m_lImages.size() && + m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper.ConfigNamesNum()) + UiInvokePopupMenu(&s_AutoMapConfigSelectID, 0, x, y, 120.0f, 12.0f+14.0f*m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper.ConfigNamesNum(), PopupSelectConfigAutoMap); +} + +int CEditor::PopupSelectConfigAutoMapResult() +{ + if(s_AutoMapConfigSelected < 0) + return -1; + + int Result = s_AutoMapConfigSelected; + s_AutoMapConfigSelected = -1; + return Result; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } diff --git a/src/game/gamecore.cpp b/src/game/gamecore.cpp index 3047d42e1..168f935a5 100644 --- a/src/game/gamecore.cpp +++ b/src/game/gamecore.cpp @@ -345,8 +345,12 @@ void CCharacterCore::Tick(bool UseInput) } } +<<<<<<< HEAD //if(m_pWorld && m_pWorld->m_Tuning.m_PlayerCollision) if(m_pWorld/* && m_pWorld->m_Tuning.m_PlayerCollision*/) +======= + if(m_pWorld) +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe { for(int i = 0; i < MAX_CLIENTS; i++) { @@ -363,11 +367,18 @@ 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); +<<<<<<< HEAD if (m_pWorld->m_Tuning.m_PlayerCollision) if(Distance < PhysSize*1.25f && Distance > 0.0f) { float a = (PhysSize*1.45f - Distance); float Velocity = 0.5f; +======= + if(m_pWorld->m_Tuning.m_PlayerCollision && Distance < PhysSize*1.25f && Distance > 0.0f) + { + float a = (PhysSize*1.45f - Distance); + float Velocity = 0.5f; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe // make sure that we don't add excess force by checking the // direction against the current velocity. if not zero. @@ -379,7 +390,7 @@ void CCharacterCore::Tick(bool UseInput) } // handle hook influence - if(m_HookedPlayer == i) + if(m_HookedPlayer == i && m_pWorld->m_Tuning.m_PlayerHooking) { if(Distance > PhysSize*1.50f) // TODO: fix tweakable variable { @@ -437,6 +448,10 @@ void CCharacterCore::Move() // check player collision float Distance = distance(m_Pos, NewPos); int End = Distance+1; +<<<<<<< HEAD +======= + vec2 LastPos = m_Pos; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe for(int i = 0; i < End; i++) { float a = i/Distance; @@ -447,15 +462,27 @@ void CCharacterCore::Move() if(!pCharCore || pCharCore == this) continue; float D = distance(Pos, pCharCore->m_Pos); +<<<<<<< HEAD if(D < 28.0f*1.25f && D > 0.0f) { if(a > 0.0f) m_Pos = Pos; else +======= + if(D < 28.0f && D > 0.0f) + { + if(a > 0.0f) + m_Pos = LastPos; + else if(distance(NewPos, pCharCore->m_Pos) > D) +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe m_Pos = NewPos; return; } } +<<<<<<< HEAD +======= + LastPos = Pos; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } } diff --git a/src/game/layers.cpp b/src/game/layers.cpp index a547ba179..c89746940 100644 --- a/src/game/layers.cpp +++ b/src/game/layers.cpp @@ -39,7 +39,7 @@ void CLayers::Init(class IKernel *pKernel) if(pLayer->m_Type == LAYERTYPE_TILES) { CMapItemLayerTilemap *pTilemap = reinterpret_cast(pLayer); - if(pTilemap->m_Flags&1) + if(pTilemap->m_Flags&TILESLAYERFLAG_GAME) { m_pGameLayer = pTilemap; m_pGameGroup = pGroup; diff --git a/src/game/mapitems.h b/src/game/mapitems.h index 7c7d69d00..c8835242c 100644 --- a/src/game/mapitems.h +++ b/src/game/mapitems.h @@ -140,6 +140,7 @@ enum ROTATION_270 = (TILEFLAG_VFLIP|TILEFLAG_HFLIP|TILEFLAG_ROTATE), LAYERFLAG_DETAIL=1, + TILESLAYERFLAG_GAME=1, ENTITY_OFFSET=255-16*4, OLD_THROUGH1=25, @@ -203,13 +204,15 @@ struct CMapItemGroup_v1 struct CMapItemGroup : public CMapItemGroup_v1 { - enum { CURRENT_VERSION=2 }; + enum { CURRENT_VERSION=3 }; int m_UseClipping; int m_ClipX; int m_ClipY; int m_ClipW; int m_ClipH; + + int m_aName[3]; } ; struct CMapItemLayer @@ -235,12 +238,16 @@ struct CMapItemLayerTilemap int m_Image; int m_Data; +<<<<<<< HEAD // DDRace int m_Tele; int m_Speedup; int m_Front; int m_Switch; +======= + int m_aName[3]; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } ; struct CMapItemLayerQuads @@ -251,6 +258,8 @@ struct CMapItemLayerQuads int m_NumQuads; int m_Data; int m_Image; + + int m_aName[3]; } ; struct CMapItemVersion diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 934ea35e5..65ceb74b1 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -129,8 +129,6 @@ void CCharacter::HandleNinja() // time's up, return m_aWeapons[WEAPON_NINJA].m_Got = false; m_ActiveWeapon = m_LastWeapon; - if(m_ActiveWeapon == WEAPON_NINJA) - m_ActiveWeapon = WEAPON_GUN; SetWeapon(m_ActiveWeapon); return; @@ -190,7 +188,7 @@ void CCharacter::HandleNinja() if(m_NumObjectsHit < 10) m_apHitObjects[m_NumObjectsHit++] = aEnts[i]; - aEnts[i]->TakeDamage(vec2(0, 10.0f), g_pData->m_Weapons.m_Ninja.m_pBase->m_Damage, m_pPlayer->GetCID(), WEAPON_NINJA); + aEnts[i]->TakeDamage(vec2(0, -10.0f), g_pData->m_Weapons.m_Ninja.m_pBase->m_Damage, m_pPlayer->GetCID(), WEAPON_NINJA); } } @@ -544,9 +542,15 @@ void CCharacter::GiveNinja() { m_Ninja.m_ActivationTick = Server()->Tick(); m_aWeapons[WEAPON_NINJA].m_Got = true; +<<<<<<< HEAD if (!m_FreezeTime) m_aWeapons[WEAPON_NINJA].m_Ammo = -1; m_LastWeapon = m_ActiveWeapon; +======= + m_aWeapons[WEAPON_NINJA].m_Ammo = -1; + if (m_ActiveWeapon != WEAPON_NINJA) + m_LastWeapon = m_ActiveWeapon; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe m_ActiveWeapon = WEAPON_NINJA; if(!m_aWeapons[WEAPON_NINJA].m_Got) @@ -588,6 +592,18 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput) mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); } +void CCharacter::ResetInput() +{ + m_Input.m_Direction = 0; + m_Input.m_Hook = 0; + // simulate releasing the fire button + if((m_Input.m_Fire&1) != 0) + m_Input.m_Fire++; + m_Input.m_Fire &= INPUT_STATE_MASK; + m_Input.m_Jump = 0; + m_LatestPrevInput = m_LatestInput = m_Input; +} + void CCharacter::Tick() { /*if(m_pPlayer->m_ForceBalanced) @@ -814,7 +830,15 @@ 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]) - GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, CmaskOne(From)); + { + int 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) + Mask |= CmaskOne(i); + } + GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, Mask); + } // check for death if(m_Health <= 0) @@ -923,7 +947,8 @@ void CCharacter::Snap(int SnappingClient) pCharacter->m_Direction = m_Input.m_Direction; - if(m_pPlayer->GetCID() == SnappingClient || SnappingClient == -1 || m_pPlayer->GetCID() == GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID) + if(m_pPlayer->GetCID() == SnappingClient || SnappingClient == -1 || + (!g_Config.m_SvStrictSpectateMode && m_pPlayer->GetCID() == GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID)) { pCharacter->m_Health = m_Health; pCharacter->m_Armor = m_Armor; diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index 8adafdf33..fbca61629 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -45,6 +45,7 @@ public: void OnPredictedInput(CNetObj_PlayerInput *pNewInput); void OnDirectInput(CNetObj_PlayerInput *pNewInput); + void ResetInput(); void FireWeapon(); void Die(int Killer, int Weapon); diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp index 14ebc9327..e5eef3483 100644 --- a/src/game/server/entities/laser.cpp +++ b/src/game/server/entities/laser.cpp @@ -27,18 +27,25 @@ bool CLaser::HitCharacter(vec2 From, vec2 To) { vec2 At; CCharacter *pOwnerChar = GameServer()->GetPlayerChar(m_Owner); +<<<<<<< HEAD CCharacter *pHit; 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); else pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, g_Config.m_SvOldLaser || m_Bounces == 0 ? pOwnerChar : 0, m_Owner, pOwnerChar); +======= + CCharacter *pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, pOwnerChar); + if(!pHit) + return false; +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe 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; m_From = From; m_Pos = At; m_Energy = -1; +<<<<<<< HEAD if (m_Type == WEAPON_SHOTGUN) { vec2 Temp; @@ -60,6 +67,9 @@ bool CLaser::HitCharacter(vec2 From, vec2 To) { pHit->UnFreeze(); } +======= + pHit->TakeDamage(vec2(0.f, 0.f), GameServer()->Tuning()->m_LaserDamage, m_Owner, WEAPON_RIFLE); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe return true; } diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index c3a920d95..96c98d49b 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -394,7 +394,8 @@ void CGameContext::SendVoteStatus(int ClientID, int Total, int Yes, int No) void CGameContext::AbortVoteKickOnDisconnect(int ClientID) { - if(m_VoteCloseTime && !str_comp_num(m_aVoteCommand, "kick ", 5) && str_toint(&m_aVoteCommand[5]) == ClientID) + if(m_VoteCloseTime && ((!str_comp_num(m_aVoteCommand, "kick ", 5) && str_toint(&m_aVoteCommand[5]) == ClientID) || + (!str_comp_num(m_aVoteCommand, "set_team ", 9) && str_toint(&m_aVoteCommand[9]) == ClientID))) m_VoteCloseTime = -1; } @@ -942,6 +943,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) return; } +<<<<<<< HEAD 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); @@ -954,6 +956,11 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) str_format(aDesc, sizeof(aDesc), "move '%s' to spectators", Server()->ClientName(SpectateID)); str_format(aCmd, sizeof(aCmd), "set_team %d -1", SpectateID); } +======= + 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); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } if(aCmd[0]) @@ -990,6 +997,16 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) 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->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)) { @@ -1003,7 +1020,12 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) if(pPlayer->GetTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS) m_VoteUpdate = true; pPlayer->SetTeam(pMsg->m_Team); +<<<<<<< HEAD //(void)m_pController->CheckTeamBalance(); +======= + (void)m_pController->CheckTeamBalance(); + pPlayer->m_TeamChangeTick = Server()->Tick(); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } //else //SendBroadcast("Teams must be balanced, please join other team", ClientID); @@ -1328,19 +1350,37 @@ void CGameContext::ConSay(IConsole::IResult *pResult, void *pUserData, int Clien void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData, int ClientID) { CGameContext *pSelf = (CGameContext *)pUserData; +<<<<<<< HEAD //int ClientID = clamp(pResult->GetInteger(0), 0, (int)MAX_CLIENTS-1); int Victim = pResult->GetVictim(); int Team = clamp(pResult->GetInteger(0), -1, 1); +======= + int ClientID = clamp(pResult->GetInteger(0), 0, (int)MAX_CLIENTS-1); + int Team = clamp(pResult->GetInteger(1), -1, 1); + int Delay = 0; + if(pResult->NumArguments() > 2) + Delay = pResult->GetInteger(2); + + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "moved client %d to team %d", ClientID, Team); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe if(!pSelf->m_apPlayers[Victim]) return; +<<<<<<< HEAD char aBuf[256]; str_format(aBuf, sizeof(aBuf), "moved client %d to team %d", Victim, Team); pResult->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); pSelf->m_apPlayers[Victim]->SetTeam(Team); //(void)pSelf->m_pController->CheckTeamBalance(); +======= + pSelf->m_apPlayers[ClientID]->m_TeamChangeTick = pSelf->Server()->Tick()+pSelf->Server()->TickSpeed()*Delay*60; + pSelf->m_apPlayers[ClientID]->SetTeam(Team); + (void)pSelf->m_pController->CheckTeamBalance(); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } void CGameContext::ConSetTeamAll(IConsole::IResult *pResult, void *pUserData, int ClientID) @@ -1555,8 +1595,15 @@ void CGameContext::ConForceVote(IConsole::IResult *pResult, void *pUserData, int return; } +<<<<<<< HEAD str_format(aBuf, sizeof(aBuf), "set_team %d -1", SpectateID); pSelf->Console()->ExecuteLine(aBuf, ClientID, IConsole::CONSOLELEVEL_ADMIN, SendChatResponseAll, pSelf); +======= + str_format(aBuf, sizeof(aBuf), "admin moved '%s' to spectator (%s)", pSelf->Server()->ClientName(SpectateID), pReason); + pSelf->SendChatTarget(-1, aBuf); + str_format(aBuf, sizeof(aBuf), "set_team %d -1 %d", SpectateID, g_Config.m_SvVoteSpectateRejoindelay); + pSelf->Console()->ExecuteLine(aBuf); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } } @@ -1581,6 +1628,8 @@ void CGameContext::ConVote(IConsole::IResult *pResult, void *pUserData, int Clie else if(str_comp_nocase(pResult->GetString(0), "no") == 0) pSelf->m_VoteEnforce = CGameContext::VOTE_ENFORCE_NO_ADMIN; char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "admin forced vote %s", pResult->GetString(0)); + pSelf->SendChatTarget(-1, aBuf); str_format(aBuf, sizeof(aBuf), "forcing vote %s", pResult->GetString(0)); pResult->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); } @@ -1604,6 +1653,7 @@ void CGameContext::OnConsoleInit() m_pServer = Kernel()->RequestInterface(); m_pConsole = Kernel()->RequestInterface(); +<<<<<<< HEAD Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, "", IConsole::CONSOLELEVEL_ADMIN); Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, "", IConsole::CONSOLELEVEL_ADMIN); Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, "", IConsole::CONSOLELEVEL_ADMIN); @@ -1620,6 +1670,24 @@ void CGameContext::OnConsoleInit() Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, ConForceVote, this, "", IConsole::CONSOLELEVEL_ADMIN); Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, "", IConsole::CONSOLELEVEL_ADMIN); Console()->Register("vote", "r", CFGFLAG_SERVER, ConVote, this, "", IConsole::CONSOLELEVEL_ADMIN); +======= + 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("change_map", "?r", CFGFLAG_SERVER|CFGFLAG_STORE, ConChangeMap, this, "Change 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"); + Console()->Register("set_team", "ii?i", CFGFLAG_SERVER, ConSetTeam, this, "Set team of player to team"); + Console()->Register("set_team_all", "i", CFGFLAG_SERVER, ConSetTeamAll, this, "Set team of all players to team"); + + Console()->Register("add_vote", "sr", CFGFLAG_SERVER, ConAddVote, this, "Add a voting option"); + Console()->Register("remove_vote", "s", CFGFLAG_SERVER, ConRemoveVote, this, "remove a voting option"); + Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, ConForceVote, this, "Force a voting option"); + Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, "Clears the voting options"); + Console()->Register("vote", "r", CFGFLAG_SERVER, ConVote, this, "Force a vote to yes/no"); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe Console()->Chain("sv_motd", ConchainSpecialMotdupdate, this); diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index 7bfb0c42e..726add04e 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -72,11 +72,32 @@ void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type) // get spawn point for(int i = 0; i < m_aNumSpawnPoints[Type]; i++) { +<<<<<<< HEAD /*// check if the position is occupado if(GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, 0, 1, CGameWorld::ENTTYPE_CHARACTER)) continue;*/ +======= + // check if the position is occupado + CCharacter *aEnts[MAX_CLIENTS]; + int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); + vec2 Positions[5] = { vec2(0.0f, 0.0f), vec2(-32.0f, 0.0f), vec2(0.0f, -32.0f), vec2(32.0f, 0.0f), vec2(0.0f, 32.0f) }; // start, left, up, right, down + int Result = -1; + for(int Index = 0; Index < 5 && Result == -1; ++Index) + { + Result = Index; + for(int c = 0; c < Num; ++c) + if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i]+Positions[Index]) || + distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]+Positions[Index]) <= aEnts[c]->m_ProximityRadius) + { + Result = -1; + break; + } + } + if(Result == -1) + continue; // try next spawn point +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe - vec2 P = m_aaSpawnPoints[Type][i]; + vec2 P = m_aaSpawnPoints[Type][i]+Positions[Result]; float S = EvaluateSpawnPos(pEval, P); if(!pEval->m_Got || pEval->m_Score > S) { @@ -87,47 +108,6 @@ void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type) } } -void IGameController::FindFreeSpawn(CSpawnEval *pEval, int Type) -{ - // pick the spawn point that is least occupied and has free space for spawning around it - for(int i = 0; i < m_aNumSpawnPoints[Type]; i++) - { - - CCharacter *aEnts[MAX_CLIENTS]; - int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); - float Score = 0.0f; - for(int c = 0; c < Num; ++c) - Score += 96.0f - distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]); - - if(!pEval->m_Got || pEval->m_Score > Score) - { - // start, left, up, right, down - vec2 Positions[5] = { vec2(0.0f, 0.0f), vec2(-32.0f, 0.0f), vec2(0.0f, -32.0f), vec2(32.0f, 0.0f), vec2(0.0f, 32.0f) }; - - // check for free space - int Result = -1; - for(int Index = 0; Index < 5 && Result == -1; ++Index) - { - Result = Index; - for(int c = 0; c < Num; ++c) - if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i]+Positions[Index]) || - distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]+Positions[Index]) <= aEnts[c]->m_ProximityRadius) - { - Result = -1; - break; - } - } - - if(Result == -1) - continue; // try next spawn point - - pEval->m_Got = true; - pEval->m_Score = Score; - pEval->m_Pos = m_aaSpawnPoints[Type][i]+Positions[Result]; - } - } -} - bool IGameController::CanSpawn(int Team, vec2 *pOutPos) { CSpawnEval Eval; @@ -156,6 +136,7 @@ bool IGameController::CanSpawn(int Team, vec2 *pOutPos) EvaluateSpawnType(&Eval, 2); //} +<<<<<<< HEAD // handle crappy maps if(!Eval.m_Got) { @@ -178,6 +159,8 @@ bool IGameController::CanSpawn(int Team, vec2 *pOutPos) //} } +======= +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe *pOutPos = Eval.m_Pos; return Eval.m_Got; } @@ -458,6 +441,7 @@ void IGameController::StartRound() //m_aTeamscore[TEAM_RED] = 0; //m_aTeamscore[TEAM_BLUE] = 0; m_ForceBalanced = false; + Server()->DemoRecorder_HandleAutoStart(); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS); GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); @@ -762,6 +746,8 @@ void IGameController::Tick() } } } + + DoWincheck(); } @@ -906,51 +892,50 @@ bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam) return true; } -void IGameController::DoPlayerScoreWincheck() +void IGameController::DoWincheck() { - if(m_GameOverTick == -1 && !m_Warmup) + if(m_GameOverTick == -1 && !m_Warmup && !GameServer()->m_World.m_ResetRequested) { - // gather some stats - int Topscore = 0; - int TopscoreCount = 0; - for(int i = 0; i < MAX_CLIENTS; i++) + if(IsTeamplay()) { - if(GameServer()->m_apPlayers[i]) + // check score win condition + if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) { - if(GameServer()->m_apPlayers[i]->m_Score > Topscore) - { - Topscore = GameServer()->m_apPlayers[i]->m_Score; - TopscoreCount = 1; - } - else if(GameServer()->m_apPlayers[i]->m_Score == Topscore) - TopscoreCount++; + if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) + EndRound(); + else + m_SuddenDeath = 1; } } - - // check score win condition - if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) || - (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + else { - if(TopscoreCount == 1) - EndRound(); - else - m_SuddenDeath = 1; - } - } -} + // gather some stats + int Topscore = 0; + int TopscoreCount = 0; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i]) + { + if(GameServer()->m_apPlayers[i]->m_Score > Topscore) + { + Topscore = GameServer()->m_apPlayers[i]->m_Score; + TopscoreCount = 1; + } + else if(GameServer()->m_apPlayers[i]->m_Score == Topscore) + TopscoreCount++; + } + } -void IGameController::DoTeamScoreWincheck() -{ - if(m_GameOverTick == -1 && !m_Warmup) - { - // check score win condition - if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || - (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) - { - if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) - EndRound(); - else - m_SuddenDeath = 1; + // check score win condition + if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + { + if(TopscoreCount == 1) + EndRound(); + else + m_SuddenDeath = 1; + } } } }*/ diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h index 849a3a347..7b52f8420 100644 --- a/src/game/server/gamecontroller.h +++ b/src/game/server/gamecontroller.h @@ -41,7 +41,6 @@ protected: float EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos); void EvaluateSpawnType(CSpawnEval *pEval, int Type); - void FindFreeSpawn(CSpawnEval *pEval, int Type); bool EvaluateSpawn(class CPlayer *pP, vec2 *pPos); //void CycleMap(); @@ -71,8 +70,12 @@ public: IGameController(class CGameContext *pGameServer); virtual ~IGameController(); +<<<<<<< HEAD //void DoTeamScoreWincheck(); //void DoPlayerScoreWincheck(); +======= + virtual void DoWincheck(); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe void DoWarmup(int Seconds); diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp index 768d09e72..c17f8aab7 100644 --- a/src/game/server/gamemodes/ctf.cpp +++ b/src/game/server/gamemodes/ctf.cpp @@ -1,7 +1,13 @@ /* (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. */ +<<<<<<< HEAD /* +======= +#include + +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe #include + #include #include #include @@ -64,6 +70,30 @@ int CGameControllerCTF::OnCharacterDeath(class CCharacter *pVictim, class CPlaye return HadFlag; } +void CGameControllerCTF::DoWincheck() +{ + if(m_GameOverTick == -1 && !m_Warmup) + { + // check score win condition + if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + { + if(m_SuddenDeath) + { + if(m_aTeamscore[TEAM_RED]/100 != m_aTeamscore[TEAM_BLUE]/100) + EndRound(); + } + else + { + if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) + EndRound(); + else + m_SuddenDeath = 1; + } + } + } +} + bool CGameControllerCTF::CanBeMovedOnBalance(int ClientID) { CCharacter* Character = GameServer()->m_apPlayers[ClientID]->GetCharacter(); @@ -118,7 +148,8 @@ void CGameControllerCTF::Tick() { IGameController::Tick(); - DoTeamScoreWincheck(); + if(GameServer()->m_World.m_ResetRequested || GameServer()->m_World.m_Paused) + return; for(int fi = 0; fi < 2; fi++) { @@ -221,13 +252,16 @@ void CGameControllerCTF::Tick() for(int c = 0; c < MAX_CLIENTS; c++) { - if(!GameServer()->m_apPlayers[c]) + CPlayer *pPlayer = GameServer()->m_apPlayers[c]; + if(!pPlayer) continue; - if(GameServer()->m_apPlayers[c]->GetTeam() == fi) - GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, GameServer()->m_apPlayers[c]->GetCID()); + if(pPlayer->GetTeam() == TEAM_SPECTATORS && pPlayer->m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[pPlayer->m_SpectatorID] && GameServer()->m_apPlayers[pPlayer->m_SpectatorID]->GetTeam() == fi) + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c); + else if(pPlayer->GetTeam() == fi) + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c); else - GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, GameServer()->m_apPlayers[c]->GetCID()); + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, c); } break; } diff --git a/src/game/server/gamemodes/ctf.h b/src/game/server/gamemodes/ctf.h index 04606a1c9..6f3af3b20 100644 --- a/src/game/server/gamemodes/ctf.h +++ b/src/game/server/gamemodes/ctf.h @@ -12,6 +12,7 @@ public: class CFlag *m_apFlags[2]; CGameControllerCTF(class CGameContext *pGameServer); + virtual void DoWincheck(); virtual bool CanBeMovedOnBalance(int ClientID); virtual void Snap(int SnappingClient); virtual void Tick(); diff --git a/src/game/server/gamemodes/dm.cpp b/src/game/server/gamemodes/dm.cpp index 6c9c1dd80..2017cfea6 100644 --- a/src/game/server/gamemodes/dm.cpp +++ b/src/game/server/gamemodes/dm.cpp @@ -12,7 +12,6 @@ CGameControllerDM::CGameControllerDM(class CGameContext *pGameServer) void CGameControllerDM::Tick() { - DoPlayerScoreWincheck(); IGameController::Tick(); } */ diff --git a/src/game/server/gamemodes/mod.cpp b/src/game/server/gamemodes/mod.cpp index a9307d4fb..03712b21d 100644 --- a/src/game/server/gamemodes/mod.cpp +++ b/src/game/server/gamemodes/mod.cpp @@ -16,8 +16,6 @@ CGameControllerMOD::CGameControllerMOD(class CGameContext *pGameServer) void CGameControllerMOD::Tick() { // this is the main part of the gamemode, this function is run every tick - DoPlayerScoreWincheck(); // checks for winners, no teams version - //DoTeamScoreWincheck(); // checks for winners, two teams version IGameController::Tick(); } diff --git a/src/game/server/gamemodes/tdm.cpp b/src/game/server/gamemodes/tdm.cpp index a538f75be..d0bb2d6a0 100644 --- a/src/game/server/gamemodes/tdm.cpp +++ b/src/game/server/gamemodes/tdm.cpp @@ -49,7 +49,6 @@ void CGameControllerTDM::Snap(int SnappingClient) void CGameControllerTDM::Tick() { - DoTeamScoreWincheck(); IGameController::Tick(); } */ diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 9d98629b5..1b07c03c3 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -27,6 +27,7 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team) m_Team = GameServer()->m_pController->ClampTeam(Team); m_SpectatorID = SPEC_FREEVIEW; m_LastActionTick = Server()->Tick(); +<<<<<<< HEAD // DDRace @@ -45,6 +46,9 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team) // Variable initialized: m_Last_Team = 0; +======= + m_TeamChangeTick = Server()->Tick(); +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe } CPlayer::~CPlayer() @@ -90,6 +94,9 @@ void CPlayer::Tick() } } + if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW) + m_ViewPos -= vec2(clamp(m_ViewPos.x-m_LatestActivity.m_TargetX, -500.0f, 500.0f), clamp(m_ViewPos.y-m_LatestActivity.m_TargetY, -400.0f, 400.0f)); + if(!m_pCharacter && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick()) m_Spawning = true; @@ -212,9 +219,19 @@ void CPlayer::OnPredictedInput(CNetObj_PlayerInput *NewInput) void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) { - // skip the input if chat is active - if((m_PlayerFlags&PLAYERFLAG_CHATTING) && (NewInput->m_PlayerFlags&PLAYERFLAG_CHATTING)) - return; + if(NewInput->m_PlayerFlags&PLAYERFLAG_CHATTING) + { + // skip the input if chat is active + if(m_PlayerFlags&PLAYERFLAG_CHATTING) + return; + + // reset input + if(m_pCharacter) + m_pCharacter->ResetInput(); + + m_PlayerFlags = NewInput->m_PlayerFlags; + return; + } m_PlayerFlags = NewInput->m_PlayerFlags; @@ -224,10 +241,13 @@ void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) if(!m_pCharacter && m_Team != TEAM_SPECTATORS && (NewInput->m_Fire&1)) m_Spawning = true; +<<<<<<< HEAD if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW) m_ViewPos = vec2(NewInput->m_TargetX, NewInput->m_TargetY); if (AfkTimer(NewInput->m_TargetX, NewInput->m_TargetY)) return; // we must return if kicked, as player struct is already deleted +======= +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe // check for activity if(NewInput->m_Direction || m_LatestActivity.m_TargetX != NewInput->m_TargetX || m_LatestActivity.m_TargetY != NewInput->m_TargetY || NewInput->m_Jump || diff --git a/src/game/server/player.h b/src/game/server/player.h index 0d3597520..654d36163 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -78,6 +78,7 @@ public: int m_ScoreStartTick; bool m_ForceBalanced; int m_LastActionTick; + int m_TeamChangeTick; struct { int m_TargetX; diff --git a/src/game/variables.h b/src/game/variables.h index 31dbd973b..26c68fc22 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -41,10 +41,18 @@ MACRO_CONFIG_INT(PlayerColorBody, player_color_body, 65408, 0, 0xFFFFFF, CFGFLAG MACRO_CONFIG_INT(PlayerColorFeet, player_color_feet, 65408, 0, 0xFFFFFF, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Player feet color", IConsole::CONSOLELEVEL_USER) MACRO_CONFIG_STR(PlayerSkin, player_skin, 24, "default", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Player skin", IConsole::CONSOLELEVEL_USER) +<<<<<<< HEAD MACRO_CONFIG_INT(UiPage, ui_page, 5, 0, 10, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface page", IConsole::CONSOLELEVEL_USER) MACRO_CONFIG_INT(UiToolboxPage, ui_toolbox_page, 0, 0, 2, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Toolbox page", IConsole::CONSOLELEVEL_USER) MACRO_CONFIG_STR(UiServerAddress, ui_server_address, 64, "localhost:8303", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface server address", IConsole::CONSOLELEVEL_USER) MACRO_CONFIG_INT(UiScale, ui_scale, 100, 50, 150, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface scale", IConsole::CONSOLELEVEL_USER) +======= +MACRO_CONFIG_INT(UiPage, ui_page, 5, 0, 10, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface page") +MACRO_CONFIG_INT(UiToolboxPage, ui_toolbox_page, 0, 0, 2, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Toolbox page") +MACRO_CONFIG_STR(UiServerAddress, ui_server_address, 64, "localhost:8303", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface server address") +MACRO_CONFIG_INT(UiScale, ui_scale, 100, 50, 150, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface scale") +MACRO_CONFIG_INT(UiMousesens, ui_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity for menus/editor") +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe MACRO_CONFIG_INT(UiColorHue, ui_color_hue, 160, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color hue", IConsole::CONSOLELEVEL_USER) MACRO_CONFIG_INT(UiColorSat, ui_color_sat, 70, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color saturation", IConsole::CONSOLELEVEL_USER) @@ -54,6 +62,7 @@ MACRO_CONFIG_INT(UiColorAlpha, ui_color_alpha, 228, 0, 255, CFGFLAG_CLIENT|CFGFL MACRO_CONFIG_INT(GfxNoclip, gfx_noclip, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Disable clipping", IConsole::CONSOLELEVEL_USER) // server +<<<<<<< HEAD MACRO_CONFIG_INT(SvWarmup, sv_warmup, 0, 0, 0, CFGFLAG_SERVER, "Number of seconds to do warmup before round starts", IConsole::CONSOLELEVEL_ADMIN) MACRO_CONFIG_STR(SvMotd, sv_motd, 900, "", CFGFLAG_SERVER, "Message of the day to display for the clients", IConsole::CONSOLELEVEL_ADMIN) MACRO_CONFIG_INT(SvTeamdamage, sv_teamdamage, 0, 0, 1, CFGFLAG_SERVER, "Team damage", IConsole::CONSOLELEVEL_ADMIN) @@ -77,6 +86,33 @@ MACRO_CONFIG_INT(SvVoteSpectate, sv_vote_spectate, 1, 0, 1, CFGFLAG_SERVER, "All MACRO_CONFIG_INT(SvVoteKick, sv_vote_kick, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to kick players", IConsole::CONSOLELEVEL_ADMIN) MACRO_CONFIG_INT(SvVoteKickMin, sv_vote_kick_min, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "Minimum number of players required to start a kick vote", IConsole::CONSOLELEVEL_ADMIN) 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", IConsole::CONSOLELEVEL_ADMIN) +======= +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") +MACRO_CONFIG_INT(SvTeamdamage, sv_teamdamage, 0, 0, 1, CFGFLAG_SERVER, "Team damage") +MACRO_CONFIG_STR(SvMaprotation, sv_maprotation, 768, "", CFGFLAG_SERVER, "Maps to rotate between") +MACRO_CONFIG_INT(SvRoundsPerMap, sv_rounds_per_map, 1, 1, 100, CFGFLAG_SERVER, "Number of rounds on each map before rotating") +MACRO_CONFIG_INT(SvPowerups, sv_powerups, 1, 0, 1, CFGFLAG_SERVER, "Allow powerups like ninja") +MACRO_CONFIG_INT(SvScorelimit, sv_scorelimit, 20, 0, 1000, CFGFLAG_SERVER, "Score limit (0 disables)") +MACRO_CONFIG_INT(SvTimelimit, sv_timelimit, 0, 0, 1000, CFGFLAG_SERVER, "Time limit in minutes (0 disables)") +MACRO_CONFIG_STR(SvGametype, sv_gametype, 32, "dm", CFGFLAG_SERVER, "Game type (dm, tdm, ctf)") +MACRO_CONFIG_INT(SvTournamentMode, sv_tournament_mode, 0, 0, 1, CFGFLAG_SERVER, "Tournament mode. When enabled, players joins the server as spectator") +MACRO_CONFIG_INT(SvSpamprotection, sv_spamprotection, 1, 0, 1, CFGFLAG_SERVER, "Spam protection") + +MACRO_CONFIG_INT(SvRespawnDelayTDM, sv_respawn_delay_tdm, 3, 0, 10, CFGFLAG_SERVER, "Time needed to respawn after death in tdm gametype") + +MACRO_CONFIG_INT(SvSpectatorSlots, sv_spectator_slots, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "Number of slots to reserve for spectators") +MACRO_CONFIG_INT(SvTeambalanceTime, sv_teambalance_time, 1, 0, 1000, CFGFLAG_SERVER, "How many minutes to wait before autobalancing teams") +MACRO_CONFIG_INT(SvInactiveKickTime, sv_inactivekick_time, 3, 0, 1000, CFGFLAG_SERVER, "How many minutes to wait before taking care of inactive players") +MACRO_CONFIG_INT(SvInactiveKick, sv_inactivekick, 1, 0, 2, CFGFLAG_SERVER, "How to deal with inactive players (0=move to spectator, 1=move to free spectator slot/kick, 2=kick)") + +MACRO_CONFIG_INT(SvStrictSpectateMode, sv_strict_spectate_mode, 0, 0, 1, CFGFLAG_SERVER, "Restricts information in spectator mode") +MACRO_CONFIG_INT(SvVoteSpectate, sv_vote_spectate, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to move players to spectators") +MACRO_CONFIG_INT(SvVoteSpectateRejoindelay, sv_vote_spectate_rejoindelay, 3, 0, 1000, CFGFLAG_SERVER, "How many minutes to wait before a player can rejoin after being moved to spectators by vote") +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") +>>>>>>> c56cfa12d511559b096579d4e7a80b7cb6bbb6fe // debug #ifdef CONF_DEBUG // this one can crash the server if not used correctly diff --git a/src/tools/dilate.cpp b/src/tools/dilate.cpp index ef862270f..eb770a908 100644 --- a/src/tools/dilate.cpp +++ b/src/tools/dilate.cpp @@ -48,18 +48,18 @@ static void CopyAlpha(int w, int h, CPixel *pSrc, CPixel *pDest) pDest[m].a = pSrc[m].a; } -int main(int argc, char **argv) +int DilateFile(const char *pFileName) { png_t Png; CPixel *pBuffer[3] = {0,0,0}; png_init(0, 0); - png_open_file(&Png, argv[1]); + png_open_file(&Png, pFileName); if(Png.color_type != PNG_TRUECOLOR_ALPHA) { - dbg_msg("dilate", "not an RGBA image"); - return -1; + dbg_msg("dilate", "%s: not an RGBA image", pFileName); + return 1; } pBuffer[0] = (CPixel*)mem_alloc(Png.width*Png.height*sizeof(CPixel), 1); @@ -81,9 +81,23 @@ int main(int argc, char **argv) CopyAlpha(w, h, pBuffer[0], pBuffer[1]); // save here - png_open_file_write(&Png, argv[1]); + png_open_file_write(&Png, pFileName); png_set_data(&Png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pBuffer[1]); png_close_file(&Png); return 0; } + +int main(int argc, const char **argv) +{ + dbg_logger_stdout(); + if(argc == 1) + { + dbg_msg("Usage", "%s FILE1 [ FILE2... ]", argv[0]); + return -1; + } + + for(int i = 1; i < argc; i++) + DilateFile(argv[i]); + return 0; +} diff --git a/src/tools/tileset_borderfix.cpp b/src/tools/tileset_borderfix.cpp index 6fb32d4b8..0facb9a3d 100644 --- a/src/tools/tileset_borderfix.cpp +++ b/src/tools/tileset_borderfix.cpp @@ -51,18 +51,18 @@ static void TilesetBorderfix(int w, int h, CPixel *pSrc, CPixel *pDest) } -int main(int argc, char **argv) +int FixFile(const char *pFileName) { png_t Png; CPixel *pBuffer[2] = {0,0}; png_init(0, 0); - png_open_file(&Png, argv[1]); + png_open_file(&Png, pFileName); if(Png.color_type != PNG_TRUECOLOR_ALPHA) { - dbg_msg("dilate", "not an RGBA image"); - return -1; + dbg_msg("tileset_borderfix", "%s: not an RGBA image", pFileName); + return 1; } int w = Png.width; @@ -76,9 +76,23 @@ int main(int argc, char **argv) TilesetBorderfix(w, h, pBuffer[0], pBuffer[1]); // save here - png_open_file_write(&Png, argv[1]); + png_open_file_write(&Png, pFileName); png_set_data(&Png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pBuffer[1]); png_close_file(&Png); return 0; } + +int main(int argc, const char **argv) +{ + dbg_logger_stdout(); + if(argc == 1) + { + dbg_msg("Usage", "%s FILE1 [ FILE2... ]", argv[0]); + return -1; + } + + for(int i = 1; i < argc; i++) + FixFile(argv[i]); + return 0; +}