diff options
author | Gene Pasquet <dev@etenil.net> | 2025-05-18 08:38:25 +0100 |
---|---|---|
committer | Gene Pasquet <dev@etenil.net> | 2025-05-18 08:38:25 +0100 |
commit | 1cf28e193729ee64a17e6464ce7310c67f5eae34 (patch) | |
tree | 906ff27f5e533397f4f760a245d0606232a87b1f | |
parent | be71ead1e21d55049a18747f2d0d2159b11868f3 (diff) |
Progress on animations and enemies
-rw-r--r-- | assets/monochrome-transparent.png | bin | 19451 -> 26816 bytes | |||
-rw-r--r-- | macroknight.tiled-session | 36 | ||||
-rw-r--r-- | src/macroknight/entities.hy | 69 | ||||
-rw-r--r-- | src/macroknight/game.hy | 32 | ||||
-rw-r--r-- | src/macroknight/systems.hy | 66 | ||||
-rw-r--r-- | src/macroknight/utils.hy | 32 |
6 files changed, 172 insertions, 63 deletions
diff --git a/assets/monochrome-transparent.png b/assets/monochrome-transparent.png Binary files differindex 9f8187d..3102227 100644 --- a/assets/monochrome-transparent.png +++ b/assets/monochrome-transparent.png diff --git a/macroknight.tiled-session b/macroknight.tiled-session index 20c0f18..8aa14c8 100644 --- a/macroknight.tiled-session +++ b/macroknight.tiled-session @@ -3,38 +3,45 @@ "height": 4300, "width": 2 }, - "activeFile": "assets/level-0.tmx", + "activeFile": "assets/level-3.tmx", "expandedProjectPaths": [ ".", "assets" ], "fileStates": { "assets/level-0.tmx": { + "expandedObjectLayers": [ + 7 + ], "scale": 3, "selectedLayer": 1, "viewCenter": { - "x": 245.16666666666669, - "y": 330.16666666666663 + "x": 441.5, + "y": 363.5 } }, "assets/level-1.tmx": { "scale": 4, "selectedLayer": 0, "viewCenter": { - "x": 553.625, - "y": 290.375 + "x": 319.875, + "y": 240 } }, "assets/level-3.tmx": { - "scale": 2.1404687499999997, - "selectedLayer": 0, + "expandedObjectLayers": [ + 2 + ], + "scale": 2, + "selectedLayer": 1, "viewCenter": { - "x": 320.25695306226737, - "y": 240.3679100664282 + "x": 320.25, + "y": 240.25 } }, "assets/monochrome_transparent.tsx": { - "scaleInDock": 1 + "scaleInDock": 2, + "scaleInEditor": 2 }, "assets/tileset_colored.tsx": { "scaleInDock": 1 @@ -46,11 +53,16 @@ "map.tileWidth": 16, "map.width": 40, "openFiles": [ - "assets/level-0.tmx" + "assets/level-0.tmx", + "assets/monochrome_transparent.tsx", + "assets/level-1.tmx", + "assets/level-3.tmx" ], "project": "macroknight.tiled-project", "recentFiles": [ + "assets/level-0.tmx", + "assets/monochrome_transparent.tsx", "assets/level-1.tmx", - "assets/level-0.tmx" + "assets/level-3.tmx" ] } diff --git a/src/macroknight/entities.hy b/src/macroknight/entities.hy index c490b25..760aeca 100644 --- a/src/macroknight/entities.hy +++ b/src/macroknight/entities.hy @@ -1,16 +1,19 @@ +(require hyrule [case]) (import pygame.sprite [Sprite] pygame [Surface] pygame - utils [neg merge-moves]) + utils [neg merge-moves Direction] + enum [Enum]) (defclass Entity [Sprite] - ;;; Game entity +;;; Game entity (setv _fixed False) (setv _type "none") (defn __init__ [self id tile tile-size x y] (.__init__ (super)) (setv self.id id) + (setv self.tile-size tile-size) (setv self._surf (Surface #(tile-size tile-size))) (.blit self._surf tile #(0 0)) (setv self._rect (.get_rect self._surf @@ -27,7 +30,11 @@ self._surf) (defn [property] type [self] - self._type)) + self._type) + + (defn [property] pos [self] + #((/ self.rect.x self.tile-size) + (/ self.rect.y self.tile-size)))) (defclass LevelTile [Entity] (setv _fixed True) @@ -51,20 +58,29 @@ (setv JUMP_IMPULSE 10) (setv MAX_JUMPING 100) - (defn __init__ [self id tile tile-size x y] - (.__init__ (super) id tile tile-size x y) + (defn __init__ [self id tiles tile-size x y] + (.__init__ (super) id (get tiles 0) tile-size x y) + (setv self.tiles tiles) (setv self.jumping False) (setv self.jump-move 0) (setv self.moves []) - (setv self._disp_surf (.copy self._surf))) + (setv self._disp_surf (.copy self._surf)) + (setv self.facing Direction.RIGHT) + (setv self.attacking False) + (setv self.animate-end 0)) (defn move [self move] (.append self.moves move) - (.move_ip self._rect (get move 0) (get move 1))) + (.move_ip self._rect (get move 0) (get move 1)) + (setv self.facing + (.x-from-move Direction move))) (defn [property] total-move [self] (merge-moves self.moves)) + (defn attack [self] + (setv self.attacking True)) + (defn jump [self] (setv self.jumping True) (when (< self.jump-move self.MAX_JUMPING) @@ -74,13 +90,21 @@ (defn ground [self] (setv self.jump-move 0)) - (defn animate [self] - (let [x-move (get self.total-move 0)] - (setv self._disp_surf - (cond - (< x-move 0) (pygame.transform.flip self._surf True False) - (> x-move 0) (.copy self._surf) - True self._disp_surf)))) + (defn animate [self ticks] + ;; Attack animation + (when (and self.attacking (= self.animate-end 0)) + (setv self.animate-end (+ ticks 200)) + (.blit self._surf (get self.tiles 1) #(0 0))) + (when (and self.attacking (> ticks self.animate-end)) + (setv self.animate-end 0) + (setv self.attacking False) + (.blit self._surf (get self.tiles 0) #(0 0))) + + ;; Facing direction + (setv self._disp_surf + (case self.facing + Direction.LEFT (pygame.transform.flip self._surf True False) + Direction.RIGHT (.copy self._surf)))) (defn flush [self] (setv self.moves [])) @@ -89,21 +113,30 @@ self._disp_surf)) (defclass Enemy [Entity] - (setv _type "enemy")) + (setv _type "enemy") -(defclass Enemy2Swords [Enemy] (defn __init__ [self id tile tile-size x y] (.__init__ (super) id tile tile-size x y) + (setv self.facing Direction.RIGHT) + (setv self.attacking False) (.flush self)) - + (defn move [self move] (.append self.moves move) - (.move_ip self._rect (get move 0) (get move 1))) + (.move_ip self._rect (get move 0) (get move 1)) + (setv self.facing + (.x-from-move Direction move))) (defn flush [self] (setv self.moves [])) (defn ground [self]) + + (defn animate [self ticks] + (setv self._disp_surf + (case self.facing + Direction.LEFT (pygame.transform.flip self._surf True False) + Direction.RIGHT (.copy self._surf)))) (defn [property] total-move [self] (merge-moves self.moves))) diff --git a/src/macroknight/game.hy b/src/macroknight/game.hy index 4e1ce7e..95cb8b1 100644 --- a/src/macroknight/game.hy +++ b/src/macroknight/game.hy @@ -2,11 +2,11 @@ (require hyrule *) (import pygame pytmx.util_pygame [load_pygame] - entities [Player LevelTile Goal Enemy2Swords] + entities [Player LevelTile Goal Enemy] tiles [TileSet draw-tile] utils [neg] text [render-text] - systems [apply-gravity apply-collisions GoalHit] + systems [apply-gravity apply-collisions run-enemies GoalHit] math [floor]) (pygame.init) @@ -48,11 +48,11 @@ tile-x tile-y)) "Enemy1" (.append entities - (Enemy2Swords (len entities) - (get tileset.tiles 128) - TILE_SIZE - tile-x - tile-y))))) + (Enemy (len entities) + (get tileset.tiles 128) + TILE_SIZE + tile-x + tile-y))))) (setv player-pos (let [player-objects (lfor ent (get level.layers 1) :if (= ent.type "Player") ent)] @@ -61,7 +61,8 @@ #((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)) + (setv player (Player 1 [(get tileset.tiles 28) + (get tileset.tiles 29)] TILE_SIZE #* player-pos)) (.append entities player) (setv macro-input-mode False) (setv macro-wait-time 0) @@ -86,7 +87,7 @@ (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]) + (when (in event.key [pygame.K_a pygame.K_w pygame.K_a pygame.K_s pygame.K_d pygame.K_RETURN]) (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) @@ -115,7 +116,9 @@ 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))) + pygame.K_d (.move player #((* 2 TILE_SIZE) 0)) + pygame.K_RETURN (.attack player)) + (if (= command-id (- (len macro-commands) 1)) (do (setv macro-commands [None None None]) @@ -132,6 +135,7 @@ 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) + pygame.K_RETURN (draw-tile screen tileset 329 x-pos 5) None (draw-tile screen tileset 725 x-pos 5))))) ;; Not in macro mode @@ -145,21 +149,21 @@ 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)))) + pygame.K_d (.move player #(player.SPEED 0)) + pygame.K_RETURN (.attack player))) - - (try (when (any ongoing_inputs) (for [entity entities] (apply-collisions entity entities))) - (.animate player) + (.animate player (pygame.time.get_ticks)) (.flush player) ;; Apply systems (for [entity entities] (apply-gravity entity entities) + (run-enemies entity entities) (apply-collisions entity entities)) (except [GoalHit] diff --git a/src/macroknight/systems.hy b/src/macroknight/systems.hy index fccccd0..8c1bb9c 100644 --- a/src/macroknight/systems.hy +++ b/src/macroknight/systems.hy @@ -1,25 +1,53 @@ +(import utils [sub-points Direction]) + ;; Define systems here (setv GRAVITY 5) (defclass GoalHit [Exception]) -(defn apply-gravity [entity entities] - (when (not entity.fixed) - (.move entity #(0 GRAVITY)))) +(defn entities-by-type [entities types] + (gfor ent entities + :if (in ent.type types) + ent)) + +(defmacro defsystem [name pred #* body] + `(defn ~name [entity entities] + (when ~pred + ~@body))) + +(defsystem apply-gravity + (not entity.fixed) + (.move entity #(0 GRAVITY))) + +(defsystem apply-collisions + (not entity.fixed) + (for [ent (gfor enti entities + :if (!= enti.id entity.id) + enti)] + (when (.colliderect entity.rect ent.rect) + (if (= ent.type "goal") + (raise (GoalHit)) + (let [collision-rect (.clip entity.rect ent.rect) + move-x (get entity.total-move 0) + move-y (get entity.total-move 1)] + (when (!= move-x 0) + (.move entity #((* (if (> move-x 0) -1 1) collision-rect.width) 0))) + (when (!= move-y 0) + (.move entity #(0 (* (if (> move-y 0) -1 1) collision-rect.height)))))) + (.ground entity)))) -(defn apply-collisions [entity entities] - (when (not entity.fixed) - (for [ent (gfor enti entities - :if (!= enti.id entity.id) - enti)] - (when (.colliderect entity.rect ent.rect) - (if (= ent.type "goal") - (raise (GoalHit)) - (let [collision-rect (.clip entity.rect ent.rect) - move-x (get entity.total-move 0) - move-y (get entity.total-move 1)] - (when (!= move-x 0) - (.move entity #((* (if (> move-x 0) -1 1) collision-rect.width) 0))) - (when (!= move-y 0) - (.move entity #(0 (* (if (> move-y 0) -1 1) collision-rect.height)))))) - (.ground entity))))) +(defsystem run-enemies + (= entity.type "enemy") + ;; If the player is on the tile next to the enemy, attack. + (let [player (next (entities-by-type entities ["player"])) + dist (sub-points entity.pos player.pos) + direction (.x-from-move Direction dist)] + ;; If facing the player and within reach, attack) + (when (and (= direction entity.facing) + (< (get dist 1) 1) ;; Same level + (< (abs (get dist 0)) 2) + (not entity.attacking)) + (setv entity.attacking True) + (print "attack") + ) + )) diff --git a/src/macroknight/utils.hy b/src/macroknight/utils.hy index a857d02..dde8d2e 100644 --- a/src/macroknight/utils.hy +++ b/src/macroknight/utils.hy @@ -1,3 +1,31 @@ +(import enum [Enum]) + +(defclass Direction [Enum] + (setv UP "UP") + (setv DOWN "DOWN") + (setv LEFT "LEFT") + (setv RIGHT "RIGHT") + + (defn [staticmethod] from-move [move] + #((.x-from-move Direction move) + (.y-from-move Direction move))) + + (defn [staticmethod] x-from-move [move] + (if (>= (get move 0) 0) + Direction.RIGHT + Direction.LEFT)) + + (defn [staticmethod] y-from-move [move] + (if (>= (get move 0) 0) + Direction.DOWN + Direction.UP)) + + (defn [staticmethod] x-between-points [point1 point2] + (let [delta-x (abs (- (get point1 0) (get point2 0)))] + (if (>= delta-x 0) + Direction.RIGHT + Direction.LEFT)))) + (defn neg [value] (* -1 value)) @@ -12,3 +40,7 @@ #((+ (get end-move 0) (get move 0)) (+ (get end-move 1) (get move 1))))) end-move)) + +(defn sub-points [point1 point2] + #((- (get point2 0) (get point1 0)) + (- (get point2 1) (get point2 1)))) |