;;;  lpmud.el
;;;		Mode for running LPMUD (or possibly other MUDs) in
;;;		Emacs.
;;;
;;;  Last time modified:  1992-06-13
;;;
;;;
;;;  Copyright (C) 1992	Lysator Academic Computer Club,
;;;			Linköping University,  Sweden
;;;
;;;  Everyone is granted permission to copy, modify and redistribute
;;;  this code, provided the people they give it to can.
;;;
;;;
;;;  Authors:	Lars Willför  &  Thomas Bellman
;;;		Lysator Academic Computer Club
;;;		Linköping University
;;;		Sweden
;;;
;;;  email:	willfor@lysator.liu.se
;;;		Bellman@Lysator.LiU.SE
;;;
;;;
;;;  I started writing this because I wanted to use it myself.  I
;;;  realese it in the hope that someone else will find it useful too,
;;;  but there are of course no guaranties that it will work as
;;;  intended or in any other way either!
;;;
;;;  Mail bug reports and ideas to willfor@lysator.liu.se.


;;;  MAJOR CHANGES SINCE PREVIOUS VERSION (or something...)

;;;  -	The auto-actions have become more general and flexible.
;;;  -  New syntax for setting default values for variables.  Your
;;;	old .lpmud-init.el will probably not work any longer.
;;;  -  New improved history.  Not just one command long.
;;;  -	Lots of other improvements.



;;;  INSTALLATION

;;;  The only file you need is the file "lpmud.el"
;;;  Byte compile the file using M-x byte-compile-file.  In your ~/.emacs
;;;  file, or in the system-wide default.el file, add the line
;;;	(autoload 'lpmud "lpmud" "Run LP-MUD in Emacs" t)
;;;
;;;  If you put the file in a directory is not in your load-path you
;;;  must use the pull path to the file in the second argument, e.g.:
;;;    (autoload 'lpmud "/users/foo/joe/junk/lpmud" "Run LP-MUD in Emacs" t)



;;;  STARTING UP

;;;  Start it up by typing M-x lpmud in Emacs.  Give the name of
;;;  the machine and the port number, when Emacs asks for a
;;;  connection.  If no port number is specified it defaults to
;;;  2000.  Enter your player name and password when asked
;;;  for.  Then wait until you are connected.



;;;  KEY BINDINGS

;;;  Return	Send line to LPMUD.
;;;  M-s	Cast a spell.  Which spell is set in the variable
;;;		lpmud-spell.  This variable is local for each buffer.
;;;  M-m	Cast a missile.
;;;  M-q	Buy and drink a drink.  Which drink is set in the
;;;		variable lpmud-drink (buffer-local).  Note that
;;;		sometimes Emacs is a bit too fast, and sends the
;;;		drink command so fast that LPMUD misses it.  As
;;;		of yet, the only sollution is to type the drink
;;;		command by hand if that happens.
;;;  M-.	Score
;;;  M-Return	Redo last *typed* command.
;;;  M-p	Step backwards in the history list.
;;;  M-n	Step forward in the history list.
;;;  Tab	Expand abbreviation.  Some abbrevs are predefined in
;;;		LPMUD mode - gc, dc, sc, ec, fc and ste.  They must be
;;;		separate words, i e there must be a space before them,
;;;		and you must press tab directly after them with no
;;;		space between.  If no abbrevation is found, then
;;;		dynamic abbrevation expansion takes place.
;;;  Lfd	Expand abbreviation and send resulting line to LPMUD.
;;;		Note that dynamic abbrevation expansion does not take
;;;		place when pressing Lfd.

;;;  If you are lucky, and have the right terminal libraries
;;;  loaded, your terminal sends the right things, and the moon
;;;  is in the correct phase, the following keys on the keypad
;;;  might also be working:
;;;  1		Go south-west
;;;  2		Go south
;;;  3		Go south-east
;;;  4		Go west
;;;  6		Go east
;;;  7		Go north-west
;;;  8		Go north
;;;  9		Go north-east
;;;  0		Inventory
;;;  .		Score
;;;  ,		Get all
;;;  PF-1	Go up
;;;  PF-2	Go down



;;;  CUSTOMIZATION

;;;  You can customize the LPMUD mode by having a file named
;;;  ".lpmud-init.el" in your home directory.  This file is
;;;  loaded and executed when the LPMUD mode is loaded.  Also,
;;;  whenever you start up the LPMUD mode, the value of the
;;;  variable lpmud-mode-hook is called if non-nil.  In the
;;;  distribution there is an example .lpmud-init.el file, where
;;;  you can see some of the things that you can do there.

;;;  VARIABLES

;;;  There are some variables that you can set that affect some
;;;  of the features of the LPMUD mode.

;;;  lpmud-drink	The drink you drink using the M-q command.
;;;  lpmud-spell	The spell cast with M-s.
;;;  lpmud-local-auto-actions	Deals with auto-actions.  See separate
;;;  lpmud-global-auto-actions	section about auto-actions.

;;;  These can be given different default values for different
;;;  MUDs and player names.  To set the defaults, you do like
;;;  this:

;;;  (setq lpmud-option-table
;;;        '((("nanny" "130.236.254.13")
;;;               (("bellman")
;;;                    (lpmud-drink "firebreather")
;;;                    (lpmud-spell "fireball"))
;;;               (("tbellman")
;;;                    (lpmud-drink "beer")
;;;                    (lpmud-spell "shock")))
;;;          (("milou" "genesis")
;;;               (("tbellman" "zork")
;;;                    (lpmud-drink "beer")
;;;		       (gurka "tahoe")
;;;                    (lpmud-spell "shock")))))

