Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
202 KB
Referenced Files
None
Subscribers
None
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

Mime Type
text/x-diff
Expires
Fri, May 15, 3:13 PM (7 h, 10 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63938
Default Alt Text
(202 KB)

Event Timeline