remove-collided-entities
update-projectile-locations
update-enemy-locations
+ update-enemy-flippyness
update-frame-count
maybe-render-fps-display
schedule-next-frame
:path-fn path/projectile-path-on-level
})
+(def DirectionEnum {"NONE" 0 "CW" 1 "CCW" 2})
+
+(defn direction-string-from-value
+ [val]
+ (first (first (filter #(= 1 (peek %)) (into [] maptest)))))
+
(defn build-enemy
"Returns a dictionary describing an enemy on the given level and segment,
and starting on the given step. Step defaults to 0 (innermost step of
{:step step
:stride 1
:segment seg-idx
- :path-fn path/flipper-path-on-level
:level level
:hits-remaining 1
- :bounding-fn path/flipper-path-bounding-box
+ :path-fn #([])
+ :bounding-fn #(identity 0)
+
+ :flip-dir (DirectionEnum "NONE")
+ :flip-point [0 0]
+ :flip-stride 1
+ :flip-max-angle 0
+ :flip-cur-angle 0
})
+(defn build-flipper
+ [level seg-idx & {:keys [step] :or {step 0}}]
+ (assoc (build-enemy level seg-idx :step step)
+ :bounding-fn path/flipper-path-bounding-box
+ :path-fn path/flipper-path-on-level
+ :flip-dir (DirectionEnum "NONE")
+ :flip-point [0 0]
+ :flip-stride 1
+ :flip-max-angle 0
+ :flip-cur-angle 0
+ ))
+
+(defn mark-flipper-for-flipping
+ [flipper direction stride seg-idx cw?]
+ (assoc flipper
+ :stride 0
+ :flip-dir (DirectionEnum direction)
+ :flip-stride stride
+ :flip-cur-angle 0
+ :flip-point (path/flip-point-between-segments
+ (:level flipper)
+ (:segment flipper)
+ seg-idx
+ (:step flipper)
+ cw?)
+ :flip-max-angle (path/flip-angle-between-segments
+ (:level flipper)
+ (:segment flipper)
+ seg-idx
+ cw?)))
+
+(defn random-direction-string
+ []
+ (condp = (rand-int 2)
+ 0 "CW"
+ "CCW"))
+
+(defn segment-for-flip-direction
+ [flipper flip-dir]
+ (condp = flip-dir
+ "CW" (segment-entity-cw flipper)
+ (segment-entity-ccw flipper)))
+
+(defn maybe-engage-flipping
+ [flipper]
+ (let [should-flip (and (= (:step flipper) 100) (= (:flip-dir flipper) (DirectionEnum "NONE")))
+ flip-dir (random-direction-string)
+ flip-seg-idx (segment-for-flip-direction flipper flip-dir)
+ cw? (= flip-dir "CW")]
+ (if (and should-flip
+ (not= flip-seg-idx (:segment flipper)))
+ (mark-flipper-for-flipping flipper flip-dir 1 flip-seg-idx cw?)
+ flipper)))
+
+(defn consider-flipping
+ [entity-list]
+ ((fn [oldlist newlist]
+ (let [entity (first oldlist)]
+ (if (empty? entity)
+ newlist
+ (recur (rest oldlist)
+ (cons (maybe-engage-flipping entity) newlist))))
+ ) entity-list []))
+
+(defn update-enemy-flippyness
+ [game-state]
+ (let [{enemy-list :enemy-list} game-state]
+ (assoc game-state :enemy-list (consider-flipping enemy-list))))
+
+
(defn build-player
"Returns a dictionary describing a player on the given level and segment."
[level seg-idx]
(conj projectile-list
(build-projectile level seg-idx stride :step step))))
-(defn segment-player-left
+(defn segment-entity-cw
"Returns the segment to the left of the player. Loops around the level
on connected levels, and stops at 0 on unconnected levels."
[player]
new-seg)))
-(defn segment-player-right
+(defn segment-entity-ccw
"Returns the segment to the right of the player. Loops around the level
on connected levels, and stops at max on unconnected levels."
[player]
(condp = key
key-codes/RIGHT (assoc game-state
:player
- (assoc player :segment (segment-player-right player)))
+ (assoc player :segment (segment-entity-ccw player)))
key-codes/LEFT (assoc game-state
:player
- (assoc player :segment (segment-player-left player)))
+ (assoc player :segment (segment-entity-cw player)))
key-codes/SPACE (assoc game-state
:projectile-list
(add-player-projectile projectile-list player))
(.moveTo context (first point0) (peek point0))
(.lineTo context (first point1) (peek point1))
(.stroke context))
-
+
+(defn max-flipper-angle
+ []
+ ;; get (x0,y0) and (x1,y1) of corners of current segment
+ ;; gamma = atan2(y1-y0,x1-x0)
+ ;; theta = PI-gamma
+ )
+
+(defn draw-path-rotated
+ [context origin vecs skipfirst? point angle]
+ ;; determine x/y translate and origin offsets by difference between
+ ;; cartesian midpoint of segment and cartesian corner of segment.
+ ;;
+ ;; angle starts at 0, and ends at some angle difference between
+ (do
+ (.save context)
+ (.translate context
+ (- (first origin) (first point))
+ (- (peek origin) (peek point)))
+ (.rotate context angle)
+ ((fn [origin vecs skip?]
+ (if (empty? vecs)
+ nil
+ (let [line (first vecs)
+ point (path/rebase-origin (path/polar-to-cartesian-coords line)
+ origin)]
+ (.lineTo context (first point) (peek point))
+ (recur point (next vecs) false))))
+ [(first point) (peek point)] vecs skipfirst?)
+ ;;[0 0] vecs skipfirst?)
+ (.stroke context)
+ (.restore context)
+ ))
(defn draw-path
"Draws a 'path', a vector of multiple polar coordinates, on an HTML5 2D
[context dims level entity-list]
(doseq [entity entity-list]
(.beginPath context)
- (draw-path context
+ (draw-path-rotated context
(path/polar-to-cartesian-centered
(path/polar-entity-coord entity)
dims)
(path/round-path ((:path-fn entity) entity))
- true)
+ true
+ (:flip-point entity)
+ ;;[20 -20]
+ (:flip-max-angle entity)
+ ;;0
+ )
(.closePath context)))
(defn draw-board
(def *level6_lines* (vec (oblong-level [135 45 90 135 45 90 135 45 90 135 45 90
135 45 90 135 45 90 135 45 90 135 45] 15 80)))
+(def *level7_lines* (vec (oblong-level [135 45 135 45] 15 3)))
+
(defn make-level-entry
(make-level-entry *level3_lines* false)
(make-level-entry *level4_lines* false)
(make-level-entry *level5_lines* false)
- (make-level-entry *level6_lines* true)])
+ (make-level-entry *level6_lines* true)
+ (make-level-entry *level7_lines* false)])
[(+ (first point1) (first point0))
(- (peek point1) (peek point0))])
+(defn cartesian-edge-coordinates
+ "Returns a pair of cartesian coordinates [[x0 y0] [x1 y1]], representing
+ the points on the edges of the given segment of the given level at the
+ given step.
+
+ That is, this returns the two points at the edge of a segment between which
+ an entity would be drawn."
+ [level seg-idx step]
+ (let [edges (polar-lines-for-segment level seg-idx false)
+ edge-steps (step-lengths-for-segment-lines level seg-idx)
+ offset0 (* (first edge-steps) step)
+ offset1 (* (peek edge-steps) step)
+ point0 (polar-extend offset0 (first edges))
+ point1 (polar-extend offset1 (peek edges))]
+ [(polar-to-cartesian-coords point0)
+ (polar-to-cartesian-coords point1)]))
+
+(defn cartesian-point-between-segments
+ [level seg-idx0 seg-idx1 step]
+ (let [line (edge-line-between-segments level seg-idx0 seg-idx1)
+ line-steps (step-length-for-level-line level line)
+ offset (* line-steps step)
+ point0 (polar-extend offset line)]
+ (polar-to-cartesian-coords point0)))
+
+
+(defn edge-line-between-segments
+ [level seg-idx0 seg-idx1]
+ (let [segs0 (get (:segments level) seg-idx0)
+ segs1 (get (:segments level) seg-idx1)
+ allsegs (flatten [segs0 segs1])]
+ (first (for [[id freq] (frequencies allsegs) :when (> freq 1)]
+ (get (:lines level) id)))))
+
+(defn flip-angle-between-segments
+ "Returns the angle, in radians, between the two given segments on the
+given level."
+ [level seg-idx-cur seg-idx-new cw?]
+ (let [angle-cur (segment-angle level seg-idx-cur)
+ angle-new (segment-angle level seg-idx-new)]
+ (- 0 (- (+ angle-new 3.14159265) angle-cur))
+ ))
+
+(defn flip-point-between-segments
+ [level seg-idx-cur seg-idx-new step cw?]
+ (let [[x0 y0] (cartesian-point-between-segments level
+ seg-idx-cur
+ seg-idx-new
+ step)
+ [x1 y1] (polar-to-cartesian-coords
+ (polar-segment-midpoint level seg-idx-cur step))
+ edge-points (cartesian-edge-coordinates level seg-idx-new step)]
+ (.log js/console (pr-str "Edge points: " edge-points
+ "\nPivot point: " [x0 y0]))
+ (.log js/console (pr-str "Result: " [(- x0 x1) (- y0 y1)]))
+ [(- x1 x0) (- y0 y1)]))
+
+(comment
+(defn flip-point-between-segments
+ [level seg-idx-cur seg-idx-new step cw?]
+ (let [[x0 y0] (polar-to-cartesian-coords
+ (polar-segment-midpoint level seg-idx-cur step))
+ [[x1 y1] [x2 y2]] (cartesian-edge-coordinates level seg-idx-new step)]
+ (.log js/console (str "CW? " (pr-str cw?) " "
+ (pr-str [(- x0 x2) (- y2 y0)]
+ [(- x1 x0) (- y0 y1)])))
+ (if cw?
+ [(- x0 x2) (- y2 y0)]
+ [(- x1 x0) (- y0 y1)])))
+)
(defn rebase-origin
"Return cartesian coordinate 'point' in relation to 'origin'."
(polar-to-cartesian-coords line1)]
))
+(defn polar-segment-midpoint
+ "Returns current polar coordinates to the entity."
+ [level seg-idx step]
+ (let [steplen (step-length-segment-midpoint level seg-idx)
+ offset (* steplen step)
+ midpoint (segment-midpoint level seg-idx)]
+ (polar-extend offset midpoint)))
(defn polar-entity-coord
"Returns current polar coordinates to the entity."
[entity]
+ (polar-segment-midpoint (:level entity)
+ (:segment entity)
+ (:step entity)))
+
+
+(comment
+ (defn polar-entity-coord
+ "Returns current polar coordinates to the entity."
+ [entity]
(let [steplen (step-length-segment-midpoint (:level entity)
(:segment entity))
offset (* steplen (:step entity))
midpoint (segment-midpoint (:level entity) (:segment entity))]
(polar-extend offset midpoint)))
+ )
(defn step-length-segment-midpoint
"Finds the 'step length' of a line through the middle of a level's segment.
(first point1))
(:steps level))))
+(defn step-length-for-level-line
+ [level line]
+ (let [longline (scale-polar-coord (:length-fn level) line)]
+ (step-length-line level line longline)))
+
(defn step-lengths-for-segment-lines
"Returns a vector [len0 len1] with the 'step length' for the two edge
lines that mark the boundaries of the given segment."
[length path]
(map #(polar-extend length %) path))
+(defn segment-angle
+ "Returns the angle (in radians) of the given segment.
+ The angle of a segment is the angle of any line projected onto it."
+ [level seg-idx]
+ (let [[point0 point1] (polar-lines-for-segment level seg-idx false)]
+ (apply js/Math.atan2
+ (vec (reverse (map - (polar-to-cartesian-coords point0)
+ (polar-to-cartesian-coords point1)))))))
+
(defn enemy-angle
- "Returns the angle from origin that the enemy needs to be rotated to
- appear in the correct orientation at its current spot on the level.
- In reality, it returns the angle of the line that traverses the segment
- across the midpoint of the enemy. TODO: This should be renamed to
+ "Returns the angle (in degrees) from origin that the enemy needs to be
+ rotated to appear in the correct orientation at its current spot on the
+ level. In reality, it returns the angle of the line that traverses the
+ segment across the midpoint of the enemy. TODO: This should be renamed to
'entity-angle', it works with anything on the board."
[enemy]
- (let [edges (polar-lines-for-segment (:level enemy)
- (:segment enemy)
- false)
- edge-steps (step-lengths-for-segment-lines (:level enemy)
- (:segment enemy))
- offset0 (* (first edge-steps) (:step enemy))
- offset1 (* (peek edge-steps) (:step enemy))
- point0 (polar-extend offset0 (first edges))
- point1 (polar-extend offset1 (peek edges))]
- (util/rad-to-deg
- (apply js/Math.atan2
- (vec (reverse (map - (polar-to-cartesian-coords point0)
- (polar-to-cartesian-coords point1))))))))
-
+ (util/rad-to-deg (segment-angle (:level enemy) (:segment enemy))))
+
(defn entity-desired-width
"Returns how wide the given enemy should be drawn to span the full width
of its current location. In reality, that means returning the length of