Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
158 KB
Referenced Files
None
Subscribers
None
diff --git a/data/gfx/menu/statistics.png b/data/gfx/menu/statistics.png
new file mode 100644
index 0000000..31b9c43
Binary files /dev/null and b/data/gfx/menu/statistics.png differ
diff --git a/data/locale/zh_CN.po b/data/locale/zh_CN.po
index 4b2e0e5..5fc3f57 100644
--- a/data/locale/zh_CN.po
+++ b/data/locale/zh_CN.po
@@ -1,1036 +1,1085 @@
# Simplified Chinese translation for Me and My Shadow
# Copyright (C) 2012 Me and My Shadow
# This file is distributed under the same license (GNU GPLv3) as the meandmyshadow package.
# acme_pjz <acme_pjz@hotmail.com>, 2012
#
msgid ""
msgstr ""
"Project-Id-Version: Me and my shadow\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-06-03 16:20+0200\n"
-"PO-Revision-Date: 2012-06-04 18:22+0800\n"
+"POT-Creation-Date: 2012-07-29 17:24+0800\n"
+"PO-Revision-Date: 2012-07-29 17:25+0800\n"
"Last-Translator: acme_pjz <acme_pjz@hotmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"X-Poedit-Language: Chinese\n"
"X-Poedit-Country: CHINA\n"
"X-Poedit-SourceCharset: utf-8\n"
"X-Poedit-Basepath: .\n"
+"X-Poedit-KeywordsList: _;__\n"
"X-Poedit-SearchPath-0: F:\\Projects\\meandmyshadow\\src\n"
-#: ../src/Addons.cpp:40
-#: ../src/TitleMenu.cpp:50
+#: F:\Projects\meandmyshadow\src/Addons.cpp:41
msgid "Addons"
msgstr "附加组件"
-#: ../src/Addons.cpp:58
+#: F:\Projects\meandmyshadow\src/Addons.cpp:59
msgid "Unable to initialize addon menu:"
msgstr "不能初始化附加组件菜单:"
-#: ../src/Addons.cpp:66
-#: ../src/Addons.cpp:115
-#: ../src/LevelEditSelect.cpp:69
-#: ../src/LevelSelect.cpp:186
+#: F:\Projects\meandmyshadow\src/Addons.cpp:67
+#: F:\Projects\meandmyshadow\src/Addons.cpp:116
msgid "Back"
msgstr "后退"
-#: ../src/Addons.cpp:97
+#: F:\Projects\meandmyshadow\src/Addons.cpp:98
msgid "Levels"
msgstr "关卡列表"
-#: ../src/Addons.cpp:98
+#: F:\Projects\meandmyshadow\src/Addons.cpp:99
msgid "Level Packs"
msgstr "关卡包"
-#: ../src/Addons.cpp:99
+#: F:\Projects\meandmyshadow\src/Addons.cpp:100
msgid "Themes"
msgstr "主题"
-#: ../src/Addons.cpp:119
-#: ../src/Addons.cpp:595
+#: F:\Projects\meandmyshadow\src/Addons.cpp:120
+#: F:\Projects\meandmyshadow\src/Addons.cpp:601
msgid "Install"
msgstr "安装"
-#: ../src/Addons.cpp:123
+#: F:\Projects\meandmyshadow\src/Addons.cpp:124
msgid "Update"
msgstr "升级"
-#: ../src/Addons.cpp:400
-#: ../src/Addons.cpp:417
-#: ../src/Addons.cpp:434
-#: ../src/Addons.cpp:461
-#: ../src/Addons.cpp:478
-#: ../src/Addons.cpp:492
+#: F:\Projects\meandmyshadow\src/Addons.cpp:135
+msgid "ERROR: unable to download addons file!"
+msgstr "错误:无法下载附加组件!"
+
+#: F:\Projects\meandmyshadow\src/Addons.cpp:147
+msgid "ERROR: unable to load addon_list file!"
+msgstr "错误:无法加载文件addon_list!"
+
+#: F:\Projects\meandmyshadow\src/Addons.cpp:158
+msgid "ERROR: Invalid file format of addons file!"
+msgstr "错误:文件addons的格式无效!"
+
+#: F:\Projects\meandmyshadow\src/Addons.cpp:179
+msgid "ERROR: Unable to create the installed_addons file."
+msgstr "错误:无法创建文件installed_addons。"
+
+#: F:\Projects\meandmyshadow\src/Addons.cpp:191
+msgid "ERROR: Invalid file format of the installed_addons!"
+msgstr "错误:文件installed_addons的格式无效!"
+
+#: F:\Projects\meandmyshadow\src/Addons.cpp:406
+#: F:\Projects\meandmyshadow\src/Addons.cpp:423
+#: F:\Projects\meandmyshadow\src/Addons.cpp:440
+#: F:\Projects\meandmyshadow\src/Addons.cpp:467
+#: F:\Projects\meandmyshadow\src/Addons.cpp:484
+#: F:\Projects\meandmyshadow\src/Addons.cpp:498
msgid "ERROR: Unable to download addon!"
msgstr "错误:无法下载附加组件"
-#: ../src/Addons.cpp:400
-#: ../src/Addons.cpp:417
-#: ../src/Addons.cpp:434
-#: ../src/Addons.cpp:461
-#: ../src/Addons.cpp:478
-#: ../src/Addons.cpp:492
+#: F:\Projects\meandmyshadow\src/Addons.cpp:406
+#: F:\Projects\meandmyshadow\src/Addons.cpp:423
+#: F:\Projects\meandmyshadow\src/Addons.cpp:440
+#: F:\Projects\meandmyshadow\src/Addons.cpp:467
+#: F:\Projects\meandmyshadow\src/Addons.cpp:484
+#: F:\Projects\meandmyshadow\src/Addons.cpp:498
msgid "ERROR:"
msgstr "错误:"
-#: ../src/Addons.cpp:590
+#: F:\Projects\meandmyshadow\src/Addons.cpp:596
msgid "Uninstall"
msgstr "卸载"
-#: ../src/Functions.cpp:592
-#: ../src/Functions.cpp:593
-#: ../src/Functions.cpp:594
+#: F:\Projects\meandmyshadow\src/Block.cpp:547
+msgid "On"
+msgstr "开启"
+
+#: F:\Projects\meandmyshadow\src/Block.cpp:548
+msgid "Off"
+msgstr "关闭"
+
+#: F:\Projects\meandmyshadow\src/Functions.cpp:598
+#: F:\Projects\meandmyshadow\src/Functions.cpp:599
+#: F:\Projects\meandmyshadow\src/Functions.cpp:600
msgid "knewave"
msgstr "DroidSansFallback"
-#: ../src/Functions.cpp:598
+#: F:\Projects\meandmyshadow\src/Functions.cpp:604
msgid "Blokletters-Viltstift"
msgstr "DroidSansFallback"
-#: ../src/Functions.cpp:1132
-#: ../src/Functions.cpp:1159
-#: ../src/Functions.cpp:1523
-#: ../src/InputManager.cpp:196
-#: ../src/LevelEditor.cpp:1017
-#: ../src/LevelEditor.cpp:1630
-#: ../src/LevelEditor.cpp:1687
-#: ../src/LevelEditor.cpp:1750
-#: ../src/LevelEditor.cpp:1830
-#: ../src/LevelEditor.cpp:1936
-#: ../src/LevelEditor.cpp:1996
-#: ../src/LevelEditSelect.cpp:175
-#: ../src/LevelEditSelect.cpp:215
-#: ../src/LevelEditSelect.cpp:263
+#: F:\Projects\meandmyshadow\src/Functions.cpp:656
+msgid "Loading..."
+msgstr "正在读取..."
+
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1181
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1208
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1546
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:233
msgid "OK"
msgstr "确定"
-#: ../src/Functions.cpp:1133
-#: ../src/Functions.cpp:1145
-#: ../src/Functions.cpp:1155
-#: ../src/Functions.cpp:1527
-#: ../src/LevelEditor.cpp:1021
-#: ../src/LevelEditor.cpp:1634
-#: ../src/LevelEditor.cpp:1691
-#: ../src/LevelEditor.cpp:1754
-#: ../src/LevelEditor.cpp:1834
-#: ../src/LevelEditor.cpp:1940
-#: ../src/LevelEditor.cpp:2000
-#: ../src/LevelEditSelect.cpp:179
-#: ../src/LevelEditSelect.cpp:219
-#: ../src/LevelEditSelect.cpp:267
-#: ../src/TitleMenu.cpp:451
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1182
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1194
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1204
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1550
msgid "Cancel"
msgstr "取消"
-#: ../src/Functions.cpp:1137
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1186
msgid "Abort"
msgstr "终止"
-#: ../src/Functions.cpp:1138
-#: ../src/Functions.cpp:1154
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1187
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1203
msgid "Retry"
msgstr "重试"
-#: ../src/Functions.cpp:1139
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1188
msgid "Ignore"
msgstr "忽略"
-#: ../src/Functions.cpp:1143
-#: ../src/Functions.cpp:1149
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1192
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1198
msgid "Yes"
msgstr "是"
-#: ../src/Functions.cpp:1144
-#: ../src/Functions.cpp:1150
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1193
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1199
msgid "No"
msgstr "否"
-#: ../src/Functions.cpp:1291
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1317
#, c-format
msgid ""
"%s already exists.\n"
"Do you want to overwrite it?"
msgstr ""
"%s 已经存在。\n"
"你是否想要覆盖它?"
-#: ../src/Functions.cpp:1291
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1317
msgid "Overwrite Prompt"
msgstr "文件覆盖提示"
-#: ../src/Functions.cpp:1312
-#: ../src/Functions.cpp:1330
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1338
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1356
#, c-format
msgid "Can't open file %s."
msgstr "不能打开文件 %s。"
-#: ../src/Functions.cpp:1312
-#: ../src/Functions.cpp:1330
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1338
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1356
msgid "Error"
msgstr "错误"
-#: ../src/Functions.cpp:1464
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1487
msgid "Save File"
msgstr "保存文件"
-#: ../src/Functions.cpp:1464
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1487
msgid "Load File"
msgstr "打开文件"
-#: ../src/Functions.cpp:1468
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1491
msgid "Search In"
msgstr "查找范围"
-#: ../src/Functions.cpp:1478
+#: F:\Projects\meandmyshadow\src/Functions.cpp:1501
msgid "File Name"
msgstr "文件名"
-#: ../src/Game.cpp:233
-#: ../src/Game.cpp:830
+#: F:\Projects\meandmyshadow\src/Game.cpp:245
+#: F:\Projects\meandmyshadow\src/Game.cpp:851
#, c-format
msgid "Level %d %s"
msgstr "第 %d 关 %s"
-#: ../src/Game.cpp:657
+#: F:\Projects\meandmyshadow\src/Game.cpp:669
#, c-format
msgid "Press %s key to save the game."
msgstr "按 %s 键来保存游戏。"
-#: ../src/Game.cpp:662
+#: F:\Projects\meandmyshadow\src/Game.cpp:674
#, c-format
msgid "Press %s key to swap the position of player and shadow."
msgstr "按 %s 键交换你和阴影的位置。"
-#: ../src/Game.cpp:667
+#: F:\Projects\meandmyshadow\src/Game.cpp:679
#, c-format
msgid "Press %s key to activate the switch."
msgstr "按 %s 键来激活开关。"
-#: ../src/Game.cpp:672
+#: F:\Projects\meandmyshadow\src/Game.cpp:684
#, c-format
msgid "Press %s key to teleport."
msgstr "按 %s 键传送。"
-#: ../src/Game.cpp:713
+#: F:\Projects\meandmyshadow\src/Game.cpp:725
#, c-format
msgid "Press %s to restart current level or press %s to load the game."
msgstr "按 %s 键重新开始游戏,或者按 %s 键读取进度。"
-#: ../src/Game.cpp:725
+#: F:\Projects\meandmyshadow\src/Game.cpp:737
#, c-format
msgid "Press %s to restart current level."
msgstr "按 %s 键重新开始游戏。"
-#: ../src/Game.cpp:739
+#: F:\Projects\meandmyshadow\src/Game.cpp:751
msgid "Your shadow has died."
msgstr "你的阴影死掉了。"
-#: ../src/Game.cpp:782
+#: F:\Projects\meandmyshadow\src/Game.cpp:803
#, c-format
msgid "%d recordings"
msgstr "记录数 %d"
-#: ../src/Game.cpp:822
+#: F:\Projects\meandmyshadow\src/Game.cpp:843
msgid "You've finished:"
msgstr "恭喜你完成了:"
-#: ../src/Game.cpp:1002
+#: F:\Projects\meandmyshadow\src/Game.cpp:1028
#, c-format
msgid "Time: %-.2fs"
msgstr "时间: %-.2f秒"
-#: ../src/Game.cpp:1011
+#: F:\Projects\meandmyshadow\src/Game.cpp:1037
#, c-format
msgid "Best time: %-.2fs"
msgstr "最佳时间: %-.2f秒"
-#: ../src/Game.cpp:1022
+#: F:\Projects\meandmyshadow\src/Game.cpp:1048
#, c-format
msgid "Target time: %-.2fs"
msgstr "目标时间: %-.2f秒"
-#: ../src/Game.cpp:1043
+#: F:\Projects\meandmyshadow\src/Game.cpp:1069
#, c-format
msgid "Recordings: %d"
msgstr "记录次数: %d"
-#: ../src/Game.cpp:1051
+#: F:\Projects\meandmyshadow\src/Game.cpp:1077
#, c-format
msgid "Best recordings: %d"
msgstr "最佳记录次数: %d"
-#: ../src/Game.cpp:1061
+#: F:\Projects\meandmyshadow\src/Game.cpp:1087
#, c-format
msgid "Target recordings: %d"
msgstr "目标记录次数: %d"
-#: ../src/Game.cpp:1074
+#: F:\Projects\meandmyshadow\src/Game.cpp:1100
#, c-format
msgid "You earned the %s medal"
msgstr "你拿到了%s"
-#: ../src/Game.cpp:1074
+#: F:\Projects\meandmyshadow\src/Game.cpp:1100
msgid "GOLD"
msgstr "金牌"
-#: ../src/Game.cpp:1074
+#: F:\Projects\meandmyshadow\src/Game.cpp:1100
msgid "SILVER"
msgstr "银牌"
-#: ../src/Game.cpp:1074
+#: F:\Projects\meandmyshadow\src/Game.cpp:1100
msgid "BRONZE"
msgstr "铜牌"
-#: ../src/Game.cpp:1089
+#: F:\Projects\meandmyshadow\src/Game.cpp:1115
msgid "Menu"
msgstr "菜单"
-#: ../src/Game.cpp:1096
-#: ../src/InputManager.cpp:41
+#: F:\Projects\meandmyshadow\src/Game.cpp:1122
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Restart"
msgstr "重新开始"
-#: ../src/Game.cpp:1103
+#: F:\Projects\meandmyshadow\src/Game.cpp:1129
msgid "Next"
msgstr "下一关"
-#: ../src/Game.cpp:1146
+#: F:\Projects\meandmyshadow\src/Game.cpp:1184
msgid "Game replay is done."
msgstr "游戏重放已经完成。"
-#: ../src/Game.cpp:1146
+#: F:\Projects\meandmyshadow\src/Game.cpp:1184
msgid "Game Replay"
msgstr "游戏重放"
-#: ../src/Game.cpp:1303
-#: ../src/Game.cpp:1305
+#: F:\Projects\meandmyshadow\src/Game.cpp:1355
+#: F:\Projects\meandmyshadow\src/Game.cpp:1357
msgid "Congratulations"
msgstr "恭喜你"
-#: ../src/Game.cpp:1305
+#: F:\Projects\meandmyshadow\src/Game.cpp:1357
msgid "You have finished the levelpack!"
msgstr "你已经完成了整个关卡包!"
-#: ../src/InputManager.cpp:40
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Up (in menu)"
msgstr "上(在菜单中)"
-#: ../src/InputManager.cpp:40
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Down (in menu)"
msgstr "下(在菜单中)"
-#: ../src/InputManager.cpp:40
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Left"
msgstr "左"
-#: ../src/InputManager.cpp:40
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Right"
msgstr "右"
-#: ../src/InputManager.cpp:40
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Jump"
msgstr "跳跃"
-#: ../src/InputManager.cpp:40
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Action"
msgstr "动作键"
-#: ../src/InputManager.cpp:40
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Space (Record)"
msgstr "空格(记录键)"
-#: ../src/InputManager.cpp:41
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Escape"
msgstr "退出"
-#: ../src/InputManager.cpp:41
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Tab (View shadow/Level prop.)"
msgstr "Tab (切换视角/关卡属性)"
-#: ../src/InputManager.cpp:41
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Save game (in editor)"
msgstr "保存游戏(在地图编辑器中)"
-#: ../src/InputManager.cpp:41
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Load game"
msgstr "读取游戏"
-#: ../src/InputManager.cpp:41
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Swap (in editor)"
msgstr "交换玩家和阴影的位置(在地图编辑器中)"
-#: ../src/InputManager.cpp:42
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:45
msgid "Teleport (in editor)"
msgstr "传送(在地图编辑器中)"
-#: ../src/InputManager.cpp:42
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:45
msgid "Suicide (in editor)"
msgstr "自杀(在地图编辑器中)"
-#: ../src/InputManager.cpp:42
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:45
msgid "Shift (in editor)"
msgstr "Shift (地图编辑器辅助按键)"
-#: ../src/InputManager.cpp:42
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:45
msgid "Next block type (in Editor)"
msgstr "下一个砖块类型(在地图编辑器中)"
-#: ../src/InputManager.cpp:43
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:46
msgid "Previous block type (in editor)"
msgstr "上一个砖块类型(在地图编辑器中)"
-#: ../src/InputManager.cpp:43
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:46
msgid "Select (in menu)"
msgstr "选择键(在菜单中)"
-#: ../src/InputManager.cpp:163
-#: ../src/TitleMenu.cpp:430
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:204
msgid "Config Keys"
msgstr "设置按键"
-#: ../src/InputManager.cpp:166
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:207
msgid "Select an item and press a key to config it."
msgstr "选择一个项目,然后按键进行设置。"
-#: ../src/InputManager.cpp:179
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:221
msgid "Primary key"
msgstr "主按键"
-#: ../src/InputManager.cpp:180
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:222
msgid "Alternative key"
msgstr "辅助按键"
-#: ../src/InputManager.cpp:186
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:228
msgid "Unset the key"
msgstr "取消设置"
-#: ../src/InputManager.cpp:300
+#: F:\Projects\meandmyshadow\src/InputManager.cpp:298
msgid "(Not set)"
msgstr "(未设置)"
-#: ../src/LevelEditor.cpp:121
-msgid "Toolbox"
-msgstr "工具箱"
-
-#: ../src/LevelEditor.cpp:180
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:62
msgid "Block"
msgstr "砖块"
-#: ../src/LevelEditor.cpp:180
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:62
msgid "Player Start"
msgstr "玩家起点"
-#: ../src/LevelEditor.cpp:180
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:62
msgid "Shadow Start"
msgstr "阴影起点"
-#: ../src/LevelEditor.cpp:181
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:63
msgid "Exit"
msgstr "终点"
-#: ../src/LevelEditor.cpp:181
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:63
msgid "Shadow Block"
msgstr "阴影砖块"
-#: ../src/LevelEditor.cpp:181
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:63
msgid "Spikes"
msgstr "带刺砖块"
-#: ../src/LevelEditor.cpp:182
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:64
msgid "Checkpoint"
msgstr "记录点"
-#: ../src/LevelEditor.cpp:182
-#: ../src/LevelEditSelect.cpp:258
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:64
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:290
msgid "Swap"
msgstr "交换点"
-#: ../src/LevelEditor.cpp:182
-#: ../src/LevelEditor.cpp:1976
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:64
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2461
msgid "Fragile"
msgstr "易碎砖块"
-#: ../src/LevelEditor.cpp:183
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:65
msgid "Moving Block"
msgstr "移动砖块"
-#: ../src/LevelEditor.cpp:183
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:65
msgid "Moving Shadow Block"
msgstr "移动阴影砖块"
-#: ../src/LevelEditor.cpp:183
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:65
msgid "Moving Spikes"
msgstr "移动的刺"
-#: ../src/LevelEditor.cpp:184
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:66
msgid "Teleporter"
msgstr "传送点"
-#: ../src/LevelEditor.cpp:184
-#: ../src/LevelEditor.cpp:1884
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:66
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2380
msgid "Button"
msgstr "按钮"
-#: ../src/LevelEditor.cpp:184
-#: ../src/LevelEditor.cpp:1886
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:66
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2382
msgid "Switch"
msgstr "开关"
-#: ../src/LevelEditor.cpp:185
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:67
msgid "Conveyor Belt"
msgstr "传送带"
-#: ../src/LevelEditor.cpp:185
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:67
msgid "Shadow Conveyor Belt"
msgstr "阴影传送带"
-#: ../src/LevelEditor.cpp:185
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:67
msgid "Notification Block"
msgstr "消息方块"
-#: ../src/LevelEditor.cpp:185
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:67
msgid "Collectable"
msgstr "可收集的物品"
-#: ../src/LevelEditor.cpp:529
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:153
+msgid "Toolbox"
+msgstr "工具箱"
+
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:505
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3117
+msgid "Select"
+msgstr "选择"
+
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:520
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3126
+msgid "Configure"
+msgstr "设置"
+
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:535
+msgid "Link"
+msgstr "链接"
+
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:550
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3123
+msgid "Delete"
+msgstr "删除"
+
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:939
msgid "Are you sure you want to quit?"
msgstr "你确定要退出吗?"
-#: ../src/LevelEditor.cpp:529
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:939
msgid "Quit prompt"
msgstr "退出提示"
-#: ../src/LevelEditor.cpp:957
-#: ../src/LevelEditor.cpp:959
-#: ../src/LevelEditor.cpp:2609
-#: ../src/LevelEditor.cpp:2611
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1038
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1040
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1498
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1500
#, c-format
msgid "Level \"%s\" saved"
msgstr "关卡“%s”已保存"
-#: ../src/LevelEditor.cpp:957
-#: ../src/LevelEditor.cpp:959
-#: ../src/LevelEditor.cpp:2609
-#: ../src/LevelEditor.cpp:2611
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1038
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1040
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1498
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1500
msgid "Saved"
msgstr "已保存"
-#: ../src/LevelEditor.cpp:972
-#: ../src/LevelEditor.cpp:2732
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1513
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3132
msgid "Level settings"
msgstr "关卡设置"
-#: ../src/LevelEditor.cpp:976
-#: ../src/LevelEditSelect.cpp:154
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1517
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:202
msgid "Name:"
msgstr "名称:"
-#: ../src/LevelEditor.cpp:982
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1523
msgid "Theme:"
msgstr "主题:"
-#: ../src/LevelEditor.cpp:997
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1538
msgid "Target time (s):"
msgstr "目标时间(秒):"
-#: ../src/LevelEditor.cpp:1008
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1549
msgid "Target recordings:"
msgstr "目标记录数:"
-#: ../src/LevelEditor.cpp:1576
-#: ../src/LevelEditor.cpp:1793
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2110
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2300
msgid "Defined"
msgstr "已设置"
-#: ../src/LevelEditor.cpp:1579
-#: ../src/LevelEditor.cpp:1796
-#: ../src/LevelEditor.cpp:1878
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2113
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2303
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2374
msgid "None"
msgstr "无"
-#: ../src/LevelEditor.cpp:1586
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2120
msgid "Moving block"
msgstr "移动砖块"
-#: ../src/LevelEditor.cpp:1589
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2123
msgid "Moving shadow block"
msgstr "移动阴影砖块"
-#: ../src/LevelEditor.cpp:1592
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2126
msgid "Moving spikes"
msgstr "移动的刺"
-#: ../src/LevelEditor.cpp:1599
-#: ../src/LevelEditor.cpp:1736
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2133
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2252
msgid "Enabled"
msgstr "启用"
-#: ../src/LevelEditor.cpp:1605
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2139
msgid "Loop"
msgstr "循环"
-#: ../src/LevelEditor.cpp:1611
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2145
msgid "Path"
msgstr "路径"
-#: ../src/LevelEditor.cpp:1671
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2196
msgid "Notification block"
msgstr "消息方块"
-#: ../src/LevelEditor.cpp:1674
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2199
msgid "Enter message here:"
msgstr "输入消息:"
-#: ../src/LevelEditor.cpp:1728
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2244
msgid "Shadow Conveyor belt"
msgstr "阴影传送带"
-#: ../src/LevelEditor.cpp:1730
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2246
msgid "Conveyor belt"
msgstr "传送带"
-#: ../src/LevelEditor.cpp:1742
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2258
msgid "Enter speed here:"
msgstr "输入速度:"
-#: ../src/LevelEditor.cpp:1800
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2307
msgid "Portal"
msgstr "传送门"
-#: ../src/LevelEditor.cpp:1803
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2310
msgid "Activate on touch"
msgstr "接触时传送"
-#: ../src/LevelEditor.cpp:1809
-#: ../src/LevelEditor.cpp:1914
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2316
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2410
msgid "Targets:"
msgstr "目标:"
-#: ../src/LevelEditor.cpp:1875
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2371
#, c-format
msgid "%d Defined"
msgstr "已设置 %d 个"
-#: ../src/LevelEditor.cpp:1891
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2387
msgid "Behaviour:"
msgstr "行为:"
-#: ../src/LevelEditor.cpp:1897
-msgid "On"
-msgstr "开启"
-
-#: ../src/LevelEditor.cpp:1898
-msgid "Off"
-msgstr "关闭"
-
-#: ../src/LevelEditor.cpp:1899
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2395
msgid "Toggle"
msgstr "切换"
-#: ../src/LevelEditor.cpp:1979
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2464
msgid "State:"
msgstr "状态:"
-#: ../src/LevelEditor.cpp:1985
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2470
msgid "Complete"
msgstr "完整的"
-#: ../src/LevelEditor.cpp:1986
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2471
msgid "One step"
msgstr "踩了一次"
-#: ../src/LevelEditor.cpp:1987
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2472
msgid "Two steps"
msgstr "踩了两次"
-#: ../src/LevelEditor.cpp:1988
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2473
msgid "Gone"
msgstr "已经破碎"
-#: ../src/LevelEditor.cpp:2717
-msgid "Select"
-msgstr "选择"
-
-#: ../src/LevelEditor.cpp:2720
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3120
msgid "Add"
msgstr "添加"
-#: ../src/LevelEditor.cpp:2723
-msgid "Delete"
-msgstr "删除"
-
-#: ../src/LevelEditor.cpp:2726
-msgid "Configure"
-msgstr "设置"
-
-#: ../src/LevelEditor.cpp:2729
-#: ../src/LevelPlaySelect.cpp:65
-#: ../src/TitleMenu.cpp:47
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3129
+#: F:\Projects\meandmyshadow\src/LevelPlaySelect.cpp:66
msgid "Play"
msgstr "开始游戏"
-#: ../src/LevelEditor.cpp:2735
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3135
msgid "Save level"
msgstr "保存关卡"
-#: ../src/LevelEditor.cpp:2738
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3138
msgid "Back to menu"
msgstr "回主菜单"
-#: ../src/LevelEditor.cpp:2776
-#: ../src/LevelEditor.cpp:2782
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3187
+#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3193
#, c-format
msgid "Movespeed: %s"
msgstr "移动速度: %s"
-#: ../src/LevelEditSelect.cpp:41
-#: ../src/TitleMenu.cpp:49
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:43
msgid "Map Editor"
msgstr "地图编辑器"
-#: ../src/LevelEditSelect.cpp:78
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:80
msgid "New Levelpack"
msgstr "新建关卡包"
-#: ../src/LevelEditSelect.cpp:83
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:85
msgid "Pack Properties"
msgstr "关卡包属性"
-#: ../src/LevelEditSelect.cpp:88
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:90
msgid "Remove Pack"
msgstr "删除关卡包"
-#: ../src/LevelEditSelect.cpp:93
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:95
msgid "Move Map"
msgstr "移动地图"
-#: ../src/LevelEditSelect.cpp:99
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:103
msgid "Remove Map"
msgstr "删除地图"
-#: ../src/LevelEditSelect.cpp:104
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:108
msgid "Edit Map"
msgstr "编辑地图"
-#: ../src/LevelEditSelect.cpp:151
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:199
msgid "Properties"
msgstr "属性"
-#: ../src/LevelEditSelect.cpp:161
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:211
msgid "Description:"
msgstr "描述:"
-#: ../src/LevelEditSelect.cpp:168
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:220
msgid "Congratulation text:"
msgstr "完成提示:"
-#: ../src/LevelEditSelect.cpp:203
-#: ../src/LevelEditSelect.cpp:371
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:248
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:401
msgid "Add level"
msgstr "增加关卡"
-#: ../src/LevelEditSelect.cpp:206
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:251
msgid "File name:"
msgstr "文件名:"
-#: ../src/LevelEditSelect.cpp:243
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:275
msgid "Move level"
msgstr "移动关卡"
-#: ../src/LevelEditSelect.cpp:246
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:278
msgid "Level: "
msgstr "关卡:"
-#: ../src/LevelEditSelect.cpp:256
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:288
msgid "Before"
msgstr "之前"
-#: ../src/LevelEditSelect.cpp:257
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:289
msgid "After"
msgstr "之后"
-#: ../src/LevelEditSelect.cpp:439
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:464
msgid "Are you sure?"
msgstr "你确定吗?"
-#: ../src/LevelEditSelect.cpp:439
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:464
msgid "Remove prompt"
msgstr "删除提示"
-#: ../src/LevelEditSelect.cpp:579
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:609
msgid "No file name given for the new level."
msgstr "没有给新关卡指定文件名。"
-#: ../src/LevelEditSelect.cpp:579
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:609
msgid "Missing file name"
msgstr "文件名未指定"
-#: ../src/LevelEditSelect.cpp:656
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:686
msgid "The entered level number isn't valid!"
msgstr "输入的关卡编号无效!"
-#: ../src/LevelEditSelect.cpp:656
+#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:686
msgid "Illegal number"
msgstr "无效编号"
-#: ../src/LevelPlaySelect.cpp:43
+#: F:\Projects\meandmyshadow\src/LevelPlaySelect.cpp:44
msgid "Select Level"
msgstr "选择关卡"
-#: ../src/LevelPlaySelect.cpp:92
+#: F:\Projects\meandmyshadow\src/LevelPlaySelect.cpp:93
msgid "Choose a level"
msgstr "选择一个关卡"
-#: ../src/LevelPlaySelect.cpp:93
-#: ../src/LevelPlaySelect.cpp:231
-#: ../src/LevelPlaySelect.cpp:242
+#: F:\Projects\meandmyshadow\src/LevelPlaySelect.cpp:94
msgid "Time:"
msgstr "时间:"
-#: ../src/LevelPlaySelect.cpp:94
-#: ../src/LevelPlaySelect.cpp:240
-#: ../src/LevelPlaySelect.cpp:243
+#: F:\Projects\meandmyshadow\src/LevelPlaySelect.cpp:95
msgid "Recordings:"
msgstr "记录次数:"
-#: ../src/TitleMenu.cpp:48
+#: F:\Projects\meandmyshadow\src/StatisticsManager.cpp:269
+msgid "New achievement:"
+msgstr "新成就:"
+
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:49
msgid "Options"
msgstr "选项"
-#: ../src/TitleMenu.cpp:51
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:52
msgid "Quit"
msgstr "退出"
-#: ../src/TitleMenu.cpp:109
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:120
msgid "Enable internet in order to install addons."
msgstr "启用网络访问,才能下载附加组件。"
-#: ../src/TitleMenu.cpp:109
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:120
msgid "Internet disabled"
msgstr "网络已禁用"
-#: ../src/TitleMenu.cpp:188
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:203
msgid "Settings"
msgstr "选项"
-#: ../src/TitleMenu.cpp:236
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:247
msgid "Music"
msgstr "音乐"
-#: ../src/TitleMenu.cpp:244
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:255
msgid "Sound"
msgstr "音效"
-#: ../src/TitleMenu.cpp:252
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:263
msgid "Fullscreen"
msgstr "全屏幕"
-#: ../src/TitleMenu.cpp:257
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:268
msgid "Resolution"
msgstr "分辨率"
-#: ../src/TitleMenu.cpp:339
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:350
msgid "Language"
msgstr "语言"
-#: ../src/TitleMenu.cpp:348
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:359
msgid "Auto-Detect"
msgstr "自动检测"
-#: ../src/TitleMenu.cpp:376
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:387
msgid "Theme"
msgstr "主题"
-#: ../src/TitleMenu.cpp:409
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:420
msgid "Level themes"
msgstr "默认主题"
-#: ../src/TitleMenu.cpp:414
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:425
msgid "Internet"
msgstr "网络"
-#: ../src/TitleMenu.cpp:420
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:431
msgid "Internet proxy"
msgstr "网络代理"
-#: ../src/TitleMenu.cpp:437
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:448
msgid "Clear Progress"
msgstr "清除进度"
-#: ../src/TitleMenu.cpp:456
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:467
msgid "Save Changes"
msgstr "保存变更"
-#: ../src/TitleMenu.cpp:564
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:577
msgid "Do you really want to reset level progress?"
msgstr "你确定要清除游戏进度吗?"
-#: ../src/TitleMenu.cpp:564
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:577
msgid "Warning"
msgstr "警告"
+#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:674
+msgid "Credits"
+msgstr "作者"
+
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:1
msgid "backspace"
msgstr "backspace"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:1
msgid "tab"
msgstr "tab"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:1
msgid "clear"
msgstr "clear"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:1
msgid "return"
msgstr "回车"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:1
msgid "pause"
msgstr "pause"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:1
msgid "escape"
msgstr "escape"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:1
msgid "space"
msgstr "空格"
+#: F:\Projects\meandmyshadow\src/dummy.h:1
msgid "delete"
msgstr "delete"
+#: F:\Projects\meandmyshadow\src/dummy.h:1
msgid "enter"
msgstr "enter"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:1
msgid "equals"
msgstr "equals"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:2
msgid "up"
msgstr "上"
+#: F:\Projects\meandmyshadow\src/dummy.h:2
msgid "down"
msgstr "下"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:2
msgid "right"
msgstr "右"
+#: F:\Projects\meandmyshadow\src/dummy.h:2
msgid "left"
msgstr "左"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:2
msgid "insert"
msgstr "insert"
+#: F:\Projects\meandmyshadow\src/dummy.h:2
msgid "home"
msgstr "home"
+#: F:\Projects\meandmyshadow\src/dummy.h:2
msgid "end"
msgstr "end"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:2
msgid "page up"
msgstr "page up"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:2
msgid "page down"
msgstr "page down"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:2
msgid "numlock"
msgstr "numlock"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:2
msgid "caps lock"
msgstr "caps lock"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:3
msgid "scroll lock"
msgstr "scroll lock"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:3
msgid "right shift"
msgstr "右 shift"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:3
msgid "left shift"
msgstr "左 shift"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:3
msgid "right ctrl"
msgstr "右 ctrl"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:3
msgid "left ctrl"
msgstr "左 ctrl"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:3
msgid "right alt"
msgstr "右 alt"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:3
msgid "left alt"
msgstr "左 alt"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:4
msgid "right meta"
msgstr "右 meta"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:4
msgid "left meta"
msgstr "左 meta"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:4
msgid "left super"
msgstr "左 super"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:4
msgid "right super"
msgstr "右 super"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:4
msgid "alt gr"
msgstr "alt gr"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:4
msgid "compose"
msgstr "compose"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:4
msgid "help"
msgstr "help"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:4
msgid "print screen"
msgstr "print screen"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:5
msgid "sys req"
msgstr "sys req"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:5
msgid "break"
msgstr "break"
+#: F:\Projects\meandmyshadow\src/dummy.h:5
msgid "menu"
msgstr "menu"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:5
msgid "power"
msgstr "power"
# TRANSLATORS: name of a key
+#: F:\Projects\meandmyshadow\src/dummy.h:5
msgid "euro"
msgstr "euro"
+#: F:\Projects\meandmyshadow\src/dummy.h:5
msgid "undo"
msgstr "undo"
-msgid "Credits"
-msgstr "作者"
diff --git a/src/Functions.cpp b/src/Functions.cpp
index 0944c88..aa82d5e 100644
--- a/src/Functions.cpp
+++ b/src/Functions.cpp
@@ -1,1587 +1,1591 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_gfxPrimitives.h>
#include <SDL/SDL_rotozoom.h>
#include <SDL/SDL_syswm.h>
#include <string>
#include "Globals.h"
#include "Functions.h"
#include "FileManager.h"
#include "Objects.h"
#include "Player.h"
#include "GameObjects.h"
#include "LevelPack.h"
#include "TitleMenu.h"
#include "LevelEditSelect.h"
#include "LevelEditor.h"
#include "Game.h"
#include "LevelPlaySelect.h"
#include "Addons.h"
#include "InputManager.h"
#include "ImageManager.h"
#include "MusicManager.h"
#include "LevelPackManager.h"
#include "ThemeManager.h"
#include "GUIListBox.h"
#include "GUIOverlay.h"
#include "StatisticsManager.h"
+#include "StatisticsScreen.h"
#include "libs/tinyformat/tinyformat.h"
#include "libs/tinygettext/tinygettext.hpp"
#include "libs/tinygettext/log.hpp"
extern "C" {
#include "libs/findlocale/findlocale.h"
}
#ifdef HARDWARE_ACCELERATION
#include <GL/gl.h>
#include <GL/glu.h>
//fix some Windows header bug
#ifndef GL_BGR
#define GL_BGR GL_BGR_EXT
#endif
#ifndef GL_BGRA
#define GL_BGRA GL_BGRA_EXT
#endif
#endif
using namespace std;
#ifdef WIN32
#include <windows.h>
#include <shlobj.h>
#else
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#endif
//Workaround for the resizing below 800x600 for X systems.
#ifdef __linux__
#include<X11/Xlib.h>
#include<X11/Xutil.h>
#endif
//Initialise the imagemanager.
//The ImageManager is used to prevent loading images multiple times.
ImageManager imageManager;
//Initialise the musicManager.
//The MusicManager is used to prevent loading music files multiple times and for playing/fading music.
MusicManager musicManager;
//Initialise the levelPackManager.
//The LevelPackManager is used to prevent loading levelpacks multiple times and for the game to know which levelpacks there are.
LevelPackManager levelPackManager;
//Map containing changed settings using command line arguments.
map<string,string> tmpSettings;
//Pointer to the settings object.
//It is used to load and save the settings file and change the settings.
Settings* settings=0;
#ifdef HARDWARE_ACCELERATION
GLuint screenTexture;
#endif
SDL_Surface* loadImage(string file){
//We use the imageManager to load the file.
return imageManager.loadImage(file);
}
void applySurface(int x,int y,SDL_Surface* source,SDL_Surface* dest,SDL_Rect* clip){
//The offset is needed to draw at the right location.
SDL_Rect offset;
offset.x=x;
offset.y=y;
//Let SDL do the drawing of the surface.
SDL_BlitSurface(source,clip,dest,&offset);
}
void drawRect(int x,int y,int w,int h,SDL_Surface* dest,Uint32 color){
//NOTE: We let SDL_gfx render it.
rectangleRGBA(dest,x,y,x+w,y+h,color >> 24,color >> 16,color >> 8,255);
}
//Draw a box with anti-aliased borders using SDL_gfx.
void drawGUIBox(int x,int y,int w,int h,SDL_Surface* dest,Uint32 color){
//Fill content's background color from function parameter
boxRGBA(dest,x+1,y+1,x+w-2,y+h-2,color >> 24,color >> 16,color >> 8,color >> 0);
//Draw first black borders around content and leave 1 pixel in every corner
lineRGBA(dest,x+1,y,x+w-2,y,0,0,0,255);
lineRGBA(dest,x+1,y+h-1,x+w-2,y+h-1,0,0,0,255);
lineRGBA(dest,x,y+1,x,y+h-2,0,0,0,255);
lineRGBA(dest,x+w-1,y+1,x+w-1,y+h-2,0,0,0,255);
//Fill the corners with transperent color to create anti-aliased borders
pixelRGBA(dest,x,y,0,0,0,160);
pixelRGBA(dest,x,y+h-1,0,0,0,160);
pixelRGBA(dest,x+w-1,y,0,0,0,160);
pixelRGBA(dest,x+w-1,y+h-1,0,0,0,160);
//Draw second lighter border around content
rectangleRGBA(dest,x+1,y+1,x+w-2,y+h-2,0,0,0,64);
//Create anti-aliasing in corners of second border
pixelRGBA(dest,x+1,y+1,0,0,0,50);
pixelRGBA(dest,x+1,y+h-2,0,0,0,50);
pixelRGBA(dest,x+w-2,y+1,0,0,0,50);
pixelRGBA(dest,x+w-2,y+h-2,0,0,0,50);
}
void drawLine(int x1,int y1,int x2,int y2,SDL_Surface* dest,Uint32 color){
//NOTE: We let SDL_gfx render it.
lineRGBA(dest,x1,y1,x2,y2,color >> 24,color >> 16,color >> 8,255);
}
void drawLineWithArrow(int x1,int y1,int x2,int y2,SDL_Surface* dest,Uint32 color,int spacing,int offset,int xsize,int ysize){
//Draw line first
drawLine(x1,y1,x2,y2,dest,color);
//calc delta and length
double dx=x2-x1;
double dy=y2-y1;
double length=sqrt(dx*dx+dy*dy);
if(length<0.001) return;
//calc the unit vector
dx/=length; dy/=length;
//Now draw arrows on it
for(double p=offset;p<length;p+=spacing){
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx-ysize*dy+0.5),int(y1+(p-xsize)*dy+ysize*dx+0.5),dest,color);
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx+ysize*dy+0.5),int(y1+(p-xsize)*dy-ysize*dx+0.5),dest,color);
}
}
bool createScreen(){
//Check if we are going fullscreen.
if(settings->getBoolValue("fullscreen"))
pickFullscreenResolution();
//Set the screen_width and height.
SCREEN_WIDTH=atoi(settings->getValue("width").c_str());
SCREEN_HEIGHT=atoi(settings->getValue("height").c_str());
//Update the camera.
camera.w=SCREEN_WIDTH;
camera.h=SCREEN_HEIGHT;
//Boolean if this is the first screen creation.
bool initial=true;
//Check if we should use gl or software rendering.
if(settings->getBoolValue("gl")){
#ifdef HARDWARE_ACCELERATION
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,16);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE,32);
SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,0);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
//Set the video mode.
Uint32 flags=SDL_HWSURFACE | SDL_OPENGL;
if(settings->getBoolValue("fullscreen"))
flags|=SDL_FULLSCREEN;
else if(settings->getBoolValue("resizable"))
flags|=SDL_RESIZABLE;
if(SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,flags)==NULL){
fprintf(stderr,"FATAL ERROR: SDL_SetVideoMode failed\n");
return false;
}
//Delete the old screen.
//Warning: only if previous mode is OpenGL mode.
//NOTE: The previous mode can't switch during runtime.
if(screen){
SDL_FreeSurface(screen);
screen=NULL;
//There was a screen so this isn't the initial screen creation.
initial=false;
}
//Create a screen
screen=SDL_CreateRGBSurface(SDL_HWSURFACE,SCREEN_WIDTH,SCREEN_HEIGHT,32,0x00FF0000,0x0000FF00,0x000000FF,0);
//Create a texture.
glDeleteTextures(1,&screenTexture);
glGenTextures(1,&screenTexture);
//And set up gl correctly.
glClearColor(0, 0, 0, 0);
glClearDepth(1.0f);
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 1, -1);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_TEXTURE_2D);
glLoadIdentity();
#else
//NOTE: Hardware accelerated rendering requested but compiled without.
cerr<<"FATAL ERROR: Unable to use hardware acceleration (compiled without)."<<endl;
return false;
#endif
}else{
//Set the flags.
Uint32 flags=SDL_HWSURFACE | SDL_DOUBLEBUF;
if(settings->getBoolValue("fullscreen"))
flags|=SDL_FULLSCREEN;
else if(settings->getBoolValue("resizable"))
flags|=SDL_RESIZABLE;
//Check if there already was a screen.
if(screen)
initial=false;
//Create the screen and check if there weren't any errors.
screen=SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,flags);
if(screen==NULL){
fprintf(stderr,"FATAL ERROR: SDL_SetVideoMode failed\n");
return false;
}
}
//Now configure the newly created window (if windowed).
if(settings->getBoolValue("fullscreen")==false)
configureWindow(initial);
//Create the temp surface, just a replica of the screen surface, free the previous one if any.
if(tempSurface)
SDL_FreeSurface(tempSurface);
tempSurface=SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,
screen->w,screen->h,screen->format->BitsPerPixel,
screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0);
//Set the the window caption.
SDL_WM_SetCaption(("Me and My Shadow "+version).c_str(),NULL);
SDL_EnableUNICODE(1);
//Nothing went wrong so return true.
return true;
}
#ifdef WIN32
static WNDPROC m_OldWindowProc=NULL;
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
if(msg==WM_GETMINMAXINFO){
if(m_OldWindowProc){
CallWindowProc(m_OldWindowProc,hwnd,msg,wParam,lParam);
}else{
DefWindowProc(hwnd,msg,wParam,lParam);
}
RECT r={0,0,800,600};
AdjustWindowRect(&r,GetWindowLong(hwnd,GWL_STYLE),FALSE);
MINMAXINFO *info=(MINMAXINFO*)lParam;
info->ptMinTrackSize.x=r.right-r.left;
info->ptMinTrackSize.y=r.bottom-r.top;
return 0;
}else{
if(m_OldWindowProc){
return CallWindowProc(m_OldWindowProc,hwnd,msg,wParam,lParam);
}else{
return DefWindowProc(hwnd,msg,wParam,lParam);
}
}
}
#endif
void pickFullscreenResolution(){
//Vector that will hold the resolutions to choose from.
vector<_res> resolutionList;
//Enumerate avaliable resolutions using SDL_ListModes()
//Note: we enumerate fullscreen resolutions because
// windowed resolutions always can be arbitrary
if(resolutionList.empty()){
SDL_Rect **modes=SDL_ListModes(NULL,SDL_FULLSCREEN|SDL_HWSURFACE);
if(modes==NULL || ((intptr_t)modes) == -1){
cout<<"Error: Can't enumerate avaliable screen resolutions."
" Use predefined screen resolutions list instead."<<endl;
static const _res predefinedResolutionList[] = {
{800,600},
{1024,600},
{1024,768},
{1152,864},
{1280,720},
{1280,768},
{1280,800},
{1280,960},
{1280,1024},
{1360,768},
{1366,768},
{1440,900},
{1600,900},
{1600,1200},
{1680,1080},
{1920,1080},
{1920,1200},
{2560,1440},
{3840,2160}
};
//Fill the resolutionList.
for(unsigned int i=0;i<sizeof(predefinedResolutionList)/sizeof(_res);i++){
resolutionList.push_back(predefinedResolutionList[i]);
}
}else{
//Fill the resolutionList.
for(unsigned int i=0;modes[i]!=NULL;i++){
//Check if the resolution is higher than the minimum (800x600).
if(modes[i]->w>=800 && modes[i]->h>=600){
_res res={modes[i]->w, modes[i]->h};
resolutionList.push_back(res);
}
}
//Reverse it so that we begin with the lowest resolution.
reverse(resolutionList.begin(),resolutionList.end());
}
}
//The resolution that will hold the final result, we start with the minimum (800x600).
_res closestMatch={800,600};
int width=atoi(getSettings()->getValue("width").c_str());
//int height=atoi(getSettings()->getValue("height").c_str());
//Now loop through the resolutionList.
for(int i=0;i<(int)resolutionList.size();i++){
//The delta between the closestMatch and the resolution from the list.
int dM=(closestMatch.w-resolutionList[i].w);
//The delta between the target width and the resolution from the list.
int dT=(width-resolutionList[i].w);
//Since the resolutions are getting higher the lower (more negative) the further away it is.
//That's why we check if the deltaMatch is lower than the the deltaTarget.
if((dM)<(dT)){
closestMatch.w=resolutionList[i].w;
closestMatch.h=resolutionList[i].h;
}
}
//Now set the resolution to the closest match.
char s[64];
sprintf(s,"%d",closestMatch.w);
getSettings()->setValue("width",s);
sprintf(s,"%d",closestMatch.h);
getSettings()->setValue("height",s);
}
void configureWindow(bool initial){
//We only need to configure the window if it's resizable.
if(!getSettings()->getBoolValue("resizable"))
return;
//Retrieve the WM info from SDL containing the window handle.
struct SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWMInfo(&wmInfo);
#ifdef __linux__
//We assume that a linux system running meandmyshadow is also running an Xorg server.
if(wmInfo.subsystem==SDL_SYSWM_X11){
//Create the size hints to give to the window.
XSizeHints* sizeHints;
if(!(sizeHints=XAllocSizeHints())){
cerr<<"ERROR: Unable to allocate memory for XSizeHings."<<endl;
return;
}
//Configure the size hint.
sizeHints->flags=PMinSize;
sizeHints->min_width=800;
sizeHints->min_height=600;
//Set the normal hints of the window.
(void)wmInfo.info.x11.lock_func;
XSetNormalHints(wmInfo.info.x11.display,wmInfo.info.x11.wmwindow,sizeHints);
(void)wmInfo.info.x11.unlock_func;
//Free size hint structure
XFree(sizeHints);
}else{
//No X11 so an unsupported window manager.
cerr<<"WARNING: Unsupported window manager."<<endl;
}
#elif defined(WIN32)
//We overwrite the window proc of SDL
WNDPROC wndproc=(WNDPROC)GetWindowLong(wmInfo.window,GWL_WNDPROC);
if(wndproc!=NULL && wndproc!=(WNDPROC)WindowProc){
m_OldWindowProc=wndproc;
SetWindowLong(wmInfo.window,GWL_WNDPROC,(LONG)(WNDPROC)WindowProc);
}
#endif
}
void onVideoResize(){
//Check if the resize event isn't malformed.
if(event.resize.w<=0 || event.resize.h<=0)
return;
//Check the size limit.
if(event.resize.w<800)
event.resize.w=800;
if(event.resize.h<600)
event.resize.h=600;
//Check if it really resizes.
if(SCREEN_WIDTH==event.resize.w && SCREEN_HEIGHT==event.resize.h)
return;
char s[32];
//Set the new width and height.
sprintf(s,"%d",event.resize.w);
getSettings()->setValue("width",s);
sprintf(s,"%d",event.resize.h);
getSettings()->setValue("height",s);
//Do resizing.
if(!createScreen())
return;
//Tell the theme to resize.
if(!loadTheme(""))
return;
//The new resolution is valid.
//Now we can save the settings. (TODO: should we save?)
//saveSettings();
//And let the currentState update it's GUI to the new resolution.
currentState->resize();
}
bool init(){
//Initialze SDL.
if(SDL_Init(SDL_INIT_EVERYTHING)==-1) {
fprintf(stderr,"FATAL ERROR: SDL_Init failed\n");
return false;
}
//Initialze SDL_mixer (audio).
if(Mix_OpenAudio(22050,MIX_DEFAULT_FORMAT,2,512)==-1){
fprintf(stderr,"FATAL ERROR: Mix_OpenAudio failed\n");
return false;
}
//Set the volume.
Mix_Volume(-1,atoi(settings->getValue("sound").c_str()));
//Initialze SDL_ttf (fonts).
if(TTF_Init()==-1){
fprintf(stderr,"FATAL ERROR: TTF_Init failed\n");
return false;
}
//Create the screen.
if(!createScreen())
return false;
//Load key config. Then initalize joystick support.
inputMgr.loadConfig();
inputMgr.openAllJoysitcks();
//Init tinygettext for translations for the right language
dictionaryManager = new tinygettext::DictionaryManager();
dictionaryManager->add_directory(getDataPath()+"locale");
dictionaryManager->set_charset("UTF-8");
//Check if user have defined own language. If not, find it out for the player using findlocale
string lang=getSettings()->getValue("lang");
if(lang.length()>0){
printf("Locale set by user to %s\n",lang.c_str());
language=lang;
}else{
FL_Locale *locale;
FL_FindLocale(&locale,FL_MESSAGES);
printf("Locale isn't set by user: %s\n",locale->lang);
language=locale->lang;
if(locale->country!=NULL){
language+=string("_")+string(locale->country);
}
if(locale->variant!=NULL){
language+=string("@")+string(locale->variant);
}
FL_FreeLocale(&locale);
}
//Now set the language in the dictionaryManager.
dictionaryManager->set_language(tinygettext::Language::from_name(language));
//Disable annoying 'Couldn't translate: blah blah blah'
tinygettext::Log::set_log_info_callback(NULL);
//Create the types of blocks.
for(int i=0;i<TYPE_MAX;i++){
Game::blockNameMap[Game::blockName[i]]=i;
}
//Nothing went wrong so we return true.
return true;
}
static TTF_Font* loadFont(const char* name,int size){
TTF_Font* tmpFont=TTF_OpenFont((getDataPath()+"font/"+name+".ttf").c_str(),size);
if(tmpFont){
return tmpFont;
}else{
return TTF_OpenFont((getDataPath()+"font/DroidSansFallback.ttf").c_str(),size);
}
}
bool loadFonts(){
//Load the fonts.
//NOTE: This is a separate method because it will be called separately when re-initing in case of language change.
//First close the fonts if needed.
if(fontTitle)
TTF_CloseFont(fontTitle);
if(fontGUI)
TTF_CloseFont(fontGUI);
if(fontGUISmall)
TTF_CloseFont(fontGUISmall);
if(fontText)
TTF_CloseFont(fontText);
/// TRANSLATORS: Font used in GUI:
/// - Use "knewave" for languages using Latin and Latin-derived alphabets
/// - "DroidSansFallback" can be used for non-Latin writing systems
fontTitle=loadFont(_("knewave"),55);
fontGUI=loadFont(_("knewave"),32);
fontGUISmall=loadFont(_("knewave"),24);
/// TRANSLATORS: Font used for normal text:
/// - Use "Blokletters-Viltstift" for languages using Latin and Latin-derived alphabets
/// - "DroidSansFallback" can be used for non-Latin writing systems
fontText=loadFont(_("Blokletters-Viltstift"),16);
if(fontTitle==NULL || fontGUI==NULL || fontGUISmall==NULL || fontText==NULL){
printf("ERROR: Unable to load fonts! \n");
return false;
}
//Nothing went wrong so return true.
return true;
}
//Generate small arrows used for some GUI widgets.
static void generateArrows(){
TTF_Font* fontArrow=loadFont(_("knewave"),18);
if(arrowLeft1){
SDL_FreeSurface(arrowLeft1);
SDL_FreeSurface(arrowRight1);
SDL_FreeSurface(arrowLeft2);
SDL_FreeSurface(arrowRight2);
}
arrowLeft1=TTF_RenderUTF8_Blended(fontArrow,"<",themeTextColor);
arrowRight1=TTF_RenderUTF8_Blended(fontArrow,">",themeTextColor);
arrowLeft2=TTF_RenderUTF8_Blended(fontArrow,"<",themeTextColorDialog);
arrowRight2=TTF_RenderUTF8_Blended(fontArrow,">",themeTextColorDialog);
TTF_CloseFont(fontArrow);
}
bool loadTheme(string name){
//Load default fallback theme if it isn't loaded yet
if(objThemes.themeCount()==0){
if(objThemes.appendThemeFromFile(getDataPath()+"themes/Cloudscape/theme.mnmstheme")==NULL){
printf("ERROR: Can't load default theme file\n");
return false;
}
}
//Resize background or load specific theme
if(name==""||name.empty()){
objThemes.scaleToScreen();
}else{
string theme=processFileName(name);
if(objThemes.appendThemeFromFile(theme+"/theme.mnmstheme")==NULL){
printf("ERROR: Can't load theme %s\n",theme.c_str());
return false;
}
}
generateArrows();
//Everything went fine so return true.
return true;
}
static Mix_Chunk* loadWAV(const char* s){
Mix_Chunk* c=Mix_LoadWAV(s);
if(c!=NULL) return c;
printf("ERROR: Can't load sound file %s: %s\n",s,SDL_GetError());
return NULL;
}
bool loadFiles(){
//Load the fonts.
if(!loadFonts())
return false;
//Show a loading screen
{
SDL_Rect r={0,0,screen->w,screen->h};
SDL_FillRect(screen,&r,0);
SDL_Color fg={255,255,255};
SDL_Surface *surface=TTF_RenderUTF8_Blended(fontTitle,_("Loading..."),fg);
if(surface!=NULL){
r.x=(screen->w-surface->w)/2;
r.y=(screen->h-surface->h)/2;
SDL_BlitSurface(surface,NULL,screen,&r);
SDL_FreeSurface(surface);
}
SDL_Flip(screen);
}
musicManager.destroy();
//Load the music and play it.
if(musicManager.loadMusic((getDataPath()+"music/menu.music")).empty()){
printf("WARNING: Unable to load background music! \n");
}
musicManager.playMusic("menu",false);
//Always load the default music list for fallback.
musicManager.loadMusicList((getDataPath()+"music/default.list"));
//Load the configured music list.
getMusicManager()->loadMusicList((getDataPath()+"music/"+getSettings()->getValue("musiclist")+".list"));
getMusicManager()->setMusicList(getSettings()->getValue("musiclist"));
//Check if music is enabled.
if(getSettings()->getBoolValue("music"))
getMusicManager()->setEnabled();
//Load the sound effects
jumpSound=loadWAV((getDataPath()+"sfx/jump.wav").c_str());
hitSound=loadWAV((getDataPath()+"sfx/hit.wav").c_str());
saveSound=loadWAV((getDataPath()+"sfx/checkpoint.wav").c_str());
swapSound=loadWAV((getDataPath()+"sfx/swap.wav").c_str());
toggleSound=loadWAV((getDataPath()+"sfx/toggle.wav").c_str());
errorSound=loadWAV((getDataPath()+"sfx/error.wav").c_str());
collectSound=loadWAV((getDataPath()+"sfx/collect.wav").c_str());
achievementSound=loadWAV((getDataPath()+"sfx/achievement.ogg").c_str());
levelPackManager.destroy();
//Now sum up all the levelpacks.
vector<string> v=enumAllDirs(getDataPath()+"levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getDataPath()+"levelpacks/"+*i);
}
v=enumAllDirs(getUserPath(USER_DATA)+"levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getUserPath(USER_DATA)+"levelpacks/"+*i);
}
v=enumAllDirs(getUserPath(USER_DATA)+"custom/levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getUserPath(USER_DATA)+"custom/levelpacks/"+*i);
}
//Now we add a special levelpack that will contain the levels not in a levelpack.
LevelPack* levelsPack=new LevelPack;
levelsPack->levelpackName="Levels";
LevelPack* customLevelsPack=new LevelPack;
customLevelsPack->levelpackName="Custom Levels";
//List the addon levels and add them one for one.
v=enumAllFiles(getUserPath(USER_DATA)+"levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelsPack->addLevel(getUserPath(USER_DATA)+"levels/"+*i);
levelsPack->setLocked(levelsPack->getLevelCount()-1);
}
//List the custom levels and add them one for one.
v=enumAllFiles(getUserPath(USER_DATA)+"custom/levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelsPack->addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
levelsPack->setLocked(levelsPack->getLevelCount()-1);
customLevelsPack->addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
customLevelsPack->setLocked(customLevelsPack->getLevelCount()-1);
}
//Add them to the manager.
levelPackManager.addLevelPack(levelsPack);
levelPackManager.addLevelPack(customLevelsPack);
//Load statistics
statsMgr.loadPicture();
statsMgr.registerAchievements();
statsMgr.loadFile(getUserPath(USER_CONFIG)+"statistics");
//Do something ugly and slow
statsMgr.updateCompletedLevelsAndAchievements();
//Load the theme, both menu and default.
if(!loadTheme(getSettings()->getValue("theme")))
return false;
//Nothing failed so return true.
return true;
}
bool loadSettings(){
settings=new Settings(getUserPath(USER_CONFIG)+"meandmyshadow.cfg");
settings->parseFile();
//Now apply settings changed through command line arguments, if any.
map<string,string>::iterator it;
for(it=tmpSettings.begin();it!=tmpSettings.end();++it){
settings->setValue(it->first,it->second);
}
tmpSettings.clear();
//Always return true?
return true;
}
bool saveSettings(){
settings->save();
//Always return true?
return true;
}
Settings* getSettings(){
return settings;
}
MusicManager* getMusicManager(){
return &musicManager;
}
LevelPackManager* getLevelPackManager(){
return &levelPackManager;
}
void flipScreen(){
if(settings->getBoolValue("gl")){
#ifdef HARDWARE_ACCELERATION
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
//Create a texture from the screen surface.
glBindTexture(GL_TEXTURE_2D,screenTexture);
//Set the texture's stretching properties
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D,0,screen->format->BytesPerPixel,screen->w,screen->h,0,GL_BGRA,GL_UNSIGNED_BYTE,screen->pixels);
glBegin(GL_QUADS);
glTexCoord2i(0,0); glVertex3f(0,0,0);
glTexCoord2i(1,0); glVertex3f(SCREEN_WIDTH,0,0);
glTexCoord2i(1,1); glVertex3f(SCREEN_WIDTH,SCREEN_HEIGHT,0);
glTexCoord2i(0,1); glVertex3f(0,SCREEN_HEIGHT,0);
glEnd();
SDL_GL_SwapBuffers();
#else
//NOTE: Trying to flip the screen using gl while compiled without.
cerr<<"FATAL ERROR: Unable to draw to screen using OpenGL (compiled without)."<<endl;
#endif
}else{
SDL_Flip(screen);
}
}
void clean(){
//Save statistics
statsMgr.saveFile(getUserPath(USER_CONFIG)+"statistics");
//We delete the settings.
if(settings){
delete settings;
settings=NULL;
}
//Get rid of the currentstate.
//NOTE: The state is probably already deleted by the changeState function.
if(currentState)
delete currentState;
//Destroy the GUI if present.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Destroy the imageManager.
imageManager.destroy();
//Destroy the musicManager.
musicManager.destroy();
//Destroy all sounds
Mix_FreeChunk(jumpSound);
Mix_FreeChunk(hitSound);
Mix_FreeChunk(saveSound);
Mix_FreeChunk(swapSound);
Mix_FreeChunk(toggleSound);
Mix_FreeChunk(errorSound);
Mix_FreeChunk(collectSound);
Mix_FreeChunk(achievementSound);
//Destroy the levelPackManager.
levelPackManager.destroy();
levels=NULL;
//Close all joysticks.
inputMgr.closeAllJoysticks();
//Close the fonts and quit SDL_ttf.
TTF_CloseFont(fontTitle);
TTF_CloseFont(fontGUI);
TTF_CloseFont(fontGUISmall);
TTF_CloseFont(fontText);
TTF_Quit();
//Remove the temp surface.
SDL_FreeSurface(tempSurface);
//Stop audio.and quit
Mix_CloseAudio();
Mix_Quit();
//And finally quit SDL.
SDL_Quit();
}
void setNextState(int newstate){
//Only change the state when we aren't already exiting.
if(nextState!=STATE_EXIT){
nextState=newstate;
}
}
void changeState(){
//Check if there's a nextState.
if(nextState!=STATE_NULL){
//Delete the currentState.
delete currentState;
currentState=NULL;
//Set the currentState to the nextState.
stateID=nextState;
nextState=STATE_NULL;
//Init the state.
switch(stateID){
case STATE_GAME:
currentState=new Game();
break;
case STATE_MENU:
currentState=new Menu();
break;
case STATE_LEVEL_SELECT:
currentState=new LevelPlaySelect();
break;
case STATE_LEVEL_EDIT_SELECT:
currentState=new LevelEditSelect();
break;
case STATE_LEVEL_EDITOR:
currentState=new LevelEditor();
break;
case STATE_OPTIONS:
currentState=new Options();
break;
case STATE_ADDONS:
currentState=new Addons();
break;
case STATE_CREDITS:
currentState=new Credits();
break;
+ case STATE_STATISTICS:
+ currentState=new StatisticsScreen();
+ break;
}
//NOTE: STATE_EXIT isn't mentioned, meaning that currentState is null.
//This way the game loop will break and the program will exit.
//Fade out.
int fade=255;
SDL_BlitSurface(screen,NULL,tempSurface,NULL);
while(fade>0){
fade-=17;
if(fade<0)
fade=0;
SDL_FillRect(screen,NULL,0);
SDL_SetAlpha(tempSurface, SDL_SRCALPHA, fade);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
flipScreen();
SDL_Delay(25);
}
}
}
void musicStoppedHook(){
//We just call the musicStopped method of the MusicManager.
musicManager.musicStopped();
}
bool checkCollision(const SDL_Rect& a,const SDL_Rect& b){
//Check if the left side of box a isn't past the right side of b.
if(a.x>=b.x+b.w){
return false;
}
//Check if the right side of box a isn't left of the left side of b.
if(a.x+a.w<=b.x){
return false;
}
//Check if the top side of box a isn't under the bottom side of b.
if(a.y>=b.y+b.h){
return false;
}
//Check if the bottom side of box a isn't above the top side of b.
if(a.y+a.h<=b.y){
return false;
}
//We have collision.
return true;
}
void setCamera(const SDL_Rect* r,int count){
//SetCamera only works in the Level editor and mouse is inside window.
if(stateID==STATE_LEVEL_EDITOR&&(SDL_GetAppState()&SDL_APPMOUSEFOCUS)){
//Get the mouse coordinates.
int x,y;
SDL_GetMouseState(&x,&y);
//Make sure we avoid the toolbar.
SDL_Rect mouse={x,y,0,0};
//SDL_Rect toolbar={(SCREEN_WIDTH-460)/2,SCREEN_HEIGHT-50,460,50};
for(int i=0;i<count;i++){
if(checkCollision(mouse,r[i]))
return;
}
//Check if the mouse is near the left edge of the screen.
//Else check if the mouse is near the right edge.
if(x<50){
//We're near the left edge so move the camera.
camera.x-=5;
}else if(x>SCREEN_WIDTH-50){
//We're near the right edge so move the camera.
camera.x+=5;
}
//Check if the mouse is near the top edge of the screen.
//Else check if the mouse is near the bottom edge.
if(y<50){
//We're near the top edge so move the camera.
camera.y-=5;
}else if(y>SCREEN_HEIGHT-50){
//We're near the bottom edge so move the camera.
camera.y+=5;
}
}
}
int parseArguments(int argc, char** argv){
//Loop through all arguments.
//We start at one since 0 is the command itself.
for(int i=1;i<argc;i++){
string argument=argv[i];
//Check if the argument is the data-dir.
if(argument=="--data-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Configure the dataPath with the given path.
dataPath=argv[i];
if(!getDataPath().empty()){
char c=dataPath[dataPath.size()-1];
if(c!='/'&&c!='\\') dataPath+="/";
}
}else if(argument=="--user-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Configure the userPath with the given path.
userPath=argv[i];
if(!userPath.empty()){
char c=userPath[userPath.size()-1];
if(c!='/'&&c!='\\') userPath+="/";
}
}else if(argument=="-f" || argument=="-fullscreen" || argument=="--fullscreen"){
tmpSettings["fullscreen"]="1";
}else if(argument=="-w" || argument=="-windowed" || argument=="--windowed"){
tmpSettings["fullscreen"]="0";
}else if(argument=="-mv" || argument=="-music" || argument=="--music"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Now set the music volume.
tmpSettings["music"]=argv[i];
}else if(argument=="-sv" || argument=="-sound" || argument=="--sound"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Now set sound volume.
tmpSettings["sound"]=argv[i];
}else if(argument=="-set" || argument=="--set"){
//We need a second and a third argument so we increase i.
i+=2;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//And set the setting.
tmpSettings[argv[i-1]]=argv[i];
}else if(argument=="-v" || argument=="-version" || argument=="--version"){
//Print the version.
printf("%s\n",version.c_str());
return 0;
}else if(argument=="-h" || argument=="-help" || argument=="--help"){
//If the help is requested we'll return false without printing an error.
//This way the usage/help text will be printed.
return -1;
}else{
//Any other argument is unknow so we return false.
printf("ERROR: Unknown argument %s\n\n",argument.c_str());
return -1;
}
}
//If everything went well we can return true.
return 1;
}
//Special structure that will recieve the GUIEventCallbacks of the messagebox.
struct msgBoxHandler:public GUIEventCallback{
public:
//Integer containing the ret(urn) value of the messageBox.
int ret;
public:
//Constructor.
msgBoxHandler():ret(0){}
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Make sure it's a click event.
if(eventType==GUIEventClick){
//Set the return value.
ret=obj->value;
//After a click event we can delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
};
msgBoxResult msgBox(string prompt,msgBoxButtons buttons,const string& title){
//Create the event handler.
msgBoxHandler objHandler;
//The GUI objects.
GUIObject* obj;
//Create the GUIObjectRoot, the height and y location is temp.
//It depends on the content what it will be.
GUIObject* root=new GUIObject((SCREEN_WIDTH-600)/2,200,600,200,GUIObjectFrame,title.c_str());
//Integer containing the current y location used to grow dynamic depending on the content.
int y=50;
//Now process the prompt.
{
//Pointer to the string.
char* lps=(char*)prompt.c_str();
//Pointer to a character.
char* lp=NULL;
//We keep looping forever.
//The only way out is with the break statement.
for(;;){
//As long as it's still the same sentence we continue.
//It will stop when there's a newline or end of line.
for(lp=lps;*lp!='\n'&&*lp!='\r'&&*lp!=0;lp++);
//Store the character we stopped on. (End or newline)
char c=*lp;
//Set the character in the string to 0, making lps a string containing one sentence.
*lp=0;
//Add a GUIObjectLabel with the sentence.
root->addChild(new GUIObject(0,y,root->width,25,GUIObjectLabel,lps,0,true,true,GUIGravityCenter));
//Increase y with 25, about the height of the text.
y+=25;
//Check the stored character if it was a stop.
if(c==0){
//It was so break out of the for loop.
lps=lp;
break;
}
//It wasn't meaning more will follow.
//We set lps to point after the "newline" forming a new string.
lps=lp+1;
}
}
//Add 70 to y to leave some space between the content and the buttons.
y+=70;
//Recalc the size of the message box.
root->top=(SCREEN_HEIGHT-y)/2;
root->height=y;
//Now we need to add the buttons.
//Integer containing the number of buttons to add.
int count=0;
//Array with the return codes for the buttons.
int value[3]={0};
//Array containing the captation for the buttons.
string button[3]={"","",""};
switch(buttons){
case MsgBoxOKCancel:
count=2;
button[0]=_("OK");value[0]=MsgBoxOK;
button[1]=_("Cancel");value[1]=MsgBoxCancel;
break;
case MsgBoxAbortRetryIgnore:
count=3;
button[0]=_("Abort");value[0]=MsgBoxAbort;
button[1]=_("Retry");value[1]=MsgBoxRetry;
button[2]=_("Ignore");value[2]=MsgBoxIgnore;
break;
case MsgBoxYesNoCancel:
count=3;
button[0]=_("Yes");value[0]=MsgBoxYes;
button[1]=_("No");value[1]=MsgBoxNo;
button[2]=_("Cancel");value[2]=MsgBoxCancel;
break;
case MsgBoxYesNo:
count=2;
button[0]=_("Yes");value[0]=MsgBoxYes;
button[1]=_("No");value[1]=MsgBoxNo;
break;
case MsgBoxRetryCancel:
count=2;
button[0]=_("Retry");value[0]=MsgBoxRetry;
button[1]=_("Cancel");value[1]=MsgBoxCancel;
break;
default:
count=1;
button[0]=_("OK");value[0]=MsgBoxOK;
break;
}
//Now we start making the buttons.
{
//Reduce y so that the buttons fit inside the frame.
y-=40;
double places[3]={0.0};
if(count==1){
places[0]=0.5;
}else if(count==2){
places[0]=0.4;
places[1]=0.6;
}else if(count==3){
places[0]=0.3;
places[1]=0.5;
places[2]=0.7;
}
//Loop to add the buttons.
for(int i=0;i<count;i++){
obj=new GUIObject(root->width*places[i],y,-1,36,GUIObjectButton,button[i].c_str(),value[i],true,true,GUIGravityCenter);
obj->eventCallback=&objHandler;
root->addChild(obj);
}
}
//Now we dim the screen and keep the GUI rendering/updating.
GUIOverlay* overlay=new GUIOverlay(root);
overlay->enterLoop(true);
//And return the result.
return (msgBoxResult)objHandler.ret;
}
struct fileDialogHandler:public GUIEventCallback{
public:
//The ret(urn) value, true=ok and false=cancel
bool ret;
//Boolean if it's a save dialog.
bool isSave;
//Boolean if the file should be verified.
bool verifyFile;
//Boolean if files should be listed instead of directories.
bool files;
//Pointer to the textfield containing the filename.
GUIObject* txtName;
//Pointer to the listbox containing the different files.
GUIListBox* lstFile;
//The extension the files listed should have.
const char* extension;
//The current filename.
string fileName;
//The current search path.
string path;
//Vector containing the search paths.
vector<string> searchPath;
public:
//Constructor.
fileDialogHandler(bool isSave=false,bool verifyFile=false, bool files=true):ret(false),
isSave(isSave),verifyFile(verifyFile),
files(files),txtName(NULL),lstFile(NULL),extension(NULL){}
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Check for the ok event.
if(name=="cmdOK"){
//Get the entered fileName from the text field.
std::string s=txtName->caption;
//If it doesn't contain a slash we need to add the path to the fileName.
if(s.find_first_of("/")==string::npos)
s=path+s;
//If the string empty we return.
if(s.empty() || s.find_first_of("*?")!=string::npos)
return;
//We only need to check for extensions if it isn't a folder dialog.
if(files){
//If there isn't right extension add it.
size_t found=s.find_first_of(".");
if(found!=string::npos)
s.replace(s.begin()+found+1,s.end(),extension);
else if (s.substr(found+1)!=extension)
s.append(string(".")+extension);
}
//Check if we should save or load the file.
//
if(isSave){
//Open the file with read permission to check if it already exists.
FILE* f;
f=fopen(processFileName(s).c_str(),"rb");
//Check if it exists.
if(f){
//Close the file.
fclose(f);
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//Prompt the user with a Yes or No question.
/// TRANSLATORS: Filename is coming before this text
if(msgBox(tfm::format(_("%s already exists.\nDo you want to overwrite it?"),s),MsgBoxYesNo,_("Overwrite Prompt"))!=MsgBoxYes){
//He answered no, so we return.
return;
}
}
//Check if we should verify the file.
//Verifying only applies to files not to directories.
if(verifyFile && files){
//Open the file with write permission.
f=fopen(processFileName(s).c_str(),"wb");
//Check if their aren't problems.
if(f){
//Close the file.
fclose(f);
}else{
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//The file can't be opened so tell the user.
msgBox(tfm::format(_("Can't open file %s."),s),MsgBoxOKOnly,_("Error"));
return;
}
}
}else if(verifyFile && files){
//We need to verify a file for opening.
FILE *f;
f=fopen(processFileName(s).c_str(),"rb");
//Check if it didn't fail.
if(f){
//Succes, so close the file.
fclose(f);
}else{
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//Unable to open file so tell the user.
msgBox(tfm::format(_("Can't open file %s."),s),MsgBoxOKOnly,_("Error"));
return;
}
}
//If we haven't returned then it's fine.
//Set the fileName to the chosen file.
fileName=s;
//Delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Set return to true.
ret=true;
}else if(name=="cmdCancel"){
//Cancel means we can kill the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="lstFile"){
//Get a pointer to the listbox.
GUIListBox* obj1=lstFile;
//Make sure the option exist and change textfield to it.
if(obj1!=NULL && txtName!=NULL && obj1->value>=0 && obj1->value<(int)obj1->item.size()){
txtName->caption=obj1->item[obj1->value];
}
}else if(name=="lstSearchIn"){
//Get the searchpath listbox.
GUISingleLineListBox *obj1=dynamic_cast<GUISingleLineListBox*>(obj);
//Check if the entry exists.
if(obj1!=NULL && lstFile!=NULL && obj1->value>=0 && obj1->value<(int)searchPath.size()){
//Temp string.
string s;
//Get the new search path.
path=searchPath[obj1->value];
//Make sure it isn't empty.
if(!path.empty()){
//Process the filename.
s=processFileName(path);
}else{
//It's empty so we give the userpath.
s=getUserPath();
}
//Fill the list with files or directories.
if(files) {
lstFile->item=enumAllFiles(s,extension);
}else
lstFile->item=enumAllDirs(s);
//Remove any selection from the list.
lstFile->value=-1;
}
}
}
};
bool fileDialog(string& fileName,const char* title,const char* extension,const char* path,bool isSave,bool verifyFile,bool files){
//Pointer to GUIObject to make the GUI with.
GUIObject* obj;
//Create the fileDialogHandler, used for event handling.
fileDialogHandler objHandler(isSave,verifyFile,files);
//Vector containing the pathNames.
vector<string> pathNames;
//Set the extension of the objHandler.
objHandler.extension=extension;
//We now need to splits the given path into multiple path names.
if(path && path[0]){
//The string isn't empty.
//Pointer to the paths string.
char* lp=(char*)path;
//Pointer to the first newline.
char* lps=strchr(lp,'\n');
//Pointer used for checking if their's another newline.
//It will indicate if it's the last set or not.
char* lpe;
//Check for a newline.
if(lps){
//We have newline(s) so loop forever.
//We can only break out of the loop when the string ends.
for(;;){
//Add the first searchpath.
//This is the beginning of the string (lp) to the first newline. (lps)
objHandler.searchPath.push_back(string(lp,lps-lp));
//We should have another newline so search for it.
lpe=strchr(lps+1,'\n');
if(lpe){
//We found it so we add that to the pathname.
pathNames.push_back(string(lps+1,lpe-lps-1));
//And start over again by setting lp to the start of a new set of searchPath/pathName.
lp=lpe+1;
}else{
//There is no newline anymore, meaning the last entry, the rest of the string must be the pathName.
pathNames.push_back(string(lps+1));
//And break out of the loop.
break;
}
//We haven't broken out so search for a newline.
lps=strchr(lp,'\n');
//If there isn't a newline break.
if(!lps)
break;
}
}else{
//There is no newline thus the whole string is the searchPath.
objHandler.searchPath.push_back(path);
}
}else{
//Empty so put an empty string as searchPath.
objHandler.searchPath.push_back(string());
}
//It's time to create the GUI.
//If there are more than one pathNames we need to add a GUISingleLineListBox.
int base_y=pathNames.empty()?20:60;
//Create the frame.
GUIObject* root=new GUIObject(100,100-base_y/2,600,400+base_y,GUIObjectFrame,title?title:(isSave?_("Save File"):_("Load File")));
//Create the search path list box if needed.
if(!pathNames.empty()){
root->addChild(new GUIObject(8,40,184,36,GUIObjectLabel,_("Search In")));
GUISingleLineListBox* obj1=new GUISingleLineListBox(160,40,432,36);
obj1->item=pathNames;
obj1->value=0;
obj1->name="lstSearchIn";
obj1->eventCallback=&objHandler;
root->addChild(obj1);
}
//Add the FileName label and textfield.
root->addChild(new GUIObject(8,20+base_y,184,36,GUIObjectLabel,_("File Name")));
{
//Fill the textbox with the given fileName.
string s=fileName;
if(!isSave){
//But only if it isn't empty.
if(s.empty() && extension && extension[0])
s=string("*.")+string(extension);
}
//Create the textbox and add it to the GUI.
objHandler.txtName=new GUIObject(160,20+base_y,432,36,GUIObjectTextBox,s.c_str());
root->addChild(objHandler.txtName);
}
//Now we add the ListBox containing the files or directories.
{
GUIListBox* obj1=new GUIListBox(8,60+base_y,584,292);
//Get the searchPath.
string s=objHandler.searchPath[0];
//Make sure it isn't empty.
if(!s.empty()){
objHandler.path=s;
s=processFileName(s);
}else{
s=getUserPath();
}
//Check if we should list files or directories.
if(files){
//Fill the list with files.
obj1->item=enumAllFiles(s,extension);
}else{
//Fill the list with directories.
obj1->item=enumAllDirs(s);
}
obj1->name="lstFile";
obj1->eventCallback=&objHandler;
root->addChild(obj1);
objHandler.lstFile=obj1;
}
//Now create the OK and Cancel buttons.
obj=new GUIObject(200,360+base_y,192,36,GUIObjectButton,_("OK"));
obj->name="cmdOK";
obj->eventCallback=&objHandler;
root->addChild(obj);
obj=new GUIObject(400,360+base_y,192,36,GUIObjectButton,_("Cancel"));
obj->name="cmdCancel";
obj->eventCallback=&objHandler;
root->addChild(obj);
//Create the gui overlay.
GUIOverlay* overlay=new GUIOverlay(root);
overlay->enterLoop();
//Now determine what the return value is (and if there is one).
if(objHandler.ret)
fileName=objHandler.fileName;
return objHandler.ret;
}
diff --git a/src/Globals.h b/src/Globals.h
index 12b9a61..a1a203b 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -1,210 +1,212 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLOBALS_H
#define GLOBALS_H
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_ttf.h>
#include <string>
#include "libs/tinygettext/tinygettext.hpp"
#ifdef WIN32
//#define DATA_PATH
#else
#include "config.h"
#endif
#define TITLE_FONT_RAISE 19
#define GUI_FONT_RAISE 5
//Global constants
//The width of the screen.
extern int SCREEN_WIDTH;
//The height of the screen.
extern int SCREEN_HEIGHT;
//The depth of the screen.
const int SCREEN_BPP=32;
//Strin containing the
const std::string version="V0.5 Development version";
//The height of the current level.
extern int LEVEL_HEIGHT;
//The width of the current level.
extern int LEVEL_WIDTH;
//The target frames per seconds.
const int g_FPS=40;
//The language that in which the game should be translated.
extern std::string language;
//The DictionaryManager that is used to translate the game itself.
extern tinygettext::DictionaryManager* dictionaryManager;
//The screen surface, it's used to draw on before it's drawn to the real screen.
extern SDL_Surface* screen;
//SDL_Surface with the same dimensions as screen which can be used for all kinds of (temp) drawing.
extern SDL_Surface* tempSurface;
//Font that is used for titles.
//Knewave large.
extern TTF_Font* fontTitle;
//Font that is used for captions of buttons and other GUI elements.
//Knewave small.
extern TTF_Font* fontGUI;
//Font that is used for long captions of buttons and other GUI elements.
//Knewave smaller.
extern TTF_Font* fontGUISmall;
//Font that is used for (long) text.
//Blokletter-Viltstift small.
extern TTF_Font* fontText;
//Small arrows used for GUI widgets.
//2 directions and 2 different/same colors depending on theme.
extern SDL_Surface* arrowLeft1;
extern SDL_Surface* arrowRight1;
extern SDL_Surface* arrowLeft2;
extern SDL_Surface* arrowRight2;
//Sound played when the player jumps.
extern Mix_Chunk* jumpSound;
//Sound played when the player dies.
extern Mix_Chunk* hitSound;
//Sound played when the saves a state.
extern Mix_Chunk* saveSound;
//Sound played when the player swaps.
extern Mix_Chunk* swapSound;
//Sound played when the player toggles a switch.
extern Mix_Chunk* toggleSound;
//The sound played when the player tries something that doesn't work.
//For example a broken portal or swapping the shadow into a shadow block.
extern Mix_Chunk* errorSound;
//Sound played when the player picks up a collectable.
extern Mix_Chunk* collectSound;
//Sound played when an achievement is achieved..
extern Mix_Chunk* achievementSound;
//Event, used for event handling.
extern SDL_Event event;
//GUI
class GUIObject;
extern GUIObject *GUIObjectRoot;
//The state id of the current state.
extern int stateID;
//Integer containing what the next state will be.
extern int nextState;
//String containing the name of the current level.
extern std::string levelName;
//SDL rectangle used to store the camera.
//x is the x location of the camera.
//y is the y location of the camera.
//w is the width of the camera. (equal to SCREEN_WIDTH)
//h is the height of the camera. (equal to SCREEN_HEIGHT)
extern SDL_Rect camera;
//Themable colors
extern SDL_Color themeTextColor;
extern SDL_Color themeTextColorDialog;
//Enumeration containing the ids of the game states.
enum GameStates
{
//State null is a special state used to indicate no state.
//This is used when no next state is defined.
STATE_NULL,
//This state is before the actual leveleditor used to make levelpacks.
STATE_LEVEL_EDIT_SELECT,
//This state is for the level editor.
STATE_LEVEL_EDITOR,
//This state is for the main menu.
STATE_MENU,
//This state is for the actual game.
STATE_GAME,
//Special state used when exiting meandmyshadow.
STATE_EXIT,
//This state is for the help screen.
STATE_LEVEL_SELECT,
//This state is for the options screen.
STATE_OPTIONS,
//This state is for the addon screen.
STATE_ADDONS,
//This state is for credits screen
- STATE_CREDITS
+ STATE_CREDITS,
+ //This state is for statistics screen
+ STATE_STATISTICS,
};
//Enumeration containing the ids of the different block types.
enum GameTileType{
//The normal solid block.
TYPE_BLOCK=0,
//Block representing the start location of the player.
TYPE_START_PLAYER,
//Block representing the start location of the shadow.
TYPE_START_SHADOW,
//The exit of the level.
TYPE_EXIT,
//The shadow block which is only solid for the shadow.
TYPE_SHADOW_BLOCK,
//Block that can kill both the player and the shadow.
TYPE_SPIKES,
//Special point where the player can save.
TYPE_CHECKPOINT,
//Block that will switch the location of the player and the shadow when invoked.
TYPE_SWAP,
//Block that will crumble to dust when stepped on it for the third time.
TYPE_FRAGILE,
//Normal block that moves along a path.
TYPE_MOVING_BLOCK,
//Shadow block that moves along a path.
TYPE_MOVING_SHADOW_BLOCK,
//A spike block that moves along a path.
TYPE_MOVING_SPIKES,
//Special block which, once entered, moves the player/shadow to a different portal.
TYPE_PORTAL,
//A block with a button which can activate or stop moving blocks, converyor belts
TYPE_BUTTON,
//A switch which can activate or stop moving blocks, converyor belts
TYPE_SWITCH,
//Solid block which works like
TYPE_CONVEYOR_BELT,
TYPE_SHADOW_CONVEYOR_BELT,
//Block that contains a message that can be read.
TYPE_NOTIFICATION_BLOCK,
//A collectable that is able to open locked doors
TYPE_COLLECTABLE,
//The (max) number of tiles.
TYPE_MAX
};
#endif
diff --git a/src/StatisticsManager.cpp b/src/StatisticsManager.cpp
index 70b2cba..23a9cf9 100644
--- a/src/StatisticsManager.cpp
+++ b/src/StatisticsManager.cpp
@@ -1,466 +1,486 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "StatisticsManager.h"
#include "FileManager.h"
#include "TreeStorageNode.h"
#include "POASerializer.h"
#include "Functions.h"
#include "LevelPackManager.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
using namespace std;
StatisticsManager statsMgr;
static const int achievementDisplayTime=100;
static const int achievementIntervalTime=120;
//internal struct for achievement info
struct AchievementInfo{
//achievement id for save to statistics file
const char* id;
//achievement name for display
const char* name;
//achievement image. NULL for no image. will be loaded at getDataPath()+imageFile
const char* imageFile;
//SDL_Surface of achievement image.
SDL_Surface* imageSurface;
//image offset and size.
SDL_Rect r;
//achievement description. supports multi-line text
const char* description;
};
static AchievementInfo achievementList[]={
{"newbie",__("Newbie"),"themes/Cloudscape/player.png",NULL,{0,0,23,40},__("Congratulations, you completed one level!")},
- {"experienced",__("Experienced player"),"themes/Cloudscape/player.png",NULL,{0,0,23,40},__("Completed 50 levels")},
- {"expert",__("Expert"),"gfx/medals.png",NULL,{60,0,30,30},__("Earned 50 gold medal")},
- {"tutorial",__("Graduate"),"gfx/medals.png",NULL,{60,0,30,30},__("Complete the tutorial level pack")},
- {"tutorialGold",__("Outstanding graduate"),"gfx/medals.png",NULL,{60,0,30,30},__("Complete the tutorial level pack with all levels gold medal")},
+ {"experienced",__("Experienced player"),"themes/Cloudscape/player.png",NULL,{0,0,23,40},__("Completed 50 levels.")},
+ {"expert",__("Expert"),"gfx/medals.png",NULL,{60,0,30,30},__("Earned 50 gold medal.")},
+ {"tutorial",__("Graduate"),"gfx/medals.png",NULL,{60,0,30,30},__("Complete the tutorial level pack.")},
+ {"tutorialGold",__("Outstanding graduate"),"gfx/medals.png",NULL,{60,0,30,30},__("Complete the tutorial level pack with all levels gold medal.")},
//test only
{"hello","Hello, World!","themes/Cloudscape/player.png",NULL,{0,0,23,40},"Welcome to Me and My Shadow!\n123\n456\n\n789"},
{"123","123","themes/Cloudscape/shadow.png",NULL,{0,0,23,40},"Welcome to Me and My Shadow!\n123\n456\n\n789"},
//end of achievements
{NULL,NULL,NULL,NULL,{0,0,0,0},NULL}
};
static map<string,AchievementInfo*> avaliableAchievements;
//================================================================
StatisticsManager::StatisticsManager(){
bmDropShadow=NULL;
bmAchievement=NULL;
clear();
}
StatisticsManager::~StatisticsManager(){
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
}
void StatisticsManager::clear(){
playerTravelingDistance=shadowTravelingDistance=0.0f;
playerJumps=shadowJumps
=playerDies=shadowDies
=completedLevels=silverLevels=goldLevels
=recordTimes=switchTimes=swapTimes
=playTime=levelEditTime
=createdLevels=0;
achievements.clear();
queuedAchievements.clear();
achievementTime=0;
currentAchievement=0;
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
}
#define LOAD_STATS(var,func) { \
vector<string> &v=node.attributes[ #var ]; \
if(!v.empty() && !v[0].empty()) \
var=func(v[0].c_str()); \
}
void StatisticsManager::loadFile(const std::string& fileName){
clear();
ifstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
POASerializer serializer;
if(!serializer.readNode(file,&node,true)) return;
//load statistics
LOAD_STATS(playerTravelingDistance,atof);
LOAD_STATS(shadowTravelingDistance,atof);
LOAD_STATS(playerJumps,atoi);
LOAD_STATS(shadowJumps,atoi);
LOAD_STATS(playerDies,atoi);
LOAD_STATS(shadowDies,atoi);
LOAD_STATS(recordTimes,atoi);
LOAD_STATS(switchTimes,atoi);
LOAD_STATS(swapTimes,atoi);
LOAD_STATS(playTime,atoi);
LOAD_STATS(levelEditTime,atoi);
LOAD_STATS(createdLevels,atoi);
//load achievements.
{
vector<string> &v=node.attributes["achievements"];
for(unsigned int i=0;i<v.size();i++){
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(v[i]);
if(it!=avaliableAchievements.end()){
achievements[it->first]=it->second;
}
}
}
}
#define SAVE_STATS(var,pattern) { \
sprintf(s,pattern,var); \
node.attributes[ #var ].push_back(s); \
}
void StatisticsManager::saveFile(const std::string& fileName){
char s[64];
ofstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
//save statistics
SAVE_STATS(playerTravelingDistance,"%.2f");
SAVE_STATS(shadowTravelingDistance,"%.2f");
SAVE_STATS(playerJumps,"%d");
SAVE_STATS(shadowJumps,"%d");
SAVE_STATS(playerDies,"%d");
SAVE_STATS(shadowDies,"%d");
SAVE_STATS(recordTimes,"%d");
SAVE_STATS(switchTimes,"%d");
SAVE_STATS(swapTimes,"%d");
SAVE_STATS(playTime,"%d");
SAVE_STATS(levelEditTime,"%d");
SAVE_STATS(createdLevels,"%d");
//save achievements.
{
vector<string>& v=node.attributes["achievements"];
for(map<string,AchievementInfo*>::iterator it=achievements.begin();it!=achievements.end();++it){
v.push_back(it->first);
}
}
POASerializer serializer;
serializer.writeNode(&node,file,true,true);
}
void StatisticsManager::loadPicture(){
//Load drop shadow picture
bmDropShadow=loadImage(getDataPath()+"gfx/dropshadow.png");
}
void StatisticsManager::registerAchievements(){
if(!avaliableAchievements.empty()) return;
for(int i=0;achievementList[i].id!=NULL;i++){
avaliableAchievements[achievementList[i].id]=&achievementList[i];
if(achievementList[i].imageFile!=NULL){
achievementList[i].imageSurface=loadImage(getDataPath()+achievementList[i].imageFile);
}
}
}
void StatisticsManager::render(){
+ //debug
+ if(achievementTime==0){
+ if(SDL_GetKeyState(NULL)[SDLK_1]) newAchievement("Hello, World!",false);
+ if(SDL_GetKeyState(NULL)[SDLK_2]) newAchievement("123",false);
+ }
+
if(achievementTime==0 && bmAchievement==NULL && currentAchievement<(int)queuedAchievements.size()){
//create surface
- createAchievementSurface(queuedAchievements[currentAchievement++]);
+ bmAchievement=createAchievementSurface(queuedAchievements[currentAchievement++]);
//check if queue is empty
if(currentAchievement>=(int)queuedAchievements.size()){
queuedAchievements.clear();
currentAchievement=0;
}
//play a sound
if(getSettings()->getBoolValue("sound")){
Mix_PlayChannel(-1,achievementSound,0);
}
}
//check if we need to display achievements
if(bmAchievement){
achievementTime++;
if(achievementTime<=0){
return;
}else if(achievementTime<=5){
drawAchievement(achievementTime);
}else if(achievementTime<=achievementDisplayTime-5){
drawAchievement(5);
}else if(achievementTime<achievementDisplayTime){
drawAchievement(achievementDisplayTime-achievementTime);
}else if(achievementTime>=achievementIntervalTime){
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
achievementTime=0;
}
}
}
void StatisticsManager::newAchievement(const std::string& id,bool save){
//check avaliable achievements
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(id);
if(it==avaliableAchievements.end()) return;
//check if already have this achievement
if(save){
map<string,AchievementInfo*>::iterator it2=achievements.find(id);
if(it2!=achievements.end()) return;
achievements[id]=it->second;
}
//add it to queue
queuedAchievements.push_back(it->second);
}
-void StatisticsManager::createAchievementSurface(AchievementInfo* info){
- if(info==NULL || info->id==NULL) return;
-
- //delete old surface
- if(bmAchievement) SDL_FreeSurface(bmAchievement);
+SDL_Surface* StatisticsManager::createAchievementSurface(AchievementInfo* info,SDL_Surface* surface,SDL_Rect* rect,bool showTip){
+ if(info==NULL || info->id==NULL) return NULL;
//prepare text
- SDL_Surface *title0,*title1;
+ SDL_Surface *title0=NULL,*title1=NULL;
vector<SDL_Surface*> descSurfaces;
SDL_Color fg={0,0,0};
+ int fontHeight=TTF_FontLineSkip(fontText);
- title0=TTF_RenderUTF8_Blended(fontText,_("New achievement:"),fg);
+ if(showTip) title0=TTF_RenderUTF8_Blended(fontText,_("New achievement:"),fg);
title1=TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg);
if(info->description!=NULL){
string description=_(info->description);
string::size_type lps=0,lpe;
for(;;){
lpe=description.find('\n',lps);
if(lpe==string::npos){
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps)+' ').c_str(),fg));
break;
}else{
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps,lpe-lps)+' ').c_str(),fg));
lps=lpe+1;
}
}
}
//calculate the size
int w=0,h=0,w1=8,h1=0;
if(title0!=NULL){
if(title0->w>w) w=title0->w;
h1+=title0->h;
}
if(title1!=NULL){
if(title1->w>w) w=title1->w;
h1+=title1->h;
}
if(info->imageSurface!=NULL){
w1+=info->r.w+8;
w+=info->r.w+8;
if(info->r.h>h1) h1=info->r.h;
}
h=h1+8;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
if(descSurfaces[i]->w>w) w=descSurfaces[i]->w;
- h+=descSurfaces[i]->h;
}
}
+ h+=descSurfaces.size()*fontHeight;
w+=16;
h+=16;
- //create surface
- bmAchievement=SDL_CreateRGBSurface(SDL_HWSURFACE,w,h,
- screen->format->BitsPerPixel,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0);
+ //check if size is specified
+ int left=0,top=0;
+ if(rect!=NULL){
+ if(surface!=NULL){
+ left=rect->x;
+ top=rect->y;
+ }
+ if(rect->w>0) w=rect->w;
+ else rect->w=w;
+ rect->h=h;
+ }
+
+ //create surface if necessary
+ if(surface==NULL){
+ surface=SDL_CreateRGBSurface(SDL_HWSURFACE,w,h,
+ screen->format->BitsPerPixel,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0);
+ }
//draw background
- drawGUIBox(0,0,w,h,bmAchievement,0xFFFFFFFFU);
+ drawGUIBox(left,top,w,h,surface,0xFFFFFFFFU);
//draw picture
if(info->imageSurface!=NULL){
- SDL_Rect r={8,8+(h1-info->r.h)/2,0,0};
- SDL_BlitSurface(info->imageSurface,&info->r,bmAchievement,&r);
+ SDL_Rect r={left+8,top+8+(h1-info->r.h)/2,0,0};
+ SDL_BlitSurface(info->imageSurface,&info->r,surface,&r);
}
//draw text
h=8;
if(title0!=NULL){
- SDL_Rect r={w1,h,0,0};
- SDL_BlitSurface(title0,NULL,bmAchievement,&r);
+ SDL_Rect r={left+w1,top+h,0,0};
+ SDL_BlitSurface(title0,NULL,surface,&r);
h+=title0->h;
}
if(title1!=NULL){
- SDL_Rect r={w1,h,0,0};
- SDL_BlitSurface(title1,NULL,bmAchievement,&r);
+ SDL_Rect r={left+w1,top+h,0,0};
+ SDL_BlitSurface(title1,NULL,surface,&r);
}
h=h1+16;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
- SDL_Rect r={8,h,0,0};
- SDL_BlitSurface(descSurfaces[i],NULL,bmAchievement,&r);
- h+=descSurfaces[i]->h;
+ SDL_Rect r={left+8,top+h+i*fontHeight,0,0};
+ SDL_BlitSurface(descSurfaces[i],NULL,surface,&r);
}
}
//clean up
if(title0) SDL_FreeSurface(title0);
if(title1) SDL_FreeSurface(title1);
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
SDL_FreeSurface(descSurfaces[i]);
}
}
+
+ //over
+ return surface;
}
void StatisticsManager::drawAchievement(int alpha){
if(bmAchievement==NULL) return;
if(alpha<=0) return;
if(alpha>5) alpha=5;
SDL_Rect r={screen->w-32-bmAchievement->w,32,
bmAchievement->w,bmAchievement->h};
//draw the surface
SDL_SetAlpha(bmAchievement,SDL_SRCALPHA,alpha*40);
SDL_BlitSurface(bmAchievement,NULL,screen,&r);
//draw drop shadow - corner
{
int w1=r.w/2,w2=r.w-w1,h1=r.h/2,h2=r.h-h1;
if(w1>16) w1=16;
if(w2>16) w2=16;
if(h1>16) h1=16;
if(h2>16) h2=16;
int x=(5-alpha)*64;
//top-left
SDL_Rect r1={x,0,w1+16,h1+16},r2={r.x-16,r.y-16,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//top-right
r1.x=x+48-w2;r1.w=w2+16;r2.x=r.x+r.w-w2;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom-right
r1.y=48-h2;r1.h=h2+16;r2.y=r.y+r.h-h2;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom-left
r1.x=x;r1.w=w1+16;r2.x=r.x-16;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
}
//draw drop shadow - border
int i=r.w-32;
while(i>0){
int ii=i>128?128:i;
//top
SDL_Rect r1={0,256-alpha*16,ii,16},r2={r.x+r.w-16-i,r.y-16,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom
r1.x=128;r2.y=r.y+r.h;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
i-=ii;
}
i=r.h-32;
while(i>0){
int ii=i>128?128:i;
//top
SDL_Rect r1={512-alpha*16,0,16,ii},r2={r.x-16,r.y+r.h-16-i,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom
r1.y=128;r2.x=r.x+r.w;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
i-=ii;
}
}
void StatisticsManager::updateCompletedLevelsAndAchievements(){
completedLevels=silverLevels=goldLevels=0;
LevelPackManager *lpm=getLevelPackManager();
vector<string> v=lpm->enumLevelPacks();
bool tutorial=false,tutorialGold=false;
for(unsigned int i=0;i<v.size();i++){
string& s=v[i];
LevelPack *levels=lpm->getLevelPack(s);
levels->loadProgress(getUserPath(USER_DATA)+"progress/"+s+".progress");
bool b=false;
if(s=="tutorial"){
b=tutorial=tutorialGold=true;
}
for(int n=0,m=levels->getLevelCount();n<m;n++){
LevelPack::Level *lv=levels->getLevel(n);
int medal=lv->won;
if(medal){
if(lv->targetTime<0 || lv->time<=lv->targetTime)
medal++;
if(lv->targetRecordings<0 || lv->recordings<=lv->targetRecordings)
medal++;
completedLevels++;
if(medal==2) silverLevels++;
if(medal==3) goldLevels++;
if(medal!=3 && b) tutorialGold=false;
}else if(b){
tutorial=tutorialGold=false;
}
}
}
//upadte achievements
if(completedLevels>=1) newAchievement("newbie");
if(tutorial) newAchievement("tutorial");
if(tutorialGold) newAchievement("tutorialGold");
if(completedLevels>=50) newAchievement("experienced");
if(goldLevels>=50) newAchievement("expert");
}
diff --git a/src/StatisticsManager.h b/src/StatisticsManager.h
index 3f65dc8..ddce635 100644
--- a/src/StatisticsManager.h
+++ b/src/StatisticsManager.h
@@ -1,102 +1,112 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STATISTICSMANAGER_H
#define STATISTICSMANAGER_H
#include <SDL/SDL.h>
#include <string>
#include <vector>
#include <map>
//internal struct for achievement info
struct AchievementInfo;
+class StatisticsScreen;
+
class StatisticsManager{
+ friend class StatisticsScreen;
public:
//Player and shadow traveling distance (m), 1 block = 1 meter
float playerTravelingDistance,shadowTravelingDistance;
//Player and shadow jumps
int playerJumps,shadowJumps;
//Player and shadow dies
int playerDies,shadowDies;
//Completed levels. Note: this is dynamically calculated, and doesn't save to file. (TODO:)
int completedLevels,silverLevels,goldLevels;
//Record times
int recordTimes;
//number of switched pulled
int switchTimes;
//swap times
int swapTimes;
//play time (s)
int playTime;
//level edit time (s)
int levelEditTime;
//created levels
int createdLevels;
private:
//current achievement displayed time
int achievementTime;
//some picture
SDL_Surface *bmDropShadow;
//SDL_Surface for current achievement (excluding drop shadow)
SDL_Surface *bmAchievement;
//currently owned achievements
std::map<std::string,AchievementInfo*> achievements;
//queued achievements for display
std::vector<AchievementInfo*> queuedAchievements;
//currently displayed achievement
int currentAchievement;
public:
StatisticsManager();
~StatisticsManager();
//clear the statistics and achievements.
void clear();
//load needed picture
void loadPicture();
//register avaliable achievements
static void registerAchievements();
//load statistics file.
void loadFile(const std::string& fileName);
//save statistics file.
void saveFile(const std::string& fileName);
//add or display a new achievement.
//name: the achievement id. if can't find it in avaliable achievement, nothing happens.
//save: if true then save to currently owned achievements. if it already exists in
//currently owned achievements, nothing happens.
//if false then just added it to queue, including duplicated achievements.
void newAchievement(const std::string& id,bool save=true);
//if there are new achievements, draw it on the screen,
//otherwise do nothing.
void render();
//Call this function to update completed levels.
//Completed level count is not realtime-updated, due to lazy programmers. :)
void updateCompletedLevelsAndAchievements();
+
+ //create a SDL_Surface contains specified achievements or draw to existing surface.
+ //info: achievement info.
+ //surface: specifies SDL_Surface to draw on. if NULL then new surface will be created.
+ //rect [in, out, optional]: specifies position and optionally width to draw on. height will be returned.
+ //if NULL then will be drawn on top-left corner. if surface is NULL then rect->x and rect->y are ignored.
+ //showTip: shows "New achievement" tip
+ //return value: SDL_Surface contains specified achievements or NULL if any error occured.
+ SDL_Surface* createAchievementSurface(AchievementInfo* info,SDL_Surface* surface=NULL,SDL_Rect* rect=NULL,bool showTip=true);
private:
- //internal function
- void createAchievementSurface(AchievementInfo* info);
//internal function. alpha should be 1-5, 5 means fully opaque (not really)
void drawAchievement(int alpha);
};
extern StatisticsManager statsMgr;
#endif
\ No newline at end of file
diff --git a/src/StatisticsScreen.cpp b/src/StatisticsScreen.cpp
new file mode 100644
index 0000000..91c6e61
--- /dev/null
+++ b/src/StatisticsScreen.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2012 Me and My Shadow
+ *
+ * This file is part of Me and My Shadow.
+ *
+ * Me and My Shadow is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Me and My Shadow is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string>
+#include <vector>
+#include <map>
+#include "StatisticsManager.h"
+#include "StatisticsScreen.h"
+#include "Globals.h"
+#include "Functions.h"
+#include "ThemeManager.h"
+#include "InputManager.h"
+#include "GUIListBox.h"
+#include "GUIScrollBar.h"
+#include <SDL/SDL_image.h>
+
+using namespace std;
+
+//GUI events are handled here.
+//name: The name of the element that invoked the event.
+//obj: Pointer to the object that invoked the event.
+//eventType: Integer containing the type of event.
+void StatisticsScreen::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
+ //Check what type of event it was.
+ if(eventType==GUIEventClick){
+ if(name=="cmdBack"){
+ //Goto the main menu.
+ setNextState(STATE_MENU);
+ }
+ }
+}
+
+#define DRAW_PLAYER_STATISTICS(name,var,format) { \
+ surface=TTF_RenderUTF8_Blended(fontGUISmall,name,themeTextColor); \
+ SDL_SetAlpha(surface,0,0xFF); \
+ applySurface(0,y,surface,stats,NULL); \
+ y+=surface->h; \
+ SDL_FreeSurface(surface); \
+ sprintf(s,format,statsMgr.player##var+statsMgr.shadow##var); \
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor); \
+ SDL_SetAlpha(surface,0,0xFF); \
+ applySurface(400-surface->w,y-surface->h,surface,stats,NULL); \
+ SDL_FreeSurface(surface); \
+ sprintf(s,format,statsMgr.player##var); \
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor); \
+ SDL_SetAlpha(surface,0,0xFF); \
+ applySurface(520-surface->w,y-surface->h,surface,stats,NULL); \
+ SDL_FreeSurface(surface); \
+ sprintf(s,format,statsMgr.shadow##var); \
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor); \
+ SDL_SetAlpha(surface,0,0xFF); \
+ applySurface(640-surface->w,y-surface->h,surface,stats,NULL); \
+ SDL_FreeSurface(surface); \
+}
+
+#define DRAW_MISC_STATISTICS_1(name1,var1,format1) { \
+ surface=TTF_RenderUTF8_Blended(fontGUISmall,name1,themeTextColor); \
+ SDL_SetAlpha(surface,0,0xFF); \
+ applySurface(0,y,surface,stats,NULL); \
+ x=surface->w+8; \
+ y+=surface->h; \
+ SDL_FreeSurface(surface); \
+ sprintf(s,format1,statsMgr.var1); \
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor); \
+ SDL_SetAlpha(surface,0,0xFF); \
+ applySurface(x,y-surface->h,surface,stats,NULL); \
+ SDL_FreeSurface(surface); \
+}
+
+//we are so lazy that we just use height of the first one, ignore second one
+#define DRAW_MISC_STATISTICS_2(name1,var1,format1,name2,var2,format2) { \
+ surface=TTF_RenderUTF8_Blended(fontGUISmall,name1,themeTextColor); \
+ SDL_SetAlpha(surface,0,0xFF); \
+ applySurface(0,y,surface,stats,NULL); \
+ x=surface->w+8; \
+ y+=surface->h; \
+ SDL_FreeSurface(surface); \
+ sprintf(s,format1,statsMgr.var1); \
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor); \
+ SDL_SetAlpha(surface,0,0xFF); \
+ applySurface(x,y-surface->h,surface,stats,NULL); \
+ SDL_FreeSurface(surface); \
+ surface=TTF_RenderUTF8_Blended(fontGUISmall,name2,themeTextColor); \
+ SDL_SetAlpha(surface,0,0xFF); \
+ applySurface(320,y-surface->h,surface,stats,NULL); \
+ x=surface->w+328; \
+ SDL_FreeSurface(surface); \
+ sprintf(s,format2,statsMgr.var2); \
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor); \
+ SDL_SetAlpha(surface,0,0xFF); \
+ applySurface(x,y-surface->h,surface,stats,NULL); \
+ SDL_FreeSurface(surface); \
+}
+
+//Constructor.
+StatisticsScreen::StatisticsScreen(){
+ //Load needed pictures.
+ //Note: we don't use ImageManager because we need to process these pictures.
+ SDL_Surface *bmPlayer=IMG_Load((getDataPath()+"themes/Cloudscape/player.png").c_str());
+ SDL_Surface *bmShadow=IMG_Load((getDataPath()+"themes/Cloudscape/shadow.png").c_str());
+ SDL_Surface *bmMedal=IMG_Load((getDataPath()+"gfx/medals.png").c_str());
+
+ //disable the alpha channel
+ SDL_SetAlpha(bmPlayer,0,0xFF);
+ SDL_SetAlpha(bmShadow,0,0xFF);
+ SDL_SetAlpha(bmMedal,0,0xFF);
+
+ //Render the title.
+ title=TTF_RenderUTF8_Blended(fontTitle,_("Statistics and Achievements"),themeTextColor);
+
+ //Render stats.
+ Uint32 rmask, gmask, bmask, amask;
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ rmask = 0xff000000;
+ gmask = 0x00ff0000;
+ bmask = 0x0000ff00;
+ amask = 0x000000ff;
+#else
+ rmask = 0x000000ff;
+ gmask = 0x0000ff00;
+ bmask = 0x00ff0000;
+ amask = 0xff000000;
+#endif
+ stats=SDL_CreateRGBSurface(SDL_SWSURFACE,640,400,32,rmask,gmask,bmask,amask);
+
+ char s[64];
+ SDL_Surface *surface;
+ SDL_Rect r;
+ int x,y=0;
+ Uint32 clr=SDL_MapRGB(stats->format,themeTextColor.r,themeTextColor.g,themeTextColor.b);
+
+ //Player and shadow specific statistics
+ surface=TTF_RenderUTF8_Blended(fontGUISmall,_("Total"),themeTextColor);
+ SDL_SetAlpha(surface,0,0xFF);
+ applySurface(400-surface->w,40-surface->h,surface,stats,NULL);
+ SDL_FreeSurface(surface);
+ r.x=0;r.y=0;r.w=23;r.h=40;
+ applySurface(520-r.w,0,bmPlayer,stats,&r);
+ applySurface(640-r.w,0,bmShadow,stats,&r);
+ y+=40;
+
+ DRAW_PLAYER_STATISTICS(_("Traveling distance (m)"),TravelingDistance,"%0.2f");
+ DRAW_PLAYER_STATISTICS(_("Jump times"),Jumps,"%d");
+ DRAW_PLAYER_STATISTICS(_("Die times"),Dies,"%d");
+
+ //Game specific statistics
+ r.x=0;r.y=y;r.w=stats->w;r.h=2;
+ SDL_FillRect(stats,&r,clr);
+ y+=2;
+
+ DRAW_MISC_STATISTICS_2(_("Record times:"),recordTimes,"%d",_("Switch pulled times:"),switchTimes,"%d");
+ DRAW_MISC_STATISTICS_1(_("Swap times:"),swapTimes,"%d");
+
+ //Level specific statistics
+ r.x=0;r.y=y;r.w=stats->w;r.h=2;
+ SDL_FillRect(stats,&r,clr);
+ y+=2;
+
+ surface=TTF_RenderUTF8_Blended(fontGUISmall,_("Completed levels:"),themeTextColor);
+ SDL_SetAlpha(surface,0,0xFF);
+ applySurface(0,y,surface,stats,NULL);
+ x=surface->w+8;
+ y+=surface->h;
+ SDL_FreeSurface(surface);
+
+ sprintf(s,"%d",statsMgr.completedLevels);
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor);
+ SDL_SetAlpha(surface,0,0xFF);
+ applySurface(x,y-surface->h,surface,stats,NULL);
+ SDL_FreeSurface(surface);
+
+ sprintf(s,"%d",statsMgr.completedLevels-statsMgr.goldLevels-statsMgr.silverLevels);
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor);
+ SDL_SetAlpha(surface,0,0xFF);
+ applySurface(400-surface->w,y-surface->h,surface,stats,NULL);
+ r.x=0;r.y=0;r.w=30;r.h=30;
+ applySurface(400-surface->w-30,y-30,bmMedal,stats,&r);
+ SDL_FreeSurface(surface);
+
+ sprintf(s,"%d",statsMgr.silverLevels);
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor);
+ SDL_SetAlpha(surface,0,0xFF);
+ applySurface(520-surface->w,y-surface->h,surface,stats,NULL);
+ r.x+=30;
+ applySurface(520-surface->w-30,y-30,bmMedal,stats,&r);
+ SDL_FreeSurface(surface);
+
+ sprintf(s,"%d",statsMgr.goldLevels);
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor);
+ SDL_SetAlpha(surface,0,0xFF);
+ applySurface(640-surface->w,y-surface->h,surface,stats,NULL);
+ r.x+=30;
+ applySurface(640-surface->w-30,y-30,bmMedal,stats,&r);
+ SDL_FreeSurface(surface);
+
+ //Other statistics
+ r.x=0;r.y=y;r.w=stats->w;r.h=2;
+ SDL_FillRect(stats,&r,clr);
+ y+=2;
+
+ surface=TTF_RenderUTF8_Blended(fontGUISmall,_("Playing time:"),themeTextColor);
+ SDL_SetAlpha(surface,0,0xFF);
+ applySurface(0,y,surface,stats,NULL);
+ x=surface->w+8;
+ y+=surface->h;
+ SDL_FreeSurface(surface);
+
+ sprintf(s,"%02d:%02d:%02d",statsMgr.playTime/3600,(statsMgr.playTime/60)%60,statsMgr.playTime%60);
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor);
+ SDL_SetAlpha(surface,0,0xFF);
+ applySurface(x,y-surface->h,surface,stats,NULL);
+ SDL_FreeSurface(surface);
+
+ surface=TTF_RenderUTF8_Blended(fontGUISmall,_("Level editing time:"),themeTextColor);
+ SDL_SetAlpha(surface,0,0xFF);
+ applySurface(320,y-surface->h,surface,stats,NULL);
+ x=surface->w+328;
+ SDL_FreeSurface(surface);
+
+ sprintf(s,"%02d:%02d:%02d",statsMgr.levelEditTime/3600,(statsMgr.levelEditTime/60)%60,statsMgr.levelEditTime%60);
+ surface=TTF_RenderUTF8_Blended(fontText,s,themeTextColor);
+ SDL_SetAlpha(surface,0,0xFF);
+ applySurface(x,y-surface->h,surface,stats,NULL);
+ SDL_FreeSurface(surface);
+
+ DRAW_MISC_STATISTICS_1(_("Created levels:"),createdLevels,"%d");
+
+ //Free loaded surface
+ SDL_FreeSurface(bmPlayer);
+ SDL_FreeSurface(bmShadow);
+ SDL_FreeSurface(bmMedal);
+
+ //Create GUI
+ achievements=NULL;
+ createGUI();
+}
+
+//Destructor.
+StatisticsScreen::~StatisticsScreen(){
+ //Delete the GUI.
+ if(GUIObjectRoot){
+ delete GUIObjectRoot;
+ GUIObjectRoot=NULL;
+ }
+
+ //Free images
+ SDL_FreeSurface(title);
+ SDL_FreeSurface(stats);
+ SDL_FreeSurface(achievements);
+}
+
+//Method that will create the GUI for the options menu.
+void StatisticsScreen::createGUI(){
+ //Draw achievements
+ if(achievements) SDL_FreeSurface(achievements);
+
+ vector<SDL_Surface*> surfaces;
+ int w=SCREEN_WIDTH-128-16,h=0;
+
+ for(map<string,AchievementInfo*>::iterator it=statsMgr.achievements.begin();
+ it!=statsMgr.achievements.end();it++)
+ {
+ SDL_Rect r={0,0,w,0};
+ SDL_Surface *surface=statsMgr.createAchievementSurface(it->second,NULL,&r,false);
+ if(surface!=NULL){
+ surfaces.push_back(surface);
+ h+=r.h;
+ }
+ }
+
+ if(surfaces.empty()){
+ achievements=TTF_RenderUTF8_Blended(fontText,_("You don't have any achievements."),themeTextColor);
+ }else{
+ achievements=SDL_CreateRGBSurface(SDL_HWSURFACE,w,h,
+ screen->format->BitsPerPixel,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0);
+
+ h=0;
+ for(unsigned int i=0;i<surfaces.size();i++){
+ SDL_Rect r={0,h,0,0};
+ SDL_BlitSurface(surfaces[i],NULL,achievements,&r);
+ h+=surfaces[i]->h;
+ SDL_FreeSurface(surfaces[i]);
+ }
+ }
+
+ //Create the root element of the GUI.
+ if(GUIObjectRoot){
+ delete GUIObjectRoot;
+ GUIObjectRoot=NULL;
+ }
+ GUIObjectRoot=new GUIObject(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GUIObjectNone);
+
+ //Create back button.
+ GUIObject* obj=new GUIObject(SCREEN_WIDTH*0.5,SCREEN_HEIGHT-60,-1,36,GUIObjectButton,_("Back"),0,true,true,GUIGravityCenter);
+ obj->name="cmdBack";
+ obj->eventCallback=this;
+ GUIObjectRoot->addChild(obj);
+
+ //Create list box
+ listBox=new GUISingleLineListBox((SCREEN_WIDTH-500)/2,104,500,32);
+ listBox->item.push_back(_("Statistics"));
+ listBox->item.push_back(_("Achievements"));
+ listBox->value=0;
+ GUIObjectRoot->addChild(listBox);
+
+ //Create vertical scrollbar.
+ h-=SCREEN_HEIGHT-144-80;
+ if(h<0) h=0;
+ scrollbarV=new GUIScrollBar(SCREEN_WIDTH-64-16,144,16,SCREEN_HEIGHT-144-80,1,0,0,h,16,SCREEN_HEIGHT-144-80,true,false);
+ GUIObjectRoot->addChild(scrollbarV);
+}
+
+//In this method all the key and mouse events should be handled.
+//Note: The GUIEvents won't be handled here.
+void StatisticsScreen::handleEvents(){
+ //Check if we need to quit, if so enter the exit state.
+ if(event.type==SDL_QUIT){
+ setNextState(STATE_EXIT);
+ }
+
+ //Check if the escape button is pressed, if so go back to the main menu.
+ if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
+ setNextState(STATE_MENU);
+ }
+
+ //Check for scrolling down and up with mouse scroll wheel.
+ if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELDOWN && scrollbarV->visible){
+ if(scrollbarV->value<scrollbarV->maxValue)
+ scrollbarV->value+=scrollbarV->smallChange*4;
+ if(scrollbarV->value>scrollbarV->maxValue)
+ scrollbarV->value=scrollbarV->maxValue;
+ return;
+ }else if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELUP && scrollbarV->visible){
+ if(scrollbarV->value>0)
+ scrollbarV->value-=scrollbarV->smallChange*4;
+ if(scrollbarV->value<0)
+ scrollbarV->value=0;
+ return;
+ }
+}
+
+//All the logic that needs to be done should go in this method.
+void StatisticsScreen::logic(){
+}
+
+//This method handles all the rendering.
+void StatisticsScreen::render(){
+ //Draw background.
+ objThemes.getBackground(true)->draw(screen);
+ objThemes.getBackground(true)->updateAnimation();
+
+ //Draw title.
+ applySurface((SCREEN_WIDTH-title->w)/2,40-TITLE_FONT_RAISE,title,screen,NULL);
+
+ switch(listBox->value){
+ case 0:
+ //statistics
+ scrollbarV->visible=false;
+ applySurface((SCREEN_WIDTH-stats->w)/2,144,stats,screen,NULL);
+ break;
+ case 1:
+ //achievements
+ {
+ scrollbarV->visible=(scrollbarV->maxValue>0);
+
+ SDL_Rect r1={0,scrollbarV->value,achievements->w,SCREEN_HEIGHT-144-80};
+ SDL_Rect r2={64,144,0,0};
+ SDL_BlitSurface(achievements,&r1,screen,&r2);
+ }
+ break;
+ }
+}
+
+//Method that will be called when the screen size has been changed in runtime.
+void StatisticsScreen::resize(){
+ //Recreate the gui to fit the new resolution.
+ createGUI();
+}
diff --git a/src/StatisticsScreen.h b/src/StatisticsScreen.h
new file mode 100644
index 0000000..7118bf5
--- /dev/null
+++ b/src/StatisticsScreen.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 Me and My Shadow
+ *
+ * This file is part of Me and My Shadow.
+ *
+ * Me and My Shadow is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Me and My Shadow is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef STATISTICSSCREEN_H
+#define STATISTICSSCREEN_H
+
+#include <SDL/SDL.h>
+#include "GameState.h"
+#include "GUIObject.h"
+
+class GUISingleLineListBox;
+class GUIScrollBar;
+
+class StatisticsScreen:public GameState, private GUIEventCallback{
+private:
+ //contains title.
+ SDL_Surface* title;
+ //contains statistics
+ SDL_Surface* stats;
+ //contains list of achievements.
+ SDL_Surface* achievements;
+
+ //the list box used to switch from statistics and achievements
+ GUISingleLineListBox* listBox;
+
+ //the scroll bar
+ GUIScrollBar* scrollbarV;
+
+ //GUI events are handled here.
+ //name: The name of the element that invoked the event.
+ //obj: Pointer to the object that invoked the event.
+ //eventType: Integer containing the type of event.
+ void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType);
+public:
+ //Constructor.
+ StatisticsScreen();
+ //Destructor.
+ virtual ~StatisticsScreen();
+
+ //Method that will create the GUI for the options menu.
+ void createGUI();
+
+ //In this method all the key and mouse events should be handled.
+ //Note: The GUIEvents won't be handled here.
+ virtual void handleEvents();
+
+ //All the logic that needs to be done should go in this method.
+ virtual void logic();
+
+ //This method handles all the rendering.
+ virtual void render();
+
+ //Method that will be called when the screen size has been changed in runtime.
+ virtual void resize();
+};
+
+#endif
diff --git a/src/TitleMenu.cpp b/src/TitleMenu.cpp
index 4db6f5c..ab6e034 100644
--- a/src/TitleMenu.cpp
+++ b/src/TitleMenu.cpp
@@ -1,874 +1,907 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Functions.h"
#include "GameState.h"
#include "Globals.h"
#include "TitleMenu.h"
#include "GUIListBox.h"
#include "InputManager.h"
#include <iostream>
#include <algorithm>
#include <sstream>
#include "libs/tinygettext/tinygettext.hpp"
using namespace std;
/////////////////////////MAIN_MENU//////////////////////////////////
//Integer containing the highlighted/selected menu option.
static int highlight=0;
Menu::Menu(){
highlight=0;
animation=0;
//Load the title image.
title=loadImage(getDataPath()+"gfx/menu/title.png");
//Now render the five entries.
//SDL_Color black={0,0,0};
entries[0]=TTF_RenderUTF8_Blended(fontTitle,_("Play"),themeTextColor);
entries[1]=TTF_RenderUTF8_Blended(fontTitle,_("Options"),themeTextColor);
entries[2]=TTF_RenderUTF8_Blended(fontTitle,_("Map Editor"),themeTextColor);
entries[3]=TTF_RenderUTF8_Blended(fontTitle,_("Addons"),themeTextColor);
entries[4]=TTF_RenderUTF8_Blended(fontTitle,_("Quit"),themeTextColor);
entries[5]=TTF_RenderUTF8_Blended(fontTitle,">",themeTextColor);
entries[6]=TTF_RenderUTF8_Blended(fontTitle,"<",themeTextColor);
//Load the credits icon.
+ statisticsIcon=loadImage(getDataPath()+"gfx/menu/statistics.png");
creditsIcon=loadImage(getDataPath()+"gfx/menu/credits.png");
}
Menu::~Menu(){
//We need to free the five text surfaceses.
for(unsigned int i=0;i<7;i++)
SDL_FreeSurface(entries[i]);
}
void Menu::handleEvents(){
//Get the x and y location of the mouse.
int x,y;
SDL_GetMouseState(&x,&y);
//Calculate which option is highlighted using the location of the mouse.
//Only if mouse is 'doing something'
if(event.type==SDL_MOUSEMOTION || event.type==SDL_MOUSEBUTTONDOWN){
+ highlight=0;
+
if(x>=200&&x<SCREEN_WIDTH-200&&y>=(SCREEN_HEIGHT-250)/2&&y<(SCREEN_HEIGHT-200)/2+320){
highlight=(y-((SCREEN_HEIGHT-200)/2-64))/64;
+ if(highlight>5) highlight=0;
}
- //Also check the credits icon.
- if(x>=SCREEN_WIDTH-48&&x<SCREEN_WIDTH&&y>SCREEN_HEIGHT-48&&y<SCREEN_HEIGHT){
- highlight=0;
- if(event.type==SDL_MOUSEBUTTONDOWN)
- setNextState(STATE_CREDITS);
+ //Also check the icons.
+ if(y>=SCREEN_HEIGHT-56&&y<SCREEN_HEIGHT-8){
+ if(x>=SCREEN_WIDTH-8){
+ //do nothing
+ }else if(x>=SCREEN_WIDTH-56){
+ highlight=7;
+ }else if(x>=SCREEN_WIDTH-104){
+ highlight=6;
+ }
}
}
//Down/Up -arrows move highlight
if(inputMgr.isKeyDownEvent(INPUTMGR_DOWN)){
highlight++;
- if(highlight>=6)
- highlight=5;
+ if(highlight>7)
+ highlight=0;
}
if(inputMgr.isKeyDownEvent(INPUTMGR_UP)){
highlight--;
if(highlight<1)
- highlight=1;
+ highlight=7;
}
//Check if there's a press event.
if((event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT) ||
(inputMgr.isKeyUpEvent(INPUTMGR_SELECT))){
//We have one so check which selected/highlighted option needs to be done.
switch(highlight){
case 1:
//Enter the levelSelect state.
setNextState(STATE_LEVEL_SELECT);
break;
case 2:
//Enter the options state.
setNextState(STATE_OPTIONS);
break;
case 3:
//Enter the levelEditor, but first set the level to a default leveledit map.
levelName="";
setNextState(STATE_LEVEL_EDIT_SELECT);
break;
case 4:
//Check if internet is enabled.
if(!getSettings()->getBoolValue("internet")){
msgBox(_("Enable internet in order to install addons."),MsgBoxOKOnly,_("Internet disabled"));
break;
}
//Enter the addons state.
setNextState(STATE_ADDONS);
break;
case 5:
//We quit, so we enter the exit state.
setNextState(STATE_EXIT);
break;
+ case 6:
+ //Show the statistics screen.
+ setNextState(STATE_STATISTICS);
+ break;
+ case 7:
+ //Show the credits screen.
+ setNextState(STATE_CREDITS);
+ break;
}
}
//We also need to quit the menu when escape is pressed.
if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
setNextState(STATE_EXIT);
}
//Check if we need to quit, if so we enter the exit state.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
}
//Nothing to do here
void Menu::logic(){
animation++;
if(animation>10)
animation=-10;
}
void Menu::render(){
//Draw background.
objThemes.getBackground(true)->draw(screen);
objThemes.getBackground(true)->updateAnimation();
//Draw the title.
applySurface((SCREEN_WIDTH-title->w)/2,40,title,screen,NULL);
//Draw the menu entries.
for(unsigned int i=0;i<5;i++){
applySurface((SCREEN_WIDTH-entries[i]->w)/2,(SCREEN_HEIGHT-200)/2+64*i+(64-entries[i]->h)/2,entries[i],screen,NULL);
}
//Check if an option is selected/highlighted.
- if(highlight>0){
+ if(highlight>0 && highlight<=5){
//Draw the '>' sign, which is entry 5.
int x=(SCREEN_WIDTH-entries[highlight-1]->w)/2-(25-abs(animation)/2)-entries[5]->w;
int y=(SCREEN_HEIGHT-200)/2-64+64*highlight+(64-entries[5]->h)/2;
applySurface(x,y,entries[5],screen,NULL);
//Draw the '<' sign, which is entry 6.
x=(SCREEN_WIDTH-entries[highlight-1]->w)/2+entries[highlight-1]->w+(25-abs(animation)/2);
y=(SCREEN_HEIGHT-200)/2-64+64*highlight+(64-entries[6]->h)/2;
applySurface(x,y,entries[6],screen,NULL);
}
+ //Check if an icon is selected/highlighted and draw tooltip
+ if(highlight==6){
+ SDL_Color fg={0,0,0};
+ SDL_Surface *surface=TTF_RenderUTF8_Blended(fontText,_("Statistics and Achievements"),fg);
+ drawGUIBox(SCREEN_WIDTH-64-surface->w-2,SCREEN_HEIGHT-56-surface->h-2,surface->w+4,surface->h+4,screen,0xFFFFFF00|230);
+ applySurface(SCREEN_WIDTH-64-surface->w,SCREEN_HEIGHT-56-surface->h,surface,screen,NULL);
+ SDL_FreeSurface(surface);
+ }
+ if(highlight==7){
+ SDL_Color fg={0,0,0};
+ SDL_Surface *surface=TTF_RenderUTF8_Blended(fontText,_("Credits"),fg);
+ drawGUIBox(SCREEN_WIDTH-16-surface->w-2,SCREEN_HEIGHT-56-surface->h-2,surface->w+4,surface->h+4,screen,0xFFFFFF00|230);
+ applySurface(SCREEN_WIDTH-16-surface->w,SCREEN_HEIGHT-56-surface->h,surface,screen,NULL);
+ SDL_FreeSurface(surface);
+ }
+
//Draw the credits icon.
- applySurface(SCREEN_WIDTH-40,SCREEN_HEIGHT-40,creditsIcon,screen,NULL);
+ applySurface(SCREEN_WIDTH-96,SCREEN_HEIGHT-48,statisticsIcon,screen,NULL);
+ applySurface(SCREEN_WIDTH-48,SCREEN_HEIGHT-48,creditsIcon,screen,NULL);
}
void Menu::resize(){}
/////////////////////////OPTIONS_MENU//////////////////////////////////
//Some varables for the options.
static bool fullscreen,leveltheme,internet;
static string themeName,languageName;
static int lastLang,lastRes;
static bool useProxy;
static string internetProxy;
static bool restartFlag;
static _res currentRes;
static vector<_res> resolutionList;
Options::Options(){
//Render the title.
title=TTF_RenderUTF8_Blended(fontTitle,_("Settings"),themeTextColor);
lastJumpSound=0;
//Set some default settings.
fullscreen=getSettings()->getBoolValue("fullscreen");
languageName=getSettings()->getValue("lang");
themeName=processFileName(getSettings()->getValue("theme"));
leveltheme=getSettings()->getBoolValue("leveltheme");
internet=getSettings()->getBoolValue("internet");
internetProxy=getSettings()->getValue("internet-proxy");
useProxy=!internetProxy.empty();
//Set the restartFlag false.
restartFlag=false;
//Now create the gui.
createGUI();
}
Options::~Options(){
//Delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Free the title image.
SDL_FreeSurface(title);
}
void Options::createGUI(){
//Variables for positioning
int x = (SCREEN_WIDTH-540)/2;
int liftY=40; //TODO: This is variable for laziness of maths...
//Create the root element of the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
GUIObjectRoot=new GUIObject(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GUIObjectNone);
//Now we create GUIObjects for every option.
GUIObject* obj=new GUIObject(x,150-liftY,240,36,GUIObjectLabel,_("Music"));
GUIObjectRoot->addChild(obj);
musicSlider=new GUISlider(x+220,150-liftY,256,36,atoi(getSettings()->getValue("music").c_str()),0,128,15);
musicSlider->name="sldMusic";
musicSlider->eventCallback=this;
GUIObjectRoot->addChild(musicSlider);
obj=new GUIObject(x,190-liftY,240,36,GUIObjectLabel,_("Sound"));
GUIObjectRoot->addChild(obj);
soundSlider=new GUISlider(x+220,190-liftY,256,36,atoi(getSettings()->getValue("sound").c_str()),0,128,15);
soundSlider->name="sldSound";
soundSlider->eventCallback=this;
GUIObjectRoot->addChild(soundSlider);
obj=new GUIObject(x,230-liftY,240,36,GUIObjectCheckBox,_("Fullscreen"),fullscreen?1:0);
obj->name="chkFullscreen";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
obj=new GUIObject(x,270-liftY,240,36,GUIObjectLabel,_("Resolution"));
obj->name="lstResolution";
GUIObjectRoot->addChild(obj);
//Create list with many different resolutions
resolutions = new GUISingleLineListBox(x+220,270-liftY,300,36);
resolutions->value=-1;
//Enumerate avaliable resolutions using SDL_ListModes()
//Note: we enumerate fullscreen resolutions because
// windowed resolutions always can be arbitrary
if(resolutionList.empty()){
SDL_Rect **modes=SDL_ListModes(NULL,SDL_FULLSCREEN|SDL_HWSURFACE);
if(modes==NULL || ((intptr_t)modes) == -1){
cout<<"Error: Can't enumerate avaliable screen resolutions."
" Use predefined screen resolutions list instead."<<endl;
static const _res predefinedResolutionList[] = {
{800,600},
{1024,600},
{1024,768},
{1152,864},
{1280,720},
{1280,768},
{1280,800},
{1280,960},
{1280,1024},
{1360,768},
{1366,768},
{1440,900},
{1600,900},
{1600,1200},
{1680,1080},
{1920,1080},
{1920,1200},
{2560,1440},
{3840,2160}
};
for(unsigned int i=0;i<sizeof(predefinedResolutionList)/sizeof(_res);i++){
resolutionList.push_back(predefinedResolutionList[i]);
}
}else{
for(unsigned int i=0;modes[i]!=NULL;i++){
//Check if the resolution is big enough
if(modes[i]->w>=800 && modes[i]->h>=600){
_res res={modes[i]->w, modes[i]->h};
resolutionList.push_back(res);
}
}
reverse(resolutionList.begin(),resolutionList.end());
}
}
//Get current resolution from config file. Thus it can be user defined
currentRes.w=atoi(getSettings()->getValue("width").c_str());
currentRes.h=atoi(getSettings()->getValue("height").c_str());
for (int i=0; i<(int)resolutionList.size();i++){
//Create a string from width and height and then add it to list
ostringstream out;
out << resolutionList[i].w << "x" << resolutionList[i].h;
resolutions->item.push_back(out.str());
//Check if current resolution matches, select it
if (resolutionList[i].w==currentRes.w && resolutionList[i].h==currentRes.h){
resolutions->value=i;
}
}
//Add current resolution if it isn't already in the list
if(resolutions->value==-1){
ostringstream out;
out << currentRes.w << "x" << currentRes.h;
resolutions->item.push_back(out.str());
resolutions->value=resolutions->item.size()-1;
}
lastRes=resolutions->value;
GUIObjectRoot->addChild(resolutions);
obj=new GUIObject(x,310-liftY,240,36,GUIObjectLabel,_("Language"));
obj->name="lstResolution";
GUIObjectRoot->addChild(obj);
//Create GUI list with available languages
langs = new GUISingleLineListBox(x+220,310-liftY,300,36);
langs->name="lstLanguages";
/// TRANSLATORS: as detect user's language automatically
langs->item.push_back(_("Auto-Detect"));
langValues.push_back("");
langs->item.push_back("English");
langValues.push_back("en");
//Get a list of every available language
set<tinygettext::Language> languages = dictionaryManager->get_languages();
for (set<tinygettext::Language>::iterator s0 = languages.begin(); s0 != languages.end(); ++s0){
//If language in loop is the same in config file, then select it
if(getSettings()->getValue("lang")==s0->str()){
lastLang=distance(languages.begin(),s0)+2;
}
//Add language in loop to list and listbox
langs->item.push_back(s0->get_name());
langValues.push_back(s0->str());
}
//If Auto or English are selected
if(getSettings()->getValue("lang")==""){
lastLang=0;
}else if(getSettings()->getValue("lang")=="en"){
lastLang=1;
}
langs->value=lastLang;
GUIObjectRoot->addChild(langs);
obj=new GUIObject(x,350-liftY,240,36,GUIObjectLabel,_("Theme"));
obj->name="theme";
GUIObjectRoot->addChild(obj);
//Create the theme option gui element.
theme=new GUISingleLineListBox(x+220,350-liftY,300,36);
theme->name="lstTheme";
vector<string> v=enumAllDirs(getUserPath(USER_DATA)+"themes/");
for(vector<string>::iterator i = v.begin(); i != v.end(); ++i){
themeLocations[*i]=getUserPath(USER_DATA)+"themes/"+*i;
}
vector<string> v2=enumAllDirs(getDataPath()+"themes/");
for(vector<string>::iterator i = v2.begin(); i != v2.end(); ++i){
themeLocations[*i]=getDataPath()+"themes/"+*i;
}
v.insert(v.end(), v2.begin(), v2.end());
//Try to find the configured theme so we can display it.
int value=-1;
for(vector<string>::iterator i = v.begin(); i != v.end(); ++i){
if(themeLocations[*i]==themeName) {
value=i-v.begin();
}
}
theme->item=v;
if(value==-1)
value=theme->item.size()-1;
theme->value=value;
//NOTE: We call the event handling method to correctly set the themename.
GUIEventCallback_OnEvent("lstTheme",theme,GUIEventChange);
theme->eventCallback=this;
GUIObjectRoot->addChild(theme);
obj=new GUIObject(x,390-liftY,240,36,GUIObjectCheckBox,_("Level themes"),leveltheme?1:0);
obj->name="chkLeveltheme";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
obj=new GUIObject(x,430-liftY,240,36,GUIObjectCheckBox,_("Internet"),internet?1:0);
obj->name="chkInternet";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
//new: proxy settings
obj=new GUIObject(x,470-liftY,240,36,GUIObjectLabel,_("Internet proxy"));
obj->name="chkProxy";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
obj=new GUIObject(x+220,470-liftY,300,36,GUIObjectTextBox,internetProxy.c_str());
obj->name="txtProxy";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
//new: key settings
GUIObject* b1=new GUIObject(SCREEN_WIDTH*0.3,SCREEN_HEIGHT-120,-1,36,GUIObjectButton,_("Config Keys"),0,true,true,GUIGravityCenter);
b1->name="cmdKeys";
b1->eventCallback=this;
GUIObjectRoot->addChild(b1);
//Reset progress settings.
/// TRANSLATORS: Used for button which clear any level progress like unlocked levels and highscores.
GUIObject* b2=new GUIObject(SCREEN_WIDTH*0.7,SCREEN_HEIGHT-120,-1,36,GUIObjectButton,_("Clear Progress"),0,true,true,GUIGravityCenter);
b2->name="cmdReset";
b2->eventCallback=this;
GUIObjectRoot->addChild(b2);
b1->render(0,0,false);
b2->render(0,0,false);
if(b2->left-b2->gravityX < b1->left+b1->width-b1->gravityX){
b1->smallFont=true;
b1->width=-1;
b2->smallFont=true;
b2->width=-1;
}
b1=new GUIObject(SCREEN_WIDTH*0.3,SCREEN_HEIGHT-60,-1,36,GUIObjectButton,_("Cancel"),0,true,true,GUIGravityCenter);
b1->name="cmdBack";
b1->eventCallback=this;
GUIObjectRoot->addChild(b1);
b2=new GUIObject(SCREEN_WIDTH*0.7,SCREEN_HEIGHT-60,-1,36,GUIObjectButton,_("Save Changes"),0,true,true,GUIGravityCenter);
b2->name="cmdSave";
b2->eventCallback=this;
GUIObjectRoot->addChild(b2);
b1->render(0,0,false);
b2->render(0,0,false);
if(b2->left-b2->gravityX < b1->left+b1->width-b1->gravityX){
b1->smallFont=true;
b1->width=-1;
b2->smallFont=true;
b2->width=-1;
}
}
static string convertInt(int i){
stringstream ss;
ss << i;
return ss.str();
}
void Options::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Check what type of event it was.
if(eventType==GUIEventClick){
if(name=="cmdBack"){
//TODO: Reset the key changes.
//Reset the music volume.
getMusicManager()->setVolume(atoi(getSettings()->getValue("music").c_str()));
Mix_Volume(-1,atoi(getSettings()->getValue("sound").c_str()));
//And goto the main menu.
setNextState(STATE_MENU);
}else if(name=="cmdSave"){
//Save is pressed thus save
char s[64];
sprintf(s,"%d",soundSlider->value);
getSettings()->setValue("sound",s);
sprintf(s,"%d",musicSlider->value);
getSettings()->setValue("music",s);
getMusicManager()->setEnabled(musicSlider->value>0);
Mix_Volume(-1,soundSlider->value);
getSettings()->setValue("fullscreen",fullscreen?"1":"0");
getSettings()->setValue("leveltheme",leveltheme?"1":"0");
getSettings()->setValue("internet",internet?"1":"0");
getSettings()->setValue("theme",themeName);
loadTheme(themeName);
if(!useProxy)
internetProxy.clear();
getSettings()->setValue("internet-proxy",internetProxy);
getSettings()->setValue("lang",langValues.at(langs->value));
//Is resolution from the list or is it user defined in config file
if(resolutions->value<(int)resolutionList.size()){
getSettings()->setValue("width",convertInt(resolutionList[resolutions->value].w));
getSettings()->setValue("height",convertInt(resolutionList[resolutions->value].h));
}else{
getSettings()->setValue("width",convertInt(currentRes.w));
getSettings()->setValue("height",convertInt(currentRes.h));
}
//Save the key configuration.
inputMgr.saveConfig();
//Save the settings.
saveSettings();
//Before we return check if some .
if(restartFlag || resolutions->value!=lastRes){
//The resolution changed so we need to recreate the screen.
if(!createScreen()){
//Screen creation failed so set to safe settings.
getSettings()->setValue("fullscreen","0");
getSettings()->setValue("width",convertInt(resolutionList[lastRes].w));
getSettings()->setValue("height",convertInt(resolutionList[lastRes].h));
if(!createScreen()){
//Everything fails so quit.
setNextState(STATE_EXIT);
return;
}
}
//The screen is created, now load the (menu) theme.
if(!loadTheme("")){
//Loading the theme failed so quit.
setNextState(STATE_EXIT);
return;
}
}
if(langs->value!=lastLang){
//We set the language.
language=langValues.at(langs->value);
dictionaryManager->set_language(tinygettext::Language::from_name(langValues.at(langs->value)));
getLevelPackManager()->updateLanguage();
//And reload the font.
if(!loadFonts()){
//Loading failed so quit.
setNextState(STATE_EXIT);
return;
}
}
//Now return to the main menu.
setNextState(STATE_MENU);
}else if(name=="cmdKeys"){
inputMgr.showConfig();
}else if(name=="cmdReset"){
if(msgBox(_("Do you really want to reset level progress?"),MsgBoxYesNo,_("Warning"))==MsgBoxYes){
//We delete the progress folder.
#ifdef WIN32
removeDirectory((getUserPath()+"progress").c_str());
createDirectory((getUserPath()+"progress").c_str());
#else
removeDirectory((getUserPath(USER_DATA)+"/progress").c_str());
createDirectory((getUserPath(USER_DATA)+"/progress").c_str());
#endif
}
return;
}else if(name=="chkFullscreen"){
fullscreen=obj->value?true:false;
//Check if fullscreen changed.
if(fullscreen==getSettings()->getBoolValue("fullscreen")){
//We disable the restart message flag.
restartFlag=false;
}else{
//We set the restart message flag.
restartFlag=true;
}
}else if(name=="chkLeveltheme"){
leveltheme=obj->value?true:false;
}else if(name=="chkInternet"){
internet=obj->value?true:false;
}else if(name=="chkProxy"){
useProxy=obj->value?true:false;
}
}
if(name=="lstTheme"){
if(theme!=NULL && theme->value>=0 && theme->value<(int)theme->item.size()){
//Check if the theme is installed in the data path.
if(themeLocations[theme->item[theme->value]].find(getDataPath())!=string::npos){
themeName="%DATA%/themes/"+fileNameFromPath(themeLocations[theme->item[theme->value]]);
}else if(themeLocations[theme->item[theme->value]].find(getUserPath(USER_DATA))!=string::npos){
themeName="%USER%/themes/"+fileNameFromPath(themeLocations[theme->item[theme->value]]);
}else{
themeName=themeLocations[theme->item[theme->value]];
}
}
}else if(name=="txtProxy"){
internetProxy=obj->caption;
//Check if the internetProxy field is empty.
useProxy=!internetProxy.empty();
}else if(name=="sldMusic"){
getMusicManager()->setEnabled(musicSlider->value>0);
getMusicManager()->setVolume(musicSlider->value);
}else if(name=="sldSound"){
Mix_Volume(-1,soundSlider->value);
if(lastJumpSound==0){
Mix_PlayChannel(-1,jumpSound,0);
lastJumpSound=15;
}
}
}
void Options::handleEvents(){
//Check if we need to quit, if so enter the exit state.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
//Check if the escape button is pressed, if so go back to the main menu.
if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
setNextState(STATE_MENU);
}
}
void Options::logic(){
//Increase the lastJumpSound variable if needed.
if(lastJumpSound!=0){
lastJumpSound--;
}
}
void Options::render(){
//Draw background.
objThemes.getBackground(true)->draw(screen);
objThemes.getBackground(true)->updateAnimation();
//Now render the title.
applySurface((SCREEN_WIDTH-title->w)/2,40-TITLE_FONT_RAISE,title,screen,NULL);
//NOTE: The rendering of the GUI is done in Main.
}
void Options::resize(){
//Recreate the gui to fit the new resolution.
createGUI();
}
/////////////////////////CREDITS_MENU//////////////////////////////////
Credits::Credits(){
//Render the title.
title=TTF_RenderUTF8_Blended(fontTitle,_("Credits"),themeTextColor);
//Vector that will hold every line of the credits.
vector<string> credits;
//Open the AUTHORS file and read every line.
{
ifstream fin((getDataPath()+"/../AUTHORS").c_str());
if(!fin.is_open()) {
cerr<<"ERROR: Unable to open the AUTHORS file."<<endl;
credits.push_back("ERROR: Unable to open the AUTHORS file.");
credits.push_back("");
}
//Loop the lines of the file.
string line;
while(getline(fin,line)){
credits.push_back(line);
}
}
//Enter a new line between the two files.
credits.push_back("");
//Open the Credits.text file and read every line.
{
ifstream fin((getDataPath()+"/Credits.txt").c_str());
if(!fin.is_open()) {
cerr<<"ERROR: Unable to open the Credits.txt file."<<endl;
credits.push_back("ERROR: Unable to open the Credits.txt file.");
credits.push_back("");
}
//Loop the lines of the file.
string line;
while(getline(fin,line)){
credits.push_back(line);
//NOTE: Some sections point to other credits files.
if(line=="music/") {
vector<string> musicCredits=getMusicManager()->createCredits();
credits.insert(credits.end(),musicCredits.begin(),musicCredits.end());
}
}
}
//Now determine the number of lines and calculate the height of the resulting credits surface.
int lines=credits.size();
int fontHeight=TTF_FontLineSkip(fontText);
int maxW=0;
//Find out the width of the longest line
for(int i=0;i<lines;i++){
if(credits[i][0]!='\0'){
int w;
TTF_SizeUTF8(fontText,credits[i].c_str(),&w,NULL);
if(w>maxW)
maxW=w;
}
}
//Make sure that the surface works both on little and big endian systems which have different byte-order.
Uint32 rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
//Finally create the surface and draw every line of text there
creditsText=SDL_CreateRGBSurface(SDL_SWSURFACE,maxW,lines*fontHeight,32,rmask,gmask,bmask,amask);
for(int i=0;i<lines;i++){
if(credits[i][0]!='\0'){
SDL_Surface* lineSurf=TTF_RenderUTF8_Blended(fontText,credits[i].c_str(),themeTextColor);
SDL_SetAlpha(lineSurf,0,0xFF);
SDL_SetAlpha(creditsText,SDL_SRCALPHA,SDL_ALPHA_TRANSPARENT);
applySurface(0,fontHeight*i,lineSurf,creditsText,NULL);
SDL_FreeSurface(lineSurf);
}
}
//Create GUI
createGUI();
}
Credits::~Credits(){
//Delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Free images
SDL_FreeSurface(title);
SDL_FreeSurface(creditsText);
}
void Credits::createGUI(){
//Create the root element of the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
GUIObjectRoot=new GUIObject(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GUIObjectNone);
//Create back button.
GUIObject* obj=new GUIObject(SCREEN_WIDTH*0.5,SCREEN_HEIGHT-60,-1,36,GUIObjectButton,_("Back"),0,true,true,GUIGravityCenter);
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
//Create vertical scrollbar.
scrollbarV=new GUIScrollBar(SCREEN_WIDTH-64-16,128,16,SCREEN_HEIGHT-128-92,1,0,0,creditsText->h/8-(SCREEN_HEIGHT-128-92)/8);
GUIObjectRoot->addChild(scrollbarV);
//If text is too long, create horizontal scrollbar.
if(creditsText->w>SCREEN_WIDTH-128){
scrollbarH=new GUIScrollBar(64,SCREEN_HEIGHT-92,SCREEN_WIDTH-128-16,16,0,0,0,creditsText->w/8-(SCREEN_WIDTH-64-92)/8);
GUIObjectRoot->addChild(scrollbarH);
}else{
scrollbarH=NULL;
}
}
void Credits::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Check what type of event it was.
if(eventType==GUIEventClick){
if(name=="cmdBack"){
//Goto the main menu.
setNextState(STATE_MENU);
}
}
}
void Credits::handleEvents(){
//Check if we need to quit, if so enter the exit state.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
//Check if the escape button is pressed, if so go back to the main menu.
if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
setNextState(STATE_MENU);
}
//Check for scrolling down and up with mouse scroll wheel.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELDOWN && scrollbarV){
if(scrollbarV->value<scrollbarV->maxValue)
scrollbarV->value+=scrollbarV->smallChange;
if(scrollbarV->value>scrollbarV->maxValue)
scrollbarV->value=scrollbarV->maxValue;
return;
}else if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELUP && scrollbarV){
if(scrollbarV->value>0)
scrollbarV->value-=scrollbarV->smallChange;
if(scrollbarV->value<0)
scrollbarV->value=0;
return;
}
}
void Credits::logic(){
}
void Credits::render(){
//Draw background.
objThemes.getBackground(true)->draw(screen);
objThemes.getBackground(true)->updateAnimation();
//Now render the title.
applySurface((SCREEN_WIDTH-title->w)/2,40-TITLE_FONT_RAISE,title,screen,NULL);
//Clip and draw text accoring to scrollbars' values.
SDL_Rect r;
if(scrollbarH)
r.x = scrollbarH->value*8;
else
r.x = 0;
r.y = scrollbarV->value*8;
r.w = SCREEN_WIDTH-128-16;
r.h = SCREEN_HEIGHT-128-92;
applySurface(64,128,creditsText,screen,&r);
//NOTE: The rendering of the GUI is done in Main.
}
void Credits::resize(){
//Recreate the gui to fit the new resolution.
createGUI();
}
diff --git a/src/TitleMenu.h b/src/TitleMenu.h
index 40cce60..a3df8ba 100644
--- a/src/TitleMenu.h
+++ b/src/TitleMenu.h
@@ -1,148 +1,151 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TITLE_MENU_H
#define TITLE_MENU_H
#include <SDL/SDL.h>
#include "GameState.h"
//Included for the Options menu.
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUISlider.h"
//The Main menu.
class Menu : public GameState{
private:
//The title of the main menu.
SDL_Surface* title;
//Array containg pointers to the five main menu entries.
//The last two are the '>' and '<' characters.
SDL_Surface* entries[7];
+ //The icon for the statistics menu.
+ SDL_Surface* statisticsIcon;
+
//The icon for the credits menu.
SDL_Surface* creditsIcon;
//Integer used for animations.
int animation;
public:
//Constructor.
Menu();
//Destructor.
~Menu();
//Inherited from GameState.
void handleEvents();
void logic();
void render();
void resize();
};
//The Options menu.
class Options : public GameState, private GUIEventCallback{
private:
//The title of the options menu.
SDL_Surface* title;
//Slider used to set the music volume
GUISlider* musicSlider;
//Slider used to set the sound volume
GUISlider* soundSlider;
//Integer to keep track of the time passed since last playing the test sound.
int lastJumpSound;
//ListBox containing the themes the user can choose out.
GUISingleLineListBox* theme;
//Map containing the locations the themes are stored.
//The key is the name of the theme and the value the path.
std::map<std::string,std::string> themeLocations;
//Available languages
GUISingleLineListBox* langs;
std::vector<std::string> langValues;
//Resolution list
GUISingleLineListBox* resolutions;
//GUI events are handled here.
//name: The name of the element that invoked the event.
//obj: Pointer to the object that invoked the event.
//eventType: Integer containing the type of event.
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType);
public:
//Constructor.
Options();
//Destructor.
~Options();
//Method that will create the GUI for the options menu.
void createGUI();
//Inherited from GameState.
void handleEvents();
void logic();
void render();
void resize();
};
//A very simple structure for resolutions
struct _res{
int w,h;
};
//The Credits menu.
class Credits : public GameState, private GUIEventCallback{
private:
//The title of the credits menu.
SDL_Surface* title;
SDL_Surface* creditsText;
//The scrollbars
GUIScrollBar* scrollbarH;
GUIScrollBar* scrollbarV;
//GUI events are handled here.
//name: The name of the element that invoked the event.
//obj: Pointer to the object that invoked the event.
//eventType: Integer containing the type of event.
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType);
public:
//Constructor.
Credits();
//Destructor.
~Credits();
//Method that will create the GUI for the options menu.
void createGUI();
//Inherited from GameState.
void handleEvents();
void logic();
void render();
void resize();
};
#endif

File Metadata

Mime Type
text/x-diff
Expires
Sun, May 17, 10:03 AM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
64232
Default Alt Text
(158 KB)

Event Timeline