天天看點

org-mode遊戲化的簡單實作

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