Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
100 KB
Referenced Files
None
Subscribers
None
diff --git a/data/locale/zh_CN.po b/data/locale/zh_CN.po
index b1f3bf8..9bb1e85 100644
--- a/data/locale/zh_CN.po
+++ b/data/locale/zh_CN.po
@@ -1,1333 +1,1333 @@
# 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-08-19 16:33+0800\n"
-"PO-Revision-Date: 2012-08-19 16:36+0800\n"
+"PO-Revision-Date: 2012-08-19 16:57+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"
#: F:\Projects\meandmyshadow\src/Addons.cpp:46
msgid "Addons"
msgstr "附加组件"
#: F:\Projects\meandmyshadow\src/Addons.cpp:64
msgid "Unable to initialize addon menu:"
msgstr "不能初始化附加组件菜单:"
#: F:\Projects\meandmyshadow\src/Addons.cpp:72
#: F:\Projects\meandmyshadow\src/Addons.cpp:121
msgid "Back"
msgstr "后退"
#: F:\Projects\meandmyshadow\src/Addons.cpp:103
msgid "Levels"
msgstr "关卡列表"
#: F:\Projects\meandmyshadow\src/Addons.cpp:104
msgid "Level Packs"
msgstr "关卡包"
#: F:\Projects\meandmyshadow\src/Addons.cpp:105
msgid "Themes"
msgstr "主题"
#: F:\Projects\meandmyshadow\src/Addons.cpp:125
#: F:\Projects\meandmyshadow\src/Addons.cpp:606
msgid "Install"
msgstr "安装"
#: F:\Projects\meandmyshadow\src/Addons.cpp:129
msgid "Update"
msgstr "升级"
#: F:\Projects\meandmyshadow\src/Addons.cpp:140
msgid "ERROR: unable to download addons file!"
msgstr "错误:无法下载附加组件!"
#: F:\Projects\meandmyshadow\src/Addons.cpp:152
msgid "ERROR: unable to load addon_list file!"
msgstr "错误:无法加载文件addon_list!"
#: F:\Projects\meandmyshadow\src/Addons.cpp:163
msgid "ERROR: Invalid file format of addons file!"
msgstr "错误:文件addons的格式无效!"
#: F:\Projects\meandmyshadow\src/Addons.cpp:184
msgid "ERROR: Unable to create the installed_addons file."
msgstr "错误:无法创建文件installed_addons。"
#: F:\Projects\meandmyshadow\src/Addons.cpp:196
msgid "ERROR: Invalid file format of the installed_addons!"
msgstr "错误:文件installed_addons的格式无效!"
#: F:\Projects\meandmyshadow\src/Addons.cpp:411
#: F:\Projects\meandmyshadow\src/Addons.cpp:428
#: F:\Projects\meandmyshadow\src/Addons.cpp:445
#: F:\Projects\meandmyshadow\src/Addons.cpp:472
#: F:\Projects\meandmyshadow\src/Addons.cpp:489
#: F:\Projects\meandmyshadow\src/Addons.cpp:503
msgid "ERROR: Unable to download addon!"
msgstr "错误:无法下载附加组件"
#: F:\Projects\meandmyshadow\src/Addons.cpp:411
#: F:\Projects\meandmyshadow\src/Addons.cpp:428
#: F:\Projects\meandmyshadow\src/Addons.cpp:445
#: F:\Projects\meandmyshadow\src/Addons.cpp:472
#: F:\Projects\meandmyshadow\src/Addons.cpp:489
#: F:\Projects\meandmyshadow\src/Addons.cpp:503
msgid "ERROR:"
msgstr "错误:"
#: F:\Projects\meandmyshadow\src/Addons.cpp:601
msgid "Uninstall"
msgstr "卸载"
#: 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:605
#: F:\Projects\meandmyshadow\src/Functions.cpp:606
#: F:\Projects\meandmyshadow\src/Functions.cpp:607
#: F:\Projects\meandmyshadow\src/Functions.cpp:623
msgid "knewave"
msgstr "DroidSansFallback"
#: F:\Projects\meandmyshadow\src/Functions.cpp:611
msgid "Blokletters-Viltstift"
msgstr "DroidSansFallback"
#: F:\Projects\meandmyshadow\src/Functions.cpp:684
msgid "Loading..."
msgstr "正在读取..."
#: F:\Projects\meandmyshadow\src/Functions.cpp:1215
#: F:\Projects\meandmyshadow\src/Functions.cpp:1242
#: F:\Projects\meandmyshadow\src/Functions.cpp:1580
#: F:\Projects\meandmyshadow\src/InputManager.cpp:233
msgid "OK"
msgstr "确定"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1216
#: F:\Projects\meandmyshadow\src/Functions.cpp:1228
#: F:\Projects\meandmyshadow\src/Functions.cpp:1238
#: F:\Projects\meandmyshadow\src/Functions.cpp:1584
msgid "Cancel"
msgstr "取消"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1220
msgid "Abort"
msgstr "终止"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1221
#: F:\Projects\meandmyshadow\src/Functions.cpp:1237
msgid "Retry"
msgstr "重试"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1222
msgid "Ignore"
msgstr "忽略"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1226
#: F:\Projects\meandmyshadow\src/Functions.cpp:1232
msgid "Yes"
msgstr "是"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1227
#: F:\Projects\meandmyshadow\src/Functions.cpp:1233
msgid "No"
msgstr "否"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1351
#, c-format
msgid ""
"%s already exists.\n"
"Do you want to overwrite it?"
msgstr ""
"%s 已经存在。\n"
"你是否想要覆盖它?"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1351
msgid "Overwrite Prompt"
msgstr "文件覆盖提示"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1372
#: F:\Projects\meandmyshadow\src/Functions.cpp:1390
#, c-format
msgid "Can't open file %s."
msgstr "不能打开文件 %s。"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1372
#: F:\Projects\meandmyshadow\src/Functions.cpp:1390
msgid "Error"
msgstr "错误"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1521
msgid "Save File"
msgstr "保存文件"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1521
msgid "Load File"
msgstr "打开文件"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1525
msgid "Search In"
msgstr "查找范围"
#: F:\Projects\meandmyshadow\src/Functions.cpp:1535
msgid "File Name"
msgstr "文件名"
#: F:\Projects\meandmyshadow\src/Game.cpp:246
#: F:\Projects\meandmyshadow\src/Game.cpp:915
#, c-format
msgid "Level %d %s"
msgstr "第 %d 关 %s"
#: F:\Projects\meandmyshadow\src/Game.cpp:735
#, c-format
msgid "Press %s key to save the game."
msgstr "按 %s 键来保存游戏。"
#: F:\Projects\meandmyshadow\src/Game.cpp:740
#, c-format
msgid "Press %s key to swap the position of player and shadow."
msgstr "按 %s 键交换你和阴影的位置。"
#: F:\Projects\meandmyshadow\src/Game.cpp:745
#, c-format
msgid "Press %s key to activate the switch."
msgstr "按 %s 键来激活开关。"
#: F:\Projects\meandmyshadow\src/Game.cpp:750
#, c-format
msgid "Press %s key to teleport."
msgstr "按 %s 键传送。"
#: F:\Projects\meandmyshadow\src/Game.cpp:791
#, c-format
msgid "Press %s to restart current level or press %s to load the game."
msgstr "按 %s 键重新开始游戏,或者按 %s 键读取进度。"
#: F:\Projects\meandmyshadow\src/Game.cpp:803
#, c-format
msgid "Press %s to restart current level."
msgstr "按 %s 键重新开始游戏。"
#: F:\Projects\meandmyshadow\src/Game.cpp:817
msgid "Your shadow has died."
msgstr "你的阴影死掉了。"
#: F:\Projects\meandmyshadow\src/Game.cpp:868
#, c-format
msgid "%d recordings"
msgstr "记录数 %d"
#: F:\Projects\meandmyshadow\src/Game.cpp:907
msgid "You've finished:"
msgstr "恭喜你完成了:"
#: F:\Projects\meandmyshadow\src/Game.cpp:1093
#, c-format
msgid "Time: %-.2fs"
msgstr "时间: %-.2f秒"
#: F:\Projects\meandmyshadow\src/Game.cpp:1102
#, c-format
msgid "Best time: %-.2fs"
msgstr "最佳时间: %-.2f秒"
#: F:\Projects\meandmyshadow\src/Game.cpp:1113
#, c-format
msgid "Target time: %-.2fs"
msgstr "目标时间: %-.2f秒"
#: F:\Projects\meandmyshadow\src/Game.cpp:1134
#, c-format
msgid "Recordings: %d"
msgstr "记录次数: %d"
#: F:\Projects\meandmyshadow\src/Game.cpp:1142
#, c-format
msgid "Best recordings: %d"
msgstr "最佳记录次数: %d"
#: F:\Projects\meandmyshadow\src/Game.cpp:1152
#, c-format
msgid "Target recordings: %d"
msgstr "目标记录次数: %d"
#: F:\Projects\meandmyshadow\src/Game.cpp:1165
#, c-format
msgid "You earned the %s medal"
msgstr "你拿到了%s"
#: F:\Projects\meandmyshadow\src/Game.cpp:1165
msgid "GOLD"
msgstr "金牌"
#: F:\Projects\meandmyshadow\src/Game.cpp:1165
msgid "SILVER"
msgstr "银牌"
#: F:\Projects\meandmyshadow\src/Game.cpp:1165
msgid "BRONZE"
msgstr "铜牌"
#: F:\Projects\meandmyshadow\src/Game.cpp:1180
msgid "Menu"
msgstr "菜单"
#: F:\Projects\meandmyshadow\src/Game.cpp:1187
#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Restart"
msgstr "重新开始"
#: F:\Projects\meandmyshadow\src/Game.cpp:1194
msgid "Next"
msgstr "下一关"
#: F:\Projects\meandmyshadow\src/Game.cpp:1249
msgid "Game replay is done."
msgstr "游戏重放已经完成。"
#: F:\Projects\meandmyshadow\src/Game.cpp:1249
msgid "Game Replay"
msgstr "游戏重放"
#: F:\Projects\meandmyshadow\src/Game.cpp:1420
#: F:\Projects\meandmyshadow\src/Game.cpp:1422
msgid "Congratulations"
msgstr "恭喜你"
#: F:\Projects\meandmyshadow\src/Game.cpp:1422
msgid "You have finished the levelpack!"
msgstr "你已经完成了整个关卡包!"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Up (in menu)"
msgstr "上(在菜单中)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Down (in menu)"
msgstr "下(在菜单中)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Left"
msgstr "左"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Right"
msgstr "右"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Jump"
msgstr "跳跃"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Action"
msgstr "动作键"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Space (Record)"
msgstr "空格(记录键)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:43
msgid "Cancel recording"
msgstr "取消记录"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Escape"
msgstr "退出"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Tab (View shadow/Level prop.)"
msgstr "Tab (切换视角/关卡属性)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Save game (in editor)"
msgstr "保存游戏(在地图编辑器中)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Load game"
msgstr "读取游戏"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:44
msgid "Swap (in editor)"
msgstr "交换玩家和阴影的位置(在地图编辑器中)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:45
msgid "Teleport (in editor)"
msgstr "传送(在地图编辑器中)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:45
msgid "Suicide (in editor)"
msgstr "自杀(在地图编辑器中)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:45
msgid "Shift (in editor)"
msgstr "Shift (地图编辑器辅助按键)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:45
msgid "Next block type (in Editor)"
msgstr "下一个砖块类型(在地图编辑器中)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:46
msgid "Previous block type (in editor)"
msgstr "上一个砖块类型(在地图编辑器中)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:46
msgid "Select (in menu)"
msgstr "选择键(在菜单中)"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:204
msgid "Config Keys"
msgstr "设置按键"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:207
msgid "Select an item and press a key to config it."
msgstr "选择一个项目,然后按键进行设置。"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:221
msgid "Primary key"
msgstr "主按键"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:222
msgid "Alternative key"
msgstr "辅助按键"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:228
msgid "Unset the key"
msgstr "取消设置"
#: F:\Projects\meandmyshadow\src/InputManager.cpp:298
msgid "(Not set)"
msgstr "(未设置)"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:63
msgid "Block"
msgstr "砖块"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:63
msgid "Player Start"
msgstr "玩家起点"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:63
msgid "Shadow Start"
msgstr "阴影起点"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:64
msgid "Exit"
msgstr "终点"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:64
msgid "Shadow Block"
msgstr "阴影砖块"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:64
msgid "Spikes"
msgstr "带刺砖块"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:65
msgid "Checkpoint"
msgstr "记录点"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:65
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:293
msgid "Swap"
msgstr "交换点"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:65
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2468
msgid "Fragile"
msgstr "易碎砖块"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:66
msgid "Moving Block"
msgstr "移动砖块"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:66
msgid "Moving Shadow Block"
msgstr "移动阴影砖块"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:66
msgid "Moving Spikes"
msgstr "移动的刺"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:67
msgid "Teleporter"
msgstr "传送点"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:67
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2387
msgid "Button"
msgstr "按钮"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:67
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2389
msgid "Switch"
msgstr "开关"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:68
msgid "Conveyor Belt"
msgstr "传送带"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:68
msgid "Shadow Conveyor Belt"
msgstr "阴影传送带"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:68
msgid "Notification Block"
msgstr "消息方块"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:68
msgid "Collectable"
msgstr "可收集的物品"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:154
msgid "Toolbox"
msgstr "工具箱"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:506
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3126
msgid "Select"
msgstr "选择"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:521
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3135
msgid "Configure"
msgstr "设置"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:536
msgid "Link"
msgstr "链接"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:551
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3132
msgid "Delete"
msgstr "删除"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:946
msgid "Are you sure you want to quit?"
msgstr "你确定要退出吗?"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:946
msgid "Quit prompt"
msgstr "退出提示"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1045
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1047
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1505
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1507
#, c-format
msgid "Level \"%s\" saved"
msgstr "关卡“%s”已保存"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1045
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1047
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1505
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1507
msgid "Saved"
msgstr "已保存"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1520
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3141
msgid "Level settings"
msgstr "关卡设置"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1524
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:205
msgid "Name:"
msgstr "名称:"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1530
msgid "Theme:"
msgstr "主题:"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1545
msgid "Target time (s):"
msgstr "目标时间(秒):"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:1556
msgid "Target recordings:"
msgstr "目标记录数:"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2117
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2307
msgid "Defined"
msgstr "已设置"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2120
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2310
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2381
msgid "None"
msgstr "无"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2127
msgid "Moving block"
msgstr "移动砖块"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2130
msgid "Moving shadow block"
msgstr "移动阴影砖块"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2133
msgid "Moving spikes"
msgstr "移动的刺"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2140
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2259
msgid "Enabled"
msgstr "启用"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2146
msgid "Loop"
msgstr "循环"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2152
msgid "Path"
msgstr "路径"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2203
msgid "Notification block"
msgstr "消息方块"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2206
msgid "Enter message here:"
msgstr "输入消息:"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2251
msgid "Shadow Conveyor belt"
msgstr "阴影传送带"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2253
msgid "Conveyor belt"
msgstr "传送带"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2265
msgid "Enter speed here:"
msgstr "输入速度:"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2314
msgid "Portal"
msgstr "传送门"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2317
msgid "Activate on touch"
msgstr "接触时传送"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2323
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2417
msgid "Targets:"
msgstr "目标:"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2378
#, c-format
msgid "%d Defined"
msgstr "已设置 %d 个"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2394
msgid "Behaviour:"
msgstr "行为:"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2402
msgid "Toggle"
msgstr "切换"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2471
msgid "State:"
msgstr "状态:"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2477
msgid "Complete"
msgstr "完整的"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2478
msgid "One step"
msgstr "踩了一次"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2479
msgid "Two steps"
msgstr "踩了两次"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:2480
msgid "Gone"
msgstr "已经破碎"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3129
msgid "Add"
msgstr "添加"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3138
#: F:\Projects\meandmyshadow\src/LevelPlaySelect.cpp:69
msgid "Play"
msgstr "开始游戏"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3144
msgid "Save level"
msgstr "保存关卡"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3147
msgid "Back to menu"
msgstr "回主菜单"
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3196
#: F:\Projects\meandmyshadow\src/LevelEditor.cpp:3202
#, c-format
msgid "Movespeed: %s"
msgstr "移动速度: %s"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:46
msgid "Map Editor"
msgstr "地图编辑器"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:83
msgid "New Levelpack"
msgstr "新建关卡包"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:88
msgid "Pack Properties"
msgstr "关卡包属性"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:93
msgid "Remove Pack"
msgstr "删除关卡包"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:98
msgid "Move Map"
msgstr "移动地图"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:106
msgid "Remove Map"
msgstr "删除地图"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:111
msgid "Edit Map"
msgstr "编辑地图"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:202
msgid "Properties"
msgstr "属性"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:214
msgid "Description:"
msgstr "描述:"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:223
msgid "Congratulation text:"
msgstr "完成提示:"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:251
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:404
msgid "Add level"
msgstr "增加关卡"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:254
msgid "File name:"
msgstr "文件名:"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:278
msgid "Move level"
msgstr "移动关卡"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:281
msgid "Level: "
msgstr "关卡:"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:291
msgid "Before"
msgstr "之前"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:292
msgid "After"
msgstr "之后"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:467
msgid "Are you sure?"
msgstr "你确定吗?"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:467
msgid "Remove prompt"
msgstr "删除提示"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:612
msgid "No file name given for the new level."
msgstr "没有给新关卡指定文件名。"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:612
msgid "Missing file name"
msgstr "文件名未指定"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:693
msgid "The entered level number isn't valid!"
msgstr "输入的关卡编号无效!"
#: F:\Projects\meandmyshadow\src/LevelEditSelect.cpp:693
msgid "Illegal number"
msgstr "无效编号"
#: F:\Projects\meandmyshadow\src/LevelPlaySelect.cpp:47
msgid "Select Level"
msgstr "选择关卡"
#: F:\Projects\meandmyshadow\src/LevelPlaySelect.cpp:96
msgid "Choose a level"
msgstr "选择一个关卡"
#: F:\Projects\meandmyshadow\src/LevelPlaySelect.cpp:97
msgid "Time:"
msgstr "时间:"
#: F:\Projects\meandmyshadow\src/LevelPlaySelect.cpp:98
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:178
msgid "Recordings:"
msgstr "记录次数:"
#: F:\Projects\meandmyshadow\src/StatisticsManager.cpp:359
msgid "New achievement:"
msgstr "新成就:"
#: F:\Projects\meandmyshadow\src/StatisticsManager.cpp:367
#, c-format
msgid "Achieved at %s"
msgstr "完成于 %s"
#: F:\Projects\meandmyshadow\src/StatisticsManager.cpp:373
msgid "Unknown achievement"
msgstr "未知成就"
#: F:\Projects\meandmyshadow\src/StatisticsManager.cpp:379
#, c-format
msgid "Achieved %0.1f%%"
msgstr "已完成 %0.1f%%"
#: F:\Projects\meandmyshadow\src/StatisticsManager.cpp:383
msgid "Not achieved"
msgstr "未完成"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:135
msgid "Achievements and Statistics"
msgstr "成就与统计信息"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:159
msgid "Total"
msgstr "总计"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:168
msgid "Traveling distance (m)"
msgstr "旅行距离(米)"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:169
msgid "Jump times"
msgstr "跳跃次数"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:170
msgid "Die times"
msgstr "死亡次数"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:171
msgid "Squashed times"
-msgstr "被夹次数:"
+msgstr "被夹次数"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:178
msgid "Switch pulled times:"
msgstr "使用开关次数:"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:179
msgid "Swap times:"
msgstr "交换次数:"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:186
msgid "Completed levels:"
msgstr "完成关卡数:"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:228
msgid "In-game time:"
msgstr "游戏用时:"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:241
msgid "Level editing time:"
msgstr "地图编辑用时:"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:253
msgid "Created levels:"
msgstr "创建的关卡:"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:341
msgid "Achievements"
msgstr "成就"
#: F:\Projects\meandmyshadow\src/StatisticsScreen.cpp:342
msgid "Statistics"
msgstr "统计信息"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:50
msgid "Options"
msgstr "选项"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:53
msgid "Quit"
msgstr "退出"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:129
msgid "Enable internet in order to install addons."
msgstr "启用网络访问,才能下载附加组件。"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:129
msgid "Internet disabled"
msgstr "网络已禁用"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:206
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:710
msgid "Credits"
msgstr "作者"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:237
msgid "Settings"
msgstr "选项"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:281
msgid "Music"
msgstr "音乐"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:289
msgid "Sound"
msgstr "音效"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:297
msgid "Fullscreen"
msgstr "全屏幕"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:302
msgid "Resolution"
msgstr "分辨率"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:384
msgid "Language"
msgstr "语言"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:393
msgid "Auto-Detect"
msgstr "自动检测"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:421
msgid "Theme"
msgstr "主题"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:454
msgid "Level themes"
msgstr "默认主题"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:459
msgid "Internet"
msgstr "网络"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:465
msgid "Internet proxy"
msgstr "网络代理"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:482
msgid "Clear Progress"
msgstr "清除进度"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:501
msgid "Save Changes"
msgstr "保存变更"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:611
msgid "Do you really want to reset level progress?"
msgstr "你确定要清除游戏进度吗?"
#: F:\Projects\meandmyshadow\src/TitleMenu.cpp:611
msgid "Warning"
msgstr "警告"
#: F:\Projects\meandmyshadow\src/AchievementList.h:24
msgid "Newbie"
msgstr "新手"
#: F:\Projects\meandmyshadow\src/AchievementList.h:24
msgid "Congratulations, you completed one level!"
msgstr "恭喜你成功完成了一个关卡!"
#: F:\Projects\meandmyshadow\src/AchievementList.h:25
msgid "Experienced player"
msgstr "有经验的玩家"
#: F:\Projects\meandmyshadow\src/AchievementList.h:25
msgid "Completed 50 levels."
msgstr "完成50个关卡。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:26
msgid "Good job!"
msgstr "干得好!"
#: F:\Projects\meandmyshadow\src/AchievementList.h:26
msgid "Get your first gold medal."
msgstr "拿到你的第一个金牌。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:27
msgid "Expert"
msgstr "专家"
#: F:\Projects\meandmyshadow\src/AchievementList.h:27
msgid "Earned 50 gold medal."
msgstr "拿到50个金牌。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:29
msgid "Graduate"
msgstr "毕业生"
#: F:\Projects\meandmyshadow\src/AchievementList.h:29
msgid "Complete the tutorial level pack."
msgstr "完成教程关卡包。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:30
msgid "Outstanding graduate"
msgstr "优秀毕业生"
#: F:\Projects\meandmyshadow\src/AchievementList.h:30
msgid "Complete the tutorial level pack with all levels gold medal."
msgstr "以全部金牌的成绩完成教学关卡包。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:32
msgid "Addicted"
msgstr "入迷"
#: F:\Projects\meandmyshadow\src/AchievementList.h:32
msgid "Played Me and My Shadow for more than 2 hours."
msgstr "玩Me and My Shadow超过2小时。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:33
msgid "Me and My Shadow loyal fan"
msgstr "Me and My Shadow忠实粉丝"
#: F:\Projects\meandmyshadow\src/AchievementList.h:33
msgid "Played Me and My Shadow for more than 24 hours."
msgstr "玩Me and My Shadow超过24小时。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:35
msgid "Constructor"
msgstr "建造师"
#: F:\Projects\meandmyshadow\src/AchievementList.h:35
msgid "Use the level editor for more than 2 hours."
msgstr "使用地图编辑器超过2小时。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:36
msgid "The creator"
msgstr "造物主"
#: F:\Projects\meandmyshadow\src/AchievementList.h:36
msgid "Use the level editor for more than 24 hours."
msgstr "使用地图编辑器超过24小时。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:38
msgid "Look, cute level!"
msgstr "看,可爱的关卡!"
#: F:\Projects\meandmyshadow\src/AchievementList.h:38
msgid "Created your first level."
msgstr "创建你的第一个关卡。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:39
msgid "The level museum"
msgstr "关卡博物馆"
#: F:\Projects\meandmyshadow\src/AchievementList.h:39
msgid "Created 50 levels."
msgstr "创建了50个关卡。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:41
msgid "Frog"
msgstr "青蛙"
#: F:\Projects\meandmyshadow\src/AchievementList.h:41
msgid "Jump for 1000 times."
msgstr "跳跃了1000次。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:43
msgid "Be careful!"
msgstr "小心!"
#: F:\Projects\meandmyshadow\src/AchievementList.h:43
msgid "The first death."
msgstr "第一次挂掉。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:44
msgid "It doesn't matter..."
msgstr "没关系……"
#: F:\Projects\meandmyshadow\src/AchievementList.h:44
msgid "Died for 50 times."
msgstr "挂掉50次。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:45
msgid "Expert of trial and error"
msgstr "试错专家"
#: F:\Projects\meandmyshadow\src/AchievementList.h:45
msgid "Died for 1000 times."
msgstr "挂掉1000次。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:47
msgid "Keep an eye for moving walls!"
msgstr "小心移动的墙!"
#: F:\Projects\meandmyshadow\src/AchievementList.h:47
msgid "First time being squashed."
msgstr "第一次被夹死。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:48
msgid "Potato masher"
msgstr "马铃薯捣碎器"
#: F:\Projects\meandmyshadow\src/AchievementList.h:48
msgid "Squashed for 50 times."
msgstr "被夹死50次。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:50
msgid "Double kill"
msgstr "双重击杀"
#: F:\Projects\meandmyshadow\src/AchievementList.h:50
msgid "Make both player and shadow die."
msgstr "让玩家和阴影都挂掉。"
#: F:\Projects\meandmyshadow\src/AchievementList.h:52
msgid "Programmer"
msgstr "程序员"
#: F:\Projects\meandmyshadow\src/AchievementList.h:52
msgid "Played the development version of Me and My Shadow."
msgstr "玩过Me and My Shadow的开发版本。"
# 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 ""
#~ "You don't have any achievements now. Play the game and try to earn some!"
#~ msgstr "你目前没有获得任何成就。试着通过玩游戏获取一些!"
#, fuzzy
#~ msgid "Record times:"
#~ msgstr "记录次数:"
diff --git a/src/AchievementList.h b/src/AchievementList.h
index f07f1af..3561d11 100644
--- a/src/AchievementList.h
+++ b/src/AchievementList.h
@@ -1,56 +1,61 @@
/*
* 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/>.
*/
// Note: This is an internal file for all avaliable achievements.
// Don't include it in other files!
AchievementInfo achievementList[]={
{"newbie",__("Newbie"),"themes/Cloudscape/player.png",{0,0,23,40},__("Congratulations, you completed one level!"),ACHIEVEMT_TITLE},
{"experienced",__("Experienced player"),"themes/Cloudscape/player.png",{0,0,23,40},__("Completed 50 levels."),ACHIEVEMT_PROGRESS},
{"goodjob",__("Good job!"),"gfx/medals.png",{60,0,30,30},__("Get your first gold medal."),ACHIEVEMT_ALL},
{"expert",__("Expert"),"gfx/medals.png",{60,0,30,30},__("Earned 50 gold medal."),ACHIEVEMT_PROGRESS},
{"tutorial",__("Graduate"),"gfx/medals.png",{60,0,30,30},__("Complete the tutorial level pack."),ACHIEVEMT_PROGRESS},
{"tutorialGold",__("Outstanding graduate"),"gfx/medals.png",{60,0,30,30},__("Complete the tutorial level pack with all levels gold medal."),ACHIEVEMT_PROGRESS},
{"addicted",__("Addicted"),"themes/Cloudscape/player.png",{0,0,23,40},__("Played Me and My Shadow for more than 2 hours.")},
{"loyalFan",__("Me and My Shadow loyal fan"),"themes/Cloudscape/player.png",{0,0,23,40},__("Played Me and My Shadow for more than 24 hours.")},
{"constructor",__("Constructor"),"gfx/gui.png",{112,16,16,16},__("Use the level editor for more than 2 hours.")},
{"constructor2",__("The creator"),"gfx/gui.png",{112,16,16,16},__("Use the level editor for more than 24 hours.")},
{"create1",__("Look, cute level!"),"gfx/gui.png",{112,16,16,16},__("Created your first level."),ACHIEVEMT_ALL},
{"create50",__("The level museum"),"gfx/gui.png",{112,16,16,16},__("Created 50 levels."),ACHIEVEMT_PROGRESS},
{"frog",__("Frog"),"themes/Cloudscape/player.png",{0,0,23,40},__("Jump for 1000 times."),ACHIEVEMT_PROGRESS},
+ {"travel100",__("Wanderer"),"themes/Cloudscape/player.png",{0,0,23,40},__("Traveled for 100 meter."),ACHIEVEMT_PROGRESS},
+ {"travel1k",__("Runner"),"themes/Cloudscape/player.png",{0,0,23,40},__("Traveled for 1 kilometer."),ACHIEVEMT_PROGRESS},
+ {"travel10k",__("Long runner"),"themes/Cloudscape/player.png",{0,0,23,40},__("Traveled for 10 kilometer."),ACHIEVEMT_PROGRESS},
+ {"travel42k",__("Marathon runner"),"themes/Cloudscape/player.png",{0,0,23,40},__("Traveled for 42,195 meter."),ACHIEVEMT_PROGRESS},
+
{"die1",__("Be careful!"),"themes/Cloudscape/deathright.png",{0,14,23,40},__("The first death."),ACHIEVEMT_ALL},
{"die50",__("It doesn't matter..."),"themes/Cloudscape/deathright.png",{0,14,23,40},__("Died for 50 times.")},
{"die1000",__("Expert of trial and error"),"themes/Cloudscape/deathright.png",{0,14,23,40},__("Died for 1000 times.")},
{"squash1",__("Keep an eye for moving walls!"),"themes/Cloudscape/deathright.png",{0,14,23,40},__("First time being squashed.")},
{"suqash50",__("Potato masher"),"themes/Cloudscape/deathright.png",{0,14,23,40},__("Squashed for 50 times.")},
{"doubleKill",__("Double kill"),"themes/Cloudscape/deathright.png",{0,14,23,40},__("Make both player and shadow die.")},
{"programmer",__("Programmer"),"gfx/gui.png",{112,16,16,16},__("Played the development version of Me and My Shadow."),ACHIEVEMT_TITLE},
//end of achievements
{}
};
diff --git a/src/Player.cpp b/src/Player.cpp
index 411285d..5a731aa 100644
--- a/src/Player.cpp
+++ b/src/Player.cpp
@@ -1,1413 +1,1438 @@
/*
* 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 "Player.h"
#include "Game.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "Objects.h"
#include "InputManager.h"
#include "StatisticsManager.h"
#include "MD5.h"
#include <iostream>
#include <fstream>
#include <SDL/SDL.h>
#ifdef __APPLE__
#include <SDL_mixer/SDL_mixer.h>
#include <SDL_ttf/SDL_ttf.h>
#else
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_ttf.h>
#endif
using namespace std;
#ifdef RECORD_FILE_DEBUG
string recordKeyPressLog,recordKeyPressLog_saved;
vector<SDL_Rect> recordPlayerPosition,recordPlayerPosition_saved;
#endif
Player::Player(Game* objParent):xVelBase(0),yVelBase(0),objParent(objParent),recordSaved(false),
inAirSaved(false),isJumpSaved(false),onGroundSaved(false),canMoveSaved(false),holdingOtherSaved(false){
//Set the dimensions of the player.
//The size of the player is 21x40.
box.x=0;
box.y=0;
box.w=21;
box.h=40;
//Set his velocity to zero.
xVel=0;
yVel=0;
//Set the start position.
fx=0;
fy=0;
//Set some default values.
inAir=true;
isJump=false;
onGround=true;
shadowCall=false;
shadow=false;
canMove=true;
holdingOther=false;
dead=false;
record=false;
downKeyPressed=false;
spaceKeyPressed=false;
recordIndex=-1;
#ifdef RECORD_FILE_DEBUG
recordKeyPressLog.clear();
recordKeyPressLog_saved.clear();
recordPlayerPosition.clear();
recordPlayerPosition_saved.clear();
#endif
objNotificationBlock=NULL;
//Some default values for animation variables.
direction=0;
jumpTime=0;
state=stateSaved=0;
//xVelSaved is used to store if there's a state saved or not.
xVelSaved=yVelSaved=0x80000000;
objCurrentStand=objLastStand=objLastTeleport=objShadowBlock=NULL;
}
Player::~Player(){
//Do nothing here
}
bool Player::isPlayFromRecord(){
return recordIndex>=0; // && recordIndex<(int)recordButton.size();
}
//get the game record object.
std::vector<int>* Player::getRecord(){
return &recordButton;
}
#ifdef RECORD_FILE_DEBUG
string& Player::keyPressLog(){
return recordKeyPressLog;
}
vector<SDL_Rect>& Player::playerPosition(){
return recordPlayerPosition;
}
#endif
//play the record.
void Player::playRecord(){
//TODO:
recordIndex=0;
}
void Player::spaceKeyDown(class Shadow* shadow){
//Start recording or stop, depending on the recording state.
if(record==false){
//We start recording.
if(shadow->called==true){
//The shadow is still busy so first stop him before we can start recording.
shadowCall=false;
shadow->called=false;
shadow->playerButton.clear();
}else if(!dead){
//The shadow isn't moving and we aren't dead so start recording.
record=true;
//We start a recording meaning we need to increase recordings by one.
objParent->recordings++;
}
}else{
//The player is recording so stop recording and call the shadow.
record=false;
shadowCall=true;
}
}
void Player::handleInput(class Shadow* shadow){
//Check if we should read the input from record file.
//Actually, we read input from record file in
//another function shadowSetState.
bool readFromRecord=false;
if(recordIndex>=0 && recordIndex<(int)recordButton.size()) readFromRecord=true;
if(!readFromRecord){
//Reset horizontal velocity.
xVel=0;
if(inputMgr.isKeyDown(INPUTMGR_RIGHT)){
//Walking to the right.
xVel+=7;
}
if(inputMgr.isKeyDown(INPUTMGR_LEFT)){
//Walking to the left.
xVel-=7;
}
//Check if a key has been released.
if(/*event.type==SDL_KEYUP || */!inputMgr.isKeyDown(INPUTMGR_ACTION)){
//It has so downKeyPressed can't be true.
downKeyPressed=false;
}
/*
//Don't reset spaceKeyPressed or when you press the space key
//and release another key then the bug occurs. (ticket #44)
if(event.type==SDL_KEYUP || !inputMgr.isKeyDown(INPUTMGR_SPACE)){
spaceKeyPressed=false;
}*/
}
//Check if a key is pressed (down).
if(inputMgr.isKeyDownEvent(INPUTMGR_JUMP) && !readFromRecord){
//The up key, if we aren't in the air we start jumping.
//Fixed a potential bug
if(!inAir && !isJump){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Jump key pressed\n",objParent->time);
cout<<c;
recordKeyPressLog+=c;
#endif
isJump=true;
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_SPACE) && !readFromRecord){
//Fixed a potential bug
if(!spaceKeyPressed){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Space key pressed\n",objParent->time);
cout<<c;
recordKeyPressLog+=c;
#endif
spaceKeyDown(shadow);
spaceKeyPressed=true;
}
}else if(record && !readFromRecord && inputMgr.isKeyDownEvent(INPUTMGR_CANCELRECORDING)){
//Cancel current recording
//Search the recorded button and clear the last space key press
int i=recordButton.size()-1;
for(;i>=0;i--){
if(recordButton[i] & PlayerButtonSpace){
recordButton[i] &= ~PlayerButtonSpace;
break;
}
}
if(i>=0){
//Clear the recording at the player's side.
playerButton.clear();
line.clear();
//reset the record flag
record=false;
//decrese the record count
objParent->recordings--;
}else{
cout<<"Failed to find last recording"<<endl;
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_ACTION)){
//Downkey is pressed.
//Fixed a potential bug
if(!downKeyPressed){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Action key pressed\n",objParent->time);
cout<<c;
recordKeyPressLog+=c;
#endif
downKeyPressed=true;
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_SAVE)){
//F2 only works in the level editor.
if(!(dead || shadow->dead) && stateID==STATE_LEVEL_EDITOR){
//Save the state. (delayed)
if(objParent)
objParent->saveStateNextTime=true;
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_LOAD) && !readFromRecord){
//F3 is used to load the last state.
if(objParent)
objParent->loadStateNextTime=true;
}else if(inputMgr.isKeyDownEvent(INPUTMGR_SWAP)){
//F4 will swap the player and the shadow, but only in the level editor.
if(!(dead || shadow->dead) && stateID==STATE_LEVEL_EDITOR){
swapState(shadow);
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_TELEPORT)){
//F5 will revive and teleoprt the player to the cursor. Only works in the level editor.
//Shift+F5 teleports the shadow.
if(stateID==STATE_LEVEL_EDITOR){
//get the position of the cursor.
int x,y;
SDL_GetMouseState(&x,&y);
x+=camera.x;
y+=camera.y;
if(inputMgr.isKeyDown(INPUTMGR_SHIFT)){
//teleports the shadow.
shadow->dead=false;
shadow->box.x=x;
shadow->box.y=y;
}else{
//teleports the player.
dead=false;
box.x=x;
box.y=y;
}
//play sound?
if(getSettings()->getBoolValue("sound")){
Mix_PlayChannel(-1,swapSound,0);
}
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_SUICIDE)){
//F12 is suicide and only works in the leveleditor.
if(stateID==STATE_LEVEL_EDITOR){
die();
shadow->die();
}
}
}
void Player::setPosition(int x,int y){
box.x=x;
box.y=y;
}
void Player::move(vector<GameObject*> &levelObjects){
//Pointer to a checkpoint.
GameObject* objCheckPoint=NULL;
//Pointer to a swap.
GameObject* objSwap=NULL;
//Set the objShadowBlock to NULL.
//Only for swapping to prevent the shadow from swapping in a shadow block.
objShadowBlock=NULL;
//Set the objNotificationBlock to NULL.
objNotificationBlock=NULL;
//Set the object the player is currently standing to NULL.
objCurrentStand=NULL;
//Check if the player is still alive.
if(dead==false){
//Add gravity
if(inAir==true){
yVel+=1;
//Cap fall speed to 13.
if(yVel>13){
yVel=13;
}
}
//Boolean if the player is moved, used for squash detection.
bool playerMoved=false;
+ //Indicates player or shadow is traveling, we should add it to traveling distance
+ bool isTraveling=true;
//The current location of the player, used to set the player back if he's squashed to prevent displacement.
int lastX=box.x;
int lastY=box.y;
//Check if the player can move.
if(canMove==true){
//Check if the player is moving or not.
if(xVel>0){
direction=0;
onGround=false;
if(appearance.currentStateName!="walkright"){
appearance.changeState("walkright");
}
}else if(xVel<0){
direction=1;
onGround=false;
if(appearance.currentStateName!="walkleft"){
appearance.changeState("walkleft");
}
}else if(xVel==0){
onGround=true;
if(direction==1){
appearance.changeState("standleft");
}else{
appearance.changeState("standright");
}
}
//Move the player.
box.x+=xVel;
//Loop through the levelobjects.
for(unsigned int o=0; o<levelObjects.size(); o++){
//Check if the player can walk on the object.
if(levelObjects[o]->queryProperties(GameObjectProperty_PlayerCanWalkOn,this)){
//Get the collision box of the levelobject.
SDL_Rect r=levelObjects[o]->getBox();
//Check collision with the player.
if(checkCollision(box,r)){
//We have collision, get the velocity of the box.
SDL_Rect v=levelObjects[o]->getBox(BoxType_Delta);
//Check on which side of the box the player is.
if(box.x + box.w/2 <= r.x + r.w/2){
//The left side of the block.
if(xVel+xVelBase>v.x){
if(box.x>r.x-box.w){
box.x=r.x-box.w;
playerMoved=true;
}
}
}else{
//The right side of the block.
if(xVel+xVelBase<v.x){
if(box.x<r.x+r.w){
box.x=r.x+r.w;
playerMoved=true;
}
}
}
}
}
}
}
//Now apply the yVel. (gravity, jumping, etc..)
box.y+=yVel;
//Pointer to the object the player standed on.
GameObject* lastStand=NULL;
//???
inAir=true;
canMove=true;
//Boolean if the player can teleport.
bool canTeleport=true;
//Loop through all the levelObjects.
for(unsigned int o=0; o<levelObjects.size(); o++){
//Check if the object is solid.
if(levelObjects[o]->queryProperties(GameObjectProperty_PlayerCanWalkOn,this)){
SDL_Rect r=levelObjects[o]->getBox();
if(checkCollision(r,box)==true){ //TODO:fix some bug
SDL_Rect v=levelObjects[o]->getBox(BoxType_Delta);
if(box.y+box.h/2<=r.y+r.h/2){
if(yVel>=v.y || yVel>=0){
inAir=false;
box.y=r.y-box.h;
yVel=1; //???
lastStand=levelObjects[o];
lastStand->onEvent(GameObjectEvent_PlayerIsOn);
//The player is moved, if it's a moving block check for squating.
if(v.y!=0){
playerMoved=true;
}
}
}else{
//FIXME: The player can have a yVel of 0 and get squashed if he is standing on the other.
bool holding=objParent->shadow.holdingOther;
if(shadow)
holding=objParent->player.holdingOther;
if(yVel<=v.y+1 || holding){
yVel=v.y>0?v.y:0;
if(box.y<r.y+r.h){
if(!holding)
box.y=r.y+r.h;
//The player is moved, if it's a moving block check for squating.
if(v.y!=0){
playerMoved=true;
}
}
}
}
}
}
//Check if the object is a checkpoint.
if(levelObjects[o]->type==TYPE_CHECKPOINT && checkCollision(box,levelObjects[o]->getBox())){
//If we're not the shadow set the gameTip to Checkpoint.
if(!shadow && objParent!=NULL)
objParent->gameTipIndex=TYPE_CHECKPOINT;
//And let objCheckPoint point to this object.
objCheckPoint=levelObjects[o];
}
//Check if the object is a swap.
if(levelObjects[o]->type==TYPE_SWAP && checkCollision(box,levelObjects[o]->getBox())){
//If we're not the shadow set the gameTip to swap.
if(!shadow && objParent!=NULL)
objParent->gameTipIndex=TYPE_SWAP;
//And let objSwap point to this object.
objSwap=levelObjects[o];
}
//Check if the object is an exit.
//This doesn't work if the state is Level editor.
if(levelObjects[o]->type==TYPE_EXIT && stateID!=STATE_LEVEL_EDITOR && checkCollision(box,levelObjects[o]->getBox())){
//Check to see if we have enough keys to finish the level
if (objParent->currentCollectables>=objParent->totalCollectables){
//We can't just handle the winning here (in the middle of the update cycle)/
//So set won in Game true.
objParent->won=true;
}
}
//Check if the object is a portal.
if(levelObjects[o]->type==TYPE_PORTAL && checkCollision(box,levelObjects[o]->getBox())){
//Check if the teleport id isn't empty.
if((dynamic_cast<Block*>(levelObjects[o]))->id.empty()){
cerr<<"Warning: Invalid teleport id!"<<endl;
canTeleport=false;
}
//If we're not the shadow set the gameTip to portal.
if(!shadow && objParent!=NULL)
objParent->gameTipIndex=TYPE_PORTAL;
//Check if we can teleport and should (downkey -or- auto).
if(canTeleport && (downKeyPressed || (levelObjects[o]->queryProperties(GameObjectProperty_Flags,this)&1))){
canTeleport=false;
if(downKeyPressed || levelObjects[o]!=objLastTeleport){
downKeyPressed=false;
//Loop the levelobjects again to find the destination.
for(unsigned int oo=o+1;;){
//We started at our index+1.
//Meaning that if we reach the end of the vector then we need to start at the beginning.
if(oo>=levelObjects.size())
oo-=(int)levelObjects.size();
//It also means that if we reach the same index we need to stop.
//If the for loop breaks this way then we have no succes.
if(oo==o){
//Couldn't teleport so play the error sound.
if(getSettings()->getBoolValue("sound")){
Mix_PlayChannel(-1,errorSound,0);
}
break;
}
//Check if the second (oo) object is a portal.
if(levelObjects[oo]->type==TYPE_PORTAL){
//Check the id against the destination of the first portal.
if((dynamic_cast<Block*>(levelObjects[o]))->destination==(dynamic_cast<Block*>(levelObjects[oo]))->id){
//Call the event.
levelObjects[o]->onEvent(GameObjectEvent_OnToggle);
objLastTeleport=levelObjects[oo];
//Get the destination location and teleport the player.
SDL_Rect r=levelObjects[oo]->getBox();
box.x=r.x+5;
box.y=r.y+2;
+ //We don't count it to traveling distance.
+ isTraveling=false;
+
//Check if music/sound is enabled.
if(getSettings()->getBoolValue("sound")){
Mix_PlayChannel(-1,swapSound,0);
}
break;
}
}
//Increase oo.
oo++;
}
}
}
}
//Check if the object is a switch.
if(levelObjects[o]->type==TYPE_SWITCH && checkCollision(box,levelObjects[o]->getBox())){
//If we're not the shadow set the gameTip to switch.
if(!shadow && objParent!=NULL)
objParent->gameTipIndex=TYPE_SWITCH;
//If the down key is pressed then invoke an event.
if(downKeyPressed){
//Play the animation.
levelObjects[o]->playAnimation(1);
//Check if sound is enabled, if so play the toggle sound.
if(getSettings()->getBoolValue("sound")==true){
Mix_PlayChannel(-1,toggleSound,0);
}
if(objParent!=NULL){
//Make sure that the id isn't emtpy.
if(!(dynamic_cast<Block*>(levelObjects[o]))->id.empty()){
objParent->broadcastObjectEvent(0x10000 | (levelObjects[o]->queryProperties(GameObjectProperty_Flags,this)&3),
-1,(dynamic_cast<Block*>(levelObjects[o]))->id.c_str());
}else{
cerr<<"Warning: invalid switch id!"<<endl;
}
}
}
}
//Check if the object is a shadow block, only if we are the player.
if((levelObjects[o]->type==TYPE_SHADOW_BLOCK || levelObjects[o]->type==TYPE_MOVING_SHADOW_BLOCK) && checkCollision(box,levelObjects[o]->getBox()) && !shadow){
objShadowBlock=levelObjects[o];
}
//Check if the object is a notification block, only if we are the player.
if(levelObjects[o]->type==TYPE_NOTIFICATION_BLOCK && checkCollision(box,levelObjects[o]->getBox()) && !shadow){
objNotificationBlock=levelObjects[o];
}
//Check if the object is a collectable
if(levelObjects[o]->type==TYPE_COLLECTABLE && checkCollision(box,levelObjects[o]->getBox())){
//Check if collectable is active (if it's not it's equal to 1(inactive))
if (levelObjects[o]->queryProperties(GameObjectProperty_Flags, this)==0) {
//Toggle an event
levelObjects[o]->onEvent(GameObjectEvent_OnToggle);
//Increase the current number of collectables
objParent->currentCollectables++;
if(getSettings()->getBoolValue("sound"))
Mix_PlayChannel(-1,collectSound,0);
//Open exit(s)
if (objParent->currentCollectables>=objParent->totalCollectables){
for(unsigned int i=0;i<levelObjects.size();i++){
if(levelObjects[i]->type==TYPE_EXIT){
Block *obj=dynamic_cast<Block*>(levelObjects[i]);
if(obj!=NULL){
levelObjects[i]->onEvent(GameObjectEvent_OnSwitchOn);
}
}
}
}
}
}
//Check if the object is deadly.
if(levelObjects[o]->queryProperties(GameObjectProperty_IsSpikes,this)){
//It is so get the collision box.
SDL_Rect r=levelObjects[o]->getBox();
//TODO: pixel-accuracy hit test.
//For now we shrink the box.
r.x+=2;
r.y+=2;
r.w-=4;
r.h-=4;
//Check collision, if the player collides then let him die.
if(checkCollision(box,r)){
//Now make sure we don't collide with a different block.
for(unsigned int oo=o+1;;){
//We started at our index+1.
//Meaning that if we reach the end of the vector then we need to start at the beginning.
if(oo>=levelObjects.size())
oo-=(int)levelObjects.size();
//It also means that if we reach the same index we need to stop.
//If the for loop breaks this way then we have no succes.
if(oo==o){
//Nothing found so call the die method.
die();
break;
}
//Check if the second (oo) object is a block.
if(levelObjects[oo]->queryProperties(GameObjectProperty_PlayerCanWalkOn,this)){
//Get the collision box.
SDL_Rect r2=levelObjects[oo]->getBox();
if(checkCollision(box,r2)){
//Check if the top isn't covered.
if(r2.y>r.y){
//It isn't covered so create a box for collision detection
SDL_Rect tmp={r2.x,r2.y,r2.w,r2.y-r.y};
if(checkCollision(box,tmp)){
//We hit spikes so die?
die();
break;
}
}
//Check if the left side isn't covered.
if(r2.x>r.x){
//It isn't covered so create a box for collision detection
SDL_Rect tmp={r2.x,r2.y,r2.x-r.x,r2.h};
if(checkCollision(box,tmp)){
//We hit spikes so die?
die();
break;
}
}
//Check if the right side isn't covered.
if(r2.x+r2.w>r.x+r.w){
//It isn't covered so create a box for collision detection
SDL_Rect tmp={r.x+r.w,r2.y,(r2.x+r2.w)-(r.x+r.w),r2.h};
if(checkCollision(box,tmp)){
//We hit spikes so die?
die();
break;
}
}
//Check if the bottom isn't covered.
if(r2.y+r2.h>r.y+r.h){
//It isn't covered so create a box for collision detection
SDL_Rect tmp={r2.x,r.y+r.h,r2.w,(r2.y+r2.h)-(r.y+r.h)};
if(checkCollision(box,tmp)){
//We hit spikes so die?
die();
break;
}
}
//Check collision with the player and the block and with the block and the spikes.
if(checkCollision(box,r)){
//We break the loop to prevent going round (and calling the die() method).
break;
}
}
}
//Increase oo.
oo++;
}
}
}
}
//Check if the player was moved, if so check if the player is squashed.
if(playerMoved){
for(unsigned int o=0;o<levelObjects.size();o++){
SDL_Rect r2=levelObjects[o]->getBox();
if(levelObjects[o]->queryProperties(GameObjectProperty_PlayerCanWalkOn,this) && checkCollision(box,r2)){
//The player is squashed so first move him back.
box.x=lastX;
box.y=lastY;
//Update statistics.
if(!dead && !objParent->player.isPlayFromRecord() && !objParent->interlevel){
if(shadow) statsMgr.shadowSquashed++;
else statsMgr.playerSquashed++;
switch(statsMgr.playerSquashed+statsMgr.shadowSquashed){
case 1:
statsMgr.newAchievement("squash1");
break;
case 50:
statsMgr.newAchievement("squash50");
break;
}
}
//Now call the die method.
die();
}
}
}
//Check if the player fell of the level, if so let him die but without animation.
if(box.y>LEVEL_HEIGHT)
die(false);
//Check if the player changed blocks, meaning stepped onto a block.
objCurrentStand=lastStand;
if(lastStand!=objLastStand){
objLastStand=lastStand;
if(lastStand){
//Call the walk on event of the laststand.
lastStand->onEvent(GameObjectEvent_PlayerWalkOn);
//Bugfix for Fragile blocks.
if(lastStand->type==TYPE_FRAGILE && !lastStand->queryProperties(GameObjectProperty_PlayerCanWalkOn,this)){
inAir=true;
onGround=false;
isJump=false;
}
}
}
//Check if the player can teleport.
if(canTeleport)
objLastTeleport=NULL;
//Check the checkpoint pointer only if the downkey is pressed.
//new: don't save the game if playing game record
if(objParent!=NULL && downKeyPressed && objCheckPoint!=NULL && !isPlayFromRecord()){
//Checkpoint thus save the state.
if(objParent->canSaveState()){
objParent->saveStateNextTime=true;
objParent->objLastCheckPoint=objCheckPoint;
}
}
//Check the swap pointer only if the down key is pressed.
if(objSwap!=NULL && downKeyPressed && objParent!=NULL){
//Now check if the shadow we're the shadow or not.
if(shadow){
if(!(dead || objParent->player.dead)){
//Check if the player isn't in front of a shadow block.
if(!objParent->player.objShadowBlock){
objParent->player.swapState(this);
objSwap->playAnimation(1);
+ //We don't count it to traveling distance.
+ isTraveling=false;
}else{
//We can't swap so play the error sound.
if(getSettings()->getBoolValue("sound")==true){
Mix_PlayChannel(-1,errorSound,0);
}
}
}
}else{
if(!(dead || objParent->shadow.dead)){
//Check if the player isn't in front of a shadow block.
if(!objShadowBlock){
swapState(&objParent->shadow);
objSwap->playAnimation(1);
+ //We don't count it to traveling distance.
+ isTraveling=false;
}else{
//We can't swap so play the error sound.
if(getSettings()->getBoolValue("sound")==true){
Mix_PlayChannel(-1,errorSound,0);
}
}
}
}
}
//Check for jump appearance (inAir).
if(inAir && !dead){
if(direction==1){
if(yVel>0){
if(appearance.currentStateName!="fallleft")
appearance.changeState("fallleft");
}else{
if(appearance.currentStateName!="jumpleft")
appearance.changeState("jumpleft");
}
}else{
if(yVel>0){
if(appearance.currentStateName!="fallright")
appearance.changeState("fallright");
}else{
if(appearance.currentStateName!="jumpright")
appearance.changeState("jumpright");
}
}
}
+
+ //Update traveling distance statistics.
+ if(isTraveling && (lastX!=box.x || lastY!=box.y)){
+ float dx=float(lastX-box.x),dy=float(lastY-box.y);
+ float d0=statsMgr.playerTravelingDistance+statsMgr.shadowTravelingDistance,
+ d=sqrtf(dx*dx+dy*dy)/50.0f;
+ if(shadow) statsMgr.shadowTravelingDistance+=d;
+ else statsMgr.playerTravelingDistance+=d;
+
+ //Update achievement
+ d+=d0;
+ if(d0<=100.0f && d>=100.0f) statsMgr.newAchievement("travel100");
+ if(d0<=1000.0f && d>=1000.0f) statsMgr.newAchievement("travel1k");
+ if(d0<=10000.0f && d>=10000.0f) statsMgr.newAchievement("travel10k");
+ if(d0<=42195.0f && d>=42195.0f) statsMgr.newAchievement("travel42k");
+ }
}
//Finally we reset some stuff.
downKeyPressed=false;
xVelBase=0;
yVelBase=0;
}
void Player::jump(){
//Check if the player is dead or not.
if(dead==true){
//The player can't jump if he's dead.
isJump=false;
}
//Check if the player can jump.
if(isJump==true && inAir==false){
//Set the jump velocity.
yVel=-13;
inAir=true;
isJump=false;
jumpTime++;
//Update statistics
if(!objParent->player.isPlayFromRecord() && !objParent->interlevel){
if(shadow) statsMgr.shadowJumps++;
else statsMgr.playerJumps++;
if(statsMgr.playerJumps+statsMgr.shadowJumps==1000) statsMgr.newAchievement("frog");
}
//Check if sound is enabled, if so play the jump sound.
if(getSettings()->getBoolValue("sound")==true){
Mix_PlayChannel(-1,jumpSound,0);
}
}
}
void Player::show(){
//Check if we should render the recorded line.
//Only do this when we're recording and we're not the shadow.
if(shadow==false && record==true){
//FIXME: Adding an entry not in update but in render?
line.push_back(SDL_Rect());
line[line.size()-1].x=box.x+11;
line[line.size()-1].y=box.y+20;
//Loop through the line dots and draw them.
for(int l=0; l<(signed)line.size(); l++){
appearance.drawState("line",screen,line[l].x-camera.x,line[l].y-camera.y,NULL);
}
}
//NOTE: We do logic here, because it's only needed by the appearance.
appearance.updateAnimation();
appearance.draw(screen, box.x-camera.x, box.y-camera.y, NULL);
}
void Player::shadowSetState(){
int currentKey=0;
/*//debug
extern int block_test_count;
extern bool block_test_only;
if(SDL_GetKeyState(NULL)[SDLK_p]){
block_test_count=recordButton.size();
}
if(block_test_count==(int)recordButton.size()){
block_test_only=true;
}*/
//Check if we should read the input from record file.
if(recordIndex>=0){ // && recordIndex<(int)recordButton.size()){
//read the input from record file
if(recordIndex<(int)recordButton.size()){
currentKey=recordButton[recordIndex];
recordIndex++;
}
//Reset horizontal velocity.
xVel=0;
if(currentKey&PlayerButtonRight){
//Walking to the right.
xVel+=7;
}
if(currentKey&PlayerButtonLeft){
//Walking to the left.
xVel-=7;
}
if(currentKey&PlayerButtonJump){
//The up key, if we aren't in the air we start jumping.
if(inAir==false){
isJump=true;
}else{
//Shouldn't go here
cout<<"Replay BUG"<<endl;
}
}
//check the down key
downKeyPressed=(currentKey&PlayerButtonDown)!=0;
//check the space key
if(currentKey&PlayerButtonSpace){
spaceKeyDown(&objParent->shadow);
}
}else{
//read the input from keyboard.
recordIndex=-1;
//Check for xvelocity.
if(xVel>0)
currentKey|=PlayerButtonRight;
if(xVel<0)
currentKey|=PlayerButtonLeft;
//Check for jumping.
if(isJump){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Jump key recorded\n",objParent->time-1);
cout<<c;
recordKeyPressLog+=c;
#endif
currentKey|=PlayerButtonJump;
}
//Check if the downbutton is pressed.
if(downKeyPressed){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Action key recorded\n",objParent->time-1);
cout<<c;
recordKeyPressLog+=c;
#endif
currentKey|=PlayerButtonDown;
}
if(spaceKeyPressed){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Space key recorded\n",objParent->time-1);
cout<<c;
recordKeyPressLog+=c;
#endif
currentKey|=PlayerButtonSpace;
}
//Record it.
recordButton.push_back(currentKey);
}
#ifdef RECORD_FILE_DEBUG
if(recordIndex>=0){
if(recordIndex>0 && recordIndex<=int(recordPlayerPosition.size())/2){
SDL_Rect &r1=recordPlayerPosition[recordIndex*2-2];
SDL_Rect &r2=recordPlayerPosition[recordIndex*2-1];
if(r1.x!=box.x || r1.y!=box.y || r2.x!=objParent->shadow.box.x || r2.y!=objParent->shadow.box.y){
char c[192];
sprintf(c,"Replay ERROR [%05d] %d %d %d %d Expected: %d %d %d %d\n",
objParent->time-1,box.x,box.y,objParent->shadow.box.x,objParent->shadow.box.y,r1.x,r1.y,r2.x,r2.y);
cout<<c;
}
}
}else{
recordPlayerPosition.push_back(box);
recordPlayerPosition.push_back(objParent->shadow.box);
}
#endif
//reset spaceKeyPressed.
spaceKeyPressed=false;
//Only add an entry if the player is recording.
if(record){
//Add the action.
if(!dead){
playerButton.push_back(currentKey);
//Change the state.
state++;
}
}
}
void Player::shadowGiveState(Shadow* shadow){
//Check if the player calls the shadow.
if(shadowCall==true){
//Clear any recording still with the shadow.
shadow->playerButton.clear();
//Loop the recorded moves and add them to the one of the shadow.
for(unsigned int s=0;s<playerButton.size();s++){
shadow->playerButton.push_back(playerButton[s]);
}
//Reset the state of both the player and the shadow.
stateReset();
shadow->stateReset();
//Clear the recording at the player's side.
playerButton.clear();
line.clear();
//Set shadowCall false
shadowCall=false;
//And let the shadow know that the player called him.
shadow->meCall();
}
}
void Player::stateReset(){
//Reset the state by setting it to 0.
state=0;
}
void Player::otherCheck(class Player* other){
//First make sure the player isn't dead.
//And check for velocity of the block the player is standing on.
if(!dead){
if(objCurrentStand!=NULL){
//Now get the velocity of the object the player is standing on.
SDL_Rect v=objCurrentStand->getBox(BoxType_Velocity);
//Set the base velocity to the velocity of the object.
xVelBase=v.x;
yVelBase=v.y;
//Already move the player box.
box.x+=v.x;
box.y+=v.y;
}
}
//Now do the same for the shadow.
if(!other->dead){
if(other->objCurrentStand!=NULL){
//Now get the velocity of the object the shadow is standing on.
SDL_Rect v=other->objCurrentStand->getBox(BoxType_Velocity);
//Set the base velocity to the velocity of the object.
other->xVelBase=v.x;
other->yVelBase=v.y;
//Already move the shadow box.
other->box.x+=v.x;
other->box.y+=v.y;
}
}
//Now check if the player is on the shadow.
//First make sure they are both alive.
if(!dead && !other->dead){
//Get the box of the shadow.
SDL_Rect boxShadow=other->getBox();
//Check if the player is on top of the shadow.
if(checkCollision(box,boxShadow)==true){
//We have collision now check if the other is standing on top of you.
if(box.y+box.h<=boxShadow.y+13 && !other->inAir){
int yVelocity=yVel-1;
if(yVelocity>0){
box.y-=yVel;
box.y+=boxShadow.y-(box.y+box.h);
inAir=false;
canMove=false;
onGround=true;
other->holdingOther=true;
other->appearance.changeState("holding");
//Change our own appearance to standing.
if(direction==1){
appearance.changeState("standleft");
}else{
appearance.changeState("standright");
}
//Apply the velocity the shadow has.
box.x+=other->xVelBase;
box.y+=other->yVelBase;
}
}else if(boxShadow.y+boxShadow.h<=box.y+13 && !inAir){
int yVelocity=other->yVel-1;
if(yVelocity>0){
other->box.y-=other->yVel;
other->box.y+=box.y-(other->box.y+other->box.h);
other->inAir=false;
other->canMove=false;
other->onGround=true;
holdingOther=true;
appearance.changeState("holding");
//Change our own appearance to standing.
if(other->direction==1){
other->appearance.changeState("standleft");
}else{
other->appearance.changeState("standright");
}
//Apply the velocity the shadow has.
other->box.x+=xVelBase;
other->box.y+=yVelBase;
}
}
}else{
holdingOther=false;
other->holdingOther=false;
}
}
//And set currentStand to null.
objCurrentStand=NULL;
other->objCurrentStand=NULL;
}
SDL_Rect Player::getBox(){
return box;
}
void Player::setMyCamera(){
//Only change the camera when the player isn't dead.
if(dead)
return;
//Check if the level fit's horizontally inside the camera.
if(camera.w>LEVEL_WIDTH){
camera.x=-(camera.w-LEVEL_WIDTH)/2;
}else{
//Check if the player is halfway pass the halfright of the screen.
if(box.x>camera.x+(SCREEN_WIDTH/2+50)){
//It is so ease the camera to the right.
camera.x+=(box.x-camera.x-(SCREEN_WIDTH/2))>>4;
//Check if the camera isn't going too far.
if(box.x<camera.x+(SCREEN_WIDTH/2+50)){
camera.x=box.x-(SCREEN_WIDTH/2+50);
}
}
//Check if the player is halfway pass the halfleft of the screen.
if(box.x<camera.x+(SCREEN_WIDTH/2-50)){
//It is so ease the camera to the left.
camera.x+=(box.x-camera.x-(SCREEN_WIDTH/2))>>4;
//Check if the camera isn't going too far.
if(box.x>camera.x+(SCREEN_WIDTH/2-50)){
camera.x=box.x-(SCREEN_WIDTH/2-50);
}
}
//If the camera is too far to the left we set it to 0.
if(camera.x<0){
camera.x=0;
}
//If the camera is too far to the right we set it to the max right.
if(camera.x+camera.w>LEVEL_WIDTH){
camera.x=LEVEL_WIDTH-camera.w;
}
}
//Check if the level fit's vertically inside the camera.
if(camera.h>LEVEL_HEIGHT){
//We don't centre vertical because the bottom line of the level (deadly) will be mid air.
camera.y=-(camera.h-LEVEL_HEIGHT);
}else{
//Check if the player is halfway pass the lower half of the screen.
if(box.y>camera.y+(SCREEN_HEIGHT/2+50)){
//If is so ease the camera down.
camera.y+=(box.y-camera.y-(SCREEN_HEIGHT/2))>>4;
//Check if the camera isn't going too far.
if(box.y<camera.y+(SCREEN_HEIGHT/2+50)){
camera.y=box.y-(SCREEN_HEIGHT/2+50);
}
}
//Check if the player is halfway pass the upper half of the screen.
if(box.y<camera.y+(SCREEN_HEIGHT/2-50)){
//It is so ease the camera up.
camera.y+=(box.y-camera.y-(SCREEN_HEIGHT/2))>>4;
//Check if the camera isn't going too far.
if(box.y>camera.y+(SCREEN_HEIGHT/2-50)){
camera.y=box.y-(SCREEN_HEIGHT/2-50);
}
}
//If the camera is too far up we set it to 0.
if(camera.y<0){
camera.y=0;
}
//If the camera is too far down we set it to the max down.
if(camera.y+camera.h>LEVEL_HEIGHT){
camera.y=LEVEL_HEIGHT-camera.h;
}
}
}
void Player::reset(bool save){
//Set the location of the player to it's initial state.
box.x=fx;
box.y=fy;
//Reset back to default value.
inAir=true;
isJump=false;
onGround=true;
shadowCall=false;
canMove=true;
holdingOther=false;
dead=false;
record=false;
downKeyPressed=false;
spaceKeyPressed=false;
//Some animation variables.
appearance.resetAnimation(save);
appearance.changeState("standright");
direction=0;
state=0;
xVel=0; //??? fixed a strange bug in game replay
yVel=0;
objCurrentStand=NULL;
objNotificationBlock=NULL;
//Clear the recording.
line.clear();
playerButton.clear();
recordButton.clear();
recordIndex=-1;
#ifdef RECORD_FILE_DEBUG
recordKeyPressLog.clear();
recordPlayerPosition.clear();
#endif
//xVelSaved is used to indicate if there's a state saved or not.
if(save){
xVelSaved=0x80000000;
}
}
void Player::saveState(){
//We can only save the state when the player isn't dead.
if(!dead){
boxSaved.x=box.x;
boxSaved.y=box.y;
xVelSaved=xVel;
yVelSaved=yVel;
inAirSaved=inAir;
isJumpSaved=isJump;
onGroundSaved=onGround;
canMoveSaved=canMove;
holdingOtherSaved=holdingOther;
stateSaved=state;
//Let the appearance save.
appearance.saveAnimation();
//Save any recording stuff.
recordSaved=record;
playerButtonSaved=playerButton;
lineSaved=line;
//Save the record
savedRecordButton=recordButton;
#ifdef RECORD_FILE_DEBUG
recordKeyPressLog_saved=recordKeyPressLog;
recordPlayerPosition_saved=recordPlayerPosition;
#endif
//Only play the sound when it's enabled.
if(getSettings()->getBoolValue("sound")==true){
//To prevent playing the sound twice, only the player can cause the sound.
if(!shadow)
Mix_PlayChannel(-1,saveSound,0);
}
}
}
void Player::loadState(){
//Check with xVelSaved if there's a saved state.
if(xVelSaved==int(0x80000000)){
//There isn't so reset the game to load the first initial state.
//NOTE: There's no need in removing the saved state since there is none.
reset(false);
return;
}
//Restore the saved values.
box.x=boxSaved.x;
box.y=boxSaved.y;
//xVel is set to 0 since it's saved counterpart is used to indicate a saved state.
xVel=0;
yVel=yVelSaved;
//Restore the saved values.
inAir=inAirSaved;
isJump=isJumpSaved;
onGround=onGroundSaved;
canMove=canMoveSaved;
holdingOther=holdingOtherSaved;
dead=false;
record=false;
shadowCall=false;
state=stateSaved;
//Restore the appearance.
appearance.loadAnimation();
//Restore any recorded stuff.
record=recordSaved;
playerButton=playerButtonSaved;
line=lineSaved;
//Load the previously saved record
recordButton=savedRecordButton;
#ifdef RECORD_FILE_DEBUG
recordKeyPressLog=recordKeyPressLog_saved;
recordPlayerPosition=recordPlayerPosition_saved;
#endif
}
void Player::swapState(Player* other){
//We need to swap the values of the player with the ones of the given player.
swap(box.x,other->box.x);
swap(box.y,other->box.y);
//NOTE: xVel isn't there since it's used for something else.
swap(yVel,other->yVel);
swap(inAir,other->inAir);
swap(isJump,other->isJump);
swap(onGround,other->onGround);
swap(canMove,other->canMove);
swap(holdingOther,other->holdingOther);
swap(dead,other->dead);
//Also reset the state of the other.
other->stateReset();
//Play the swap sound.
if(getSettings()->getBoolValue("sound")==true){
Mix_PlayChannel(-1,swapSound,0);
}
}
bool Player::canSaveState(){
//We can only save the state if the player isn't dead.
return !dead;
}
bool Player::canLoadState(){
//We use xVelSaved to indicate if a state is saved or not.
return xVelSaved != int(0x80000000);
}
void Player::die(bool animation){
//Make sure the player isn't already dead.
if(!dead){
dead=true;
//If sound is enabled run the hit sound.
if(getSettings()->getBoolValue("sound")==true){
Mix_PlayChannel(-1,hitSound,0);
}
//Change the apearance to die (if animation is true).
if(animation){
if(appearance.currentStateName.find("right")!=std::string::npos){
appearance.changeState("dieright");
}else{
appearance.changeState("dieleft");
}
}
//Update statistics
if(!objParent->player.isPlayFromRecord() && !objParent->interlevel){
if(shadow) statsMgr.shadowDies++;
else statsMgr.playerDies++;
switch(statsMgr.playerDies+statsMgr.shadowDies){
case 1:
statsMgr.newAchievement("die1");
break;
case 50:
statsMgr.newAchievement("die50");
break;
case 1000:
statsMgr.newAchievement("die1000");
break;
}
if(objParent->player.dead && objParent->shadow.dead) statsMgr.newAchievement("doubleKill");
}
}
//We set the jumpTime to 120 when this is the shadow.
//That's the countdown for the "Your shadow has died." message.
if(shadow){
jumpTime=80;
}
}
diff --git a/src/StatisticsManager.cpp b/src/StatisticsManager.cpp
index 832b754..c1f8287 100644
--- a/src/StatisticsManager.cpp
+++ b/src/StatisticsManager.cpp
@@ -1,720 +1,738 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "StatisticsManager.h"
#include "FileManager.h"
#include "TreeStorageNode.h"
#include "POASerializer.h"
#include "Functions.h"
#include "LevelPackManager.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include "libs/tinyformat/tinyformat.h"
using namespace std;
StatisticsManager statsMgr;
static const int achievementDisplayTime=100;
static const int achievementIntervalTime=120;
#include "AchievementList.h"
static map<string,AchievementInfo*> avaliableAchievements;
//================================================================
StatisticsManager::StatisticsManager(){
bmDropShadow=NULL;
bmQuestionMark=NULL;
bmAchievement=NULL;
startTime=time(NULL);
tutorialLevels=0;
clear();
}
StatisticsManager::~StatisticsManager(){
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
}
void StatisticsManager::clear(){
playerTravelingDistance=shadowTravelingDistance=0.0f;
playerJumps=shadowJumps
=playerDies=shadowDies
=playerSquashed=shadowSquashed
=completedLevels=silverLevels=goldLevels
=recordTimes=switchTimes=swapTimes
=playTime=levelEditTime
=createdLevels=tutorialCompleted=tutorialGold=0;
achievements.clear();
queuedAchievements.clear();
achievementTime=0;
currentAchievement=0;
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
}
#define LOAD_STATS(var,func) { \
vector<string> &v=node.attributes[ #var ]; \
if(!v.empty() && !v[0].empty()) \
var=func(v[0].c_str()); \
}
void StatisticsManager::loadFile(const std::string& fileName){
clear();
ifstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
POASerializer serializer;
if(!serializer.readNode(file,&node,true)) return;
//load statistics
LOAD_STATS(playerTravelingDistance,atof);
LOAD_STATS(shadowTravelingDistance,atof);
LOAD_STATS(playerJumps,atoi);
LOAD_STATS(shadowJumps,atoi);
LOAD_STATS(playerDies,atoi);
LOAD_STATS(shadowDies,atoi);
LOAD_STATS(playerSquashed,atoi);
LOAD_STATS(shadowSquashed,atoi);
LOAD_STATS(recordTimes,atoi);
LOAD_STATS(switchTimes,atoi);
LOAD_STATS(swapTimes,atoi);
LOAD_STATS(playTime,atoi);
LOAD_STATS(levelEditTime,atoi);
LOAD_STATS(createdLevels,atoi);
//load achievements.
//format is: name;time,name;time,...
{
vector<string> &v=node.attributes["achievements"];
for(unsigned int i=0;i<v.size();i++){
string s=v[i];
time_t t=0;
string::size_type lps=s.find(';');
if(lps!=string::npos){
string s1=s.substr(lps+1);
s=s.substr(0,lps);
long long n;
sscanf(s1.c_str(),
#ifdef WIN32
"%I64d",
#else
"%Ld",
#endif
&n);
t=(time_t)n;
}
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(s);
if(it!=avaliableAchievements.end()){
OwnedAchievement ach={t,it->second};
achievements[it->first]=ach;
}
}
}
}
//Call when level edit is start
void StatisticsManager::startLevelEdit(){
levelEditStartTime=time(NULL);
}
//Call when level edit is end
void StatisticsManager::endLevelEdit(){
levelEditTime+=time(NULL)-levelEditStartTime;
}
//update in-game time
void StatisticsManager::updatePlayTime(){
time_t endTime=time(NULL);
playTime+=endTime-startTime;
startTime=endTime;
}
#define SAVE_STATS(var,pattern) { \
sprintf(s,pattern,var); \
node.attributes[ #var ].push_back(s); \
}
void StatisticsManager::saveFile(const std::string& fileName){
char s[64];
//update in-game time
updatePlayTime();
ofstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
//save statistics
SAVE_STATS(playerTravelingDistance,"%.2f");
SAVE_STATS(shadowTravelingDistance,"%.2f");
SAVE_STATS(playerJumps,"%d");
SAVE_STATS(shadowJumps,"%d");
SAVE_STATS(playerDies,"%d");
SAVE_STATS(shadowDies,"%d");
SAVE_STATS(playerSquashed,"%d");
SAVE_STATS(shadowSquashed,"%d");
SAVE_STATS(recordTimes,"%d");
SAVE_STATS(switchTimes,"%d");
SAVE_STATS(swapTimes,"%d");
SAVE_STATS(playTime,"%d");
SAVE_STATS(levelEditTime,"%d");
SAVE_STATS(createdLevels,"%d");
//save achievements.
//format is: name;time,name;time,...
{
vector<string>& v=node.attributes["achievements"];
for(map<string,OwnedAchievement>::iterator it=achievements.begin();it!=achievements.end();++it){
stringstream strm;
char s[32];
long long n=it->second.achievedTime;
sprintf(s,
#ifdef WIN32
"%I64d",
#else
"%Ld",
#endif
n);
strm<<it->first<<";"<<s;
v.push_back(strm.str());
}
}
POASerializer serializer;
serializer.writeNode(&node,file,true,true);
}
void StatisticsManager::loadPicture(){
//Load drop shadow picture
bmDropShadow=loadImage(getDataPath()+"gfx/dropshadow.png");
bmQuestionMark=loadImage(getDataPath()+"gfx/menu/questionmark.png");
}
void StatisticsManager::registerAchievements(){
if(!avaliableAchievements.empty()) return;
for(int i=0;achievementList[i].id!=NULL;i++){
avaliableAchievements[achievementList[i].id]=&achievementList[i];
if(achievementList[i].imageFile!=NULL){
achievementList[i].imageSurface=loadImage(getDataPath()+achievementList[i].imageFile);
}
}
}
void StatisticsManager::render(){
if(achievementTime==0 && bmAchievement==NULL && currentAchievement<(int)queuedAchievements.size()){
//create surface
bmAchievement=createAchievementSurface(queuedAchievements[currentAchievement++]);
drawGUIBox(0,0,bmAchievement->w,bmAchievement->h,bmAchievement,0xFFFFFF00);
//check if queue is empty
if(currentAchievement>=(int)queuedAchievements.size()){
queuedAchievements.clear();
currentAchievement=0;
}
//play a sound
if(getSettings()->getBoolValue("sound")){
Mix_PlayChannel(-1,achievementSound,0);
}
}
//check if we need to display achievements
if(bmAchievement){
achievementTime++;
if(achievementTime<=0){
return;
}else if(achievementTime<=5){
drawAchievement(achievementTime);
}else if(achievementTime<=achievementDisplayTime-5){
drawAchievement(5);
}else if(achievementTime<achievementDisplayTime){
drawAchievement(achievementDisplayTime-achievementTime);
}else if(achievementTime>=achievementIntervalTime){
if(bmAchievement){
SDL_FreeSurface(bmAchievement);
bmAchievement=NULL;
}
achievementTime=0;
}
}
}
void StatisticsManager::newAchievement(const std::string& id,bool save){
//check avaliable achievements
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(id);
if(it==avaliableAchievements.end()) return;
//check if already have this achievement
if(save){
map<string,OwnedAchievement>::iterator it2=achievements.find(id);
if(it2!=achievements.end()) return;
OwnedAchievement ach={time(NULL),it->second};
achievements[id]=ach;
}
//add it to queue
queuedAchievements.push_back(it->second);
}
float StatisticsManager::getAchievementProgress(AchievementInfo* info){
if(!strcmp(info->id,"experienced")){
return float(completedLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"expert")){
return float(goldLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"tutorial")){
if(tutorialLevels>0)
return float(tutorialCompleted)/float(tutorialLevels)*100.0f;
else
return 0.0f;
}
if(!strcmp(info->id,"tutorialGold")){
if(tutorialLevels>0)
return float(tutorialGold)/float(tutorialLevels)*100.0f;
else
return 0.0f;
}
if(!strcmp(info->id,"create50")){
return float(createdLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"frog")){
return float(playerJumps+shadowJumps)/1000.0f*100.0f;
}
if(!strcmp(info->id,"die50")){
return float(playerDies+shadowDies)/50.0f*100.0f;
}
if(!strcmp(info->id,"die1000")){
return float(playerDies+shadowDies)/1000.0f*100.0f;
}
if(!strcmp(info->id,"suqash50")){
return float(playerSquashed+shadowSquashed)/50.0f*100.0f;
}
+ if(!strcmp(info->id,"travel100")){
+ return (playerTravelingDistance+shadowTravelingDistance)/100.0f*100.0f;
+ }
+ if(!strcmp(info->id,"travel1k")){
+ return (playerTravelingDistance+shadowTravelingDistance)/1000.0f*100.0f;
+ }
+ if(!strcmp(info->id,"travel10k")){
+ return (playerTravelingDistance+shadowTravelingDistance)/10000.0f*100.0f;
+ }
+ if(!strcmp(info->id,"travel42k")){
+ return (playerTravelingDistance+shadowTravelingDistance)/42195.0f*100.0f;
+ }
//not found
return 0.0f;
}
SDL_Surface* StatisticsManager::createAchievementSurface(AchievementInfo* info,SDL_Surface* surface,SDL_Rect* rect,bool showTip,const time_t *achievedTime){
if(info==NULL || info->id==NULL) return NULL;
//prepare text
SDL_Surface *title0=NULL,*title1=NULL;
vector<SDL_Surface*> descSurfaces;
SDL_Color fg={0,0,0};
int fontHeight=TTF_FontLineSkip(fontText);
bool showDescription=false;
bool showImage=false;
float achievementProgress=0.0f;
if(showTip){
title0=TTF_RenderUTF8_Blended(fontText,_("New achievement:"),fg);
title1=TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg);
showDescription=showImage=true;
}else if(achievedTime){
char s[128];
strftime(s,sizeof(s),"%c",localtime(achievedTime));
stringstream strm;
tinyformat::format(strm,_("Achieved at %s"),s);
title1=TTF_RenderUTF8_Blended(fontText,strm.str().c_str(),fg);
title0=TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg);
showDescription=showImage=true;
}else if(info->displayStyle==ACHIEVEMT_HIDDEN){
title0=TTF_RenderUTF8_Blended(fontGUISmall,_("Unknown achievement"),fg);
}else{
if(info->displayStyle==ACHIEVEMT_PROGRESS){
achievementProgress=getAchievementProgress(info);
stringstream strm;
tinyformat::format(strm,_("Achieved %0.1f%%"),achievementProgress);
title1=TTF_RenderUTF8_Blended(fontText,strm.str().c_str(),fg);
}else{
title1=TTF_RenderUTF8_Blended(fontText,_("Not achieved"),fg);
}
title0=TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg);
showDescription= info->displayStyle==ACHIEVEMT_ALL || info->displayStyle==ACHIEVEMT_PROGRESS;
showImage=true;
}
if(info->description!=NULL && showDescription){
string description=_(info->description);
string::size_type lps=0,lpe;
for(;;){
lpe=description.find('\n',lps);
if(lpe==string::npos){
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps)+' ').c_str(),fg));
break;
}else{
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps,lpe-lps)+' ').c_str(),fg));
lps=lpe+1;
}
}
}
//calculate the size
int w=0,h=0,w1=8,h1=0;
if(title0!=NULL){
if(title0->w>w) w=title0->w;
h1+=title0->h;
}
if(title1!=NULL){
if(title1->w>w) w=title1->w;
h1+=title1->h;
/*//calc progress bar size
if(!showTip && !achievedTime && info->displayStyle==ACHIEVEMT_PROGRESS){
h1+=4;
}*/
}
if(showImage){
if(info->imageSurface!=NULL){
w1+=info->r.w+8;
w+=info->r.w+8;
if(info->r.h>h1) h1=info->r.h;
}
}else{
w1+=bmQuestionMark->w+8;
w+=bmQuestionMark->w+8;
if(bmQuestionMark->h>h1) h1=bmQuestionMark->h;
}
h=h1+8;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
if(descSurfaces[i]->w>w) w=descSurfaces[i]->w;
}
}
h+=descSurfaces.size()*fontHeight;
w+=16;
h+=16;
//check if size is specified
int left=0,top=0;
if(rect!=NULL){
if(surface!=NULL){
left=rect->x;
top=rect->y;
}
if(rect->w>0) w=rect->w;
else rect->w=w;
rect->h=h;
}
//create surface if necessary
if(surface==NULL){
surface=SDL_CreateRGBSurface(SDL_HWSURFACE,w,h,
screen->format->BitsPerPixel,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0);
}
//draw background
SDL_Rect r={left,top,w,h};
if(showTip || achievedTime){
SDL_FillRect(surface,&r,SDL_MapRGB(surface->format,255,255,255));
}else{
SDL_FillRect(surface,&r,SDL_MapRGB(surface->format,192,192,192));
}
//draw picture
if(showImage){
if(info->imageSurface!=NULL){
SDL_Rect r={left+8,top+8+(h1-info->r.h)/2,0,0};
SDL_BlitSurface(info->imageSurface,&info->r,surface,&r);
}
}else{
SDL_Rect r={left+8,top+8+(h1-bmQuestionMark->h)/2,0,0};
SDL_BlitSurface(bmQuestionMark,NULL,surface,&r);
}
//draw text
h=8;
if(title0!=NULL){
SDL_Rect r={left+w1,top+h,0,0};
SDL_BlitSurface(title0,NULL,surface,&r);
h+=title0->h;
}
if(title1!=NULL){
SDL_Rect r={left+w1,top+h,0,0};
//draw progress bar
if(!showTip && !achievedTime && info->displayStyle==ACHIEVEMT_PROGRESS){
SDL_Rect r1={r.x,r.y,w-8-r.x,title1->h};
SDL_FillRect(surface,&r1,SDL_MapRGB(surface->format,96,96,96));
r1.x++;
r1.y++;
r1.w-=2;
r1.h-=2;
SDL_FillRect(surface,&r1,SDL_MapRGB(surface->format,216,216,216));
r1.w=int(achievementProgress/100.0f*float(r1.w));
SDL_FillRect(surface,&r1,SDL_MapRGB(surface->format,144,144,144));
//???
r.x+=2;
r.y+=2;
}
SDL_BlitSurface(title1,NULL,surface,&r);
}
h=h1+16;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
SDL_Rect r={left+8,top+h+i*fontHeight,0,0};
SDL_BlitSurface(descSurfaces[i],NULL,surface,&r);
}
}
//clean up
if(title0) SDL_FreeSurface(title0);
if(title1) SDL_FreeSurface(title1);
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
SDL_FreeSurface(descSurfaces[i]);
}
}
//over
return surface;
}
void StatisticsManager::drawAchievement(int alpha){
if(bmAchievement==NULL) return;
if(alpha<=0) return;
if(alpha>5) alpha=5;
SDL_Rect r={screen->w-32-bmAchievement->w,32,
bmAchievement->w,bmAchievement->h};
//draw the surface
SDL_SetAlpha(bmAchievement,SDL_SRCALPHA,alpha*40);
SDL_BlitSurface(bmAchievement,NULL,screen,&r);
//draw drop shadow - corner
{
int w1=r.w/2,w2=r.w-w1,h1=r.h/2,h2=r.h-h1;
if(w1>16) w1=16;
if(w2>16) w2=16;
if(h1>16) h1=16;
if(h2>16) h2=16;
int x=(5-alpha)*64;
//top-left
SDL_Rect r1={x,0,w1+16,h1+16},r2={r.x-16,r.y-16,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//top-right
r1.x=x+48-w2;r1.w=w2+16;r2.x=r.x+r.w-w2;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom-right
r1.y=48-h2;r1.h=h2+16;r2.y=r.y+r.h-h2;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom-left
r1.x=x;r1.w=w1+16;r2.x=r.x-16;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
}
//draw drop shadow - border
int i=r.w-32;
while(i>0){
int ii=i>128?128:i;
//top
SDL_Rect r1={0,256-alpha*16,ii,16},r2={r.x+r.w-16-i,r.y-16,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom
r1.x=128;r2.y=r.y+r.h;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
i-=ii;
}
i=r.h-32;
while(i>0){
int ii=i>128?128:i;
//top
SDL_Rect r1={512-alpha*16,0,16,ii},r2={r.x-16,r.y+r.h-16-i,0,0};
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
//bottom
r1.y=128;r2.x=r.x+r.w;
SDL_BlitSurface(bmDropShadow,&r1,screen,&r2);
i-=ii;
}
}
void StatisticsManager::reloadCompletedLevelsAndAchievements(){
completedLevels=silverLevels=goldLevels=0;
LevelPackManager *lpm=getLevelPackManager();
vector<string> v=lpm->enumLevelPacks();
bool tutorial=false,tutorialIsGold=false;
for(unsigned int i=0;i<v.size();i++){
string& s=v[i];
LevelPack *levels=lpm->getLevelPack(s);
levels->loadProgress(getUserPath(USER_DATA)+"progress/"+s+".progress");
bool b=false;
if(s=="tutorial"){
tutorialLevels=levels->getLevelCount();
tutorialCompleted=tutorialGold=0;
b=tutorial=tutorialIsGold=true;
}
for(int n=0,m=levels->getLevelCount();n<m;n++){
LevelPack::Level *lv=levels->getLevel(n);
int medal=lv->won;
if(medal){
if(lv->targetTime<0 || lv->time<=lv->targetTime)
medal++;
if(lv->targetRecordings<0 || lv->recordings<=lv->targetRecordings)
medal++;
completedLevels++;
if(b) tutorialCompleted++;
if(medal==2) silverLevels++;
if(medal==3){
goldLevels++;
if(b) tutorialGold++;
}
if(medal!=3 && b) tutorialIsGold=false;
}else if(b){
tutorial=tutorialIsGold=false;
}
}
}
//upadte achievements
updateLevelAchievements();
updateTutorialAchievementsInternal((tutorial?1:0)|(tutorialIsGold?2:0));
}
void StatisticsManager::reloadOtherAchievements(){
int i;
if(playTime>=7200) newAchievement("addicted");
if(playTime>=86400) newAchievement("loyalFan");
if(levelEditTime>=7200) newAchievement("constructor");
if(levelEditTime>=86400) newAchievement("constructor2");
if(createdLevels>=1) newAchievement("create1");
if(createdLevels>=50) newAchievement("create50");
i=playerJumps+shadowJumps;
if(i>=1000) newAchievement("frog");
i=playerDies+shadowDies;
if(i>=1) newAchievement("die1");
if(i>=50) newAchievement("die50");
if(i>=1000) newAchievement("die1000");
i=playerSquashed+shadowSquashed;
if(i>=1) newAchievement("squash1");
if(i>=50) newAchievement("squash50");
+ float d=playerTravelingDistance+shadowTravelingDistance;
+ if(d>=100.0f) newAchievement("travel100");
+ if(d>=1000.0f) newAchievement("travel1k");
+ if(d>=10000.0f) newAchievement("travel10k");
+ if(d>=42195.0f) newAchievement("travel42k");
+
if(version.find("Development")!=string::npos) newAchievement("programmer");
}
//Update level specified achievements.
//Make sure the completed level count is correct.
void StatisticsManager::updateLevelAchievements(){
if(completedLevels>=1) newAchievement("newbie");
if(goldLevels>=1) newAchievement("goodjob");
if(completedLevels>=50) newAchievement("experienced");
if(goldLevels>=50) newAchievement("expert");
}
//Update tutorial specified achievements.
//Make sure the level progress of tutorial is correct.
void StatisticsManager::updateTutorialAchievements(){
//find tutorial level pack
LevelPackManager *lpm=getLevelPackManager();
LevelPack *levels=lpm->getLevelPack("tutorial");
if(levels==NULL) return;
bool tutorial=true,tutorialIsGold=true;
tutorialLevels=levels->getLevelCount();
tutorialCompleted=tutorialGold=0;
for(int n=0,m=levels->getLevelCount();n<m;n++){
LevelPack::Level *lv=levels->getLevel(n);
int medal=lv->won;
if(medal){
if(lv->targetTime<0 || lv->time<=lv->targetTime)
medal++;
if(lv->targetRecordings<0 || lv->recordings<=lv->targetRecordings)
medal++;
tutorialCompleted++;
if(medal!=3) tutorialIsGold=false;
else tutorialGold++;
}else{
tutorial=tutorialIsGold=false;
break;
}
}
//upadte achievements
updateTutorialAchievementsInternal((tutorial?1:0)|(tutorialIsGold?2:0));
}
//internal function
//flags: a bit-field value indicates which achievements we have.
void StatisticsManager::updateTutorialAchievementsInternal(int flags){
if(flags&1) newAchievement("tutorial");
if(flags&2) newAchievement("tutorialGold");
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, May 16, 10:09 AM (19 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
64146
Default Alt Text
(100 KB)

Event Timeline