Now we'll create a command to pick up objects in our world. We'll need some helper functions for this. To help think about which helper functions we need to create, let's list the things we need to do:

  1. Check to see if the item we seek is present
  2. Set the object location of player for the object we seek
  3. Update the list of objects
  4. Update the game state
(defun good-pick (item-name)
  (io:format "~nYou are now carrying the ~s.~n"
             (list (atom_to_list item-name))))

(defun check-item
  ((item-name (= (match-object name obj-name) obj)) (when (== item-name obj-name))
    (good-pick item-name)
    (set-object-location obj 'player))
  ((_ obj) obj))

We saw something like this before, with our function that conditionally printed a new line. In check-item/2, there are two matches against function arguments: one is checking the value of a passed object name and the record's object name; the other doesn't care. If there's no match with the object name, the state isn't updated -- the old state is simply returned.

Here are some more helper functions we'll need:

(defun update-items (item-name game-state)
    (lambda (obj) (check-item item-name obj))
    (state-objects game-state)))

(defun get-item-names (game-state)
    (lambda (x) (object-name x))
    (whats-here? game-state)))

(defun bad-pick ()
  (io:format "~nThat item is not here.~n"))

That's all the helper functions we need; now for the main attraction:

(defun pickup-item
  ((item-name (= (match-state player-location player-loc objects objs) game-state))
    (case (lists:member item-name (get-item-names game-state))
              game-state (update-items item-name game-state)))

Our pickup-item function takes two arguments:

  1. the name of an item, and
  2. the game state record

But it's also doing some pattern matching: its extracting the player's current location as well as the list of objects in the game. Then it checks to see if the item passed as an argument is in present in the player's current location.

Now let's cast another SPEL to make the command easier to use:

(defspel pickup (item-name game-state)
  `(pickup-item ',item-name ,game-state))

Now let's try our new SPEL:

lfe> (pickup whiskey-bottle state)
You are now carrying the whiskey-bottle.

Again,for now we're going to keep ignoring the state data that is returned with each of our commands -- we'll be taking care of that in the next chapter!

lfe> (pickup frog state)
That item is not here.

Now let's add a couple more useful commands: first, a command that lets us see our current inventory of items we're carrying (with the obligatory helper functions first!):

(defun inv-obj
  (((match-state objects objs))
        (((match-object location 'player)) 'true)
        ((_) 'false))

(defun inv-name (game-state)
    (lambda (x) (object-name x))
    (inv-obj game-state)))

(defun get-inv-str (game-state)
      (lambda (x) (++ " - " (atom_to_list x) "\n"))
      (inv-name game-state))

Did you notice our inv-obj helper function has a match-lambda? In LFE, you can even do pattern matching in anonymous functions!

(defun display-inv (game-state)
  (let ((inv-str (get-inv-str game-state)))
    (case inv-str
      ('() (io:format "~nYou are not carrying anything.~n"))
      (_ (io:format "~nYou are carrying the following:~n~s"
                    (list inv-str))))))

Try it out:

lfe> (display-inv state)
You are not carrying anything.

Now pick up the bucket and try again:

lfe> (set state (pickup bucket state))
You are now carrying the bucket.
lfe> (display-inv state)
You are carrying the following:
 - bucket

Now a function that tells us if we have a certain object on us:

(defun inv? (item-name game-state)
  (lists:member item-name (inv-name game-state)))

Try it out:

lfe> (inv? 'bucket state)
lfe> (inv? 'chain state)