12 Org Gamification
游戏化是一个很火的概念,Org-mode是一个实现GTD的极好工具,将两者结合起来想必非常有趣.
下面就是一个简单的对Org-mode游戏化的尝试,主要就是通过完成任务赚取积分,用然后用积分购买奖励物品.
1 积分操作
完成任务可以获取到积分,默认情况下[#A]级任务增加30积分,[#B]级任务增加20积分,[#C]级任务增加10积分.
但可以通过为每个entry设置REWARD属性的方式自定义完成该entry所获得的奖励积分数,需要为整数.
(defun org-gamification-point-to-score()
"定位到积分行"
(interactive)
(goto-char (point-min))
(when (not (search-forward-regexp "^#\\+SCORES: " nil t))
(goto-char (point-max))
(if (search-backward-regexp "^#\\+" nil t)
(progn
(end-of-line)
(newline))
(progn
(goto-char (point-min))
(newline)
(previous-line)))
(beginning-of-line)
(insert "#+SCORES: "))
)
(defun org-gamification-get-score()
"获取当前累计的积分数量"
(save-excursion
(org-gamification-point-to-score)
(string-to-int (buffer-substring-no-properties (point) (line-end-position))))
)
(defun org-gamification-update-score(&optional newScore) "更新当前积分"
(save-excursion
(org-gamification-point-to-score)
(insert (number-to-string newScore))
(insert " points")
(backward-word)
(kill-line)))
(defun org-gamification-add-score (score)
"增加指定积分"
(save-excursion
(let (newScore)
(setq newScore (+ score (org-gamification-get-score)))
(org-gamification-update-score newScore))))
(defun org-gamification-remove-score-able-p (score)
"判断是否能够扣减指定分数"
(> (org-gamification-get-score) score))
(defun org-gamification-remove-score (score)
"减少指定积分"
(save-excursion
(if (org-gamification-remove-score-able-p score)
(org-gamification-update-score (- (org-gamification-get-score) score))
(message "积分不足"))))
(defun org-gamification-get-entry-reward-score ()
"获取完成entry该获得的积分"
(save-excursion
;;(org-back-to-heading t)
(if (org-entry-get nil "REWARD" t)
(string-to-int (org-entry-get nil "REWARD" t))
(+ 10 (/ (org-get-priority (thing-at-point 'line)) 100)))))
2 奖励操作
可以使用积分购买奖励物品,带有`REWARD`标签的headline被认为是奖励物品
将奖励物品的标记为完成状态表示购买该奖励物品,会减少积分
使用命令`org-gamification-point-to-reward`跳转到奖励物品部分的headline处
使用命令`org-gamification-add-reward`来增加待购买的奖励物品
(defun org-gamification-point-to-reward ()
"跳转到REWARD headline"
(interactive)
(let (reward-headline-pos)
(setq reward-headline-pos (org-find-exact-headline-in-buffer "REWARDS" nil t))
(when (null reward-headline-pos)
(goto-char (point-max))
(newline)
(beginning-of-line)
(insert "* REWARDS")
(beginning-of-line)
(setq reward-headline-pos (point))
)
(goto-char reward-headline-pos))
)
(defun org-gamification-add-reward ()
"增加奖励物品,需要用积分购买"
(interactive)
(save-excursion
(let (reward-name reward-price)
(org-gamification-point-to-reward)
(end-of-line)
(setq reward-name (read-string "请输入奖品名称: "))
(org-insert-subheading nil)
(insert reward-name)
(setq reward-price (read-string "请输入奖品价格(整数): "))
(org-set-property "PRICE" (int-to-string (string-to-int reward-price)))
(org-set-tags-to ":REWARD:")
))
)
(defun org-gamification-buy-reward-able-p()
"判断是否能够购买奖励物品"
(if (org-entry-get nil "PRICE" t)
(org-gamification-remove-score-able-p (string-to-int (org-entry-get nil "PRICE" t)))
(progn
(message "该奖励没有设置PRICE")
nil)))
(defun org-gamification-buy-reward ()
"购买奖励物品,会减少积分"
(when (org-gamification-buy-reward-able-p)
(org-gamification-remove-score (string-to-int (org-entry-get nil "PRICE" t)))))
(defun org-gamification-sell-reward ()
"售卖奖励物品,会增加积分"
(if (org-entry-get nil "PRICE" t)
(org-gamification-add-score (string-to-int (org-entry-get nil "PRICE" t)))
(message "该奖励没有设置PRICE"))
)
3 游戏环境初始化
使用命令`org-gamification-init`来初始化游戏,积分会清空为0
(defun org-gamification-reward-p ()
"判断该entry是否属于奖励"
(save-excursion
(org-back-to-heading)
(find "REWARD" (org-get-tags) :test 'string=)))
(defun org-gamification-entry-trigger (task-plist)
"完成事项,增加积分"
(let (from-state to-state )
(setq from-state (plist-get task-plist :from))
(setq to-state (plist-get task-plist :to))
(save-excursion
(when (and (member to-state org-done-keywords) ( or (member from-state org-not-done-keywords) (null from-state)))
(if (org-gamification-reward-p)
(org-gamification-buy-reward)
(org-gamification-add-score (org-gamification-get-entry-reward-score))))
(when (and (or (member to-state org-not-done-keywords) (null to-state)) (member from-state org-done-keywords))
(if (org-gamification-reward-p)
(org-gamification-sell-reward)
(org-gamification-remove-score (org-gamification-get-entry-reward-score))))
))
)
(defun org-gamification-entry-blocker (task-plist)
"若动作会将积分变成负数,则不能进行该动作"
(let (from-state to-state )
(setq from-state (plist-get task-plist :from))
(setq to-state (plist-get task-plist :to))
(save-excursion
(cond ((and (member to-state org-done-keywords) ( or (member from-state org-not-done-keywords) (null from-state)))
(if (org-gamification-reward-p)
(org-gamification-buy-reward-able-p)
t))
((and (or (member to-state org-not-done-keywords) (null to-state)) (member from-state org-done-keywords))
(if (not (org-gamification-reward-p))
(org-gamification-remove-score-able-p (org-gamification-get-entry-reward-score))
t))
(t t))
))
)
(defun org-gamification-init()
"org游戏化初始化函数 初始化积分为0 初始化游戏的hook "
(org-gamification-update-score 0)
(org-gamification-start)
)
(defun org-gamification-start ()
"初始化游戏的hook"
(interactive)
(add-to-list 'org-trigger-hook 'org-gamification-entry-trigger)
(add-to-list 'org-blocker-hook 'org-gamification-entry-blocker)
)
(defun org-gamification-end ()
"结束游戏"
(interactive)
(setq org-trigger-hook (remove 'org-gamification-entry-trigger org-trigger-hook))
(setq org-blocker-hook (remove 'org-gamification-entry-blocker org-blocker-hook))
)