;;;  This sets the variable 'lpmud-drink' to "firebreather" and the
;;;  variable 'lpmud-spell to "fireball" when playing as player
;;;  "bellman" in any of the MUDs "nanny" of "130.236.254.13".  When
;;;  playing as "tbellman" or "zork" on "milou" or "genesis", it sets
;;;  the variable 'lpmud-drink' to "beer", 'gurka' to "tahoe" and
;;;  'lpmud-spell' to "shock".  Note that all variables found on this
;;;  list automatically becomes buffer-local for that buffer (but not
;;;  globally buffer-local).



;;;  AUTO-ACTIONS

;;;  This is a fairly advanced feature, but quite a fun one.  You
;;;  might want to bow back to all players who bow to you.  But
;;;  typing all this "bow kalle", "bow harry", and so on, can get
;;;  tedious, and you want to bow automatically.  This is where
;;;  the auto-actions enter!  You simply have a list of strings
;;;  (actually regexps, so take care with special characters)
;;;  that you want to match against, and the corresponding
;;;  actions that is to be performed.

;;;  There are two variables controlling the auto-actions:
;;;  'lpmud-global-auto-actions' which is global for all buffers, and
;;;  'lpmud-local-auto-actions', which is local for each LPMUD buffer
;;;  and can be defaulted via the table feature described above.  Both
;;;  variables are lists of lists describing the auto-actions.

;;;  The first element of each sublist is a regexp that describes the
;;;  string that should be matched.  Note that the matching is done
;;;  *in* *the* *buffer*, and not on the exact string coming from the
;;;  MUD.  Thus it may not be safe to match against the beginning of a
;;;  line ("^"), since the output might (and often does!) come
;;;  directly after the prompt, i e it can look like this on the
;;;  screen:

;;;		> Foo bows before you.

;;;  Where "> " is the prompt and "Foo bows before you." is the text
;;;  written by MUD when Foo bowed before you.

;;;  If the second element is a string, then that and all the
;;;  rest of the elements are evaluated and the results are
;;;  concatenated and sent back to the MUD.  To have an elisp
;;;  form first in the "reply string", you could let the first
;;;  reply element (i e the second element of the sublist) be the
;;;  empty string "".

;;;  If however the second element is a list, then the second
;;;  element of the list is called as a function with three
;;;  arguments: start, stop and found.  All three are pointers
;;;  into the LPMUD mode buffer.  'Start' and 'stop' are bounding
;;;  information that tells which part of the buffer Emacs was
;;;  sent from the LPMUD, and 'found' tells the position where
;;;  the match-string was actually found.

;;;  Here comes an example of an auto-action list:

;;;	(("\\(\\w*\\) bows before you"
;;;	  "bow " (buffer-substring (match-beginning 1)
;;;				   (match-end 1)))
;;;	 ("enters the game"	"yawn")
;;;	 ("pukes on you"	(function return-puke)))



;;;  GETTING RID OF ED

;;;  If you are a wizard, you have probably come across the 'ed'
;;;  editor in LPMUD, and if you are used to Emacs, or even vi,
;;;  you probably think ed is a real pain.  Well, using this
;;;  package, you can edit your files in Emacs and send them to
;;;  LPMUD very easy.

