For the lisp game jam http://itch.io/jam/january-2016-lisp-game-jam. Also practice for my future forever project game (not yet named). This is a roguelike where you play a necromancer able to summon skeletons and any other powers I have time for.

crypts-and-corpses.lisp 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. ;;;; crypts-and-corpses.lisp
  2. (in-package #:crypts-and-corpses)
  3. (defstruct game-map
  4. (width 250 :type fixnum :read-only t)
  5. (height 250 :type fixnum :read-only t)
  6. (mobiles (make-hash-table) :type hash-table :read-only t))
  7. (defclass entity ()
  8. ((width :initarg :width :type fixnum :initform 1)
  9. (height :initarg :height :type fixnum :initform 1)
  10. (location :initarg :location :initform 0 :type fixnum)))
  11. (defclass tile-entity (entity)
  12. ((codepoint :initarg :codepoint :initform #x3f :type fixnum)))
  13. (defclass viewport (entity)
  14. ((width :initarg :width :type fixnum :initform 100)
  15. (height :initarg :height :type fixnum :initform 40)
  16. (location :initarg :location :type fixnum :initform 0)))
  17. (defclass mobile (tile-entity)
  18. ((health :initarg :health :type fixnum)
  19. (mana :initarg :mana :type fixnum)
  20. (health-regen :initarg :health-regen :initform 0 :type fixnum)
  21. (mana-regen :initarg :mana-regen :initform 0 :type fixnum)))
  22. (defclass player-character (mobile)
  23. ((ranged-attack-damage :initarg :ranged-attack-damage :initform 5 :type fixnum)
  24. (ranged-attack-cost :initarg :ranged-attack-cost :initform 5 :type fixnum)))
  25. (defclass enemy (mobile) ())
  26. (defclass minion (mobile) ())
  27. (defun calc-coord (location map)
  28. (values (mod location (game-map-width map)) (floor (/ location (game-map-width map)))))
  29. (defun get-window-size ()
  30. (apply 'values (mapcar #'parse-integer (split-sequence #\x (terminal-get "window.size" "0x0")))))
  31. ;; DEPRECATED until I find a use for it
  32. ;; (defun calc-location (x y map)
  33. ;; (+ (* (game-map-width map) (cdr y)) (car x)))
  34. ;; (defgeneric set-location (object x y))
  35. ;; (defmethod set-location ((object tile-entity) x y)
  36. ;; (setf (slot-value object 'location) (calc-location x y)))
  37. (defgeneric end-of-turn (object))
  38. (defmethod end-of-turn ((object mobile))
  39. (incf (slot-value object 'health) (slot-value object 'health-regen))
  40. (incf (slot-value object 'mana) (slot-value object 'mana-regen)))
  41. (defgeneric move-left (object map))
  42. (defgeneric move-right (object map))
  43. (defgeneric move-up (object map))
  44. (defgeneric move-down (object map))
  45. (defgeneric update-location (object map old-location new-location))
  46. (defun overrun-p (old-location new-location map)
  47. (multiple-value-bind (old-x old-y) (calc-coord old-location map)
  48. (multiple-value-bind (new-x new-y) (calc-coord new-location map)
  49. (and (< old-y new-y)
  50. (> old-x new-x)))))
  51. (defmethod update-location ((object entity) map old-location new-location)
  52. (unless (or (out-of-bounds-p new-location map)
  53. (overrun-p old-location (+ new-location (slot-value object 'width)) map)
  54. (out-of-bounds-p (+ new-location (* (slot-value object 'height) (game-map-width map))) map))
  55. (setf (slot-value object 'location) new-location)))
  56. (defmethod update-location ((object mobile) map old-location new-location)
  57. (unless (out-of-bounds-p new-location map)
  58. (setf (slot-value object 'location) new-location)
  59. (setf (gethash new-location (game-map-mobiles map)) object)
  60. (remhash old-location (game-map-mobiles map))))
  61. (defun out-of-bounds-p (new-location map)
  62. (multiple-value-bind (x y) (calc-coord new-location map)
  63. (or (> 0 x)
  64. (> 0 y)
  65. (>= x (game-map-width map))
  66. (>= y (game-map-height map)))))
  67. (defmethod move-left ((object entity) map)
  68. (let ((old-location (slot-value object 'location))
  69. (new-location (1- (slot-value object 'location))))
  70. (unless (= 0 (calc-coord old-location map))
  71. (update-location object map old-location new-location))))
  72. (defmethod move-up ((object entity) map)
  73. (let ((old-location (slot-value object 'location))
  74. (new-location (- (slot-value object 'location) (game-map-width map))))
  75. (update-location object map old-location new-location)))
  76. (defmethod move-down ((object entity) map)
  77. (let ((old-location (slot-value object 'location))
  78. (new-location (+ (slot-value object 'location) (game-map-width map))))
  79. (update-location object map old-location new-location)))
  80. (defmethod move-right ((object entity) map)
  81. (let ((old-location (slot-value object 'location))
  82. (new-location (1+ (slot-value object 'location))))
  83. (unless (= 0 (calc-coord new-location map))
  84. (update-location object map old-location new-location))))
  85. (defun intro-message ()
  86. (loop as key = (terminal-read) do
  87. (multiple-value-bind (win-width win-height) (get-window-size)
  88. (terminal-print (floor (- (/ win-width 2) 10)) (floor (1- (/ win-height 2))) "Crypts and Corpses")
  89. (terminal-print (floor (- (/ win-width 2) 11)) (floor (1+ (/ win-height 2))) "Press enter to start"))
  90. (terminal-refresh)
  91. until (= key bearlibterminal-ffi:+tk-enter+)))
  92. (defun write-map-to-terminal (map viewport)
  93. (labels ((write-cell (key tile) (multiple-value-bind (x y) (calc-coord (- key (slot-value viewport 'location)) map)
  94. (terminal-put x y (slot-value tile 'codepoint)))))
  95. (maphash #'write-cell (game-map-mobiles map))))
  96. (defun initialize-map (map player)
  97. (setf (gethash (slot-value player 'location) (game-map-mobiles map)) player)
  98. map)
  99. (defun handle-input (key map viewport player)
  100. (cond
  101. ((= key bearlibterminal-ffi:+tk-up+) (move-up player map))
  102. ((= key bearlibterminal-ffi:+tk-down+) (move-down player map))
  103. ((= key bearlibterminal-ffi:+tk-right+) (move-right player map))
  104. ((= key bearlibterminal-ffi:+tk-left+) (move-left player map))
  105. ((= key bearlibterminal-ffi:+tk-w+) (move-up viewport map))
  106. ((= key bearlibterminal-ffi:+tk-a+) (move-left viewport map))
  107. ((= key bearlibterminal-ffi:+tk-d+) (move-right viewport map))
  108. ((= key bearlibterminal-ffi:+tk-s+) (move-down viewport map))
  109. ((= key bearlibterminal-ffi:+tk-space+) (update-location viewport map (slot-value viewport 'location) (slot-value player 'location)))))
  110. (defun create-viewport ()
  111. (multiple-value-bind (width height) (get-window-size)
  112. (make-instance 'viewport :width width :height height)))
  113. (defun main-loop ()
  114. (let ((player (make-instance
  115. 'player-character
  116. :codepoint #x263a
  117. :location 0)))
  118. (loop with map = (initialize-map (make-instance 'game-map) player)
  119. with viewport = (create-viewport)
  120. as key = (terminal-read) do
  121. (handle-input key map viewport player)
  122. (terminal-clear)
  123. (write-map-to-terminal map viewport)
  124. (terminal-refresh)
  125. until (or (= key bearlibterminal-ffi:+tk-close+)
  126. (= key bearlibterminal-ffi:+tk-escape+)))))
  127. (defun start ()
  128. (terminal-open)
  129. (terminal-set "window: size=100x40, title='Crypts and Corpses'")
  130. (terminal-set "font: ./fonts/DejaVuSansMono.ttf, size=16")
  131. (intro-message)
  132. (main-loop)
  133. (terminal-close))