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))
)