diff options
| author | Teetime <anton.tsoulos@yahoo.de> | 2011-10-08 14:44:36 +0200 |
|---|---|---|
| committer | Teetime <anton.tsoulos@yahoo.de> | 2011-10-08 14:44:36 +0200 |
| commit | 093a562302af1572132a2b8b7fa827f98f65a961 (patch) | |
| tree | 66d54dde5164c8884832096030e22afca0c8f4c7 | |
| parent | de7c2a5f47cc1c854e6210ba056888e7a7ae7a08 (diff) | |
| parent | 50edfd37c0ed57ff793b79d06edd0bde1f6cf1bd (diff) | |
| download | zcatch-093a562302af1572132a2b8b7fa827f98f65a961.tar.gz zcatch-093a562302af1572132a2b8b7fa827f98f65a961.zip | |
Merge branch 'master' of github.com:Teetime/teeworlds into zcatch
337 files changed, 7623 insertions, 2496 deletions
diff --git a/data/countryflags/AD.png b/data/countryflags/AD.png new file mode 100644 index 00000000..9977c009 --- /dev/null +++ b/data/countryflags/AD.png Binary files differdiff --git a/data/countryflags/AE.png b/data/countryflags/AE.png new file mode 100644 index 00000000..7be66c75 --- /dev/null +++ b/data/countryflags/AE.png Binary files differdiff --git a/data/countryflags/AF.png b/data/countryflags/AF.png new file mode 100644 index 00000000..e7f20d1c --- /dev/null +++ b/data/countryflags/AF.png Binary files differdiff --git a/data/countryflags/AG.png b/data/countryflags/AG.png new file mode 100644 index 00000000..22fdded8 --- /dev/null +++ b/data/countryflags/AG.png Binary files differdiff --git a/data/countryflags/AI.png b/data/countryflags/AI.png new file mode 100644 index 00000000..9056a2b2 --- /dev/null +++ b/data/countryflags/AI.png Binary files differdiff --git a/data/countryflags/AL.png b/data/countryflags/AL.png new file mode 100644 index 00000000..acc74619 --- /dev/null +++ b/data/countryflags/AL.png Binary files differdiff --git a/data/countryflags/AM.png b/data/countryflags/AM.png new file mode 100644 index 00000000..139c30d9 --- /dev/null +++ b/data/countryflags/AM.png Binary files differdiff --git a/data/countryflags/AO.png b/data/countryflags/AO.png new file mode 100644 index 00000000..5b3ca638 --- /dev/null +++ b/data/countryflags/AO.png Binary files differdiff --git a/data/countryflags/AR.png b/data/countryflags/AR.png index 0ce5f92b..878458c7 100644 --- a/data/countryflags/AR.png +++ b/data/countryflags/AR.png Binary files differdiff --git a/data/countryflags/AS.png b/data/countryflags/AS.png new file mode 100644 index 00000000..b4add724 --- /dev/null +++ b/data/countryflags/AS.png Binary files differdiff --git a/data/countryflags/AT.png b/data/countryflags/AT.png index c1fbe9d4..13c6a599 100644 --- a/data/countryflags/AT.png +++ b/data/countryflags/AT.png Binary files differdiff --git a/data/countryflags/AU.png b/data/countryflags/AU.png index 361f22e4..6e334645 100644 --- a/data/countryflags/AU.png +++ b/data/countryflags/AU.png Binary files differdiff --git a/data/countryflags/AW.png b/data/countryflags/AW.png new file mode 100644 index 00000000..9c89e68c --- /dev/null +++ b/data/countryflags/AW.png Binary files differdiff --git a/data/countryflags/AX.png b/data/countryflags/AX.png new file mode 100644 index 00000000..d7fb2236 --- /dev/null +++ b/data/countryflags/AX.png Binary files differdiff --git a/data/countryflags/AZ.png b/data/countryflags/AZ.png new file mode 100644 index 00000000..49bcb835 --- /dev/null +++ b/data/countryflags/AZ.png Binary files differdiff --git a/data/countryflags/BA.png b/data/countryflags/BA.png index 3a77a439..21397f63 100644 --- a/data/countryflags/BA.png +++ b/data/countryflags/BA.png Binary files differdiff --git a/data/countryflags/BB.png b/data/countryflags/BB.png new file mode 100644 index 00000000..051b3032 --- /dev/null +++ b/data/countryflags/BB.png Binary files differdiff --git a/data/countryflags/BD.png b/data/countryflags/BD.png new file mode 100644 index 00000000..1488cf27 --- /dev/null +++ b/data/countryflags/BD.png Binary files differdiff --git a/data/countryflags/BE.png b/data/countryflags/BE.png index 409e8552..b6135ec6 100644 --- a/data/countryflags/BE.png +++ b/data/countryflags/BE.png Binary files differdiff --git a/data/countryflags/BF.png b/data/countryflags/BF.png new file mode 100644 index 00000000..fd1fa109 --- /dev/null +++ b/data/countryflags/BF.png Binary files differdiff --git a/data/countryflags/BG.png b/data/countryflags/BG.png index 7f9a6590..f323e5d0 100644 --- a/data/countryflags/BG.png +++ b/data/countryflags/BG.png Binary files differdiff --git a/data/countryflags/BH.png b/data/countryflags/BH.png new file mode 100644 index 00000000..59c8a487 --- /dev/null +++ b/data/countryflags/BH.png Binary files differdiff --git a/data/countryflags/BI.png b/data/countryflags/BI.png new file mode 100644 index 00000000..08f0ec47 --- /dev/null +++ b/data/countryflags/BI.png Binary files differdiff --git a/data/countryflags/BJ.png b/data/countryflags/BJ.png new file mode 100644 index 00000000..74725ce9 --- /dev/null +++ b/data/countryflags/BJ.png Binary files differdiff --git a/data/countryflags/BL.png b/data/countryflags/BL.png new file mode 100644 index 00000000..692401bd --- /dev/null +++ b/data/countryflags/BL.png Binary files differdiff --git a/data/countryflags/BM.png b/data/countryflags/BM.png new file mode 100644 index 00000000..9d7cd871 --- /dev/null +++ b/data/countryflags/BM.png Binary files differdiff --git a/data/countryflags/BN.png b/data/countryflags/BN.png new file mode 100644 index 00000000..38dd8cc9 --- /dev/null +++ b/data/countryflags/BN.png Binary files differdiff --git a/data/countryflags/BO.png b/data/countryflags/BO.png new file mode 100644 index 00000000..028e1fe3 --- /dev/null +++ b/data/countryflags/BO.png Binary files differdiff --git a/data/countryflags/BR.png b/data/countryflags/BR.png index aada91d5..5c2ea746 100644 --- a/data/countryflags/BR.png +++ b/data/countryflags/BR.png Binary files differdiff --git a/data/countryflags/BS.png b/data/countryflags/BS.png new file mode 100644 index 00000000..aae921df --- /dev/null +++ b/data/countryflags/BS.png Binary files differdiff --git a/data/countryflags/BT.png b/data/countryflags/BT.png new file mode 100644 index 00000000..130b9045 --- /dev/null +++ b/data/countryflags/BT.png Binary files differdiff --git a/data/countryflags/BW.png b/data/countryflags/BW.png new file mode 100644 index 00000000..d7782c6e --- /dev/null +++ b/data/countryflags/BW.png Binary files differdiff --git a/data/countryflags/BY.png b/data/countryflags/BY.png index 6052eea4..c05a3664 100644 --- a/data/countryflags/BY.png +++ b/data/countryflags/BY.png Binary files differdiff --git a/data/countryflags/BZ.png b/data/countryflags/BZ.png new file mode 100644 index 00000000..93f832e5 --- /dev/null +++ b/data/countryflags/BZ.png Binary files differdiff --git a/data/countryflags/CA.png b/data/countryflags/CA.png index 9898ba57..9d9f4a1e 100644 --- a/data/countryflags/CA.png +++ b/data/countryflags/CA.png Binary files differdiff --git a/data/countryflags/CC.png b/data/countryflags/CC.png new file mode 100644 index 00000000..ef1b1dd2 --- /dev/null +++ b/data/countryflags/CC.png Binary files differdiff --git a/data/countryflags/CD.png b/data/countryflags/CD.png new file mode 100644 index 00000000..3c5f2f95 --- /dev/null +++ b/data/countryflags/CD.png Binary files differdiff --git a/data/countryflags/CF.png b/data/countryflags/CF.png new file mode 100644 index 00000000..e42de4dc --- /dev/null +++ b/data/countryflags/CF.png Binary files differdiff --git a/data/countryflags/CG.png b/data/countryflags/CG.png new file mode 100644 index 00000000..99a97ea1 --- /dev/null +++ b/data/countryflags/CG.png Binary files differdiff --git a/data/countryflags/CH.png b/data/countryflags/CH.png index fa8d6482..27881d0c 100644 --- a/data/countryflags/CH.png +++ b/data/countryflags/CH.png Binary files differdiff --git a/data/countryflags/CI.png b/data/countryflags/CI.png new file mode 100644 index 00000000..fa80f60b --- /dev/null +++ b/data/countryflags/CI.png Binary files differdiff --git a/data/countryflags/CK.png b/data/countryflags/CK.png new file mode 100644 index 00000000..c7a8d325 --- /dev/null +++ b/data/countryflags/CK.png Binary files differdiff --git a/data/countryflags/CL.png b/data/countryflags/CL.png index 571851c7..327b27f6 100644 --- a/data/countryflags/CL.png +++ b/data/countryflags/CL.png Binary files differdiff --git a/data/countryflags/CM.png b/data/countryflags/CM.png new file mode 100644 index 00000000..9e983e2d --- /dev/null +++ b/data/countryflags/CM.png Binary files differdiff --git a/data/countryflags/CN.png b/data/countryflags/CN.png index 3251bd9a..1996131e 100644 --- a/data/countryflags/CN.png +++ b/data/countryflags/CN.png Binary files differdiff --git a/data/countryflags/CO.png b/data/countryflags/CO.png index 5fdda4a5..61f8fa55 100644 --- a/data/countryflags/CO.png +++ b/data/countryflags/CO.png Binary files differdiff --git a/data/countryflags/CR.png b/data/countryflags/CR.png new file mode 100644 index 00000000..f6a02efd --- /dev/null +++ b/data/countryflags/CR.png Binary files differdiff --git a/data/countryflags/CU.png b/data/countryflags/CU.png new file mode 100644 index 00000000..0dbee8c2 --- /dev/null +++ b/data/countryflags/CU.png Binary files differdiff --git a/data/countryflags/CV.png b/data/countryflags/CV.png new file mode 100644 index 00000000..95afe4b5 --- /dev/null +++ b/data/countryflags/CV.png Binary files differdiff --git a/data/countryflags/CW.png b/data/countryflags/CW.png new file mode 100644 index 00000000..079ec877 --- /dev/null +++ b/data/countryflags/CW.png Binary files differdiff --git a/data/countryflags/CX.png b/data/countryflags/CX.png new file mode 100644 index 00000000..12051034 --- /dev/null +++ b/data/countryflags/CX.png Binary files differdiff --git a/data/countryflags/CY.png b/data/countryflags/CY.png new file mode 100644 index 00000000..dd34d6c4 --- /dev/null +++ b/data/countryflags/CY.png Binary files differdiff --git a/data/countryflags/CZ.png b/data/countryflags/CZ.png index cb750877..b4df10db 100644 --- a/data/countryflags/CZ.png +++ b/data/countryflags/CZ.png Binary files differdiff --git a/data/countryflags/DE.png b/data/countryflags/DE.png index 699a43d8..dde74c9e 100644 --- a/data/countryflags/DE.png +++ b/data/countryflags/DE.png Binary files differdiff --git a/data/countryflags/DJ.png b/data/countryflags/DJ.png new file mode 100644 index 00000000..62a8c250 --- /dev/null +++ b/data/countryflags/DJ.png Binary files differdiff --git a/data/countryflags/DK.png b/data/countryflags/DK.png index df04cf93..4125f604 100644 --- a/data/countryflags/DK.png +++ b/data/countryflags/DK.png Binary files differdiff --git a/data/countryflags/DM.png b/data/countryflags/DM.png new file mode 100644 index 00000000..3ea6cf59 --- /dev/null +++ b/data/countryflags/DM.png Binary files differdiff --git a/data/countryflags/DO.png b/data/countryflags/DO.png new file mode 100644 index 00000000..f70848f5 --- /dev/null +++ b/data/countryflags/DO.png Binary files differdiff --git a/data/countryflags/DZ.png b/data/countryflags/DZ.png new file mode 100644 index 00000000..31df99f3 --- /dev/null +++ b/data/countryflags/DZ.png Binary files differdiff --git a/data/countryflags/EC.png b/data/countryflags/EC.png new file mode 100644 index 00000000..80dfbaa2 --- /dev/null +++ b/data/countryflags/EC.png Binary files differdiff --git a/data/countryflags/EE.png b/data/countryflags/EE.png index 45ef482f..6dc9933a 100644 --- a/data/countryflags/EE.png +++ b/data/countryflags/EE.png Binary files differdiff --git a/data/countryflags/EG.png b/data/countryflags/EG.png index 3cd437d3..b0c3bf52 100644 --- a/data/countryflags/EG.png +++ b/data/countryflags/EG.png Binary files differdiff --git a/data/countryflags/EH.png b/data/countryflags/EH.png new file mode 100644 index 00000000..9af42a3b --- /dev/null +++ b/data/countryflags/EH.png Binary files differdiff --git a/data/countryflags/ER.png b/data/countryflags/ER.png new file mode 100644 index 00000000..88dd0706 --- /dev/null +++ b/data/countryflags/ER.png Binary files differdiff --git a/data/countryflags/ES.png b/data/countryflags/ES.png index b507a328..412b2107 100644 --- a/data/countryflags/ES.png +++ b/data/countryflags/ES.png Binary files differdiff --git a/data/countryflags/ET.png b/data/countryflags/ET.png new file mode 100644 index 00000000..9588ce9a --- /dev/null +++ b/data/countryflags/ET.png Binary files differdiff --git a/data/countryflags/FI.png b/data/countryflags/FI.png index 96b39593..0373e03e 100644 --- a/data/countryflags/FI.png +++ b/data/countryflags/FI.png Binary files differdiff --git a/data/countryflags/FJ.png b/data/countryflags/FJ.png new file mode 100644 index 00000000..9d2af629 --- /dev/null +++ b/data/countryflags/FJ.png Binary files differdiff --git a/data/countryflags/FK.png b/data/countryflags/FK.png new file mode 100644 index 00000000..a3f2ad34 --- /dev/null +++ b/data/countryflags/FK.png Binary files differdiff --git a/data/countryflags/FM.png b/data/countryflags/FM.png new file mode 100644 index 00000000..c0cd4cf1 --- /dev/null +++ b/data/countryflags/FM.png Binary files differdiff --git a/data/countryflags/FO.png b/data/countryflags/FO.png new file mode 100644 index 00000000..e46c2eb3 --- /dev/null +++ b/data/countryflags/FO.png Binary files differdiff --git a/data/countryflags/FR.png b/data/countryflags/FR.png index f85cfa49..eb4ec8ab 100644 --- a/data/countryflags/FR.png +++ b/data/countryflags/FR.png Binary files differdiff --git a/data/countryflags/GA.png b/data/countryflags/GA.png new file mode 100644 index 00000000..446d380d --- /dev/null +++ b/data/countryflags/GA.png Binary files differdiff --git a/data/countryflags/GB.png b/data/countryflags/GB.png index 240adcc7..4aa46067 100644 --- a/data/countryflags/GB.png +++ b/data/countryflags/GB.png Binary files differdiff --git a/data/countryflags/GD.png b/data/countryflags/GD.png new file mode 100644 index 00000000..538e8e09 --- /dev/null +++ b/data/countryflags/GD.png Binary files differdiff --git a/data/countryflags/GE.png b/data/countryflags/GE.png new file mode 100644 index 00000000..67953847 --- /dev/null +++ b/data/countryflags/GE.png Binary files differdiff --git a/data/countryflags/GF.png b/data/countryflags/GF.png new file mode 100644 index 00000000..2b804f54 --- /dev/null +++ b/data/countryflags/GF.png Binary files differdiff --git a/data/countryflags/GG.png b/data/countryflags/GG.png new file mode 100644 index 00000000..472924fb --- /dev/null +++ b/data/countryflags/GG.png Binary files differdiff --git a/data/countryflags/GH.png b/data/countryflags/GH.png new file mode 100644 index 00000000..2809250f --- /dev/null +++ b/data/countryflags/GH.png Binary files differdiff --git a/data/countryflags/GI.png b/data/countryflags/GI.png new file mode 100644 index 00000000..9c88bfd4 --- /dev/null +++ b/data/countryflags/GI.png Binary files differdiff --git a/data/countryflags/GL.png b/data/countryflags/GL.png new file mode 100644 index 00000000..6191d23c --- /dev/null +++ b/data/countryflags/GL.png Binary files differdiff --git a/data/countryflags/GM.png b/data/countryflags/GM.png new file mode 100644 index 00000000..ce94adfd --- /dev/null +++ b/data/countryflags/GM.png Binary files differdiff --git a/data/countryflags/GN.png b/data/countryflags/GN.png new file mode 100644 index 00000000..3f698559 --- /dev/null +++ b/data/countryflags/GN.png Binary files differdiff --git a/data/countryflags/GP.png b/data/countryflags/GP.png new file mode 100644 index 00000000..bff1c31f --- /dev/null +++ b/data/countryflags/GP.png Binary files differdiff --git a/data/countryflags/GQ.png b/data/countryflags/GQ.png new file mode 100644 index 00000000..3a7dc2c5 --- /dev/null +++ b/data/countryflags/GQ.png Binary files differdiff --git a/data/countryflags/GR.png b/data/countryflags/GR.png index 18eaeb28..a9dcd902 100644 --- a/data/countryflags/GR.png +++ b/data/countryflags/GR.png Binary files differdiff --git a/data/countryflags/GS.png b/data/countryflags/GS.png new file mode 100644 index 00000000..bdeda46f --- /dev/null +++ b/data/countryflags/GS.png Binary files differdiff --git a/data/countryflags/GT.png b/data/countryflags/GT.png new file mode 100644 index 00000000..9ed9c11b --- /dev/null +++ b/data/countryflags/GT.png Binary files differdiff --git a/data/countryflags/GU.png b/data/countryflags/GU.png new file mode 100644 index 00000000..1a0affcd --- /dev/null +++ b/data/countryflags/GU.png Binary files differdiff --git a/data/countryflags/GW.png b/data/countryflags/GW.png new file mode 100644 index 00000000..b22b8d0e --- /dev/null +++ b/data/countryflags/GW.png Binary files differdiff --git a/data/countryflags/GY.png b/data/countryflags/GY.png new file mode 100644 index 00000000..35fd8c8f --- /dev/null +++ b/data/countryflags/GY.png Binary files differdiff --git a/data/countryflags/HK.png b/data/countryflags/HK.png new file mode 100644 index 00000000..6e4b18ce --- /dev/null +++ b/data/countryflags/HK.png Binary files differdiff --git a/data/countryflags/HN.png b/data/countryflags/HN.png new file mode 100644 index 00000000..09504718 --- /dev/null +++ b/data/countryflags/HN.png Binary files differdiff --git a/data/countryflags/HR.png b/data/countryflags/HR.png index 6097adc7..c844d4f4 100644 --- a/data/countryflags/HR.png +++ b/data/countryflags/HR.png Binary files differdiff --git a/data/countryflags/HT.png b/data/countryflags/HT.png new file mode 100644 index 00000000..3ab3e79f --- /dev/null +++ b/data/countryflags/HT.png Binary files differdiff --git a/data/countryflags/HU.png b/data/countryflags/HU.png index 20c21f0e..8106da42 100644 --- a/data/countryflags/HU.png +++ b/data/countryflags/HU.png Binary files differdiff --git a/data/countryflags/ID.png b/data/countryflags/ID.png index 510bac08..49b52e3c 100644 --- a/data/countryflags/ID.png +++ b/data/countryflags/ID.png Binary files differdiff --git a/data/countryflags/IE.png b/data/countryflags/IE.png new file mode 100644 index 00000000..df9be88f --- /dev/null +++ b/data/countryflags/IE.png Binary files differdiff --git a/data/countryflags/IL.png b/data/countryflags/IL.png index 3852a8f9..ce4acc73 100644 --- a/data/countryflags/IL.png +++ b/data/countryflags/IL.png Binary files differdiff --git a/data/countryflags/IM.png b/data/countryflags/IM.png new file mode 100644 index 00000000..efd55fe4 --- /dev/null +++ b/data/countryflags/IM.png Binary files differdiff --git a/data/countryflags/IN.png b/data/countryflags/IN.png index 91d34696..c4f8dbf0 100644 --- a/data/countryflags/IN.png +++ b/data/countryflags/IN.png Binary files differdiff --git a/data/countryflags/IO.png b/data/countryflags/IO.png new file mode 100644 index 00000000..e983a3f2 --- /dev/null +++ b/data/countryflags/IO.png Binary files differdiff --git a/data/countryflags/IQ.png b/data/countryflags/IQ.png new file mode 100644 index 00000000..81e7a8b4 --- /dev/null +++ b/data/countryflags/IQ.png Binary files differdiff --git a/data/countryflags/IR.png b/data/countryflags/IR.png index 141a0e56..dcb71c3a 100644 --- a/data/countryflags/IR.png +++ b/data/countryflags/IR.png Binary files differdiff --git a/data/countryflags/IS.png b/data/countryflags/IS.png new file mode 100644 index 00000000..4307fdfd --- /dev/null +++ b/data/countryflags/IS.png Binary files differdiff --git a/data/countryflags/IT.png b/data/countryflags/IT.png index 4d4a1b3e..7372d812 100644 --- a/data/countryflags/IT.png +++ b/data/countryflags/IT.png Binary files differdiff --git a/data/countryflags/JE.png b/data/countryflags/JE.png new file mode 100644 index 00000000..a6b50b0c --- /dev/null +++ b/data/countryflags/JE.png Binary files differdiff --git a/data/countryflags/JM.png b/data/countryflags/JM.png new file mode 100644 index 00000000..94cf3abf --- /dev/null +++ b/data/countryflags/JM.png Binary files differdiff --git a/data/countryflags/JO.png b/data/countryflags/JO.png new file mode 100644 index 00000000..0bb6cb65 --- /dev/null +++ b/data/countryflags/JO.png Binary files differdiff --git a/data/countryflags/JP.png b/data/countryflags/JP.png new file mode 100644 index 00000000..ddfdfb5c --- /dev/null +++ b/data/countryflags/JP.png Binary files differdiff --git a/data/countryflags/KE.png b/data/countryflags/KE.png new file mode 100644 index 00000000..e44c27d6 --- /dev/null +++ b/data/countryflags/KE.png Binary files differdiff --git a/data/countryflags/KG.png b/data/countryflags/KG.png new file mode 100644 index 00000000..1c57fbbc --- /dev/null +++ b/data/countryflags/KG.png Binary files differdiff --git a/data/countryflags/KH.png b/data/countryflags/KH.png new file mode 100644 index 00000000..102bc024 --- /dev/null +++ b/data/countryflags/KH.png Binary files differdiff --git a/data/countryflags/KI.png b/data/countryflags/KI.png new file mode 100644 index 00000000..7f62de60 --- /dev/null +++ b/data/countryflags/KI.png Binary files differdiff --git a/data/countryflags/KM.png b/data/countryflags/KM.png new file mode 100644 index 00000000..f6a77ecc --- /dev/null +++ b/data/countryflags/KM.png Binary files differdiff --git a/data/countryflags/KN.png b/data/countryflags/KN.png new file mode 100644 index 00000000..06f6a9f9 --- /dev/null +++ b/data/countryflags/KN.png Binary files differdiff --git a/data/countryflags/KP.png b/data/countryflags/KP.png new file mode 100644 index 00000000..48093061 --- /dev/null +++ b/data/countryflags/KP.png Binary files differdiff --git a/data/countryflags/KR.png b/data/countryflags/KR.png new file mode 100644 index 00000000..32a83733 --- /dev/null +++ b/data/countryflags/KR.png Binary files differdiff --git a/data/countryflags/KW.png b/data/countryflags/KW.png new file mode 100644 index 00000000..557e38b1 --- /dev/null +++ b/data/countryflags/KW.png Binary files differdiff --git a/data/countryflags/KY.png b/data/countryflags/KY.png new file mode 100644 index 00000000..020dbeef --- /dev/null +++ b/data/countryflags/KY.png Binary files differdiff --git a/data/countryflags/KZ.png b/data/countryflags/KZ.png index e3db4f93..d0b6eede 100644 --- a/data/countryflags/KZ.png +++ b/data/countryflags/KZ.png Binary files differdiff --git a/data/countryflags/LA.png b/data/countryflags/LA.png new file mode 100644 index 00000000..bfe966f6 --- /dev/null +++ b/data/countryflags/LA.png Binary files differdiff --git a/data/countryflags/LB.png b/data/countryflags/LB.png new file mode 100644 index 00000000..3c8ef00c --- /dev/null +++ b/data/countryflags/LB.png Binary files differdiff --git a/data/countryflags/LC.png b/data/countryflags/LC.png new file mode 100644 index 00000000..19b5f78a --- /dev/null +++ b/data/countryflags/LC.png Binary files differdiff --git a/data/countryflags/LI.png b/data/countryflags/LI.png new file mode 100644 index 00000000..06daeeb8 --- /dev/null +++ b/data/countryflags/LI.png Binary files differdiff --git a/data/countryflags/LK.png b/data/countryflags/LK.png new file mode 100644 index 00000000..f7710ae9 --- /dev/null +++ b/data/countryflags/LK.png Binary files differdiff --git a/data/countryflags/LR.png b/data/countryflags/LR.png new file mode 100644 index 00000000..6d361dff --- /dev/null +++ b/data/countryflags/LR.png Binary files differdiff --git a/data/countryflags/LS.png b/data/countryflags/LS.png new file mode 100644 index 00000000..02a844fe --- /dev/null +++ b/data/countryflags/LS.png Binary files differdiff --git a/data/countryflags/LT.png b/data/countryflags/LT.png index dd1d3d13..c37e857f 100644 --- a/data/countryflags/LT.png +++ b/data/countryflags/LT.png Binary files differdiff --git a/data/countryflags/LU.png b/data/countryflags/LU.png index 01377c56..ed89b616 100644 --- a/data/countryflags/LU.png +++ b/data/countryflags/LU.png Binary files differdiff --git a/data/countryflags/LV.png b/data/countryflags/LV.png index 8915e25d..f5f51afc 100644 --- a/data/countryflags/LV.png +++ b/data/countryflags/LV.png Binary files differdiff --git a/data/countryflags/LY.png b/data/countryflags/LY.png new file mode 100644 index 00000000..1dda0198 --- /dev/null +++ b/data/countryflags/LY.png Binary files differdiff --git a/data/countryflags/MA.png b/data/countryflags/MA.png new file mode 100644 index 00000000..0ae330bb --- /dev/null +++ b/data/countryflags/MA.png Binary files differdiff --git a/data/countryflags/MC.png b/data/countryflags/MC.png new file mode 100644 index 00000000..5a73c3c1 --- /dev/null +++ b/data/countryflags/MC.png Binary files differdiff --git a/data/countryflags/MD.png b/data/countryflags/MD.png new file mode 100644 index 00000000..d713b079 --- /dev/null +++ b/data/countryflags/MD.png Binary files differdiff --git a/data/countryflags/ME.png b/data/countryflags/ME.png new file mode 100644 index 00000000..f6348bda --- /dev/null +++ b/data/countryflags/ME.png Binary files differdiff --git a/data/countryflags/MF.png b/data/countryflags/MF.png new file mode 100644 index 00000000..0fa201c9 --- /dev/null +++ b/data/countryflags/MF.png Binary files differdiff --git a/data/countryflags/MG.png b/data/countryflags/MG.png new file mode 100644 index 00000000..52030837 --- /dev/null +++ b/data/countryflags/MG.png Binary files differdiff --git a/data/countryflags/MH.png b/data/countryflags/MH.png new file mode 100644 index 00000000..40d7bd3d --- /dev/null +++ b/data/countryflags/MH.png Binary files differdiff --git a/data/countryflags/MK.png b/data/countryflags/MK.png new file mode 100644 index 00000000..3743d81a --- /dev/null +++ b/data/countryflags/MK.png Binary files differdiff --git a/data/countryflags/ML.png b/data/countryflags/ML.png new file mode 100644 index 00000000..d548a1e2 --- /dev/null +++ b/data/countryflags/ML.png Binary files differdiff --git a/data/countryflags/MM.png b/data/countryflags/MM.png new file mode 100644 index 00000000..58c098b8 --- /dev/null +++ b/data/countryflags/MM.png Binary files differdiff --git a/data/countryflags/MN.png b/data/countryflags/MN.png new file mode 100644 index 00000000..1d9f5159 --- /dev/null +++ b/data/countryflags/MN.png Binary files differdiff --git a/data/countryflags/MO.png b/data/countryflags/MO.png new file mode 100644 index 00000000..6adda298 --- /dev/null +++ b/data/countryflags/MO.png Binary files differdiff --git a/data/countryflags/MP.png b/data/countryflags/MP.png new file mode 100644 index 00000000..6b308645 --- /dev/null +++ b/data/countryflags/MP.png Binary files differdiff --git a/data/countryflags/MQ.png b/data/countryflags/MQ.png new file mode 100644 index 00000000..2f3c556a --- /dev/null +++ b/data/countryflags/MQ.png Binary files differdiff --git a/data/countryflags/MR.png b/data/countryflags/MR.png new file mode 100644 index 00000000..8c6462ba --- /dev/null +++ b/data/countryflags/MR.png Binary files differdiff --git a/data/countryflags/MS.png b/data/countryflags/MS.png new file mode 100644 index 00000000..27312023 --- /dev/null +++ b/data/countryflags/MS.png Binary files differdiff --git a/data/countryflags/MT.png b/data/countryflags/MT.png new file mode 100644 index 00000000..043ea73e --- /dev/null +++ b/data/countryflags/MT.png Binary files differdiff --git a/data/countryflags/MU.png b/data/countryflags/MU.png new file mode 100644 index 00000000..02a5f3af --- /dev/null +++ b/data/countryflags/MU.png Binary files differdiff --git a/data/countryflags/MV.png b/data/countryflags/MV.png new file mode 100644 index 00000000..6a3fd961 --- /dev/null +++ b/data/countryflags/MV.png Binary files differdiff --git a/data/countryflags/MW.png b/data/countryflags/MW.png new file mode 100644 index 00000000..5d26e5f7 --- /dev/null +++ b/data/countryflags/MW.png Binary files differdiff --git a/data/countryflags/MX.png b/data/countryflags/MX.png index 9505d623..19c7ddea 100644 --- a/data/countryflags/MX.png +++ b/data/countryflags/MX.png Binary files differdiff --git a/data/countryflags/MY.png b/data/countryflags/MY.png new file mode 100644 index 00000000..885ba638 --- /dev/null +++ b/data/countryflags/MY.png Binary files differdiff --git a/data/countryflags/MZ.png b/data/countryflags/MZ.png new file mode 100644 index 00000000..caa5cb4d --- /dev/null +++ b/data/countryflags/MZ.png Binary files differdiff --git a/data/countryflags/NA.png b/data/countryflags/NA.png new file mode 100644 index 00000000..f6936933 --- /dev/null +++ b/data/countryflags/NA.png Binary files differdiff --git a/data/countryflags/NC.png b/data/countryflags/NC.png new file mode 100644 index 00000000..5f3b0892 --- /dev/null +++ b/data/countryflags/NC.png Binary files differdiff --git a/data/countryflags/NE.png b/data/countryflags/NE.png new file mode 100644 index 00000000..7d73f48e --- /dev/null +++ b/data/countryflags/NE.png Binary files differdiff --git a/data/countryflags/NF.png b/data/countryflags/NF.png new file mode 100644 index 00000000..5f5401d7 --- /dev/null +++ b/data/countryflags/NF.png Binary files differdiff --git a/data/countryflags/NG.png b/data/countryflags/NG.png new file mode 100644 index 00000000..e9030716 --- /dev/null +++ b/data/countryflags/NG.png Binary files differdiff --git a/data/countryflags/NI.png b/data/countryflags/NI.png new file mode 100644 index 00000000..19df0e95 --- /dev/null +++ b/data/countryflags/NI.png Binary files differdiff --git a/data/countryflags/NL.png b/data/countryflags/NL.png index 5882be49..ba54b03c 100644 --- a/data/countryflags/NL.png +++ b/data/countryflags/NL.png Binary files differdiff --git a/data/countryflags/NO.png b/data/countryflags/NO.png index 2e9bbec9..6f085323 100644 --- a/data/countryflags/NO.png +++ b/data/countryflags/NO.png Binary files differdiff --git a/data/countryflags/NP.png b/data/countryflags/NP.png new file mode 100644 index 00000000..53c7510a --- /dev/null +++ b/data/countryflags/NP.png Binary files differdiff --git a/data/countryflags/NR.png b/data/countryflags/NR.png new file mode 100644 index 00000000..562be4fc --- /dev/null +++ b/data/countryflags/NR.png Binary files differdiff --git a/data/countryflags/NU.png b/data/countryflags/NU.png new file mode 100644 index 00000000..1928bab6 --- /dev/null +++ b/data/countryflags/NU.png Binary files differdiff --git a/data/countryflags/NZ.png b/data/countryflags/NZ.png new file mode 100644 index 00000000..4e210491 --- /dev/null +++ b/data/countryflags/NZ.png Binary files differdiff --git a/data/countryflags/OM.png b/data/countryflags/OM.png new file mode 100644 index 00000000..8b6aba47 --- /dev/null +++ b/data/countryflags/OM.png Binary files differdiff --git a/data/countryflags/PA.png b/data/countryflags/PA.png new file mode 100644 index 00000000..ef051c61 --- /dev/null +++ b/data/countryflags/PA.png Binary files differdiff --git a/data/countryflags/PE.png b/data/countryflags/PE.png new file mode 100644 index 00000000..f6188650 --- /dev/null +++ b/data/countryflags/PE.png Binary files differdiff --git a/data/countryflags/PF.png b/data/countryflags/PF.png new file mode 100644 index 00000000..bf04554d --- /dev/null +++ b/data/countryflags/PF.png Binary files differdiff --git a/data/countryflags/PG.png b/data/countryflags/PG.png new file mode 100644 index 00000000..04b36a38 --- /dev/null +++ b/data/countryflags/PG.png Binary files differdiff --git a/data/countryflags/PH.png b/data/countryflags/PH.png index ebcec1f2..edb506c0 100644 --- a/data/countryflags/PH.png +++ b/data/countryflags/PH.png Binary files differdiff --git a/data/countryflags/PK.png b/data/countryflags/PK.png index 6e4e4950..6030b86b 100644 --- a/data/countryflags/PK.png +++ b/data/countryflags/PK.png Binary files differdiff --git a/data/countryflags/PL.png b/data/countryflags/PL.png index 6e6a5c95..03b9e71f 100644 --- a/data/countryflags/PL.png +++ b/data/countryflags/PL.png Binary files differdiff --git a/data/countryflags/PM.png b/data/countryflags/PM.png new file mode 100644 index 00000000..dd0dce93 --- /dev/null +++ b/data/countryflags/PM.png Binary files differdiff --git a/data/countryflags/PN.png b/data/countryflags/PN.png new file mode 100644 index 00000000..a14628bb --- /dev/null +++ b/data/countryflags/PN.png Binary files differdiff --git a/data/countryflags/PR.png b/data/countryflags/PR.png new file mode 100644 index 00000000..6a3f6014 --- /dev/null +++ b/data/countryflags/PR.png Binary files differdiff --git a/data/countryflags/PT.png b/data/countryflags/PT.png index b3be0973..ca90e6e9 100644 --- a/data/countryflags/PT.png +++ b/data/countryflags/PT.png Binary files differdiff --git a/data/countryflags/PW.png b/data/countryflags/PW.png new file mode 100644 index 00000000..49ee4480 --- /dev/null +++ b/data/countryflags/PW.png Binary files differdiff --git a/data/countryflags/PY.png b/data/countryflags/PY.png new file mode 100644 index 00000000..948f7151 --- /dev/null +++ b/data/countryflags/PY.png Binary files differdiff --git a/data/countryflags/QA.png b/data/countryflags/QA.png new file mode 100644 index 00000000..cae22d2b --- /dev/null +++ b/data/countryflags/QA.png Binary files differdiff --git a/data/countryflags/RE.png b/data/countryflags/RE.png new file mode 100644 index 00000000..5efe1da4 --- /dev/null +++ b/data/countryflags/RE.png Binary files differdiff --git a/data/countryflags/RO.png b/data/countryflags/RO.png index 4eae6d22..6ea76a88 100644 --- a/data/countryflags/RO.png +++ b/data/countryflags/RO.png Binary files differdiff --git a/data/countryflags/RS.png b/data/countryflags/RS.png index 7cc06394..24e067b4 100644 --- a/data/countryflags/RS.png +++ b/data/countryflags/RS.png Binary files differdiff --git a/data/countryflags/RU.png b/data/countryflags/RU.png index ed293810..74a8528f 100644 --- a/data/countryflags/RU.png +++ b/data/countryflags/RU.png Binary files differdiff --git a/data/countryflags/RW.png b/data/countryflags/RW.png new file mode 100644 index 00000000..57d3ac5f --- /dev/null +++ b/data/countryflags/RW.png Binary files differdiff --git a/data/countryflags/SA.png b/data/countryflags/SA.png index fbfb6cf5..93598245 100644 --- a/data/countryflags/SA.png +++ b/data/countryflags/SA.png Binary files differdiff --git a/data/countryflags/SB.png b/data/countryflags/SB.png new file mode 100644 index 00000000..7c4c468d --- /dev/null +++ b/data/countryflags/SB.png Binary files differdiff --git a/data/countryflags/SC.png b/data/countryflags/SC.png new file mode 100644 index 00000000..5237f2e8 --- /dev/null +++ b/data/countryflags/SC.png Binary files differdiff --git a/data/countryflags/SD.png b/data/countryflags/SD.png new file mode 100644 index 00000000..6adab5ad --- /dev/null +++ b/data/countryflags/SD.png Binary files differdiff --git a/data/countryflags/SE.png b/data/countryflags/SE.png index c5c9cda9..47dcef3f 100644 --- a/data/countryflags/SE.png +++ b/data/countryflags/SE.png Binary files differdiff --git a/data/countryflags/SG.png b/data/countryflags/SG.png new file mode 100644 index 00000000..c54a7608 --- /dev/null +++ b/data/countryflags/SG.png Binary files differdiff --git a/data/countryflags/SH.png b/data/countryflags/SH.png new file mode 100644 index 00000000..a9b6fc9f --- /dev/null +++ b/data/countryflags/SH.png Binary files differdiff --git a/data/countryflags/SI.png b/data/countryflags/SI.png new file mode 100644 index 00000000..1e553fd5 --- /dev/null +++ b/data/countryflags/SI.png Binary files differdiff --git a/data/countryflags/SK.png b/data/countryflags/SK.png index 5bd535a1..9c927e8f 100644 --- a/data/countryflags/SK.png +++ b/data/countryflags/SK.png Binary files differdiff --git a/data/countryflags/SL.png b/data/countryflags/SL.png new file mode 100644 index 00000000..5d1d42fe --- /dev/null +++ b/data/countryflags/SL.png Binary files differdiff --git a/data/countryflags/SM.png b/data/countryflags/SM.png new file mode 100644 index 00000000..96726305 --- /dev/null +++ b/data/countryflags/SM.png Binary files differdiff --git a/data/countryflags/SN.png b/data/countryflags/SN.png new file mode 100644 index 00000000..42a57326 --- /dev/null +++ b/data/countryflags/SN.png Binary files differdiff --git a/data/countryflags/SO.png b/data/countryflags/SO.png new file mode 100644 index 00000000..93815d27 --- /dev/null +++ b/data/countryflags/SO.png Binary files differdiff --git a/data/countryflags/SR.png b/data/countryflags/SR.png new file mode 100644 index 00000000..06fe40f3 --- /dev/null +++ b/data/countryflags/SR.png Binary files differdiff --git a/data/countryflags/SS.png b/data/countryflags/SS.png new file mode 100644 index 00000000..203c51da --- /dev/null +++ b/data/countryflags/SS.png Binary files differdiff --git a/data/countryflags/ST.png b/data/countryflags/ST.png new file mode 100644 index 00000000..748030a0 --- /dev/null +++ b/data/countryflags/ST.png Binary files differdiff --git a/data/countryflags/SV.png b/data/countryflags/SV.png index d313b0c1..d03b8be4 100644 --- a/data/countryflags/SV.png +++ b/data/countryflags/SV.png Binary files differdiff --git a/data/countryflags/SX.png b/data/countryflags/SX.png new file mode 100644 index 00000000..61606935 --- /dev/null +++ b/data/countryflags/SX.png Binary files differdiff --git a/data/countryflags/SY.png b/data/countryflags/SY.png new file mode 100644 index 00000000..04342871 --- /dev/null +++ b/data/countryflags/SY.png Binary files differdiff --git a/data/countryflags/SZ.png b/data/countryflags/SZ.png new file mode 100644 index 00000000..bea99b2c --- /dev/null +++ b/data/countryflags/SZ.png Binary files differdiff --git a/data/countryflags/TC.png b/data/countryflags/TC.png new file mode 100644 index 00000000..0abdc845 --- /dev/null +++ b/data/countryflags/TC.png Binary files differdiff --git a/data/countryflags/TD.png b/data/countryflags/TD.png new file mode 100644 index 00000000..4ffcc670 --- /dev/null +++ b/data/countryflags/TD.png Binary files differdiff --git a/data/countryflags/TF.png b/data/countryflags/TF.png new file mode 100644 index 00000000..d3095b29 --- /dev/null +++ b/data/countryflags/TF.png Binary files differdiff --git a/data/countryflags/TG.png b/data/countryflags/TG.png new file mode 100644 index 00000000..797f48cd --- /dev/null +++ b/data/countryflags/TG.png Binary files differdiff --git a/data/countryflags/TH.png b/data/countryflags/TH.png new file mode 100644 index 00000000..97e22df3 --- /dev/null +++ b/data/countryflags/TH.png Binary files differdiff --git a/data/countryflags/TJ.png b/data/countryflags/TJ.png new file mode 100644 index 00000000..54d9830f --- /dev/null +++ b/data/countryflags/TJ.png Binary files differdiff --git a/data/countryflags/TK.png b/data/countryflags/TK.png new file mode 100644 index 00000000..623f69e0 --- /dev/null +++ b/data/countryflags/TK.png Binary files differdiff --git a/data/countryflags/TL.png b/data/countryflags/TL.png new file mode 100644 index 00000000..5c6f1ff7 --- /dev/null +++ b/data/countryflags/TL.png Binary files differdiff --git a/data/countryflags/TM.png b/data/countryflags/TM.png new file mode 100644 index 00000000..40767589 --- /dev/null +++ b/data/countryflags/TM.png Binary files differdiff --git a/data/countryflags/TN.png b/data/countryflags/TN.png new file mode 100644 index 00000000..10f9f696 --- /dev/null +++ b/data/countryflags/TN.png Binary files differdiff --git a/data/countryflags/TO.png b/data/countryflags/TO.png new file mode 100644 index 00000000..a7a703c9 --- /dev/null +++ b/data/countryflags/TO.png Binary files differdiff --git a/data/countryflags/TR.png b/data/countryflags/TR.png index 8bc78a72..2df563de 100644 --- a/data/countryflags/TR.png +++ b/data/countryflags/TR.png Binary files differdiff --git a/data/countryflags/TT.png b/data/countryflags/TT.png new file mode 100644 index 00000000..c3f21b2b --- /dev/null +++ b/data/countryflags/TT.png Binary files differdiff --git a/data/countryflags/TV.png b/data/countryflags/TV.png new file mode 100644 index 00000000..ede2dbca --- /dev/null +++ b/data/countryflags/TV.png Binary files differdiff --git a/data/countryflags/TW.png b/data/countryflags/TW.png new file mode 100644 index 00000000..e3c48eb2 --- /dev/null +++ b/data/countryflags/TW.png Binary files differdiff --git a/data/countryflags/TZ.png b/data/countryflags/TZ.png new file mode 100644 index 00000000..38ad129d --- /dev/null +++ b/data/countryflags/TZ.png Binary files differdiff --git a/data/countryflags/UA.png b/data/countryflags/UA.png index 1356e7cb..f9fc86cd 100644 --- a/data/countryflags/UA.png +++ b/data/countryflags/UA.png Binary files differdiff --git a/data/countryflags/UG.png b/data/countryflags/UG.png new file mode 100644 index 00000000..92537e9b --- /dev/null +++ b/data/countryflags/UG.png Binary files differdiff --git a/data/countryflags/US.png b/data/countryflags/US.png index 96dfca89..448930c2 100644 --- a/data/countryflags/US.png +++ b/data/countryflags/US.png Binary files differdiff --git a/data/countryflags/UY.png b/data/countryflags/UY.png new file mode 100644 index 00000000..4c950068 --- /dev/null +++ b/data/countryflags/UY.png Binary files differdiff --git a/data/countryflags/UZ.png b/data/countryflags/UZ.png new file mode 100644 index 00000000..0689f217 --- /dev/null +++ b/data/countryflags/UZ.png Binary files differdiff --git a/data/countryflags/VA.png b/data/countryflags/VA.png new file mode 100644 index 00000000..e957e06d --- /dev/null +++ b/data/countryflags/VA.png Binary files differdiff --git a/data/countryflags/VC.png b/data/countryflags/VC.png new file mode 100644 index 00000000..60f90b55 --- /dev/null +++ b/data/countryflags/VC.png Binary files differdiff --git a/data/countryflags/VE.png b/data/countryflags/VE.png new file mode 100644 index 00000000..e36f7a4b --- /dev/null +++ b/data/countryflags/VE.png Binary files differdiff --git a/data/countryflags/VG.png b/data/countryflags/VG.png new file mode 100644 index 00000000..99069e25 --- /dev/null +++ b/data/countryflags/VG.png Binary files differdiff --git a/data/countryflags/VI.png b/data/countryflags/VI.png new file mode 100644 index 00000000..05fa911a --- /dev/null +++ b/data/countryflags/VI.png Binary files differdiff --git a/data/countryflags/VN.png b/data/countryflags/VN.png new file mode 100644 index 00000000..c4e19d79 --- /dev/null +++ b/data/countryflags/VN.png Binary files differdiff --git a/data/countryflags/VU.png b/data/countryflags/VU.png new file mode 100644 index 00000000..228083ba --- /dev/null +++ b/data/countryflags/VU.png Binary files differdiff --git a/data/countryflags/WF.png b/data/countryflags/WF.png new file mode 100644 index 00000000..aeeba249 --- /dev/null +++ b/data/countryflags/WF.png Binary files differdiff --git a/data/countryflags/WS.png b/data/countryflags/WS.png new file mode 100644 index 00000000..5d16a5d9 --- /dev/null +++ b/data/countryflags/WS.png Binary files differdiff --git a/data/countryflags/XEN.png b/data/countryflags/XEN.png index 8388316c..08e3ef29 100644 --- a/data/countryflags/XEN.png +++ b/data/countryflags/XEN.png Binary files differdiff --git a/data/countryflags/XNI.png b/data/countryflags/XNI.png index 410615b0..18083db0 100644 --- a/data/countryflags/XNI.png +++ b/data/countryflags/XNI.png Binary files differdiff --git a/data/countryflags/XSC.png b/data/countryflags/XSC.png index f3b9ee03..3002fd50 100644 --- a/data/countryflags/XSC.png +++ b/data/countryflags/XSC.png Binary files differdiff --git a/data/countryflags/XWA.png b/data/countryflags/XWA.png index bae3809e..3b6b86be 100644 --- a/data/countryflags/XWA.png +++ b/data/countryflags/XWA.png Binary files differdiff --git a/data/countryflags/YE.png b/data/countryflags/YE.png new file mode 100644 index 00000000..73686007 --- /dev/null +++ b/data/countryflags/YE.png Binary files differdiff --git a/data/countryflags/ZA.png b/data/countryflags/ZA.png index c7e30de0..21aec1ed 100644 --- a/data/countryflags/ZA.png +++ b/data/countryflags/ZA.png Binary files differdiff --git a/data/countryflags/ZM.png b/data/countryflags/ZM.png new file mode 100644 index 00000000..2160d527 --- /dev/null +++ b/data/countryflags/ZM.png Binary files differdiff --git a/data/countryflags/ZW.png b/data/countryflags/ZW.png new file mode 100644 index 00000000..b6c870ca --- /dev/null +++ b/data/countryflags/ZW.png Binary files differdiff --git a/data/countryflags/index.txt b/data/countryflags/index.txt index f841c194..3b60f1b2 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 00000000..0102a197 --- /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 00000000..b909eb0e --- /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 00000000..ada1f3ac --- /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 00000000..eaeaec8e --- /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 00000000..672995c0 --- /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 d5c9ccd8..4888db6a 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 @@ -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,23 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Player country: -== - -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 00000000..a83a0d05 --- /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 80b043a3..38df7891 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 == Настройки на Играча @@ -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,33 +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 -== - -Player country: -== - -Spectate next -== - Spectate previous == -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/czech.txt b/data/languages/czech.txt index 7f26b274..5d50f47a 100644 --- a/data/languages/czech.txt +++ b/data/languages/czech.txt @@ -1,3 +1,4 @@ + ##### translated strings ##### %d Bytes @@ -25,7 +26,7 @@ == Zbývá sekund: %i %s wins! -== %s zvítězil! +== Vítězí %s! -Page %d- == -Strana %d- @@ -397,7 +398,7 @@ Player == Hráč Player country: -== Národnost hráče: +== Národnost: Player options == Nastavení hráčů @@ -550,7 +551,7 @@ Spectate next == Pozorovat dalšího Spectate previous -== Pozorovat předchozího +== Pozorovat minulého Spectator mode == Nastavit pozorování @@ -583,7 +584,7 @@ Team chat == Týmový chat Teeworlds %s is out! Download it at www.teeworlds.com! -== Vyšlo nové Teeworlds %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 diff --git a/data/languages/dutch.txt b/data/languages/dutch.txt index 978ff608..4375f784 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 @@ -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,14 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Player country: -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/finnish.txt b/data/languages/finnish.txt index 8aab046f..77ce6cf7 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 @@ -509,7 +518,7 @@ Show chat == Näytä chatti Show friends only -== Näytä ystävät +== 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,23 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Player country: -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/hungarian.txt b/data/languages/hungarian.txt new file mode 100644 index 00000000..49f3116a --- /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 fa5862c9..b7a6179b 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 4e81ec97..d566da49 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 @@ -509,7 +518,7 @@ Show chat == Mostra chat Show friends only -== Mostra amici +== 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,39 +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 -== - -Player country: -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### + +== ## translated strings ##### + diff --git a/data/languages/polish.txt b/data/languages/polish.txt index 6e651e75..e955e1e6 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,225 +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 country: -== - -Player options -== - -Quit anyway? -== - -REC %3d:%02d -== - -Reason: -== - -Record demo -== - -Remove -== - -Remove friend -== - -Rename -== - -Rename demo -== - -Server address: -== - -Server filter -== - -Show friends only -== - -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: -== +no limit +== bez limitu -Vote command: -== +##### needs translation ##### -Vote description: -== +##### old translations ##### -no limit +utracić swojej pracy! == -##### old translations ##### - diff --git a/data/languages/portuguese.txt b/data/languages/portuguese.txt index e129a23b..e3c7c6cb 100644 --- a/data/languages/portuguese.txt +++ b/data/languages/portuguese.txt @@ -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,40 +482,40 @@ 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 only == Mostrar amigos @@ -521,10 +524,10 @@ 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,18 +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 ##### -Player country: -== - ##### old translations ##### diff --git a/data/languages/romanian.txt b/data/languages/romanian.txt index 41bc3774..e38faadb 100644 --- a/data/languages/romanian.txt +++ b/data/languages/romanian.txt @@ -332,7 +332,7 @@ Movement == Mișcare Mute when not active -== Mută la inactivate +== Opreşte sunetul la inactivate Name == Nume diff --git a/data/languages/russian.txt b/data/languages/russian.txt index 0e4a7c22..329f4a69 100644 --- a/data/languages/russian.txt +++ b/data/languages/russian.txt @@ -2,7 +2,7 @@ ##### translated strings ##### %d Bytes -== %d Байтов +== %d байт %d of %d servers, %d players == %d из %d серверов, %d игроков @@ -14,19 +14,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 +47,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 +83,7 @@ Call vote == Голосовать Change settings -== Настройки +== Изменить настройки Chat == Чат @@ -95,19 +95,19 @@ Client == Клиент Close -== Закрыть +== Выход Compatible version == Совместимая версия Connect -== Играть +== Подключиться Connecting to -== Соединяемся с +== Подключение к Connection Problems... -== Проблемы с соединением... +== Проблемы со связью... Console == Консоль @@ -119,10 +119,10 @@ Count players only == Считать только игроков Country -== Страна +== Флаг вашей страны Crc: -== Crc-сумма: +== Crc: Created: == Создан: @@ -134,7 +134,7 @@ Current version: %s == Текущая версия: %s Custom colors -== Произвольный цвет +== Свои цвета Delete == Удалить @@ -143,79 +143,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 +227,7 @@ Game type == Тип игры Game types: -== Типы игры: +== Тип игры: General == Основные @@ -233,19 +236,19 @@ Graphics == Графика Grenade -== Граната +== Гранатомёт Hammer -== Молоток +== Молот Has people playing -== Есть игроки на сервере +== Не пустой сервер High Detail -== Высокая Детализация +== Высокая детализация Hook -== Цепь +== Крюк Host address == Адрес сервера @@ -260,22 +263,22 @@ Internet == Интернет Invalid Demo -== Невалидное демо +== Недопустимое демо Join blue -== К синим +== За синих Join game -== Войти в игру +== Играть Join red -== К красным +== За красных Jump == Прыжок Kick player -== Кикнуть игрока +== Забанить игрока LAN == LAN @@ -284,7 +287,7 @@ Language == Язык Length: -== Длина: +== Длина Lht. == Яркость @@ -302,10 +305,10 @@ Map: == Карта: Max Screenshots -== Максимум Скриншотов +== Максимальное количество снимков Max demos -== Максимум Демо +== Максимальное количество демо Maximum ping: == Макс. пинг: @@ -314,40 +317,40 @@ 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 == Ник @@ -362,22 +365,22 @@ No servers found == Сервера не найдены No servers match your filter criteria -== Нет серверов, подходящих под Ваш фильтр +== Нет серверов, подходящих под ваш фильтр Ok -== Ok +== ОК Open == Открыть Parent Folder -== Корневая папка +== Родительский каталог Password == Пароль Password incorrect -== Неверный пароль +== Пароль Ping == Пинг @@ -386,40 +389,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 +434,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 +482,28 @@ Score == Очки Score board -== Результат +== Табло Score limit -== Лимит на очки +== Лимит очков Scoreboard -== Результат +== Табло Screenshot -== Скриншот +== Снимок Server address: -== Адрес сервера: +== Адрес сервера Server details -== Информация о сервере +== Детали сервера Server filter -== Фильтр сервера +== Фильтр серверов Server info -== Инфо +== Информация Server not full == Сервер не заполнен @@ -512,22 +518,22 @@ Show chat == Показать чат Show friends only -== Показывать друзей +== Только с друзьями Show ingame HUD -== Показывать HUD +== Показывать внутриигровой HUD Show name plates -== Показывать имена над игроками +== Показывать ники игроков Show only supported -== Показывать только поддерживаемые +== Показывать только поддерживаемые разрешения экрана Size: -== Размер +== Размер: Skins -== Модели +== Скины Sound == Звук @@ -536,16 +542,16 @@ Sound error == Звуковая ошибка Sound volume -== Громкость +== Громкость звука Spectate == Наблюдать Spectate next -== Наблюдать следующего +== Наблюдать след. Spectate previous -== Наблюдать предыдущего +== Наблюдать пред. Spectator mode == Наблюдатель @@ -560,13 +566,16 @@ Standard map == Стандартная карта Stop record -== Остановить запись +== Стоп + +Strict gametype filter +== Строгий фильтр режим. Sudden Death -== Внезапная смерть +== Быстрая смерть Switch weapon on pickup -== Сменить оружие на подобранное +== Переключать оружие при подборе Team == Команда @@ -575,28 +584,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 +623,10 @@ Unable to rename the demo == Невозможно переименовать демо Use sounds -== Звук +== Использовать звуки Use team colors for name plates -== Использовать цвет команды для имён игроков +== Командные цвета для ников игроков V-Sync == Вертикальная синхронизация @@ -629,10 +638,10 @@ Version: == Версия: Vote command: -== Голосование: +== Комманда голосования: Vote description: -== Причина голосования: +== Описание голосования: Vote no == Против @@ -644,7 +653,7 @@ Voting == Голосование Warmup -== Разогрев +== Разминка Weapon == Оружие @@ -656,7 +665,7 @@ Yes == Да You must restart the game for all settings to take effect. -== Вы должны перезапустить игру, чтобы настройки применились. +== Перезапустите игру для применения изменений. Your skin == Ваш скин @@ -666,14 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Player country: -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/serbian.txt b/data/languages/serbian.txt index 64c8fb37..e4ec95c2 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,223 +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 country: -== - -Player options -== - -Quit anyway? -== - -REC %3d:%02d -== - -Reason: -== - -Record demo -== - -Remove -== - -Remove friend -== - -Rename -== - -Rename demo -== - -Server address: -== - -Server filter -== - -Show friends only -== - -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 57f96e25..48816234 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 @@ -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,23 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Player country: -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/spanish.txt b/data/languages/spanish.txt index f54dc175..5348d16a 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 @@ -509,16 +518,16 @@ Show chat == Mostrar chat Show friends only -== Mostrar amigos +== 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,23 +675,5 @@ no limit ##### needs translation ##### -Demofile: %s -== - -Play background music -== - -Player country: -== - -Spectate next -== - -Spectate previous -== - -Strict gametype filter -== - ##### old translations ##### diff --git a/data/languages/swedish.txt b/data/languages/swedish.txt index 38bde2f2..7f48d95d 100644 --- a/data/languages/swedish.txt +++ b/data/languages/swedish.txt @@ -1,3 +1,4 @@ + ##### translated strings ##### %d Bytes @@ -144,6 +145,9 @@ Delete demo Demo details == Demoinformation +Demofile: %s +== Demofil: %s + Demos == Demon @@ -387,9 +391,15 @@ Pistol Play == Spela +Play background music +== Aktivera bakgrundsmusik + Player == Spelare +Player country: +== Land + Player options == Spelaralternativ @@ -537,6 +547,12 @@ Sound volume Spectate == Åskåda +Spectate next +== Se på nästa + +Spectate previous +== Se på föregående + Spectator mode == Åskådarläge @@ -552,6 +568,9 @@ Standard map Stop record == Sluta spela in +Strict gametype filter +== Strikt speltypsfilter + Sudden Death == Plötslig död @@ -654,23 +673,7 @@ Your skin no limit == Ingen gräns -Demofile: %s -== Demofil: %s - -Play background music -== Aktivera bakgrundsmusik - -Player country: -== Land - -Spectate next -== Se på nästa - -Spectate previous -== Se på föregående - -Strict gametype filter -== Strikt speltypsfilter +##### needs translation ##### ##### old translations ##### diff --git a/data/languages/ukrainian.txt b/data/languages/ukrainian.txt index 9e696f51..93566829 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 == Скинути налаштування diff --git a/other/sdl/vc2005libs/SDL.dll b/other/sdl/vc2005libs/SDL.dll index 628cdfcf..429ae545 100644 --- a/other/sdl/vc2005libs/SDL.dll +++ b/other/sdl/vc2005libs/SDL.dll Binary files differdiff --git a/other/sdl/vc2005libs/SDL.lib b/other/sdl/vc2005libs/SDL.lib index 5b3f17c5..f4e860f8 100644 --- a/other/sdl/vc2005libs/SDL.lib +++ b/other/sdl/vc2005libs/SDL.lib Binary files differdiff --git a/other/sdl/vc2005libs/SDLmain.lib b/other/sdl/vc2005libs/SDLmain.lib index 945b9ad8..825c03b9 100644 --- a/other/sdl/vc2005libs/SDLmain.lib +++ b/other/sdl/vc2005libs/SDLmain.lib Binary files differdiff --git a/scripts/cmd5.py b/scripts/cmd5.py index a06d45a7..9b4804c7 100644 --- a/scripts/cmd5.py +++ b/scripts/cmd5.py @@ -30,6 +30,6 @@ for filename in sys.argv[1:]: hash = hashlib.md5(f).hexdigest().lower()[16:] #TODO 0.7: improve nethash creation -if hash == "5c1e637ffddf3a37": +if hash == "63d6e69c6025feff": hash = "626fce9a778df4d4" print('#define GAME_NETVERSION_HASH "%s"' % hash) diff --git a/src/base/system.c b/src/base/system.c index 01f0b398..466e3ca6 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -42,10 +42,6 @@ #include <fcntl.h> #include <direct.h> #include <errno.h> - - #ifndef EWOULDBLOCK - #define EWOULDBLOCK WSAEWOULDBLOCK - #endif #else #error NOT IMPLEMENTED #endif @@ -823,8 +819,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); @@ -852,16 +846,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; } @@ -870,6 +854,7 @@ NETSOCKET net_udp_create(NETADDR bindaddr) { NETSOCKET sock = invalid_socket; NETADDR tmpbindaddr = bindaddr; + int broadcast = 1; if(bindaddr.type&NETTYPE_IPV4) { @@ -885,6 +870,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) @@ -901,6 +892,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 */ @@ -1010,32 +1007,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; + int socket = -1; + + /* bind, we should check for error */ + 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; + } + } - /* create socket */ - sock.type |= NETTYPE_IPV4; - sock.ipv4sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock.ipv4sock < 0) - return invalid_socket; + if(bindaddr.type&NETTYPE_IPV6) + { + struct sockaddr_in6 addr; + 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_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) @@ -1059,7 +1072,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) @@ -1085,30 +1098,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; @@ -1117,55 +1131,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; } @@ -1176,12 +1209,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() diff --git a/src/base/system.h b/src/base/system.h index de579076..aaa5b43f 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 @@ -1094,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 65ab761e..966e8f61 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 diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 90cb2011..bec7d4d6 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 <new> #include <stdlib.h> // qsort #include <stdarg.h> @@ -26,6 +27,7 @@ #include <engine/shared/compression.h> #include <engine/shared/datafile.h> #include <engine/shared/demo.h> +#include <engine/shared/filecollection.h> #include <engine/shared/mapchecker.h> #include <engine/shared/network.h> #include <engine/shared/packer.h> @@ -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<int>(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<CFileCollection *>(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(); @@ -1313,11 +1134,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) { @@ -1799,7 +1637,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) @@ -1881,7 +1719,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(); @@ -2328,7 +2166,12 @@ void CClient::RegisterCommands() m_pConsole->Chain("br_filter_serveraddress", ConchainServerBrowserUpdate, this); } -static CClient m_Client; +static CClient *CreateClient() +{ + CClient *pClient = static_cast<CClient *>(mem_alloc(sizeof(CClient), 1)); + mem_zero(pClient, sizeof(CClient)); + return new(pClient) CClient; +} /* Server Time @@ -2359,9 +2202,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"); @@ -2414,12 +2258,12 @@ int main(int argc, const char **argv) // ignore_convention pEngineMasterServer->Load(); // register all console commands - m_Client.RegisterCommands(); + pClient->RegisterCommands(); pKernel->RequestInterface<IGameClient>()->OnConsoleInit(); // init client's interfaces - m_Client.InitInterfaces(); + pClient->InitInterfaces(); // execute config file pConsole->ExecuteFile("settings.cfg"); @@ -2434,11 +2278,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 ddcd1167..1504a4e4 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 99f82b50..eca39edb 100644 --- a/src/engine/client/friends.cpp +++ b/src/engine/client/friends.cpp @@ -12,6 +12,7 @@ CFriends::CFriends() { mem_zero(m_aFriends, sizeof(m_aFriends)); + m_NumFriends = 0; } void CFriends::ConAddFriend(IConsole::IResult *pResult, void *pUserData) diff --git a/src/engine/client/graphics.cpp b/src/engine/client/graphics.cpp index 76fd3400..d1f0b8a8 100644 --- a/src/engine/client/graphics.cpp +++ b/src/engine/client/graphics.cpp @@ -179,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); } diff --git a/src/engine/console.h b/src/engine/console.h index f8ec67b0..0abf4ad2 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 @@ -35,10 +45,18 @@ 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; + + virtual const CCommandInfo *NextCommandInfo(int AccessLevel, int FlagMask) const = 0; + + int GetAccessLevel() const { return m_AccessLevel; } }; typedef void (*FPrintCallback)(const char *pStr, void *pUser); @@ -46,12 +64,15 @@ public: typedef void (*FCommandCallback)(IResult *pResult, void *pUserData); 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; - virtual void Register(const char *pName, const char *pParams, - int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp) = 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; virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser) = 0; virtual void StoreCommands(bool Store) = 0; @@ -60,8 +81,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; + + virtual void SetAccessLevel(int AccessLevel) = 0; }; extern IConsole *CreateConsole(int FlagMask); diff --git a/src/engine/masterserver.h b/src/engine/masterserver.h index 74a394dc..57433993 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 28a97ecc..31134ca9 100644 --- a/src/engine/server.h +++ b/src/engine/server.h @@ -56,6 +56,8 @@ public: virtual bool IsAuthed(int ClientID) = 0; virtual void Kick(int ClientID, const char *pReason) = 0; + + virtual void DemoRecorder_HandleAutoStart() = 0; }; class IGameServer : public IInterface diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 0e43e73f..193547cc 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 <base/math.h> #include <base/system.h> #include <engine/config.h> @@ -15,6 +16,8 @@ #include <engine/shared/config.h> #include <engine/shared/datafile.h> #include <engine/shared/demo.h> +#include <engine/shared/econ.h> +#include <engine/shared/filecollection.h> #include <engine/shared/mapchecker.h> #include <engine/shared/network.h> #include <engine/shared/packer.h> @@ -181,6 +184,7 @@ CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta) m_MapReload = 0; m_RconClientID = -1; + m_RconAuthLevel = AUTHED_ADMIN; Init(); } @@ -280,6 +284,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); } @@ -571,8 +580,9 @@ 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; + pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; pThis->m_aClients[ClientID].Reset(); return 0; } @@ -596,8 +606,9 @@ 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; + pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; pThis->m_aClients[ClientID].m_Snapshots.PurgeAll(); return 0; } @@ -635,13 +646,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; @@ -813,8 +855,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; + m_RconAuthLevel = AUTHED_ADMIN; } } else if(Msg == NETMSG_RCON_AUTH) @@ -825,20 +871,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) @@ -1020,6 +1086,8 @@ void CServer::PumpNetwork() else ProcessClientPacket(&Packet); } + + m_Econ.Update(); } char *CServer::GetMapName() @@ -1094,7 +1162,7 @@ int CServer::Run() m_pStorage = Kernel()->RequestInterface<IStorage>(); // - Console()->RegisterPrintCallback(SendRconLineAuthed, this); + m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, SendRconLineAuthed, this); // load map if(!LoadMap(g_Config.m_SvMap)) @@ -1117,7 +1185,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"); @@ -1126,6 +1193,8 @@ int CServer::Run() m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this); + m_Econ.Init(Console()); + char aBuf[256]; str_format(aBuf, sizeof(aBuf), "server name is '%s'", g_Config.m_SvName); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); @@ -1220,10 +1289,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(); @@ -1261,6 +1332,8 @@ int CServer::Run() { if(m_aClients[i].m_State != CClient::STATE_EMPTY) m_NetServer.Drop(i, "Server shutdown"); + + m_Econ.Shutdown(); } GameServer()->OnShutdown(); @@ -1292,7 +1365,7 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser) 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); @@ -1308,6 +1381,20 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser) pServer->Console()->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); } @@ -1325,6 +1412,11 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser) pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself"); return; } + else if(pServer->m_aClients[ClientID].m_Authed > pServer->m_RconAuthLevel) + { + pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied"); + return; + } Addr = pServer->m_NetServer.ClientAddr(ClientID); pServer->BanAdd(Addr, Minutes*60, pReason); @@ -1430,6 +1522,25 @@ void CServer::ConShutdown(IConsole::IResult *pResult, void *pUser) ((CServer *)pUser)->m_RunServer = 0; } +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) { CServer* pServer = (CServer *)pUser; @@ -1470,26 +1581,67 @@ 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<CServer *>(pUserData); + const IConsole::CCommandInfo *pInfo = pThis->Console()->GetCommandInfo(pResult->GetString(0), CFGFLAG_SERVER, false); + int OldAccessLevel; + 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<CServer *>(pUserData); + pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0)); + } +} + void CServer::RegisterCommands() { m_pConsole = Kernel()->RequestInterface<IConsole>(); - Console()->Register("kick", "i?r", CFGFLAG_SERVER, ConKick, this, ""); - Console()->Register("ban", "s?ir", CFGFLAG_SERVER|CFGFLAG_STORE, ConBan, this, ""); - Console()->Register("unban", "s", CFGFLAG_SERVER|CFGFLAG_STORE, ConUnban, this, ""); - Console()->Register("bans", "", CFGFLAG_SERVER|CFGFLAG_STORE, ConBans, this, ""); - Console()->Register("status", "", CFGFLAG_SERVER, ConStatus, this, ""); - Console()->Register("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, ""); + 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, ""); - Console()->Register("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, ""); + 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, ""); + Console()->Register("reload", "", CFGFLAG_SERVER, ConMapReload, this, "Reload the map"); Console()->Chain("sv_name", ConchainSpecialInfoupdate, this); Console()->Chain("password", ConchainSpecialInfoupdate, this); Console()->Chain("sv_max_clients_per_ip", ConchainMaxclientsperipUpdate, this); + Console()->Chain("mod_command", ConchainModCommandUpdate, this); + Console()->Chain("console_output_level", ConchainConsoleOutputLevelUpdate, this); } diff --git a/src/engine/server/server.h b/src/engine/server/server.h index be36a856..d8fdd8fa 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -49,6 +49,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: @@ -93,6 +102,8 @@ public: int m_Authed; int m_AuthTries; + const IConsole::CCommandInfo *m_pRconCmdToSend; + void Reset(); }; @@ -102,6 +113,7 @@ public: CSnapshotBuilder m_SnapshotBuilder; CSnapIDPool m_IDPool; CNetServer m_NetServer; + CEcon m_Econ; IEngineMap *m_pMap; @@ -110,6 +122,8 @@ public: int m_RunServer; int m_MapReload; int m_RconClientID; + int m_RconAuthLevel; + int m_PrintCBIndex; int64 m_Lastheartbeat; //static NETADDR4 master_server; @@ -134,6 +148,8 @@ public: void Kick(int ClientID, const char *pReason); + void DemoRecorder_HandleAutoStart(); + //int Tick() int64 TickStartTime(int Tick); //int TickSpeed() @@ -161,6 +177,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); @@ -169,7 +189,6 @@ public: int BanAdd(NETADDR Addr, int Seconds, const char *pReason); int BanRemove(NETADDR Addr); - void PumpNetwork(); char *GetMapName(); @@ -189,6 +208,8 @@ public: static void ConMapReload(IConsole::IResult *pResult, void *pUser); 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/shared/config_variables.h b/src/engine/shared/config_variables.h index 31a8128a..c812063a 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -82,9 +82,19 @@ MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_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") +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") diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp index 3fd73543..e4cb1991 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 <new> + +#include <base/math.h> #include <base/system.h> -#include <engine/shared/protocol.h> + #include <engine/storage.h> -#include "console.h" +#include <engine/shared/protocol.h> + #include "config.h" +#include "console.h" #include "linereader.h" const char *CConsole::CResult::GetString(unsigned Index) @@ -29,6 +33,29 @@ float CConsole::CResult::GetFloat(unsigned Index) return str_tofloat(m_apArgs[Index]); } +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; +} + // the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces @@ -146,20 +173,34 @@ 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) { - 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)); } 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) { - 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); + } } } @@ -247,38 +288,47 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr) if(ParseStart(&Result, pStr, (pEnd-pStr) + 1) != 0) return; - if (!*Result.m_pCommand) + if(!*Result.m_pCommand) return; CCommand *pCommand = FindCommand(Result.m_pCommand, m_FlagMask); if(pCommand) { - int IsStrokeCommand = 0; - if(Result.m_pCommand[0] == '+') - { - // insert the stroke direction token - Result.AddArgument(m_paStrokeStr[Stroke]); - IsStrokeCommand = 1; - } - - if(Stroke || IsStrokeCommand) + if(pCommand->GetAccessLevel() >= m_AccessLevel) { - if(ParseArgs(&Result, pCommand->m_pParams)) + int IsStrokeCommand = 0; + if(Result.m_pCommand[0] == '+') { - 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); + // insert the stroke direction token + Result.AddArgument(m_paStrokeStr[Stroke]); + IsStrokeCommand = 1; } - 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); } - else - pCommand->m_pfnCallback(&Result, pCommand->m_pUserData); + } + 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); } } else if(Stroke) @@ -292,12 +342,11 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr) } } -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); @@ -307,8 +356,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) { @@ -383,6 +431,62 @@ void CConsole::Con_Exec(IResult *pResult, void *pUserData) ((CConsole*)pUserData)->ExecuteFile(pResult->GetString(0)); } +void CConsole::ConModCommandAccess(IResult *pResult, void *pUser) +{ + CConsole* pConsole = static_cast<CConsole *>(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<CConsole *>(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; @@ -463,14 +567,17 @@ static void StrVariableCommand(IConsole::IResult *pResult, void *pUserData) 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; - m_pPrintCallbackUserdata = 0; - m_pfnPrintCallback = 0; + mem_zero(m_aPrintCB, sizeof(m_aPrintCB)); + m_NumPrintCB = 0; m_pStorage = 0; @@ -478,6 +585,9 @@ CConsole::CConsole(int FlagMask) Register("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Echo, this, "Echo the text"); Register("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Exec, this, "Execute the specified file"); + 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) \ { \ @@ -521,20 +631,131 @@ 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) { - 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; + 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<char *>(pCommand->m_pName), pName, TEMPCMD_NAME_LENGTH); + str_copy(const_cast<char *>(pCommand->m_pHelp), pHelp, TEMPCMD_HELP_LENGTH); + str_copy(const_cast<char *>(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<char *>(m_TempCommands.Allocate(TEMPCMD_NAME_LENGTH)); + str_copy(pMem, pName, TEMPCMD_NAME_LENGTH); + pCommand->m_pName = pMem; + pMem = static_cast<char *>(m_TempCommands.Allocate(TEMPCMD_HELP_LENGTH)); + str_copy(pMem, pHelp, TEMPCMD_HELP_LENGTH); + pCommand->m_pHelp = pMem; + pMem = static_cast<char *>(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; + } + } - pCommand->m_pNext = m_pFirstCommand; - m_pFirstCommand = pCommand; + m_TempCommands.Reset(); + m_pRecycleList = 0; } void CConsole::Con_Chain(IResult *pResult, void *pUserData) @@ -580,9 +801,18 @@ void CConsole::StoreCommands(bool Store) } -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 0866d8e3..6989c696 100644 --- a/src/engine/shared/console.h +++ b/src/engine/shared/console.h @@ -13,8 +13,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)); } }; @@ -41,16 +46,27 @@ class CConsole : public IConsole CExecFile *m_pFirstExec; class IStorage *m_pStorage; + int m_AccessLevel; + + CCommand *m_pRecycleList; + CHeap m_TempCommands; 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); void ExecuteFileRecurse(const char *pFilename); void ExecuteLineStroked(int Stroke, const char *pStr); - FPrintCallback m_pfnPrintCallback; - void *m_pPrintCallbackUserdata; + struct + { + int m_OutputLevel; + FPrintCallback m_pfnPrintCallback; + void *m_pPrintCallbackUserdata; + } m_aPrintCB[MAX_PRINT_CB]; + int m_NumPrintCB; enum { @@ -134,16 +150,21 @@ 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); 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(); virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser); virtual void StoreCommands(bool Store); @@ -151,8 +172,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); + + void SetAccessLevel(int AccessLevel) { m_AccessLevel = clamp(AccessLevel, (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_MOD)); } }; #endif diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp index 00410038..e2215635 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<CItemTypeInfo *>(mem_alloc(sizeof(CItemTypeInfo) * MAX_ITEM_TYPES, 1)); + m_pItems = static_cast<CItemInfo *>(mem_alloc(sizeof(CItemInfo) * MAX_ITEMS, 1)); + m_pDatas = static_cast<CDataInfo *>(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 9f27f968..cafce20e 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/econ.cpp b/src/engine/shared/econ.cpp new file mode 100644 index 00000000..617cdbd6 --- /dev/null +++ b/src/engine/shared/econ.cpp @@ -0,0 +1,173 @@ +#include <engine/console.h> +#include <engine/shared/config.h> + +#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<CEcon *>(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<CEcon *>(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 00000000..daec34c4 --- /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 00000000..622534f2 --- /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 <base/math.h> + +#include <engine/storage.h> + +#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<int>(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<CFileCollection *>(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 00000000..ac633892 --- /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 1bf402ca..1271eeaf 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<IEngine>(); @@ -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 425d970a..d10c03b6 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,12 +316,66 @@ 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; } // void SetMaxClientsPerIP(int Max); }; +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 @@ -320,6 +405,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 00000000..13ed3751 --- /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 <base/system.h> +#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 00000000..75b581fa --- /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 <base/system.h> +#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 f3fbfa32..b100e1a2 100644 --- a/src/engine/shared/network_server.cpp +++ b/src/engine/shared/network_server.cpp @@ -221,10 +221,10 @@ int CNetServer::BanAdd(NETADDR Addr, int Seconds, const char *pReason) char Buf[128]; NETADDR BanAddr; - int Mins = (Seconds + 59) / 60; - if(Mins) + if(Stamp > -1) { - 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); @@ -255,7 +255,7 @@ int CNetServer::Update() } // remove expired bans - while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires < Now) + while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires > -1 && m_BanPool_FirstUsed->m_Info.m_Expires < Now) { CBan *pBan = m_BanPool_FirstUsed; BanRemoveByObject(pBan); @@ -307,10 +307,10 @@ int CNetServer::Recv(CNetChunk *pChunk) { // banned, reply with a message char BanStr[128]; - if(pBan->m_Info.m_Expires) + if(pBan->m_Info.m_Expires > -1) { 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 4a4895ad..ba04da8a 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/game/client/components/console.cpp b/src/game/client/components/console.cpp index 2f5c49ad..f2e9e65d 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -51,7 +51,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) @@ -147,14 +147,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); } } } @@ -190,7 +192,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; } } } @@ -449,19 +461,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); } } @@ -650,6 +662,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<CGameConsole *>(pUserData); + pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0)); + } +} + void CGameConsole::PrintLine(int Type, const char *pLine) { if(Type == CONSOLETYPE_LOCAL) @@ -667,7 +689,7 @@ void CGameConsole::OnConsoleInit() m_pConsole = Kernel()->RequestInterface<IConsole>(); // - Console()->RegisterPrintCallback(ClientConsolePrintCallback, this); + m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, ClientConsolePrintCallback, this); Console()->Register("toggle_local_console", "", CFGFLAG_CLIENT, ConToggleLocalConsole, this, "Toggle local console"); Console()->Register("toggle_remote_console", "", CFGFLAG_CLIENT, ConToggleRemoteConsole, this, "Toggle remote console"); @@ -675,6 +697,8 @@ void CGameConsole::OnConsoleInit() 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); } void CGameConsole::OnStateChange(int NewState, int OldState) diff --git a/src/game/client/components/console.h b/src/game/client/components/console.h index 003a9423..6bcc75a6 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; @@ -74,6 +78,7 @@ class CGameConsole : public CComponent 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); public: enum diff --git a/src/game/client/components/countryflags.cpp b/src/game/client/components/countryflags.cpp index d6b30fe0..ef350cd7 100644 --- a/src/game/client/components/countryflags.cpp +++ b/src/game/client/components/countryflags.cpp @@ -69,6 +69,7 @@ void CCountryFlags::LoadCountryflagsIndexfile() // add entry CCountryFlag CountryFlag; 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); diff --git a/src/game/client/components/countryflags.h b/src/game/client/components/countryflags.h index 15eb8598..ad24a762 100644 --- a/src/game/client/components/countryflags.h +++ b/src/game/client/components/countryflags.h @@ -12,9 +12,10 @@ 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(); diff --git a/src/game/client/components/emoticon.cpp b/src/game/client/components/emoticon.cpp index 741a604f..b2f48b80 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 58fc75b9..11343912 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -108,7 +108,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 b9a2af16..096f9cc5 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -190,9 +190,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 9826d863..8f330f78 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -1125,7 +1125,9 @@ int CMenus::Render() 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; @@ -1135,6 +1137,7 @@ int CMenus::Render() 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); } } @@ -1348,6 +1351,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; diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index f2926f87..8501c67d 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -252,7 +252,8 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) (!m_lFriends[f].m_pFriendInfo->m_aName[0] || NameHash == m_lFriends[f].m_pFriendInfo->m_NameHash)) { m_lFriends[f].m_NumFound++; - break; + if(m_lFriends[f].m_pFriendInfo->m_aName[0]) + break; } } } diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 3fb0ab94..51fdbd29 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -215,7 +215,9 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) 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; @@ -225,6 +227,7 @@ 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); } } @@ -812,10 +815,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; } }; @@ -830,6 +834,7 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array< } char aOrigin[128]; + char aReplacement[128]; CLineReader LineReader; LineReader.Init(File); char *pLine; @@ -839,14 +844,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(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(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); @@ -856,7 +879,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); } @@ -870,7 +893,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) @@ -889,7 +912,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/spectator.cpp b/src/game/client/components/spectator.cpp index e98df118..c09b2ee2 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 a3c2dfc2..7b6b1192 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -341,6 +341,9 @@ void CGameClient::OnReset() m_All.m_paComponents[i]->OnReset(); m_DemoSpecID = SPEC_FREEVIEW; + m_FlagDropTick[TEAM_RED] = 0; + m_FlagDropTick[TEAM_BLUE] = 0; + m_Tuning = CTuningParams(); } @@ -783,6 +786,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; diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 859e1e7b..4783f8b4 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -89,6 +89,7 @@ public: bool m_SuppressEvents; bool m_NewTick; bool m_NewPredictedTick; + int m_FlagDropTick[2]; // TODO: move this CTuningParams m_Tuning; diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp index 29b891c2..2de85d66 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 dc7207be..d3d7fc40 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 33cc1c7d..23fa42e0 100644 --- a/src/game/client/render_map.cpp +++ b/src/game/client/render_map.cpp @@ -78,7 +78,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) { Graphics()->QuadsBegin(); float Conv = 1/255.0f; @@ -162,7 +162,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; @@ -174,8 +175,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 2161bc77..00a30c15 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 017abf7c..7cd78d6f 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 00000000..528e459b --- /dev/null +++ b/src/game/editor/auto_map.cpp @@ -0,0 +1,202 @@ +#include <stdio.h> // sscanf + +#include <engine/console.h> +#include <engine/storage.h> +#include <engine/shared/linereader.h> + +#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 00000000..ee570378 --- /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 <base/tl/array.h> + +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<CPosRule> m_aRules; + int m_Flag; + int m_RandomValue; + bool m_BaseTile; + }; + + struct CConfiguration + { + array<CIndexRule> 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<CConfiguration> 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 8c186085..500e600b 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -8,20 +8,20 @@ #include <engine/client.h> #include <engine/console.h> #include <engine/graphics.h> -#include <engine/textrender.h> #include <engine/input.h> #include <engine/keys.h> #include <engine/storage.h> +#include <engine/textrender.h> -#include <game/client/ui.h> #include <game/gamecore.h> +#include <game/localization.h> +#include <game/client/lineinput.h> #include <game/client/render.h> +#include <game/client/ui.h> #include <game/generated/client_data.h> +#include "auto_map.h" #include "editor.h" -#include <game/client/lineinput.h> - -#include <game/localization.h> int CEditor::ms_CheckerTexture; int CEditor::ms_BackgroundTexture; @@ -41,9 +41,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; @@ -189,6 +190,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 *********************************************************/ @@ -196,32 +215,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++) { @@ -235,7 +281,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) { @@ -251,8 +301,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]; @@ -268,19 +318,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; } @@ -443,12 +521,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); } @@ -466,6 +544,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); @@ -738,13 +859,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,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) @@ -916,10 +1065,41 @@ void CEditor::DoQuad(CQuad *q, int Index) else if(s_Operation == OP_MOVE_ALL) { // move all points including pivot - for(int v = 0; v < 5; v++) + if(m_GridActive) { - q->m_aPoints[v].x += f2fx(wx-s_LastWx); - q->m_aPoints[v].y += f2fx(wy-s_LastWy); + 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) @@ -1050,12 +1230,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)) - { - pQuad->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<<m)) + { + 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); + + 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)) + { + pQuad->m_aPoints[m].x += f2fx(dx); + pQuad->m_aPoints[m].y += f2fx(dy); + } + } } else if(s_Operation == OP_MOVEUV) { @@ -1248,6 +1453,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) @@ -1768,9 +1975,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; @@ -1781,6 +1992,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; @@ -1807,7 +2032,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); @@ -1815,16 +2040,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(), 120, 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); } @@ -1840,6 +2071,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); @@ -1853,15 +2087,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; @@ -1899,6 +2142,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) @@ -1930,6 +2174,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()) @@ -2063,6 +2308,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; @@ -2244,9 +2503,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) @@ -2595,8 +2854,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; } } @@ -3055,6 +3314,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; @@ -3152,7 +3423,6 @@ void CEditor::Render() Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } - } void CEditor::Reset(bool CreateDefault) @@ -3190,6 +3460,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()) @@ -3230,7 +3518,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)); } @@ -3337,6 +3625,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 c7779954..1a904953 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 <base/system.h> +#include <math.h> + #include <base/math.h> -#include <base/tl/array.h> +#include <base/system.h> + #include <base/tl/algorithm.h> +#include <base/tl/array.h> #include <base/tl/sorted_array.h> #include <base/tl/string.h> -#include <math.h> +#include <game/client/ui.h> #include <game/mapitems.h> #include <game/client/render.h> -#include <engine/shared/datafile.h> #include <engine/shared/config.h> +#include <engine/shared/datafile.h> #include <engine/editor.h> #include <engine/graphics.h> -#include <game/client/ui.h> +#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 @@ -400,6 +406,8 @@ public: int m_Width; int m_Height; CColor m_Color; + int m_ColorEnv; + int m_ColorEnvOffset; CTile *m_pTiles; }; @@ -470,6 +478,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; @@ -551,6 +562,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; @@ -651,12 +665,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); @@ -665,10 +681,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(); @@ -684,6 +702,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); @@ -694,6 +713,9 @@ public: void PopupSelectGametileOpInvoke(float x, float y); int PopupSelectGameTileOpResult(); + + void PopupSelectConfigAutoMapInvoke(float x, float y); + int PopupSelectConfigAutoMapResult(); vec4 ButtonColorMul(const void *pID); @@ -733,6 +755,8 @@ public: int Length = pEnd > pExtractedName ? min(BufferSize, (int)(pEnd-pExtractedName+1)) : BufferSize; str_copy(pName, pExtractedName, Length); } + + int GetLineDistance(); }; // make sure to inline this function diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index 7207e49f..68330f03 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -266,6 +266,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) @@ -278,23 +281,24 @@ 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; - Item.m_Flags = pLayer->m_Game; + 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); + df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item); GItem.m_NumLayers++; @@ -307,7 +311,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; @@ -315,6 +319,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 @@ -449,6 +457,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 @@ -486,6 +497,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; @@ -498,7 +513,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); @@ -508,10 +523,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; @@ -519,7 +533,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)); @@ -543,6 +561,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 f4a5fb76..7e879c3e 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 d2a8a1e5..d0b66405 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 b792eda9..5662613c 100644 --- a/src/game/editor/layer_tiles.cpp +++ b/src/game/editor/layer_tiles.cpp @@ -15,7 +15,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; @@ -25,6 +25,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_pTiles = new CTile[m_Width*m_Height]; mem_zero(m_pTiles, m_Width*m_Height*sizeof(CTile)); @@ -62,7 +64,8 @@ void CLayerTiles::Render() m_TexID = m_pEditor->m_Map.m_lImages[m_Image]->m_TexID; Graphics()->TextureSet(m_TexID); vec4 Color = vec4(m_Color.r/255.0f, m_Color.g/255.0f, m_Color.b/255.0f, m_Color.a/255.0f); - m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT); + 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); } int CLayerTiles::ConvertX(float x) const { return (int)(x/32.0f); } @@ -360,14 +363,32 @@ void CLayerTiles::ShowInfo() 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(); - if(m_pEditor->m_Map.m_pGameLayer == 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 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()); @@ -394,6 +415,8 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) PROP_SHIFT, PROP_IMAGE, PROP_COLOR, + PROP_COLOR_ENV, + PROP_COLOR_ENV_OFFSET, NUM_PROPS, }; @@ -409,6 +432,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}, }; @@ -447,6 +472,10 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) m_Color.b = (NewVal>>8)&0xff; m_Color.a = NewVal&0xff; } + if(Prop == PROP_COLOR_ENV) + m_ColorEnv = clamp(NewVal-1, -1, m_pEditor->m_Map.m_lEnvelopes.size()-1); + 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 3ae29725..f29ae7e2 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 <base/tl/array.h> + #include <engine/console.h> #include <engine/graphics.h> #include <engine/input.h> #include <engine/keys.h> #include <engine/storage.h> + #include "editor.h" @@ -147,6 +151,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; } @@ -160,9 +165,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, @@ -239,6 +257,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]; @@ -528,8 +558,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); @@ -737,3 +767,43 @@ int CEditor::PopupSelectGameTileOpResult() s_GametileOpSelected = -1; return Result; } + +static int s_AutoMapConfigSelected = -1; + +int CEditor::PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View) +{ + CLayerTiles *pLayer = static_cast<CLayerTiles*>(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; + } + + return 0; +} + +void CEditor::PopupSelectConfigAutoMapInvoke(float x, float y) +{ + static int s_AutoMapConfigSelectID = 0; + s_AutoMapConfigSelected = -1; + CLayerTiles *pLayer = static_cast<CLayerTiles*>(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; +} diff --git a/src/game/gamecore.cpp b/src/game/gamecore.cpp index d2a1652c..af086df2 100644 --- a/src/game/gamecore.cpp +++ b/src/game/gamecore.cpp @@ -362,35 +362,37 @@ void CCharacterCore::Move() m_Vel.x = m_Vel.x*RampValue; - vec2 NewPos = m_Pos; + vec2 NewPos = m_Pos; m_pCollision->MoveBox(&NewPos, &m_Vel, vec2(28.0f, 28.0f), 0); m_Vel.x = m_Vel.x*(1.0f/RampValue); if(m_pWorld && m_pWorld->m_Tuning.m_PlayerCollision) { - // check player collision - float Distance = distance(m_Pos, NewPos); - int End = Distance+1; - for(int i = 0; i < End; i++) - { - float a = i/Distance; - vec2 Pos = mix(m_Pos, NewPos, a); - for(int p = 0; p < MAX_CLIENTS; p++) - { - CCharacterCore *pCharCore = m_pWorld->m_apCharacters[p]; - if(!pCharCore || pCharCore == this) - continue; - float D = distance(Pos, pCharCore->m_Pos); - if(D < 28.0f*1.25f && D > 0.0f) - { - if(a > 0.0f) - m_Pos = Pos; - else - m_Pos = NewPos; - return; - } - } + // check player collision + float Distance = distance(m_Pos, NewPos); + int End = Distance+1; + vec2 LastPos = m_Pos; + for(int i = 0; i < End; i++) + { + float a = i/Distance; + vec2 Pos = mix(m_Pos, NewPos, a); + for(int p = 0; p < MAX_CLIENTS; p++) + { + CCharacterCore *pCharCore = m_pWorld->m_apCharacters[p]; + if(!pCharCore || pCharCore == this) + continue; + float D = distance(Pos, pCharCore->m_Pos); + if(D < 28.0f && D > 0.0f) + { + if(a > 0.0f) + m_Pos = LastPos; + else if(distance(NewPos, pCharCore->m_Pos) > D) + m_Pos = NewPos; + return; + } + } + LastPos = Pos; } } diff --git a/src/game/mapitems.h b/src/game/mapitems.h index d99d6724..fb66e12d 100644 --- a/src/game/mapitems.h +++ b/src/game/mapitems.h @@ -53,6 +53,7 @@ enum TILEFLAG_ROTATE=8, LAYERFLAG_DETAIL=1, + TILESLAYERFLAG_GAME=1, ENTITY_OFFSET=255-16*4, }; @@ -114,13 +115,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 @@ -145,6 +148,8 @@ struct CMapItemLayerTilemap int m_Image; int m_Data; + + int m_aName[3]; } ; struct CMapItemLayerQuads @@ -155,6 +160,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 899557f2..d7fd74a8 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -196,7 +196,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); } } diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index af3c672d..0865cf08 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1427,6 +1427,8 @@ void CGameContext::ConForceVote(IConsole::IResult *pResult, void *pUserData) return; } + 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); } @@ -1453,6 +1455,8 @@ void CGameContext::ConVote(IConsole::IResult *pResult, void *pUserData) else if(str_comp_nocase(pResult->GetString(0), "no") == 0) pSelf->m_VoteEnforce = CGameContext::VOTE_ENFORCE_NO; 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)); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); } @@ -1476,22 +1480,22 @@ void CGameContext::OnConsoleInit() m_pServer = Kernel()->RequestInterface<IServer>(); m_pConsole = Kernel()->RequestInterface<IConsole>(); - Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, ""); - Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, ""); - Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, ""); - - Console()->Register("change_map", "?r", CFGFLAG_SERVER|CFGFLAG_STORE, ConChangeMap, this, ""); - Console()->Register("restart", "?i", CFGFLAG_SERVER|CFGFLAG_STORE, ConRestart, this, ""); - Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConBroadcast, this, ""); - Console()->Register("say", "r", CFGFLAG_SERVER, ConSay, this, ""); - Console()->Register("set_team", "ii?i", CFGFLAG_SERVER, ConSetTeam, this, ""); - Console()->Register("set_team_all", "i", CFGFLAG_SERVER, ConSetTeamAll, this, ""); - - Console()->Register("add_vote", "sr", CFGFLAG_SERVER, ConAddVote, this, ""); - Console()->Register("remove_vote", "s", CFGFLAG_SERVER, ConRemoveVote, this, ""); - Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, ConForceVote, this, ""); - Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, ""); - Console()->Register("vote", "r", CFGFLAG_SERVER, ConVote, this, ""); + 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"); + 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"); Console()->Chain("sv_motd", ConchainSpecialMotdupdate, this); } diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index 56407a77..eb9dc9b5 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -227,6 +227,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); @@ -672,7 +673,7 @@ bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam) void IGameController::DoWincheck() { - if(m_GameOverTick == -1 && !m_Warmup) + if(m_GameOverTick == -1 && !m_Warmup && !GameServer()->m_World.m_ResetRequested) { if(IsTeamplay()) { diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 7bac6454..cccff50f 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -112,6 +112,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; @@ -259,9 +262,6 @@ void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) if(!m_pCharacter && m_Team != TEAM_SPECTATORS && (NewInput->m_Fire&1)) m_Spawning = true; - if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW) - m_ViewPos = vec2(NewInput->m_TargetX, NewInput->m_TargetY); - // 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/variables.h b/src/game/variables.h index 28d8ceea..c913a46b 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -45,6 +45,7 @@ MACRO_CONFIG_INT(UiPage, ui_page, 5, 0, 10, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interf 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") MACRO_CONFIG_INT(UiColorHue, ui_color_hue, 160, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color hue") MACRO_CONFIG_INT(UiColorSat, ui_color_sat, 70, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color saturation") @@ -95,4 +96,4 @@ MACRO_CONFIG_INT(SvAllowJoin, sv_allow_join, 2, 0, 2, CFGFLAG_SERVER, "Allow new //0 - Wait for next round; 1 = Allowed to join; 2 = Will join when person with the most kills die MACRO_CONFIG_INT(SvColorIndicator, sv_color_indicator, 1, 0, 1, CFGFLAG_SERVER, "Color tees apropriate to the number of currently catched players") MACRO_CONFIG_INT(SvBonus, sv_bonus, 0, 0, 10000, CFGFLAG_SERVER, "Give the last player extra points") -MACRO_CONFIG_INT(SvFollowCatcher, sv_follow_catcher, 1, 0, 1, CFGFLAG_SERVER, "If a victim should follow his catcher") \ No newline at end of file +MACRO_CONFIG_INT(SvFollowCatcher, sv_follow_catcher, 1, 0, 1, CFGFLAG_SERVER, "If a victim should follow his catcher") diff --git a/src/game/version.h b/src/game/version.h index c7f04b75..76b6d4ab 100644 --- a/src/game/version.h +++ b/src/game/version.h @@ -3,6 +3,6 @@ #ifndef GAME_VERSION_H #define GAME_VERSION_H #include "generated/nethash.cpp" -#define GAME_VERSION "0.6 trunk" +#define GAME_VERSION "0.6.1" #define GAME_NETVERSION "0.6 " GAME_NETVERSION_HASH #endif diff --git a/src/tools/dilate.cpp b/src/tools/dilate.cpp index ef862270..eb770a90 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 6fb32d4b..0facb9a3 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; +} |