(local tiled (require "lib.tiled"))
(local bump (require "lib.bump"))
(local draw (require "draw"))
(local hud (require "hud"))
(local laser (require "laser"))
(local sensor (require "sensor"))
(local lint (require "lint"))
(local sound (require "sound"))
(local autopilot (require "autopilot"))
(local map (lint (tiled "map.lua" ["bump"])))
(local world (bump.newWorld))
(local state {:tx 200 :ty 500
:rovers [{:theta 0 :docked? true :type :rover}
{:theta 3 :docked? false :type :rover}
{:theta 2 :docked? false :type :rover}
{:theta 0 :docked? true :type :rover}]
:probe {:theta math.pi :type :probe :rovers []}
:flags {} :messages [] :map map :world world
:echo (fn [s msg] (table.insert s.messages 1 msg))})
(: map :bump_init world)
(: world :add state.probe 105 1205 30 24)
(: world :add (. state.rovers 2) 165 1200 10 10) (: world :add (. state.rovers 3) 145 1212 10 10)
(local turn-speed 1)
(local rover-move-speed 82)
(local probe-move-speed 64)
(set state.selected state.probe)
(let [layer (: map :addCustomLayer "player" 8)]
(set layer.sprites [(unpack state.rovers)])
(tset layer.sprites 0 state.probe)
(set layer.draw (partial draw.draw-player world state)))
(set map.layers.sensors.draw draw.draw-sensors)
(set map.layers.doors.draw draw.draw-doors)
(global s state)
(fn calculate-new-rover-position [rover dt]
(let [(x y) (: world :getRect rover)
new-x (+ x (* (math.cos rover.theta) rover-move-speed dt))
new-y (+ y (* (math.sin rover.theta) rover-move-speed dt))]
(values new-x new-y)))
(fn within? [item box margin]
(let [(x y width height) (: world :getRect item)]
(and (< (- box.x margin) x (+ x width) (+ (+ box.x box.width) margin))
(< (- box.y margin) y (+ y height) (+ (+ box.y box.height) margin)))))
(fn terminal-check [cols unit set-mode]
(set state.selected.in-term? false)
(each [_ col (ipairs cols)]
(when (and col.other.properties col.other.properties.terminal
(within? col.item col.other 0))
(set unit.in-term? true)
(when (not unit.in-term-last-tick?)
(set-mode :term col.other.properties.terminal)))))
(fn collide-filter [_item other]
(if (or (love.keyboard.isDown "backspace") (and other.properties other.properties.terminal))
:cross
:slide))
(fn rover-forward [set-mode r delta]
(let [(new-x new-y) (calculate-new-rover-position r delta)
(_ _ cols) (: world :move r new-x new-y collide-filter)]
(terminal-check cols r set-mode)))
(fn move-rover [dt set-mode]
(when (love.keyboard.isDown "left")
(set state.selected.theta (- state.selected.theta (* 2 dt turn-speed))))
(when (love.keyboard.isDown "right")
(set state.selected.theta (+ state.selected.theta (* 2 dt turn-speed))))
(when (or (love.keyboard.isDown "up") (love.keyboard.isDown "down"))
(rover-forward set-mode state.selected
(if (love.keyboard.isDown "up") dt (- dt)))))
(fn move-probe [dt set-mode]
(let [left? (if (love.keyboard.isDown "left") 1 0)
right? (if (love.keyboard.isDown "right") 1 0)
up? (if (love.keyboard.isDown "up") 1 0)
down? (if (love.keyboard.isDown "down") 1 0)]
(set state.probe.stuck? (and (> (+ left? right? up? down?) 0)
(not state.probe.mobile?)))
(when (and (> (+ left? right? up? down?) 0) state.probe.mobile?)
(let [speed (if (love.keyboard.isDown "=") 164 probe-move-speed)
(x y) (: world :getRect state.selected)
new-x (+ x
(- (* left? speed dt))
(* right? speed dt))
new-y (+ y
(- (* up? speed dt))
(* down? speed dt))
(_ _ cols) (: world :move state.selected new-x new-y
collide-filter)]
(terminal-check cols state.selected set-mode)))))
(fn scroll [state dt x y]
(let [dist (lume.distance x y (+ state.tx 180) (+ state.ty 112))
delta (if (and (> dist 200) state.intro-complete?)
(* dt probe-move-speed (* (math.sqrt (* dist 100)) 0.02))
(* dt probe-move-speed))]
(when (< (+ state.tx 260) x)
(set state.tx (math.min (+ state.tx delta) 1559)))
(when (< x (+ state.tx 80))
(set state.tx (math.max (- state.tx delta) 0)))
(when (< (+ state.ty 165) y)
(set state.ty (math.min (+ state.ty delta) 1054)))
(when (< y (+ state.ty 100))
(set state.ty (math.max (- state.ty delta) 0)))))
(fn update [dt set-mode]
(set state.probe.stuck? false)
(pcall (fn [] (hud.update state world map dt)))
(: map :update dt)
(scroll state dt (: world :getRect state.selected))
(when (not state.selected.immobilized?)
(let [dt2 (if (love.keyboard.isDown "lshift" "rshift") (* dt 0.2) dt)]
(when (= :rover state.selected.type)
(move-rover dt2 set-mode))
(when (= :probe state.selected.type)
(move-probe dt2 set-mode))
(when (love.keyboard.isDown "," "w")
(set state.probe.theta (- state.probe.theta (* dt2 turn-speed))))
(when (love.keyboard.isDown "." "v")
(set state.probe.theta (+ state.probe.theta (* dt2 turn-speed))))))
(sensor.update state map world dt)
(autopilot.update state world dt (partial rover-forward set-mode))
(if (love.keyboard.isDown "space" "lctrl" "rctrl" "capslock")
(sound.play :laser)
(sound.stop :laser))
(set state.laser (and (love.keyboard.isDown "space" "lctrl"
"rctrl" "capslock")
(let [(x y w h) (: world :getRect state.probe)]
(laser.fire (+ x (/ w 2))
(+ y (/ h 2) -6)
state.probe.theta state world map
[] [state.probe] 64))))
(when (= :win state.laser)
(set-mode :win))
(set state.selected.in-term-last-tick? state.selected.in-term?))
(fn enough-docked? [] (< 2 (# (lume.filter state.rovers :docked?))))
TODO(local offsets [[-10 -10] [20 -10] [20 20] [-10 20]])
(fn deploy [n]
(tset (. state.rovers n) :docked? false)
(set state.probe.mobile? (enough-docked?))
(let [[ox oy] (. offsets n)
(px py) (: world :getRect state.probe)]
(: world :add (. state.rovers n) (+ px ox) (+ py oy) 10 10)))
(fn dock []
(let [(x y w h) (: world :getRect state.probe)]
(when (and (= state.selected.type :rover)
(within? state.selected {:x x :y y :width w :height h} 12))
(set state.selected.docked? true)
(set state.probe.mobile? (enough-docked?))
(: world :remove state.selected)
(set state.selected state.probe))))
(fn select [n]
(when n (autopilot.disable))
(set state.selected (if n
(. state.rovers n)
state.probe))
(when (and n (not (: world :hasItem (. state.rovers n))))
(deploy n)))
(local keymap {:1 (partial select 1) :2 (partial select 2)
:3 (partial select 3) :4 (partial select 4)
:0 select :5 select "`" select
:return dock
:backspace (fn [] (autopilot.enable) (select))})
(fn keypressed [key set-mode]
(let [f (. keymap key)]
(if (or (= "escape" key) (= "f1" key))
(set-mode :pause)
(= (type f) "function")
(f))))
{:draw (partial draw.draw map world state)
:update update
:keypressed keypressed}