aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGene Pasquet <dev@etenil.net>2025-05-18 08:38:25 +0100
committerGene Pasquet <dev@etenil.net>2025-05-18 08:38:25 +0100
commit1cf28e193729ee64a17e6464ce7310c67f5eae34 (patch)
tree906ff27f5e533397f4f760a245d0606232a87b1f
parentbe71ead1e21d55049a18747f2d0d2159b11868f3 (diff)
Progress on animations and enemies
-rw-r--r--assets/monochrome-transparent.pngbin19451 -> 26816 bytes
-rw-r--r--macroknight.tiled-session36
-rw-r--r--src/macroknight/entities.hy69
-rw-r--r--src/macroknight/game.hy32
-rw-r--r--src/macroknight/systems.hy66
-rw-r--r--src/macroknight/utils.hy32
6 files changed, 172 insertions, 63 deletions
diff --git a/assets/monochrome-transparent.png b/assets/monochrome-transparent.png
index 9f8187d..3102227 100644
--- a/assets/monochrome-transparent.png
+++ b/assets/monochrome-transparent.png
Binary files differ
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))))