                                               (assoc m k (get v :num-harvestable-cells 0)))
                                             {}
                                             (get-in state [:world :places])))
+        world-time     (rw/get-time state)
+        fruit-rotted?  (atom false)
         ;viewport-cells (apply concat
         ;                 (map-indexed (fn [vy line]
         ;                                (map-indexed (fn [vx cell]

            ;; rot fruit
            ;; TODO: only rot fruit if it is in the set of visible cells.
            (as-> xy-fns
              (if (some (fn [item] (< (get item :rot-time (rw/get-time state)) (rw/get-time state)))
                                        cell-items)
                (conj xy-fns [[wx wy]
                              (fn rot-cell-fruit [cell]
                                (update-in cell [:items] (fn [items]
                                  (remove (fn [item] (< (get item :rot-time (rw/get-time state))
                                                        (rw/get-time state)))
                                           items))))])
                xy-fns)))))
      []
      (vec (rv/cellsxy-in-viewport state)))]
    (as-> state state
      (rw/update-cells state xy-fns)
      (reduce-kv (fn [state place-id harvest-count]
                   (assoc-in state [:world :places place-id :num-harvestable-cells] harvest-count))

                ;; chance of dropped a fruit
                (if (= (rr/uniform-int 0 300) 0)
                  ;; make the fruit item and find an adjacent free cell to drop it into
                  (let [item    (assoc (ig/id->item (get cell :fruit-type)) :rot-time (+ world-time (rr/uniform-int 25 35)))
                        adj-xys (remove (fn [[x y]] (or (not (rv/xy-in-viewport? state x y))
                                                         (rw/type->collide?
                                                           (get (rw/get-cell state x y) :type))))
