aboutsummaryrefslogtreecommitdiff
path: root/src/macroknight/game.hy
blob: 1676e5a26476a79957f8f645eb0f360e93cb726f (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
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
(require hy)
(require hyrule *)
(import pygame
        pytmx.util_pygame [load_pygame]
        entities [Player LevelTile Goal]
        tiles [TileSet draw-tile]
        utils [neg]
        text [render-text]
        systems [apply-gravity apply-collisions GoalHit]
        math [floor])

(pygame.init)

(setv TILE_SCALING 1)
(setv TILE_SIZE (* TILE_SCALING 16))
(setv MACRO_STEP_WAIT 300)
(setv MACRO_COOLDOWN 2000)

(setv screen (pygame.display.set_mode #((* TILE_SCALING 640) (* TILE_SCALING 480))))
(setv clock (pygame.time.Clock))
(setv tileset (TileSet "assets/monochrome-transparent.png" TILE_SCALING TILE_SIZE TILE_SIZE 1))
(setv levels [(load_pygame "assets/level-0.tmx")
              (load_pygame "assets/level-1.tmx")])
(setv level-id 0)
(defn abs-to-tile-index [abs-id]
  (int (floor (/ abs-id TILE_SIZE))))

(setv game-running True)

(while game-running

  ;; Load the level
  (setv running True)
  (setv level (get levels level-id))
  (setv entities [])
  (setv player-pos #(5 5))
  (for [item (get level.layers 1)]
    (let [tile-x (abs-to-tile-index item.x)
          tile-y (abs-to-tile-index item.y)]
      (case item.type
            "Player" (setv player-pos #(tile-x tile-y))
            "Goal" (.append entities
                            (Goal (len entities)
                                  (get tileset.tiles 0)
                                  TILE_SIZE
                                  (abs-to-tile-index item.x)
                                  (abs-to-tile-index item.y))))))
  
  (setv player-pos
        (let [player-objects (lfor ent (get level.layers 1) :if (= ent.type "Player") ent)]
          (if (any player-objects)
              (let [player-object (get player-objects 0)]
                #((abs-to-tile-index player-object.x)
                   (abs-to-tile-index player-object.y)))
              #(5 5))))
  (setv player (Player 1 (get tileset.tiles 28) TILE_SIZE #* player-pos))
  (.append entities player)
  (setv macro-input-mode False)
  (setv macro-wait-time 0)
  (setv macro-commands [None None None])
  (let [id-offset (len entities)]
    (for [#(id tiledef) (enumerate (.tiles (get level.layers 0)))]
      (let [x (get tiledef 0)
            y (get tiledef 1)
            tile (get tiledef 2)]
        (.append entities (LevelTile (+ id id-offset) tile TILE_SIZE x y TILE_SCALING)))))

  (setv ongoing_inputs [])

  (while running
    (for [event (pygame.event.get)]
      (case event.type
            pygame.QUIT (do
                          (setv running False)
                          (setv game-running False))
            pygame.KEYDOWN (if (= event.key pygame.K_ESCAPE)
                               (do
                                 (setv running False)
                                 (setv game-running False))
                               (if macro-input-mode
                                   (when (in event.key [pygame.K_a pygame.K_w pygame.K_a pygame.K_s pygame.K_d])
                                     (setv (get macro-commands (.index macro-commands None)) event.key))
                                   (if (and (= event.key pygame.K_SPACE) (= macro-wait-time 0))
                                       (setv macro-input-mode True)
                                       (.append ongoing_inputs event.key))))
            pygame.KEYUP (when (in event.key ongoing_inputs)
                           (.remove ongoing_inputs event.key))))

    (.fill screen "#000000")

    ;; Render special objects
    (for [item (get level.layers 1)]
      (case item.type 
            "Text" (render-text screen
                                tileset
                                (.upper item.text)
                                (abs-to-tile-index item.x)
                                (abs-to-tile-index item.y))))

    (if macro-input-mode
        ;; If the commands list is full
        (if (get macro-commands -1)
            ;; Process commands
            (do
              (let [#(command-id command) (get (lfor command (enumerate macro-commands) :if (get command 1) command) 0)]
                (case command
                      pygame.K_a (.move player #((neg (* 2 TILE_SIZE)) 0))
                      pygame.K_s (.move player #(0 TILE_SIZE))
                      pygame.K_w (.move player #(0 (neg (/ player.MAX_JUMPING 2))))
                      pygame.K_d (.move player #((* 2 TILE_SIZE) 0)))
                (if (= command-id (- (len macro-commands) 1))
                    (do
                      (setv macro-commands [None None None])
                      (setv macro-input-mode False)
                      (setv macro-wait-time (+ (pygame.time.get_ticks) MACRO_COOLDOWN)))
                    (setv (get macro-commands command-id) None)))
              (pygame.time.wait MACRO_STEP_WAIT))

            ;; If there's still space in the commands list
            (for [#(num command) (enumerate macro-commands)]
              (let [x-pos (+ 4 num)]
                (case command
                      pygame.K_w (draw-tile screen tileset 1057 x-pos 5)
                      pygame.K_d (draw-tile screen tileset 1058 x-pos 5)
                      pygame.K_s (draw-tile screen tileset 1059 x-pos 5)
                      pygame.K_a (draw-tile screen tileset 1060 x-pos 5)
                      None (draw-tile screen tileset 725 x-pos 5)))))

        ;; Not in macro mode
        (do
          (when (> macro-wait-time 0)
            (let [progress (round (* 3 (/ (- macro-wait-time (pygame.time.get_ticks)) MACRO_COOLDOWN)))]
              (for [indicator (range progress)]
                (draw-tile screen tileset 725 (+ 4 indicator) 5))))
          (for [inp ongoing_inputs]
            (case inp
                  pygame.K_a (.move player #((neg player.SPEED) 0))
                  pygame.K_s (.move player #(0 1))
                  pygame.K_w (.jump player)
                  pygame.K_d (.move player #(player.SPEED 0))))

          (try
            (when (any ongoing_inputs)
              (for [entity entities]
                (apply-collisions entity entities)))

            (.flush player)

            ;; Apply systems
            (for [entity entities]
              (apply-gravity entity entities)
              (apply-collisions entity entities))
            
            (except [GoalHit]
              (setv level-id (+ level-id 1))
              (setv running False)
              (setv game-running (< level-id (len levels)))))

          (.flush player)))


    (for [entity entities]
      (.blit screen entity.surf entity.rect))

    (pygame.display.flip)

    (when (and (!= 0 macro-wait-time)
               (> (pygame.time.get_ticks) macro-wait-time))
      (setv macro-wait-time 0))

    (.tick clock 60)))

(do
  (.fill screen "#000000")

  (render-text screen
               tileset
               "YOU WIN"
               10
               10)

  (pygame.display.flip)
  (pygame.time.wait 1000))

(pygame.quit)