|
@@ -33,20 +33,22 @@
|
33
|
33
|
((width :initarg :width :type fixnum :initform 1)
|
34
|
34
|
(height :initarg :height :type fixnum :initform 1)
|
35
|
35
|
(location :initarg :location :initform 0 :type fixnum :reader get-location)
|
36
|
|
- (color :initarg :color :initform (color-from-name "#ffffff"))))
|
|
36
|
+ (color :initarg :color :initform (color-from-name "#ffffff"))
|
|
37
|
+ (blocks-light :initform nil :reader get-blocks-light)))
|
37
|
38
|
|
38
|
39
|
(defclass tile-entity (entity)
|
39
|
40
|
((codepoint :initarg :codepoint :initform #x3f :type fixnum)))
|
40
|
41
|
|
41
|
42
|
(defclass wall (tile-entity)
|
42
|
43
|
((codepoint :initarg :codepoint :initform #x23 :type fixnum)
|
43
|
|
- (color :initarg :color :initform (color-from-name "#999999"))))
|
|
44
|
+ (color :initarg :color :initform (color-from-name "#999999"))
|
|
45
|
+ (blocks-light :initform t)))
|
44
|
46
|
|
45
|
47
|
(defclass floor-tile (tile-entity)
|
46
|
48
|
((codepoint :initarg :codepoint :initform #xb7 :type fixnum)
|
47
|
49
|
(color :initarg :color :initform (color-from-name "#3f3f3f"))
|
48
|
|
- (player-map-value :initform nil)
|
49
|
|
- (enemy-map-value :initform nil)))
|
|
50
|
+ (player-map-value :initform nil :reader get-player-map-value)
|
|
51
|
+ (enemy-map-value :initform nil :reader get-enemy-map-value)))
|
50
|
52
|
|
51
|
53
|
(defclass viewport (entity)
|
52
|
54
|
((width :initarg :width :type fixnum :initform 99)
|
|
@@ -59,7 +61,8 @@
|
59
|
61
|
(max-health :initarg :health :type fixnum :initform 10)
|
60
|
62
|
(max-mana :initarg :mana :type fixnum :initform 10)
|
61
|
63
|
(health-regen :initarg :health-regen :initform 0 :type fixnum)
|
62
|
|
- (mana-regen :initarg :mana-regen :initform 0 :type fixnum)))
|
|
64
|
+ (mana-regen :initarg :mana-regen :initform 0 :type fixnum)
|
|
65
|
+ (vision-range :initform 4 :initarg :vision-rage :type fixnum :reader get-vision-range)))
|
63
|
66
|
|
64
|
67
|
(defclass player-character (mobile)
|
65
|
68
|
((health :initarg :health :type fixnum :initform 10)
|
|
@@ -73,18 +76,19 @@
|
73
|
76
|
|
74
|
77
|
(defclass enemy (mobile)
|
75
|
78
|
((codepoint :initform #x263a)
|
76
|
|
- (melee-attack-damage :initarg :ranged-attack-damage :initform 1 :type fixnum)
|
|
79
|
+ (melee-attack-damage :initarg :melee-attack-damage :initform 1 :type fixnum)
|
77
|
80
|
(health-regen :initarg :health-regen :initform 1 :type fixnum)
|
78
|
81
|
(color :initarg :color :initform (color-from-name "#1919ff"))))
|
79
|
82
|
|
80
|
83
|
(defclass tomb (floor-tile)
|
81
|
84
|
((codepoint :initform #x2616)
|
82
|
|
- (color :initform (color-from-name "yellow"))
|
|
85
|
+ (color :initform (color-from-name "#ffff00"))
|
83
|
86
|
(awakened :initform nil :reader awakened-p :accessor awakened)))
|
84
|
87
|
|
85
|
88
|
(defclass minion (mobile)
|
86
|
|
- ((color :initarg :color :initform (color-from-name "yellow"))
|
87
|
|
- (codepoint :initform #x265f)))
|
|
89
|
+ ((color :initarg :color :initform (color-from-name "#ffff00"))
|
|
90
|
+ (codepoint :initform #x2639)
|
|
91
|
+ (melee-attack-damage :initform 1 :type fixnum )))
|
88
|
92
|
|
89
|
93
|
(defun calc-coord (location map)
|
90
|
94
|
(values (mod location (game-map-width map)) (floor (/ location (game-map-width map)))))
|
|
@@ -308,13 +312,19 @@
|
308
|
312
|
(maphash #'write-cell (game-map-terrain map))
|
309
|
313
|
(maphash #'write-cell (game-map-mobiles map)))))
|
310
|
314
|
|
|
315
|
+(defun adjacent-open-tiles (tile map)
|
|
316
|
+ (when tile
|
|
317
|
+ (let* ((mobile-neighbors (mapcar #'get-location (get-neighbors tile map (game-map-mobiles map))))
|
|
318
|
+ (terrain-neighbors (mapcar #'get-location (get-neighbors tile map (game-map-terrain map) :filter-fun #'walkablep))))
|
|
319
|
+ (mapcar (lambda (item) (gethash item (game-map-terrain map)))
|
|
320
|
+ (remove-if (lambda (item) (find item mobile-neighbors))
|
|
321
|
+ terrain-neighbors)))))
|
|
322
|
+
|
311
|
323
|
;; TODO: Update to be able to handle any distance, not just 1
|
|
324
|
+;; TODO: Update to include `tile' for consideration
|
312
|
325
|
(defun nearest-open-tile (tile map)
|
313
|
326
|
(declare (type entity tile))
|
314
|
|
- (let* ((mobile-neighbors (mapcar #'get-location (get-neighbors tile map (game-map-mobiles map))))
|
315
|
|
- (terrain-neighbors (mapcar #'get-location (get-neighbors tile map (game-map-terrain map) :filter-fun #'walkablep)))
|
316
|
|
- (available-neighbors (remove-if (lambda (item) (find item mobile-neighbors)) terrain-neighbors)))
|
317
|
|
- (first available-neighbors)))
|
|
327
|
+ (first (adjacent-open-tiles tile map)))
|
318
|
328
|
|
319
|
329
|
(defun in-room-p (location map room)
|
320
|
330
|
(declare (type crawler::dungeon-room room))
|
|
@@ -326,8 +336,9 @@
|
326
|
336
|
(<= y y2)))))
|
327
|
337
|
|
328
|
338
|
(defun find-room (location map)
|
329
|
|
- (labels ((location-in-room-p (room) (in-room-p location map room)))
|
330
|
|
- (find-if #'location-in-room-p (slot-value (slot-value map 'dungeon) 'crawler::rooms))))
|
|
339
|
+ (when location
|
|
340
|
+ (labels ((location-in-room-p (room) (in-room-p location map room)))
|
|
341
|
+ (find-if #'location-in-room-p (slot-value (slot-value map 'dungeon) 'crawler::rooms)))))
|
331
|
342
|
|
332
|
343
|
(defun find-arc (x y x0 y0 map)
|
333
|
344
|
(labels ((calc-location-map (x y) (calc-location x y map)))
|
|
@@ -448,7 +459,7 @@
|
448
|
459
|
(loop for room in (crawler::rooms (game-map-dungeon map))
|
449
|
460
|
when (not (equal room (find-room (get-location player) map))) do
|
450
|
461
|
(dotimes (times (random 3))
|
451
|
|
- (let* ((location (nearest-open-tile (gethash (room-center room map) (game-map-terrain map)) map))
|
|
462
|
+ (let* ((location (get-location (nearest-open-tile (gethash (room-center room map) (game-map-terrain map)) map)))
|
452
|
463
|
(enemy (make-instance 'enemy :location location)))
|
453
|
464
|
(when location
|
454
|
465
|
(setf (gethash location (game-map-mobiles map)) enemy))))))
|
|
@@ -456,7 +467,7 @@
|
456
|
467
|
(defun spawn-tombs (map)
|
457
|
468
|
(loop for room in (crawler::rooms (game-map-dungeon map)) do
|
458
|
469
|
(when (= 2 (random 3))
|
459
|
|
- (let* ((location (nearest-open-tile (gethash (room-center room map) (game-map-terrain map)) map))
|
|
470
|
+ (let* ((location (get-location (nearest-open-tile (gethash (room-center room map) (game-map-terrain map)) map)))
|
460
|
471
|
(tomb (make-instance 'tomb :location location)))
|
461
|
472
|
(when location
|
462
|
473
|
(setf (gethash location (game-map-terrain map)) tomb))))))
|
|
@@ -492,8 +503,9 @@
|
492
|
503
|
;; Insert all source floor tiles in queue with priority and slot value zero
|
493
|
504
|
(mapc (lambda (item)
|
494
|
505
|
(with-slots (location) item
|
495
|
|
- (setf (get-slot (gethash location tiles)) 0)
|
496
|
|
- (cl-heap:enqueue pending (gethash location tiles) 0))) sources)
|
|
506
|
+ (when location
|
|
507
|
+ (setf (get-slot (gethash location tiles)) 0)
|
|
508
|
+ (cl-heap:enqueue pending (gethash location tiles) 0)))) sources)
|
497
|
509
|
(loop as tile = (cl-heap:dequeue pending)
|
498
|
510
|
while tile do
|
499
|
511
|
(loop with value = (1+ (get-slot tile))
|
|
@@ -554,21 +566,22 @@
|
554
|
566
|
collect pair))))
|
555
|
567
|
|
556
|
568
|
(defun spawn-minion (tomb map)
|
557
|
|
- (let* ((location (nearest-open-tile tomb map))
|
|
569
|
+ (let* ((location (get-location (nearest-open-tile tomb map)))
|
558
|
570
|
(minion (make-instance 'minion :location location)))
|
559
|
571
|
(setf (gethash location (game-map-mobiles map)) minion)))
|
560
|
572
|
|
561
|
|
-(defun wake-up-the-dead (map player)
|
562
|
|
- (let ((player-room (find-room (get-location player) map))
|
563
|
|
- (tomb-rooms (find-tomb-rooms-with-tomb map)))
|
564
|
|
- (loop for tomb-room in tomb-rooms
|
565
|
|
- when (and player-room
|
566
|
|
- tomb-room
|
567
|
|
- (equal player-room (cadr tomb-room))
|
568
|
|
- (not (awakened (car tomb-room)))) do
|
569
|
|
- (let ((tomb (car tomb-room)))
|
570
|
|
- (setf (awakened tomb) t)
|
571
|
|
- (spawn-minion tomb map)))))
|
|
573
|
+(defun wake-the-dead (map player)
|
|
574
|
+ (when (get-location player)
|
|
575
|
+ (let ((player-room (find-room (get-location player) map))
|
|
576
|
+ (tomb-rooms (find-tomb-rooms-with-tomb map)))
|
|
577
|
+ (loop for tomb-room in tomb-rooms
|
|
578
|
+ when (and player-room
|
|
579
|
+ tomb-room
|
|
580
|
+ (equal player-room (cadr tomb-room))
|
|
581
|
+ (not (awakened (car tomb-room)))) do
|
|
582
|
+ (let ((tomb (car tomb-room)))
|
|
583
|
+ (setf (awakened tomb) t)
|
|
584
|
+ (spawn-minion tomb map))))))
|
572
|
585
|
|
573
|
586
|
(defun end-of-turn-tasks (player map)
|
574
|
587
|
(maphash (lambda (location object)
|
|
@@ -578,7 +591,7 @@
|
578
|
591
|
(setf (slot-value object 'location) nil)
|
579
|
592
|
(remhash location (game-map-mobiles map)))
|
580
|
593
|
(end-of-turn object)))) (game-map-mobiles map))
|
581
|
|
- (wake-up-the-dead map player)
|
|
594
|
+ (wake-the-dead map player)
|
582
|
595
|
(make-dijkstra-map (list player) 'player-map-value (game-map-terrain map))
|
583
|
596
|
(make-dijkstra-map
|
584
|
597
|
(remove-if-not #'enemy-p
|
|
@@ -596,6 +609,92 @@
|
596
|
609
|
(slot-value player 'mana)
|
597
|
610
|
x y)))))
|
598
|
611
|
|
|
612
|
+(defun ally-p (mobile)
|
|
613
|
+ (typep mobile 'minion))
|
|
614
|
+
|
|
615
|
+(defun bash (basher bashee)
|
|
616
|
+ (decf (slot-value bashee 'health) (slot-value basher 'melee-attack-damage)))
|
|
617
|
+
|
|
618
|
+(defun can-see-player-p (mobile map player)
|
|
619
|
+ (multiple-value-bind (mob-x mob-y) (calc-coord (get-location mobile) map)
|
|
620
|
+ (multiple-value-bind (player-x player-y) (calc-coord (get-location player) map)
|
|
621
|
+ (or (equal (find-room (get-location mobile) map)
|
|
622
|
+ (find-room (get-location player) map))
|
|
623
|
+ (let ((line (ortho-line mob-x mob-y player-x player-y))
|
|
624
|
+ (max (get-vision-range mobile)))
|
|
625
|
+ (loop with counter = 0
|
|
626
|
+ as current = (pop line)
|
|
627
|
+ while (and current (< counter max))
|
|
628
|
+ as cur-loc = (calc-location (first current) (cadr current) map)
|
|
629
|
+ as terrain = (gethash cur-loc (game-map-terrain map))
|
|
630
|
+ as mobile = (gethash cur-loc (game-map-mobiles map))
|
|
631
|
+ while (and (or (not terrain) (not (get-blocks-light terrain)))
|
|
632
|
+ (or (not mobile) (not (get-blocks-light mobile))))
|
|
633
|
+ finally (return (equal player mobile))))))))
|
|
634
|
+
|
|
635
|
+(defun move-toward-enemy (mobile map)
|
|
636
|
+ (let* ((tiles (adjacent-open-tiles mobile map))
|
|
637
|
+ (best-tile (first (sort tiles #'< :key #'get-enemy-map-value))))
|
|
638
|
+ (when best-tile
|
|
639
|
+ (remhash (get-location mobile) (game-map-mobiles map))
|
|
640
|
+ (setf (slot-value mobile 'location) (get-location best-tile))
|
|
641
|
+ (setf (gethash (get-location best-tile) (game-map-mobiles map)) mobile))))
|
|
642
|
+
|
|
643
|
+(defun distance-to-player (mobile map player &key (max 100))
|
|
644
|
+ (let* ((terrain (game-map-terrain map))
|
|
645
|
+ (tile (gethash (get-location mobile) terrain))
|
|
646
|
+ (player-tile (gethash (get-location player) terrain)))
|
|
647
|
+ (loop with counter = 0
|
|
648
|
+ while (and (not (equal tile player-tile)) (< counter max)) do
|
|
649
|
+ (incf counter)
|
|
650
|
+ (setf tile (first (sort (adjacent-open-tiles tile map) #'< :key #'get-player-map-value)))
|
|
651
|
+ finally (return counter))))
|
|
652
|
+
|
|
653
|
+(defun move-ally-toward-player (mobile map player)
|
|
654
|
+ (when (< 3 (distance-to-player mobile map player :max 3))
|
|
655
|
+ (move-toward-player mobile map)))
|
|
656
|
+
|
|
657
|
+(defun move-toward-player (mobile map)
|
|
658
|
+ (let* ((tiles (adjacent-open-tiles mobile map))
|
|
659
|
+ (best-tile (first (sort tiles #'< :key #'get-player-map-value))))
|
|
660
|
+ (when best-tile
|
|
661
|
+ (remhash (get-location mobile) (game-map-mobiles map))
|
|
662
|
+ (setf (slot-value mobile 'location) (get-location best-tile))
|
|
663
|
+ (setf (gethash (get-location best-tile) (game-map-mobiles map)) mobile))))
|
|
664
|
+
|
|
665
|
+(defun adjacent-enemies (mobile map)
|
|
666
|
+ (get-neighbors mobile map (game-map-mobiles map) :filter-fun #'enemy-p))
|
|
667
|
+
|
|
668
|
+(defun adjacent-allies-player (mobile map)
|
|
669
|
+ (get-neighbors mobile map (game-map-mobiles map) :filter-fun (lambda (mobile) (or (typep mobile 'player-character) (typep mobile 'minion)))))
|
|
670
|
+
|
|
671
|
+(defun ally-think-and-do (mobile player)
|
|
672
|
+ (declare (type minion mobile))
|
|
673
|
+ (let ((adjacent-enemy (first (adjacent-enemies mobile map))))
|
|
674
|
+ (cond
|
|
675
|
+ (adjacent-enemy (bash mobile adjacent-enemy))
|
|
676
|
+ ((can-see-player-p mobile map player) (move-toward-enemy mobile map))
|
|
677
|
+ (:otherwise (move-ally-toward-player mobile map player)))))
|
|
678
|
+
|
|
679
|
+(defun enemy-think-and-do (mobile player)
|
|
680
|
+ (declare (type enemy mobile))
|
|
681
|
+ (let ((adjacent-enemy (first (adjacent-allies-player mobile map))))
|
|
682
|
+ (cond
|
|
683
|
+ (adjacent-enemy (bash mobile adjacent-enemy))
|
|
684
|
+ ((can-see-player-p mobile map player) (move-toward-player mobile map)))))
|
|
685
|
+
|
|
686
|
+(defun allies-do-stuff (map player)
|
|
687
|
+ (labels ((curried-ally-think-and-do (mobile) (ally-think-and-do mobile player)))
|
|
688
|
+ (mapc #'curried-ally-think-and-do (remove-if-not #'ally-p (alexandria:hash-table-values (game-map-mobiles map))))))
|
|
689
|
+
|
|
690
|
+(defun enemies-do-stuff (map player)
|
|
691
|
+ (labels ((curried-enemy-think-and-do (mobile) (enemy-think-and-do mobile player)))
|
|
692
|
+ (mapc #'curried-enemy-think-and-do (remove-if-not #'enemy-p (alexandria:hash-table-values (game-map-mobiles map))))))
|
|
693
|
+
|
|
694
|
+(defun ai-do-stuff (map player)
|
|
695
|
+ (allies-do-stuff map player)
|
|
696
|
+ (enemies-do-stuff map player))
|
|
697
|
+
|
599
|
698
|
(defun display-lose ()
|
600
|
699
|
(terminal-clear)
|
601
|
700
|
(terminal-print 0 0 "You lose!")
|
|
@@ -628,6 +727,7 @@
|
628
|
727
|
(setf prev-key key)
|
629
|
728
|
(unless (or (= key bearlibterminal-ffi:+tk-r+)
|
630
|
729
|
(= key bearlibterminal-ffi:+tk-m+))
|
|
730
|
+ (when (get-location player) (ai-do-stuff map player))
|
631
|
731
|
(end-of-turn-tasks player map))
|
632
|
732
|
(terminal-clear)
|
633
|
733
|
(write-map-to-terminal player map viewport)
|