Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
116 KB
Referenced Files
None
Subscribers
None
diff --git a/data/levelpacks/default/locale/messages.pot b/data/levelpacks/default/locale/messages.pot
new file mode 100644
index 0000000..91369a6
--- /dev/null
+++ b/data/levelpacks/default/locale/messages.pot
@@ -0,0 +1,179 @@
+# 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: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-04-01 17:56+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"
+
+#: ./default/map04.map:1
+msgid "Double trouble"
+msgstr ""
+
+#: ./default/3.map:1
+msgid "Running in the Sky"
+msgstr ""
+
+#: ./default/Remote.map:1
+msgid "Remote control"
+msgstr ""
+
+#: ./default/Volcano.map:1
+msgid "Volcano"
+msgstr ""
+
+#: ./default/Switches.map:1
+msgid "Switches"
+msgstr ""
+
+#: ./default/QuickSwap.map:1
+msgid "Quick swap"
+msgstr ""
+
+#: ./default/levels.lst:1
+msgid "Default"
+msgstr ""
+
+#: ./default/5.map:1
+msgid "stopping the spikes"
+msgstr ""
+
+#: ./default/Sweeper.map:1
+msgid "Sweeper"
+msgstr ""
+
+#: ./default/1.map:1
+msgid "Building Teamwork"
+msgstr ""
+
+#: ./default/map02.map:1
+msgid "Snail"
+msgstr ""
+
+#: ./default/2.map:1
+msgid "Tricky Jumping"
+msgstr ""
+
+#: ./default/Regroup.map:1
+msgid "Regroup"
+msgstr ""
+
+#: ./default/map05.map:1
+msgid "Wall breaking"
+msgstr ""
+
+#: ./default/Skyscrapers.map:1
+msgid "Skyscrapers"
+msgstr ""
+
+#: ./default/map01.map:1
+msgid "Simple"
+msgstr ""
+
+#: ./default/4.map:1
+msgid "Both Up and Down"
+msgstr ""
+
+#: ./default/map03.map:1
+msgid "Spiky travel"
+msgstr ""
+
+#: ./default/Towers.map:1
+msgid "Towers"
+msgstr ""
+
+#: ./default/Timing.map:1
+msgid "Timing"
+msgstr ""
+
+#: ./default/.svn/text-base/Timing.map.svn-base:1
+msgid "Timing"
+msgstr ""
+
+#: ./default/.svn/text-base/4.map.svn-base:1
+msgid "Both Up and Down"
+msgstr ""
+
+#: ./default/.svn/text-base/map05.map.svn-base:1
+msgid "Wall breaking"
+msgstr ""
+
+#: ./default/.svn/text-base/Remote.map.svn-base:1
+msgid "Remote control"
+msgstr ""
+
+#: ./default/.svn/text-base/1.map.svn-base:1
+msgid "Building Teamwork"
+msgstr ""
+
+#: ./default/.svn/text-base/3.map.svn-base:1
+msgid "Running in the Sky"
+msgstr ""
+
+#: ./default/.svn/text-base/Switches.map.svn-base:1
+msgid "Switches"
+msgstr ""
+
+#: ./default/.svn/text-base/map04.map.svn-base:1
+msgid "Double trouble"
+msgstr ""
+
+#: ./default/.svn/text-base/QuickSwap.map.svn-base:1
+msgid "Quick swap"
+msgstr ""
+
+#: ./default/.svn/text-base/Regroup.map.svn-base:1
+msgid "Regroup"
+msgstr ""
+
+#: ./default/.svn/text-base/levels.lst.svn-base:1
+msgid "Default"
+msgstr ""
+
+#: ./default/.svn/text-base/Towers.map.svn-base:1
+msgid "Towers"
+msgstr ""
+
+#: ./default/.svn/text-base/2.map.svn-base:1
+msgid "Tricky Jumping"
+msgstr ""
+
+#: ./default/.svn/text-base/Skyscrapers.map.svn-base:1
+msgid "Skyscrapers"
+msgstr ""
+
+#: ./default/.svn/text-base/Sweeper.map.svn-base:1
+msgid "Sweeper"
+msgstr ""
+
+#: ./default/.svn/text-base/map01.map.svn-base:1
+msgid "Simple"
+msgstr ""
+
+#: ./default/.svn/text-base/5.map.svn-base:1
+msgid "stopping the spikes"
+msgstr ""
+
+#: ./default/.svn/text-base/map03.map.svn-base:1
+msgid "Spiky travel"
+msgstr ""
+
+#: ./default/.svn/text-base/Volcano.map.svn-base:1
+msgid "Volcano"
+msgstr ""
+
+#: ./default/.svn/text-base/map02.map.svn-base:1
+msgid "Snail"
+msgstr ""
+
diff --git a/data/levelpacks/tutorial/locale/messages.pot b/data/levelpacks/tutorial/locale/messages.pot
new file mode 100644
index 0000000..325a37c
--- /dev/null
+++ b/data/levelpacks/tutorial/locale/messages.pot
@@ -0,0 +1,563 @@
+# 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: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-04-01 17:56+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"
+
+#: tutorial/tut03.map:1
+msgid "Jumping around"
+msgstr ""
+
+#: tutorial/tut03.map:13
+msgid "See those gaps over there?\nReach the finish without falling."
+msgstr ""
+
+#: tutorial/tut03.map:34
+msgid "Good job!"
+msgstr ""
+
+#: tutorial/tut04.map:1
+msgid "First spikes"
+msgstr ""
+
+#: tutorial/tut04.map:19
+msgid "Spikes are deadly.\nDon't touch them!"
+msgstr ""
+
+#: tutorial/tut16.map:1
+msgid "The switch"
+msgstr ""
+
+#: tutorial/tut16.map:14
+msgid "There's another type of trigger: the switch.\nGo and see what happens when you press\nthe switch."
+msgstr ""
+
+#: tutorial/tut06.map:1
+msgid "Shadow walk"
+msgstr ""
+
+#: tutorial/tut06.map:23
+msgid "TIP:\nAlthough you can't jump on those blocks\nyou can record the jumps for your shadow."
+msgstr ""
+
+#: tutorial/tut06.map:27
+msgid "Only your shadow can stand on those shadow\nblocks. Get him up there."
+msgstr ""
+
+#: tutorial/tut23.map:1
+msgid "Shadow swap"
+msgstr ""
+
+#: tutorial/tut23.map:22
+msgid "You need your shadow to reach the exit.\nMake use of the swapper to get him down (or \nto get yourself down)."
+msgstr ""
+
+#: tutorial/tut23.map:33
+msgid "TIP:\nWhen your shadow is trapped stand on the\nright side of the shadow blocks. Now record \nthe down key and let your shadow mimic."
+msgstr ""
+
+#: tutorial/tut01.map:1
+msgid "Walk in the park"
+msgstr ""
+
+#: tutorial/tut01.map:18
+msgid "Welcome to Me and My Shadow.\nUse the arrow keys to walk to the exit.\n\nGood luck!"
+msgstr ""
+
+#: tutorial/tut08.map:1
+msgid "Teamwork"
+msgstr ""
+
+#: tutorial/tut08.map:21
+msgid "You and your shadow need to work together.\nMove your shadow towards the wall and jump\non him."
+msgstr ""
+
+#: tutorial/tut22.map:1
+msgid "Swappoints"
+msgstr ""
+
+#: tutorial/tut22.map:13
+msgid "Now it's time for something completely\ndifferent: swappoints. When you or your\nshadow activate them you'll swap places."
+msgstr ""
+
+#: tutorial/tut22.map:41
+msgid "TIP:\nLet your shadow walk over the shadow bridge.\nThen activate the swappoint."
+msgstr ""
+
+#: tutorial/levels.lst~:1
+msgid "You have finished the tutorial!"
+msgstr ""
+
+#: tutorial/levels.lst~:2
+msgid "Step to step introduction"
+msgstr ""
+
+#: tutorial/tut14.map:1
+msgid "Conveyor madness"
+msgstr ""
+
+#: tutorial/tut14.map:72
+msgid "When standing on conveyor belts you'll\nmove without walking."
+msgstr ""
+
+#: tutorial/tut14.map:81
+msgid "Let the shadow finish the level by walking to\nthe finish. But don't stand still because your\nshadow will move back to the wall."
+msgstr ""
+
+#: tutorial/tut05.map:1
+msgid "Shadow"
+msgstr ""
+
+#: tutorial/tut05.map:29
+msgid "You can't reach the exit, but your shadow can.\nPress space to record your moves.\nPress space once again to let your shadow\nmimic your recording."
+msgstr ""
+
+#: tutorial/tut05.map:32
+msgid "TIP:\nThink what moves your shadow has to make.\nThen let your character record those moves.\nYou can break it down into smaller recordings."
+msgstr ""
+
+#: tutorial/levels.lst:1
+msgid "You have finished the tutorial!"
+msgstr ""
+
+#: tutorial/levels.lst:2
+msgid "Step by step introduction"
+msgstr ""
+
+#: tutorial/tut12.map:1
+msgid "Moving shadow blocks"
+msgstr ""
+
+#: tutorial/tut12.map:17
+msgid "Now you'll need to help your shadow cross.\n"
+msgstr ""
+
+#: tutorial/tut20.map:1
+msgid "One-way portal"
+msgstr ""
+
+#: tutorial/tut20.map:18
+msgid "There are always two portals in a level.\nUnless they are broken, but this doesn't mean\nyou can always go back the way you came."
+msgstr ""
+
+#: tutorial/tut20.map:48
+msgid "Now try to go through the portal again."
+msgstr ""
+
+#: tutorial/tut02.map:1
+msgid "First jumps"
+msgstr ""
+
+#: tutorial/tut02.map:18
+msgid "You can jump using the up key.\nTry jumping over these blocks."
+msgstr ""
+
+#: tutorial/tut15.map:1
+msgid "Triggering"
+msgstr ""
+
+#: tutorial/tut15.map:44
+msgid "This looks like a normal row of blocks.\nBut watch what happens when you push the\nbutton in front of you."
+msgstr ""
+
+#: tutorial/tut10.map:1
+msgid "Checkpoints"
+msgstr ""
+
+#: tutorial/tut10.map:19
+msgid "You can save your progress in a level with\ncheckpoints. You can restore them at any\ntime using the F3 button."
+msgstr ""
+
+#: tutorial/tut10.map:35
+msgid "TIP:\nTry to get your shadow here as well.\nIf you stand on him you can jump further."
+msgstr ""
+
+#: tutorial/tut09.map:1
+msgid "Fragile"
+msgstr ""
+
+#: tutorial/tut09.map:14
+msgid "Those blocks are fragile.\nIf you step on them too often they'll break."
+msgstr ""
+
+#: tutorial/tut13.map:1
+msgid "Moving spikes"
+msgstr ""
+
+#: tutorial/tut13.map:18
+msgid "Watch out for the moving spikes."
+msgstr ""
+
+#: tutorial/tut07.map:1
+msgid "Shadow challenge"
+msgstr ""
+
+#: tutorial/tut07.map:19
+msgid "Spikes are not only deadly for you,\nbut also for your shadow."
+msgstr ""
+
+#: tutorial/tut07.map:33
+msgid "If your shadow dies you'll have to restart.\nRestart the game by pressing the 'R' key."
+msgstr ""
+
+#: tutorial/tut24.map:1
+msgid "Warning"
+msgstr ""
+
+#: tutorial/tut24.map:18
+msgid "That's all there is.\nNow it's time to put your skills to the test.\nEnter the exit to go to the last level.\nGood luck!"
+msgstr ""
+
+#: tutorial/tut19.map:1
+msgid "First portals"
+msgstr ""
+
+#: tutorial/tut19.map:39
+msgid "Now it's time to check out the portals.\nTo get to the exit you'll have to take the portal.\nWalk to it and press the down arrow to\nactivate."
+msgstr ""
+
+#: tutorial/tut19.map:42
+msgid "NOTE:\nYou can go back by entering this portal.\nIt is however a bit different, you don't have to\npress the down key, but walk in it."
+msgstr ""
+
+#: tutorial/tut21.map:1
+msgid "Portal mayhem"
+msgstr ""
+
+#: tutorial/tut21.map:16
+msgid "Portals point to another portal or to nothing.\nTry to reach the exit in this portal mayhem."
+msgstr ""
+
+#: tutorial/tut21.map:156
+msgid "Now choose one of the two."
+msgstr ""
+
+#: tutorial/tut21.map:187
+msgid "A dead end, you'd better go back and choose\nthe other portal."
+msgstr ""
+
+#: tutorial/tut18.map:1
+msgid "Stop trigger"
+msgstr ""
+
+#: tutorial/tut18.map:14
+msgid "This trigger will deactivate the moving block.\nTry to stop it at the right moment.\nYou can only do this once, so if it fails you'll\nhave to reset the level with the 'R' key."
+msgstr ""
+
+#: tutorial/tut11.map:1
+msgid "Moving blocks"
+msgstr ""
+
+#: tutorial/tut11.map:17
+msgid "Until now the levels were static.\nThere are however moving blocks."
+msgstr ""
+
+#: tutorial/tut17.map:1
+msgid "Toggle trigger"
+msgstr ""
+
+#: tutorial/tut17.map:14
+msgid "You've only seen triggers that activate other\nblocks, but they can also deactivate or toggle\nthem."
+msgstr ""
+
+#: tutorial/tut25.map:1
+msgid "Final"
+msgstr ""
+
+#: tutorial/tut25.map:13
+msgid "The very best of luck!"
+msgstr ""
+
+#: tutorial/tut25.map:46
+msgid "TIP:\nTry to get your shadow in front of the shadow\nwall. If he falls down you'd better restart."
+msgstr ""
+
+#: tutorial/tut25.map:318
+msgid "You have made it!"
+msgstr ""
+
+#: tutorial/tut25.map:459
+msgid "Where could your shadow be?"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut20.map.svn-base:1
+msgid "One-way portal"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut20.map.svn-base:18
+msgid "There are always two portals in a level.\nUnless they are broken, but this doesn't mean\nyou can always go back the way you came."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut20.map.svn-base:48
+msgid "Now try to go through the portal again."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut19.map.svn-base:1
+msgid "First portals"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut19.map.svn-base:39
+msgid "Now it's time to check out the portals.\nTo get to the exit you'll have to take the portal.\nWalk to it and press the down arrow to\nactivate."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut19.map.svn-base:42
+msgid "NOTE:\nYou can go back by entering this portal.\nIt is however a bit different, you don't have to\npress the down key, but walk in it."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut14.map.svn-base:1
+msgid "Conveyor madness"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut14.map.svn-base:72
+msgid "When standing on conveyor belts you'll\nmove without walking."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut14.map.svn-base:81
+msgid "Let the shadow finish the level by walking to\nthe finish. But don't stand still because your\nshadow will move back to the wall."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut12.map.svn-base:1
+msgid "Moving shadow blocks"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut12.map.svn-base:17
+msgid "Now you'll need to help your shadow cross.\n"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut18.map.svn-base:1
+msgid "Stop trigger"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut18.map.svn-base:14
+msgid "This trigger will deactivate the moving block.\nTry to stop it at the right moment.\nYou can only do this once, so if it fails you'll\nhave to reset the level with the 'R' key."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut08.map.svn-base:1
+msgid "Teamwork"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut08.map.svn-base:21
+msgid "You and your shadow need to work together.\nMove your shadow towards the wall and jump\non him."
+msgstr ""
+
+#: tutorial/.svn/text-base/levels.lst.svn-base:1
+msgid "You have finished the tutorial!"
+msgstr ""
+
+#: tutorial/.svn/text-base/levels.lst.svn-base:2
+msgid "Step by step introduction"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut13.map.svn-base:1
+msgid "Moving spikes"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut13.map.svn-base:18
+msgid "Watch out for the moving spikes."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut11.map.svn-base:1
+msgid "Moving blocks"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut11.map.svn-base:17
+msgid "Until now the levels were static.\nThere are however moving blocks."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut16.map.svn-base:1
+msgid "The switch"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut16.map.svn-base:14
+msgid "There's another type of trigger: the switch.\nGo and see what happens when you press\nthe switch."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut06.map.svn-base:1
+msgid "Shadow walk"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut06.map.svn-base:23
+msgid "TIP:\nAlthough you can't jump on those blocks\nyou can record the jumps for your shadow."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut06.map.svn-base:27
+msgid "Only your shadow can stand on those shadow\nblocks. Get him up there."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut02.map.svn-base:1
+msgid "First jumps"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut02.map.svn-base:18
+msgid "You can jump using the up key.\nTry jumping over these blocks."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut21.map.svn-base:1
+msgid "Portal mayhem"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut21.map.svn-base:16
+msgid "Portals point to another portal or to nothing.\nTry to reach the exit in this portal mayhem."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut21.map.svn-base:156
+msgid "Now choose one of the two."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut21.map.svn-base:187
+msgid "A dead end, you'd better go back and choose\nthe other portal."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut23.map.svn-base:1
+msgid "Shadow swap"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut23.map.svn-base:22
+msgid "You need your shadow to reach the exit.\nMake use of the swapper to get him down (or \nto get yourself down)."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut23.map.svn-base:33
+msgid "TIP:\nWhen your shadow is trapped stand on the\nright side of the shadow blocks. Now record \nthe down key and let your shadow mimic."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut10.map.svn-base:1
+msgid "Checkpoints"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut10.map.svn-base:19
+msgid "You can save your progress in a level with\ncheckpoints. You can restore them at any\ntime using the F3 button."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut10.map.svn-base:35
+msgid "TIP:\nTry to get your shadow here as well.\nIf you stand on him you can jump further."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut04.map.svn-base:1
+msgid "First spikes"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut04.map.svn-base:19
+msgid "Spikes are deadly.\nDon't touch them!"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut15.map.svn-base:1
+msgid "Triggering"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut15.map.svn-base:44
+msgid "This looks like a normal row of blocks.\nBut watch what happens when you push the\nbutton in front of you."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut17.map.svn-base:1
+msgid "Toggle trigger"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut17.map.svn-base:14
+msgid "You've only seen triggers that activate other\nblocks, but they can also deactivate or toggle\nthem."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut05.map.svn-base:1
+msgid "Shadow"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut05.map.svn-base:29
+msgid "You can't reach the exit, but your shadow can.\nPress space to record your moves.\nPress space once again to let your shadow\nmimic your recording."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut05.map.svn-base:32
+msgid "TIP:\nThink what moves your shadow has to make.\nThen let your character record those moves.\nYou can break it down into smaller recordings."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut25.map.svn-base:1
+msgid "Final"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut25.map.svn-base:13
+msgid "The very best of luck!"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut25.map.svn-base:46
+msgid "TIP:\nTry to get your shadow in front of the shadow\nwall. If he falls down you'd better restart."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut25.map.svn-base:318
+msgid "You have made it!"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut25.map.svn-base:459
+msgid "Where could your shadow be?"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut01.map.svn-base:1
+msgid "Walk in the park"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut01.map.svn-base:18
+msgid "Welcome to Me and My Shadow.\nUse the arrow keys to walk to the exit.\n\nGood luck!"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut07.map.svn-base:1
+msgid "Shadow challenge"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut07.map.svn-base:19
+msgid "Spikes are not only deadly for you,\nbut also for your shadow."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut07.map.svn-base:33
+msgid "If your shadow dies you'll have to restart.\nRestart the game by pressing the 'R' key."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut09.map.svn-base:1
+msgid "Fragile"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut09.map.svn-base:14
+msgid "Those blocks are fragile.\nIf you step on them too often they'll break."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut24.map.svn-base:1
+msgid "Warning"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut24.map.svn-base:18
+msgid "That's all there is.\nNow it's time to put your skills to the test.\nEnter the exit to go to the last level.\nGood luck!"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut22.map.svn-base:1
+msgid "Swappoints"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut22.map.svn-base:13
+msgid "Now it's time for something completely\ndifferent: swappoints. When you or your\nshadow activate them you'll swap places."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut22.map.svn-base:41
+msgid "TIP:\nLet your shadow walk over the shadow bridge.\nThen activate the swappoint."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut03.map.svn-base:1
+msgid "Jumping around"
+msgstr ""
+
+#: tutorial/.svn/text-base/tut03.map.svn-base:13
+msgid "See those gaps over there?\nReach the finish without falling."
+msgstr ""
+
+#: tutorial/.svn/text-base/tut03.map.svn-base:34
+msgid "Good job!"
+msgstr ""
+
diff --git a/src/Game.cpp b/src/Game.cpp
index 8b5c895..6c36f00 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,1193 +1,1194 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include "GameState.h"
#include "Globals.h"
#include "Functions.h"
#include "FileManager.h"
#include "GameObjects.h"
#include "ThemeManager.h"
#include "Objects.h"
#include "Game.h"
#include "TreeStorageNode.h"
#include "POASerializer.h"
#include "InputManager.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <algorithm>
#include <locale>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libs/tinyformat/tinyformat.h"
using namespace std;
const char* Game::blockName[TYPE_MAX]={"Block","PlayerStart","ShadowStart",
"Exit","ShadowBlock","Spikes",
"Checkpoint","Swap","Fragile",
"MovingBlock","MovingShadowBlock","MovingSpikes",
"Teleporter","Button","Switch",
"ConveyorBelt","ShadowConveyorBelt","NotificationBlock",
};
map<string,int> Game::blockNameMap;
string Game::recordFile;
Game::Game(bool loadLevel):isReset(false)
,currentLevelNode(NULL)
,customTheme(NULL)
,background(NULL)
,won(false)
,interlevel(false)
,gameTipIndex(0)
,time(0),timeSaved(0)
,recordings(0),recordingsSaved(0)
,shadowCam(false)
,player(this),shadow(this),objLastCheckPoint(NULL){
//Reserve the memory for the GameObject tips.
memset(bmTips,0,sizeof(bmTips));
action=loadImage(getDataPath()+"gfx/actions.png");
medals=loadImage(getDataPath()+"gfx/medals.png");
//Check if we should load record file.
if(!recordFile.empty()){
loadRecord(recordFile.c_str());
recordFile.clear();
return;
}
//If we should load the level then load it.
if(loadLevel){
this->loadLevel(levels->getLevelpackPath()+levels->getLevelFile());
levels->saveLevelProgress();
}
}
Game::~Game(){
//Simply call our destroy method.
destroy();
}
void Game::destroy(){
//Loop through the levelObjects and delete them.
for(unsigned int i=0;i<levelObjects.size();i++)
delete levelObjects[i];
//Done now clear the levelObjects vector.
levelObjects.clear();
//Clear the name and the editor data.
levelName.clear();
levelFile.clear();
editorData.clear();
//Loop through the tips.
for(int i=0;i<TYPE_MAX;i++){
//If it exist free the SDL_Surface.
if(bmTips[i])
SDL_FreeSurface(bmTips[i]);
}
memset(bmTips,0,sizeof(bmTips));
//Remove everything from the themeManager.
background=NULL;
if(customTheme)
objThemes.removeTheme();
customTheme=NULL;
//delete current level (if any)
if(currentLevelNode){
delete currentLevelNode;
currentLevelNode=NULL;
}
//Reset the time.
time=timeSaved=0;
recordings=recordingsSaved=0;
}
void Game::loadLevelFromNode(TreeStorageNode* obj,const string& fileName){
//Make sure there's nothing left from any previous levels.
destroy();
//set current level to loaded one.
currentLevelNode=obj;
//Temp var used for block locations.
SDL_Rect box;
//Set the level dimensions to the default, it will probably be changed by the editorData,
//but 800x600 is a fallback.
LEVEL_WIDTH=800;
LEVEL_HEIGHT=600;
//Load the additional data.
for(map<string,vector<string> >::iterator i=obj->attributes.begin();i!=obj->attributes.end();i++){
if(i->first=="size"){
//We found the size attribute.
if(i->second.size()>=2){
//Set the dimensions of the level.
LEVEL_WIDTH=atoi(i->second[0].c_str());
LEVEL_HEIGHT=atoi(i->second[1].c_str());
}
}else if(i->second.size()>0){
//Any other data will be put into the editorData.
editorData[i->first]=i->second[0];
}
}
//Get the theme.
{
//If a theme is configured then load it.
string theme=processFileName(getSettings()->getValue("theme"));
//Check if it isn't the default theme, because if it is it's already loaded.
if(fileNameFromPath(theme)!="Cloudscape") {
customTheme=objThemes.appendThemeFromFile(theme+"/theme.mnmstheme");
}
//Check if level themes are enabled.
if(getSettings()->getBoolValue("leveltheme")) {
string &s=editorData["theme"];
if(!s.empty()){
customTheme=objThemes.appendThemeFromFile(processFileName(theme)+"/theme.mnmstheme");
}
}
//Set the Appearance of the player and the shadow.
objThemes.getCharacter(false)->createInstance(&player.appearance);
objThemes.getCharacter(true)->createInstance(&shadow.appearance);
}
for(unsigned int i=0;i<obj->subNodes.size();i++){
TreeStorageNode* obj1=obj->subNodes[i];
if(obj1==NULL) continue;
if(obj1->name=="tile" && obj1->value.size()>=3){
int objectType=blockNameMap[obj1->value[0]];
box.x=atoi(obj1->value[1].c_str());
box.y=atoi(obj1->value[2].c_str());
map<string,string> obj;
for(map<string,vector<string> >::iterator i=obj1->attributes.begin();i!=obj1->attributes.end();i++){
if(i->second.size()>0) obj[i->first]=i->second[0];
}
levelObjects.push_back( new Block ( box.x, box.y, objectType, this) );
levelObjects.back()->setEditorData(obj);
}
}
//Set the levelName to the name of the current level.
levelName=editorData["name"];
levelFile=fileName;
//Some extra stuff only needed when not in the levelEditor.
if(stateID!=STATE_LEVEL_EDITOR){
//We create a text with the text "Level <levelno> <levelName>".
//It will be shown in the left bottom corner of the screen.
string s;
if (levels->getLevelCount()>1){
s=tfm::format(_("Level %d %s"),levels->getCurrentLevel()+1,editorData["name"]);
}
SDL_Color fg={0,0,0,0};
SDL_Color bg={255,255,255,0};
bmTips[0]=TTF_RenderUTF8_Shaded(fontText,s.c_str(),fg,bg);
if(bmTips[0])
SDL_SetAlpha(bmTips[0],SDL_SRCALPHA,160);
}
//Get the background
background=objThemes.getBackground();
if(background)
background->resetAnimation(true);
}
void Game::loadLevel(string fileName){
//Create a TreeStorageNode that will hold the loaded data.
TreeStorageNode *obj=new TreeStorageNode();
{
POASerializer objSerializer;
string s=fileName;
//Parse the file.
if(!objSerializer.loadNodeFromFile(s.c_str(),obj,true)){
cout<<"Can't load level file "<<s<<endl;
delete obj;
return;
}
}
//Now call another function.
loadLevelFromNode(obj,fileName);
}
void Game::saveRecord(const char* fileName){
//check if current level is NULL (which should be impossible)
if(currentLevelNode==NULL) return;
TreeStorageNode obj;
POASerializer objSerializer;
//put current level to the node.
currentLevelNode->name="map";
obj.subNodes.push_back(currentLevelNode);
//serialize the game record using RLE compression.
#define PUSH_BACK \
if(j>0){ \
if(j>1){ \
sprintf(c,"%d*%d",last,j); \
}else{ \
sprintf(c,"%d",last); \
} \
v.push_back(c); \
}
vector<string> &v=obj.attributes["record"];
vector<int> *record=player.getRecord();
char c[64];
int i,j=0,last;
for(i=0;i<(int)record->size();i++){
int currentKey=(*record)[i];
if(j==0 || currentKey!=last){
PUSH_BACK;
last=currentKey;
j=1;
}else{
j++;
}
}
PUSH_BACK;
#undef PUSH_BACK
#ifdef RECORD_FILE_DEBUG
//add record file debug data.
{
obj.attributes["recordKeyPressLog"].push_back(player.keyPressLog());
vector<SDL_Rect> &playerPosition=player.playerPosition();
string s;
char c[32];
sprintf(c,"%d\n",int(playerPosition.size()));
s=c;
for(unsigned int i=0;i<playerPosition.size();i++){
SDL_Rect& r=playerPosition[i];
sprintf(c,"%d %d\n",r.x,r.y);
s+=c;
}
obj.attributes["recordPlayerPosition"].push_back(s);
}
#endif
//save it
objSerializer.saveNodeToFile(fileName,&obj,true,true);
//remove current level from node to prevent delete it.
obj.subNodes.clear();
}
void Game::loadRecord(const char* fileName){
//Create a TreeStorageNode that will hold the loaded data.
TreeStorageNode obj;
{
POASerializer objSerializer;
string s=fileName;
//Parse the file.
if(!objSerializer.loadNodeFromFile(s.c_str(),&obj,true)){
cout<<"Can't load record file "<<s<<endl;
return;
}
}
//find the node named 'map'.
bool loaded=false;
for(unsigned int i=0;i<obj.subNodes.size();i++){
if(obj.subNodes[i]->name=="map"){
//load the level. (fileName=???)
loadLevelFromNode(obj.subNodes[i],"???");
//remove this node to prevent delete it.
obj.subNodes[i]=NULL;
//over
loaded=true;
break;
}
}
if(!loaded){
cout<<"ERROR: Can't find subnode named 'map' from record file"<<endl;
return;
}
//load the record.
{
vector<int> *record=player.getRecord();
record->clear();
vector<string> &v=obj.attributes["record"];
for(unsigned int i=0;i<v.size();i++){
string &s=v[i];
string::size_type pos=s.find_first_of('*');
if(pos==string::npos){
//1 item only.
int i=atoi(s.c_str());
record->push_back(i);
}else{
//contains many items.
int i=atoi(s.substr(0,pos).c_str());
int j=atoi(s.substr(pos+1).c_str());
for(;j>0;j--){
record->push_back(i);
}
}
}
}
#ifdef RECORD_FILE_DEBUG
//load the debug data
{
vector<string> &v=obj.attributes["recordPlayerPosition"];
vector<SDL_Rect> &playerPosition=player.playerPosition();
playerPosition.clear();
if(!v.empty()){
if(!v[0].empty()){
stringstream st(v[0]);
int m;
st>>m;
for(int i=0;i<m;i++){
SDL_Rect r;
st>>r.x>>r.y;
r.w=0;
r.h=0;
playerPosition.push_back(r);
}
}
}
}
#endif
//play the record.
//TODO: tell the level manager don't save the level progress.
player.playRecord();
shadow.playRecord(); //???
}
/////////////EVENT///////////////
void Game::handleEvents(){
//First of all let the player handle input.
player.handleInput(&shadow);
//Check for an SDL_QUIT event.
if(event.type==SDL_QUIT){
//We need to quit so enter STATE_EXIT.
setNextState(STATE_EXIT);
}
//Check for the escape key.
if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
//Escape means we go one level up, to the level select state.
setNextState(STATE_LEVEL_SELECT);
//Save the progress.
levels->saveLevelProgress();
//And change the music back to the menu music.
getMusicManager()->playMusic("menu");
}
//Check if 'r' is pressed.
if(inputMgr.isKeyDownEvent(INPUTMGR_RESTART)){
//Check if it isn't a replay.
if(!player.isPlayFromRecord()){
//Set reset true.
isReset=true;
}else if(interlevel){
//We can also reset during the inter level screen.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
isReset=true;
}
}
//Check for the next level buttons when in the interlevel popup.
if(inputMgr.isKeyDownEvent(INPUTMGR_SPACE) || (event.type==SDL_KEYDOWN && (event.key.keysym.sym==SDLK_RETURN || event.key.keysym.sym==SDLK_RCTRL))){
if(interlevel){
//The interlevel popup is shown so we need to delete it.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Now goto the next level.
gotoNextLevel();
}
}
//Check if tab is pressed.
if(inputMgr.isKeyDownEvent(INPUTMGR_TAB)){
shadowCam=!shadowCam;
}
}
/////////////////LOGIC///////////////////
void Game::logic(){
//Add one tick to the time.
time++;
//Let the player store his move, if recording.
player.shadowSetState();
//Let the player give his recording to the shadow, if configured.
player.shadowGiveState(&shadow);
//Let the player jump.
player.jump();
//Let him move.
player.move(levelObjects);
//And let the camera follow him.
if(!shadowCam){
player.setMyCamera();
}else{
shadow.setMyCamera();
}
//Now let the shadow decide his move, if he's playing a recording.
shadow.moveLogic();
//Let the shadow jump.
shadow.jump();
//Let the shadow move.
shadow.move(levelObjects);
//Some levelObjects can move so update them.
for(unsigned int i=0;i<levelObjects.size();i++){
levelObjects[i]->move();
}
//Process any event in the queue.
for(unsigned int idx=0;idx<eventQueue.size();idx++){
//Get the event from the queue.
typeGameObjectEvent &e=eventQueue[idx];
//Check if the it has an id attached to it.
if(e.flags|1){
//Loop through the levelObjects and give them the event if they have the right id.
for(unsigned int i=0;i<levelObjects.size();i++){
if(e.objectType<0 || levelObjects[i]->type==e.objectType){
Block *obj=dynamic_cast<Block*>(levelObjects[i]);
if(obj!=NULL && obj->id==e.id){
levelObjects[i]->onEvent(e.eventType);
}
}
}
}else{
//Loop through the levelObjects and give them the event.
for(unsigned int i=0;i<levelObjects.size();i++){
if(e.objectType<0 || levelObjects[i]->type==e.objectType){
levelObjects[i]->onEvent(e.eventType);
}
}
}
}
//Done processing the events so clear the queue.
eventQueue.clear();
//Check collision and stuff for the shadow and player.
player.otherCheck(&shadow);
//Check if we won.
if(won){
//the string to store auto-save record path.
string bestTimeFilePath,bestRecordingFilePath;
//and if we can't get test path.
bool filePathError=false;
//Set the current level won.
levels->getLevel()->won=true;
if(levels->getLevel()->time==-1 || levels->getLevel()->time>time){
levels->getLevel()->time=time;
//save the best-time game record.
if(bestTimeFilePath.empty()){
getCurrentLevelAutoSaveRecordPath(bestTimeFilePath,bestRecordingFilePath,true);
}
if(bestTimeFilePath.empty()){
cout<<"ERROR: Couldn't get auto-save record file path"<<endl;
filePathError=true;
}else{
saveRecord(bestTimeFilePath.c_str());
}
}
if(levels->getLevel()->recordings==-1 || levels->getLevel()->recordings>recordings){
levels->getLevel()->recordings=recordings;
//save the best-recordings game record.
if(bestRecordingFilePath.empty() && !filePathError){
getCurrentLevelAutoSaveRecordPath(bestTimeFilePath,bestRecordingFilePath,true);
}
if(bestRecordingFilePath.empty()){
cout<<"ERROR: Couldn't get auto-save record file path"<<endl;
filePathError=true;
}else{
saveRecord(bestRecordingFilePath.c_str());
}
}
//Set the next level unlocked if it exists.
if(levels->getCurrentLevel()+1<levels->getLevelCount()){
levels->setLocked(levels->getCurrentLevel()+1);
}
//And save the progress.
levels->saveLevelProgress();
//Now go to the interlevel screen.
replayPlay();
//NOTE: We set isReset false to prevent the user from getting a best time of 0.00s and 0 recordings.
}
won=false;
//Check if we should reset.
if(isReset)
reset(false);
isReset=false;
}
/////////////////RENDER//////////////////
void Game::render(){
//First of all render the background.
{
//Get a pointer to the background.
ThemeBackground* bg=background;
//Check if the background is null, but there are themes.
if(bg==NULL && objThemes.themeCount()>0){
//Get the background from the first theme in the stack.
bg=objThemes[0]->getBackground();
}
//Check if the background isn't null.
if(bg){
//It isn't so draw it.
bg->draw(screen);
//And if it's the loaded background then also update the animation.
//FIXME: Updating the animation in the render method?
if(bg==background)
bg->updateAnimation();
}else{
//There's no background so fill the screen with white.
SDL_Rect r={0,0,SCREEN_WIDTH,SCREEN_HEIGHT};
SDL_FillRect(screen,&r,-1);
}
}
//Now we draw the levelObjects.
for(unsigned int o=0; o<levelObjects.size(); o++){
levelObjects[o]->show();
}
//Followed by the player and the shadow.
player.show();
shadow.show();
//Show the levelName if it isn't the level editor.
if(stateID!=STATE_LEVEL_EDITOR && bmTips[0]!=NULL && !interlevel){
applySurface(0,SCREEN_HEIGHT-bmTips[0]->h,bmTips[0],screen,NULL);
}
//Check if there's a tooltip.
//NOTE: gameTipIndex 0 is used for the levelName, 1 for shadow death, 2 for restart text, 3 for restart+checkpoint.
if(gameTipIndex>3 && gameTipIndex<TYPE_MAX){
//Check if there's a tooltip for the type.
if(bmTips[gameTipIndex]==NULL){
//There isn't thus make it.
const char* s=NULL;
string keyCode=inputMgr.getKeyCodeName(inputMgr.getKeyCode(INPUTMGR_ACTION,false));
transform(keyCode.begin(),keyCode.end(),keyCode.begin(),::toupper);
switch(gameTipIndex){
case TYPE_CHECKPOINT:
/// TRANSLATORS: Please do not remove %s from your translation:
/// - %s will be replaced with current action key
s=tfm::format(_("Press %s key to save the game."),keyCode).c_str();
break;
case TYPE_SWAP:
/// TRANSLATORS: Please do not remove %s from your translation:
/// - %s will be replaced with current action key
s=tfm::format("Press %s key to swap the position of player and shadow.",keyCode).c_str();
break;
case TYPE_SWITCH:
/// TRANSLATORS: Please do not remove %s from your translation:
/// - %s will be replaced with current action key
s=tfm::format("Press %s key to activate the switch.",keyCode).c_str();
break;
case TYPE_PORTAL:
/// TRANSLATORS: Please do not remove %s from your translation:
/// - %s will be replaced with current action key
s=tfm::format("Press %s key to teleport.",keyCode).c_str();
break;
}
//If we have a string then it's a supported GameObject type.
if(s!=NULL){
SDL_Color fg={0,0,0,0},bg={255,255,255,0};
bmTips[gameTipIndex]=TTF_RenderUTF8_Shaded(fontText,s,fg,bg);
SDL_SetAlpha(bmTips[gameTipIndex],SDL_SRCALPHA,160);
}
}
//We already have a gameTip for this type so draw it.
if(bmTips[gameTipIndex]!=NULL){
applySurface(0,0,bmTips[gameTipIndex],screen,NULL);
}
}
//Set the gameTip to 0.
gameTipIndex=0;
//Pointer to the sdl surface that will contain a message, if any.
SDL_Surface* bm=NULL;
//Check if the player is dead, meaning we draw a message.
if(player.dead){
//Get user configured restart key
string keyCodeRestart=inputMgr.getKeyCodeName(inputMgr.getKeyCode(INPUTMGR_RESTART,false));
transform(keyCodeRestart.begin(),keyCodeRestart.end(),keyCodeRestart.begin(),::toupper);
//The player is dead, check if there's a state that can be loaded.
if(player.canLoadState()){
//Now check if the tip is already made, if not make it.
if(bmTips[3]==NULL){
//Get user defined key for loading checkpoint
string keyCodeLoad=inputMgr.getKeyCodeName(inputMgr.getKeyCode(INPUTMGR_LOAD,false));
transform(keyCodeLoad.begin(),keyCodeLoad.end(),keyCodeLoad.begin(),::toupper);
//Draw string
SDL_Color fg={0,0,0,0},bg={255,255,255,0};
bmTips[3]=TTF_RenderUTF8_Shaded(fontText,
/// 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
tfm::format(_("Press %s to restart current level or press %s to load the game."),
keyCodeRestart,keyCodeLoad).c_str(),
fg,bg);
SDL_SetAlpha(bmTips[3],SDL_SRCALPHA,160);
}
bm=bmTips[3];
}else{
//Now check if the tip is already made, if not make it.
if(bmTips[2]==NULL){
SDL_Color fg={0,0,0,0},bg={255,255,255,0};
bmTips[2]=TTF_RenderUTF8_Shaded(fontText,
/// TRANSLATORS: Please do not remove %s from your translation:
/// - %s will be replaced with currently configured key to restart game
tfm::format(_("Press %s to restart current level."),keyCodeRestart).c_str(),
fg,bg);
SDL_SetAlpha(bmTips[2],SDL_SRCALPHA,160);
}
bm=bmTips[2];
}
}
//Check if the shadow has died (and there's no other notification).
//NOTE: We use the shadow's jumptime as countdown, this variable isn't used when the shadow is dead.
if(shadow.dead && bm==NULL && shadow.jumpTime>0){
//Now check if the tip is already made, if not make it.
if(bmTips[1]==NULL){
SDL_Color fg={0,0,0,0},bg={255,255,255,0};
bmTips[1]=TTF_RenderUTF8_Shaded(fontText,
_("Your shadow has died."),
fg,bg);
SDL_SetAlpha(bmTips[1],SDL_SRCALPHA,160);
}
bm=bmTips[1];
//NOTE: Logic in the render loop, we substract the shadow's jumptime by one.
shadow.jumpTime--;
}
//Draw the tip.
if(bm!=NULL)
applySurface(0,0,bm,screen,NULL);
//show time and records used in level editor.
if(stateID==STATE_LEVEL_EDITOR && time>0){
SDL_Color fg={0,0,0,0},bg={255,255,255,0};
SDL_Surface *bm;
int y=SCREEN_HEIGHT;
bm=TTF_RenderUTF8_Shaded(fontText,tfm::format(_("%d recordings"),recordings).c_str(),fg,bg);
SDL_SetAlpha(bm,SDL_SRCALPHA,160);
y-=bm->h;
applySurface(0,y,bm,screen,NULL);
SDL_FreeSurface(bm);
char c[32];
sprintf(c,"%-.2fs",time/40.0f);
bm=TTF_RenderUTF8_Shaded(fontText,c,fg,bg);
SDL_SetAlpha(bm,SDL_SRCALPHA,160);
y-=bm->h;
applySurface(0,y,bm,screen,NULL);
SDL_FreeSurface(bm);
}
//Draw the current action in the upper right corner.
if(player.record){
applySurface(SCREEN_WIDTH-50,0,action,screen,NULL);
}else if(shadow.state!=0){
SDL_Rect r={50,0,50,50};
applySurface(SCREEN_WIDTH-50,0,action,screen,&r);
}
//if the game is play from record then draw something indicates it
if(player.isPlayFromRecord()){
//Dim the screen if interlevel is true.
if(interlevel){
SDL_BlitSurface(screen,NULL,tempSurface,NULL);
SDL_FillRect(screen,NULL,0);
SDL_SetAlpha(tempSurface, SDL_SRCALPHA,220);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
//Check if the GUI isn't null.
if(GUIObjectRoot){
//==Create first box==
//Create the title
SDL_Color black={0,0,0,0};
SDL_Rect r;
/// TRANSLATORS: This is caption for finished level
SDL_Surface* bm=TTF_RenderUTF8_Blended(fontGUI,_("You've finished:"),black);
//Recreate the level string.
string s;
if (levels->getLevelCount()>0){
/// 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
s=tfm::format(_("Level %d %s"),levels->getCurrentLevel()+1,levelName);
}
SDL_Surface* bm2=TTF_RenderUTF8_Blended(fontText,s.c_str(),black);
//Now draw the first gui box so that it's bigger than longer text.
int width;
if(bm->w>bm2->w)
width=bm->w+32;
else
width=bm2->w+32;
drawGUIBox((SCREEN_WIDTH-width)/2,4,width,68,screen,0xDDDDDDA1);
// Now draw title.
r.x=(SCREEN_WIDTH-bm->w)/2;
r.y=8;
SDL_BlitSurface(bm,NULL,screen,&r);
// And then level name.
r.x=(SCREEN_WIDTH-bm2->w)/2;
r.y=44;
SDL_BlitSurface(bm2,NULL,screen,&r);
//Free drawed texts
SDL_FreeSurface(bm);
SDL_FreeSurface(bm2);
//==Create second box==
//Now draw the second gui box.
drawGUIBox(GUIObjectRoot->left,GUIObjectRoot->top,GUIObjectRoot->width,GUIObjectRoot->height,screen,0xDDDDDDA1);
//Draw the medal.
int medal=GUIObjectRoot->value;
r.x=(medal-1)*30;
r.y=0;
r.w=30;
r.h=30;
applySurface(GUIObjectRoot->left+16,GUIObjectRoot->top+92,medals,screen,&r);
applySurface(GUIObjectRoot->left+370,GUIObjectRoot->top+92,medals,screen,&r);
}
}else if((time & 0x10)==0x10){
SDL_Rect r={50,0,50,50};
applySurface(0,0,action,screen,&r);
applySurface(0,SCREEN_HEIGHT-50,action,screen,&r);
applySurface(SCREEN_WIDTH-50,SCREEN_HEIGHT-50,action,screen,&r);
}
}else if(player.objNotificationBlock){
//If the player is in front of a notification block show the message.
//And it isn't a replay.
- std::vector<char> string_data((dynamic_cast<Block*>(player.objNotificationBlock))->message.begin(), (dynamic_cast<Block*>(player.objNotificationBlock))->message.end());
+ std::string message=levels->_((dynamic_cast<Block*>(player.objNotificationBlock))->message);
+ std::vector<char> string_data(message.begin(), message.end());
string_data.push_back('\0');
int y = 20;
vector<SDL_Surface*> lines;
//Now process the prompt.
{
//Pointer to the string.
char* lps=&string_data[0];
//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;
//Integer used to center the sentence horizontally.
int x;
TTF_SizeText(fontText,lps,&x,NULL);
x=(SCREEN_WIDTH-200-x)/2;
//Color the text will be: black.
SDL_Color black={0,0,0,0};
lines.push_back(TTF_RenderUTF8_Blended(fontText,lps,black));
//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;
}
}
drawGUIBox(100,SCREEN_HEIGHT-y-25,SCREEN_WIDTH-200,y+20,screen,0xDDDDDDA1);
while(!lines.empty()){
SDL_Surface* bm=lines[0];
if(bm!=NULL){
applySurface(100+((SCREEN_WIDTH-200-bm->w)/2),SCREEN_HEIGHT-y,bm,screen,NULL);
SDL_FreeSurface(bm);
}
y-=25;
lines.erase(lines.begin());
}
}
}
void Game::replayPlay(){
interlevel=true;
//cout<<"Game::replayPlay()"<<endl;
//Fix the bug that press "r" to restart a level just before finishing it
//will cause inter-level popup appeares and the game restarts.
//Now when the game is about to finish, we don't allow to restart.
isReset=false;
//Create the gui if it isn't already done.
if(!GUIObjectRoot){
GUIObjectRoot=new GUIObject((SCREEN_WIDTH-570)/2,SCREEN_HEIGHT-140,570,135,GUIObjectNone);
//NOTE: We put the medal in the value of the GUIObjectRoot.
//The different values.
int bestTime=levels->getLevel()->time;
int targetTime=levels->getLevel()->targetTime;
int bestRecordings=levels->getLevel()->recordings;
int targetRecordings=levels->getLevel()->targetRecordings;
int medal=1;
if(targetTime<0){
medal=3;
}else{
if(targetTime<0 || bestTime<=targetTime)
medal++;
if(targetRecordings<0 || bestRecordings<=targetRecordings)
medal++;
}
//Add it to the GUIObjectRoot.
GUIObjectRoot->value=medal;
//Create the labels with the time and best time.
/// 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.
GUIObject* obj=new GUIObject(20,10,150,36,GUIObjectLabel,tfm::format(_("Time: %-.2fs"),time/40.0f).c_str());
GUIObjectRoot->childControls.push_back(obj);
/// 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.
obj=new GUIObject(20,34,150,36,GUIObjectLabel,tfm::format(_("Best time: %-.2fs"),bestTime/40.0f).c_str());
GUIObjectRoot->childControls.push_back(obj);
/// 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.
if(targetTime>=0){
obj=new GUIObject(20,58,150,36,GUIObjectLabel,tfm::format(_("Target time: %-.2fs"),targetTime/40.0f).c_str());
GUIObjectRoot->childControls.push_back(obj);
}
//Now the ones for the recordings.
/// TRANSLATORS: Please do not remove %d from your translation:
/// - %d means the number of recordings user has made
obj=new GUIObject(210,10,150,36,GUIObjectLabel,tfm::format(_("Recordings: %d"),recordings).c_str());
GUIObjectRoot->childControls.push_back(obj);
/// TRANSLATORS: Please do not remove %d from your translation:
/// - %d means the number of recordings user has made
obj=new GUIObject(210,34,150,36,GUIObjectLabel,tfm::format(_("Best recordings: %d"),bestRecordings).c_str());
GUIObjectRoot->childControls.push_back(obj);
/// TRANSLATORS: Please do not remove %d from your translation:
/// - %d means the number of recordings user has made
if(targetRecordings>=0){
obj=new GUIObject(210,58,150,36,GUIObjectLabel,tfm::format(_("Target recordings: %d"),targetRecordings).c_str());
GUIObjectRoot->childControls.push_back(obj);
}
//The medal that is earned.
/// TRANSLATORS: Please do not remove %s from your translation:
/// - %s will be replaced with name of a prize medal (gold, silver or bronze)
string s1=tfm::format(_("You earned the %s medal"),(medal>1)?(medal==3)?_("GOLD"):_("SILVER"):_("BRONZE"));
obj=new GUIObject(48,92,150,36,GUIObjectLabel,s1.c_str());
//Center it horizontally.
int width;
TTF_SizeText(fontText,s1.c_str(),&width,NULL);
obj->width=width;
obj->left=(416-width)/2;
GUIObjectRoot->childControls.push_back(obj);
//Create the three buttons, Menu, Restart, Next.
/// TRANSLATORS: used as return to the level selector menu
obj=new GUIObject(420,10,128,36,GUIObjectButton,_("Menu"));
obj->name="cmdMenu";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
/// TRANSLATORS: used as restart level
obj=new GUIObject(409,50,150,36,GUIObjectButton,_("Restart"));
obj->name="cmdRestart";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
/// TRANSLATORS: used as next level
obj=new GUIObject(420,90,128,36,GUIObjectButton,_("Next"));
obj->name="cmdNext";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
}
//We only need to reset a few things so we don't call reset().
for(unsigned int i=0;i<levelObjects.size();i++){
levelObjects[i]->reset(true);
}
//Also reset the background animation, if any.
if(background)
background->resetAnimation(true);
//Make a copy of the playerButtons.
vector<int> recordCopy=player.recordButton;
player.reset(true);
shadow.reset(true);
player.recordButton=recordCopy;
//Now play the recording.
player.playRecord();
}
void Game::recordingEnded(){
//Check if it's a normal replay, if so just stop.
if(!interlevel){
msgBox(_("Game replay is done."),MsgBoxOKOnly,_("Game Replay"));
//Go to the level select menu.
setNextState(STATE_LEVEL_SELECT);
//And change the music back to the menu music.
getMusicManager()->playMusic("menu");
}else{
//Instead of directly replaying we set won true to let the Game handle the replaying at the end of the update cycle.
won=true;
}
}
bool Game::saveState(){
//Check if the player and shadow can save the current state.
if(player.canSaveState() && shadow.canSaveState()){
//Let the player and the shadow save their state.
player.saveState();
shadow.saveState();
//Save the stats.
timeSaved=time;
recordingsSaved=recordings;
//Save other state, for example moving blocks.
for(unsigned int i=0;i<levelObjects.size();i++){
levelObjects[i]->saveState();
}
//Also save the background animation, if any.
if(background)
background->saveAnimation();
//Return true.
return true;
}
//We can't save the state so return false.
return false;
}
bool Game::loadState(){
//Check if there's a state that can be loaded.
if(player.canLoadState() && shadow.canLoadState()){
//Let the player and the shadow load their state.
player.loadState();
shadow.loadState();
//Load the stats.
time=timeSaved;
recordings=recordingsSaved;
//Load other state, for example moving blocks.
for(unsigned int i=0;i<levelObjects.size();i++){
levelObjects[i]->loadState();
}
//Also load the background animation, if any.
if(background)
background->loadAnimation();
//Return true.
return true;
}
//We can't load the state so return false.
return false;
}
void Game::reset(bool save){
//We need to reset the game so we also reset the player and the shadow.
player.reset(save);
shadow.reset(save);
//Reset the stats.
time=0;
recordings=0;
//There is no last checkpoint so set it to NULL.
if(save)
objLastCheckPoint=NULL;
//Reset other state, for example moving blocks.
for(unsigned int i=0;i<levelObjects.size();i++){
levelObjects[i]->reset(save);
}
//Also reset the background animation, if any.
if(background)
background->resetAnimation(save);
//Also set interlevel to false.
interlevel=false;
}
void Game::broadcastObjectEvent(int eventType,int objectType,const char* id){
//Create a typeGameObjectEvent that can be put into the queue.
typeGameObjectEvent e;
//Set the event type.
e.eventType=eventType;
//Set the object type.
e.objectType=objectType;
//By default flags=0.
e.flags=0;
//Unless there's an id.
if(id){
//Set flags to 0x1 and set the id.
e.flags|=1;
e.id=id;
}
//Add the event to the queue.
eventQueue.push_back(e);
}
void Game::getCurrentLevelAutoSaveRecordPath(std::string &bestTimeFilePath,std::string &bestRecordingFilePath,bool createPath){
levels->getLevelAutoSaveRecordPath(-1,bestTimeFilePath,bestRecordingFilePath,createPath);
}
void Game::gotoNextLevel(){
//Goto the next level.
levels->nextLevel();
//Check if the level exists.
if(levels->getCurrentLevel()<levels->getLevelCount()){
setNextState(STATE_GAME);
//Don't forget the music.
getMusicManager()->pickMusic();
}else{
if(!levels->congratulationText.empty()){
- msgBox(levels->congratulationText,MsgBoxOKOnly,_("Congratulations"));
+ msgBox(levels->_(levels->congratulationText),MsgBoxOKOnly,_("Congratulations"));
}else{
msgBox(_("You have finished the levelpack!"),MsgBoxOKOnly,_("Congratulations"));
}
//Now go back to the levelselect screen.
setNextState(STATE_LEVEL_SELECT);
//And set the music back to menu.
getMusicManager()->playMusic("menu");
}
}
void Game::GUIEventCallback_OnEvent(string name,GUIObject* obj,int eventType){
if(name=="cmdMenu"){
setNextState(STATE_LEVEL_SELECT);
//And change the music back to the menu music.
getMusicManager()->playMusic("menu");
}else if(name=="cmdRestart"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//And reset the game.
//new: we don't need to clear the save game because
//in level replay the game won't be saved
//TODO: it doesn't work; better leave for next release
reset(/*false*/ true);
}else if(name=="cmdNext"){
//No matter what, clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//And goto the next level.
gotoNextLevel();
}
}
diff --git a/src/LevelEditSelect.cpp b/src/LevelEditSelect.cpp
index 11d1f4f..6a0575a 100644
--- a/src/LevelEditSelect.cpp
+++ b/src/LevelEditSelect.cpp
@@ -1,653 +1,657 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include "LevelEditSelect.h"
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "Objects.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIScrollBar.h"
#include "InputManager.h"
#include "Game.h"
#include <SDL/SDL_ttf.h>
#include <SDL/SDL.h>
#include <stdio.h>
#include <algorithm>
#include <string>
#include <iostream>
#include "libs/tinyformat/tinyformat.h"
using namespace std;
LevelEditSelect::LevelEditSelect():LevelSelect(_("Map Editor"),LevelPackManager::CUSTOM_PACKS){
//The levelpack name text field.
levelpackName=new GUIObject(280,104,240,32,GUIObjectTextBox);
levelpackName->eventCallback=this;
levelpackName->visible=false;
GUIObjectRoot->childControls.push_back(levelpackName);
//Create the six buttons at the bottom of the screen.
GUIObject* obj=new GUIObject(20,SCREEN_HEIGHT-120,260,32,GUIObjectButton,_("New Levelpack"));
obj->name="cmdNewLvlpack";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
propertiesPack=new GUIObject((SCREEN_WIDTH-280)/2,SCREEN_HEIGHT-120,280,32,GUIObjectButton,_("Pack Properties"));
propertiesPack->name="cmdLvlpackProp";
propertiesPack->eventCallback=this;
GUIObjectRoot->childControls.push_back(propertiesPack);
removePack=new GUIObject(SCREEN_WIDTH-260,SCREEN_HEIGHT-120,240,32,GUIObjectButton,_("Remove Pack"));
removePack->name="cmdRmLvlpack";
removePack->eventCallback=this;
GUIObjectRoot->childControls.push_back(removePack);
move=new GUIObject(20,SCREEN_HEIGHT-60,240,32,GUIObjectButton,_("Move Map"));
move->name="cmdMoveMap";
move->eventCallback=this;
move->enabled=false;
GUIObjectRoot->childControls.push_back(move);
remove=new GUIObject((SCREEN_WIDTH-280)/2,SCREEN_HEIGHT-60,240,32,GUIObjectButton,_("Remove Map"));
remove->name="cmdRmMap";
remove->eventCallback=this;
remove->enabled=false;
GUIObjectRoot->childControls.push_back(remove);
edit=new GUIObject(SCREEN_WIDTH-280,SCREEN_HEIGHT-60,240,32,GUIObjectButton,_("Edit Map"));
edit->name="cmdEdit";
edit->eventCallback=this;
edit->enabled=false;
GUIObjectRoot->childControls.push_back(edit);
//Set the levelEditGUIObjectRoot.
levelEditGUIObjectRoot=GUIObjectRoot;
//show level list
changePack();
refresh();
}
LevelEditSelect::~LevelEditSelect(){
selectedNumber=NULL;
}
void LevelEditSelect::changePack(){
packName=levelpacks->item[levelpacks->value];
if(packName=="Custom Levels"){
//Disable some levelpack buttons.
propertiesPack->enabled=false;
removePack->enabled=false;
}else{
//Enable some levelpack buttons.
propertiesPack->enabled=true;
removePack->enabled=true;
}
//Set last levelpack.
getSettings()->setValue("lastlevelpack",packName);
//Now let levels point to the right pack.
levels=getLevelPackManager()->getLevelPack(packName);
}
void LevelEditSelect::packProperties(){
//Open a message popup.
//NOTE: We can always point GUIObjectRoot to the main gui by using levelEditGUIObjectRoot.
GUIObjectRoot=new GUIObject((SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-320)/2,600,320,GUIObjectFrame,_("Properties"));
GUIObject* obj;
obj=new GUIObject(40,50,240,36,GUIObjectLabel,_("Name:"));
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(60,80,480,36,GUIObjectTextBox,packName.c_str());
obj->name="LvlpackName";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(40,120,240,36,GUIObjectLabel,_("Description:"));
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(60,150,480,36,GUIObjectTextBox,levels->levelpackDescription.c_str());
obj->name="LvlpackDescription";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(40,190,240,36,GUIObjectLabel,_("Congratulation text:"));
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(60,220,480,36,GUIObjectTextBox,levels->congratulationText.c_str());
obj->name="LvlpackCongratulation";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,320-44,150,36,GUIObjectButton,_("OK"));
obj->name="cfgOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,320-44,150,36,GUIObjectButton,_("Cancel"));
obj->name="cfgCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
flipScreen();
SDL_Delay(30);
}
//We're done so set the original GUIObjectRoot back.
GUIObjectRoot=levelEditGUIObjectRoot;
}
void LevelEditSelect::addLevel(){
//Open a message popup.
//NOTE: We can always point GUIObjectRoot to the main gui by using levelEditGUIObjectRoot.
GUIObjectRoot=new GUIObject((SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-200)/2,600,200,GUIObjectFrame,_("Add level"));
GUIObject* obj;
obj=new GUIObject(40,80,240,36,GUIObjectLabel,_("File name:"));
GUIObjectRoot->childControls.push_back(obj);
char s[64];
sprintf(s,"map%02d.map",levels->getLevelCount()+1);
obj=new GUIObject(300,80,240,36,GUIObjectTextBox,s);
obj->name="LvlFile";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,200-44,150,36,GUIObjectButton,_("OK"));
obj->name="cfgAddOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,200-44,150,36,GUIObjectButton,_("Cancel"));
obj->name="cfgAddCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
flipScreen();
SDL_Delay(30);
}
//We're done so set the original GUIObjectRoot back.
GUIObjectRoot=levelEditGUIObjectRoot;
}
void LevelEditSelect::moveLevel(){
//Open a message popup.
//NOTE: We can always point GUIObjectRoot to the main gui by using levelEditGUIObjectRoot.
GUIObjectRoot=new GUIObject((SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-200)/2,600,200,GUIObjectFrame,_("Move level"));
GUIObject* obj;
obj=new GUIObject(40,80,240,36,GUIObjectLabel,_("Level: "));
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(300,80,240,36,GUIObjectTextBox,"1");
obj->name=_("MoveLevel");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUISingleLineListBox(40,120,240,36);
obj->name="lstPlacement";
vector<string> v;
v.push_back(_("Before"));
v.push_back(_("After"));
v.push_back(_("Swap"));
(dynamic_cast<GUISingleLineListBox*>(obj))->item=v;
obj->value=0;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,200-44,150,36,GUIObjectButton,_("OK"));
obj->name="cfgMoveOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,200-44,150,36,GUIObjectButton,_("Cancel"));
obj->name="cfgMoveCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
flipScreen();
SDL_Delay(30);
}
//We're done so set the original GUIObjectRoot back.
GUIObjectRoot=levelEditGUIObjectRoot;
}
void LevelEditSelect::refresh(){
int m=levels->getLevelCount();
numbers.clear();
//clear the selected level
if(selectedNumber!=NULL){
selectedNumber=NULL;
}
//Disable the level specific buttons.
move->enabled=false;
remove->enabled=false;
edit->enabled=false;
for(int n=0;n<=m;n++){
numbers.push_back(Number());
}
for(int n=0;n<m;n++){
SDL_Rect box={(n%10)*64+80,(n/10)*64+184,0,0};
numbers[n].init(n,box);
}
SDL_Rect box={(m%10)*64+80,(m/10)*64+184,0,0};
numbers[m].init("+",box);
m++; //including the "+" button
if(m>LEVELS_DISPLAYED_IN_SCREEN){
levelScrollBar->maxValue=(m-LEVELS_DISPLAYED_IN_SCREEN+9)/10;
levelScrollBar->visible=true;
}else{
levelScrollBar->maxValue=0;
levelScrollBar->visible=false;
}
- levelpackDescription->caption=levels->levelpackDescription;
- int width,height;
- TTF_SizeText(fontText,levels->levelpackDescription.c_str(),&width,&height);
- levelpackDescription->left=(800-width)/2;
+ if(!levels->levelpackDescription.empty()){
+ levelpackDescription->caption=levels->_(levels->levelpackDescription);
+ int width,height;
+ TTF_SizeText(fontText,levels->_(levels->levelpackDescription),&width,&height);
+ levelpackDescription->left=(800-width)/2;
+ }else{
+ levelpackDescription->caption="";
+ }
}
void LevelEditSelect::selectNumber(unsigned int number,bool selected){
if(selected){
levels->setCurrentLevel(number);
setNextState(STATE_LEVEL_EDITOR);
//Pick music from the current music list.
getMusicManager()->pickMusic();
}else{
if(number==numbers.size()-1){
addLevel();
}else if(number>=0 && number<numbers.size()){
selectedNumber=&numbers[number];
//Enable the level specific buttons.
//NOTE: We check if 'remove levelpack' is enabled, if not then it's the Levels levelpack.
if(removePack->enabled)
move->enabled=true;
remove->enabled=true;
edit->enabled=true;
}
}
}
void LevelEditSelect::render(){
//Let the levelselect render.
LevelSelect::render();
}
void LevelEditSelect::renderTooltip(unsigned int number,int dy){
SDL_Color fg={0,0,0};
SDL_Surface* name;
if(number==(unsigned)levels->getLevelCount()){
//Render the name of the level.
name=TTF_RenderUTF8_Blended(fontText,_("Add level"),fg);
}else{
//Render the name of the level.
- name=TTF_RenderUTF8_Blended(fontText,levels->getLevelName(number).c_str(),fg);
+ name=TTF_RenderUTF8_Blended(fontText,levels->_(levels->getLevelName(number)),fg);
}
//Check if name isn't null.
if(name==NULL)
return;
//Now draw a square the size of the three texts combined.
SDL_Rect r=numbers[number].box;
r.y-=dy*64;
r.w=name->w;
r.h=name->h;
//Make sure the tooltip doesn't go outside the window.
if(r.y>SCREEN_HEIGHT-200){
r.y-=name->h+4;
}else{
r.y+=numbers[number].box.h+2;
}
if(r.x+r.w>SCREEN_WIDTH-50)
r.x=SCREEN_WIDTH-50-r.w;
//Draw a rectange
Uint32 color=0xFFFFFF00|240;
drawGUIBox(r.x-5,r.y-5,r.w+10,r.h+10,screen,color);
//Calc the position to draw.
SDL_Rect r2=r;
//Now we render the name if the surface isn't null.
if(name!=NULL){
//Draw the name.
SDL_BlitSurface(name,NULL,screen,&r2);
}
//And free the surfaces.
SDL_FreeSurface(name);
}
void LevelEditSelect::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//NOTE: We check for the levelpack change to enable/disable some levelpack buttons.
if(name=="cmdLvlPack"){
//We call changepack and return to prevent the LevelSelect to undo what we did.
changePack();
refresh();
return;
}
//Let the level select handle his GUI events.
LevelSelect::GUIEventCallback_OnEvent(name,obj,eventType);
//Check for the edit button.
if(name=="cmdNewLvlpack"){
//Clear the current pack name.
packName.clear();
LevelPack* levelpack=new LevelPack;
levels=levelpack;
//Create a new pack.
packProperties();
}else if(name=="cmdLvlpackProp"){
//Show the pack properties.
packProperties();
}else if(name=="cmdRmLvlpack"){
//Show an "are you sure" message.
if(msgBox(_("Are you sure?"),MsgBoxYesNo,_("Remove prompt"))==MsgBoxYes){
//Remove the directory.
if(!removeDirectory(levels->levelpackPath.c_str())){
cerr<<"ERROR: Unable to remove levelpack directory "<<levels->levelpackPath<<endl;
}
//Remove it from the vector (levelpack list).
vector<string>::iterator it;
it=find(levelpacks->item.begin(),levelpacks->item.end(),packName);
if(it!=levelpacks->item.end()){
levelpacks->item.erase(it);
}
//Remove it from the levelpackManager.
getLevelPackManager()->removeLevelPack(packName);
//And call changePack.
levelpacks->value=levelpacks->item.size()-1;
changePack();
refresh();
}
}else if(name=="cmdMoveMap"){
if(selectedNumber!=NULL){
moveLevel();
}
}else if(name=="cmdRmMap"){
if(selectedNumber!=NULL){
if(packName!="Custom Levels"){
if(!removeFile((levels->levelpackPath+"/"+levels->getLevel(selectedNumber->getNumber())->file).c_str())){
cerr<<"ERROR: Unable to remove level "<<(levels->levelpackPath+"/"+levels->getLevel(selectedNumber->getNumber())->file).c_str()<<endl;
}
levels->removeLevel(selectedNumber->getNumber());
levels->saveLevels(levels->levelpackPath+"/levels.lst");
}else{
//This is the levels levelpack so we just remove the file.
if(!removeFile(levels->getLevel(selectedNumber->getNumber())->file.c_str())){
cerr<<"ERROR: Unable to remove level "<<levels->getLevel(selectedNumber->getNumber())->file<<endl;
}
levels->removeLevel(selectedNumber->getNumber());
}
//And refresh the selection screen.
refresh();
}
}else if(name=="cmdEdit"){
if(selectedNumber!=NULL){
levels->setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_LEVEL_EDITOR);
//Pick music from the current music list.
getMusicManager()->pickMusic();
}
}
//Check for levelpack properties events.
if(name=="cfgOK"){
//Now loop throught the children of the GUIObjectRoot in search of the fields.
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="LvlpackName"){
//Check if the name changed.
if(packName!=GUIObjectRoot->childControls[i]->caption){
//Delete the old one.
if(!packName.empty()){
if(!renameDirectory((getUserPath(USER_DATA)+"custom/levelpacks/"+packName).c_str(),(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
cerr<<"ERROR: Unable to move levelpack directory "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+packName)<<" to "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
}
//Remove the old one from the levelpack manager.
getLevelPackManager()->removeLevelPack(packName);
//And the levelpack list.
vector<string>::iterator it1;
it1=find(levelpacks->item.begin(),levelpacks->item.end(),packName);
if(it1!=levelpacks->item.end()){
levelpacks->item.erase(it1);
if((unsigned)levelpacks->value>levelpacks->item.size())
levelpacks->value=levelpacks->item.size()-1;
}
}else{
if(!createDirectory((getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
cerr<<"ERROR: Unable to create levelpack directory "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
}
if(!createFile((getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst").c_str())){
cerr<<"ERROR: Unable to create levelpack file "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst")<<endl;
}
}
//And set the new name.
packName=GUIObjectRoot->childControls[i]->caption;
levels->levelpackName=packName;
levels->levelpackPath=(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/");
//Also add the levelpack location
getLevelPackManager()->addLevelPack(levels);
levelpacks->item.push_back(GUIObjectRoot->childControls[i]->caption);
levelpacks->value=levelpacks->item.size()-1;
//And call changePack.
changePack();
}
}
if(GUIObjectRoot->childControls[i]->name=="LvlpackDescription"){
levels->levelpackDescription=GUIObjectRoot->childControls[i]->caption;
}
if(GUIObjectRoot->childControls[i]->name=="LvlpackCongratulation"){
levels->congratulationText=GUIObjectRoot->childControls[i]->caption;
}
}
//Refresh the leveleditselect to show the correct information.
refresh();
//Save the configuration.
levels->saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
getSettings()->setValue("lastlevelpack",packName);
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="cfgCancel"){
//Check if packName is empty, if so it was a new levelpack and we need to revert to an existing one.
if(packName.empty()){
packName=levelpacks->item[levelpacks->value];
changePack();
}
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for add level events.
if(name=="cfgAddOK"){
//Check if the file name isn't null.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="LvlFile"){
if(GUIObjectRoot->childControls[i]->caption.empty()){
msgBox(_("No file name given for the new level."),MsgBoxOKOnly,_("Missing file name"));
return;
}else{
string tmp_caption = GUIObjectRoot->childControls[i]->caption;
//Replace all spaces with a underline.
size_t j;
for(;(j=tmp_caption.find(" "))!=string::npos;){
tmp_caption.replace(j,1,"_");
}
//If there isn't ".map" extension add it.
size_t found=tmp_caption.find_first_of(".");
if(found!=string::npos)
tmp_caption.replace(tmp_caption.begin()+found+1,tmp_caption.end(),"map");
else if (tmp_caption.substr(found+1)!="map")
tmp_caption.append(".map");
/* Create path and file in it */
string path=(levels->levelpackPath+"/"+tmp_caption);
if(packName=="Custom Levels"){
path=(getUserPath(USER_DATA)+"/custom/levels/"+tmp_caption);
}
//First check if the file doesn't exist already.
FILE* f;
f=fopen(path.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();
levelEditGUIObjectRoot->render();
//Notify the user.
msgBox(("The file "+tmp_caption+" already exists.").c_str(),MsgBoxOKOnly,"Error");
return;
}
if(!createFile(path.c_str())){
cerr<<"ERROR: Unable to create level file "<<path<<endl;
}
levels->addLevel(path);
if(packName!="Custom Levels")
levels->saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
refresh();
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
return;
}
}
}
}
}else if(name=="cfgAddCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for move level events.
if(name=="cfgMoveOK"){
//Check if the entered level number is valid.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
int level=0;
int placement=0;
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="MoveLevel"){
level=atoi(GUIObjectRoot->childControls[i]->caption.c_str());
if(level<=0 || level>levels->getLevelCount()){
msgBox(_("The entered level number isn't valid!"),MsgBoxOKOnly,_("Illegal number"));
return;
}
}
if(GUIObjectRoot->childControls[i]->name=="lstPlacement"){
placement=GUIObjectRoot->childControls[i]->value;
}
}
//Now we execute the swap/move.
//Check for the place before.
if(placement==0){
//We place the selected level before the entered level.
levels->moveLevel(selectedNumber->getNumber(),level-1);
}else if(placement==1){
//We place the selected level after the entered level.
if(level<selectedNumber->getNumber())
levels->moveLevel(selectedNumber->getNumber(),level);
else
levels->moveLevel(selectedNumber->getNumber(),level+1);
}else if(placement==2){
//We swap the selected level with the entered level.
levels->swapLevel(selectedNumber->getNumber(),level-1);
}
//And save the change.
if(packName!="Custom Levels")
levels->saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
refresh();
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="cfgMoveCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
diff --git a/src/LevelPack.cpp b/src/LevelPack.cpp
index 11587d6..89ecdb6 100644
--- a/src/LevelPack.cpp
+++ b/src/LevelPack.cpp
@@ -1,506 +1,512 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include "LevelPack.h"
#include "Functions.h"
#include "FileManager.h"
#include "TreeStorageNode.h"
#include "POASerializer.h"
#include "MD5.h"
#include <dirent.h>
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
using namespace std;
void LevelPack::clear(){
currentLevel=0;
loaded=false;
levels.clear();
levelpackDescription.clear();
levelpackPath.clear();
levelProgressFile.clear();
congratulationText.clear();
}
bool LevelPack::loadLevels(const std::string& levelListFile){
//We're going to load a new levellist so first clean any existing levels.
clear();
//If the levelListFile is empty we have nothing to load so we return false.
if(levelListFile.empty()){
cerr<<"ERROR: No levellist file given."<<endl;
return false;
}
//Process the levelListFile, create a new string since lecelListFile is constant.
string levelListNew=levelListFile;
levelpackPath=pathFromFileName(levelListNew);
//Create two input streams, one for the levellist file and one for the levelprogress.
ifstream level(levelListNew.c_str());
if(!level){
cerr<<"ERROR: Can't load level list "<<levelListNew<<endl;
return false;
}
//Load the level list file.
TreeStorageNode obj;
{
POASerializer objSerializer;
if(!objSerializer.readNode(level,&obj,true)){
cerr<<"ERROR: Invalid file format of level list "<<levelListNew<<endl;
return false;
}
}
//Check if there are translations for the levels.
{
//Try to open the locale folder in the levelpack to detect if there are translations for the levelpack.
DIR *pDir;
pDir=opendir((pathFromFileName(levelListNew)+"locale/").c_str());
if(pDir!=NULL){
//Folder is present so configure the levelDictionaryManager.
+ dictionaryManager=new tinygettext::DictionaryManager();
+ dictionaryManager->add_directory(pathFromFileName(levelListNew)+"locale/");
+ dictionaryManager->set_charset("UTF-8");
+ dictionaryManager->set_language(tinygettext::Language::from_name(language));
+ }else{
+ dictionaryManager=NULL;
}
}
//Look for the name.
{
vector<string> &v=obj.attributes["name"];
if(v.size()>0){
levelpackName=v[0];
}else{
//Name is not defined so take the folder name.
levelpackName=pathFromFileName(levelListFile);
//Remove the last character '/'
levelpackName=levelpackName.substr(0,levelpackName.size()-1);
levelpackName=fileNameFromPath(levelpackName);
}
}
//Look for the description.
{
vector<string> &v=obj.attributes["description"];
if(v.size()>0)
levelpackDescription=v[0];
}
//Look for the congratulation text.
{
vector<string> &v=obj.attributes["congratulations"];
if(v.size()>0)
congratulationText=v[0];
}
//Loop through the level list entries.
for(unsigned int i=0;i<obj.subNodes.size();i++){
TreeStorageNode* obj1=obj.subNodes[i];
if(obj1==NULL)
continue;
if(obj1->value.size()>=1 && obj1->name=="levelfile"){
Level level;
level.file=obj1->value[0];
level.targetTime=0;
level.targetRecordings=0;
//Open the level file to retrieve the name and target time/recordings.
TreeStorageNode obj;
POASerializer objSerializer;
if(objSerializer.loadNodeFromFile((levelpackPath+level.file).c_str(),&obj,true)){
//Calc the MD5 FIRST because query obj.attributes will modify internal structure.
obj.name.clear();
obj.calcMD5(level.md5Digest);
//Get the name of the level.
vector<string>& v=obj.attributes["name"];
if(v.size()>0)
level.name=v[0];
//If the name is empty then we set it to the file name.
if(level.name.empty())
level.name=fileNameFromPath(level.file);
//Get the target time of the level.
v=obj.attributes["time"];
if(v.size()>0)
level.targetTime=atoi(v[0].c_str());
else
level.targetTime=-1;
//Get the target recordings of the level.
v=obj.attributes["recordings"];
if(v.size()>0)
level.targetRecordings=atoi(v[0].c_str());
else
level.targetRecordings=-1;
}
//The default for locked is true, unless it's the first one.
level.locked=!levels.empty();
level.won=false;
level.time=-1;
level.recordings=-1;
//Add the level to the levels.
levels.push_back(level);
}
}
loaded=true;
return true;
}
void LevelPack::loadProgress(const std::string& levelProgressFile){
//Open the levelProgress file.
ifstream levelProgress;
if(!levelProgressFile.empty()){
this->levelProgressFile=levelProgressFile;
levelProgress.open(processFileName(this->levelProgressFile).c_str());
}
//Check if the file exists.
if(levelProgress){
//Now load the progress/statistics.
TreeStorageNode obj;
{
POASerializer objSerializer;
if(!objSerializer.readNode(levelProgress,&obj,true)){
cerr<<"ERROR: Invalid file format of level progress file."<<endl;
}
}
//Loop through the entries.
for(unsigned int i=0;i<obj.subNodes.size();i++){
TreeStorageNode* obj1=obj.subNodes[i];
if(obj1==NULL)
continue;
if(obj1->value.size()>=1 && obj1->name=="level"){
//We've found an entry for a level, now search the correct level.
Level* level=NULL;
for(unsigned int o=0;o<levels.size();o++){
if(obj1->value[0]==levels[o].file){
level=&levels[o];
break;
}
}
//Check if we found the level.
if(!level)
continue;
//Get the progress/statistics.
for(map<string,vector<string> >::iterator i=obj1->attributes.begin();i!=obj1->attributes.end();i++){
if(i->first=="locked"){
level->locked=(i->second[0]=="1");
}
if(i->first=="won"){
level->won=(i->second[0]=="1");
}
if(i->first=="time"){
level->time=(atoi(i->second[0].c_str()));
}
if(i->first=="recordings"){
level->recordings=(atoi(i->second[0].c_str()));
}
}
}
}
}
}
void LevelPack::saveLevels(const std::string& levelListFile){
//Get the fileName.
string levelListNew=processFileName(levelListFile);
//Open an output stream.
ofstream level(levelListNew.c_str());
//Check if we can use the file.
if(!level){
cerr<<"ERROR: Can't save level list "<<levelListNew<<endl;
return;
}
//Storage node that will contain the data that should be written.
TreeStorageNode obj;
//Make sure that there's a description.
if(!levelpackDescription.empty())
obj.attributes["description"].push_back(levelpackDescription);
//Make sure that there's a congratulation text.
if(!congratulationText.empty())
obj.attributes["congratulations"].push_back(congratulationText);
//Add the levels to the file.
for(unsigned int i=0;i<levels.size();i++){
TreeStorageNode* obj1=new TreeStorageNode;
obj1->name="levelfile";
obj1->value.push_back(fileNameFromPath(levels[i].file));
obj1->value.push_back(levels[i].name);
obj.subNodes.push_back(obj1);
//We copy them to the levelpack folder
//Check if the levelpath is relative or absolute.
if(levels[i].file[0]=='%'){
copyFile(processFileName(levels[i].file).c_str(),(pathFromFileName(levelListNew)+fileNameFromPath(levels[i].file)).c_str());
}else{
//Make sure we aren't copying to the same location.
if((levelpackPath+levels[i].file)!=(pathFromFileName(levelListNew)+fileNameFromPath(levels[i].file))){
copyFile((levelpackPath+levels[i].file).c_str(),(pathFromFileName(levelListNew)+fileNameFromPath(levels[i].file)).c_str());
}
}
}
//Write the it away.
POASerializer objSerializer;
objSerializer.writeNode(&obj,level,false,true);
}
void LevelPack::addLevel(const string& levelFileName,int levelno){
//Fill in the details.
Level level;
if(!levelpackPath.empty() && levelFileName.compare(0,levelpackPath.length(),levelpackPath)==0){
level.file=fileNameFromPath(levelFileName);
}else{
level.file=levelFileName;
}
level.targetTime=0;
level.targetRecordings=0;
//Get the name of the level.
TreeStorageNode obj;
POASerializer objSerializer;
if(objSerializer.loadNodeFromFile(levelFileName.c_str(),&obj,true)){
//Calc the MD5 FIRST because query obj.attributes will modify internal structure.
obj.name.clear();
obj.calcMD5(level.md5Digest);
//Get the name of the level.
vector<string>& v=obj.attributes["name"];
if(v.size()>0)
level.name=v[0];
//If the name is empty then we set it to the file name.
if(level.name.empty())
level.name=fileNameFromPath(levelFileName);
//Get the target time of the level.
v=obj.attributes["time"];
if(v.size()>0)
level.targetTime=atoi(v[0].c_str());
else
level.targetTime=-1;
//Get the target recordings of the level.
v=obj.attributes["recordings"];
if(v.size()>0)
level.targetRecordings=atoi(v[0].c_str());
else
level.targetRecordings=-1;
}
//Set if it should be locked or not.
level.won=false;
level.time=-1;
level.recordings=-1;
level.locked=levels.size()>0?true:false;
//Check if the level should be at the end or somewhere in the middle.
if(levelno<0 || levelno>=int(levels.size())){
levels.push_back(level);
}else{
levels.insert(levels.begin()+levelno,level);
}
//NOTE: We set loaded to true.
loaded=true;
}
void LevelPack::moveLevel(unsigned int level1,unsigned int level2){
if(level1<0 || level1>=levels.size())
return;
if(level2<0 || level2>=levels.size())
return;
if(level1==level2)
return;
levels.insert(levels.begin()+level2,levels[level1]);
if(level2<=level1)
levels.erase(levels.begin()+level1+1);
else
levels.erase(levels.begin()+level1);
}
void LevelPack::saveLevelProgress(){
//Check if the levels are loaded and a progress file is given.
if(!loaded || levelProgressFile.empty())
return;
//Open the progress file.
ofstream levelProgress(processFileName(levelProgressFile).c_str());
if(!levelProgress)
return;
//Open an output stream.
TreeStorageNode node;
//Loop through the levels.
for(unsigned int o=0;o<levels.size();o++){
TreeStorageNode* obj=new TreeStorageNode;
node.subNodes.push_back(obj);
char s[64];
//Set the name of the node.
obj->name="level";
obj->value.push_back(levels[o].file);
//Set the values.
obj->attributes["locked"].push_back(levels[o].locked?"1":"0");
obj->attributes["won"].push_back(levels[o].won?"1":"0");
sprintf(s,"%d",levels[o].time);
obj->attributes["time"].push_back(s);
sprintf(s,"%d",levels[o].recordings);
obj->attributes["recordings"].push_back(s);
}
//Create a POASerializer and write away the leve node.
POASerializer objSerializer;
objSerializer.writeNode(&node,levelProgress,true,true);
}
const string& LevelPack::getLevelName(int level){
if(level<0)
level=currentLevel;
return levels[level].name;
}
const unsigned char* LevelPack::getLevelMD5(int level){
if(level<0)
level=currentLevel;
return levels[level].md5Digest;
}
void LevelPack::getLevelAutoSaveRecordPath(int level,std::string &bestTimeFilePath,std::string &bestRecordingFilePath,bool createPath){
if(level<0)
level=currentLevel;
bestTimeFilePath.clear();
bestRecordingFilePath.clear();
//get level pack path.
string levelpackPath=LevelPack::levelpackPath;
string s=levels[level].file;
//process level pack name
for(;;){
string::size_type lps=levelpackPath.find_last_of("/\\");
if(lps==string::npos){
break;
}else if(lps==levelpackPath.size()-1){
levelpackPath.resize(lps);
}else{
levelpackPath=levelpackPath.substr(lps+1);
break;
}
}
//profess file name
{
string::size_type lps=s.find_last_of("/\\");
if(lps!=string::npos) s=s.substr(lps+1);
}
//check if it's custom level
{
string path="%USER%/records/autosave/";
if(!levelpackPath.empty()){
path+=levelpackPath;
path+='/';
}
path=processFileName(path);
if(createPath) createDirectory(path.c_str());
s=path+s;
}
//calculate MD5
s+='-';
s+=Md5::toString(levels[level].md5Digest);
//over
bestTimeFilePath=s+"-best-time.mnmsrec";
bestRecordingFilePath=s+"-best-recordings.mnmsrec";
}
void LevelPack::setLevelName(unsigned int level,const std::string& name){
if(level<levels.size())
levels[level].name=name;
}
const string& LevelPack::getLevelFile(int level){
if(level<0)
level=currentLevel;
return levels[level].file;
}
const string& LevelPack::getLevelpackPath(){
return levelpackPath;
}
struct LevelPack::Level* LevelPack::getLevel(int level){
if(level<0)
return &levels[currentLevel];
return &levels[level];
}
void LevelPack::resetLevel(int level){
if(level<0)
level=currentLevel;
//Set back to default.
levels[level].locked=(level!=0);
levels[level].won=false;
levels[level].time=-1;
levels[level].recordings=-1;
}
void LevelPack::nextLevel(){
currentLevel++;
}
bool LevelPack::getLocked(unsigned int level){
return levels[level].locked;
}
void LevelPack::setCurrentLevel(unsigned int level){
currentLevel=level;
}
void LevelPack::setLocked(unsigned int level,bool locked){
levels[level].locked=locked;
}
void LevelPack::swapLevel(unsigned int level1,unsigned int level2){
if(level1<levels.size()&&level2<levels.size()){
swap(levels[level1],levels[level2]);
}
}
void LevelPack::removeLevel(unsigned int level){
if(level<levels.size()){
levels.erase(levels.begin()+level);
}
}
diff --git a/src/LevelPack.h b/src/LevelPack.h
index f9a14b5..a666e85 100644
--- a/src/LevelPack.h
+++ b/src/LevelPack.h
@@ -1,167 +1,182 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#ifndef LEVELPACK_H
#define LEVELPACK_H
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_ttf.h>
#include <vector>
#include <string>
#include "GameObjects.h"
#include "Player.h"
+#include "libs/tinygettext/tinygettext.hpp"
+
class LevelPack{
public:
//A level entry structure.
struct Level{
//The name of the level.
string name;
//The filename of the level.
string file;
//Boolean if the level is locked.
bool locked;
//Boolean if the level is won.
bool won;
//Integer containing the number of ticks (40 = 1s) it took to finish the level.
//If there's no time the value will be -1.
int time;
//Integer containing the target time to get a medal.
int targetTime;
//Integer containing the number of recordings used to finish the level.
//When not won the value is -1.
int recordings;
//Integer containing the target recordings to get a medal.
int targetRecordings;
//MD5 of level node. :/
unsigned char md5Digest[16];
};
private:
//Index of the current level.
int currentLevel;
//Boolean if the levels are loaded.
bool loaded;
//Vector containing the filenames of the levels.
std::vector<Level> levels;
//The file name of the level progress.
std::string levelProgressFile;
public:
//The name of the levelpack.
std::string levelpackName;
//The location the levelpack is stored.
std::string levelpackPath;
//A description of the levelpack.
std::string levelpackDescription;
//The text that will be displayed when the levels are finished.
std::string congratulationText;
+ //The dictionaryManager of the levelpack, used to translate strings.
+ tinygettext::DictionaryManager* dictionaryManager;
+
//Constructor.
LevelPack():currentLevel(0),loaded(false),levels(){};
+ //gettext function
+ //message: The message to translate.
+ inline const char* _(const std::string& message){
+ if(dictionaryManager){
+ return dictionaryManager->get_dictionary().translate(message).c_str();
+ }else{
+ return message.c_str();
+ }
+ }
+
//Adds a level to the levels.
//levelFileName: The filename of the level to add.
//level: The index of the level to add.
void addLevel(const std::string& levelFileName,int levelno=-1);
//Removes a level from the levels.
//level: The index of the level to remove.
void removeLevel(unsigned int level);
//Moves the level to a given index.
//level1: The level to move.
//level2: The destination.
void moveLevel(unsigned int level1,unsigned int level2);
//Swaps two level.
//level1: The first level to swap.
//level2: The second level to swap.
void swapLevel(unsigned int level1,unsigned int level2);
//Get the levelFile for a given level.
//level: The level index to get the levelFileName from.
//Returns: String containing the levelFileName.
const std::string& getLevelFile(int level=-1);
//Get the levelpackPath of the levels.
//Returns: String containing the levelpackPath.
const std::string& getLevelpackPath();
//Get the levelName for a given level.
//level: The level index to get the levelName from.
//Returns: String containing the levelName.
const std::string& getLevelName(int level=-1);
//Sets the levelName for a given level.
//level: The level index to get the levelName from.
//name: The new name of the level.
void setLevelName(unsigned int level,const std::string& name);
//Get the MD5 for a given level.
//level: The level index.
//Returns: const unsigned char[16] contains the digest.
const unsigned char* getLevelMD5(int level=-1);
//get level's auto-save record path,
//using level's MD5, file name and other information.
void getLevelAutoSaveRecordPath(int level,std::string &bestTimeFilePath,std::string &bestRecordingFilePath,bool createPath);
//Set the currentLevel.
//level: The new current level.
void setCurrentLevel(unsigned int level);
//Get the currentLevel.
//Returns: The currentLevel.
inline int getCurrentLevel(){return currentLevel;}
//Get the levelCount.
//Returns: The level count.
inline int getLevelCount(){return levels.size();}
//Method that will return the requested level.
//level: The index of the level, default is the current level.
//Returns: Pointer to the requested level structure.
struct Level* getLevel(int level=-1);
//Method that will reset any progress/statistics for a given level.
//level: The index of the level to reset, default is currentLevel.
void resetLevel(int level=-1);
//Check if a certain level is locked.
//level: The index of the level to check.
//Returns: True if the level is locked.
bool getLocked(unsigned int level);
//Set a level locked or not.
//level: The level to (un)lock.
//locked: The new status of the level, default is unlocked (false).
void setLocked(unsigned int level,bool locked=false);
//Empties the levels.
void clear();
bool loadLevels(const std::string& levelListFile);
void loadProgress(const std::string& levelProgressFile);
void saveLevels(const std::string& levelListFile);
void saveLevelProgress();
void nextLevel();
};
#endif
diff --git a/src/LevelPlaySelect.cpp b/src/LevelPlaySelect.cpp
index 7af433f..a16fd9a 100644
--- a/src/LevelPlaySelect.cpp
+++ b/src/LevelPlaySelect.cpp
@@ -1,427 +1,431 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include "LevelPlaySelect.h"
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "Objects.h"
#include "LevelSelect.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIScrollBar.h"
#include "InputManager.h"
#include "Game.h"
#include <SDL/SDL_ttf.h>
#include <SDL/SDL.h>
#include <stdio.h>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
/////////////////////LEVEL SELECT/////////////////////
static string levelDescription,levelMedal2,levelMedal3;
static string bestTimeFilePath,bestRecordingFilePath;
LevelPlaySelect::LevelPlaySelect():LevelSelect(_("Select Level")){
//Load the play button if needed.
playButtonImage=loadImage(getDataPath()+"gfx/playbutton.png");
timeIcon=loadImage(getDataPath()+"gfx/time.png");
recordingsIcon=loadImage(getDataPath()+"gfx/recordings.png");
play=new GUIObject(SCREEN_WIDTH-240,SCREEN_HEIGHT-60,240,32,GUIObjectButton,_("Play"));
play->name="cmdPlay";
play->eventCallback=this;
play->enabled=false;
GUIObjectRoot->childControls.push_back(play);
//show level list
refresh();
}
LevelPlaySelect::~LevelPlaySelect(){
play=NULL;
recordingsIcon=NULL;
timeIcon=NULL;
}
void LevelPlaySelect::refresh(){
int m=levels->getLevelCount();
numbers.clear();
//clear the selected level
if(selectedNumber!=NULL){
delete selectedNumber;
selectedNumber=NULL;
}
//Recreate the non selected number.
selectedNumber=new Number();
SDL_Rect box={40,SCREEN_HEIGHT-130,50,50};
selectedNumber->init(" ",box);
selectedNumber->setLocked(true);
levelDescription=_("Choose a level");
levelMedal2=string(_("Time:"))+" - / -";
levelMedal3=string(_("Recordings:"))+" - / -";
bestTimeFilePath.clear();
bestRecordingFilePath.clear();
//Disable the play button.
play->enabled=false;
for(int n=0; n<m; n++){
numbers.push_back(Number());
}
for(int n=0; n<m; n++){
SDL_Rect box={(n%10)*64+80,(n/10)*64+184,0,0};
numbers[n].init(n,box);
numbers[n].setLocked(levels->getLocked(n));
int medal=levels->getLevel(n)->won;
if(medal){
if(levels->getLevel(n)->targetTime<0 || levels->getLevel(n)->time<=levels->getLevel(n)->targetTime)
medal++;
if(levels->getLevel(n)->targetRecordings<0 || levels->getLevel(n)->recordings<=levels->getLevel(n)->targetRecordings)
medal++;
}
numbers[n].setMedal(medal);
}
if(m>LEVELS_DISPLAYED_IN_SCREEN){
levelScrollBar->maxValue=(m-LEVELS_DISPLAYED_IN_SCREEN+9)/10;
levelScrollBar->visible=true;
}else{
levelScrollBar->maxValue=0;
levelScrollBar->visible=false;
}
- levelpackDescription->caption=levels->levelpackDescription;
- int width;
- TTF_SizeText(fontText,levels->levelpackDescription.c_str(),&width,NULL);
- levelpackDescription->width=width;
- levelpackDescription->left=(SCREEN_WIDTH-width)/2;
+ if(!levels->levelpackDescription.empty()){
+ levelpackDescription->caption=levels->_(levels->levelpackDescription);
+ int width;
+ TTF_SizeText(fontText,levels->_(levels->levelpackDescription),&width,NULL);
+ levelpackDescription->width=width;
+ levelpackDescription->left=(SCREEN_WIDTH-width)/2;
+ }else{
+ levelpackDescription->caption="";
+ }
}
void LevelPlaySelect::selectNumber(unsigned int number,bool selected){
if(selected){
levels->setCurrentLevel(number);
setNextState(STATE_GAME);
//Pick music from the current music list.
getMusicManager()->pickMusic();
}else{
displayLevelInfo(number);
}
}
void LevelPlaySelect::checkMouse(){
int x,y;
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
//Check if we should replay the record.
if(selectedNumber!=NULL){
SDL_Rect mouse={x,y,0,0};
if(!bestTimeFilePath.empty()){
SDL_Rect box={380,470,372,32};
if(checkCollision(box,mouse)){
Game::recordFile=bestTimeFilePath;
levels->setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_GAME);
//Pick music from the current music list.
getMusicManager()->pickMusic();
return;
}
}
if(!bestRecordingFilePath.empty()){
SDL_Rect box={380,502,372,32};
if(checkCollision(box,mouse)){
Game::recordFile=bestRecordingFilePath;
levels->setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_GAME);
//Pick music from the current music list.
getMusicManager()->pickMusic();
return;
}
}
}
//Call the base method from the super class.
LevelSelect::checkMouse();
}
void LevelPlaySelect::displayLevelInfo(int number){
//Update currently selected level
if(selectedNumber==NULL){
selectedNumber=new Number();
}
SDL_Rect box={40,SCREEN_HEIGHT-130,50,50};
selectedNumber->init(number,box);
selectedNumber->setLocked(false);
//Show level description
levelDescription=levels->getLevelName(number);
//Show level medal
int medal=levels->getLevel(number)->won;
int time=levels->getLevel(number)->time;
int targetTime=levels->getLevel(number)->targetTime;
int recordings=levels->getLevel(number)->recordings;
int targetRecordings=levels->getLevel(number)->targetRecordings;
if(medal){
if(targetTime<0){
medal=-1;
}else{
if(targetTime<0 || time<=targetTime)
medal++;
if(targetRecordings<0 || recordings<=targetRecordings)
medal++;
}
}
selectedNumber->setMedal(medal);
//Show best time and recordings TODO: don't include text and value in same string!
if(medal){
char s[64];
if(time>0)
if(targetTime>0)
sprintf(s,"%-.2fs / %-.2fs",time/40.0f,targetTime/40.0f);
else
sprintf(s,"%-.2fs / -",time/40.0f);
else
s[0]='\0';
levelMedal2=string(_("Time:"))+" "+s;
if(recordings>=0)
if(targetRecordings>=0)
sprintf(s,"%5d / %d",recordings,targetRecordings);
else
sprintf(s,"%5d / -",recordings);
else
s[0]='\0';
levelMedal3=string(_("Recordings:"))+" "+s;
}else{
levelMedal2=string(_("Time:"))+" - / -";
levelMedal3=string(_("Recordings:"))+" - / -";
}
//Show the play button.
play->enabled=true;
//Check if there is auto record file
levels->getLevelAutoSaveRecordPath(number,bestTimeFilePath,bestRecordingFilePath,false);
if(!bestTimeFilePath.empty()){
FILE *f;
f=fopen(bestTimeFilePath.c_str(),"rb");
if(f==NULL){
bestTimeFilePath.clear();
}else{
fclose(f);
}
}
if(!bestRecordingFilePath.empty()){
FILE *f;
f=fopen(bestRecordingFilePath.c_str(),"rb");
if(f==NULL){
bestRecordingFilePath.clear();
}else{
fclose(f);
}
}
}
void LevelPlaySelect::render(){
//First let the levelselect render.
LevelSelect::render();
int x,y,dy=0,m=levels->getLevelCount();
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
if(levelScrollBar)
dy=levelScrollBar->value;
//Upper bound of levels we'd like to display.
if(m>dy*10+LEVELS_DISPLAYED_IN_SCREEN)
m=dy*10+LEVELS_DISPLAYED_IN_SCREEN;
y+=dy*64;
SDL_Rect mouse={x,y,0,0};
//Show currently selected level (if any)
if(selectedNumber!=NULL){
selectedNumber->show(0);
SDL_Color fg={0,0,0};
SDL_Surface* bm;
if(!levelDescription.empty()){
bm=TTF_RenderUTF8_Blended(fontText,levelDescription.c_str(),fg);
applySurface(100,SCREEN_HEIGHT-130+(50-bm->h)/2,bm,screen,NULL);
SDL_FreeSurface(bm);
}
//Only show the replay if the level is completed (won).
if(selectedNumber->getNumber()>=0 && selectedNumber->getNumber()<levels->getLevelCount()) {
if(levels->getLevel(selectedNumber->getNumber())->won){
if(!bestTimeFilePath.empty()){
SDL_Rect r={0,0,32,32};
SDL_Rect box={380,SCREEN_HEIGHT-130,372,32};
if(checkCollision(box,mouse)){
r.x=32;
SDL_FillRect(screen,&box,0xFFCCCCCC);
}
applySurface(720,SCREEN_HEIGHT-130,playButtonImage,screen,&r);
}
if(!bestRecordingFilePath.empty()){
SDL_Rect r={0,0,32,32};
SDL_Rect box={380,SCREEN_HEIGHT-98,372,32};
if(checkCollision(box,mouse)){
r.x=32;
SDL_FillRect(screen,&box,0xFFCCCCCC);
}
applySurface(720,SCREEN_HEIGHT-98,playButtonImage,screen,&r);
}
}
}
if(!levelMedal2.empty()){
//Draw the icon.
applySurface(395,SCREEN_HEIGHT-130+3,timeIcon,screen,NULL);
//Now draw the text.
bm=TTF_RenderUTF8_Blended(fontText,levelMedal2.c_str(),fg);
applySurface(420,SCREEN_HEIGHT-130+3,bm,screen,NULL);
SDL_FreeSurface(bm);
}
if(!levelMedal3.empty()){
//Draw the icon.
applySurface(395,SCREEN_HEIGHT-98+(6)/2,recordingsIcon,screen,NULL);
//Now draw the text.
bm=TTF_RenderUTF8_Blended(fontText,levelMedal3.c_str(),fg);
applySurface(420,SCREEN_HEIGHT-98+(32-bm->h)/2,bm,screen,NULL);
SDL_FreeSurface(bm);
}
}
}
void LevelPlaySelect::renderTooltip(unsigned int number,int dy){
SDL_Color fg={0,0,0};
char s[64];
//Render the name of the level.
- SDL_Surface* name=TTF_RenderUTF8_Blended(fontText,levels->getLevelName(number).c_str(),fg);
+ SDL_Surface* name=TTF_RenderUTF8_Blended(fontText,levels->_(levels->getLevelName(number)),fg);
SDL_Surface* time=NULL;
SDL_Surface* recordings=NULL;
//The time it took.
if(levels->getLevel(number)->time>0){
sprintf(s,"%-.2fs",levels->getLevel(number)->time/40.0f);
time=TTF_RenderUTF8_Blended(fontText,s,fg);
}
//The number of recordings it took.
if(levels->getLevel(number)->recordings>=0){
sprintf(s,"%d",levels->getLevel(number)->recordings);
recordings=TTF_RenderUTF8_Blended(fontText,s,fg);
}
//Now draw a square the size of the three texts combined.
SDL_Rect r=numbers[number].box;
r.y-=dy*64;
if(time!=NULL && recordings!=NULL){
r.w=(name->w)>(25+time->w+40+recordings->w)?(name->w):(25+time->w+40+recordings->w);
r.h=name->h+5+20;
}else{
r.w=name->w;
r.h=name->h;
}
//Make sure the tooltip doesn't go outside the window.
if(r.y>SCREEN_HEIGHT-200){
r.y-=name->h+4;
}else{
r.y+=numbers[number].box.h+2;
}
if(r.x+r.w>SCREEN_WIDTH-50)
r.x=SCREEN_WIDTH-50-r.w;
//Draw a rectange
Uint32 color=0xFFFFFF00|240;
drawGUIBox(r.x-5,r.y-5,r.w+10,r.h+10,screen,color);
//Calc the position to draw.
SDL_Rect r2=r;
//Now we render the name if the surface isn't null.
if(name!=NULL){
//Draw the name.
SDL_BlitSurface(name,NULL,screen,&r2);
}
//Increase the height to leave a gap between name and stats.
r2.y+=30;
if(time!=NULL){
//Now draw the time.
applySurface(r2.x,r2.y,timeIcon,screen,NULL);
r2.x+=25;
SDL_BlitSurface(time,NULL,screen,&r2);
r2.x+=time->w+15;
}
if(recordings!=NULL){
//Now draw the recordings.
applySurface(r2.x,r2.y,recordingsIcon,screen,NULL);
r2.x+=25;
SDL_BlitSurface(recordings,NULL,screen,&r2);
}
//And free the surfaces.
SDL_FreeSurface(name);
SDL_FreeSurface(time);
SDL_FreeSurface(recordings);
}
void LevelPlaySelect::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Let the level select handle his GUI events.
LevelSelect::GUIEventCallback_OnEvent(name,obj,eventType);
//Check for the play button.
if(name=="cmdPlay"){
if(selectedNumber!=NULL){
levels->setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_GAME);
//Pick music from the current music list.
getMusicManager()->pickMusic();
}
}
}
diff --git a/tools/extract_pot.py b/tools/extract_pot.py
new file mode 100644
index 0000000..b1ccd55
--- /dev/null
+++ b/tools/extract_pot.py
@@ -0,0 +1,108 @@
+import sys, os, os.path
+
+#The path to the levelpack.
+levelpackPath = ""
+
+#The file stream to write to.
+potFile = None
+
+#Some vars to keep track of where we are/get stuff from.
+curfile = ""
+curline = 1
+
+
+#Method that will create the .pot file and open the file stream.
+def openPotFile():
+ for root, dirs, files in os.walk(levelpackPath):
+ #Check if the directory 'locale' is present.
+ fullpath = os.path.join(root, 'locale')
+ if not os.path.exists(fullpath):
+ os.makedirs(fullpath)
+ fullpath = os.path.join(fullpath, 'messages.pot')
+ global potFile
+ potFile = open(fullpath,"w")
+ break
+
+
+#Method that loops through the files of the levelpack.
+def loopfiles():
+ for root, dirs, files in os.walk(levelpackPath):
+ for f in files:
+ fullpath = os.path.join(root, f)
+ global curfile
+ curfile = fullpath
+ looplines(fullpath)
+
+#Method that loops through the lines of a given file.
+def looplines(f):
+ fileInput = open(f,"r")
+ global curline
+ curline = 1
+ for line in fileInput:
+ lookup(line)
+ curline += 1
+ fileInput.close();
+
+#Method that looks up the line to check if it contains a translatable string.
+def lookup(line):
+ signs = 0
+ #Check for a '=' sign.
+ for c in line:
+ if c == '=':
+ signs += 1
+ if signs > 1:
+ print "WARNING: Multiple '=' signs, using first one."
+ if signs > 0:
+ key = line.split( "=" )[0].rstrip('\r\n"').lstrip('"').strip()
+ value = line.split( "=" )[1]
+
+ #Check if the key is a translatable one.
+ if key in ['congratulations', 'description', 'name', 'message']:
+ writeEntry('#: ' + curfile + ':' + str(curline), value)
+
+#Method that will write the header.
+def writeHeader():
+ potFile.write('# SOME DESCRIPTIVE TITLE.\n')
+ potFile.write('# Copyright (C) YEAR THE PACKAGE\'S COPYRIGHT HOLDER\n')
+ potFile.write('# This file is distributed under the same license as the PACKAGE package.\n')
+ potFile.write('# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n')
+ potFile.write('#\n')
+ potFile.write('#, fuzzy\n')
+ potFile.write('msgid ""\n')
+ potFile.write('msgstr ""\n')
+ potFile.write('"Project-Id-Version: PACKAGE VERSION\\n"\n')
+ potFile.write('"Report-Msgid-Bugs-To: \\n"\n')
+ potFile.write('"POT-Creation-Date: 2012-04-01 17:56+0300\\n"\n')
+ potFile.write('"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"\n')
+ potFile.write('"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"\n')
+ potFile.write('"Language-Team: LANGUAGE <LL@li.org>\\n"\n')
+ potFile.write('"Language: \\n"\n')
+ potFile.write('"MIME-Version: 1.0\\n"\n')
+ potFile.write('"Content-Type: text/plain; charset=CHARSET\\n"\n')
+ potFile.write('"Content-Transfer-Encoding: 8bit\\n"\n')
+ potFile.write('\n')
+
+#Method that performs the actual writing.
+def writeEntry(comment, msgid):
+ #Remove any trailing or leading '"' or '/n'
+ msgid = msgid.rstrip('\r\n"').lstrip('"')
+
+ #Write it to the file.
+ potFile.write(comment)
+ potFile.write('\n')
+ potFile.write('msgid "' + msgid + '"\n')
+ potFile.write('msgstr ""\n')
+ potFile.write('\n')
+
+#First check command line arguments.
+if len(sys.argv) != 2:
+ print "Usage: python ./extract_pot.py <path/to/levelpack/>"
+ sys.exit(0)
+
+#Set the levelpack path
+levelpackPath = sys.argv[1]
+
+openPotFile()
+writeHeader()
+loopfiles()
+

File Metadata

Mime Type
text/x-diff
Expires
Fri, Jun 19, 8:15 PM (1 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
71429
Default Alt Text
(116 KB)

Event Timeline