-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathinterface.lisp
200 lines (175 loc) · 7.93 KB
/
interface.lisp
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
(in-package #:org.shirakumo.maiden.agents.crimes)
(define-consumer crimes (agent)
((games :initform () :accessor games)))
(defmethod reply ((game game) message &rest args)
(apply #'reply (channel game) message args))
(defmethod reply ((player player) message &rest args)
(apply #'reply (user player) message args))
(defun find-game (c ev &optional (error T))
(or (find (channel ev) (games c) :key #'channel)
(when error (error "No crimes game going on here."))))
(defun find-player (c ev &optional (error T))
(let ((game (user-game c ev error)))
(when game
(or (find (user ev) (players game) :key #'user)
(when error (error "You are not part of this game."))))))
(defun user-game (c ev &optional (error T))
(or (loop for game in (games c)
do (when (loop for player in (players game)
thereis (eql (user ev) (user player)))
(return game)))
(when error (error "You are not part of this game."))))
(defun handle-next (game)
(cond ((in-session game)
(reply game "~a is the officer!" (name (user (officer game))))
(reply game "Prompt: ~a" (text (result (officer game))))
(dolist (player (players game))
(unless (eql player (officer game))
(reply player "Prompt: ~a" (text (result (officer game))))
(loop for card in (hand player)
for i from 0
do (reply player "(~a) ~a" i (text card))))))
(T
(multiple-value-bind (winner score) (winner game)
(reply game "The game is over.~@[ The winner is ~a with ~a point~:p!~]"
(when winner (name winner)) score)))))
(defun handle-complete (game)
(when (complete-p game)
(reply game "~a, time to convict a criminal."
(name (user (officer game))))
(loop for num in (scrambled game)
for player = (elt (players game) num)
for i from 0
do (reply game "(~a): ~a" i (text (result player))))))
(define-command (crimes open-game) (c ev &key (winning-score "7") (hand-size "10"))
:command "open crimes"
(when (find-game c ev NIL)
(error "A game is already going on."))
(let ((game (make-instance 'game
:channel (channel ev)
:win-score (parse-integer winning-score)
:hand-size (parse-integer hand-size))))
(push game (games c))
(reply ev "Game opened. You can join with \"join crimes\", ~
add decks with \"add crimes deck\", ~
and finally start with \"start crimes\".")))
(define-command (crimes add-deck) (c ev name/id)
:command "add crimes deck"
(let ((game (find-game c ev)))
(unless (find (user ev) (players game) :key #'user)
(error "You are not a player of this game."))
(let ((deck (or (ignore-errors (deck name/id))
(ignore-errors (load-cardcast-deck name/id))
(error "No deck ~a found locally or on cardcast." name/id))))
;; Cache deck
(setf (deck (name deck)) deck)
(add-deck deck game)
(reply game "Deck ~a has been added. The game now has ~a call~:p and ~a response~:p."
(name deck) (length (calls game)) (length (responses game))))))
(define-command (crimes start-game) (c ev)
:command "start crimes"
(let ((game (find-game c ev)))
(unless (find (user ev) (players game) :key #'user)
(error "You are not a player of this game."))
(start game)
(dolist (player (players game))
(reply player "Reminder: Use \"commit crime\" with the number of the response you want to use next. Some calls require multiple responses."))
(handle-next game)))
(define-command (crimes end-game) (c ev)
:command "end crimes"
(let ((game (find-game c ev)))
(unless (find (user ev) (players game) :key #'user)
(error "You are not a player of this game."))
(when (in-session game)
(end game)
(handle-next game))
(setf (games c) (remove game (games c)))))
(define-command (crimes join-game) (c ev)
:command "join crimes"
(when (user-game c ev NIL)
(error "You are already participating in a game!"))
(let ((game (find-game c ev)))
(join (user ev) game)
(reply ev "Welcome, ~a. We now have ~a player~:p."
(name (user ev)) (length (players game)))))
(define-command (crimes leave-game) (c ev)
:command "leave crimes"
(unless (user-game c ev NIL)
(error "You are not a player of this game."))
(leave (user ev) (find-game c ev))
(reply ev "Ok. See you later, ~a" (name (user ev))))
(define-command (crimes submit-card) (c ev &rest cards)
:command "commit crime"
(let* ((game (user-game c ev))
(player (find-player c ev))
(result (result player)))
(dolist (card cards)
(submit (parse-integer card) player game))
(reply player "Your submission: ~a" (text result))
(if (complete-p result)
(reply player "Your submission is complete.")
(reply player "~a card~:p left to submit." (remaining-responses result)))
(handle-complete game)))
(define-command (crimes select-winner) (c ev winning-number)
:command "convict criminal"
(let ((game (user-game c ev)))
(unless (eql (user ev) (user (officer game)))
(error "Only the officer (~a) may convict a criminal."
(name (user (officer game)))))
(unless (complete-p game)
(error "Not everyone has submitted their crimes yet! Stay patient."))
(let ((winner (finish-round (parse-integer winning-number) game)))
(reply game "~a (~a) has been convicted! Their score is now at ~a point~:p."
(name (user winner)) winning-number (score winner)))
(handle-next game)))
(define-command (crimes create-deck) (c ev name &optional (title ""))
:command "create crime deck"
(let ((deck (ignore-errors (deck name))))
(when deck (error "A deck with the name ~a already exists." (name deck))))
(let ((deck (make-instance 'deck :name name :title title)))
(setf (deck name) deck)
(reply ev "Deck ~a created. Add cards to it with \"add crime call\" and \"add crime response\"."
(name deck))))
(define-command (crimes remove-deck) (c ev name)
:command "remove crime deck"
(let ((deck (deck name)))
(remove-deck deck)
(reply ev "Deck ~a removed." (name deck))))
(define-command (crimes list-decks) (c ev)
:command "list crime decks"
(let ((files (uiop:directory-files
(uiop:pathname-directory-pathname
(maiden-storage:config-pathname 'local-symbol)))))
(reply ev "Local decks: ~{~a~^, ~}" (mapcar #'pathname-name files))))
(define-command (crimes search-deck) (c ev search)
:command "search for crime deck"
(let ((decks (find-cardcast-decks search)))
(if decks
(reply ev "Cardcast decks found: ~{~{~a (~a)~}~^, ~}" decks)
(reply ev "No decks for that term found on cardcast."))))
(define-command (crimes download-deck) (c ev id &optional new-name)
:command "download crime deck"
(let* ((deck (load-cardcast-deck id))
(name (or new-name (name deck))))
(let ((deck (ignore-errors (deck name))))
(when deck (error "A deck with the name ~a already exists." (name deck))))
(setf (deck name) deck)
(reply ev "Cardcast deck downloaded to name ~a." (name deck))))
(define-command (crimes add-call) (c ev deck &string text)
:command "add crime call"
(let* ((deck (deck deck))
(card (add-call text deck)))
(save-deck deck)
(reply ev "Card ~a added to ~a." (id card) (name deck))))
(define-command (crimes add-response) (c ev deck &string text)
:command "add crime response"
(let* ((deck (deck deck))
(card (add-call text deck)))
(save-deck deck)
(reply ev "Card ~a added to ~a." (id card) (name deck))))
(define-command (crimes remove-card) (c ev card deck)
:command "remove crime card"
(let ((deck (deck deck)))
(remove-card card deck)
(save-deck deck)
(reply ev "Card ~a removed from ~a." card (name deck))))