Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F119212
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
158 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Sun, May 17, 10:03 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
64232
Default Alt Text
(158 KB)
Attached To
Mode
R79 meandmyshadow
Attached
Detach File
Event Timeline