|
| 1 | +"Interface and logic for player commands and actions." |
| 2 | + |
| 3 | + |
1 | 4 | (require
|
2 | 5 | hyrule [case ecase ebranch do-n unless]
|
3 | 6 | simalq.macros [defdataclass])
|
|
20 | 23 | "Something the player wants to do, translated from user input.")
|
21 | 24 |
|
22 | 25 | (defdataclass Action [Command]
|
23 |
| - "A discrete effect that the player can have on the gamestate.") |
| 26 | + "A discrete effect that the player can have on the game state.") |
24 | 27 | (defdataclass Wait [Action]
|
25 | 28 | "Do nothing, passing your turn (or, more precisely, passing
|
26 | 29 | one opportunity for an action).")
|
|
60 | 63 | wait key) to get info on a tile under the cursor, and any other key
|
61 | 64 | to exit look mode. If there's more than one tile on the selected
|
62 | 65 | square, you'll be prompted to choose one when you try to get info.")
|
63 |
| - |
64 | 66 | (defdataclass ShiftHistory [Command]
|
65 | 67 | "{details}"
|
66 | 68 | ; Undo or redo.
|
|
89 | 91 | None)
|
90 | 92 |
|
91 | 93 |
|
92 |
| - |
93 | 94 | (defn do-command [cmd]
|
94 | 95 | "This function is only for commands that aren't actions; see
|
95 | 96 | `do-action` for actions."
|
|
187 | 188 | (ecase G.player.game-over-state
|
188 | 189 | 'dead "You're dead"
|
189 | 190 | 'won "You won the game")))))
|
190 |
| - (when (and (.player-has? StatusEffect.Para) (is-not (type action) Wait)) |
| 191 | + (when (and |
| 192 | + (.player-has? StatusEffect.Para) |
| 193 | + (is-not (type action) Wait)) |
191 | 194 | (raise (CommandError "You're paralyzed. You can only wait for it to pass.")))
|
192 | 195 |
|
193 | 196 | (ebranch (in (type action) it)
|
194 | 197 |
|
195 | 198 | [Wait]
|
196 |
| - ; Nothing to do. This action should always succeed. |
| 199 | + ; Nothing to do. This action should always be allowed. |
197 | 200 | None
|
198 | 201 |
|
199 | 202 | [Walk UseControllableTeleporter] (do
|
| 203 | + |
200 | 204 | (defn pat [pos]
|
201 | 205 | "`at`, but excluding tiles we should ignore due to passwall."
|
202 | 206 | (gfor
|
|
206 | 210 | tile.passwallable
|
207 | 211 | (.player-has? StatusEffect.Pass)))
|
208 | 212 | tile))
|
| 213 | + |
209 | 214 | (setv d action.direction)
|
210 | 215 | (setv [target wly] (walkability G.player.pos d :monster? F))
|
211 | 216 | (when (= wly 'out-of-bounds)
|
212 | 217 | (raise (CommandError "The border of the dungeon blocks your movement.")))
|
213 | 218 | (when (= wly 'blocked-diag)
|
214 | 219 | (raise (CommandError "That diagonal is blocked by a neighbor.")))
|
| 220 | + |
215 | 221 | (for [tile (pat target)]
|
216 | 222 | (when (.hook-player-bump tile G.player.pos)
|
217 | 223 | (return)))
|
218 | 224 | (when (= wly 'bump)
|
219 | 225 | (raise (CommandError "Your way is blocked.")))
|
| 226 | + |
220 | 227 | (for [tile (pat G.player.pos)]
|
221 | 228 | (.hook-player-walk-from tile target))
|
222 | 229 | (for [tile (pat target)]
|
223 | 230 | (.hook-player-walk-to tile G.player.pos))
|
| 231 | + |
224 | 232 | ; No exceptions have stopped us, so go.
|
225 | 233 | (setv pos-was G.player.pos)
|
226 | 234 | (.move G.player target)
|
|
231 | 239 | (return))))
|
232 | 240 |
|
233 | 241 | [Shoot] (do
|
| 242 | + |
234 | 243 | (setv
|
235 | 244 | d action.direction
|
236 | 245 | target G.player.pos
|
|
239 | 248 | (when G.player.magic-arrows
|
240 | 249 | (setv magic? T)
|
241 | 250 | (-= G.player.magic-arrows 1))
|
| 251 | + |
242 | 252 | (defn animate []
|
243 | 253 | (flash-map :flash-time-s .1
|
244 | 254 | G.player.pos
|
|
247 | 257 | color.flash-player-shot-mundane)
|
248 | 258 | targets
|
249 | 259 | {}))
|
| 260 | + |
250 | 261 | (do-n G.rules.reality-bubble-size
|
251 | 262 | (setv target (+ target d))
|
252 | 263 | (unless target
|
|
277 | 288 | (unless (and magic? (!= tile.pos target))
|
278 | 289 | (return)))
|
279 | 290 | tile.blocks-player-shots
|
280 |
| - ; The arrow won't be able to leave this square, although |
281 |
| - ; it can affect other tiles in the square. |
| 291 | + ; The arrow can't leave this square, although it can |
| 292 | + ; affect other tiles in the square. |
282 | 293 | (setv blocked T)))
|
283 | 294 | (when blocked
|
284 | 295 | (animate)
|
|
302 | 313 | (when target
|
303 | 314 | (raise (CommandError "That item can't use a target.")))
|
304 | 315 | (.use item)))
|
| 316 | + ; All usable items are destroyed after one use. |
305 | 317 | (setv (get G.player.inventory action.item-ix) None))))
|
306 | 318 |
|
307 | 319 |
|
|
312 | 324 | should then end.
|
313 | 325 |
|
314 | 326 | Return a selected `Pos` or `None`."
|
| 327 | + |
315 | 328 | (setv focus G.player.pos)
|
316 | 329 | (hy.I.simalq/main.io-mode
|
317 | 330 | :draw (fn []
|
318 |
| - (hy.I.simalq/main.print-main-screen focus :status-bar F :tile-list 'nonpickable)) |
| 331 | + (hy.I.simalq/main.print-main-screen |
| 332 | + focus :status-bar F :tile-list 'nonpickable)) |
319 | 333 | :on-input (fn [key]
|
320 | 334 | (nonlocal focus)
|
321 | 335 | (setv dir-v (hy.I.simalq/keyboard.read-dir-key key))
|
|
0 commit comments