Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F118144
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
202 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/data/locale/fi.po b/data/locale/fi.po
index a5e772a..07f9990 100644
--- a/data/locale/fi.po
+++ b/data/locale/fi.po
@@ -1,1079 +1,1541 @@
# Finnish translation for Me and My Shadow
# Copyright (C) 2012 Me and My Shadow
# This file is distributed under the same license (GNU GPLv3) as the meandmyshadow package.
-# odamite <odamite@gmail.com>, 2012
+# odamite <odamite@gmail.com>, 2012-2013
#
msgid ""
msgstr ""
"Project-Id-Version: meandmyshadow 0.4\n"
-"Report-Msgid-Bugs-To: odamite@gmail.com\n"
-"POT-Creation-Date: 2012-05-21 10:48+0300\n"
-"PO-Revision-Date: 2012-06-02 16:24+0200\n"
-"Last-Translator: odamite <odamite@gmail.com>\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-04-28 21:15+0300\n"
+"PO-Revision-Date: 2013-04-28 21:18+0200\n"
+"Last-Translator: odamite <odamite@gmail.com> \n"
"Language-Team: Finnish\n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.5\n"
-#: ../src/Addons.cpp:40
-#: ../src/TitleMenu.cpp:50
+#: ../src/Addons.cpp:49 ../src/TitleMenu.cpp:53
msgid "Addons"
msgstr "Lisäosat"
-#: ../src/Addons.cpp:58
+#: ../src/Addons.cpp:79
msgid "Unable to initialize addon menu:"
msgstr "Lisäosavalikkoa ei voi luoda:"
-#: ../src/Addons.cpp:66
-#: ../src/Addons.cpp:115
-#: ../src/LevelEditSelect.cpp:69
-#: ../src/LevelSelect.cpp:186
+#: ../src/Addons.cpp:87 ../src/Addons.cpp:137 ../src/Addons.cpp:478
+#: ../src/Addons.cpp:504 ../src/LevelEditSelect.cpp:74
+#: ../src/LevelSelect.cpp:201 ../src/StatisticsScreen.cpp:276
+#: ../src/TitleMenu.cpp:821
msgid "Back"
msgstr "Takaisin"
-#: ../src/Addons.cpp:97
+#: ../src/Addons.cpp:118
msgid "Levels"
msgstr "Kentät"
-#: ../src/Addons.cpp:98
+#: ../src/Addons.cpp:119
msgid "Level Packs"
msgstr "Kenttäpaketit"
-#: ../src/Addons.cpp:99
+#: ../src/Addons.cpp:120
msgid "Themes"
msgstr "Teemat"
-#: ../src/Addons.cpp:119
-#: ../src/Addons.cpp:595
-msgid "Install"
-msgstr "Asenna"
+#: ../src/Addons.cpp:148
+#, fuzzy
+msgid "ERROR: unable to download addons file!"
+msgstr "VIRHE: Lisäosan lataaminen ei onnistu!"
+
+#: ../src/Addons.cpp:161
+#, fuzzy
+msgid "ERROR: unable to load addon_list file!"
+msgstr "VIRHE: Lisäosan lataaminen ei onnistu!"
+
+#: ../src/Addons.cpp:172
+msgid "ERROR: Invalid file format of addons file!"
+msgstr ""
+
+#: ../src/Addons.cpp:194
+#, fuzzy
+msgid "ERROR: Unable to create the installed_addons file."
+msgstr "Aktivoi internet asentaaksesi lisäosia."
+
+#: ../src/Addons.cpp:206
+msgid "ERROR: Invalid file format of the installed_addons!"
+msgstr ""
-#: ../src/Addons.cpp:123
+# TRANSLATORS: indicates the author of an addon.
+#: ../src/Addons.cpp:325 ../src/Addons.cpp:456
+#, c-format
+msgid "by %s"
+msgstr "tekijä: %s"
+
+#: ../src/Addons.cpp:333
+msgid "Installed"
+msgstr "Asennettu"
+
+#: ../src/Addons.cpp:338
+msgid "Updatable"
+msgstr "Päivitettävä"
+
+#: ../src/Addons.cpp:345
+msgid "Not installed"
+msgstr "Ei asennettu"
+
+#: ../src/Addons.cpp:473 ../src/Addons.cpp:498
+msgid "Remove"
+msgstr "Poista"
+
+#: ../src/Addons.cpp:487
msgid "Update"
msgstr "Päivitä"
-#: ../src/Addons.cpp:400
-#: ../src/Addons.cpp:417
-#: ../src/Addons.cpp:434
-#: ../src/Addons.cpp:461
-#: ../src/Addons.cpp:478
-#: ../src/Addons.cpp:492
+#: ../src/Addons.cpp:493
+msgid "Install"
+msgstr "Asenna"
+
+#: ../src/Addons.cpp:566 ../src/Addons.cpp:581 ../src/Addons.cpp:596
+#: ../src/Addons.cpp:615 ../src/Addons.cpp:630 ../src/Addons.cpp:642
msgid "ERROR: Unable to download addon!"
msgstr "VIRHE: Lisäosan lataaminen ei onnistu!"
-#: ../src/Addons.cpp:400
-#: ../src/Addons.cpp:417
-#: ../src/Addons.cpp:434
-#: ../src/Addons.cpp:461
-#: ../src/Addons.cpp:478
-#: ../src/Addons.cpp:492
+#: ../src/Addons.cpp:566 ../src/Addons.cpp:581 ../src/Addons.cpp:596
+#: ../src/Addons.cpp:615 ../src/Addons.cpp:630 ../src/Addons.cpp:642
msgid "ERROR:"
msgstr "VIRHE:"
-#: ../src/Addons.cpp:590
-msgid "Uninstall"
-msgstr "Poista"
+#: ../src/Block.cpp:638 ../src/LevelEditor.cpp:174
+msgid "On"
+msgstr "Päällä"
+
+#: ../src/Block.cpp:639 ../src/LevelEditor.cpp:175
+msgid "Off"
+msgstr "Pois"
# TRANSLATORS: Font used in GUI:
# - Use "knewave" for languages using Latin and Latin-derived alphabets
# - "freesans" can be used for non-Latin writing systems
-#: ../src/Functions.cpp:460
-#: ../src/Functions.cpp:461
-#: ../src/Functions.cpp:462
+#: ../src/Functions.cpp:699 ../src/Functions.cpp:700 ../src/Functions.cpp:701
+#: ../src/Functions.cpp:717
msgid "knewave"
msgstr "knewave"
-#: ../src/Functions.cpp:466
+#: ../src/Functions.cpp:705
msgid "Blokletters-Viltstift"
msgstr "Blokletters-Viltstift"
-#: ../src/Functions.cpp:986
-#: ../src/Functions.cpp:1013
-#: ../src/Functions.cpp:1377
-#: ../src/InputManager.cpp:196
-#: ../src/LevelEditor.cpp:1017
-#: ../src/LevelEditor.cpp:1630
-#: ../src/LevelEditor.cpp:1687
-#: ../src/LevelEditor.cpp:1750
-#: ../src/LevelEditor.cpp:1830
-#: ../src/LevelEditor.cpp:1936
-#: ../src/LevelEditor.cpp:1996
-#: ../src/LevelEditSelect.cpp:175
-#: ../src/LevelEditSelect.cpp:215
-#: ../src/LevelEditSelect.cpp:263
+#: ../src/Functions.cpp:819
+msgid "Loading..."
+msgstr "Ladataan..."
+
+#: ../src/Functions.cpp:1398 ../src/Functions.cpp:1425
+#: ../src/Functions.cpp:1763 ../src/LevelEditSelect.cpp:232
+#: ../src/LevelEditSelect.cpp:264 ../src/LevelEditSelect.cpp:300
+#: ../src/LevelEditor.cpp:387 ../src/LevelEditor.cpp:493
+#: ../src/LevelEditor.cpp:550 ../src/LevelEditor.cpp:606
+#: ../src/LevelEditor.cpp:1913
msgid "OK"
msgstr "OK"
-#: ../src/Functions.cpp:987
-#: ../src/Functions.cpp:999
-#: ../src/Functions.cpp:1009
-#: ../src/Functions.cpp:1381
-#: ../src/LevelEditor.cpp:1021
-#: ../src/LevelEditor.cpp:1634
-#: ../src/LevelEditor.cpp:1691
-#: ../src/LevelEditor.cpp:1754
-#: ../src/LevelEditor.cpp:1834
-#: ../src/LevelEditor.cpp:1940
-#: ../src/LevelEditor.cpp:2000
-#: ../src/LevelEditSelect.cpp:179
-#: ../src/LevelEditSelect.cpp:219
-#: ../src/LevelEditSelect.cpp:267
-#: ../src/TitleMenu.cpp:442
+#: ../src/Functions.cpp:1399 ../src/Functions.cpp:1411
+#: ../src/Functions.cpp:1421 ../src/Functions.cpp:1767
+#: ../src/LevelEditSelect.cpp:236 ../src/LevelEditSelect.cpp:268
+#: ../src/LevelEditSelect.cpp:304 ../src/LevelEditor.cpp:391
+#: ../src/LevelEditor.cpp:497 ../src/LevelEditor.cpp:554
+#: ../src/LevelEditor.cpp:610 ../src/LevelEditor.cpp:1917
+#: ../src/TitleMenu.cpp:516
msgid "Cancel"
msgstr "Peruuta"
-#: ../src/Functions.cpp:991
+#: ../src/Functions.cpp:1403
msgid "Abort"
msgstr "Pysäytä"
-#: ../src/Functions.cpp:992
-#: ../src/Functions.cpp:1008
+#: ../src/Functions.cpp:1404 ../src/Functions.cpp:1420
msgid "Retry"
msgstr "Uudestaan"
-#: ../src/Functions.cpp:993
+#: ../src/Functions.cpp:1405
msgid "Ignore"
msgstr "Hylkää"
-#: ../src/Functions.cpp:997
-#: ../src/Functions.cpp:1003
+#: ../src/Functions.cpp:1409 ../src/Functions.cpp:1415
msgid "Yes"
msgstr "Kyllä"
-#: ../src/Functions.cpp:998
-#: ../src/Functions.cpp:1004
+#: ../src/Functions.cpp:1410 ../src/Functions.cpp:1416
msgid "No"
msgstr "Ei"
-#: ../src/Functions.cpp:1145
+#: ../src/Functions.cpp:1534
#, c-format
msgid ""
"%s already exists.\n"
"Do you want to overwrite it?"
msgstr ""
"Tiedosto %s on jo olemassa.\n"
"Haluatko korvata sen?"
-#: ../src/Functions.cpp:1145
+#: ../src/Functions.cpp:1534
msgid "Overwrite Prompt"
msgstr "Ylikirjoitus"
-#: ../src/Functions.cpp:1166
-#: ../src/Functions.cpp:1184
+#: ../src/Functions.cpp:1555 ../src/Functions.cpp:1573
#, c-format
msgid "Can't open file %s."
msgstr "Tiedostoa %s ei voi avata."
-#: ../src/Functions.cpp:1166
-#: ../src/Functions.cpp:1184
+#: ../src/Functions.cpp:1555 ../src/Functions.cpp:1573
msgid "Error"
msgstr "Virhe"
-#: ../src/Functions.cpp:1318
+#: ../src/Functions.cpp:1704
msgid "Save File"
msgstr "Tallenna tiedosto"
-#: ../src/Functions.cpp:1318
+#: ../src/Functions.cpp:1704
msgid "Load File"
msgstr "Avaa tiedosto"
-#: ../src/Functions.cpp:1322
+#: ../src/Functions.cpp:1708
msgid "Search In"
msgstr "Etsi"
-#: ../src/Functions.cpp:1332
+#: ../src/Functions.cpp:1718
msgid "File Name"
msgstr "Tiedostonimi"
-#: ../src/Game.cpp:226
-#: ../src/Game.cpp:820
+#: ../src/Game.cpp:277 ../src/Game.cpp:1021
#, c-format
msgid "Level %d %s"
msgstr "Kenttä %d %s"
-#: ../src/Game.cpp:647
+#: ../src/Game.cpp:841
#, c-format
msgid "Press %s key to save the game."
msgstr "Paina %s tallentaaksesi peli."
-#: ../src/Game.cpp:652
+#: ../src/Game.cpp:846
#, c-format
msgid "Press %s key to swap the position of player and shadow."
msgstr "Paina %s vaihtaaksesi pelaajan ja varjon paikkoja"
-#: ../src/Game.cpp:657
+#: ../src/Game.cpp:851
#, c-format
msgid "Press %s key to activate the switch."
msgstr "Paina %s käyttääksesi vipua."
-#: ../src/Game.cpp:662
+#: ../src/Game.cpp:856
#, c-format
msgid "Press %s key to teleport."
msgstr "Paina %s käyttääksesi teleporttia."
-#: ../src/Game.cpp:703
+#: ../src/Game.cpp:897
#, c-format
msgid "Press %s to restart current level or press %s to load the game."
msgstr "Paina %s aloittaaksesi kenttä uudestaan tai paina %s ladataksesi peli."
-#: ../src/Game.cpp:715
+#: ../src/Game.cpp:909
#, c-format
msgid "Press %s to restart current level."
msgstr "Paina %s aloittaaksesi kenttä uudestaan."
-#: ../src/Game.cpp:729
+#: ../src/Game.cpp:923
msgid "Your shadow has died."
msgstr "Varjosi on kuollut."
-#: ../src/Game.cpp:772
+#: ../src/Game.cpp:974
#, c-format
msgid "%d recordings"
msgstr "%d nauhoitusta"
-#: ../src/Game.cpp:812
+#: ../src/Game.cpp:1013
msgid "You've finished:"
msgstr "Pääsit läpi:"
-#: ../src/Game.cpp:981
+#: ../src/Game.cpp:1199
#, c-format
msgid "Time: %-.2fs"
msgstr "Aikasi: %-.2fs"
-#: ../src/Game.cpp:990
+#: ../src/Game.cpp:1208
#, c-format
msgid "Best time: %-.2fs"
msgstr "Paras aikasi: %-.2fs"
-#: ../src/Game.cpp:1001
+#: ../src/Game.cpp:1219
#, c-format
msgid "Target time: %-.2fs"
msgstr "Tavoiteaika: %-.2fs"
-#: ../src/Game.cpp:1022
+#: ../src/Game.cpp:1240
#, c-format
msgid "Recordings: %d"
msgstr "Nauhoituksesi: %d"
-#: ../src/Game.cpp:1030
+#: ../src/Game.cpp:1248
#, c-format
msgid "Best recordings: %d"
msgstr "Parhaat nauhoituksesi: %d"
-#: ../src/Game.cpp:1040
+#: ../src/Game.cpp:1258
#, c-format
msgid "Target recordings: %d"
msgstr "Tavoitenauhoitukset: %d"
-#: ../src/Game.cpp:1053
+#: ../src/Game.cpp:1271
#, c-format
msgid "You earned the %s medal"
msgstr "Ansaitsit %smitalin"
-#: ../src/Game.cpp:1053
+#: ../src/Game.cpp:1271
msgid "GOLD"
msgstr "kulta"
-#: ../src/Game.cpp:1053
+#: ../src/Game.cpp:1271
msgid "SILVER"
msgstr "hopea"
-#: ../src/Game.cpp:1053
+#: ../src/Game.cpp:1271
msgid "BRONZE"
msgstr "pronssi"
-#: ../src/Game.cpp:1068
+#: ../src/Game.cpp:1286
msgid "Menu"
msgstr "Valikko"
-#: ../src/Game.cpp:1075
-#: ../src/InputManager.cpp:41
+#: ../src/Game.cpp:1293 ../src/InputManager.cpp:44
msgid "Restart"
msgstr "Uudestaan"
-#: ../src/Game.cpp:1082
+#: ../src/Game.cpp:1300
msgid "Next"
msgstr "Seuraava"
-#: ../src/Game.cpp:1122
+#: ../src/Game.cpp:1363
msgid "Game replay is done."
msgstr "Uusinta loppui."
-#: ../src/Game.cpp:1122
+#: ../src/Game.cpp:1363
msgid "Game Replay"
msgstr "Uusinta"
-#: ../src/Game.cpp:1275
-#: ../src/Game.cpp:1277
+#: ../src/Game.cpp:1627 ../src/Game.cpp:1629
msgid "Congratulations"
msgstr "Onnittelut"
-#: ../src/Game.cpp:1277
+#: ../src/Game.cpp:1629
msgid "You have finished the levelpack!"
msgstr "Pääsit kenttäpaketin läpi!"
-#: ../src/InputManager.cpp:40
+#: ../src/InputManager.cpp:43
msgid "Up (in menu)"
msgstr "Ylös (valikossa)"
-#: ../src/InputManager.cpp:40
+#: ../src/InputManager.cpp:43
msgid "Down (in menu)"
msgstr "Alas (valikossa)"
-#: ../src/InputManager.cpp:40
+#: ../src/InputManager.cpp:43
msgid "Left"
msgstr "Liiku vasemmalle"
-#: ../src/InputManager.cpp:40
+#: ../src/InputManager.cpp:43
msgid "Right"
msgstr "Liiku oikealle"
-#: ../src/InputManager.cpp:40
+#: ../src/InputManager.cpp:43
msgid "Jump"
msgstr "Hyppää"
-#: ../src/InputManager.cpp:40
+#: ../src/InputManager.cpp:43
msgid "Action"
msgstr "Käytä"
-#: ../src/InputManager.cpp:40
+#: ../src/InputManager.cpp:43
msgid "Space (Record)"
msgstr "Nauhoita"
-#: ../src/InputManager.cpp:41
+#: ../src/InputManager.cpp:43
+msgid "Cancel recording"
+msgstr "Keskeytä nauhoitus"
+
+#: ../src/InputManager.cpp:44
msgid "Escape"
msgstr "Takaisin"
-#: ../src/InputManager.cpp:41
+#: ../src/InputManager.cpp:44
msgid "Tab (View shadow/Level prop.)"
msgstr "Varjon näkymä/kentän asetukset"
-#: ../src/InputManager.cpp:41
+#: ../src/InputManager.cpp:44
msgid "Save game (in editor)"
msgstr "Tallenna peli (kenttäeditorissa)"
-#: ../src/InputManager.cpp:41
+#: ../src/InputManager.cpp:44
msgid "Load game"
msgstr "Lataa peli"
-#: ../src/InputManager.cpp:41
+#: ../src/InputManager.cpp:44
msgid "Swap (in editor)"
msgstr "Vaihda paikkoja (kenttäeditorissa)"
-#: ../src/InputManager.cpp:42
+#: ../src/InputManager.cpp:45
msgid "Teleport (in editor)"
msgstr "Teleporttaa (kenttäeditorissa)"
-#: ../src/InputManager.cpp:42
+#: ../src/InputManager.cpp:45
msgid "Suicide (in editor)"
msgstr "Kuole (kenttäeditorissa)"
-#: ../src/InputManager.cpp:42
+#: ../src/InputManager.cpp:45
msgid "Shift (in editor)"
msgstr "Vaihda paikkaa (kenttäeditorissa)"
-#: ../src/InputManager.cpp:42
+#: ../src/InputManager.cpp:45
msgid "Next block type (in Editor)"
msgstr "Seuraava palikkatyyppi (kenttäeditorissa)"
-#: ../src/InputManager.cpp:43
+#: ../src/InputManager.cpp:46
msgid "Previous block type (in editor)"
msgstr "Edellinen palikkatyyppi (kenttäeditorissa)"
-#: ../src/InputManager.cpp:43
+#: ../src/InputManager.cpp:46
msgid "Select (in menu)"
msgstr "Valitse (valikossa)"
-#: ../src/InputManager.cpp:163
-#: ../src/TitleMenu.cpp:430
-msgid "Config Keys"
-msgstr "Aseta näppäimet"
+#: ../src/InputManager.cpp:71 ../src/InputManager.cpp:96
+msgid "OR"
+msgstr "TAI"
-#: ../src/InputManager.cpp:166
-msgid "Select an item and press a key to config it."
+#: ../src/InputManager.cpp:227
+#, fuzzy
+msgid "Select an item and press a key to change it."
msgstr "Valitse kohde listasta ja paina näppäintä vaihtaaksesi se."
-#: ../src/InputManager.cpp:179
-msgid "Primary key"
-msgstr "Ensisijaiset"
+#: ../src/InputManager.cpp:230
+msgid "Press backspace to clear the selected item."
+msgstr "Tyhjennä valittu kohde painamalla askelpalautinta."
-#: ../src/InputManager.cpp:180
-msgid "Alternative key"
-msgstr "Toissijaiset"
+#: ../src/LevelEditSelect.cpp:46 ../src/TitleMenu.cpp:52
+msgid "Map Editor"
+msgstr "Kenttäeditori"
-#: ../src/InputManager.cpp:186
-msgid "Unset the key"
-msgstr "Tyhjennä valinta"
+#: ../src/LevelEditSelect.cpp:83
+msgid "New Levelpack"
+msgstr "Uusi kenttäpaketti"
-#: ../src/InputManager.cpp:300
-msgid "(Not set)"
-msgstr "(Ei määritetty)"
+#: ../src/LevelEditSelect.cpp:88
+msgid "Pack Properties"
+msgstr "Paketin asetukset"
-#: ../src/LevelEditor.cpp:121
-msgid "Toolbox"
-msgstr "Työkalulaatikko"
+#: ../src/LevelEditSelect.cpp:93
+msgid "Remove Pack"
+msgstr "Poista paketti"
-#: ../src/LevelEditor.cpp:180
+#: ../src/LevelEditSelect.cpp:98
+msgid "Move Map"
+msgstr "Siirrä kenttä"
+
+#: ../src/LevelEditSelect.cpp:106
+msgid "Remove Map"
+msgstr "Poista kenttä"
+
+#: ../src/LevelEditSelect.cpp:111
+msgid "Edit Map"
+msgstr "Muokkaa kenttää"
+
+#: ../src/LevelEditSelect.cpp:202
+msgid "Properties"
+msgstr "Asetukset"
+
+#: ../src/LevelEditSelect.cpp:205 ../src/LevelEditor.cpp:1868
+msgid "Name:"
+msgstr "Nimi:"
+
+#: ../src/LevelEditSelect.cpp:214
+msgid "Description:"
+msgstr "Kuvaus:"
+
+#: ../src/LevelEditSelect.cpp:223
+msgid "Congratulation text:"
+msgstr "Onnittelu:"
+
+#: ../src/LevelEditSelect.cpp:252 ../src/LevelEditSelect.cpp:407
+msgid "Add level"
+msgstr "Lisää kenttä"
+
+#: ../src/LevelEditSelect.cpp:255
+msgid "File name:"
+msgstr "Tiedostonimi:"
+
+#: ../src/LevelEditSelect.cpp:280
+msgid "Move level"
+msgstr "Siirrä kenttä"
+
+#: ../src/LevelEditSelect.cpp:283
+msgid "Level: "
+msgstr "Kenttä:"
+
+#: ../src/LevelEditSelect.cpp:293
+msgid "Before"
+msgstr "Ennen"
+
+#: ../src/LevelEditSelect.cpp:294
+msgid "After"
+msgstr "Jälkeen"
+
+#: ../src/LevelEditSelect.cpp:295 ../src/LevelEditor.cpp:66
+msgid "Swap"
+msgstr "Vaihda paikkoja"
+
+#: ../src/LevelEditSelect.cpp:470
+msgid "Are you sure?"
+msgstr "Oletko varma?"
+
+#: ../src/LevelEditSelect.cpp:470
+msgid "Remove prompt"
+msgstr "Poista"
+
+#: ../src/LevelEditSelect.cpp:615
+msgid "No file name given for the new level."
+msgstr "Uudelle kentälle ei annettu nimeä."
+
+#: ../src/LevelEditSelect.cpp:615
+msgid "Missing file name"
+msgstr "Puuttuva tiedostonimi"
+
+#: ../src/LevelEditSelect.cpp:672
+msgid "ERROR: Unable to add level to Levels levelpack"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:706
+msgid "The entered level number isn't valid!"
+msgstr "Kentälle annettu järjestysnumero ei kelpaa!"
+
+#: ../src/LevelEditSelect.cpp:706
+msgid "Illegal number"
+msgstr "Järjestysnumero"
+
+#: ../src/LevelEditor.cpp:64
msgid "Block"
msgstr "Palikka"
-#: ../src/LevelEditor.cpp:180
+#: ../src/LevelEditor.cpp:64
msgid "Player Start"
msgstr "Pelaajan aloituspaikka"
-#: ../src/LevelEditor.cpp:180
+#: ../src/LevelEditor.cpp:64
msgid "Shadow Start"
msgstr "Varjon aloituspaikka"
-#: ../src/LevelEditor.cpp:181
+#: ../src/LevelEditor.cpp:65
msgid "Exit"
msgstr "Uloskäynti"
-#: ../src/LevelEditor.cpp:181
+#: ../src/LevelEditor.cpp:65
msgid "Shadow Block"
msgstr "Varjopalikka"
-#: ../src/LevelEditor.cpp:181
+#: ../src/LevelEditor.cpp:65
msgid "Spikes"
msgstr "Piikit"
-#: ../src/LevelEditor.cpp:182
+#: ../src/LevelEditor.cpp:66
msgid "Checkpoint"
msgstr "Tallennuspiste"
-#: ../src/LevelEditor.cpp:182
-#: ../src/LevelEditSelect.cpp:258
-msgid "Swap"
-msgstr "Vaihda paikkoja"
-
-#: ../src/LevelEditor.cpp:182
-#: ../src/LevelEditor.cpp:1976
+#: ../src/LevelEditor.cpp:66
msgid "Fragile"
msgstr "Hauras palikka"
-#: ../src/LevelEditor.cpp:183
+#: ../src/LevelEditor.cpp:67
msgid "Moving Block"
msgstr "Liikkuva palikka"
-#: ../src/LevelEditor.cpp:183
+#: ../src/LevelEditor.cpp:67
msgid "Moving Shadow Block"
msgstr "Liikkuva varjopalikka"
-#: ../src/LevelEditor.cpp:183
+#: ../src/LevelEditor.cpp:67
msgid "Moving Spikes"
msgstr "Liikkuvat piikit"
-#: ../src/LevelEditor.cpp:184
+#: ../src/LevelEditor.cpp:68
msgid "Teleporter"
msgstr "Teleportti"
-#: ../src/LevelEditor.cpp:184
-#: ../src/LevelEditor.cpp:1884
+#: ../src/LevelEditor.cpp:68
msgid "Button"
msgstr "Painike"
-#: ../src/LevelEditor.cpp:184
-#: ../src/LevelEditor.cpp:1886
+#: ../src/LevelEditor.cpp:68
msgid "Switch"
msgstr "Vipu"
-#: ../src/LevelEditor.cpp:185
+#: ../src/LevelEditor.cpp:69
msgid "Conveyor Belt"
msgstr "Liukuhihna"
-#: ../src/LevelEditor.cpp:185
+#: ../src/LevelEditor.cpp:69
msgid "Shadow Conveyor Belt"
msgstr "Varjoliukuhihna"
-#: ../src/LevelEditor.cpp:185
+#: ../src/LevelEditor.cpp:69
msgid "Notification Block"
msgstr "Kyltti"
-#: ../src/LevelEditor.cpp:185
+#: ../src/LevelEditor.cpp:69
msgid "Collectable"
msgstr "Avain"
-#: ../src/LevelEditor.cpp:529
-msgid "Are you sure you want to quit?"
-msgstr "Haluatko varmasti poistua?"
+#: ../src/LevelEditor.cpp:69
+msgid "Pushable"
+msgstr "Työnnettävä"
-#: ../src/LevelEditor.cpp:529
-msgid "Quit prompt"
-msgstr "Poistuminen"
+#: ../src/LevelEditor.cpp:176
+msgid "Toggle"
+msgstr "Kytkin"
-#: ../src/LevelEditor.cpp:957
-#: ../src/LevelEditor.cpp:959
-#: ../src/LevelEditor.cpp:2609
-#: ../src/LevelEditor.cpp:2611
-#, c-format
-msgid "Level \"%s\" saved"
-msgstr "Kenttä \"%s\" tallennettu"
+#: ../src/LevelEditor.cpp:179
+msgid "Complete"
+msgstr "Kokonainen"
-#: ../src/LevelEditor.cpp:957
-#: ../src/LevelEditor.cpp:959
-#: ../src/LevelEditor.cpp:2609
-#: ../src/LevelEditor.cpp:2611
-msgid "Saved"
-msgstr "Tallennus"
+#: ../src/LevelEditor.cpp:180
+msgid "One step"
+msgstr "Hauras"
-#: ../src/LevelEditor.cpp:972
-#: ../src/LevelEditor.cpp:2732
-msgid "Level settings"
-msgstr "Kentän asetukset"
+#: ../src/LevelEditor.cpp:181
+msgid "Two steps"
+msgstr "Hauraampi"
-#: ../src/LevelEditor.cpp:976
-#: ../src/LevelEditSelect.cpp:154
-msgid "Name:"
-msgstr "Nimi:"
+#: ../src/LevelEditor.cpp:182
+msgid "Gone"
+msgstr "Hajonnut"
-#: ../src/LevelEditor.cpp:982
-msgid "Theme:"
-msgstr "Teema:"
+#: ../src/LevelEditor.cpp:216
+msgid "Deselect"
+msgstr "Poista valinta"
-#: ../src/LevelEditor.cpp:997
-msgid "Target time (s):"
-msgstr "Tavoiteaika:"
+#: ../src/LevelEditor.cpp:218 ../src/LevelEditor.cpp:821
+#: ../src/LevelEditor.cpp:3029
+msgid "Select"
+msgstr "Valitse"
-#: ../src/LevelEditor.cpp:1008
-msgid "Target recordings:"
-msgstr "Tavoitenauhoitukset"
+#: ../src/LevelEditor.cpp:219 ../src/LevelEditor.cpp:836
+#: ../src/LevelEditor.cpp:3035
+msgid "Delete"
+msgstr "Poista"
-#: ../src/LevelEditor.cpp:1576
-#: ../src/LevelEditor.cpp:1793
-msgid "Defined"
-msgstr "Määritetty"
+#: ../src/LevelEditor.cpp:224
+msgid "Link"
+msgstr "Yhdistä"
-#: ../src/LevelEditor.cpp:1579
-#: ../src/LevelEditor.cpp:1796
-#: ../src/LevelEditor.cpp:1878
-msgid "None"
-msgstr "Määrittämätön"
+#: ../src/LevelEditor.cpp:225
+msgid "Remove Links"
+msgstr "Poista yhteydet"
-#: ../src/LevelEditor.cpp:1586
-msgid "Moving block"
-msgstr "Liikkuva palikka"
+#: ../src/LevelEditor.cpp:229 ../src/LevelEditor.cpp:434
+msgid "Automatic"
+msgstr "Automaattinen"
-#: ../src/LevelEditor.cpp:1589
-msgid "Moving shadow block"
-msgstr "Liikkuva varjopalikka"
+#: ../src/LevelEditor.cpp:242
+msgid "Path"
+msgstr "Reitti"
-#: ../src/LevelEditor.cpp:1592
-msgid "Moving spikes"
-msgstr "Liikkuvat piikit"
+#: ../src/LevelEditor.cpp:243
+msgid "Remove Path"
+msgstr "Poista polku"
-#: ../src/LevelEditor.cpp:1599
-#: ../src/LevelEditor.cpp:1736
+#: ../src/LevelEditor.cpp:246 ../src/LevelEditor.cpp:252
+#: ../src/LevelEditor.cpp:412
msgid "Enabled"
msgstr "Aktivoitu"
-#: ../src/LevelEditor.cpp:1605
-msgid "Loop"
+#: ../src/LevelEditor.cpp:247 ../src/LevelEditor.cpp:423
+#, fuzzy
+msgid "Looping"
msgstr "Toistuva"
-#: ../src/LevelEditor.cpp:1611
-msgid "Path"
-msgstr "Reitti"
+#: ../src/LevelEditor.cpp:253
+msgid "Speed"
+msgstr "Nopeus"
+
+#: ../src/LevelEditor.cpp:263
+msgid "Message"
+msgstr "Viesti"
+
+#: ../src/LevelEditor.cpp:265 ../src/LevelEditor.cpp:270
+#: ../src/LevelEditor.cpp:511
+msgid "Scripting"
+msgstr "Skriptaus"
+
+#: ../src/LevelEditor.cpp:269 ../src/TitleMenu.cpp:235
+msgid "Settings"
+msgstr "Asetukset"
-#: ../src/LevelEditor.cpp:1671
+#: ../src/LevelEditor.cpp:369
msgid "Notification block"
msgstr "Kyltti"
-#: ../src/LevelEditor.cpp:1674
+#: ../src/LevelEditor.cpp:374
msgid "Enter message here:"
msgstr "Kirjoita viesti:"
-#: ../src/LevelEditor.cpp:1728
-msgid "Shadow Conveyor belt"
-msgstr "Varjoliukuhihna"
-
-#: ../src/LevelEditor.cpp:1730
-msgid "Conveyor belt"
-msgstr "Liukuhihna"
+#: ../src/LevelEditor.cpp:479
+msgid "Conveyor belt speed"
+msgstr "Liukuhihnan nopeus"
-#: ../src/LevelEditor.cpp:1742
+#: ../src/LevelEditor.cpp:484
msgid "Enter speed here:"
msgstr "Aseta nopeus:"
-#: ../src/LevelEditor.cpp:1800
-msgid "Portal"
-msgstr "Portaali"
-
-#: ../src/LevelEditor.cpp:1803
-msgid "Activate on touch"
-msgstr "Aktivoi koskettaessa"
+#: ../src/LevelEditor.cpp:516
+msgid "Id:"
+msgstr "Id:"
-#: ../src/LevelEditor.cpp:1809
-#: ../src/LevelEditor.cpp:1914
-msgid "Targets:"
-msgstr "Kohteet:"
+#: ../src/LevelEditor.cpp:575
+msgid "Level Scripting"
+msgstr "Kentän skriptaus"
-#: ../src/LevelEditor.cpp:1875
-#, c-format
-msgid "%d Defined"
-msgstr "%d määritelty"
-
-#: ../src/LevelEditor.cpp:1891
-msgid "Behaviour:"
-msgstr "Käytös:"
+#: ../src/LevelEditor.cpp:851
+msgid "Configure"
+msgstr "Muokkaa"
-#: ../src/LevelEditor.cpp:1897
-msgid "On"
-msgstr "Päällä"
+#: ../src/LevelEditor.cpp:1277
+msgid "Are you sure you want to quit?"
+msgstr "Haluatko varmasti poistua?"
-#: ../src/LevelEditor.cpp:1898
-msgid "Off"
-msgstr "Pois"
+#: ../src/LevelEditor.cpp:1277
+msgid "Quit prompt"
+msgstr "Poistuminen"
-#: ../src/LevelEditor.cpp:1899
-msgid "Toggle"
-msgstr "Kytkin"
+#: ../src/LevelEditor.cpp:1384 ../src/LevelEditor.cpp:1386
+#: ../src/LevelEditor.cpp:1853 ../src/LevelEditor.cpp:1855
+#, c-format
+msgid "Level \"%s\" saved"
+msgstr "Kenttä \"%s\" tallennettu"
-#: ../src/LevelEditor.cpp:1979
-msgid "State:"
-msgstr "Kunto:"
+#: ../src/LevelEditor.cpp:1384 ../src/LevelEditor.cpp:1386
+#: ../src/LevelEditor.cpp:1853 ../src/LevelEditor.cpp:1855
+msgid "Saved"
+msgstr "Tallennus"
-#: ../src/LevelEditor.cpp:1985
-msgid "Complete"
-msgstr "Kokonainen"
+#: ../src/LevelEditor.cpp:1862 ../src/LevelEditor.cpp:3041
+msgid "Level settings"
+msgstr "Kentän asetukset"
-#: ../src/LevelEditor.cpp:1986
-msgid "One step"
-msgstr "Hauras"
+#: ../src/LevelEditor.cpp:1874
+msgid "Theme:"
+msgstr "Teema:"
-#: ../src/LevelEditor.cpp:1987
-msgid "Two steps"
-msgstr "Hauraampi"
+#: ../src/LevelEditor.cpp:1882
+msgid "Target time (s):"
+msgstr "Tavoiteaika:"
-#: ../src/LevelEditor.cpp:1988
-msgid "Gone"
-msgstr "Hajonnut"
+#: ../src/LevelEditor.cpp:1896
+msgid "Target recordings:"
+msgstr "Tavoitenauhoitukset"
-#: ../src/LevelEditor.cpp:2717
-msgid "Select"
-msgstr "Valitse"
+#: ../src/LevelEditor.cpp:2996 ../src/LevelEditor.cpp:3002
+#, c-format
+msgid "Movespeed: %s"
+msgstr "Nopeus: %s"
-#: ../src/LevelEditor.cpp:2720
+#: ../src/LevelEditor.cpp:3032
msgid "Add"
msgstr "Lisää"
-#: ../src/LevelEditor.cpp:2723
-msgid "Delete"
-msgstr "Poista"
-
-#: ../src/LevelEditor.cpp:2726
-msgid "Configure"
-msgstr "Muokkaa"
-
-#: ../src/LevelEditor.cpp:2729
-#: ../src/LevelPlaySelect.cpp:65
-#: ../src/TitleMenu.cpp:47
+#: ../src/LevelEditor.cpp:3038 ../src/LevelPlaySelect.cpp:75
+#: ../src/TitleMenu.cpp:50
msgid "Play"
msgstr "Pelaa"
-#: ../src/LevelEditor.cpp:2735
+#: ../src/LevelEditor.cpp:3044
msgid "Save level"
msgstr "Tallenna kenttä"
-#: ../src/LevelEditor.cpp:2738
+#: ../src/LevelEditor.cpp:3047
msgid "Back to menu"
msgstr "Takaisin valikkoon"
-#: ../src/LevelEditor.cpp:2784
-#, c-format
-msgid "Movespeed: %s"
-msgstr "Nopeus: %s"
-
-#: ../src/LevelEditSelect.cpp:41
-#: ../src/TitleMenu.cpp:49
-msgid "Map Editor"
-msgstr "Kenttäeditori"
-
-#: ../src/LevelEditSelect.cpp:78
-msgid "New Levelpack"
-msgstr "Uusi kenttäpaketti"
-
-#: ../src/LevelEditSelect.cpp:83
-msgid "Pack Properties"
-msgstr "Paketin asetukset"
-
-#: ../src/LevelEditSelect.cpp:88
-msgid "Remove Pack"
-msgstr "Poista paketti"
-
-#: ../src/LevelEditSelect.cpp:93
-msgid "Move Map"
-msgstr "Siirrä kenttä"
-
-#: ../src/LevelEditSelect.cpp:99
-msgid "Remove Map"
-msgstr "Poista kenttä"
-
-#: ../src/LevelEditSelect.cpp:104
-msgid "Edit Map"
-msgstr "Muokkaa kenttää"
-
-#: ../src/LevelEditSelect.cpp:151
-msgid "Properties"
-msgstr "Asetukset"
-
-#: ../src/LevelEditSelect.cpp:161
-msgid "Description:"
-msgstr "Kuvaus:"
-
-#: ../src/LevelEditSelect.cpp:168
-msgid "Congratulation text:"
-msgstr "Onnittelu:"
-
-#: ../src/LevelEditSelect.cpp:203
-#: ../src/LevelEditSelect.cpp:371
-msgid "Add level"
-msgstr "Lisää kenttä"
-
-#: ../src/LevelEditSelect.cpp:206
-msgid "File name:"
-msgstr "Tiedostonimi:"
-
-#: ../src/LevelEditSelect.cpp:243
-msgid "Move level"
-msgstr "Siirrä kenttä"
+#: ../src/LevelPlaySelect.cpp:47
+msgid "Select Level"
+msgstr "Valitse kenttä"
-#: ../src/LevelEditSelect.cpp:246
-msgid "Level: "
-msgstr "Kenttä:"
+#: ../src/LevelPlaySelect.cpp:102
+msgid "Choose a level"
+msgstr "Valitse kenttä"
-#: ../src/LevelEditSelect.cpp:256
-msgid "Before"
-msgstr "Ennen"
+#: ../src/LevelPlaySelect.cpp:103
+msgid "Time:"
+msgstr "Aika:"
-#: ../src/LevelEditSelect.cpp:257
-msgid "After"
-msgstr "Jälkeen"
+#: ../src/LevelPlaySelect.cpp:104 ../src/StatisticsScreen.cpp:166
+msgid "Recordings:"
+msgstr "Nauhoitukset:"
-#: ../src/LevelEditSelect.cpp:439
-msgid "Are you sure?"
-msgstr "Oletko varma?"
+#: ../src/StatisticsManager.cpp:398
+msgid "New achievement:"
+msgstr "Uusi saavutus:"
-#: ../src/LevelEditSelect.cpp:439
-msgid "Remove prompt"
-msgstr "Poista"
+#: ../src/StatisticsManager.cpp:406
+#, fuzzy, c-format
+msgid "Achieved on %s"
+msgstr "Saavutettu %s"
-#: ../src/LevelEditSelect.cpp:579
-msgid "No file name given for the new level."
-msgstr "Uudelle kentälle ei annettu nimeä."
+#: ../src/StatisticsManager.cpp:412
+msgid "Unknown achievement"
+msgstr "Tuntematon saavutus"
-#: ../src/LevelEditSelect.cpp:579
-msgid "Missing file name"
-msgstr "Puuttuva tiedostonimi"
+#: ../src/StatisticsManager.cpp:418
+#, c-format
+msgid "Achieved %1.0f%%"
+msgstr "Saavutettu %1.0f%%"
-#: ../src/LevelEditSelect.cpp:656
-msgid "The entered level number isn't valid!"
-msgstr "Kentälle annettu järjestysnumero ei kelpaa!"
+#: ../src/StatisticsManager.cpp:422
+msgid "Not achieved"
+msgstr "Ei saavutettu"
-#: ../src/LevelEditSelect.cpp:656
-msgid "Illegal number"
-msgstr "Järjestysnumero"
+#: ../src/StatisticsScreen.cpp:135 ../src/TitleMenu.cpp:204
+msgid "Achievements and Statistics"
+msgstr "Saavutukset ja tilastot"
-#: ../src/LevelPlaySelect.cpp:43
-msgid "Select Level"
-msgstr "Valitse kenttä"
+#: ../src/StatisticsScreen.cpp:147
+msgid "Total"
+msgstr "Kokonaismäärä"
-#: ../src/LevelPlaySelect.cpp:92
-msgid "Choose a level"
-msgstr "Valitse kenttä"
+#: ../src/StatisticsScreen.cpp:156
+msgid "Traveling distance (m)"
+msgstr "Kuljettu matka (m)"
-#: ../src/LevelPlaySelect.cpp:93
-#: ../src/LevelPlaySelect.cpp:231
-#: ../src/LevelPlaySelect.cpp:242
-msgid "Time:"
-msgstr "Aika:"
+#: ../src/StatisticsScreen.cpp:157
+msgid "Jump times"
+msgstr "Hyppyjä"
-#: ../src/LevelPlaySelect.cpp:94
-#: ../src/LevelPlaySelect.cpp:240
-#: ../src/LevelPlaySelect.cpp:243
-msgid "Recordings:"
-msgstr "Nauhoitukset:"
+#: ../src/StatisticsScreen.cpp:158
+msgid "Die times"
+msgstr "Kuolemia"
-#: ../src/Main.cpp:56
-#, c-format
-msgid "Usage: %s [OPTIONS] ...\n"
-msgstr "Käyttö: %s [ASETUKSET] ...\n"
+#: ../src/StatisticsScreen.cpp:159
+msgid "Squashed times"
+msgstr "Litistymisiä"
-#: ../src/Main.cpp:57
-msgid "Available options:\n"
-msgstr "Saatavilla olevat asetukset:\n"
+#: ../src/StatisticsScreen.cpp:166
+msgid "Switch pulled times:"
+msgstr "Vipujen käyttö:"
-#: ../src/Main.cpp:58
-msgid "Specifies the data directory."
-msgstr "Aseta kansio, josta pelin tiedostot haetaan."
+#: ../src/StatisticsScreen.cpp:167
+msgid "Swap times:"
+msgstr "Vaihdot:"
-#: ../src/Main.cpp:59
-msgid "Specifies the user preferences directory."
-msgstr "Aseta kansio, josta käyttäjän asetukset haetaan"
+#: ../src/StatisticsScreen.cpp:168
+msgid "Save times:"
+msgstr "Tallennukset:"
-#: ../src/Main.cpp:60
-msgid "Run the game fullscreen."
-msgstr "Aja peli kokoruudulla."
+#: ../src/StatisticsScreen.cpp:168
+msgid "Load times:"
+msgstr "Lataukset:"
-#: ../src/Main.cpp:61
-msgid "Run the game windowed."
-msgstr "Aja peli ikkunassa."
+#: ../src/StatisticsScreen.cpp:175
+msgid "Completed levels:"
+msgstr "Suoritetus kentät:"
-#: ../src/Main.cpp:62
-msgid "Set the music volume."
-msgstr "Aseta musiikin äänenvoimakkuus."
+#: ../src/StatisticsScreen.cpp:217
+msgid "In-game time:"
+msgstr "Peliaika:"
-#: ../src/Main.cpp:63
-msgid "Set the sound volume."
-msgstr "Aseta äänien voimakkuus."
+#: ../src/StatisticsScreen.cpp:230
+msgid "Level editing time:"
+msgstr "Aika kenttäeditorissa:"
-#: ../src/Main.cpp:64
-msgid "Change a setting to a given value."
-msgstr "Vaihda tietty asutus toiseksi."
+#: ../src/StatisticsScreen.cpp:242
+msgid "Created levels:"
+msgstr "Luodut kentät:"
-#: ../src/Main.cpp:65
-msgid "Display the version and quit."
-msgstr "Näytä versio ja lopeta."
+#: ../src/StatisticsScreen.cpp:283
+msgid "Achievements"
+msgstr "Saavutukset"
-#: ../src/Main.cpp:66
-msgid "Display this help message."
-msgstr "Näytä tämä ohje."
+#: ../src/StatisticsScreen.cpp:284
+msgid "Statistics"
+msgstr "Tilastot"
-#: ../src/TitleMenu.cpp:48
+#: ../src/TitleMenu.cpp:51
msgid "Options"
msgstr "Asetukset"
-#: ../src/TitleMenu.cpp:51
+#: ../src/TitleMenu.cpp:54
msgid "Quit"
msgstr "Poistu"
-#: ../src/TitleMenu.cpp:109
+#: ../src/TitleMenu.cpp:130
msgid "Enable internet in order to install addons."
msgstr "Aktivoi internet asentaaksesi lisäosia."
-#: ../src/TitleMenu.cpp:109
+#: ../src/TitleMenu.cpp:130
msgid "Internet disabled"
msgstr "Internet pois käytöstä"
-#: ../src/TitleMenu.cpp:188
-msgid "Settings"
-msgstr "Asetukset"
+#: ../src/TitleMenu.cpp:202 ../src/TitleMenu.cpp:767
+msgid "Credits"
+msgstr "Tekijät"
+
+#: ../src/TitleMenu.cpp:289
+msgid "General"
+msgstr "Yleinen"
+
+#: ../src/TitleMenu.cpp:290
+msgid "Controls"
+msgstr "Ohjaus"
-#: ../src/TitleMenu.cpp:236
+#: ../src/TitleMenu.cpp:301
msgid "Music"
msgstr "Musiikki"
-#: ../src/TitleMenu.cpp:244
+#: ../src/TitleMenu.cpp:309
msgid "Sound"
msgstr "Äänet"
-#: ../src/TitleMenu.cpp:252
-msgid "Fullscreen"
-msgstr "Kokoruutu"
-
-#: ../src/TitleMenu.cpp:257
+#: ../src/TitleMenu.cpp:317
msgid "Resolution"
msgstr "Resoluutio"
-#: ../src/TitleMenu.cpp:339
+#: ../src/TitleMenu.cpp:399
msgid "Language"
msgstr "Kieli"
-#: ../src/TitleMenu.cpp:348
+#: ../src/TitleMenu.cpp:407
msgid "Auto-Detect"
msgstr "Valitse automaattisesti"
-#: ../src/TitleMenu.cpp:376
+#: ../src/TitleMenu.cpp:435
msgid "Theme"
msgstr "Teema"
-#: ../src/TitleMenu.cpp:409
+#: ../src/TitleMenu.cpp:469
+msgid "Internet proxy"
+msgstr "Internet proxy"
+
+#: ../src/TitleMenu.cpp:478
+msgid "Fullscreen"
+msgstr "Kokoruutu"
+
+#: ../src/TitleMenu.cpp:483
msgid "Level themes"
msgstr "Kentän teema"
-#: ../src/TitleMenu.cpp:414
+#: ../src/TitleMenu.cpp:488
msgid "Internet"
msgstr "Internet"
-#: ../src/TitleMenu.cpp:420
-msgid "Internet proxy"
-msgstr "Internet proxy"
+#: ../src/TitleMenu.cpp:493
+msgid "Fade transition"
+msgstr "Häivytys"
-#: ../src/TitleMenu.cpp:437
-msgid "Clear Progress"
-msgstr "Nollaa edistyminen"
+#: ../src/TitleMenu.cpp:498
+msgid "Quick record"
+msgstr "Nopea nauhoitus"
-#: ../src/TitleMenu.cpp:447
+#: ../src/TitleMenu.cpp:521
msgid "Save Changes"
msgstr "Tallenna"
-#: ../src/TitleMenu.cpp:546
+#: ../src/TitleMenu.cpp:702
msgid "Do you really want to reset level progress?"
msgstr "Haluatko varmasti nollata edistymisesi?"
-#: ../src/TitleMenu.cpp:546
+#: ../src/TitleMenu.cpp:702
msgid "Warning"
msgstr "Varoitus"
+#: ../src/TitleMenu.cpp:746
+msgid "Clear Progress"
+msgstr "Nollaa edistyminen"
+
+#: ../src/AchievementList.h:38
+msgid "Newbie"
+msgstr "Aloittelija"
+
+#: ../src/AchievementList.h:38
+msgid "Complete a level."
+msgstr "Suorita 1 kenttä."
+
+#: ../src/AchievementList.h:39
+msgid "Experienced player"
+msgstr "Kokenut pelaaja"
+
+#: ../src/AchievementList.h:39
+msgid "Complete 50 levels."
+msgstr "Suorita 50 kenttää."
+
+#: ../src/AchievementList.h:40
+msgid "Good job!"
+msgstr "Hienoa!"
+
+#: ../src/AchievementList.h:40
+msgid "Receive a gold medal."
+msgstr "Ansaitse kultamitali."
+
+#: ../src/AchievementList.h:41
+msgid "Expert"
+msgstr "Mestari"
+
+#: ../src/AchievementList.h:41
+msgid "Earn 50 gold medal."
+msgstr "Ansaitse 50 kultamitalia."
+
+#: ../src/AchievementList.h:43
+msgid "Graduate"
+msgstr "Valmistu"
+
+#: ../src/AchievementList.h:43
+msgid "Complete the tutorial level pack."
+msgstr "Suorita johdatuskurssi."
+
+#: ../src/AchievementList.h:44
+msgid "Outstanding graduate"
+msgstr "Erinomainen todistus"
+
+#: ../src/AchievementList.h:44
+msgid "Complete the tutorial level pack with gold for all levels."
+msgstr "Suorita johdatuskurssi ainoastaan kultamitaleilla."
+
+#: ../src/AchievementList.h:46
+msgid "Hooked"
+msgstr "Koukussa"
+
+#: ../src/AchievementList.h:46
+msgid "Play Me and My Shadow for more than 2 hours."
+msgstr "Pelaa Me and My Shadow:ta 2 tuntia."
+
+#: ../src/AchievementList.h:47
+msgid "Loyal fan of Me and My Shadow"
+msgstr "Me and My Shadow:n uskollinen fani"
+
+#: ../src/AchievementList.h:47
+msgid "Play Me and My Shadow for more than 24 hours."
+msgstr "Pelaa Me and My Shadow:ta 24 tuntia."
+
+#: ../src/AchievementList.h:49
+msgid "Constructor"
+msgstr "Rakentaja"
+
+#: ../src/AchievementList.h:49
+msgid "Use the level editor for more than 2 hours."
+msgstr "Käytä kenttäeditoria 2 tuntia."
+
+#: ../src/AchievementList.h:50
+msgid "The creator"
+msgstr "Luoja"
+
+#: ../src/AchievementList.h:50
+msgid "Use the level editor for more than 24 hours."
+msgstr "Käytä kenttäeditoria 24 tuntia."
+
+#: ../src/AchievementList.h:52
+msgid "Look, cute level!"
+msgstr ""
+
+#: ../src/AchievementList.h:52
+msgid "Create a level for the first time."
+msgstr "Luo kenttä ensimmäistä kertaa."
+
+#: ../src/AchievementList.h:53
+msgid "The level museum"
+msgstr ""
+
+#: ../src/AchievementList.h:53
+msgid "Create 50 levels."
+msgstr "Rakenna 50 kenttää."
+
+#: ../src/AchievementList.h:55
+msgid "Frog"
+msgstr "Sammakko"
+
+#: ../src/AchievementList.h:55
+msgid "Jump 1000 times."
+msgstr "Hyppää 1000 kertaa."
+
+#: ../src/AchievementList.h:57
+msgid "Wanderer"
+msgstr "Vaeltaja"
+
+#: ../src/AchievementList.h:57
+msgid "Travel 100 meters."
+msgstr "Matkusta 100 metriä."
+
+#: ../src/AchievementList.h:58
+msgid "Runner"
+msgstr "Juoksija"
+
+#: ../src/AchievementList.h:58
+msgid "Travel 1 kilometer."
+msgstr "Matkusta 1 kilometri."
+
+#: ../src/AchievementList.h:59
+msgid "Long distance runner"
+msgstr "Kestävyysjouksu"
+
+#: ../src/AchievementList.h:59
+msgid "Travel 10 kilometers."
+msgstr "Matkaa 10 kilometriä."
+
+#: ../src/AchievementList.h:60
+msgid "Marathon runner"
+msgstr "Maraton"
+
+#: ../src/AchievementList.h:60
+msgid "Travel 42,195 meters."
+msgstr "Matkaa 42,195 metriä."
+
+#: ../src/AchievementList.h:62
+msgid "Be careful!"
+msgstr "Varovasti!"
+
+#: ../src/AchievementList.h:62
+msgid "Die for the first time."
+msgstr "Kuole ensimmäistä kertaa."
+
+#: ../src/AchievementList.h:63
+msgid "It doesn't matter..."
+msgstr "Ei se haittaa..."
+
+#: ../src/AchievementList.h:63
+msgid "Die 50 times."
+msgstr "Kuole 50 kertaa."
+
+#: ../src/AchievementList.h:64
+msgid "Expert of trial and error"
+msgstr "Yritys ja erehdys"
+
+#: ../src/AchievementList.h:64
+msgid "Die 1000 times."
+msgstr "Kuole 1000 kertaa."
+
+#: ../src/AchievementList.h:66
+msgid "Keep an eye for moving blocks!"
+msgstr "Varo liikuvia palikoita!"
+
+#: ../src/AchievementList.h:66
+msgid "Get squashed for the first time."
+msgstr "Liiskaannu ensimmäistä kertaa."
+
+#: ../src/AchievementList.h:67
+msgid "Potato masher"
+msgstr ""
+
+#: ../src/AchievementList.h:67
+msgid "Get squashed 50 times."
+msgstr "Liiskaannu 50 kertaa."
+
+#: ../src/AchievementList.h:69
+msgid "Double kill"
+msgstr "Tuplatappo"
+
+#: ../src/AchievementList.h:69
+msgid "Get both the player and the shadow dead."
+msgstr "Tapa samanaikaisesti sekä pelaaja että varjo."
+
+#: ../src/AchievementList.h:71
+msgid "Bad luck"
+msgstr "Huono-onninen"
+
+#: ../src/AchievementList.h:71
+msgid "Die 5 times in under 5 seconds."
+msgstr "Kuole 5 kertaa 5 sekunnin sisällä."
+
+#: ../src/AchievementList.h:72
+msgid "This level is too dangerous"
+msgstr "Tämä kenttä on liian vaarallinen"
+
+#: ../src/AchievementList.h:72
+msgid "Die 10 times in under 5 seconds."
+msgstr "Kuole 10 kertaa 5 sekunnin sisällä."
+
+#: ../src/AchievementList.h:74
+msgid "You forgot your friend"
+msgstr "Unohdit ystäväsi"
+
+#: ../src/AchievementList.h:74
+msgid "Finish the level with the player or the shadow dead."
+msgstr "Suorita kenttä, kun pelaaja tai varjo on kuollut."
+
+#: ../src/AchievementList.h:75
+msgid "Just in time"
+msgstr "Ajoissa"
+
+#: ../src/AchievementList.h:75
+msgid "Reach the exit with the player and the shadow simultaneously."
+msgstr "Saavuta uloskäynti samanaikaisesti sekä pelaajalla että varjolla."
+
+#: ../src/AchievementList.h:77
+msgid "Recorder"
+msgstr "Nauhoittaja"
+
+#: ../src/AchievementList.h:77
+msgid "Record 100 times."
+msgstr "Nauhoita 100 kertaa."
+
+#: ../src/AchievementList.h:78
+msgid "Shadowmaster"
+msgstr "Varjomestari"
+
+#: ../src/AchievementList.h:78
+msgid "Record 1000 times."
+msgstr "Nauhoita 1000 kertaa."
+
+#: ../src/AchievementList.h:80
+msgid "Switch puller"
+msgstr "Vivun vääntäjä"
+
+#: ../src/AchievementList.h:80
+msgid "Pull the switch 100 times."
+msgstr "Vedä 100 kertaa vivuista."
+
+#: ../src/AchievementList.h:81
+msgid "The switch is broken!"
+msgstr "Vipu on rikki!"
+
+#: ../src/AchievementList.h:81
+msgid "Pull the switch 1000 times."
+msgstr "Vedä 1000 kertaa vivuista."
+
+#: ../src/AchievementList.h:83
+msgid "Swapper"
+msgstr "Vaihtaja"
+
+#: ../src/AchievementList.h:83
+msgid "Swap 100 times."
+msgstr "Käytä vaihtoa 100 kertaa."
+
+#: ../src/AchievementList.h:84
+msgid "Player to shadow to player to shadow..."
+msgstr "Edestakaisin..."
+
+#: ../src/AchievementList.h:84
+msgid "Swap 1000 times."
+msgstr "Käytä vaihtoa 1000 kertaa."
+
+#: ../src/AchievementList.h:86
+msgid "Play it save"
+msgstr "Varmuuden vuoksi"
+
+#: ../src/AchievementList.h:86
+msgid "Save 1000 times."
+msgstr "Tallenna 1000 kertaa."
+
+#: ../src/AchievementList.h:87
+msgid "This game is too hard"
+msgstr "Tämä peli on liian vaikea"
+
+#: ../src/AchievementList.h:87
+msgid "Load the game 1000 times."
+msgstr "Lataa peli 1000 kertaa."
+
+#: ../src/AchievementList.h:89
+msgid "Panic save"
+msgstr "Hätätallennus"
+
+#: ../src/AchievementList.h:89
+msgid "Save twice in 1 second."
+msgstr "Tallenna kahdesti sekunnin sisällä."
+
+#: ../src/AchievementList.h:90
+msgid "Panic load"
+msgstr "Hätälataus"
+
+#: ../src/AchievementList.h:90
+msgid "Load twice in 1 second."
+msgstr "Lataa peli kahdesti sekunnin sisällä."
+
+#: ../src/AchievementList.h:92
+msgid "Bad saving position"
+msgstr "Tuli tallennettua pahaan paikkaan"
+
+#: ../src/AchievementList.h:92
+msgid "Load the game and die within 1 second."
+msgstr "Lataa peli ja kuole sekunnun sisällä."
+
+#: ../src/AchievementList.h:93
+msgid "This level is too hard"
+msgstr "Tämä kenttä on liian vaikea"
+
+#: ../src/AchievementList.h:93
+msgid "Load the same save and die 100 times."
+msgstr "Lataa sama tallennus ja kuole 100 kertaa."
+
+#: ../src/AchievementList.h:95
+msgid "Quick swap"
+msgstr "Nopea vaihto"
+
+#: ../src/AchievementList.h:95
+msgid "Swap twice in under a second."
+msgstr "Käytä vaihtoa kahdesti sekunnin sisällä."
+
+#: ../src/AchievementList.h:98
+msgid "Horizontal confusion"
+msgstr "Sekaisin suunnasta"
+
+#: ../src/AchievementList.h:98
+msgid "Press left and right simultaneously."
+msgstr "Paina oikealle ja vasemmalla samanaikaisesti."
+
+#: ../src/AchievementList.h:100
+msgid "Programmer"
+msgstr "Ohjelmoija"
+
+#: ../src/AchievementList.h:100
+msgid "Play the development version of Me and My Shadow."
+msgstr "Pelaa Me and My Shadow:n kehittäjäversiota."
+
# TRANSLATORS: name of a key
msgid "backspace"
msgstr "askelpalautin"
# TRANSLATORS: name of a key
msgid "tab"
msgstr "sarkain"
# TRANSLATORS: name of a key
msgid "clear"
msgstr "tyhjennä"
# TRANSLATORS: name of a key
msgid "return"
msgstr "palautus"
# TRANSLATORS: name of a key
msgid "pause"
msgstr "pause"
msgid "escape"
msgstr "esc"
# TRANSLATORS: name of a key
msgid "space"
msgstr "välilyönti"
msgid "delete"
msgstr "delete"
msgid "enter"
msgstr "enter"
# TRANSLATORS: name of a key
msgid "equals"
msgstr "yhtäsuuri"
msgid "up"
msgstr "ylös"
# TRANSLATORS: name of a key
msgid "down"
msgstr "alas"
msgid "right"
msgstr "oikea"
# TRANSLATORS: name of a key
msgid "left"
msgstr "vasen"
# TRANSLATORS: name of a key
msgid "insert"
msgstr "insert"
# TRANSLATORS: name of a key
msgid "home"
msgstr "home"
msgid "end"
msgstr "end"
# TRANSLATORS: name of a key
msgid "page up"
msgstr "page up"
# TRANSLATORS: name of a key
msgid "page down"
msgstr "page down"
msgid "numlock"
msgstr "numlock"
# TRANSLATORS: name of a key
msgid "caps lock"
msgstr "caps lock"
# TRANSLATORS: name of a key
msgid "scroll lock"
msgstr "scroll lock"
# TRANSLATORS: name of a key
msgid "right shift"
msgstr "oikea vaihto"
# TRANSLATORS: name of a key
msgid "left shift"
msgstr "vasen vaihto"
# TRANSLATORS: name of a key
msgid "right ctrl"
msgstr "oikea ctrl"
# TRANSLATORS: name of a key
msgid "left ctrl"
msgstr "vasen ctrl"
# TRANSLATORS: name of a key
msgid "right alt"
msgstr "oikea alt"
# TRANSLATORS: name of a key
msgid "left alt"
msgstr "vasen alt"
# TRANSLATORS: name of a key
msgid "right meta"
msgstr "oikea meta"
# TRANSLATORS: name of a key
msgid "left meta"
msgstr "vasen meta"
# TRANSLATORS: name of a key
msgid "left super"
msgstr "vasen super"
# TRANSLATORS: name of a key
msgid "right super"
msgstr "oikea super"
# TRANSLATORS: name of a key
msgid "alt gr"
msgstr "alt gr"
# TRANSLATORS: name of a key
msgid "compose"
msgstr "compose"
# TRANSLATORS: name of a key
msgid "help"
msgstr "apu"
# TRANSLATORS: name of a key
msgid "print screen"
msgstr "print screen"
# TRANSLATORS: name of a key
msgid "sys req"
msgstr "sys req"
# TRANSLATORS: name of a key
msgid "break"
msgstr "break"
# TRANSLATORS: name of a key
msgid "menu"
msgstr "valikko"
# TRANSLATORS: name of a key
msgid "power"
msgstr "virta"
# TRANSLATORS: name of a key
msgid "euro"
msgstr "euro"
# TRANSLATORS: name of a key
msgid "undo"
msgstr "kumoa"
-msgid "Credits"
-msgstr "Tekijät"
+#~ msgid "Primary key"
+#~ msgstr "Ensisijaiset"
+
+#~ msgid "Alternative key"
+#~ msgstr "Toissijaiset"
+
+#~ msgid "Unset the key"
+#~ msgstr "Tyhjennä valinta"
+
+#~ msgid "(Not set)"
+#~ msgstr "(Ei määritetty)"
+
+#~ msgid "Config Keys"
+#~ msgstr "Aseta näppäimet"
+
+#~ msgid "Toolbox"
+#~ msgstr "Työkalulaatikko"
+
+#~ msgid "Defined"
+#~ msgstr "Määritetty"
+
+#~ msgid "None"
+#~ msgstr "Määrittämätön"
+
+#~ msgid "Moving block"
+#~ msgstr "Liikkuva palikka"
+
+#~ msgid "Moving shadow block"
+#~ msgstr "Liikkuva varjopalikka"
+
+#~ msgid "Moving spikes"
+#~ msgstr "Liikkuvat piikit"
+
+#~ msgid "Shadow Conveyor belt"
+#~ msgstr "Varjoliukuhihna"
+
+#~ msgid "Portal"
+#~ msgstr "Portaali"
+
+#~ msgid "Activate on touch"
+#~ msgstr "Aktivoi koskettaessa"
+
+#~ msgid "Targets:"
+#~ msgstr "Kohteet:"
+
+#~ msgid "%d Defined"
+#~ msgstr "%d määritelty"
+
+#~ msgid "Behaviour:"
+#~ msgstr "Käytös:"
+
+#~ msgid "State:"
+#~ msgstr "Kunto:"
+
+#~ msgid "Usage: %s [OPTIONS] ...\n"
+#~ msgstr "Käyttö: %s [ASETUKSET] ...\n"
+
+#~ msgid "Available options:\n"
+#~ msgstr "Saatavilla olevat asetukset:\n"
+
+#~ msgid "Specifies the data directory."
+#~ msgstr "Aseta kansio, josta pelin tiedostot haetaan."
+
+#~ msgid "Specifies the user preferences directory."
+#~ msgstr "Aseta kansio, josta käyttäjän asetukset haetaan"
+
+#~ msgid "Run the game fullscreen."
+#~ msgstr "Aja peli kokoruudulla."
+
+#~ msgid "Run the game windowed."
+#~ msgstr "Aja peli ikkunassa."
+
+#~ msgid "Set the music volume."
+#~ msgstr "Aseta musiikin äänenvoimakkuus."
+
+#~ msgid "Set the sound volume."
+#~ msgstr "Aseta äänien voimakkuus."
+
+#~ msgid "Change a setting to a given value."
+#~ msgstr "Vaihda tietty asutus toiseksi."
+
+#~ msgid "Display the version and quit."
+#~ msgstr "Näytä versio ja lopeta."
+
+#~ msgid "Display this help message."
+#~ msgstr "Näytä tämä ohje."
diff --git a/data/locale/messages.pot b/data/locale/messages.pot
index 9b48786..e31cd51 100644
--- a/data/locale/messages.pot
+++ b/data/locale/messages.pot
@@ -1,1463 +1,1495 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: meandmyshadow 0.5svn\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-02-22 15:23+0100\n"
+"POT-Creation-Date: 2013-04-28 21:15+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: ../src/Addons.cpp:46 ../src/TitleMenu.cpp:52
+#: ../src/Addons.cpp:49 ../src/TitleMenu.cpp:53
msgid "Addons"
msgstr ""
-#: ../src/Addons.cpp:65
+#: ../src/Addons.cpp:79
msgid "Unable to initialize addon menu:"
msgstr ""
-#: ../src/Addons.cpp:73 ../src/Addons.cpp:122 ../src/LevelEditSelect.cpp:74
-#: ../src/LevelSelect.cpp:201 ../src/StatisticsScreen.cpp:323
-#: ../src/TitleMenu.cpp:812
+#: ../src/Addons.cpp:87 ../src/Addons.cpp:137 ../src/Addons.cpp:478
+#: ../src/Addons.cpp:504 ../src/LevelEditSelect.cpp:74
+#: ../src/LevelSelect.cpp:201 ../src/StatisticsScreen.cpp:276
+#: ../src/TitleMenu.cpp:821
msgid "Back"
msgstr ""
-#: ../src/Addons.cpp:104
+#: ../src/Addons.cpp:118
msgid "Levels"
msgstr ""
-#: ../src/Addons.cpp:105
+#: ../src/Addons.cpp:119
msgid "Level Packs"
msgstr ""
-#: ../src/Addons.cpp:106
+#: ../src/Addons.cpp:120
msgid "Themes"
msgstr ""
-#: ../src/Addons.cpp:126 ../src/Addons.cpp:614
-msgid "Install"
-msgstr ""
-
-#: ../src/Addons.cpp:130
-msgid "Update"
-msgstr ""
-
-#: ../src/Addons.cpp:141
+#: ../src/Addons.cpp:148
msgid "ERROR: unable to download addons file!"
msgstr ""
-#: ../src/Addons.cpp:153
+# TRANSLATORS: addon_list is the name of a file and should not be translated.
+#: ../src/Addons.cpp:161
msgid "ERROR: unable to load addon_list file!"
msgstr ""
-#: ../src/Addons.cpp:164
+#: ../src/Addons.cpp:172
msgid "ERROR: Invalid file format of addons file!"
msgstr ""
-#: ../src/Addons.cpp:185
+# TRANSLATORS: installed_addons is the name of a file and should not be translated.
+#: ../src/Addons.cpp:194
msgid "ERROR: Unable to create the installed_addons file."
msgstr ""
-#: ../src/Addons.cpp:197
+#: ../src/Addons.cpp:206
msgid "ERROR: Invalid file format of the installed_addons!"
msgstr ""
-#: ../src/Addons.cpp:419 ../src/Addons.cpp:436 ../src/Addons.cpp:453
-#: ../src/Addons.cpp:480 ../src/Addons.cpp:497 ../src/Addons.cpp:511
-msgid "ERROR: Unable to download addon!"
+# TRANSLATORS: indicates the author of an addon.
+#: ../src/Addons.cpp:325 ../src/Addons.cpp:456
+#, c-format
+msgid "by %s"
msgstr ""
-#: ../src/Addons.cpp:419 ../src/Addons.cpp:436 ../src/Addons.cpp:453
-#: ../src/Addons.cpp:480 ../src/Addons.cpp:497 ../src/Addons.cpp:511
-msgid "ERROR:"
+#: ../src/Addons.cpp:333
+msgid "Installed"
+msgstr ""
+
+#: ../src/Addons.cpp:338
+msgid "Updatable"
+msgstr ""
+
+#: ../src/Addons.cpp:345
+msgid "Not installed"
+msgstr ""
+
+#: ../src/Addons.cpp:473 ../src/Addons.cpp:498
+msgid "Remove"
+msgstr ""
+
+#: ../src/Addons.cpp:487
+msgid "Update"
+msgstr ""
+
+#: ../src/Addons.cpp:493
+msgid "Install"
+msgstr ""
+
+#: ../src/Addons.cpp:566 ../src/Addons.cpp:581 ../src/Addons.cpp:596
+#: ../src/Addons.cpp:615 ../src/Addons.cpp:630 ../src/Addons.cpp:642
+msgid "ERROR: Unable to download addon!"
msgstr ""
-#: ../src/Addons.cpp:609
-msgid "Uninstall"
+#: ../src/Addons.cpp:566 ../src/Addons.cpp:581 ../src/Addons.cpp:596
+#: ../src/Addons.cpp:615 ../src/Addons.cpp:630 ../src/Addons.cpp:642
+msgid "ERROR:"
msgstr ""
-#: ../src/Block.cpp:606 ../src/LevelEditor.cpp:172
+#: ../src/Block.cpp:638 ../src/LevelEditor.cpp:174
msgid "On"
msgstr ""
-#: ../src/Block.cpp:607 ../src/LevelEditor.cpp:173
+#: ../src/Block.cpp:639 ../src/LevelEditor.cpp:175
msgid "Off"
msgstr ""
# TRANSLATORS: Font used in GUI:
# - Use "knewave" for languages using Latin and Latin-derived alphabets
# - "DroidSansFallback" can be used for non-Latin writing systems
-#: ../src/Functions.cpp:634 ../src/Functions.cpp:635 ../src/Functions.cpp:636
-#: ../src/Functions.cpp:652
+#: ../src/Functions.cpp:699 ../src/Functions.cpp:700 ../src/Functions.cpp:701
+#: ../src/Functions.cpp:717
msgid "knewave"
msgstr ""
# TRANSLATORS: Font used for normal text:
# - Use "Blokletters-Viltstift" for languages using Latin and Latin-derived alphabets
# - "DroidSansFallback" can be used for non-Latin writing systems
-#: ../src/Functions.cpp:640
+#: ../src/Functions.cpp:705
msgid "Blokletters-Viltstift"
msgstr ""
-#: ../src/Functions.cpp:754
+#: ../src/Functions.cpp:819
msgid "Loading..."
msgstr ""
-#: ../src/Functions.cpp:1330 ../src/Functions.cpp:1357
-#: ../src/Functions.cpp:1695 ../src/InputManager.cpp:233
-#: ../src/LevelEditor.cpp:353 ../src/LevelEditor.cpp:458
-#: ../src/LevelEditor.cpp:514 ../src/LevelEditor.cpp:1777
-#: ../src/LevelEditSelect.cpp:232 ../src/LevelEditSelect.cpp:264
-#: ../src/LevelEditSelect.cpp:300
+#: ../src/Functions.cpp:1398 ../src/Functions.cpp:1425
+#: ../src/Functions.cpp:1763 ../src/LevelEditSelect.cpp:232
+#: ../src/LevelEditSelect.cpp:264 ../src/LevelEditSelect.cpp:300
+#: ../src/LevelEditor.cpp:387 ../src/LevelEditor.cpp:493
+#: ../src/LevelEditor.cpp:550 ../src/LevelEditor.cpp:606
+#: ../src/LevelEditor.cpp:1913
msgid "OK"
msgstr ""
-#: ../src/Functions.cpp:1331 ../src/Functions.cpp:1343
-#: ../src/Functions.cpp:1353 ../src/Functions.cpp:1699
-#: ../src/LevelEditor.cpp:357 ../src/LevelEditor.cpp:462
-#: ../src/LevelEditor.cpp:518 ../src/LevelEditor.cpp:1781
+#: ../src/Functions.cpp:1399 ../src/Functions.cpp:1411
+#: ../src/Functions.cpp:1421 ../src/Functions.cpp:1767
#: ../src/LevelEditSelect.cpp:236 ../src/LevelEditSelect.cpp:268
-#: ../src/LevelEditSelect.cpp:304 ../src/TitleMenu.cpp:493
+#: ../src/LevelEditSelect.cpp:304 ../src/LevelEditor.cpp:391
+#: ../src/LevelEditor.cpp:497 ../src/LevelEditor.cpp:554
+#: ../src/LevelEditor.cpp:610 ../src/LevelEditor.cpp:1917
+#: ../src/TitleMenu.cpp:516
msgid "Cancel"
msgstr ""
-#: ../src/Functions.cpp:1335
+#: ../src/Functions.cpp:1403
msgid "Abort"
msgstr ""
-#: ../src/Functions.cpp:1336 ../src/Functions.cpp:1352
+#: ../src/Functions.cpp:1404 ../src/Functions.cpp:1420
msgid "Retry"
msgstr ""
-#: ../src/Functions.cpp:1337
+#: ../src/Functions.cpp:1405
msgid "Ignore"
msgstr ""
-#: ../src/Functions.cpp:1341 ../src/Functions.cpp:1347
+#: ../src/Functions.cpp:1409 ../src/Functions.cpp:1415
msgid "Yes"
msgstr ""
-#: ../src/Functions.cpp:1342 ../src/Functions.cpp:1348
+#: ../src/Functions.cpp:1410 ../src/Functions.cpp:1416
msgid "No"
msgstr ""
# TRANSLATORS: Filename is coming before this text
-#: ../src/Functions.cpp:1466
+#: ../src/Functions.cpp:1534
#, c-format
msgid ""
"%s already exists.\n"
"Do you want to overwrite it?"
msgstr ""
-#: ../src/Functions.cpp:1466
+#: ../src/Functions.cpp:1534
msgid "Overwrite Prompt"
msgstr ""
-#: ../src/Functions.cpp:1487 ../src/Functions.cpp:1505
+#: ../src/Functions.cpp:1555 ../src/Functions.cpp:1573
#, c-format
msgid "Can't open file %s."
msgstr ""
-#: ../src/Functions.cpp:1487 ../src/Functions.cpp:1505
+#: ../src/Functions.cpp:1555 ../src/Functions.cpp:1573
msgid "Error"
msgstr ""
-#: ../src/Functions.cpp:1636
+#: ../src/Functions.cpp:1704
msgid "Save File"
msgstr ""
-#: ../src/Functions.cpp:1636
+#: ../src/Functions.cpp:1704
msgid "Load File"
msgstr ""
-#: ../src/Functions.cpp:1640
+#: ../src/Functions.cpp:1708
msgid "Search In"
msgstr ""
-#: ../src/Functions.cpp:1650
+#: ../src/Functions.cpp:1718
msgid "File Name"
msgstr ""
# TRANSLATORS: Please do not remove %s or %d from your translation:
# - %d means the level number in a levelpack
# - %s means the name of current level
-#: ../src/Game.cpp:266 ../src/Game.cpp:951
+#: ../src/Game.cpp:277 ../src/Game.cpp:1021
#, c-format
msgid "Level %d %s"
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with current action key
-#: ../src/Game.cpp:771
+#: ../src/Game.cpp:841
#, c-format
msgid "Press %s key to save the game."
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with current action key
-#: ../src/Game.cpp:776
+#: ../src/Game.cpp:846
#, c-format
msgid "Press %s key to swap the position of player and shadow."
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with current action key
-#: ../src/Game.cpp:781
+#: ../src/Game.cpp:851
#, c-format
msgid "Press %s key to activate the switch."
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with current action key
-#: ../src/Game.cpp:786
+#: ../src/Game.cpp:856
#, c-format
msgid "Press %s key to teleport."
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - first %s means currently configured key to restart game
# - Second %s means configured key to load from last save
-#: ../src/Game.cpp:827
+#: ../src/Game.cpp:897
#, c-format
msgid "Press %s to restart current level or press %s to load the game."
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with currently configured key to restart game
-#: ../src/Game.cpp:839
+#: ../src/Game.cpp:909
#, c-format
msgid "Press %s to restart current level."
msgstr ""
-#: ../src/Game.cpp:853
+#: ../src/Game.cpp:923
msgid "Your shadow has died."
msgstr ""
-#: ../src/Game.cpp:904
+#: ../src/Game.cpp:974
#, c-format
msgid "%d recordings"
msgstr ""
# TRANSLATORS: This is caption for finished level
-#: ../src/Game.cpp:943
+#: ../src/Game.cpp:1013
msgid "You've finished:"
msgstr ""
# TRANSLATORS: Please do not remove %-.2f from your translation:
# - %-.2f means time in seconds
# - s is shortened form of a second. Try to keep it so.
-#: ../src/Game.cpp:1129
+#: ../src/Game.cpp:1199
#, c-format
msgid "Time: %-.2fs"
msgstr ""
# TRANSLATORS: Please do not remove %-.2f from your translation:
# - %-.2f means time in seconds
# - s is shortened form of a second. Try to keep it so.
-#: ../src/Game.cpp:1138
+#: ../src/Game.cpp:1208
#, c-format
msgid "Best time: %-.2fs"
msgstr ""
-#: ../src/Game.cpp:1149
+#: ../src/Game.cpp:1219
#, c-format
msgid "Target time: %-.2fs"
msgstr ""
# TRANSLATORS: Please do not remove %d from your translation:
# - %d means the number of recordings user has made
-#: ../src/Game.cpp:1170
+#: ../src/Game.cpp:1240
#, c-format
msgid "Recordings: %d"
msgstr ""
# TRANSLATORS: Please do not remove %d from your translation:
# - %d means the number of recordings user has made
-#: ../src/Game.cpp:1178
+#: ../src/Game.cpp:1248
#, c-format
msgid "Best recordings: %d"
msgstr ""
-#: ../src/Game.cpp:1188
+#: ../src/Game.cpp:1258
#, c-format
msgid "Target recordings: %d"
msgstr ""
# TRANSLATORS: Please do not remove %s from your translation:
# - %s will be replaced with name of a prize medal (gold, silver or bronze)
-#: ../src/Game.cpp:1201
+#: ../src/Game.cpp:1271
#, c-format
msgid "You earned the %s medal"
msgstr ""
-#: ../src/Game.cpp:1201
+#: ../src/Game.cpp:1271
msgid "GOLD"
msgstr ""
-#: ../src/Game.cpp:1201
+#: ../src/Game.cpp:1271
msgid "SILVER"
msgstr ""
-#: ../src/Game.cpp:1201
+#: ../src/Game.cpp:1271
msgid "BRONZE"
msgstr ""
# TRANSLATORS: used as return to the level selector menu
-#: ../src/Game.cpp:1216
+#: ../src/Game.cpp:1286
msgid "Menu"
msgstr ""
# TRANSLATORS: used as restart level
-#: ../src/Game.cpp:1223 ../src/InputManager.cpp:44
+#: ../src/Game.cpp:1293 ../src/InputManager.cpp:44
msgid "Restart"
msgstr ""
# TRANSLATORS: used as next level
-#: ../src/Game.cpp:1230
+#: ../src/Game.cpp:1300
msgid "Next"
msgstr ""
-#: ../src/Game.cpp:1288
+#: ../src/Game.cpp:1363
msgid "Game replay is done."
msgstr ""
-#: ../src/Game.cpp:1288
+#: ../src/Game.cpp:1363
msgid "Game Replay"
msgstr ""
-#: ../src/Game.cpp:1512 ../src/Game.cpp:1514
+#: ../src/Game.cpp:1627 ../src/Game.cpp:1629
msgid "Congratulations"
msgstr ""
-#: ../src/Game.cpp:1514
+#: ../src/Game.cpp:1629
msgid "You have finished the levelpack!"
msgstr ""
#: ../src/InputManager.cpp:43
msgid "Up (in menu)"
msgstr ""
#: ../src/InputManager.cpp:43
msgid "Down (in menu)"
msgstr ""
#: ../src/InputManager.cpp:43
msgid "Left"
msgstr ""
#: ../src/InputManager.cpp:43
msgid "Right"
msgstr ""
#: ../src/InputManager.cpp:43
msgid "Jump"
msgstr ""
#: ../src/InputManager.cpp:43
msgid "Action"
msgstr ""
#: ../src/InputManager.cpp:43
msgid "Space (Record)"
msgstr ""
#: ../src/InputManager.cpp:43
msgid "Cancel recording"
msgstr ""
#: ../src/InputManager.cpp:44
msgid "Escape"
msgstr ""
#: ../src/InputManager.cpp:44
msgid "Tab (View shadow/Level prop.)"
msgstr ""
#: ../src/InputManager.cpp:44
msgid "Save game (in editor)"
msgstr ""
#: ../src/InputManager.cpp:44
msgid "Load game"
msgstr ""
#: ../src/InputManager.cpp:44
msgid "Swap (in editor)"
msgstr ""
#: ../src/InputManager.cpp:45
msgid "Teleport (in editor)"
msgstr ""
#: ../src/InputManager.cpp:45
msgid "Suicide (in editor)"
msgstr ""
#: ../src/InputManager.cpp:45
msgid "Shift (in editor)"
msgstr ""
#: ../src/InputManager.cpp:45
msgid "Next block type (in Editor)"
msgstr ""
#: ../src/InputManager.cpp:46
msgid "Previous block type (in editor)"
msgstr ""
#: ../src/InputManager.cpp:46
msgid "Select (in menu)"
msgstr ""
-#: ../src/InputManager.cpp:204 ../src/TitleMenu.cpp:472
-msgid "Config Keys"
+#: ../src/InputManager.cpp:71 ../src/InputManager.cpp:96
+msgid "OR"
msgstr ""
-#: ../src/InputManager.cpp:207
-msgid "Select an item and press a key to config it."
+#: ../src/InputManager.cpp:227
+msgid "Select an item and press a key to change it."
msgstr ""
-#: ../src/InputManager.cpp:221
-msgid "Primary key"
+#: ../src/InputManager.cpp:230
+msgid "Press backspace to clear the selected item."
msgstr ""
-#: ../src/InputManager.cpp:222
-msgid "Alternative key"
+#: ../src/LevelEditSelect.cpp:46 ../src/TitleMenu.cpp:52
+msgid "Map Editor"
msgstr ""
-#: ../src/InputManager.cpp:228
-msgid "Unset the key"
+#: ../src/LevelEditSelect.cpp:83
+msgid "New Levelpack"
msgstr ""
-#: ../src/InputManager.cpp:299
-msgid "(Not set)"
+#: ../src/LevelEditSelect.cpp:88
+msgid "Pack Properties"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:93
+msgid "Remove Pack"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:98
+msgid "Move Map"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:106
+msgid "Remove Map"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:111
+msgid "Edit Map"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:202
+msgid "Properties"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:205 ../src/LevelEditor.cpp:1868
+msgid "Name:"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:214
+msgid "Description:"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:223
+msgid "Congratulation text:"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:252 ../src/LevelEditSelect.cpp:407
+msgid "Add level"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:255
+msgid "File name:"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:280
+msgid "Move level"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:283
+msgid "Level: "
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:293
+msgid "Before"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:294
+msgid "After"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:295 ../src/LevelEditor.cpp:66
+msgid "Swap"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:470
+msgid "Are you sure?"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:470
+msgid "Remove prompt"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:615
+msgid "No file name given for the new level."
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:615
+msgid "Missing file name"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:672
+msgid "ERROR: Unable to add level to Levels levelpack"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:706
+msgid "The entered level number isn't valid!"
+msgstr ""
+
+#: ../src/LevelEditSelect.cpp:706
+msgid "Illegal number"
msgstr ""
#: ../src/LevelEditor.cpp:64
msgid "Block"
msgstr ""
#: ../src/LevelEditor.cpp:64
msgid "Player Start"
msgstr ""
#: ../src/LevelEditor.cpp:64
msgid "Shadow Start"
msgstr ""
#: ../src/LevelEditor.cpp:65
msgid "Exit"
msgstr ""
#: ../src/LevelEditor.cpp:65
msgid "Shadow Block"
msgstr ""
#: ../src/LevelEditor.cpp:65
msgid "Spikes"
msgstr ""
#: ../src/LevelEditor.cpp:66
msgid "Checkpoint"
msgstr ""
-#: ../src/LevelEditor.cpp:66 ../src/LevelEditSelect.cpp:295
-msgid "Swap"
-msgstr ""
-
#: ../src/LevelEditor.cpp:66
msgid "Fragile"
msgstr ""
#: ../src/LevelEditor.cpp:67
msgid "Moving Block"
msgstr ""
#: ../src/LevelEditor.cpp:67
msgid "Moving Shadow Block"
msgstr ""
#: ../src/LevelEditor.cpp:67
msgid "Moving Spikes"
msgstr ""
#: ../src/LevelEditor.cpp:68
msgid "Teleporter"
msgstr ""
#: ../src/LevelEditor.cpp:68
msgid "Button"
msgstr ""
#: ../src/LevelEditor.cpp:68
msgid "Switch"
msgstr ""
#: ../src/LevelEditor.cpp:69
msgid "Conveyor Belt"
msgstr ""
#: ../src/LevelEditor.cpp:69
msgid "Shadow Conveyor Belt"
msgstr ""
#: ../src/LevelEditor.cpp:69
msgid "Notification Block"
msgstr ""
#: ../src/LevelEditor.cpp:69
msgid "Collectable"
msgstr ""
#: ../src/LevelEditor.cpp:69
-msgid "Puhsable"
+msgid "Pushable"
msgstr ""
-#: ../src/LevelEditor.cpp:174
+#: ../src/LevelEditor.cpp:176
msgid "Toggle"
msgstr ""
-#: ../src/LevelEditor.cpp:177
+#: ../src/LevelEditor.cpp:179
msgid "Complete"
msgstr ""
-#: ../src/LevelEditor.cpp:178
+#: ../src/LevelEditor.cpp:180
msgid "One step"
msgstr ""
-#: ../src/LevelEditor.cpp:179
+#: ../src/LevelEditor.cpp:181
msgid "Two steps"
msgstr ""
-#: ../src/LevelEditor.cpp:180
+#: ../src/LevelEditor.cpp:182
msgid "Gone"
msgstr ""
-#: ../src/LevelEditor.cpp:190
-msgid "Move"
+#: ../src/LevelEditor.cpp:216
+msgid "Deselect"
msgstr ""
-#: ../src/LevelEditor.cpp:191 ../src/LevelEditor.cpp:744
-#: ../src/LevelEditor.cpp:2843
+#: ../src/LevelEditor.cpp:218 ../src/LevelEditor.cpp:821
+#: ../src/LevelEditor.cpp:3029
+msgid "Select"
+msgstr ""
+
+#: ../src/LevelEditor.cpp:219 ../src/LevelEditor.cpp:836
+#: ../src/LevelEditor.cpp:3035
msgid "Delete"
msgstr ""
-#: ../src/LevelEditor.cpp:196
+#: ../src/LevelEditor.cpp:224
msgid "Link"
msgstr ""
-#: ../src/LevelEditor.cpp:197
+#: ../src/LevelEditor.cpp:225
msgid "Remove Links"
msgstr ""
-#: ../src/LevelEditor.cpp:201 ../src/LevelEditor.cpp:400
+#: ../src/LevelEditor.cpp:229 ../src/LevelEditor.cpp:434
msgid "Automatic"
msgstr ""
-#: ../src/LevelEditor.cpp:214
+#: ../src/LevelEditor.cpp:242
msgid "Path"
msgstr ""
-#: ../src/LevelEditor.cpp:215
+#: ../src/LevelEditor.cpp:243
msgid "Remove Path"
msgstr ""
-#: ../src/LevelEditor.cpp:218 ../src/LevelEditor.cpp:224
-#: ../src/LevelEditor.cpp:378
+#: ../src/LevelEditor.cpp:246 ../src/LevelEditor.cpp:252
+#: ../src/LevelEditor.cpp:412
msgid "Enabled"
msgstr ""
-#: ../src/LevelEditor.cpp:219 ../src/LevelEditor.cpp:389
+#: ../src/LevelEditor.cpp:247 ../src/LevelEditor.cpp:423
msgid "Looping"
msgstr ""
-#: ../src/LevelEditor.cpp:225
+#: ../src/LevelEditor.cpp:253
msgid "Speed"
msgstr ""
-#: ../src/LevelEditor.cpp:235
+#: ../src/LevelEditor.cpp:263
msgid "Message"
msgstr ""
-#: ../src/LevelEditor.cpp:237 ../src/LevelEditor.cpp:476
+#: ../src/LevelEditor.cpp:265 ../src/LevelEditor.cpp:270
+#: ../src/LevelEditor.cpp:511
msgid "Scripting"
msgstr ""
-#: ../src/LevelEditor.cpp:335
+#: ../src/LevelEditor.cpp:269 ../src/TitleMenu.cpp:235
+msgid "Settings"
+msgstr ""
+
+#: ../src/LevelEditor.cpp:369
msgid "Notification block"
msgstr ""
-#: ../src/LevelEditor.cpp:340
+#: ../src/LevelEditor.cpp:374
msgid "Enter message here:"
msgstr ""
-#: ../src/LevelEditor.cpp:445
+#: ../src/LevelEditor.cpp:479
msgid "Conveyor belt speed"
msgstr ""
-#: ../src/LevelEditor.cpp:450
+#: ../src/LevelEditor.cpp:484
msgid "Enter speed here:"
msgstr ""
-#: ../src/LevelEditor.cpp:481
+#: ../src/LevelEditor.cpp:516
msgid "Id:"
msgstr ""
-#: ../src/LevelEditor.cpp:729 ../src/LevelEditor.cpp:2837
-msgid "Select"
+#: ../src/LevelEditor.cpp:575
+msgid "Level Scripting"
msgstr ""
-#: ../src/LevelEditor.cpp:759
+#: ../src/LevelEditor.cpp:851
msgid "Configure"
msgstr ""
-#: ../src/LevelEditor.cpp:1159
+#: ../src/LevelEditor.cpp:1277
msgid "Are you sure you want to quit?"
msgstr ""
-#: ../src/LevelEditor.cpp:1159
+#: ../src/LevelEditor.cpp:1277
msgid "Quit prompt"
msgstr ""
-#: ../src/LevelEditor.cpp:1266 ../src/LevelEditor.cpp:1268
-#: ../src/LevelEditor.cpp:1727 ../src/LevelEditor.cpp:1729
+#: ../src/LevelEditor.cpp:1384 ../src/LevelEditor.cpp:1386
+#: ../src/LevelEditor.cpp:1853 ../src/LevelEditor.cpp:1855
#, c-format
msgid "Level \"%s\" saved"
msgstr ""
-#: ../src/LevelEditor.cpp:1266 ../src/LevelEditor.cpp:1268
-#: ../src/LevelEditor.cpp:1727 ../src/LevelEditor.cpp:1729
+#: ../src/LevelEditor.cpp:1384 ../src/LevelEditor.cpp:1386
+#: ../src/LevelEditor.cpp:1853 ../src/LevelEditor.cpp:1855
msgid "Saved"
msgstr ""
-#: ../src/LevelEditor.cpp:1736 ../src/LevelEditor.cpp:2849
+#: ../src/LevelEditor.cpp:1862 ../src/LevelEditor.cpp:3041
msgid "Level settings"
msgstr ""
-#: ../src/LevelEditor.cpp:1742 ../src/LevelEditSelect.cpp:205
-msgid "Name:"
-msgstr ""
-
-#: ../src/LevelEditor.cpp:1748
+#: ../src/LevelEditor.cpp:1874
msgid "Theme:"
msgstr ""
-#: ../src/LevelEditor.cpp:1756
+#: ../src/LevelEditor.cpp:1882
msgid "Target time (s):"
msgstr ""
-#: ../src/LevelEditor.cpp:1765
+#: ../src/LevelEditor.cpp:1896
msgid "Target recordings:"
msgstr ""
-#: ../src/LevelEditor.cpp:2804 ../src/LevelEditor.cpp:2810
+#: ../src/LevelEditor.cpp:2996 ../src/LevelEditor.cpp:3002
#, c-format
msgid "Movespeed: %s"
msgstr ""
-#: ../src/LevelEditor.cpp:2840
+#: ../src/LevelEditor.cpp:3032
msgid "Add"
msgstr ""
-#: ../src/LevelEditor.cpp:2846 ../src/LevelPlaySelect.cpp:69
-#: ../src/TitleMenu.cpp:49
+#: ../src/LevelEditor.cpp:3038 ../src/LevelPlaySelect.cpp:75
+#: ../src/TitleMenu.cpp:50
msgid "Play"
msgstr ""
-#: ../src/LevelEditor.cpp:2852
+#: ../src/LevelEditor.cpp:3044
msgid "Save level"
msgstr ""
-#: ../src/LevelEditor.cpp:2855
+#: ../src/LevelEditor.cpp:3047
msgid "Back to menu"
msgstr ""
-#: ../src/LevelEditSelect.cpp:46 ../src/TitleMenu.cpp:51
-msgid "Map Editor"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:83
-msgid "New Levelpack"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:88
-msgid "Pack Properties"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:93
-msgid "Remove Pack"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:98
-msgid "Move Map"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:106
-msgid "Remove Map"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:111
-msgid "Edit Map"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:202
-msgid "Properties"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:214
-msgid "Description:"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:223
-msgid "Congratulation text:"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:252 ../src/LevelEditSelect.cpp:407
-msgid "Add level"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:255
-msgid "File name:"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:280
-msgid "Move level"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:283
-msgid "Level: "
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:293
-msgid "Before"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:294
-msgid "After"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:470
-msgid "Are you sure?"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:470
-msgid "Remove prompt"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:615
-msgid "No file name given for the new level."
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:615
-msgid "Missing file name"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:672
-msgid "ERROR: Unable to add level to Levels levelpack"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:706
-msgid "The entered level number isn't valid!"
-msgstr ""
-
-#: ../src/LevelEditSelect.cpp:706
-msgid "Illegal number"
-msgstr ""
-
#: ../src/LevelPlaySelect.cpp:47
msgid "Select Level"
msgstr ""
-#: ../src/LevelPlaySelect.cpp:96
+#: ../src/LevelPlaySelect.cpp:102
msgid "Choose a level"
msgstr ""
-#: ../src/LevelPlaySelect.cpp:97
+#: ../src/LevelPlaySelect.cpp:103
msgid "Time:"
msgstr ""
-#: ../src/LevelPlaySelect.cpp:98 ../src/StatisticsScreen.cpp:166
+#: ../src/LevelPlaySelect.cpp:104 ../src/StatisticsScreen.cpp:166
msgid "Recordings:"
msgstr ""
-#: ../src/StatisticsManager.cpp:393
+#: ../src/StatisticsManager.cpp:398
msgid "New achievement:"
msgstr ""
-#: ../src/StatisticsManager.cpp:401
+#: ../src/StatisticsManager.cpp:406
#, c-format
-msgid "Achieved at %s"
+msgid "Achieved on %s"
msgstr ""
-#: ../src/StatisticsManager.cpp:407
+#: ../src/StatisticsManager.cpp:412
msgid "Unknown achievement"
msgstr ""
-#: ../src/StatisticsManager.cpp:413
+#: ../src/StatisticsManager.cpp:418
#, c-format
-msgid "Achieved %0.1f%%"
+msgid "Achieved %1.0f%%"
msgstr ""
-#: ../src/StatisticsManager.cpp:417
+#: ../src/StatisticsManager.cpp:422
msgid "Not achieved"
msgstr ""
-#: ../src/StatisticsScreen.cpp:135 ../src/TitleMenu.cpp:203
+#: ../src/StatisticsScreen.cpp:135 ../src/TitleMenu.cpp:204
msgid "Achievements and Statistics"
msgstr ""
#: ../src/StatisticsScreen.cpp:147
msgid "Total"
msgstr ""
#: ../src/StatisticsScreen.cpp:156
msgid "Traveling distance (m)"
msgstr ""
#: ../src/StatisticsScreen.cpp:157
msgid "Jump times"
msgstr ""
#: ../src/StatisticsScreen.cpp:158
msgid "Die times"
msgstr ""
#: ../src/StatisticsScreen.cpp:159
msgid "Squashed times"
msgstr ""
#: ../src/StatisticsScreen.cpp:166
msgid "Switch pulled times:"
msgstr ""
#: ../src/StatisticsScreen.cpp:167
msgid "Swap times:"
msgstr ""
#: ../src/StatisticsScreen.cpp:168
msgid "Save times:"
msgstr ""
#: ../src/StatisticsScreen.cpp:168
msgid "Load times:"
msgstr ""
#: ../src/StatisticsScreen.cpp:175
msgid "Completed levels:"
msgstr ""
#: ../src/StatisticsScreen.cpp:217
msgid "In-game time:"
msgstr ""
#: ../src/StatisticsScreen.cpp:230
msgid "Level editing time:"
msgstr ""
#: ../src/StatisticsScreen.cpp:242
msgid "Created levels:"
msgstr ""
-#: ../src/StatisticsScreen.cpp:330
+#: ../src/StatisticsScreen.cpp:283
msgid "Achievements"
msgstr ""
-#: ../src/StatisticsScreen.cpp:331
+#: ../src/StatisticsScreen.cpp:284
msgid "Statistics"
msgstr ""
-#: ../src/TitleMenu.cpp:50
+#: ../src/TitleMenu.cpp:51
msgid "Options"
msgstr ""
-#: ../src/TitleMenu.cpp:53
+#: ../src/TitleMenu.cpp:54
msgid "Quit"
msgstr ""
-#: ../src/TitleMenu.cpp:129
+#: ../src/TitleMenu.cpp:130
msgid "Enable internet in order to install addons."
msgstr ""
-#: ../src/TitleMenu.cpp:129
+#: ../src/TitleMenu.cpp:130
msgid "Internet disabled"
msgstr ""
-#: ../src/TitleMenu.cpp:201 ../src/TitleMenu.cpp:709
+#: ../src/TitleMenu.cpp:202 ../src/TitleMenu.cpp:767
msgid "Credits"
msgstr ""
-#: ../src/TitleMenu.cpp:234
-msgid "Settings"
+#: ../src/TitleMenu.cpp:289
+msgid "General"
msgstr ""
-#: ../src/TitleMenu.cpp:278
-msgid "Music"
+#: ../src/TitleMenu.cpp:290
+msgid "Controls"
msgstr ""
-#: ../src/TitleMenu.cpp:286
-msgid "Sound"
+#: ../src/TitleMenu.cpp:301
+msgid "Music"
msgstr ""
-#: ../src/TitleMenu.cpp:294
-msgid "Fullscreen"
+#: ../src/TitleMenu.cpp:309
+msgid "Sound"
msgstr ""
-#: ../src/TitleMenu.cpp:299
+#: ../src/TitleMenu.cpp:317
msgid "Resolution"
msgstr ""
-#: ../src/TitleMenu.cpp:381
+#: ../src/TitleMenu.cpp:399
msgid "Language"
msgstr ""
# TRANSLATORS: as detect user's language automatically
-#: ../src/TitleMenu.cpp:390
+#: ../src/TitleMenu.cpp:407
msgid "Auto-Detect"
msgstr ""
-#: ../src/TitleMenu.cpp:418
+#: ../src/TitleMenu.cpp:435
msgid "Theme"
msgstr ""
-#: ../src/TitleMenu.cpp:451
+#: ../src/TitleMenu.cpp:469
+msgid "Internet proxy"
+msgstr ""
+
+#: ../src/TitleMenu.cpp:478
+msgid "Fullscreen"
+msgstr ""
+
+#: ../src/TitleMenu.cpp:483
msgid "Level themes"
msgstr ""
-#: ../src/TitleMenu.cpp:456
+#: ../src/TitleMenu.cpp:488
msgid "Internet"
msgstr ""
-#: ../src/TitleMenu.cpp:462
-msgid "Internet proxy"
+#: ../src/TitleMenu.cpp:493
+msgid "Fade transition"
msgstr ""
-# TRANSLATORS: Used for button which clear any level progress like unlocked levels and highscores.
-#: ../src/TitleMenu.cpp:479
-msgid "Clear Progress"
+#: ../src/TitleMenu.cpp:498
+msgid "Quick record"
msgstr ""
-#: ../src/TitleMenu.cpp:498
+#: ../src/TitleMenu.cpp:521
msgid "Save Changes"
msgstr ""
-#: ../src/TitleMenu.cpp:610
+#: ../src/TitleMenu.cpp:702
msgid "Do you really want to reset level progress?"
msgstr ""
-#: ../src/TitleMenu.cpp:610
+#: ../src/TitleMenu.cpp:702
msgid "Warning"
msgstr ""
+# TRANSLATORS: Used for button which clear any level progress like unlocked levels and highscores.
+#: ../src/TitleMenu.cpp:746
+msgid "Clear Progress"
+msgstr ""
+
#: ../src/AchievementList.h:38
msgid "Newbie"
msgstr ""
#: ../src/AchievementList.h:38
msgid "Complete a level."
msgstr ""
#: ../src/AchievementList.h:39
msgid "Experienced player"
msgstr ""
#: ../src/AchievementList.h:39
msgid "Complete 50 levels."
msgstr ""
#: ../src/AchievementList.h:40
msgid "Good job!"
msgstr ""
#: ../src/AchievementList.h:40
msgid "Receive a gold medal."
msgstr ""
#: ../src/AchievementList.h:41
msgid "Expert"
msgstr ""
#: ../src/AchievementList.h:41
msgid "Earn 50 gold medal."
msgstr ""
#: ../src/AchievementList.h:43
msgid "Graduate"
msgstr ""
#: ../src/AchievementList.h:43
msgid "Complete the tutorial level pack."
msgstr ""
#: ../src/AchievementList.h:44
msgid "Outstanding graduate"
msgstr ""
#: ../src/AchievementList.h:44
msgid "Complete the tutorial level pack with gold for all levels."
msgstr ""
#: ../src/AchievementList.h:46
msgid "Hooked"
msgstr ""
#: ../src/AchievementList.h:46
msgid "Play Me and My Shadow for more than 2 hours."
msgstr ""
#: ../src/AchievementList.h:47
msgid "Loyal fan of Me and My Shadow"
msgstr ""
#: ../src/AchievementList.h:47
msgid "Play Me and My Shadow for more than 24 hours."
msgstr ""
#: ../src/AchievementList.h:49
msgid "Constructor"
msgstr ""
#: ../src/AchievementList.h:49
msgid "Use the level editor for more than 2 hours."
msgstr ""
#: ../src/AchievementList.h:50
msgid "The creator"
msgstr ""
#: ../src/AchievementList.h:50
msgid "Use the level editor for more than 24 hours."
msgstr ""
#: ../src/AchievementList.h:52
msgid "Look, cute level!"
msgstr ""
#: ../src/AchievementList.h:52
msgid "Create a level for the first time."
msgstr ""
#: ../src/AchievementList.h:53
msgid "The level museum"
msgstr ""
#: ../src/AchievementList.h:53
msgid "Create 50 levels."
msgstr ""
#: ../src/AchievementList.h:55
msgid "Frog"
msgstr ""
#: ../src/AchievementList.h:55
msgid "Jump 1000 times."
msgstr ""
#: ../src/AchievementList.h:57
msgid "Wanderer"
msgstr ""
#: ../src/AchievementList.h:57
msgid "Travel 100 meters."
msgstr ""
#: ../src/AchievementList.h:58
msgid "Runner"
msgstr ""
#: ../src/AchievementList.h:58
msgid "Travel 1 kilometer."
msgstr ""
#: ../src/AchievementList.h:59
msgid "Long distance runner"
msgstr ""
#: ../src/AchievementList.h:59
msgid "Travel 10 kilometers."
msgstr ""
#: ../src/AchievementList.h:60
msgid "Marathon runner"
msgstr ""
#: ../src/AchievementList.h:60
msgid "Travel 42,195 meters."
msgstr ""
#: ../src/AchievementList.h:62
msgid "Be careful!"
msgstr ""
#: ../src/AchievementList.h:62
msgid "Die for the first time."
msgstr ""
#: ../src/AchievementList.h:63
msgid "It doesn't matter..."
msgstr ""
#: ../src/AchievementList.h:63
msgid "Die 50 times."
msgstr ""
#: ../src/AchievementList.h:64
msgid "Expert of trial and error"
msgstr ""
#: ../src/AchievementList.h:64
msgid "Die 1000 times."
msgstr ""
#: ../src/AchievementList.h:66
msgid "Keep an eye for moving blocks!"
msgstr ""
#: ../src/AchievementList.h:66
msgid "Get squashed for the first time."
msgstr ""
#: ../src/AchievementList.h:67
msgid "Potato masher"
msgstr ""
#: ../src/AchievementList.h:67
msgid "Get squashed 50 times."
msgstr ""
#: ../src/AchievementList.h:69
msgid "Double kill"
msgstr ""
#: ../src/AchievementList.h:69
msgid "Get both the player and the shadow dead."
msgstr ""
#: ../src/AchievementList.h:71
msgid "Bad luck"
msgstr ""
#: ../src/AchievementList.h:71
msgid "Die 5 times in under 5 seconds."
msgstr ""
#: ../src/AchievementList.h:72
msgid "This level is too dangerous"
msgstr ""
#: ../src/AchievementList.h:72
msgid "Die 10 times in under 5 seconds."
msgstr ""
#: ../src/AchievementList.h:74
msgid "You forgot your friend"
msgstr ""
#: ../src/AchievementList.h:74
msgid "Finish the level with the player or the shadow dead."
msgstr ""
#: ../src/AchievementList.h:75
msgid "Just in time"
msgstr ""
#: ../src/AchievementList.h:75
msgid "Reach the exit with the player and the shadow simultaneously."
msgstr ""
#: ../src/AchievementList.h:77
msgid "Recorder"
msgstr ""
#: ../src/AchievementList.h:77
msgid "Record 100 times."
msgstr ""
#: ../src/AchievementList.h:78
msgid "Shadowmaster"
msgstr ""
#: ../src/AchievementList.h:78
msgid "Record 1000 times."
msgstr ""
#: ../src/AchievementList.h:80
msgid "Switch puller"
msgstr ""
#: ../src/AchievementList.h:80
msgid "Pull the switch 100 times."
msgstr ""
#: ../src/AchievementList.h:81
msgid "The switch is broken!"
msgstr ""
#: ../src/AchievementList.h:81
msgid "Pull the switch 1000 times."
msgstr ""
#: ../src/AchievementList.h:83
msgid "Swapper"
msgstr ""
#: ../src/AchievementList.h:83
msgid "Swap 100 times."
msgstr ""
#: ../src/AchievementList.h:84
msgid "Player to shadow to player to shadow..."
msgstr ""
#: ../src/AchievementList.h:84
msgid "Swap 1000 times."
msgstr ""
#: ../src/AchievementList.h:86
msgid "Play it save"
msgstr ""
#: ../src/AchievementList.h:86
msgid "Save 1000 times."
msgstr ""
#: ../src/AchievementList.h:87
msgid "This game is too hard"
msgstr ""
#: ../src/AchievementList.h:87
msgid "Load the game 1000 times."
msgstr ""
#: ../src/AchievementList.h:89
msgid "Panic save"
msgstr ""
#: ../src/AchievementList.h:89
msgid "Save twice in 1 second."
msgstr ""
#: ../src/AchievementList.h:90
msgid "Panic load"
msgstr ""
#: ../src/AchievementList.h:90
msgid "Load twice in 1 second."
msgstr ""
#: ../src/AchievementList.h:92
msgid "Bad saving position"
msgstr ""
#: ../src/AchievementList.h:92
msgid "Load the game and die within 1 second."
msgstr ""
#: ../src/AchievementList.h:93
msgid "This level is too hard"
msgstr ""
#: ../src/AchievementList.h:93
msgid "Load the same save and die 100 times."
msgstr ""
#: ../src/AchievementList.h:95
msgid "Quick swap"
msgstr ""
#: ../src/AchievementList.h:95
msgid "Swap twice in under a second."
msgstr ""
#: ../src/AchievementList.h:98
msgid "Horizontal confusion"
msgstr ""
#: ../src/AchievementList.h:98
msgid "Press left and right simultaneously."
msgstr ""
#: ../src/AchievementList.h:100
msgid "Programmer"
msgstr ""
#: ../src/AchievementList.h:100
msgid "Play the development version of Me and My Shadow."
msgstr ""
# TRANSLATORS: name of a key
msgid "backspace"
msgstr ""
# TRANSLATORS: name of a key
msgid "tab"
msgstr ""
# TRANSLATORS: name of a key
msgid "clear"
msgstr ""
# TRANSLATORS: name of a key
msgid "return"
msgstr ""
# TRANSLATORS: name of a key
msgid "pause"
msgstr ""
# TRANSLATORS: name of a key
msgid "escape"
msgstr ""
# TRANSLATORS: name of a key
msgid "space"
msgstr ""
# TRANSLATORS: name of a key
msgid "delete"
msgstr ""
# TRANSLATORS: name of a key
msgid "enter"
msgstr ""
# TRANSLATORS: name of a key
msgid "equals"
msgstr ""
# TRANSLATORS: name of a key
msgid "up"
msgstr ""
# TRANSLATORS: name of a key
msgid "down"
msgstr ""
# TRANSLATORS: name of a key
msgid "right"
msgstr ""
# TRANSLATORS: name of a key
msgid "left"
msgstr ""
# TRANSLATORS: name of a key
msgid "insert"
msgstr ""
# TRANSLATORS: name of a key
msgid "home"
msgstr ""
# TRANSLATORS: name of a key
msgid "end"
msgstr ""
# TRANSLATORS: name of a key
msgid "page up"
msgstr ""
# TRANSLATORS: name of a key
msgid "page down"
msgstr ""
# TRANSLATORS: name of a key
msgid "numlock"
msgstr ""
# TRANSLATORS: name of a key
msgid "caps lock"
msgstr ""
# TRANSLATORS: name of a key
msgid "scroll lock"
msgstr ""
# TRANSLATORS: name of a key
msgid "right shift"
msgstr ""
# TRANSLATORS: name of a key
msgid "left shift"
msgstr ""
# TRANSLATORS: name of a key
msgid "right ctrl"
msgstr ""
# TRANSLATORS: name of a key
msgid "left ctrl"
msgstr ""
# TRANSLATORS: name of a key
msgid "right alt"
msgstr ""
# TRANSLATORS: name of a key
msgid "left alt"
msgstr ""
# TRANSLATORS: name of a key
msgid "right meta"
msgstr ""
# TRANSLATORS: name of a key
msgid "left meta"
msgstr ""
# TRANSLATORS: name of a key
msgid "left super"
msgstr ""
# TRANSLATORS: name of a key
msgid "right super"
msgstr ""
# TRANSLATORS: name of a key
msgid "alt gr"
msgstr ""
# TRANSLATORS: name of a key
msgid "compose"
msgstr ""
# TRANSLATORS: name of a key
msgid "help"
msgstr ""
# TRANSLATORS: name of a key
msgid "print screen"
msgstr ""
# TRANSLATORS: name of a key
msgid "sys req"
msgstr ""
# TRANSLATORS: name of a key
msgid "break"
msgstr ""
# TRANSLATORS: name of a key
msgid "menu"
msgstr ""
# TRANSLATORS: name of a key
msgid "power"
msgstr ""
# TRANSLATORS: name of a key
msgid "euro"
msgstr ""
# TRANSLATORS: name of a key
msgid "undo"
msgstr ""
diff --git a/src/Addons.cpp b/src/Addons.cpp
index 4ec8ef4..c537b55 100644
--- a/src/Addons.cpp
+++ b/src/Addons.cpp
@@ -1,694 +1,696 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Addons.h"
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "Objects.h"
#include "GUIObject.h"
#include "GUIOverlay.h"
#include "GUIScrollBar.h"
#include "GUITextArea.h"
#include "GUIListBox.h"
#include "POASerializer.h"
#include "InputManager.h"
#include <string>
#include <sstream>
#include <iostream>
+#include "libs/tinyformat/tinyformat.h"
#include <SDL/SDL.h>
#ifdef __APPLE__
#include <SDL_ttf/SDL_ttf.h>
#else
#include <SDL/SDL_ttf.h>
#endif
using namespace std;
Addons::Addons(){
//Render the title.
title=TTF_RenderUTF8_Blended(fontTitle,_("Addons"),themeTextColor);
//Load placeholder addon icons and screenshot.
addonIcon[0]=loadImage(getDataPath()+"/gfx/addon1.png");
SDL_SetAlpha(addonIcon[0],0,0);
addonIcon[1]=loadImage(getDataPath()+"/gfx/addon2.png");
SDL_SetAlpha(addonIcon[1],0,0);
addonIcon[2]=loadImage(getDataPath()+"/gfx/addon3.png");
SDL_SetAlpha(addonIcon[2],0,0);
screenshot=loadImage(getDataPath()+"/gfx/screenshot.png");
FILE* addon=fopen((getUserPath(USER_CACHE)+"addons").c_str(),"wb");
addons=NULL;
selected=NULL;
//Clear the GUI if any.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Try to get(download) the addonsList.
if(getAddonsList(addon)==false) {
//It failed so we show the error message.
GUIObjectRoot=new GUIObject(0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
GUIObject* obj=new GUIObject(90,96,200,32,GUIObjectLabel,_("Unable to initialize addon menu:"));
obj->name="lbl";
GUIObjectRoot->addChild(obj);
obj=new GUIObject(120,130,200,32,GUIObjectLabel,error.c_str());
obj->name="lbl";
GUIObjectRoot->addChild(obj);
obj=new GUIObject(90,550,200,32,GUIObjectButton,_("Back"));
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
return;
}
//Now create the GUI.
createGUI();
}
Addons::~Addons(){
delete addons;
//Free the title surface.
SDL_FreeSurface(title);
//If the GUIObjectRoot exist delete it.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
void Addons::createGUI(){
//Downloaded the addons file now we can create the GUI.
GUIObjectRoot=new GUIObject(0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
//Create list of categories
GUISingleLineListBox *listTabs=new GUISingleLineListBox((SCREEN_WIDTH-360)/2,100,360,36);
listTabs->name="lstTabs";
listTabs->item.push_back(_("Levels"));
listTabs->item.push_back(_("Level Packs"));
listTabs->item.push_back(_("Themes"));
listTabs->value=0;
listTabs->eventCallback=this;
GUIObjectRoot->addChild(listTabs);
//Create the list for the addons.
//By default levels will be selected.
list=new GUIListBox(SCREEN_WIDTH*0.1,160,SCREEN_WIDTH*0.8,SCREEN_HEIGHT-210);
addonsToList("levels");
list->name="lstAddons";
list->clickEvents=true;
list->eventCallback=this;
list->value=-1;
GUIObjectRoot->addChild(list);
type="levels";
//The back button.
GUIObject* obj=new GUIObject(20,20,-1,32,GUIObjectButton,_("Back"));
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
}
bool Addons::getAddonsList(FILE* file){
//First we download the file.
if(downloadFile(getSettings()->getValue("addon_url"),file)==false){
//NOTE: We keep the console output English so we put the string literal here twice.
cerr<<"ERROR: unable to download addons file!"<<endl;
error=_("ERROR: unable to download addons file!");
return false;
}
fclose(file);
//Load the downloaded file.
ifstream addonFile;
addonFile.open((getUserPath(USER_CACHE)+"addons").c_str());
if(!addonFile.good()) {
//NOTE: We keep the console output English so we put the string literal here twice.
cerr<<"ERROR: unable to load addon_list file!"<<endl;
/// TRANSLATORS: addon_list is the name of a file and should not be translated.
error=_("ERROR: unable to load addon_list file!");
return false;
}
//Parse the addonsfile.
TreeStorageNode obj;
{
POASerializer objSerializer;
if(!objSerializer.readNode(addonFile,&obj,true)){
//NOTE: We keep the console output English so we put the string literal here twice.
cerr<<"ERROR: Invalid file format of addons file!"<<endl;
error=_("ERROR: Invalid file format of addons file!");
return false;
}
}
//Also load the installed_addons file.
ifstream iaddonFile;
iaddonFile.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
if(!iaddonFile) {
//The installed_addons file doesn't exist, so we create it.
ofstream iaddons;
iaddons.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
iaddons<<" "<<endl;
iaddons.close();
//Also load the installed_addons file.
iaddonFile.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
if(!iaddonFile) {
//NOTE: We keep the console output English so we put the string literal here twice.
cerr<<"ERROR: Unable to create the installed_addons file."<<endl;
/// TRANSLATORS: installed_addons is the name of a file and should not be translated.
error=_("ERROR: Unable to create the installed_addons file.");
return false;
}
}
//And parse the installed_addons file.
TreeStorageNode obj1;
{
POASerializer objSerializer;
if(!objSerializer.readNode(iaddonFile,&obj1,true)){
//NOTE: We keep the console output English so we put the string literal here twice.
cerr<<"ERROR: Invalid file format of the installed_addons!"<<endl;
error=_("ERROR: Invalid file format of the installed_addons!");
return false;
}
}
//Fill the vector.
addons = new std::vector<Addon>;
fillAddonList(*addons,obj,obj1);
//Close the files.
iaddonFile.close();
addonFile.close();
return true;
}
void Addons::fillAddonList(std::vector<Addons::Addon> &list, TreeStorageNode &addons, TreeStorageNode &installed_addons){
//Loop through the blocks of the addons file.
//These should contain the types levels, levelpacks, themes.
for(unsigned int i=0;i<addons.subNodes.size();i++){
TreeStorageNode* block=addons.subNodes[i];
if(block==NULL) continue;
string type;
type=block->name;
//Now loop the entries(subNodes) of the block.
for(unsigned int i=0;i<block->subNodes.size();i++){
TreeStorageNode* entry=block->subNodes[i];
if(entry==NULL) continue;
if(entry->name=="entry" && entry->value.size()==1){
//The entry is valid so create a new Addon.
Addon addon;
addon.icon=addon.screenshot=NULL;
addon.type=type;
addon.name=entry->value[0];
if(!entry->attributes["file"].empty())
addon.file=entry->attributes["file"][0];
if(!entry->attributes["folder"].empty())
addon.folder=entry->attributes["folder"][0];
if(!entry->attributes["author"].empty())
addon.author=entry->attributes["author"][0];
if(!entry->attributes["description"].empty())
addon.description=entry->attributes["description"][0];
if(entry->attributes["icon"].size()>1){
//There are (at least) two values, the url to the icon and its md5sum used for caching.
addon.icon=loadCachedImage(entry->attributes["icon"][0].c_str(),entry->attributes["icon"][1].c_str());
if(addon.icon)
SDL_SetAlpha(addon.icon,0,0);
}
if(entry->attributes["screenshot"].size()>1){
//There are (at least) two values, the url to the screenshot and its md5sum used for caching.
addon.screenshot=loadCachedImage(entry->attributes["screenshot"][0].c_str(),entry->attributes["screenshot"][1].c_str());
if(addon.screenshot)
SDL_SetAlpha(addon.screenshot,0,0);
}
if(!entry->attributes["version"].empty())
addon.version=atoi(entry->attributes["version"][0].c_str());
addon.upToDate=false;
addon.installed=false;
//Check if the addon is already installed.
for(unsigned int i=0;i<installed_addons.subNodes.size();i++){
TreeStorageNode* installed=installed_addons.subNodes[i];
if(installed==NULL) continue;
if(installed->name=="entry" && installed->value.size()==3){
if(addon.type.compare(installed->value[0])==0 && addon.name.compare(installed->value[1])==0) {
addon.installed=true;
addon.installedVersion=atoi(installed->value[2].c_str());
if(addon.installedVersion>=addon.version) {
addon.upToDate=true;
}
}
}
}
//Finally put him in the list.
list.push_back(addon);
}
}
}
}
void Addons::addonsToList(const std::string &type){
list->clearItems();
for(unsigned int i=0;i<addons->size();i++) {
//Check if the addon is from the right type.
if((*addons)[i].type==type) {
string entry = (*addons)[i].name + " by " + (*addons)[i].author;
if((*addons)[i].installed) {
if((*addons)[i].upToDate) {
entry += " *";
} else {
entry += " +";
}
}
SDL_Surface* surf=SDL_CreateRGBSurface(SDL_SWSURFACE,list->width,74,32,RMASK,GMASK,BMASK,AMASK);
//Check if there's an icon for the addon.
if((*addons)[i].icon){
applySurface(5,5,(*addons)[i].icon,surf,NULL);
}else{
if(type=="levels")
applySurface(5,5,addonIcon[0],surf,NULL);
else if(type=="levelpacks")
applySurface(5,5,addonIcon[1],surf,NULL);
else
applySurface(5,5,addonIcon[2],surf,NULL);
}
SDL_Color black={0,0,0,0};
SDL_Surface* nameSurf=TTF_RenderUTF8_Blended(fontGUI,(*addons)[i].name.c_str(),black);
SDL_SetAlpha(nameSurf,0,0xFF);
applySurface(74,-1,nameSurf,surf,NULL);
SDL_FreeSurface(nameSurf);
- string authorLine = "by " + (*addons)[i].author;
+ /// TRANSLATORS: indicates the author of an addon.
+ string authorLine = tfm::format(_("by %s"),(*addons)[i].author);
SDL_Surface* authorSurf=TTF_RenderUTF8_Blended(fontText,authorLine.c_str(),black);
SDL_SetAlpha(authorSurf,0,0xFF);
applySurface(74,43,authorSurf,surf,NULL);
SDL_FreeSurface(authorSurf);
if((*addons)[i].installed){
if((*addons)[i].upToDate){
SDL_Surface* infoSurf=TTF_RenderUTF8_Blended(fontText,_("Installed"),black);
SDL_SetAlpha(infoSurf,0,0xFF);
applySurface(surf->w-infoSurf->w-32,(surf->h-infoSurf->h)/2,infoSurf,surf,NULL);
SDL_FreeSurface(infoSurf);
}else{
SDL_Surface* infoSurf=TTF_RenderUTF8_Blended(fontText,_("Updatable"),black);
SDL_SetAlpha(infoSurf,0,0xFF);
applySurface(surf->w-infoSurf->w-32,(surf->h-infoSurf->h)/2,infoSurf,surf,NULL);
SDL_FreeSurface(infoSurf);
}
}else{
SDL_Color grey={127,127,127};
SDL_Surface* infoSurf=TTF_RenderUTF8_Blended(fontText,_("Not installed"),grey);
SDL_SetAlpha(infoSurf,0,0xFF);
applySurface(surf->w-infoSurf->w-32,(surf->h-infoSurf->h)/2,infoSurf,surf,NULL);
SDL_FreeSurface(infoSurf);
}
list->addItem(entry,surf);
}
}
}
bool Addons::saveInstalledAddons(){
if(!addons) return false;
//Open the file.
ofstream iaddons;
iaddons.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
if(!iaddons) return false;
//Loop all the levels.
TreeStorageNode installed;
for(unsigned int i=0;i<addons->size();i++) {
//Check if the level is installed or not.
if((*addons)[i].installed) {
TreeStorageNode *entry=new TreeStorageNode;
entry->name="entry";
entry->value.push_back((*addons)[i].type);
entry->value.push_back((*addons)[i].name);
char version[64];
sprintf(version,"%d",(*addons)[i].installedVersion);
entry->value.push_back(version);
installed.subNodes.push_back(entry);
}
}
//And write away the file.
POASerializer objSerializer;
objSerializer.writeNode(&installed,iaddons,true,true);
return true;
}
SDL_Surface* Addons::loadCachedImage(const char* url,const char* md5sum){
//Check if the image is cached.
string imageFile=getUserPath(USER_CACHE)+"images/"+md5sum;
if(fileExists(imageFile.c_str())){
//It is, so load the image.
return loadImage(imageFile);
}else{
//Download the image.
FILE* file=fopen(imageFile.c_str(),"wb");
//Downloading failed.
if(!downloadFile(url,file)){
cerr<<"ERROR: Unable to download image from "<<url<<endl;
fclose(file);
return NULL;
}
fclose(file);
//Load the image.
return loadImage(imageFile);
}
}
void Addons::handleEvents(){
//Check if we should quit.
if(event.type==SDL_QUIT){
//Save the installed addons before exiting.
saveInstalledAddons();
setNextState(STATE_EXIT);
}
//Check if escape is pressed, if so return to the main menu.
if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
setNextState(STATE_MENU);
}
}
void Addons::logic(){}
void Addons::render(){
//Draw background.
objThemes.getBackground(true)->draw(screen);
//Draw the title.
applySurface((SCREEN_WIDTH-title->w)/2,40-TITLE_FONT_RAISE,title,screen,NULL);
}
void Addons::resize(){
//Delete the gui (if any).
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Now create a new one.
createGUI();
}
void Addons::showAddon(){
//Make sure an addon is selected.
if(!selected)
return;
//Create a root object.
GUIObject* root=new GUIObject((SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-400)/2,600,400,GUIObjectFrame,selected->name.c_str());
//Create the 'by creator' label.
- GUIObject* obj=new GUIObject(0,50,600,50,GUIObjectLabel,("by "+selected->author).c_str(),0,true,true,GUIGravityCenter);
+ GUIObject* obj=new GUIObject(0,50,600,50,GUIObjectLabel,tfm::format(_("by %s"),selected->author).c_str(),0,true,true,GUIGravityCenter);
root->addChild(obj);
//Create the description text.
GUITextArea* description=new GUITextArea(10,100,370,200);
description->setString(selected->description.c_str());
description->editable=false;
description->resize();
root->addChild(description);
//Create the screenshot image.
obj=new GUIObject(390,100,200,150,GUIObjectImage);
obj->setImage(selected->screenshot?selected->screenshot:screenshot);
root->addChild(obj);
//Add buttons depending on the installed/update status.
if(selected->installed && !selected->upToDate){
GUIObject* bRemove=new GUIObject(root->width*0.97,350,-1,32,GUIObjectButton,_("Remove"),0,true,true,GUIGravityRight);
bRemove->name="cmdRemove";
bRemove->eventCallback=this;
root->addChild(bRemove);
//Create a back button.
GUIObject* bBack=new GUIObject(root->width*0.03,350,-1,32,GUIObjectButton,_("Back"),0,true,true,GUIGravityLeft);
bBack->name="cmdCloseOverlay";
bBack->eventCallback=this;
root->addChild(bBack);
//Update widget sizes.
root->render(0,0,false);
//Create a nicely centered button.
obj=new GUIObject((int)floor((bBack->left+bBack->width+bRemove->left-bRemove->width)*0.5),350,-1,32,GUIObjectButton,_("Update"),0,true,true,GUIGravityCenter);
obj->name="cmdUpdate";
obj->eventCallback=this;
root->addChild(obj);
}else{
if(!selected->installed){
obj=new GUIObject(root->width*0.9,350,-1,32,GUIObjectButton,_("Install"),0,true,true,GUIGravityRight);
obj->name="cmdInstall";
obj->eventCallback=this;
root->addChild(obj);
}else if(selected->upToDate){
obj=new GUIObject(root->width*0.9,350,-1,32,GUIObjectButton,_("Remove"),0,true,true,GUIGravityRight);
obj->name="cmdRemove";
obj->eventCallback=this;
root->addChild(obj);
}
//Create a back button.
obj=new GUIObject(root->width*0.1,350,-1,32,GUIObjectButton,_("Back"),0,true,true,GUIGravityLeft);
obj->name="cmdCloseOverlay";
obj->eventCallback=this;
root->addChild(obj);
}
new GUIOverlay(root);
}
void Addons::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
if(name=="lstTabs"){
if(obj->value==0){
addonsToList("levels");
type="levels";
}else if(obj->value==1){
addonsToList("levelpacks");
type="levelpacks";
}else{
addonsToList("themes");
type="themes";
}
list->value=0;
GUIEventCallback_OnEvent("lstAddons",list,GUIEventChange);
}else if(name=="lstAddons"){
//Check which type of event.
if(eventType==GUIEventChange){
//Get the addon struct that belongs to it.
Addon* addon=NULL;
if(!list->item.empty()) {
string entry = list->getItem(list->value);
for(unsigned int i=0;i<addons->size();i++) {
std::string prefix=(*addons)[i].name;
if(!entry.compare(0, prefix.size(), prefix)) {
addon=&(*addons)[i];
}
}
}
selected=addon;
list->value=-1;
}else if(eventType==GUIEventClick){
//Make sure an addon is selected.
if(selected){
showAddon();
}
}
}else if(name=="cmdBack"){
saveInstalledAddons();
setNextState(STATE_MENU);
}else if(name=="cmdCloseOverlay"){
//We can safely delete the GUIObjectRoot, since it's handled by the GUIOverlay.
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}else if(name=="cmdUpdate"){
//First remove the addon and then install it again.
if(type.compare("levels")==0) {
if(downloadFile(selected->file,(getUserPath(USER_DATA)+"/levels/"))!=false){
selected->upToDate=true;
selected->installedVersion=selected->version;
addonsToList("levels");
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
return;
}
}else if(type.compare("levelpacks")==0) {
if(!removeDirectory((getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/").c_str())){
cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/")<<"."<<endl;
return;
}
if(downloadFile(selected->file,(getUserPath(USER_CACHE)+"/tmp/"))!=false){
extractFile(getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath(USER_DATA)+"/levelpacks/"+selected->folder+"/");
selected->upToDate=true;
selected->installedVersion=selected->version;
addonsToList("levelpacks");
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
return;
}
}else if(type.compare("themes")==0) {
if(!removeDirectory((getUserPath(USER_DATA)+"themes/"+selected->folder+"/").c_str())){
cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"themes/"+selected->folder+"/")<<"."<<endl;
return;
}
if(downloadFile(selected->file,(getUserPath(USER_CACHE)+"/tmp/"))!=false){
extractFile((getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true)),(getUserPath(USER_DATA)+"/themes/"+selected->folder+"/"));
selected->upToDate=true;
selected->installedVersion=selected->version;
addonsToList("themes");
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
return;
}
}
}else if(name=="cmdInstall"){
//Download the addon.
if(type.compare("levels")==0) {
if(downloadFile(selected->file,getUserPath(USER_DATA)+"/levels/")!=false){
selected->upToDate=true;
selected->installed=true;
selected->installedVersion=selected->version;
addonsToList("levels");
//And add the level to the levels levelpack.
LevelPack* levelsPack=getLevelPackManager()->getLevelPack("Levels");
levelsPack->addLevel(getUserPath(USER_DATA)+"/levels/"+fileNameFromPath(selected->file));
levelsPack->setLocked(levelsPack->getLevelCount()-1);
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
return;
}
}else if(type.compare("levelpacks")==0) {
if(downloadFile(selected->file,getUserPath(USER_CACHE)+"/tmp/")!=false){
extractFile(getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath(USER_DATA)+"/levelpacks/"+selected->folder+"/");
selected->upToDate=true;
selected->installed=true;
selected->installedVersion=selected->version;
addonsToList("levelpacks");
//And add the levelpack to the levelpackManager.
getLevelPackManager()->loadLevelPack(getUserPath(USER_DATA)+"/levelpacks/"+selected->folder);
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
return;
}
}else if(type.compare("themes")==0) {
if(downloadFile(selected->file,getUserPath(USER_CACHE)+"/tmp/")!=false){
extractFile(getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath(USER_DATA)+"/themes/"+selected->folder+"/");
selected->upToDate=true;
selected->installed=true;
selected->installedVersion=selected->version;
addonsToList("themes");
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
return;
}
}
}else if(name=="cmdRemove"){
//Uninstall the addon.
if(type.compare("levels")==0) {
if(remove((getUserPath(USER_DATA)+"levels/"+fileNameFromPath(selected->file)).c_str())){
cerr<<"ERROR: Unable to remove the file "<<(getUserPath(USER_DATA) + "levels/" + fileNameFromPath(selected->file))<<"."<<endl;
return;
}
selected->upToDate=false;
selected->installed=false;
addonsToList("levels");
//And remove the level from the levels levelpack.
LevelPack* levelsPack=getLevelPackManager()->getLevelPack("Levels");
for(int i=0;i<levelsPack->getLevelCount();i++){
if(levelsPack->getLevelFile(i)==(getUserPath(USER_DATA)+"levels/"+fileNameFromPath(selected->file))){
//Remove the level and break out of the loop.
levelsPack->removeLevel(i);
break;
}
}
}else if(type.compare("levelpacks")==0) {
if(!removeDirectory((getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/").c_str())){
cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/")<<"."<<endl;
return;
}
selected->upToDate=false;
selected->installed=false;
addonsToList("levelpacks");
//And remove the levelpack from the levelpack manager.
getLevelPackManager()->removeLevelPack(selected->folder);
}else if(type.compare("themes")==0) {
if(!removeDirectory((getUserPath(USER_DATA)+"themes/"+selected->folder+"/").c_str())){
cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"themes/"+selected->folder+"/")<<"."<<endl;
return;
}
selected->upToDate=false;
selected->installed=false;
addonsToList("themes");
}
}
//NOTE: In case of install/remove/update we can delete the GUIObjectRoot, since it's managed by the GUIOverlay.
if(name=="cmdUpdate" || name=="cmdInstall" || name=="cmdRemove"){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
diff --git a/src/Functions.cpp b/src/Functions.cpp
index f4cf593..5ba276b 100644
--- a/src/Functions.cpp
+++ b/src/Functions.cpp
@@ -1,1773 +1,1780 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <math.h>
+#include <locale.h>
#include <algorithm>
#include <SDL/SDL.h>
#ifdef __APPLE__
#include <SDL_mixer/SDL_mixer.h>
#include <SDL_gfx/SDL_gfxPrimitives.h>
#include <SDL_gfx/SDL_rotozoom.h>
#else
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_gfxPrimitives.h>
#include <SDL/SDL_rotozoom.h>
#endif
#include <SDL/SDL_syswm.h>
#include <string>
#include "Globals.h"
#include "Functions.h"
#include "FileManager.h"
#include "Objects.h"
#include "Player.h"
#include "GameObjects.h"
#include "LevelPack.h"
#include "TitleMenu.h"
#include "LevelEditSelect.h"
#include "LevelEditor.h"
#include "Game.h"
#include "LevelPlaySelect.h"
#include "Addons.h"
#include "InputManager.h"
#include "ImageManager.h"
#include "MusicManager.h"
#include "LevelPackManager.h"
#include "ThemeManager.h"
#include "GUIListBox.h"
#include "GUIOverlay.h"
#include "StatisticsManager.h"
#include "StatisticsScreen.h"
#include "Cursors.h"
#include "ScriptAPI.h"
#include "libs/tinyformat/tinyformat.h"
#include "libs/tinygettext/tinygettext.hpp"
#include "libs/tinygettext/log.hpp"
#include "libs/findlocale/findlocale.h"
#ifdef HARDWARE_ACCELERATION
#include <GL/gl.h>
#include <GL/glu.h>
//fix some Windows header bug
#ifndef GL_BGR
#define GL_BGR GL_BGR_EXT
#endif
#ifndef GL_BGRA
#define GL_BGRA GL_BGRA_EXT
#endif
#endif
using namespace std;
#ifdef WIN32
#include <windows.h>
#include <shlobj.h>
#else
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#endif
//Workaround for the resizing below 800x600 for X systems.
#if defined(__linux__) && !defined(ANDROID)
#include<X11/Xlib.h>
#include<X11/Xutil.h>
#define __X11_INCLUDED__
#endif
//Initialise the imagemanager.
//The ImageManager is used to prevent loading images multiple times.
ImageManager imageManager;
//Initialise the musicManager.
//The MusicManager is used to prevent loading music files multiple times and for playing/fading music.
MusicManager musicManager;
//Initialise the levelPackManager.
//The LevelPackManager is used to prevent loading levelpacks multiple times and for the game to know which levelpacks there are.
LevelPackManager levelPackManager;
//The scriptExecutor used for executing scripts.
ScriptExecutor scriptExecutor;
//Map containing changed settings using command line arguments.
map<string,string> tmpSettings;
//Pointer to the settings object.
//It is used to load and save the settings file and change the settings.
Settings* settings=0;
#ifdef HARDWARE_ACCELERATION
GLuint screenTexture;
#endif
SDL_Surface* loadImage(string file){
//We use the imageManager to load the file.
return imageManager.loadImage(file);
}
void applySurface(int x,int y,SDL_Surface* source,SDL_Surface* dest,SDL_Rect* clip){
//The offset is needed to draw at the right location.
SDL_Rect offset;
offset.x=x;
offset.y=y;
//Let SDL do the drawing of the surface.
SDL_BlitSurface(source,clip,dest,&offset);
}
void drawRect(int x,int y,int w,int h,SDL_Surface* dest,Uint32 color){
//NOTE: We let SDL_gfx render it.
rectangleRGBA(dest,x,y,x+w,y+h,color >> 24,color >> 16,color >> 8,255);
}
//Draw a box with anti-aliased borders using SDL_gfx.
void drawGUIBox(int x,int y,int w,int h,SDL_Surface* dest,Uint32 color){
//Fill content's background color from function parameter
boxRGBA(dest,x+1,y+1,x+w-2,y+h-2,color >> 24,color >> 16,color >> 8,color >> 0);
//Draw first black borders around content and leave 1 pixel in every corner
lineRGBA(dest,x+1,y,x+w-2,y,0,0,0,255);
lineRGBA(dest,x+1,y+h-1,x+w-2,y+h-1,0,0,0,255);
lineRGBA(dest,x,y+1,x,y+h-2,0,0,0,255);
lineRGBA(dest,x+w-1,y+1,x+w-1,y+h-2,0,0,0,255);
//Fill the corners with transperent color to create anti-aliased borders
pixelRGBA(dest,x,y,0,0,0,160);
pixelRGBA(dest,x,y+h-1,0,0,0,160);
pixelRGBA(dest,x+w-1,y,0,0,0,160);
pixelRGBA(dest,x+w-1,y+h-1,0,0,0,160);
//Draw second lighter border around content
rectangleRGBA(dest,x+1,y+1,x+w-2,y+h-2,0,0,0,64);
//Create anti-aliasing in corners of second border
pixelRGBA(dest,x+1,y+1,0,0,0,50);
pixelRGBA(dest,x+1,y+h-2,0,0,0,50);
pixelRGBA(dest,x+w-2,y+1,0,0,0,50);
pixelRGBA(dest,x+w-2,y+h-2,0,0,0,50);
}
void drawLine(int x1,int y1,int x2,int y2,SDL_Surface* dest,Uint32 color){
//NOTE: We let SDL_gfx render it.
lineRGBA(dest,x1,y1,x2,y2,color >> 24,color >> 16,color >> 8,255);
}
void drawLineWithArrow(int x1,int y1,int x2,int y2,SDL_Surface* dest,Uint32 color,int spacing,int offset,int xsize,int ysize){
//Draw line first
drawLine(x1,y1,x2,y2,dest,color);
//calc delta and length
double dx=x2-x1;
double dy=y2-y1;
double length=sqrt(dx*dx+dy*dy);
if(length<0.001) return;
//calc the unit vector
dx/=length; dy/=length;
//Now draw arrows on it
for(double p=offset;p<length;p+=spacing){
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx-ysize*dy+0.5),int(y1+(p-xsize)*dy+ysize*dx+0.5),dest,color);
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx+ysize*dy+0.5),int(y1+(p-xsize)*dy-ysize*dx+0.5),dest,color);
}
}
bool createScreen(){
//Check if we are going fullscreen.
if(settings->getBoolValue("fullscreen"))
pickFullscreenResolution();
//Set the screen_width and height.
SCREEN_WIDTH=atoi(settings->getValue("width").c_str());
SCREEN_HEIGHT=atoi(settings->getValue("height").c_str());
//Update the camera.
camera.w=SCREEN_WIDTH;
camera.h=SCREEN_HEIGHT;
//Boolean if this is the first screen creation.
bool initial=true;
//Check if we should use gl or software rendering.
if(settings->getBoolValue("gl")){
#ifdef HARDWARE_ACCELERATION
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,16);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE,32);
SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,0);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
//Set the video mode.
Uint32 flags=SDL_HWSURFACE | SDL_OPENGL;
if(settings->getBoolValue("fullscreen"))
flags|=SDL_FULLSCREEN;
else if(settings->getBoolValue("resizable"))
flags|=SDL_RESIZABLE;
if(SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,flags)==NULL){
fprintf(stderr,"FATAL ERROR: SDL_SetVideoMode failed\n");
return false;
}
//Delete the old screen.
//Warning: only if previous mode is OpenGL mode.
//NOTE: The previous mode can't switch during runtime.
if(screen){
SDL_FreeSurface(screen);
screen=NULL;
//There was a screen so this isn't the initial screen creation.
initial=false;
}
//Create a screen
screen=SDL_CreateRGBSurface(SDL_HWSURFACE,SCREEN_WIDTH,SCREEN_HEIGHT,32,0x00FF0000,0x0000FF00,0x000000FF,0);
//Create a texture.
glDeleteTextures(1,&screenTexture);
glGenTextures(1,&screenTexture);
//And set up gl correctly.
glClearColor(0, 0, 0, 0);
glClearDepth(1.0f);
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 1, -1);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_TEXTURE_2D);
glLoadIdentity();
#else
//NOTE: Hardware accelerated rendering requested but compiled without.
cerr<<"FATAL ERROR: Unable to use hardware acceleration (compiled without)."<<endl;
return false;
#endif
}else{
//Set the flags.
Uint32 flags=SCREEN_FLAGS;
#if !defined(ANDROID)
flags |= SDL_DOUBLEBUF;
#endif
if(settings->getBoolValue("fullscreen"))
flags|=SDL_FULLSCREEN;
else if(settings->getBoolValue("resizable"))
flags|=SDL_RESIZABLE;
//Check if there already was a screen.
if(screen)
initial=false;
//Create the screen and check if there weren't any errors.
screen=SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,flags);
if(screen==NULL){
fprintf(stderr,"FATAL ERROR: SDL_SetVideoMode failed\n");
return false;
}
}
//Now configure the newly created window (if windowed).
if(settings->getBoolValue("fullscreen")==false)
configureWindow(initial);
//Create the temp surface, just a replica of the screen surface, free the previous one if any.
if(tempSurface)
SDL_FreeSurface(tempSurface);
tempSurface=SDL_CreateRGBSurface(SCREEN_FLAGS|SDL_SRCALPHA,
screen->w,screen->h,screen->format->BitsPerPixel,
screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0);
//Set the the window caption.
SDL_WM_SetCaption(("Me and My Shadow "+version).c_str(),NULL);
SDL_EnableUNICODE(1);
//Nothing went wrong so return true.
return true;
}
//Workaround for the resizing below 800x600 for Windows.
#ifdef WIN32
static WNDPROC m_OldWindowProc=NULL;
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
if(msg==WM_GETMINMAXINFO){
if(m_OldWindowProc){
CallWindowProc(m_OldWindowProc,hwnd,msg,wParam,lParam);
}else{
DefWindowProc(hwnd,msg,wParam,lParam);
}
RECT r={0,0,800,600};
AdjustWindowRect(&r,GetWindowLong(hwnd,GWL_STYLE),FALSE);
MINMAXINFO *info=(MINMAXINFO*)lParam;
info->ptMinTrackSize.x=r.right-r.left;
info->ptMinTrackSize.y=r.bottom-r.top;
return 0;
}else{
if(m_OldWindowProc){
return CallWindowProc(m_OldWindowProc,hwnd,msg,wParam,lParam);
}else{
return DefWindowProc(hwnd,msg,wParam,lParam);
}
}
}
#endif
void pickFullscreenResolution(){
//Vector that will hold the resolutions to choose from.
vector<_res> resolutionList;
//Enumerate available resolutions using SDL_ListModes()
//Note: we enumerate fullscreen resolutions because
// windowed resolutions always can be arbitrary
if(resolutionList.empty()){
SDL_Rect **modes=SDL_ListModes(NULL,SDL_FULLSCREEN|SCREEN_FLAGS|SDL_ANYFORMAT);
if(modes==NULL || ((intptr_t)modes) == -1){
cout<<"Error: Can't enumerate available screen resolutions."
" Use predefined screen resolutions list instead."<<endl;
static const _res predefinedResolutionList[] = {
{800,600},
{1024,600},
{1024,768},
{1152,864},
{1280,720},
{1280,768},
{1280,800},
{1280,960},
{1280,1024},
{1360,768},
{1366,768},
{1440,900},
{1600,900},
{1600,1200},
{1680,1080},
{1920,1080},
{1920,1200},
{2560,1440},
{3840,2160}
};
//Fill the resolutionList.
for(unsigned int i=0;i<sizeof(predefinedResolutionList)/sizeof(_res);i++){
resolutionList.push_back(predefinedResolutionList[i]);
}
}else{
//Fill the resolutionList.
for(unsigned int i=0;modes[i]!=NULL;i++){
//Check if the resolution is higher than the minimum (800x600).
if(modes[i]->w>=800 && modes[i]->h>=600){
_res res={modes[i]->w, modes[i]->h};
resolutionList.push_back(res);
}
}
//Reverse it so that we begin with the lowest resolution.
reverse(resolutionList.begin(),resolutionList.end());
}
}
//The resolution that will hold the final result, we start with the minimum (800x600).
_res closestMatch={800,600};
int width=atoi(getSettings()->getValue("width").c_str());
//int height=atoi(getSettings()->getValue("height").c_str());
//Now loop through the resolutionList.
for(int i=0;i<(int)resolutionList.size();i++){
//The delta between the closestMatch and the resolution from the list.
int dM=(closestMatch.w-resolutionList[i].w);
//The delta between the target width and the resolution from the list.
int dT=(width-resolutionList[i].w);
//Since the resolutions are getting higher the lower (more negative) the further away it is.
//That's why we check if the deltaMatch is lower than the the deltaTarget.
if((dM)<(dT)){
closestMatch.w=resolutionList[i].w;
closestMatch.h=resolutionList[i].h;
}
}
//Now set the resolution to the closest match.
char s[64];
sprintf(s,"%d",closestMatch.w);
getSettings()->setValue("width",s);
sprintf(s,"%d",closestMatch.h);
getSettings()->setValue("height",s);
}
#ifdef __X11_INCLUDED__
int handleXError(Display* disp,XErrorEvent* event){
//NOTE: This is UNTESTED code, there are still some things that should be tested/changed.
//NOTE: It checks against hardcoded opcodes, this should be based on included defines from the xf86vid headers instead.
//NOTE: This code assumes Xlib is in use, just like the resize restriction code for Linux.
//Print out the error message as normal.
char output[256];
XGetErrorText(disp,event->error_code,output,256);
cerr<<output<<endl;
//Check if the game is fullscreen.
if(getSettings()->getBoolValue("fullscreen")){
//Check for the exact error we want to handle differently.
if(event->error_code==BadValue && event->minor_code==10/*X_XF86VidModeSwitchToMode*/){
//The cause of this problem has likely something to do with fullscreen mode, so fallback to windowed.
cerr<<"ERROR: Xlib error code "<<event->error_code<<", request code "<<event->request_code<<"."<<endl;
cerr<<"ERROR: Falling back to windowed mode!"<<endl;
getSettings()->setValue("fullscreen","false");
createScreen();
return 0;
}
}
//Do the normal Xlib behaviour.
exit(1);
return 0;
}
#endif
void configureWindow(bool initial){
//We only need to configure the window if it's resizable.
if(!getSettings()->getBoolValue("resizable"))
return;
//Retrieve the WM info from SDL containing the window handle.
struct SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWMInfo(&wmInfo);
#ifdef __X11_INCLUDED__
//We assume that a linux system running meandmyshadow is also running an Xorg server.
if(wmInfo.subsystem==SDL_SYSWM_X11){
//Create the size hints to give to the window.
XSizeHints* sizeHints;
if(!(sizeHints=XAllocSizeHints())){
cerr<<"ERROR: Unable to allocate memory for XSizeHings."<<endl;
return;
}
//Configure the size hint.
sizeHints->flags=PMinSize;
sizeHints->min_width=800;
sizeHints->min_height=600;
//Set the normal hints of the window.
(void)wmInfo.info.x11.lock_func;
XSetNormalHints(wmInfo.info.x11.display,wmInfo.info.x11.wmwindow,sizeHints);
(void)wmInfo.info.x11.unlock_func;
//Free size hint structure
XFree(sizeHints);
}else{
//No X11 so an unsupported window manager.
cerr<<"WARNING: Unsupported window manager."<<endl;
}
#elif defined(WIN32)
//We overwrite the window proc of SDL
WNDPROC wndproc=(WNDPROC)GetWindowLong(wmInfo.window,GWL_WNDPROC);
if(wndproc!=NULL && wndproc!=(WNDPROC)WindowProc){
m_OldWindowProc=wndproc;
SetWindowLong(wmInfo.window,GWL_WNDPROC,(LONG)(WNDPROC)WindowProc);
}
#endif
}
void onVideoResize(){
//Check if the resize event isn't malformed.
if(event.resize.w<=0 || event.resize.h<=0)
return;
//Check the size limit.
if(event.resize.w<800)
event.resize.w=800;
if(event.resize.h<600)
event.resize.h=600;
//Check if it really resizes.
if(SCREEN_WIDTH==event.resize.w && SCREEN_HEIGHT==event.resize.h)
return;
char s[32];
//Set the new width and height.
sprintf(s,"%d",event.resize.w);
getSettings()->setValue("width",s);
sprintf(s,"%d",event.resize.h);
getSettings()->setValue("height",s);
//Do resizing.
if(!createScreen())
return;
//Tell the theme to resize.
if(!loadTheme(""))
return;
//The new resolution is valid.
//Now we can save the settings. (TODO: should we save?)
//saveSettings();
//And let the currentState update it's GUI to the new resolution.
currentState->resize();
}
bool init(){
//Initialze SDL.
if(SDL_Init(SDL_INIT_EVERYTHING)==-1) {
fprintf(stderr,"FATAL ERROR: SDL_Init failed\n");
return false;
}
//Initialze SDL_mixer (audio).
if(Mix_OpenAudio(22050,MIX_DEFAULT_FORMAT,2,512)==-1){
fprintf(stderr,"FATAL ERROR: Mix_OpenAudio failed\n");
return false;
}
//Set the volume.
Mix_Volume(-1,atoi(settings->getValue("sound").c_str()));
//Initialze SDL_ttf (fonts).
if(TTF_Init()==-1){
fprintf(stderr,"FATAL ERROR: TTF_Init failed\n");
return false;
}
#ifdef __X11_INCLUDED__
//Before creating the screen set the XErrorHandler in case of X11.
XSetErrorHandler(handleXError);
#endif
//Create the screen.
if(!createScreen())
return false;
//Load key config. Then initialize joystick support.
inputMgr.loadConfig();
inputMgr.openAllJoysitcks();
//Init tinygettext for translations for the right language
dictionaryManager = new tinygettext::DictionaryManager();
dictionaryManager->add_directory(getDataPath()+"locale");
dictionaryManager->set_charset("UTF-8");
//Check if user have defined own language. If not, find it out for the player using findlocale
string lang=getSettings()->getValue("lang");
if(lang.length()>0){
printf("Locale set by user to %s\n",lang.c_str());
language=lang;
}else{
FL_Locale *locale;
FL_FindLocale(&locale,FL_MESSAGES);
printf("Locale isn't set by user: %s\n",locale->lang);
language=locale->lang;
if(locale->country!=NULL){
language+=string("_")+string(locale->country);
}
if(locale->variant!=NULL){
language+=string("@")+string(locale->variant);
}
FL_FreeLocale(&locale);
}
//Now set the language in the dictionaryManager.
dictionaryManager->set_language(tinygettext::Language::from_name(language));
//Disable annoying 'Couldn't translate: blah blah blah'
tinygettext::Log::set_log_info_callback(NULL);
+
+ //Set time format to the user-preference of the system.
+ setlocale(LC_TIME,"");
//Create the types of blocks.
for(int i=0;i<TYPE_MAX;i++){
Game::blockNameMap[Game::blockName[i]]=i;
}
//Structure that holds the event type/name pair.
struct EventTypeName{
int type;
const char* name;
};
//Create the types of game object event types.
{
const EventTypeName types[]={
{GameObjectEvent_PlayerWalkOn,"playerWalkOn"},
{GameObjectEvent_PlayerIsOn,"playerIsOn"},
{GameObjectEvent_PlayerLeave,"playerLeave"},
{GameObjectEvent_OnCreate,"onCreate"},
{GameObjectEvent_OnEnterFrame,"onEnterFrame"},
{GameObjectEvent_OnToggle,"onToggle"},
{GameObjectEvent_OnSwitchOn,"onSwitchOn"},
{GameObjectEvent_OnSwitchOff,"onSwitchOff"},
{0,NULL}
};
for(int i=0;types[i].name;i++){
Game::gameObjectEventNameMap[types[i].name]=types[i].type;
Game::gameObjectEventTypeMap[types[i].type]=types[i].name;
}
}
//Create the types of level event types.
{
const EventTypeName types[]={
{LevelEvent_OnCreate,"onCreate"},
{LevelEvent_OnSave,"onSave"},
{LevelEvent_OnLoad,"onLoad"},
{LevelEvent_OnReset,"onReset"},
{0,NULL}
};
for(int i=0;types[i].name;i++){
Game::levelEventNameMap[types[i].name]=types[i].type;
Game::levelEventTypeMap[types[i].type]=types[i].name;
}
}
//Register the ScriptAPI's functions in the scriptExecutor.
registerFunctions(getScriptExecutor());
//Nothing went wrong so we return true.
return true;
}
static TTF_Font* loadFont(const char* name,int size){
TTF_Font* tmpFont=TTF_OpenFont((getDataPath()+"font/"+name+".ttf").c_str(),size);
if(tmpFont){
return tmpFont;
}else{
#if defined(ANDROID)
//Android has built-in DroidSansFallback.ttf. (?)
return TTF_OpenFont("/system/fonts/DroidSansFallback.ttf",size);
#else
return TTF_OpenFont((getDataPath()+"font/DroidSansFallback.ttf").c_str(),size);
#endif
}
}
bool loadFonts(){
//Load the fonts.
//NOTE: This is a separate method because it will be called separately when re-initing in case of language change.
//First close the fonts if needed.
if(fontTitle)
TTF_CloseFont(fontTitle);
if(fontGUI)
TTF_CloseFont(fontGUI);
if(fontGUISmall)
TTF_CloseFont(fontGUISmall);
if(fontText)
TTF_CloseFont(fontText);
/// TRANSLATORS: Font used in GUI:
/// - Use "knewave" for languages using Latin and Latin-derived alphabets
/// - "DroidSansFallback" can be used for non-Latin writing systems
fontTitle=loadFont(_("knewave"),55);
fontGUI=loadFont(_("knewave"),32);
fontGUISmall=loadFont(_("knewave"),24);
/// TRANSLATORS: Font used for normal text:
/// - Use "Blokletters-Viltstift" for languages using Latin and Latin-derived alphabets
/// - "DroidSansFallback" can be used for non-Latin writing systems
fontText=loadFont(_("Blokletters-Viltstift"),16);
if(fontTitle==NULL || fontGUI==NULL || fontGUISmall==NULL || fontText==NULL){
printf("ERROR: Unable to load fonts! \n");
return false;
}
//Nothing went wrong so return true.
return true;
}
//Generate small arrows used for some GUI widgets.
static void generateArrows(){
TTF_Font* fontArrow=loadFont(_("knewave"),18);
if(arrowLeft1){
SDL_FreeSurface(arrowLeft1);
SDL_FreeSurface(arrowRight1);
SDL_FreeSurface(arrowLeft2);
SDL_FreeSurface(arrowRight2);
}
arrowLeft1=TTF_RenderUTF8_Blended(fontArrow,"<",themeTextColor);
arrowRight1=TTF_RenderUTF8_Blended(fontArrow,">",themeTextColor);
arrowLeft2=TTF_RenderUTF8_Blended(fontArrow,"<",themeTextColorDialog);
arrowRight2=TTF_RenderUTF8_Blended(fontArrow,">",themeTextColorDialog);
TTF_CloseFont(fontArrow);
}
bool loadTheme(string name){
//Load default fallback theme if it isn't loaded yet
if(objThemes.themeCount()==0){
if(objThemes.appendThemeFromFile(getDataPath()+"themes/Cloudscape/theme.mnmstheme")==NULL){
printf("ERROR: Can't load default theme file\n");
return false;
}
}
//Resize background or load specific theme
bool success=true;
if(name==""||name.empty()){
objThemes.scaleToScreen();
}else{
string theme=processFileName(name);
if(objThemes.appendThemeFromFile(theme+"/theme.mnmstheme")==NULL){
printf("ERROR: Can't load theme %s\n",theme.c_str());
success=false;
}
}
generateArrows();
//Everything went fine so return true.
return success;
}
static Mix_Chunk* loadWAV(const char* s){
Mix_Chunk* c=Mix_LoadWAV(s);
if(c!=NULL) return c;
printf("ERROR: Can't load sound file %s: %s\n",s,SDL_GetError());
return NULL;
}
static SDL_Cursor* loadCursor(const char* image[]){
int i,row,col;
//The array that holds the data (0=white 1=black)
Uint8 data[4*32];
//The array that holds the alpha mask (0=transparent 1=visible)
Uint8 mask[4*32];
//The coordinates of the hotspot of the cursor.
int hotspotX, hotspotY;
i=-1;
//Loop through the rows and columns.
//NOTE: We assume a cursor size of 32x32.
for(row=0;row<32;++row){
for(col=0; col<32;++col){
if(col % 8) {
data[i]<<=1;
mask[i]<<=1;
}else{
++i;
data[i]=mask[i]=0;
}
switch(image[4+row][col]){
case '+':
data[i] |= 0x01;
mask[i] |= 0x01;
break;
case '.':
mask[i] |= 0x01;
break;
default:
break;
}
}
}
//Get the hotspot x and y locations from the last line of the cursor.
sscanf(image[4+row],"%d,%d",&hotspotX,&hotspotY);
return SDL_CreateCursor(data,mask,32,32,hotspotX,hotspotY);
}
bool loadFiles(){
//Load the fonts.
if(!loadFonts())
return false;
fontMono=loadFont("VeraMono",12);
//Show a loading screen
{
SDL_Rect r={0,0,screen->w,screen->h};
SDL_FillRect(screen,&r,0);
SDL_Color fg={255,255,255};
SDL_Surface *surface=TTF_RenderUTF8_Blended(fontTitle,_("Loading..."),fg);
if(surface!=NULL){
r.x=(screen->w-surface->w)/2;
r.y=(screen->h-surface->h)/2;
SDL_BlitSurface(surface,NULL,screen,&r);
SDL_FreeSurface(surface);
}
SDL_Flip(screen);
}
musicManager.destroy();
//Load the music and play it.
if(musicManager.loadMusic((getDataPath()+"music/menu.music")).empty()){
printf("WARNING: Unable to load background music! \n");
}
musicManager.playMusic("menu",false);
//Always load the default music list for fallback.
musicManager.loadMusicList((getDataPath()+"music/default.list"));
//Load the configured music list.
getMusicManager()->loadMusicList((getDataPath()+"music/"+getSettings()->getValue("musiclist")+".list"));
getMusicManager()->setMusicList(getSettings()->getValue("musiclist"));
//Check if music is enabled.
if(getSettings()->getBoolValue("music"))
getMusicManager()->setEnabled();
//Load the sound effects
jumpSound=loadWAV((getDataPath()+"sfx/jump.wav").c_str());
hitSound=loadWAV((getDataPath()+"sfx/hit.wav").c_str());
saveSound=loadWAV((getDataPath()+"sfx/checkpoint.wav").c_str());
swapSound=loadWAV((getDataPath()+"sfx/swap.wav").c_str());
toggleSound=loadWAV((getDataPath()+"sfx/toggle.wav").c_str());
errorSound=loadWAV((getDataPath()+"sfx/error.wav").c_str());
collectSound=loadWAV((getDataPath()+"sfx/collect.wav").c_str());
achievementSound=loadWAV((getDataPath()+"sfx/achievement.ogg").c_str());
//Load the cursor images from the Cursor.h file.
cursors[CURSOR_POINTER]=loadCursor(pointer);
cursors[CURSOR_CARROT]=loadCursor(ibeam);
cursors[CURSOR_DRAG]=loadCursor(closedhand);
cursors[CURSOR_SIZE_HOR]=loadCursor(size_hor);
cursors[CURSOR_SIZE_VER]=loadCursor(size_ver);
cursors[CURSOR_SIZE_FDIAG]=loadCursor(size_fdiag);
cursors[CURSOR_SIZE_BDIAG]=loadCursor(size_bdiag);
cursors[CURSOR_REMOVE]=loadCursor(remove_cursor);
//Set the default cursor right now.
SDL_SetCursor(cursors[CURSOR_POINTER]);
levelPackManager.destroy();
//Now sum up all the levelpacks.
vector<string> v=enumAllDirs(getDataPath()+"levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getDataPath()+"levelpacks/"+*i);
}
v=enumAllDirs(getUserPath(USER_DATA)+"levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getUserPath(USER_DATA)+"levelpacks/"+*i);
}
v=enumAllDirs(getUserPath(USER_DATA)+"custom/levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getUserPath(USER_DATA)+"custom/levelpacks/"+*i);
}
//Now we add a special levelpack that will contain the levels not in a levelpack.
LevelPack* levelsPack=new LevelPack;
levelsPack->levelpackName="Levels";
LevelPack* customLevelsPack=new LevelPack;
customLevelsPack->levelpackName="Custom Levels";
//List the addon levels and add them one for one.
v=enumAllFiles(getUserPath(USER_DATA)+"levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelsPack->addLevel(getUserPath(USER_DATA)+"levels/"+*i);
levelsPack->setLocked(levelsPack->getLevelCount()-1);
}
//List the custom levels and add them one for one.
v=enumAllFiles(getUserPath(USER_DATA)+"custom/levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelsPack->addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
levelsPack->setLocked(levelsPack->getLevelCount()-1);
customLevelsPack->addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
customLevelsPack->setLocked(customLevelsPack->getLevelCount()-1);
}
//Add them to the manager.
levelPackManager.addLevelPack(levelsPack);
levelPackManager.addLevelPack(customLevelsPack);
//Load statistics
statsMgr.loadPicture();
statsMgr.registerAchievements();
statsMgr.loadFile(getUserPath(USER_CONFIG)+"statistics");
//Do something ugly and slow
statsMgr.reloadCompletedLevelsAndAchievements();
statsMgr.reloadOtherAchievements();
//Load the theme, both menu and default.
//NOTE: Loading theme may fail and returning false would stop everything, default theme will be used instead.
if (!loadTheme(getSettings()->getValue("theme"))){
getSettings()->setValue("theme","%DATA%/themes/Cloudscape");
saveSettings();
}
//Nothing failed so return true.
return true;
}
bool loadSettings(){
settings=new Settings(getUserPath(USER_CONFIG)+"meandmyshadow.cfg");
settings->parseFile();
//Now apply settings changed through command line arguments, if any.
map<string,string>::iterator it;
for(it=tmpSettings.begin();it!=tmpSettings.end();++it){
settings->setValue(it->first,it->second);
}
tmpSettings.clear();
//Always return true?
return true;
}
bool saveSettings(){
settings->save();
//Always return true?
return true;
}
Settings* getSettings(){
return settings;
}
MusicManager* getMusicManager(){
return &musicManager;
}
LevelPackManager* getLevelPackManager(){
return &levelPackManager;
}
ScriptExecutor* getScriptExecutor(){
return &scriptExecutor;
}
void flipScreen(){
if(settings->getBoolValue("gl")){
#ifdef HARDWARE_ACCELERATION
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
//Create a texture from the screen surface.
glBindTexture(GL_TEXTURE_2D,screenTexture);
//Set the texture's stretching properties
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D,0,screen->format->BytesPerPixel,screen->w,screen->h,0,GL_BGRA,GL_UNSIGNED_BYTE,screen->pixels);
glBegin(GL_QUADS);
glTexCoord2i(0,0); glVertex3f(0,0,0);
glTexCoord2i(1,0); glVertex3f(SCREEN_WIDTH,0,0);
glTexCoord2i(1,1); glVertex3f(SCREEN_WIDTH,SCREEN_HEIGHT,0);
glTexCoord2i(0,1); glVertex3f(0,SCREEN_HEIGHT,0);
glEnd();
SDL_GL_SwapBuffers();
#else
//NOTE: Trying to flip the screen using gl while compiled without.
cerr<<"FATAL ERROR: Unable to draw to screen using OpenGL (compiled without)."<<endl;
#endif
}else{
SDL_Flip(screen);
}
}
void clean(){
//Save statistics
statsMgr.saveFile(getUserPath(USER_CONFIG)+"statistics");
//We delete the settings.
if(settings){
delete settings;
settings=NULL;
}
+ //Delete dictionaryManager.
+ delete dictionaryManager;
+
//Get rid of the currentstate.
//NOTE: The state is probably already deleted by the changeState function.
if(currentState)
delete currentState;
//Destroy the GUI if present.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Destroy the imageManager.
imageManager.destroy();
//Destroy the musicManager.
musicManager.destroy();
//Destroy all sounds
Mix_FreeChunk(jumpSound);
Mix_FreeChunk(hitSound);
Mix_FreeChunk(saveSound);
Mix_FreeChunk(swapSound);
Mix_FreeChunk(toggleSound);
Mix_FreeChunk(errorSound);
Mix_FreeChunk(collectSound);
Mix_FreeChunk(achievementSound);
//Destroy the cursors.
for(int i=0;i<CURSOR_MAX;i++){
SDL_FreeCursor(cursors[i]);
cursors[i]=NULL;
}
//Destroy the levelPackManager.
levelPackManager.destroy();
levels=NULL;
//Close all joysticks.
inputMgr.closeAllJoysticks();
//Close the fonts and quit SDL_ttf.
TTF_CloseFont(fontTitle);
TTF_CloseFont(fontGUI);
TTF_CloseFont(fontGUISmall);
TTF_CloseFont(fontText);
TTF_CloseFont(fontMono);
TTF_Quit();
//Remove the temp surface.
SDL_FreeSurface(tempSurface);
//Stop audio.and quit
Mix_CloseAudio();
#ifndef __APPLE__
Mix_Quit();
#endif
//And finally quit SDL.
SDL_Quit();
}
void setNextState(int newstate){
//Only change the state when we aren't already exiting.
if(nextState!=STATE_EXIT){
nextState=newstate;
}
}
void changeState(){
//Check if there's a nextState.
if(nextState!=STATE_NULL){
//Delete the currentState.
delete currentState;
currentState=NULL;
//Set the currentState to the nextState.
stateID=nextState;
nextState=STATE_NULL;
//Init the state.
switch(stateID){
case STATE_GAME:
{
currentState=NULL;
Game* game=new Game();
currentState=game;
//Check if we should load record file or a level.
if(!Game::recordFile.empty()){
game->loadRecord(Game::recordFile.c_str());
Game::recordFile.clear();
}else{
game->loadLevel(levels->getLevelpackPath()+levels->getLevelFile());
levels->saveLevelProgress();
}
}
break;
case STATE_MENU:
currentState=new Menu();
break;
case STATE_LEVEL_SELECT:
currentState=new LevelPlaySelect();
break;
case STATE_LEVEL_EDIT_SELECT:
currentState=new LevelEditSelect();
break;
case STATE_LEVEL_EDITOR:
{
currentState=NULL;
LevelEditor* levelEditor=new LevelEditor();
currentState=levelEditor;
//Load the selected level.
levelEditor->loadLevel(levels->getLevelpackPath()+levels->getLevelFile());
}
break;
case STATE_OPTIONS:
currentState=new Options();
break;
case STATE_ADDONS:
currentState=new Addons();
break;
case STATE_CREDITS:
currentState=new Credits();
break;
case STATE_STATISTICS:
currentState=new StatisticsScreen();
break;
}
//NOTE: STATE_EXIT isn't mentioned, meaning that currentState is null.
//This way the game loop will break and the program will exit.
//Fade out, if fading is enabled.
int fade=0;
if(settings->getBoolValue("fading"))
fade=255;
SDL_BlitSurface(screen,NULL,tempSurface,NULL);
while(fade>0){
fade-=17;
if(fade<0)
fade=0;
SDL_FillRect(screen,NULL,0);
SDL_SetAlpha(tempSurface, SDL_SRCALPHA, fade);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
flipScreen();
SDL_Delay(25);
}
}
}
void musicStoppedHook(){
//We just call the musicStopped method of the MusicManager.
musicManager.musicStopped();
}
bool checkCollision(const SDL_Rect& a,const SDL_Rect& b){
//Check if the left side of box a isn't past the right side of b.
if(a.x>=b.x+b.w){
return false;
}
//Check if the right side of box a isn't left of the left side of b.
if(a.x+a.w<=b.x){
return false;
}
//Check if the top side of box a isn't under the bottom side of b.
if(a.y>=b.y+b.h){
return false;
}
//Check if the bottom side of box a isn't above the top side of b.
if(a.y+a.h<=b.y){
return false;
}
//We have collision.
return true;
}
void setCamera(const SDL_Rect* r,int count){
//SetCamera only works in the Level editor and when mouse is inside window.
if(stateID==STATE_LEVEL_EDITOR&&(SDL_GetAppState()&SDL_APPMOUSEFOCUS)){
//Get the mouse coordinates.
int x,y;
SDL_GetMouseState(&x,&y);
SDL_Rect mouse={x,y,0,0};
//Don't continue here if mouse is inside one of the boxes given as parameter.
for(int i=0;i<count;i++){
if(checkCollision(mouse,r[i]))
return;
}
//Check if the mouse is near the left edge of the screen.
//Else check if the mouse is near the right edge.
if(x<50){
//We're near the left edge so move the camera.
camera.x-=5;
}else if(x>SCREEN_WIDTH-50){
//We're near the right edge so move the camera.
camera.x+=5;
}
//Check if the mouse is near the top edge of the screen.
//Else check if the mouse is near the bottom edge.
if(y<50){
//We're near the top edge so move the camera.
camera.y-=5;
}else if(y>SCREEN_HEIGHT-50){
//We're near the bottom edge so move the camera.
camera.y+=5;
}
}
}
int parseArguments(int argc, char** argv){
//Loop through all arguments.
//We start at one since 0 is the command itself.
for(int i=1;i<argc;i++){
string argument=argv[i];
//Check if the argument is the data-dir.
if(argument=="--data-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Configure the dataPath with the given path.
dataPath=argv[i];
if(!getDataPath().empty()){
char c=dataPath[dataPath.size()-1];
if(c!='/'&&c!='\\') dataPath+="/";
}
}else if(argument=="--user-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Configure the userPath with the given path.
userPath=argv[i];
if(!userPath.empty()){
char c=userPath[userPath.size()-1];
if(c!='/'&&c!='\\') userPath+="/";
}
}else if(argument=="-f" || argument=="-fullscreen" || argument=="--fullscreen"){
tmpSettings["fullscreen"]="1";
}else if(argument=="-w" || argument=="-windowed" || argument=="--windowed"){
tmpSettings["fullscreen"]="0";
}else if(argument=="-mv" || argument=="-music" || argument=="--music"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Now set the music volume.
tmpSettings["music"]=argv[i];
}else if(argument=="-sv" || argument=="-sound" || argument=="--sound"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Now set sound volume.
tmpSettings["sound"]=argv[i];
}else if(argument=="-set" || argument=="--set"){
//We need a second and a third argument so we increase i.
i+=2;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//And set the setting.
tmpSettings[argv[i-1]]=argv[i];
}else if(argument=="-v" || argument=="-version" || argument=="--version"){
//Print the version.
printf("%s\n",version.c_str());
return 0;
}else if(argument=="-h" || argument=="-help" || argument=="--help"){
//If the help is requested we'll return false without printing an error.
//This way the usage/help text will be printed.
return -1;
}else{
//Any other argument is unknow so we return false.
printf("ERROR: Unknown argument %s\n\n",argument.c_str());
return -1;
}
}
//If everything went well we can return true.
return 1;
}
//Special structure that will recieve the GUIEventCallbacks of the messagebox.
struct msgBoxHandler:public GUIEventCallback{
public:
//Integer containing the ret(urn) value of the messageBox.
int ret;
public:
//Constructor.
msgBoxHandler():ret(0){}
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Make sure it's a click event.
if(eventType==GUIEventClick){
//Set the return value.
ret=obj->value;
//After a click event we can delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
};
msgBoxResult msgBox(string prompt,msgBoxButtons buttons,const string& title){
//Create the event handler.
msgBoxHandler objHandler;
//The GUI objects.
GUIObject* obj;
//Create the GUIObjectRoot, the height and y location is temp.
//It depends on the content what it will be.
GUIObject* root=new GUIObject((SCREEN_WIDTH-600)/2,200,600,200,GUIObjectFrame,title.c_str());
//Integer containing the current y location used to grow dynamic depending on the content.
int y=50;
//Now process the prompt.
{
//Pointer to the string.
char* lps=(char*)prompt.c_str();
//Pointer to a character.
char* lp=NULL;
//We keep looping forever.
//The only way out is with the break statement.
for(;;){
//As long as it's still the same sentence we continue.
//It will stop when there's a newline or end of line.
for(lp=lps;*lp!='\n'&&*lp!='\r'&&*lp!=0;lp++);
//Store the character we stopped on. (End or newline)
char c=*lp;
//Set the character in the string to 0, making lps a string containing one sentence.
*lp=0;
//Add a GUIObjectLabel with the sentence.
root->addChild(new GUIObject(0,y,root->width,25,GUIObjectLabel,lps,0,true,true,GUIGravityCenter));
//Increase y with 25, about the height of the text.
y+=25;
//Check the stored character if it was a stop.
if(c==0){
//It was so break out of the for loop.
lps=lp;
break;
}
//It wasn't meaning more will follow.
//We set lps to point after the "newline" forming a new string.
lps=lp+1;
}
}
//Add 70 to y to leave some space between the content and the buttons.
y+=70;
//Recalc the size of the message box.
root->top=(SCREEN_HEIGHT-y)/2;
root->height=y;
//Now we need to add the buttons.
//Integer containing the number of buttons to add.
int count=0;
//Array with the return codes for the buttons.
int value[3]={0};
//Array containing the captation for the buttons.
string button[3]={"","",""};
switch(buttons){
case MsgBoxOKCancel:
count=2;
button[0]=_("OK");value[0]=MsgBoxOK;
button[1]=_("Cancel");value[1]=MsgBoxCancel;
break;
case MsgBoxAbortRetryIgnore:
count=3;
button[0]=_("Abort");value[0]=MsgBoxAbort;
button[1]=_("Retry");value[1]=MsgBoxRetry;
button[2]=_("Ignore");value[2]=MsgBoxIgnore;
break;
case MsgBoxYesNoCancel:
count=3;
button[0]=_("Yes");value[0]=MsgBoxYes;
button[1]=_("No");value[1]=MsgBoxNo;
button[2]=_("Cancel");value[2]=MsgBoxCancel;
break;
case MsgBoxYesNo:
count=2;
button[0]=_("Yes");value[0]=MsgBoxYes;
button[1]=_("No");value[1]=MsgBoxNo;
break;
case MsgBoxRetryCancel:
count=2;
button[0]=_("Retry");value[0]=MsgBoxRetry;
button[1]=_("Cancel");value[1]=MsgBoxCancel;
break;
default:
count=1;
button[0]=_("OK");value[0]=MsgBoxOK;
break;
}
//Now we start making the buttons.
{
//Reduce y so that the buttons fit inside the frame.
y-=40;
double places[3]={0.0};
if(count==1){
places[0]=0.5;
}else if(count==2){
places[0]=0.4;
places[1]=0.6;
}else if(count==3){
places[0]=0.3;
places[1]=0.5;
places[2]=0.7;
}
//Loop to add the buttons.
for(int i=0;i<count;i++){
obj=new GUIObject(root->width*places[i],y,-1,36,GUIObjectButton,button[i].c_str(),value[i],true,true,GUIGravityCenter);
obj->eventCallback=&objHandler;
root->addChild(obj);
}
}
//Now we dim the screen and keep the GUI rendering/updating.
GUIOverlay* overlay=new GUIOverlay(root);
overlay->enterLoop(true);
//And return the result.
return (msgBoxResult)objHandler.ret;
}
struct fileDialogHandler:public GUIEventCallback{
public:
//The ret(urn) value, true=ok and false=cancel
bool ret;
//Boolean if it's a save dialog.
bool isSave;
//Boolean if the file should be verified.
bool verifyFile;
//Boolean if files should be listed instead of directories.
bool files;
//Pointer to the textfield containing the filename.
GUIObject* txtName;
//Pointer to the listbox containing the different files.
GUIListBox* lstFile;
//The extension the files listed should have.
const char* extension;
//The current filename.
string fileName;
//The current search path.
string path;
//Vector containing the search paths.
vector<string> searchPath;
public:
//Constructor.
fileDialogHandler(bool isSave=false,bool verifyFile=false, bool files=true):ret(false),
isSave(isSave),verifyFile(verifyFile),
files(files),txtName(NULL),lstFile(NULL),extension(NULL){}
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Check for the ok event.
if(name=="cmdOK"){
//Get the entered fileName from the text field.
std::string s=txtName->caption;
//If it doesn't contain a slash we need to add the path to the fileName.
if(s.find_first_of("/")==string::npos)
s=path+s;
//If the string empty we return.
if(s.empty() || s.find_first_of("*?")!=string::npos)
return;
//We only need to check for extensions if it isn't a folder dialog.
if(files){
//If there isn't right extension add it.
size_t found=s.find_first_of(".");
if(found!=string::npos)
s.replace(s.begin()+found+1,s.end(),extension);
else if (s.substr(found+1)!=extension)
s.append(string(".")+extension);
}
//Check if we should save or load the file.
//
if(isSave){
//Open the file with read permission to check if it already exists.
FILE* f;
f=fopen(processFileName(s).c_str(),"rb");
//Check if it exists.
if(f){
//Close the file.
fclose(f);
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//Prompt the user with a Yes or No question.
/// TRANSLATORS: Filename is coming before this text
if(msgBox(tfm::format(_("%s already exists.\nDo you want to overwrite it?"),s),MsgBoxYesNo,_("Overwrite Prompt"))!=MsgBoxYes){
//He answered no, so we return.
return;
}
}
//Check if we should verify the file.
//Verifying only applies to files not to directories.
if(verifyFile && files){
//Open the file with write permission.
f=fopen(processFileName(s).c_str(),"wb");
//Check if their aren't problems.
if(f){
//Close the file.
fclose(f);
}else{
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//The file can't be opened so tell the user.
msgBox(tfm::format(_("Can't open file %s."),s),MsgBoxOKOnly,_("Error"));
return;
}
}
}else if(verifyFile && files){
//We need to verify a file for opening.
FILE *f;
f=fopen(processFileName(s).c_str(),"rb");
//Check if it didn't fail.
if(f){
//Succes, so close the file.
fclose(f);
}else{
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//Unable to open file so tell the user.
msgBox(tfm::format(_("Can't open file %s."),s),MsgBoxOKOnly,_("Error"));
return;
}
}
//If we haven't returned then it's fine.
//Set the fileName to the chosen file.
fileName=s;
//Delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Set return to true.
ret=true;
}else if(name=="cmdCancel"){
//Cancel means we can kill the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="lstFile"){
//Get a pointer to the listbox.
GUIListBox* obj1=lstFile;
//Make sure the option exist and change textfield to it.
if(obj1!=NULL && txtName!=NULL && obj1->value>=0 && obj1->value<(int)obj1->item.size()){
txtName->caption=obj1->item[obj1->value];
}
}else if(name=="lstSearchIn"){
//Get the searchpath listbox.
GUISingleLineListBox *obj1=dynamic_cast<GUISingleLineListBox*>(obj);
//Check if the entry exists.
if(obj1!=NULL && lstFile!=NULL && obj1->value>=0 && obj1->value<(int)searchPath.size()){
//Temp string.
string s;
//Get the new search path.
path=searchPath[obj1->value];
//Make sure it isn't empty.
if(!path.empty()){
//Process the filename.
s=processFileName(path);
}else{
//It's empty so we give the userpath.
s=getUserPath();
}
//Fill the list with files or directories.
if(files) {
lstFile->item=enumAllFiles(s,extension);
}else
lstFile->item=enumAllDirs(s);
//Remove any selection from the list.
lstFile->value=-1;
}
}
}
};
bool fileDialog(string& fileName,const char* title,const char* extension,const char* path,bool isSave,bool verifyFile,bool files){
//Pointer to GUIObject to make the GUI with.
GUIObject* obj;
//Create the fileDialogHandler, used for event handling.
fileDialogHandler objHandler(isSave,verifyFile,files);
//Vector containing the pathNames.
vector<string> pathNames;
//Set the extension of the objHandler.
objHandler.extension=extension;
//We now need to splits the given path into multiple path names.
if(path && path[0]){
//The string isn't empty.
//Pointer to the paths string.
char* lp=(char*)path;
//Pointer to the first newline.
char* lps=strchr(lp,'\n');
//Pointer used for checking if their's another newline.
//It will indicate if it's the last set or not.
char* lpe;
//Check for a newline.
if(lps){
//We have newline(s) so loop forever.
//We can only break out of the loop when the string ends.
for(;;){
//Add the first searchpath.
//This is the beginning of the string (lp) to the first newline. (lps)
objHandler.searchPath.push_back(string(lp,lps-lp));
//We should have another newline so search for it.
lpe=strchr(lps+1,'\n');
if(lpe){
//We found it so we add that to the pathname.
pathNames.push_back(string(lps+1,lpe-lps-1));
//And start over again by setting lp to the start of a new set of searchPath/pathName.
lp=lpe+1;
}else{
//There is no newline anymore, meaning the last entry, the rest of the string must be the pathName.
pathNames.push_back(string(lps+1));
//And break out of the loop.
break;
}
//We haven't broken out so search for a newline.
lps=strchr(lp,'\n');
//If there isn't a newline break.
if(!lps)
break;
}
}else{
//There is no newline thus the whole string is the searchPath.
objHandler.searchPath.push_back(path);
}
}else{
//Empty so put an empty string as searchPath.
objHandler.searchPath.push_back(string());
}
//It's time to create the GUI.
//If there are more than one pathNames we need to add a GUISingleLineListBox.
int base_y=pathNames.empty()?20:60;
//Create the frame.
GUIObject* root=new GUIObject(100,100-base_y/2,600,400+base_y,GUIObjectFrame,title?title:(isSave?_("Save File"):_("Load File")));
//Create the search path list box if needed.
if(!pathNames.empty()){
root->addChild(new GUIObject(8,40,184,36,GUIObjectLabel,_("Search In")));
GUISingleLineListBox* obj1=new GUISingleLineListBox(160,40,432,36);
obj1->item=pathNames;
obj1->value=0;
obj1->name="lstSearchIn";
obj1->eventCallback=&objHandler;
root->addChild(obj1);
}
//Add the FileName label and textfield.
root->addChild(new GUIObject(8,20+base_y,184,36,GUIObjectLabel,_("File Name")));
{
//Fill the textbox with the given fileName.
string s=fileName;
if(!isSave){
//But only if it isn't empty.
if(s.empty() && extension && extension[0])
s=string("*.")+string(extension);
}
//Create the textbox and add it to the GUI.
objHandler.txtName=new GUIObject(160,20+base_y,432,36,GUIObjectTextBox,s.c_str());
root->addChild(objHandler.txtName);
}
//Now we add the ListBox containing the files or directories.
{
GUIListBox* obj1=new GUIListBox(8,60+base_y,584,292);
//Get the searchPath.
string s=objHandler.searchPath[0];
//Make sure it isn't empty.
if(!s.empty()){
objHandler.path=s;
s=processFileName(s);
}else{
s=getUserPath();
}
//Check if we should list files or directories.
if(files){
//Fill the list with files.
obj1->item=enumAllFiles(s,extension);
}else{
//Fill the list with directories.
obj1->item=enumAllDirs(s);
}
obj1->name="lstFile";
obj1->eventCallback=&objHandler;
root->addChild(obj1);
objHandler.lstFile=obj1;
}
//Now create the OK and Cancel buttons.
obj=new GUIObject(200,360+base_y,192,36,GUIObjectButton,_("OK"));
obj->name="cmdOK";
obj->eventCallback=&objHandler;
root->addChild(obj);
obj=new GUIObject(400,360+base_y,192,36,GUIObjectButton,_("Cancel"));
obj->name="cmdCancel";
obj->eventCallback=&objHandler;
root->addChild(obj);
//Create the gui overlay.
GUIOverlay* overlay=new GUIOverlay(root);
overlay->enterLoop();
//Now determine what the return value is (and if there is one).
if(objHandler.ret)
fileName=objHandler.fileName;
return objHandler.ret;
}
diff --git a/src/InputManager.cpp b/src/InputManager.cpp
index 3fc0558..8ea40b8 100644
--- a/src/InputManager.cpp
+++ b/src/InputManager.cpp
@@ -1,432 +1,453 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "InputManager.h"
#include "Globals.h"
#include "Settings.h"
#include "Functions.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIOverlay.h"
#include <stdlib.h>
#include <stdio.h>
#include <string>
using namespace std;
InputManager inputMgr;
//the order must be the same as InputManagerKeys
static const char* keySettingNames[INPUTMGR_MAX]={
"key_up","key_down","key_left","key_right","key_jump","key_action","key_space","key_cancelRecording",
"key_escape","key_restart","key_tab","key_save","key_load","key_swap",
"key_teleport","key_suicide","key_shift","key_next","key_previous","key_select"
};
//the order must be the same as InputManagerKeys
static const char* keySettingDescription[INPUTMGR_MAX]={
__("Up (in menu)"),__("Down (in menu)"),__("Left"),__("Right"),__("Jump"),__("Action"),__("Space (Record)"),__("Cancel recording"),
__("Escape"),__("Restart"),__("Tab (View shadow/Level prop.)"),__("Save game (in editor)"),__("Load game"),__("Swap (in editor)"),
__("Teleport (in editor)"),__("Suicide (in editor)"),__("Shift (in editor)"),__("Next block type (in Editor)"),
__("Previous block type (in editor)"), __("Select (in menu)")
};
//A class that handles the gui events of the inputDialog.
class InputDialogHandler:public GUIEventCallback{
private:
//the list box which contains keys.
GUIListBox* listBox;
//the parent object.
InputManager* parent;
- //check if it's alternative key
- bool isAlternativeKey;
//update specified key config item
void updateConfigItem(int index){
- //get the description
+ //Get the description of the key.
std::string s=_(keySettingDescription[index]);
s+=": ";
- //get key code name
- int keyCode=parent->getKeyCode((InputManagerKeys)index,isAlternativeKey);
+ //Get the key code name.
+ int keyCode=parent->getKeyCode((InputManagerKeys)index,false);
s+=_(InputManager::getKeyCodeName(keyCode));
+
+ //Add the alternative key if there is one.
+ int keyCodeAlt=parent->getKeyCode((InputManagerKeys)index,true);
+ if(keyCodeAlt!=0){
+ s+=" ";
+ s+=_("OR");
+ s+=" ";
+ s+=_(InputManager::getKeyCodeName(keyCodeAlt));
+ }
- //show it
+ //Update item.
listBox->updateItem(index,s);
}
public:
//Constructor.
- InputDialogHandler(GUIListBox* listBox,InputManager* parent):listBox(listBox),parent(parent),isAlternativeKey(false){
+ InputDialogHandler(GUIListBox* listBox,InputManager* parent):listBox(listBox),parent(parent){
//load the available keys to the list box.
for(int i=0;i<INPUTMGR_MAX;i++){
- //get the description
+ //Get the description of the key.
std::string s=_(keySettingDescription[i]);
s+=": ";
- //get key code name
+ //Get key code name.
int keyCode=parent->getKeyCode((InputManagerKeys)i,false);
s+=_(InputManager::getKeyCodeName(keyCode));
+
+ //Add the alternative key if there is one.
+ int keyCodeAlt=parent->getKeyCode((InputManagerKeys)i,true);
+ if(keyCodeAlt!=0){
+ s+=" ";
+ s+=_("OR");
+ s+=" ";
+ s+=_(InputManager::getKeyCodeName(keyCodeAlt));
+ }
- //add item
+ //Add item.
listBox->addItem(s);
}
}
- //when a key is pressed call this to set the key to currently-selected item
+ //When a key is pressed call this to set the key to currently-selected item.
void onKeyDown(int keyCode){
- //check if an item is selected.
+ //Check if an item is selected.
int index=listBox->value;
if(index<0 || index>=INPUTMGR_MAX) return;
-
- //set it.
- parent->setKeyCode((InputManagerKeys)index,keyCode,isAlternativeKey);
- updateConfigItem(index);
+
+ //Update the key.
+ //SDLK_BACKSPACE will erase the key.
+ if(keyCode==SDLK_BACKSPACE){
+ parent->setKeyCode((InputManagerKeys)index,0,true);
+ parent->setKeyCode((InputManagerKeys)index,0,false);
+ updateConfigItem(index);
+ }else{
+ //Update the main key if there isn't one. Otherwise update the alternative key if there isn't one.
+ int key=parent->getKeyCode((InputManagerKeys)index,false);
+ int altKey=parent->getKeyCode((InputManagerKeys)index,true);
+ if(key==0){
+ parent->setKeyCode((InputManagerKeys)index,keyCode,false);
+ }else if((altKey==0)&&(keyCode!=key)){
+ parent->setKeyCode((InputManagerKeys)index,keyCode,true);
+ }
+ updateConfigItem(index);
+ }
}
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
- //Make sure it's a click event.
- if(eventType==GUIEventClick){
- if(name=="cmdUnset"){
- onKeyDown(0);
- }else if(name=="lstType"){
- isAlternativeKey=(obj->value==1);
- for(int i=0;i<INPUTMGR_MAX;i++){
- updateConfigItem(i);
- }
- }
- }
+ //Do nothing...
}
};
//Event handler.
static InputDialogHandler* handler;
//A GUIObject that is used to create events for key presses.
class GUIKeyListener:public GUIObject{
//Leave empty.
~GUIKeyListener(){}
bool handleEvents(int x,int y,bool enabled,bool visible,bool processed){
if(enabled && handler){
if(event.type==SDL_KEYDOWN){
handler->onKeyDown(event.key.keysym.sym);
}
//Joystick
else if(event.type==SDL_JOYAXISMOTION){
if(event.jaxis.value>3200){
handler->onKeyDown(0x00010001 | (int(event.jaxis.axis)<<8));
}else if(event.jaxis.value<-3200){
handler->onKeyDown(0x000100FF | (int(event.jaxis.axis)<<8));
}
}
else if(event.type==SDL_JOYBUTTONDOWN){
handler->onKeyDown(0x00020000 | (int(event.jbutton.button)<<8));
}
else if(event.type==SDL_JOYHATMOTION){
if(event.jhat.value & SDL_HAT_LEFT){
handler->onKeyDown(0x00030000 | (int(event.jhat.hat)<<8) | SDL_HAT_LEFT);
}else if(event.jhat.value & SDL_HAT_RIGHT){
handler->onKeyDown(0x00030000 | (int(event.jhat.hat)<<8) | SDL_HAT_RIGHT);
}else if(event.jhat.value & SDL_HAT_UP){
handler->onKeyDown(0x00030000 | (int(event.jhat.hat)<<8) | SDL_HAT_UP);
}else if(event.jhat.value & SDL_HAT_DOWN){
handler->onKeyDown(0x00030000 | (int(event.jhat.hat)<<8) | SDL_HAT_DOWN);
}
}
}
return false;
}
//Nothing to do.
void render(){}
};
int InputManager::getKeyCode(InputManagerKeys key,bool isAlternativeKey){
if(isAlternativeKey) return alternativeKeys[key];
else return keys[key];
}
void InputManager::setKeyCode(InputManagerKeys key,int keyCode,bool isAlternativeKey){
if(isAlternativeKey) alternativeKeys[key]=keyCode;
else keys[key]=keyCode;
}
void InputManager::loadConfig(){
- int i;
- for(i=0;i<INPUTMGR_MAX;i++){
+ for(int i=0;i<INPUTMGR_MAX;i++){
string s=keySettingNames[i];
-
+
keys[i]=atoi(getSettings()->getValue(s).c_str());
-
+
s+="2";
alternativeKeys[i]=atoi(getSettings()->getValue(s).c_str());
+
+ //Move the alternative key to main key if the main key is empty.
+ if(keys[i]==0&&alternativeKeys[i]!=0){
+ keys[i]=alternativeKeys[i];
+ alternativeKeys[i]=0;
+ }
+
+ //Remove duplicate.
+ if(keys[i]==alternativeKeys[i])
+ alternativeKeys[i]=0;
}
}
void InputManager::saveConfig(){
int i;
char c[32];
for(i=0;i<INPUTMGR_MAX;i++){
string s=keySettingNames[i];
+
+ //Remove duplicate.
+ if(keys[i]==alternativeKeys[i])
+ alternativeKeys[i]=0;
sprintf(c,"%d",keys[i]);
getSettings()->setValue(s,c);
s+="2";
sprintf(c,"%d",alternativeKeys[i]);
getSettings()->setValue(s,c);
}
}
GUIObject* InputManager::showConfig(int height){
//Create the new GUI.
GUIObject* root=new GUIObject(0,0,SCREEN_WIDTH,height,GUIObjectNone);
//Instruction label.
- GUIObject* obj=new GUIObject(0,0,root->width,36,GUIObjectLabel,_("Select an item and press a key to config it."),0,true,true,GUIGravityCenter);
+ GUIObject* obj=new GUIObject(0,6,root->width,36,GUIObjectLabel,_("Select an item and press a key to change it."),0,true,true,GUIGravityCenter);
+ root->addChild(obj);
+
+ obj=new GUIObject(0,30,root->width,36,GUIObjectLabel,_("Press backspace to clear the selected item."),0,true,true,GUIGravityCenter);
root->addChild(obj);
//The listbox for keys.
- GUIListBox *listBox=new GUIListBox(SCREEN_WIDTH*0.15,72,SCREEN_WIDTH*0.7,height-36-72-8);
+ GUIListBox *listBox=new GUIListBox(SCREEN_WIDTH*0.15,72,SCREEN_WIDTH*0.7,height-72-8);
root->addChild(listBox);
//Create the event handler.
if(handler)
delete handler;
handler=new InputDialogHandler(listBox,this);
-
- //Listbox for selection between primary and alternative keys.
- GUISingleLineListBox *listBox0=new GUISingleLineListBox(SCREEN_WIDTH/2,32,360,36,true,true,GUIGravityCenter);
- listBox0->name="lstType";
- listBox0->item.push_back(_("Primary key"));
- listBox0->item.push_back(_("Alternative key"));
- listBox0->value=0;
- listBox0->eventCallback=handler;
- root->addChild(listBox0);
-
- //Button to unset selected key in the listbox.
- obj=new GUIObject(root->width/2,height-36,-1,36,GUIObjectButton,_("Unset the key"),0,true,true,GUIGravityCenter);
- obj->name="cmdUnset";
- obj->eventCallback=handler;
- root->addChild(obj);
obj=new GUIKeyListener();
root->addChild(obj);
//Return final widget.
return root;
}
-//get key name from key code
+//Get key name from key code.
std::string InputManager::getKeyCodeName(int keyCode){
char c[64];
if(keyCode>0 && keyCode <0x1000){
- //keyboard
+ //Keyboard.
char* s=SDL_GetKeyName((SDLKey)keyCode);
if(s!=NULL){
return s;
}else{
sprintf(c,"(Key %d)",keyCode);
return c;
}
}else if(keyCode>0x1000){
- //Joystick. first set it to invalid value
+ //Joystick. First set it to invalid value.
sprintf(c,"(Joystick 0x%08X)",keyCode);
- //check type
+ //Check the input type.
switch((keyCode & 0x00FF0000)>>16){
case 1:
- //axis
+ //Axis.
switch(keyCode & 0xFF){
case 1:
sprintf(c,"Joystick axis %d +",(keyCode & 0x0000FF00)>>8);
break;
case 0xFF:
sprintf(c,"Joystick axis %d -",(keyCode & 0x0000FF00)>>8);
break;
}
break;
case 2:
- //button
+ //Button.
sprintf(c,"Joystick button %d",(keyCode & 0x0000FF00)>>8);
break;
case 3:
- //hat
+ //Hat.
switch(keyCode & 0xFF){
case SDL_HAT_LEFT:
sprintf(c,"Joystick hat %d left",(keyCode & 0x0000FF00)>>8);
break;
case SDL_HAT_RIGHT:
sprintf(c,"Joystick hat %d right",(keyCode & 0x0000FF00)>>8);
break;
case SDL_HAT_UP:
sprintf(c,"Joystick hat %d up",(keyCode & 0x0000FF00)>>8);
break;
case SDL_HAT_DOWN:
sprintf(c,"Joystick hat %d down",(keyCode & 0x0000FF00)>>8);
break;
}
break;
}
return c;
}else{
- //unknown??
- return _("(Not set)");
+ //Disabled or unknown.
+ return "-";
}
}
InputManager::InputManager(){
- //clear the array.
+ //Clear the arrays.
for(int i=0;i<INPUTMGR_MAX;i++){
keys[i]=alternativeKeys[i]=keyFlags[i]=0;
}
}
InputManager::~InputManager(){
closeAllJoysticks();
}
int InputManager::getKeyState(int keyCode,int oldState,bool hasEvent){
int state=0;
if(keyCode>0 && keyCode<0x1000){
- //keyboard
+ //Keyboard.
if(hasEvent){
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==keyCode){
state|=0x2;
}
if(event.type==SDL_KEYUP && event.key.keysym.sym==keyCode){
state|=0x4;
}
}
if(keyCode<SDLK_LAST && SDL_GetKeyState(NULL)[keyCode]){
state|=0x1;
}
}else if(keyCode>0x1000){
- //Joystick
+ //Joystick.
int index=(keyCode & 0x0000FF00)>>8;
int value=keyCode & 0xFF;
int i,v;
switch((keyCode & 0x00FF0000)>>16){
case 1:
- //axis
+ //Axis.
if(hasEvent){
if(event.type==SDL_JOYAXISMOTION && event.jaxis.axis==index){
if((value==1 && event.jaxis.value>3200) || (value==0xFF && event.jaxis.value<-3200)){
if((oldState & 0x1)==0) state|=0x2;
}else{
if(oldState & 0x1) state|=0x4;
}
}
}
for(i=0;i<(int)joysticks.size();i++){
v=SDL_JoystickGetAxis(joysticks[i],index);
if((value==1 && v>3200) || (value==0xFF && v<-3200)){
state|=0x1;
break;
}
}
break;
case 2:
- //button
+ //Button.
if(hasEvent){
if(event.type==SDL_JOYBUTTONDOWN && event.jbutton.button==index){
state|=0x2;
}
if(event.type==SDL_JOYBUTTONUP && event.jbutton.button==index){
state|=0x4;
}
}
for(i=0;i<(int)joysticks.size();i++){
v=SDL_JoystickGetButton(joysticks[i],index);
if(v){
state|=0x1;
break;
}
}
break;
case 3:
- //hat
+ //Hat.
if(hasEvent){
if(event.type==SDL_JOYHATMOTION && event.jhat.hat==index){
if(event.jhat.value & value){
if((oldState & 0x1)==0) state|=0x2;
}else{
if(oldState & 0x1) state|=0x4;
}
}
}
for(i=0;i<(int)joysticks.size();i++){
v=SDL_JoystickGetHat(joysticks[i],index);
if(v & value){
state|=0x1;
break;
}
}
break;
}
}
return state;
}
-//update the key state, according to current SDL event, etc.
+//Update the key state, according to current SDL event, etc.
void InputManager::updateState(bool hasEvent){
for(int i=0;i<INPUTMGR_MAX;i++){
keyFlags[i]=getKeyState(keys[i],keyFlags[i],hasEvent)|getKeyState(alternativeKeys[i],keyFlags[i],hasEvent);
}
}
-//check if there is KeyDown event.
+//Check if there is KeyDown event.
bool InputManager::isKeyDownEvent(InputManagerKeys key){
return keyFlags[key]&0x2;
}
-//check if there is KeyUp event.
+//Check if there is KeyUp event.
bool InputManager::isKeyUpEvent(InputManagerKeys key){
return keyFlags[key]&0x4;
}
-//check if specified key is down.
+//Check if specified key is down.
bool InputManager::isKeyDown(InputManagerKeys key){
return keyFlags[key]&0x1;
}
-//open all joysticks.
+//Open all joysticks.
void InputManager::openAllJoysitcks(){
int i,m;
//First close previous joysticks.
closeAllJoysticks();
- //open all joysticks.
+ //Open all joysticks.
m=SDL_NumJoysticks();
for(i=0;i<m;i++){
SDL_Joystick *j=SDL_JoystickOpen(i);
if(j==NULL){
printf("ERROR: Couldn't open Joystick %d\n",i);
}else{
joysticks.push_back(j);
}
}
}
-//close all joysticks.
+//Close all joysticks.
void InputManager::closeAllJoysticks(){
for(int i=0;i<(int)joysticks.size();i++){
SDL_JoystickClose(joysticks[i]);
}
joysticks.clear();
}
diff --git a/src/InputManager.h b/src/InputManager.h
index 013882e..fa8cebb 100644
--- a/src/InputManager.h
+++ b/src/InputManager.h
@@ -1,115 +1,115 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INPUTMANAGER_H
#define INPUTMANAGER_H
#include <SDL/SDL.h>
#include <vector>
#include <string>
#include "GUIObject.h"
enum InputManagerKeys{
INPUTMGR_UP,
INPUTMGR_DOWN,
INPUTMGR_LEFT,
INPUTMGR_RIGHT,
INPUTMGR_JUMP,
INPUTMGR_ACTION,
INPUTMGR_SPACE,
INPUTMGR_CANCELRECORDING,
INPUTMGR_ESCAPE,
INPUTMGR_RESTART,
INPUTMGR_TAB,
INPUTMGR_SAVE,
INPUTMGR_LOAD,
INPUTMGR_SWAP,
INPUTMGR_TELEPORT,
INPUTMGR_SUICIDE,
INPUTMGR_SHIFT,
INPUTMGR_NEXT,
INPUTMGR_PREVIOUS,
INPUTMGR_SELECT,
INPUTMGR_MAX
};
class InputManager{
public:
InputManager();
~InputManager();
//Get and set key code of each key.
int getKeyCode(InputManagerKeys key,bool isAlternativeKey);
void setKeyCode(InputManagerKeys key,int keyCode,bool isAlternativeKey);
//Load and save key settings from config file.
void loadConfig();
void saveConfig();
//Show the config screen.
GUIObject* showConfig(int height);
//Get key name from key code
static std::string getKeyCodeName(int keyCode);
//Update the key state, according to current SDL event, etc.
void updateState(bool hasEvent);
//Check if there is KeyDown event.
bool isKeyDownEvent(InputManagerKeys key);
//Check if there is KeyUp event.
bool isKeyUpEvent(InputManagerKeys key);
//Check if specified key is down.
bool isKeyDown(InputManagerKeys key);
//Open all joysticks.
void openAllJoysitcks();
//Close all joysticks.
void closeAllJoysticks();
private:
- //the key code of each key.
+ //The key code of each key.
// - note of key code:
- // 0 means this key is disabled (??)
+ // 0 means this key is disabled
// 1 to 4095 (0xFFF) means keyboard keys,
// currently SDLKey is less than 4095
// >= 4096: bit field value means joystick.
// 0xWWXXYYZZ
// WW = joystick index. currently unused, should be 0.
// XX = joystick button type: 1-axis 2-button 3-hat, currently ball is unsupported
// YY = joystick button index. (we assume joystick has at most 256 buttons)
// ZZ = value. if type=axis then value should be 1 or 0xFF.
// if type=button then it's unused.
// if type=hat then it's SDL_HAT_LEFT, SDL_HAT_RIGHT, SDL_HAT_UP or SDL_HAT_DOWN.
int keys[INPUTMGR_MAX];
int alternativeKeys[INPUTMGR_MAX];
- //the bit-field flag array saves the key states.
+ //The bit-field flag array saves the key states.
// 0x1 means the key is down.
// 0x2 means KeyDown event.
// 0x4 means KeyUp event.
int keyFlags[INPUTMGR_MAX];
- //contains all joysticks.
+ //Contains all joysticks.
std::vector<SDL_Joystick*> joysticks;
- //internal function
+ //Internal function.
int getKeyState(int keyCode,int oldState,bool hasEvent);
};
extern InputManager inputMgr;
#endif //INPUTMANAGER_H
diff --git a/src/StatisticsManager.cpp b/src/StatisticsManager.cpp
index 62003c4..fd15b9c 100644
--- a/src/StatisticsManager.cpp
+++ b/src/StatisticsManager.cpp
@@ -1,773 +1,780 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "StatisticsManager.h"
#include "FileManager.h"
#include "TreeStorageNode.h"
#include "POASerializer.h"
#include "Functions.h"
#include "LevelPackManager.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include "libs/tinyformat/tinyformat.h"
+#ifdef __APPLE__
+#include <SDL_gfx/SDL_gfxPrimitives.h>
+#else
+#include <SDL/SDL_gfxPrimitives.h>
+#endif
using namespace std;
StatisticsManager statsMgr;
static const int achievementDisplayTime=(FPS*4500)/1000;
static const int achievementIntervalTime=achievementDisplayTime+(FPS*500)/1000;
#include "AchievementList.h"
static map<string,AchievementInfo*> avaliableAchievements;
//================================================================
StatisticsManager::StatisticsManager(){
bmDropShadow=NULL;
bmQuestionMark=NULL;
bmAchievement=NULL;
startTime=time(NULL);
tutorialLevels=0;
clear();
}
StatisticsManager::~StatisticsManager(){
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
}
void StatisticsManager::clear(){
playerTravelingDistance=shadowTravelingDistance=0.0f;
playerJumps=shadowJumps
=playerDies=shadowDies
=playerSquashed=shadowSquashed
=completedLevels=silverLevels=goldLevels
=recordTimes=switchTimes=swapTimes=saveTimes=loadTimes
=playTime=levelEditTime
=createdLevels=tutorialCompleted=tutorialGold=0;
achievements.clear();
queuedAchievements.clear();
achievementTime=0;
currentAchievement=0;
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
}
#define LOAD_STATS(var,func) { \
vector<string> &v=node.attributes[ #var ]; \
if(!v.empty() && !v[0].empty()) \
var=func(v[0].c_str()); \
}
void StatisticsManager::loadFile(const std::string& fileName){
clear();
ifstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
POASerializer serializer;
if(!serializer.readNode(file,&node,true)) return;
//load statistics
LOAD_STATS(playerTravelingDistance,atof);
LOAD_STATS(shadowTravelingDistance,atof);
LOAD_STATS(playerJumps,atoi);
LOAD_STATS(shadowJumps,atoi);
LOAD_STATS(playerDies,atoi);
LOAD_STATS(shadowDies,atoi);
LOAD_STATS(playerSquashed,atoi);
LOAD_STATS(shadowSquashed,atoi);
LOAD_STATS(recordTimes,atoi);
LOAD_STATS(switchTimes,atoi);
LOAD_STATS(swapTimes,atoi);
LOAD_STATS(saveTimes,atoi);
LOAD_STATS(loadTimes,atoi);
LOAD_STATS(playTime,atoi);
LOAD_STATS(levelEditTime,atoi);
LOAD_STATS(createdLevels,atoi);
//load achievements.
//format is: name;time,name;time,...
{
vector<string> &v=node.attributes["achievements"];
for(unsigned int i=0;i<v.size();i++){
string s=v[i];
time_t t=0;
string::size_type lps=s.find(';');
if(lps!=string::npos){
string s1=s.substr(lps+1);
s=s.substr(0,lps);
long long n;
sscanf(s1.c_str(),
#ifdef WIN32
"%I64d",
#else
"%Ld",
#endif
&n);
t=(time_t)n;
}
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(s);
if(it!=avaliableAchievements.end()){
OwnedAchievement ach={t,it->second};
achievements[it->first]=ach;
}
}
}
}
//Call when level edit is start
void StatisticsManager::startLevelEdit(){
levelEditStartTime=time(NULL);
}
//Call when level edit is end
void StatisticsManager::endLevelEdit(){
levelEditTime+=time(NULL)-levelEditStartTime;
}
//update in-game time
void StatisticsManager::updatePlayTime(){
time_t endTime=time(NULL);
playTime+=endTime-startTime;
startTime=endTime;
}
#define SAVE_STATS(var,pattern) { \
sprintf(s,pattern,var); \
node.attributes[ #var ].push_back(s); \
}
void StatisticsManager::saveFile(const std::string& fileName){
char s[64];
//update in-game time
updatePlayTime();
ofstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
//save statistics
SAVE_STATS(playerTravelingDistance,"%.2f");
SAVE_STATS(shadowTravelingDistance,"%.2f");
SAVE_STATS(playerJumps,"%d");
SAVE_STATS(shadowJumps,"%d");
SAVE_STATS(playerDies,"%d");
SAVE_STATS(shadowDies,"%d");
SAVE_STATS(playerSquashed,"%d");
SAVE_STATS(shadowSquashed,"%d");
SAVE_STATS(recordTimes,"%d");
SAVE_STATS(switchTimes,"%d");
SAVE_STATS(swapTimes,"%d");
SAVE_STATS(saveTimes,"%d");
SAVE_STATS(loadTimes,"%d");
SAVE_STATS(playTime,"%d");
SAVE_STATS(levelEditTime,"%d");
SAVE_STATS(createdLevels,"%d");
//save achievements.
//format is: name;time,name;time,...
{
vector<string>& v=node.attributes["achievements"];
for(map<string,OwnedAchievement>::iterator it=achievements.begin();it!=achievements.end();++it){
stringstream strm;
char s[32];
long long n=it->second.achievedTime;
sprintf(s,
#ifdef WIN32
"%I64d",
#else
"%Ld",
#endif
n);
strm<<it->first<<";"<<s;
v.push_back(strm.str());
}
}
POASerializer serializer;
serializer.writeNode(&node,file,true,true);
}
void StatisticsManager::loadPicture(){
//Load drop shadow picture
bmDropShadow=loadImage(getDataPath()+"gfx/dropshadow.png");
bmQuestionMark=loadImage(getDataPath()+"gfx/menu/questionmark.png");
}
void StatisticsManager::registerAchievements(){
if(!avaliableAchievements.empty()) return;
for(int i=0;achievementList[i].id!=NULL;i++){
avaliableAchievements[achievementList[i].id]=&achievementList[i];
if(achievementList[i].imageFile!=NULL){
achievementList[i].imageSurface=loadImage(getDataPath()+achievementList[i].imageFile);
}
}
}
void StatisticsManager::render(){
if(achievementTime==0 && bmAchievement==NULL && currentAchievement<(int)queuedAchievements.size()){
//create surface
bmAchievement=createAchievementSurface(queuedAchievements[currentAchievement++]);
drawGUIBox(0,0,bmAchievement->w,bmAchievement->h,bmAchievement,0xFFFFFF00);
//check if queue is empty
if(currentAchievement>=(int)queuedAchievements.size()){
queuedAchievements.clear();
currentAchievement=0;
}
//play a sound
if(getSettings()->getBoolValue("sound")){
Mix_PlayChannel(-1,achievementSound,0);
}
}
//check if we need to display achievements
if(bmAchievement){
achievementTime++;
if(achievementTime<=0){
return;
}else if(achievementTime<=5){
drawAchievement(achievementTime);
}else if(achievementTime<=achievementDisplayTime-5){
drawAchievement(5);
}else if(achievementTime<achievementDisplayTime){
drawAchievement(achievementDisplayTime-achievementTime);
}else if(achievementTime>=achievementIntervalTime){
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
achievementTime=0;
}
}
}
void StatisticsManager::newAchievement(const std::string& id,bool save){
//check avaliable achievements
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(id);
if(it==avaliableAchievements.end()) return;
//check if already have this achievement
if(save){
map<string,OwnedAchievement>::iterator it2=achievements.find(id);
if(it2!=achievements.end()) return;
OwnedAchievement ach={time(NULL),it->second};
achievements[id]=ach;
}
//add it to queue
queuedAchievements.push_back(it->second);
}
float StatisticsManager::getAchievementProgress(AchievementInfo* info){
if(!strcmp(info->id,"experienced")){
return float(completedLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"expert")){
return float(goldLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"tutorial")){
if(tutorialLevels>0)
return float(tutorialCompleted)/float(tutorialLevels)*100.0f;
else
return 0.0f;
}
if(!strcmp(info->id,"tutorialGold")){
if(tutorialLevels>0)
return float(tutorialGold)/float(tutorialLevels)*100.0f;
else
return 0.0f;
}
if(!strcmp(info->id,"create50")){
return float(createdLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"frog")){
return float(playerJumps+shadowJumps)/1000.0f*100.0f;
}
if(!strcmp(info->id,"die50")){
return float(playerDies+shadowDies)/50.0f*100.0f;
}
if(!strcmp(info->id,"die1000")){
return float(playerDies+shadowDies)/1000.0f*100.0f;
}
if(!strcmp(info->id,"suqash50")){
return float(playerSquashed+shadowSquashed)/50.0f*100.0f;
}
if(!strcmp(info->id,"travel100")){
return (playerTravelingDistance+shadowTravelingDistance)/100.0f*100.0f;
}
if(!strcmp(info->id,"travel1k")){
return (playerTravelingDistance+shadowTravelingDistance)/1000.0f*100.0f;
}
if(!strcmp(info->id,"travel10k")){
return (playerTravelingDistance+shadowTravelingDistance)/10000.0f*100.0f;
}
if(!strcmp(info->id,"travel42k")){
return (playerTravelingDistance+shadowTravelingDistance)/42195.0f*100.0f;
}
if(!strcmp(info->id,"record100")){
return float(recordTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"record1k")){
return float(recordTimes)/1000.0f*100.0f;
}
if(!strcmp(info->id,"switch100")){
return float(switchTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"switch1k")){
return float(switchTimes)/1000.0f*100.0f;
}
if(!strcmp(info->id,"swap100")){
return float(swapTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"swap1k")){
return float(swapTimes)/1000.0f*100.0f;
}
//not found
return 0.0f;
}
SDL_Surface* StatisticsManager::createAchievementSurface(AchievementInfo* info,SDL_Surface* surface,SDL_Rect* rect,bool showTip,const time_t *achievedTime){
if(info==NULL || info->id==NULL) return NULL;
//prepare text
SDL_Surface *title0=NULL,*title1=NULL;
vector<SDL_Surface*> descSurfaces;
SDL_Color fg={0,0,0};
int fontHeight=TTF_FontLineSkip(fontText);
bool showDescription=false;
bool showImage=false;
float achievementProgress=0.0f;
if(showTip){
title0=TTF_RenderUTF8_Blended(fontText,_("New achievement:"),fg);
title1=TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg);
showDescription=showImage=true;
}else if(achievedTime){
char s[128];
strftime(s,sizeof(s),"%c",localtime(achievedTime));
stringstream strm;
- tinyformat::format(strm,_("Achieved at %s"),s);
+ tinyformat::format(strm,_("Achieved on %s"),s);
title1=TTF_RenderUTF8_Blended(fontText,strm.str().c_str(),fg);
title0=TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg);
showDescription=showImage=true;
}else if(info->displayStyle==ACHIEVEMENT_HIDDEN){
title0=TTF_RenderUTF8_Blended(fontGUISmall,_("Unknown achievement"),fg);
}else{
if(info->displayStyle==ACHIEVEMENT_PROGRESS){
achievementProgress=getAchievementProgress(info);
stringstream strm;
- tinyformat::format(strm,_("Achieved %0.1f%%"),achievementProgress);
+ tinyformat::format(strm,_("Achieved %1.0f%%"),achievementProgress);
title1=TTF_RenderUTF8_Blended(fontText,strm.str().c_str(),fg);
}else{
title1=TTF_RenderUTF8_Blended(fontText,_("Not achieved"),fg);
}
title0=TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg);
showDescription= info->displayStyle==ACHIEVEMENT_ALL || info->displayStyle==ACHIEVEMENT_PROGRESS;
showImage=true;
}
if(info->description!=NULL && showDescription){
string description=_(info->description);
string::size_type lps=0,lpe;
for(;;){
lpe=description.find('\n',lps);
if(lpe==string::npos){
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps)+' ').c_str(),fg));
break;
}else{
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps,lpe-lps)+' ').c_str(),fg));
lps=lpe+1;
}
}
}
//calculate the size
int w=0,h=0,w1=8,h1=0;
if(title0!=NULL){
if(title0->w>w) w=title0->w;
h1+=title0->h;
}
if(title1!=NULL){
if(title1->w>w) w=title1->w;
h1+=title1->h;
/*//calc progress bar size
if(!showTip && !achievedTime && info->displayStyle==ACHIEVEMENT_PROGRESS){
h1+=4;
}*/
}
if(showImage){
if(info->imageSurface!=NULL){
w1+=info->r.w+8;
w+=info->r.w+8;
if(info->r.h>h1) h1=info->r.h;
}
}else{
w1+=bmQuestionMark->w+8;
w+=bmQuestionMark->w+8;
if(bmQuestionMark->h>h1) h1=bmQuestionMark->h;
}
h=h1+8;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
if(descSurfaces[i]->w>w) w=descSurfaces[i]->w;
}
}
h+=descSurfaces.size()*fontHeight;
w+=16;
h+=16;
//check if size is specified
int left=0,top=0;
if(rect!=NULL){
if(surface!=NULL){
left=rect->x;
top=rect->y;
}
if(rect->w>0) w=rect->w;
else rect->w=w;
rect->h=h;
}
//create surface if necessary
if(surface==NULL){
surface=SDL_CreateRGBSurface(SCREEN_FLAGS,w,h,
screen->format->BitsPerPixel,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0);
}
//draw background
SDL_Rect r={left,top,w,h};
if(showTip || achievedTime){
SDL_FillRect(surface,&r,SDL_MapRGB(surface->format,255,255,255));
}else{
SDL_FillRect(surface,&r,SDL_MapRGB(surface->format,192,192,192));
}
//draw picture
if(showImage){
if(info->imageSurface!=NULL){
SDL_Rect r={left+8,top+8+(h1-info->r.h)/2,0,0};
SDL_BlitSurface(info->imageSurface,&info->r,surface,&r);
}
}else{
SDL_Rect r={left+8,top+8+(h1-bmQuestionMark->h)/2,0,0};
SDL_BlitSurface(bmQuestionMark,NULL,surface,&r);
}
//draw text
h=8;
if(title0!=NULL){
SDL_Rect r={left+w1,top+h,0,0};
SDL_BlitSurface(title0,NULL,surface,&r);
h+=title0->h;
}
if(title1!=NULL){
SDL_Rect r={left+w1,top+h,0,0};
- //draw progress bar
+ //Draw progress bar.
if(!showTip && !achievedTime && info->displayStyle==ACHIEVEMENT_PROGRESS){
+ //Draw borders.
SDL_Rect r1={r.x,r.y,w-8-r.x,title1->h};
- SDL_FillRect(surface,&r1,SDL_MapRGB(surface->format,96,96,96));
+ drawGUIBox(r1.x,r1.y,r1.w,r1.h,surface,0x1D);
+
+ //Draw progress.
r1.x++;
r1.y++;
- r1.w-=2;
- r1.h-=2;
- SDL_FillRect(surface,&r1,SDL_MapRGB(surface->format,216,216,216));
r1.w=int(achievementProgress/100.0f*float(r1.w));
- SDL_FillRect(surface,&r1,SDL_MapRGB(surface->format,144,144,144));
+ r1.h-=3;
+ boxRGBA(surface,r1.x,r1.y,r1.x+r1.w,r1.y+r1.h,0,0,0,100);
//???
r.x+=2;
r.y+=2;
}
-
+
+ //Draw text.
SDL_BlitSurface(title1,NULL,surface,&r);
}
h=h1+16;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
SDL_Rect r={left+8,top+h+i*fontHeight,0,0};
SDL_BlitSurface(descSurfaces[i],NULL,surface,&r);
}
}
//clean up
if(title0) SDL_FreeSurface(title0);
if(title1) SDL_FreeSurface(title1);
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
SDL_FreeSurface(descSurfaces[i]);
}
}
//over
return surface;
}
void StatisticsManager::drawAchievement(int alpha){
if(bmAchievement==NULL) return;
if(alpha<=0) return;
if(alpha>5) alpha=5;
SDL_Rect r={screen->w-32-bmAchievement->w,32,
bmAchievement->w,bmAchievement->h};
//draw the surface
SDL_SetAlpha(bmAchievement,SDL_SRCALPHA,alpha*40);
SDL_BlitSurface(bmAchievement,NULL,screen,&r);
//draw drop shadow - corner
{
int w1=r.w/2,w2=r.w-w1,h1=r.h/2,h2=r.h-h1;
if(w1>16) w1=16;
if(w2>16) w2=16;
if(h1>16) h1=16;
if(h2>16) h2=16;
int x=(5-alpha)*64;
//top-left
SDL_Rect r1={x,0,w1+16,h1+16},r2={r.x-16,r.y-16,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//top-right
r1.x=x+48-w2;r1.w=w2+16;r2.x=r.x+r.w-w2;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom-right
r1.y=48-h2;r1.h=h2+16;r2.y=r.y+r.h-h2;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom-left
r1.x=x;r1.w=w1+16;r2.x=r.x-16;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
}
//draw drop shadow - border
int i=r.w-32;
while(i>0){
int ii=i>128?128:i;
//top
SDL_Rect r1={0,256-alpha*16,ii,16},r2={r.x+r.w-16-i,r.y-16,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom
r1.x=128;r2.y=r.y+r.h;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
i-=ii;
}
i=r.h-32;
while(i>0){
int ii=i>128?128:i;
//top
SDL_Rect r1={512-alpha*16,0,16,ii},r2={r.x-16,r.y+r.h-16-i,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom
r1.y=128;r2.x=r.x+r.w;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
i-=ii;
}
}
void StatisticsManager::reloadCompletedLevelsAndAchievements(){
completedLevels=silverLevels=goldLevels=0;
LevelPackManager *lpm=getLevelPackManager();
vector<string> v=lpm->enumLevelPacks();
bool tutorial=false,tutorialIsGold=false;
for(unsigned int i=0;i<v.size();i++){
string& s=v[i];
LevelPack *levels=lpm->getLevelPack(s);
levels->loadProgress(getUserPath(USER_DATA)+"progress/"+s+".progress");
bool b=false;
if(s=="tutorial"){
tutorialLevels=levels->getLevelCount();
tutorialCompleted=tutorialGold=0;
b=tutorial=tutorialIsGold=true;
}
for(int n=0,m=levels->getLevelCount();n<m;n++){
LevelPack::Level *lv=levels->getLevel(n);
int medal=lv->won;
if(medal){
if(lv->targetTime<0 || lv->time<=lv->targetTime)
medal++;
if(lv->targetRecordings<0 || lv->recordings<=lv->targetRecordings)
medal++;
completedLevels++;
if(b) tutorialCompleted++;
if(medal==2) silverLevels++;
if(medal==3){
goldLevels++;
if(b) tutorialGold++;
}
if(medal!=3 && b) tutorialIsGold=false;
}else if(b){
tutorial=tutorialIsGold=false;
}
}
}
//upadte achievements
updateLevelAchievements();
updateTutorialAchievementsInternal((tutorial?1:0)|(tutorialIsGold?2:0));
}
void StatisticsManager::reloadOtherAchievements(){
int i;
if(playTime>=7200) newAchievement("addicted");
if(playTime>=86400) newAchievement("loyalFan");
if(levelEditTime>=7200) newAchievement("constructor");
if(levelEditTime>=86400) newAchievement("constructor2");
if(createdLevels>=1) newAchievement("create1");
if(createdLevels>=50) newAchievement("create50");
i=playerJumps+shadowJumps;
if(i>=1000) newAchievement("frog");
i=playerDies+shadowDies;
if(i>=1) newAchievement("die1");
if(i>=50) newAchievement("die50");
if(i>=1000) newAchievement("die1000");
i=playerSquashed+shadowSquashed;
if(i>=1) newAchievement("squash1");
if(i>=50) newAchievement("squash50");
float d=playerTravelingDistance+shadowTravelingDistance;
if(d>=100.0f) newAchievement("travel100");
if(d>=1000.0f) newAchievement("travel1k");
if(d>=10000.0f) newAchievement("travel10k");
if(d>=42195.0f) newAchievement("travel42k");
if(recordTimes>=100) newAchievement("record100");
if(recordTimes>=1000) newAchievement("record1k");
if(switchTimes>=100) newAchievement("switch100");
if(switchTimes>=1000) newAchievement("switch1k");
if(swapTimes>=100) newAchievement("swap100");
if(swapTimes>=1000) newAchievement("swap1k");
if(saveTimes>=1000) newAchievement("save1k");
if(loadTimes>=1000) newAchievement("load1k");
if(version.find("Development")!=string::npos) newAchievement("programmer");
}
//Update level specified achievements.
//Make sure the completed level count is correct.
void StatisticsManager::updateLevelAchievements(){
if(completedLevels>=1) newAchievement("newbie");
if(goldLevels>=1) newAchievement("goodjob");
if(completedLevels>=50) newAchievement("experienced");
if(goldLevels>=50) newAchievement("expert");
}
//Update tutorial specified achievements.
//Make sure the level progress of tutorial is correct.
void StatisticsManager::updateTutorialAchievements(){
//find tutorial level pack
LevelPackManager *lpm=getLevelPackManager();
LevelPack *levels=lpm->getLevelPack("tutorial");
if(levels==NULL) return;
bool tutorial=true,tutorialIsGold=true;
tutorialLevels=levels->getLevelCount();
tutorialCompleted=tutorialGold=0;
for(int n=0,m=levels->getLevelCount();n<m;n++){
LevelPack::Level *lv=levels->getLevel(n);
int medal=lv->won;
if(medal){
if(lv->targetTime<0 || lv->time<=lv->targetTime)
medal++;
if(lv->targetRecordings<0 || lv->recordings<=lv->targetRecordings)
medal++;
tutorialCompleted++;
if(medal!=3) tutorialIsGold=false;
else tutorialGold++;
}else{
tutorial=tutorialIsGold=false;
break;
}
}
//upadte achievements
updateTutorialAchievementsInternal((tutorial?1:0)|(tutorialIsGold?2:0));
}
//internal function
//flags: a bit-field value indicates which achievements we have.
void StatisticsManager::updateTutorialAchievementsInternal(int flags){
if(flags&1) newAchievement("tutorial");
if(flags&2) newAchievement("tutorialGold");
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, May 15, 3:13 PM (5 h, 11 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63938
Default Alt Text
(202 KB)
Attached To
Mode
R79 meandmyshadow
Attached
Detach File
Event Timeline