;;;  Once you have your file in an Emacs buffer, to send it, you
;;;  just do like this:

;;;  1.		Enter ed.
;;;  2.		Type C-x C-w.
;;;  3.		Tell Emacs the name of the buffer you want to send.
;;;  4.		Wait.
;;;  5.		Write the file, using the "w" command to ed.
;;;  6.		Exit ed using the "q" command.

;;;  Finished!  Simple, isn't it?  Could it be better?  (Sure, it
;;;  *could*, but that's not implemented yet.  Go do something
;;;  yourself instead of complaining! ;-)

;;;  If LPmud did buffer things properly the file transmission would
;;;  have been faster and easier to implement -- unfortunately it
;;;  doesn't.  When sending multiple lines at a time to an LPmud some
;;;  of the lines are likely to end up in the bit bucket.  Because of
;;;  this we have to wait for a new prompt before sending the next
;;;  line to make sure that nothing is lost.

;;;  (The server bug that caused all these problems in the first place
;;;  is supposed to be fixed in LPmud 2.4...)


;;;  KNOWN BUGS

;;;  -  If you want more than one connection to the same MUD, you
;;;	must give the address differently in the different
;;;	buffers.  E g, you could say Nanny.Lysator.LiU.SE 2000 in
;;;	one buffer, and 130.236.254.13 2000 in aonother.
;;;  -  Probably lots of other things.  But I haven't got the
;;;	time to fix them right now.  Fix it yourself, and tell me
;;;	what changes you have done, and it might get into our
;;;	version too.



;;;  THINGS WE WANT TO DO

;;;  -	Searching in the history list, a la C-s and C-r in BASH.
;;;  -	Better support for queueing commands, i e send one line at a
;;;	time and wait for a prompt before sending the next line.  This
;;;	would make M-q more reliable.
;;;  -  Finding lots of free keys to put keybindings on... (not *that*
;;;     important...)


;;;  VARIABLE DEFINITIONS

(defconst lpmud-init-file "~/.lpmud-init.el"
  "Init file for lpmud mode")

(defvar lpmud-file-string)		;; bug: should not be defvar
(defvar lpmud-send-pos)			;; bug: should not be defvar

(defvar lpmud-mode-map nil
  "Keymap used for lpmud mode")

(defvar lpmud-syntax-table nil
  "Syntax table for lpmud mode")

(defvar lpmud-option-table nil
  "*Table containing describing the options you want to set for LPMUD.")

(defvar lpmud-drink "special"
  "*The drink you want to drink.  Used in M-q command.")

(defvar lpmud-spell "shock"
  "*The spell you normally cast (using M-s).")

