summary history branches tags files
tempest/tempest/levels.cljs
;;
;; This file is part of tempest-cljs
;; Copyright (c) 2012, Trevor Bentley
;; All rights reserved.
;; See LICENSE file for details.
;;

(ns ^{:doc "
Functions related to generating paths representing levels.
"}
  tempest.levels
  (:require [tempest.util :as util]))

;;
;; ## Level Terminology:
;;
;; *length* and *depth* both refer to how far from origin the inner
;; line is drawn, in pixels.
;;
;; *length-fn* is a function to determine how long between inner and
;; outer line.  Takes one argument 'r' to the inner line.  Returns
;; 'r' to the outer line.  Default is 'inner r' multiplied by 4.
;;
;; *width* is how wide, in pixels, the outer line segment is.
;;
;; ## Level design
;;
;; Levels are defined by a vector of polar coordinates [r theta],
;; which are used to build a vector of 'segments' that form a level.
;;
;; Levels can be manually specified by building a vector of lines
;; manually.
;;
;; Some types of levels can be built automatically by calling helper
;; functions in this module with various parameters.
;;
;; Levels are drawn radially, from the center point of the canvas.
;;
;; Levels are stored in the \*levels\* vector as a list of maps.
;;
;; Enemies travel up segments in steps.  A level has the same number
;; of steps per segment, but the size of the steps can vary depending
;; on the dimensions of the segment.  Instead of keeping track of its
;; coordinates, an enemy keeps track of which segment it is on, and
;; how many steps up the segment.
;;


(def ^{:doc "Default length, in pixels, from origin to inner line."}
  *default-line-length* 20)

(def ^{:doc "Default length function, returns argument*4"}
  *default-length-fn* #(* 4 %))

(def *default-steps-per-segment* 200)

(defn build-unlinked-segment-list [max-x]
  (vec ((fn [x segments]
    (if (= x 0)
      segments
      (recur (dec x) (cons [(dec x) x] segments)))
    ) max-x [])))

(defn build-segment-list [max-x linked?]
  (let [segments (build-unlinked-segment-list max-x)]
    (if (true? linked?)
      (conj segments [(last (last segments)) (first (first segments))])
      segments)
    )
  )




;; ## "Flat" level functions
;;
;; Functions for generating "flat" levels: levels where the edge appears as
;; a straight line.  Something like this garbage:
;;
;;           ___________________________
;;          /  /  / |  |  |  |  |   \   \
;;         /  |  |  |  |  |  |   |   \   \
;;        /  /  |  |   |  |   |   |   |   \
;;       /  /  /   |  |   |   |   |    |   \
;;      /  /  /   |   |   |   |    |    |   \
;;     ----------------------------------------
;;
;; Flat levels always start with a line dropped straight down, and build
;; out symmetrically from there.  The width of segments at the "outer" edge
;; (closer to the player) is uniform.
;;

(defn theta-flat 
  "Return theta for segment n.  Width is width of segment at the outer edge,
   closest to the player, and depth is the distance to origin."
  [n width depth]
  (js/Math.round (util/rad-to-deg (js/Math.atan (/ (* (+ n 1) width) depth)))))

(defn r-flat 
  "Return r for given theta (see theta-flat)."
  [theta depth]
  (js/Math.round (/ depth (js/Math.cos (util/deg-to-rad theta)))))

(defn r-theta-pair-flat 
  "Returns [r theta] for nth straight line segment.  angle-center is the
   angle that theta should be in reference to (probably 270 degrees, a line
   straight down), and angle-multiplier should be -1 to built left or 1 to
   build right."
  [n width depth angle-center angle-multiplier]
  (let [th (theta-flat n width depth)]
    [(r-flat th depth) (+ angle-center (* th angle-multiplier))]))

(defn flat-level 
  "Return a list of line segments representing a flat level with segment-count
   segments ON EACH SIDE OF CENTER (2*segment-count total), width at the player
   edge of segment-width, and distance from origin to inner-edge as
   segment-depth"
  [segment-count segment-width segment-depth]
  (concat (reverse (map #(r-theta-pair-flat % segment-width segment-depth 270 -1) (range segment-count)))
          [[80 270]]
          (map #(r-theta-pair-flat % segment-width segment-depth 270 1) (range segment-count))))




;; ## "Oblong" level functions
;;
;; Functions for generating oblong triangles using Law of Cosines.
;; Use to generate arbitrary levels from a list of angles, gamma(0)..gamma(N),
;; where gamma is the angle between the previous line segment 'towards the
;; player' and the line segment that makes the 'width' of the segment.
;;
;;                   ____
;;                  /    / \
;;                 /    /   \ 
;;                /    /     /
;;               /    /     /
;;       gamma1-/->  /     /
;;             /____/     /
;;                   \