blob: e6c1ec1b42c1ccb699076a49e51b0f881aef12bf (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
(module downstroke/ai *
(import scheme
(chicken base)
(chicken keyword)
(only srfi-1 find)
states
downstroke/entity
downstroke/world)
;; Patrol speed in pixels per frame
(define *patrol-speed* 1)
;; Chase behavior constants
(define *chase-speed* 2)
(define *detect-range-x* 80)
(define *detect-range-y* 16)
(define *chase-max-distance* 96)
(define *blind-spot-x* 16)
(define *attack-range-x* 20)
(define *attack-duration* 10)
;; ---- Geometry helpers ----
(define (entity-center-x e)
(+ (entity-ref e #:x 0) (/ (entity-ref e #:width 0) 2)))
(define (entity-center-y e)
(+ (entity-ref e #:y 0) (/ (entity-ref e #:height 0) 2)))
(define (direction-to entity target)
(if (>= (entity-center-x target) (entity-center-x entity)) 1 -1))
;; ---- Entity update helpers ----
;; Find player by #:tags list (tag-based lookup, Milestone 10)
(define (find-player entities)
(find (lambda (e) (member 'player (entity-ref e #:tags '()))) entities))
;; Set both facing fields in the same direction
(define (face-toward entity dir)
(entity-set (entity-set entity #:ai-facing dir) #:facing dir))
;; Face a direction and set horizontal velocity
(define (move-toward entity dir speed)
(entity-set (face-toward entity dir) #:vx (* speed dir)))
;; ---- Detection predicates ----
(define (player-in-range? entity entities)
(let ((player (find-player entities)))
(and player
(let ((dx (abs (- (entity-center-x player) (entity-center-x entity))))
(dy (abs (- (entity-center-y player) (entity-center-y entity)))))
(and (<= dx *detect-range-x*)
(<= dy *detect-range-y*)
(not (and (< (entity-center-y player) (entity-center-y entity))
(<= dx *blind-spot-x*))))))))
(define (player-in-attack-range? entity entities)
(let ((player (find-player entities)))
(and player
(<= (abs (- (entity-center-x player) (entity-center-x entity))) *attack-range-x*)
(<= (abs (- (entity-center-y player) (entity-center-y entity))) *detect-range-y*))))
;; ---- State machine ----
(define (idle-trigger) #f)
(define (patrol-trigger) #f)
(define (chase-trigger) #f)
(define (make-enemy-ai-machine)
(state-machine idle
(idle-trigger ! idle -> patrol)
(patrol-trigger ! patrol -> chase)
(chase-trigger ! chase -> patrol)))
;; ---- Per-state handlers ----
(define (ai-state-idle entity sm)
(%make-transition sm 'patrol)
(entity-set entity #:vx (* *patrol-speed* (entity-ref entity #:ai-facing 1))))
(define (patrol-wall-flip entity)
(let ((new-facing (- (entity-ref entity #:ai-facing 1))))
(move-toward entity new-facing *patrol-speed*)))
(define (ai-state-patrol entity entities sm)
(if (player-in-range? entity entities)
(let* ((player (find-player entities))
(dir (direction-to entity player)))
(%make-transition sm 'chase)
(move-toward (entity-set entity #:chase-origin-x (entity-ref entity #:x 0))
dir *chase-speed*))
(if (zero? (entity-ref entity #:vx 0))
(patrol-wall-flip entity)
entity)))
(define (chase-give-up? entity entities)
(let ((distance-chased (abs (- (entity-ref entity #:x 0)
(entity-ref entity #:chase-origin-x 0)))))
(or (not (player-in-range? entity entities))
(> distance-chased *chase-max-distance*))))
(define (ai-state-chase entity entities sm)
(if (player-in-attack-range? entity entities)
(let* ((player (find-player entities))
(dir (direction-to entity player))
(stopped (entity-set (face-toward entity dir) #:vx 0)))
(if (= (entity-ref stopped #:attack-timer 0) 0)
(entity-set stopped #:attack-timer *attack-duration*)
stopped))
(if (chase-give-up? entity entities)
(begin
(%make-transition sm 'patrol)
(entity-set entity #:vx (* *patrol-speed* (entity-ref entity #:ai-facing 1))))
(move-toward entity
(direction-to entity (find-player entities))
*chase-speed*))))
;; ---- Top-level dispatcher ----
;; Update AI for a single enemy entity.
;; entities: all entities in the current scene (used for player detection).
(define (update-enemy-ai entity entities)
(if (entity-ref entity #:disabled #f)
entity
(let ((sm (entity-ref entity #:ai-machine #f)))
(if (not sm)
entity
(case (machine-state sm)
((idle) (ai-state-idle entity sm))
((patrol) (ai-state-patrol entity entities sm))
((chase) (ai-state-chase entity entities sm))
(else entity))))))
)
|