(defvar lpmud-global-auto-actions '()
  "List of MUD actions to take on certain conditions.
Performs a string-match on car of every element in list, and if it
mathces any of the output from MUD, then does lpmud-send-string on
cadr of element.  E g if set to
	((\"Harry arrives\"	\"puke harry\")
	 (\"A bag\"		\"exa bag\"))
then you puke on Harry everytime he arrives, and everytime you see
a bag, you examine it.")

(defvar lpmud-local-auto-actions '()
  "List of buffer-local MUD actions to take on certain conditions.
Behaves the same as 'lpmud-global-auto-actions', but is local to each
buffer, i e to each MUD connection.")

(defvar lpmud-max-history-size 30
  "Maximum size of the lpmud command history.
When the command history gets bigger than this limit old commands
are discarded.")

(defvar lpmud-history nil "History list for lpmud.")
(defvar lpmud-history-insert-position 0
  "Position to insert commands in the history list.")
(defvar lpmud-history-look-position 0
  "Where we are looking in the history list.")

(defvar lpmud-host-name nil "Name of LPMUD host connected to.")
(defvar lpmud-player-name nil "Name of LPMUD player.")
(defvar lpmud-process nil "The process which talks to the LPMUD server")
(defvar lpmud-mode-hook nil "Hook to be run whenever LPMUD-mode is entered.")


(defun lpmud (host charname)
  "Open a telnet connection to lpmud."
  (interactive "sOpen lpmud connection to host: \nsName: ")
  (require 'shell)
  (let ((name (concat host "-lpmud")))
    (switch-to-buffer (make-shell name "telnet"))
    ;; Need to make these variables buffer-local first...
    (make-local-variable 'lpmud-host-name)
    (make-local-variable 'lpmud-player-name)
    (make-local-variable 'lpmud-passwd)
    (setq lpmud-host-name host		; Was: (substring host 0 (string-match " " host))
	  lpmud-player-name charname
	  lpmud-passwd (lpmud-silent-read "Password: "))
    (lpmud-mode)
    (lpmud-init-history)
    (set-process-filter lpmud-process 'lpmud-initial-filter)
    (set-process-buffer lpmud-process (current-buffer))
    ;; (erase-buffer)
    (process-send-string lpmud-process (concat "open "
					       (if (string-match " " host)
						   host
						 (concat host " 2000"))
					       "\n"))))


(setq lpmud-mode-map (make-sparse-keymap))

(define-key lpmud-mode-map "\r" 'lpmud-send-input)
(define-key lpmud-mode-map "\n" 'lpmud-expand-abbrev-and-send)


(defun lpmud-make-syntax ()
  "Create the syntax table for lpmud mode."
  (if lpmud-syntax-table
      ()
    (setq lpmud-syntax-table (standard-syntax-table))
    (modify-syntax-entry ?( "." lpmud-syntax-table)
    (modify-syntax-entry ?) "." lpmud-syntax-table)
    (modify-syntax-entry ?{ "." lpmud-syntax-table)
    (modify-syntax-entry ?} "." lpmud-syntax-table)
    (modify-syntax-entry ?[ "." lpmud-syntax-table)
    (modify-syntax-entry ?] "." lpmud-syntax-table)))


(defun lpmud-mode ()
  "Mode used while playing LPmud."
  (interactive)

  ;; Kill all buffer-local bindings, EXCEPT for lpmud-host-name,
  ;; lpmud-player-name and lpmud-passwd.
  (let ((temp-host-name lpmud-host-name)
	(temp-player-name lpmud-player-name)
	(temp-passwd lpmud-passwd))
    (kill-all-local-variables)
    (setq lpmud-host-name temp-host-name
	  lpmud-player-name temp-player-name
	  lpmud-passwd temp-passwd))

  (make-local-variable 'lpmud-host-name)
  (make-local-variable 'lpmud-player-name)
  (make-local-variable 'lpmud-passwd)
  (make-local-variable 'lpmud-history)
  (make-local-variable 'lpmud-history-insert-position)
  (make-local-variable 'lpmud-history-look-position)
  (make-local-variable 'lpmud-drink)
  (make-local-variable 'lpmud-spell)
  (make-local-variable 'lpmud-local-auto-actions)
  (make-local-variable 'lpmud-host-name)
  (make-local-variable 'lpmud-player-name)
  (make-local-variable 'lpmud-history)
  (make-local-variable 'lpmud-history-insert-position)
  (make-local-variable 'lpmud-history-look-position)
  (make-local-variable 'lpmud-process)

  (setq lpmud-process (get-buffer-process (current-buffer)))
  (setq major-mode 'lpmud-mode)
  (setq mode-name "Lpmud")
  (setq mode-line-process '(": %s"))
  (use-local-map lpmud-mode-map)
  (lpmud-make-syntax)
  (set-syntax-table lpmud-syntax-table)
  (setq local-abbrev-table lpmud-mode-abbrev-table)
  (make-local-variable 'scroll-step)
  (setq scroll-step 1)
  (mapcar (function
	   (lambda (value-pair)
	     (set (make-local-variable (car value-pair))
		  (car (cdr value-pair)))))
	  (cdr (car (lpmud-find-entry
		     lpmud-player-name
		     (cdr (car (lpmud-find-entry lpmud-host-name
						 lpmud-option-table)))))))
  (run-hooks 'lpmud-mode-hook))



(defun lpmud-find-entry (object list)
  (while (and list
	      (not (lpmud-member object (car (car list)))))
    (setq list (cdr list)))
  list)


(defun lpmud-member (object list)
  "Tests if OBJECT is member of LIST.  Comparison is made with equal.
Returns the tail of LIST from the found object, or nil if not found."
  (while (and list
	      (not (equal object (car list))))
    (setq list (cdr list)))
  list)



(defun lpmud-send-input (arg)
  (interactive "p")
  (end-of-line)
  (let ((command (buffer-substring (process-mark
				    lpmud-process)
				   (point))))
    (lpmud-add-command-to-history command)
    (while (not (zerop arg))
      (lpmud-send-string command)
      (setq arg (1- arg))))
  (delete-region (process-mark lpmud-process)
		 (point))
  (goto-char (point-max))
  (set-marker (process-mark lpmud-process)
	      (point)))


(defun lpmud-silent-read (prompt-str)
  "Read a string in the minibuffer without echoing.
One parameter - the prompt string."
  (interactive "sPrompt string: ")
  (message prompt-str)
  (let ((input-string "")
	(input-char)
	(cursor-in-echo-area t))
    (while (not (eq (setq input-char (read-char))
		    ?\r))
      (progn
	(message prompt-str)
	(setq input-string
	      (cond
	       ((eq input-char ?\C-?)
		(if (equal (length input-string)
			   0)
		    ""
		  (substring input-string 0 -1)))
	       ((eq input-char ?\C-u)
		"")
	       (t
		(concat input-string (char-to-string input-char)))))))
    input-string))

;;; COMMANDS TO HANDLE THE HISTORY

(defun lpmud-history-index (index)
  "Returns a normalized index into the history list."
  (let (real-index)
    (setq real-index (% index lpmud-max-history-size))
    (if (< real-index 0)
	(setq real-index (1- lpmud-max-history-size)))
    real-index))


(defun lpmud-init-history ()
  "Initialize the history list for an LP-MUD buffer."
  (make-local-variable 'lpmud-history)
  (make-local-variable 'lpmud-history-insert-position)
  (make-local-variable 'lpmud-history-look-position)
  (setq lpmud-history (make-vector lpmud-max-history-size nil))
  (setq lpmud-history-insert-position 0)
  (setq lpmud-history-look-position 0))


(defun lpmud-add-command-to-history (command)
  "Add a string to the history list of an LPMUD buffer."
  (if (not (or (string= "" command)	; Don't add empty commands.
	       (string-match "^[ \t]*$" command)))
      (progn
	(aset lpmud-history lpmud-history-insert-position command)
	(setq lpmud-history-insert-position
	      (lpmud-history-index (1+ lpmud-history-insert-position)))
	(setq lpmud-history-look-position lpmud-history-insert-position)
	(aset lpmud-history lpmud-history-insert-position nil))))


(defun lpmud-previous-history (arg)
  "Step ARG steps backward in the history list."
  (interactive "p")
  (let* (new-pos hist-string)
    (setq new-pos
	  (lpmud-history-index (- lpmud-history-look-position arg)))
    (setq hist-string (aref lpmud-history new-pos))

    (goto-char (point-max))
    (delete-region (process-mark
		    lpmud-process)
		   (point))
    (if hist-string
	(progn
	  (insert hist-string))
      (if (/= new-pos lpmud-history-insert-position)
	  (message "Out of history")))
    (setq lpmud-history-look-position new-pos)))


(defun lpmud-next-history (arg)
  "Step ARG steps forwards in the history list."
  (interactive "p")
  (lpmud-previous-history (- arg)))


(defun lpmud-last-history ()
  (interactive)
  (setq lpmud-history-look-position
	(lpmud-history-index (1- lpmud-history-insert-position))))


(defun lpmud-repeat-last-command (arg)
  (interactive "p")
  (let ((command (aref lpmud-history
		       (lpmud-history-index
			(1- lpmud-history-look-position)))))
    (if command
	(while (not (zerop arg))
	  (lpmud-send-string command)
	  (setq arg (1- arg))))))


(defun lpmud-expand-abbrev-and-send (arg)
  "Expand abbrev and send input to MUD"
  (interactive "p")
  (expand-abbrev)
  (lpmud-send-input arg))


(defun lpmud-initial-filter (proc string)
  (save-excursion
    (set-buffer (process-buffer proc))
    (let (lpmud-global-auto-actions lpmud-local-auto-actions)
      (cond ((string-match "unknown host" string)
	     (process-send-string proc "quit\n")
	     (error "Unknown host (emacs sighs deeply)."))
	    ((string-match "Connection refused" string)
	     (process-send-string proc "quit\n")
	     (error "Connection refused (emacs sighs deeply)."))
	    ((or (string-match "name:" string)
		 (string-match "heter du:" string))
	     (lpmud-filter proc string)
	     (process-send-string proc (concat lpmud-player-name "\n")))
	    ((or (string-match "[pP]assword:[ \t]*" string)
		 (string-match ".*senord:[ \t]*" string))
	     (lpmud-filter proc string)
	     (process-send-string proc (concat lpmud-passwd "\n"))
	     (setq lpmud-passwd nil))
	    ((not lpmud-passwd)
	     (if (string-match "\n" string)
		 (set-process-filter proc 'lpmud-filter))
	     (lpmud-filter proc string))
	    (t
	      (lpmud-filter proc string))))))


(defmacro lpmud-handle-auto-action ()
  ;; Macro to check if a certain auto-action should be performed, and
  ;; perform it if it should.  This macro can only be used in the proper
  ;; place, i e inside the 'let' form in the function 'lpmud-filter',
  ;; since it uses some local variable names.
  '(function
    (lambda (action)
      (if (setq string-found
		(progn
		  (goto-char start-pos)
		  (if (re-search-forward (car action) end-pos t)
		      (point)
		    nil)))
	  (cond
	   ((stringp (car (cdr action)))
	    ;; String?  Then just send it to the MUD.
	    (lpmud-send-string (apply (function concat)
				      (mapcar (function eval)
					      (cdr action)))))
	   ((listp (car (cdr action)))
	    ;; Function?  Call it.
	    (funcall (car (cdr (car (cdr action)))) start-pos end-pos
		     string-found)))))))

;; ???

(defun lpmud-filter (proc string)
  (save-excursion
    (set-buffer (process-buffer proc))
    (let ((old-point (point-marker)))
      (goto-char (process-mark proc))
      (let ((start-pos (point))
	    end-pos string-found)
	(insert-before-markers string)
	(set-marker (process-mark proc) (setq end-pos (point)))
	(while (search-backward "\r" start-pos t)
	  (delete-char 1))
	(mapcar (lpmud-handle-auto-action)
		lpmud-global-auto-actions)
	(mapcar (lpmud-handle-auto-action)
		lpmud-local-auto-actions))
	(goto-char old-point)))

  (if (and (get-buffer-window (process-buffer proc))
	   (lpmud-in-last-line-p (point)))
      (let ((old-selected-window (selected-window)))
	(select-window (get-buffer-window (process-buffer proc)))
	(recenter -1)
	(select-window old-selected-window))))



(defun lpmud-in-last-line-p (position)
  "Returns nil if POSITION is not in the last line of the buffer."
  (>= position
      (save-excursion
	(goto-char (point-max))
	(beginning-of-line)
	(point))))



(defun lpmud-send-file-filter (proc string)
  (if (string-match "*\C-h" string)
      (if lpmud-send-pos
	  (let ((newpos (string-match "\n"
				      lpmud-file-string
				      (1+ lpmud-send-pos))))
	       (process-send-string proc
				    (concat
				     (substring lpmud-file-string
						(1+ lpmud-send-pos)
						newpos)
				     "\n"))
	       (message (int-to-string lpmud-send-pos))
	       (setq lpmud-send-pos newpos))
	(set-process-filter proc 'lpmud-filter)
	(process-send-string proc ".\n")
	(message "Transmission completed."))))


(defun lpmud-send-string (string)
  (interactive "sSend string to lpmud: ")
  (process-send-string lpmud-process
		       (concat string "\n")))


(defun lpmud-send-file (filename)
  (interactive "fSend file to lpmud: ")
  (let ((old-buffer (current-buffer)))
    (set-buffer (generate-new-buffer "*lpmud-file-transfer*"))
    (insert-file filename)
    (let ((file-text (buffer-string)))
      (kill-buffer (current-buffer))
      (set-buffer old-buffer)
      (lpmud-ed-send-string file-text))))


(defun lpmud-send-buffer (buffername)
  (interactive "BSend buffer to lpmud: ")
  (let ((old-buffer (current-buffer)))
    (set-buffer buffername)
    (lpmud-ed-send-string (prog1 (buffer-string) (set-buffer old-buffer)))))


;; Nu funkar det h{r. Synd att det {r fel s{tt att g|ra det p}... ;-/
;; ?????

(defun lpmud-delete-backward-char (arg killp)
  "Same thing as delete-backward-char except that it doesn't delete lpmud prompt."
  (interactive "*p\nP")
  (delete-backward-char
   (if (and (lpmud-in-last-line-p (point))
	    (> arg 0))
       (let ((old-point (point)))
	 (beginning-of-line)
	 (if (looking-at "> ")
	     (forward-char 2))
	 (prog1
	     (max
	      (min arg
		   (- old-point (point)))
	      0)
	   (goto-char old-point)))
     arg) killp))

(defun lpmud-kill-word (arg)
  "kill-word that will not delete backwards over the lpmud prompt."
  (interactive "*p")
  (let ((old-point (point))
	(end-of-delete (progn (forward-word arg)
			      (point))))
    (if (or (> end-of-delete old-point)
	     (not (lpmud-in-last-line-p old-point))
	     (lpmud-in-last-line-p end-of-delete))
	(kill-region old-point end-of-delete)
      (goto-char old-point)
      (lpmud-beginning-of-line 1)
      (kill-region (point) old-point))))

(defun lpmud-backward-kill-word (arg)
  "backward-kill-word that will not delete backwards over the lpmud prompt."
  (interactive "*p")
  (lpmud-kill-word (- arg)))

(defun lpmud-beginning-of-line (arg)
  "Same things as beginning-of-line, but move before the LPmud prompt."
  (interactive "p")
  (beginning-of-line arg)
  (if (and (lpmud-in-last-line-p (point))
	   (looking-at "> "))
      (goto-char (+ (point) 2))))

;;;  KEY DEFINITIONS

(define-key lpmud-mode-map "\C-x\C-w" 'lpmud-send-buffer)


(defun lpmud-do-command (commands)
  (` (lambda (arg) (interactive "p")
       (while (>= (setq arg (1- arg)) 0)
	 (lpmud-send-string (apply (function concat)
				   (mapcar (function eval)
					   '(, commands))))))))

(defun lpmud-expand-abbrev (arg)
  (interactive "*P")
  (or (expand-abbrev)
      (dabbrev-expand arg)))

(mapcar (function (lambda (def)
		    (define-key lpmud-mode-map (car def)
		      (lpmud-do-command (cdr def)))))
	'(("\M-."	"score")
	  ("\M-q"	"buy " lpmud-drink "\ndrink " lpmud-drink)
	  ("\M-m"	"missile")
	  ("\M-s"	lpmud-spell)))

;;;  DEFINE ABBREVIATION TABLE
(define-abbrev-table 'lpmud-mode-abbrev-table
  '(("fc"	"from corpse"		nil	0)
    ("gc"	"get corpse"		nil	0)
    ("dc"	"drop corpse"		nil	0)
    ("ec"	"exa corpse"		nil	0)
    ("sc"	"search corpse"		nil	0)
    ("ste"	"stethoscope"		nil	0)))


;;;  VARIOUS OTHER KEYS
(define-key lpmud-mode-map "\M-\r" 'lpmud-repeat-last-command)
(define-key lpmud-mode-map "\C-?" 'lpmud-delete-backward-char)
(define-key lpmud-mode-map "\M-d" 'lpmud-kill-word)
(define-key lpmud-mode-map "\M-\C-?" 'lpmud-backward-kill-word)
(define-key lpmud-mode-map "\C-a" 'lpmud-beginning-of-line)
(define-key lpmud-mode-map "\M-p" 'lpmud-previous-history)
(define-key lpmud-mode-map "\M-n" 'lpmud-next-history)
(define-key lpmud-mode-map "\t" 'lpmud-expand-abbrev)



;;; KEYPAD

(defun lpmud-define-function-key (keypad-entry)
  "Defines a function key if it is present on the current terminal.
KEYPAD-ENTRY is a cons cell with the CAR being the character for the
function key to define, and the CDR being the string that should be sent
to he MUD when the function key is pressed.  E g (?5 . \"look\") would define
the `5' key on the keypad to send `look' to the MUD if there is a keypad on
the terminal."

  (if (function-key-sequence (car keypad-entry))
      (define-key lpmud-mode-map
	(function-key-sequence (car keypad-entry))
	(` (lambda ()
	     (interactive)
	     (lpmud-send-string (, (cdr keypad-entry))))))))


(if (require 'keypad)
    (mapcar (function lpmud-define-function-key)
	    '((?e . "look")
	      (?, . "get all")
	      (?0 . "i")
	      (?. . "score")
	      (?1 . "sw")
	      (?2 . "s")
	      (?3 . "se")
	      (?4 . "w")
	      (?6 . "e")
	      (?7 . "nw")
	      (?8 . "n")
	      (?9 . "ne")
	      (?\C-a . "u")
	      (?\C-b . "d"))))
	    


(if (file-readable-p lpmud-init-file)
    (save-excursion
      (find-file lpmud-init-file)
      (eval-current-buffer)))
