Selaa lähdekoodia

Make AI happen

Lily Carpenter 9 vuotta sitten
vanhempi
commit
72f63249a5
1 muutettua tiedostoa jossa 132 lisäystä ja 32 poistoa
  1. 132 32
      src/crypts-and-corpses.lisp

+ 132 - 32
src/crypts-and-corpses.lisp

@@ -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)