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