{map, filter, fold, foldl, lines, reject} = require 'prelude-ls'

layerBack = null
layerGame = null
layerUI = null
phaser = null
gtime = 0.0
# always alive
camera = null
key1 = null
key2 = null
tasks = null
reset = null
nextResetWorld = 0
# temp
cata = null
wingman = null
wingman2 = null
mich = null
parachute = null
whee = null
brush = null
tank = null
door = null
drone = null
heaven = null
puffImages = []
cloudImages = []
speedToBreakDown = 350

g = {}
g.width = 1280
g.height = 720
g.cx = g.width * 0.5
g.cy = g.height * 0.5
g.fps = 30
g.dt = 1.0 / g.fps
g.ground = 0
g.pi = Math.PI
actors = []

currMusicTrack = null
music = {}
music.song_20160828_140724_215 = null
aa = {}
aa.sing1 = null
aa.flap1 = null
aa.flap2 = null
aa.flap3 = null
aa.catapult = null
aa.hit = null
aa.crash = null
aa.scratch = null
aa.bubble1 = null
aa.bubble2 = null
aa.bubble3 = null
aa.hmm = null
aa.wood = null
finalizeAA=!->
	aa.bubbles = [aa.bubble1, aa.bubble2, aa.bubble3]
	aa.flaps = [aa.flap1, aa.flap2, aa.flap3]

# default:true
g.dev_startFromCatapult = true
g.dev_playbutton = true
g.dev_music = true
g.dev_soundfx = true
g.dev_closerParachute = true
# default:false
g.dev_fragileDoor = false
g.dev_pretendMobile = true

# these are set later
g.dev_desktop = false
g.dev_mobile = false
g.dev_android = false
g.dev_ios = false

clamp=(x,min,max)->
	return max if x > max
	return min if x < min
	x
saturate=(x)->
	return 1 if x > 1
	return 0 if x < 0
	x
normalRange=(x, x0, x1)->
	x = (x - x0) / (x1 - x0)
	saturate x
tentCurve=(t) ->
	t = saturate(t)
	r = 0
	if t <= 0.5 => r = t * 2.0
	else => r = 1.0 - (t - 0.5) * 2.0
	r
lerp=(a,b,t)-> a + (b - a) * t
degrees=(a)-> a*180.0/Math.PI
radians=(a)-> a*Math.PI/180.0
frand=(min,max)-> min+Math.random()*(max - min)
irand=(min,max)-> Math.floor(min+Math.random()*(max - min + 1))
rsign=-> if Math.random()<0.5 => -1 else 1
sign=(a)->
	return 1 if a >= 0
	return -1
roll=(n)-> irand(1, n) == 1
fchance=(x)-> x<Math.random()
clampPositive=(x)-> Math.max(0,x)
acopy=(data)-> [a for a in data]
ocopy=(data)-> {[k,v] for k,v of data}
vlength=(x,y)-> Math.sqrt(x*x + y*y)
rotatePoint=(p, theta)!->
	cos = Math.cos(theta)
	sin = Math.sin(theta)
	x1 = p[0] * cos - p[1] * sin
	y1 = p[1] * cos + p[0] * sin
	p[0] = x1
	p[1] = y1
contains=(arr,value)->
	for a in arr
		if a == value
			return true
	return false
#scrambled=(arr)->	
findIndex=(arr,value)->
	index = 0
	for a in arr
		if a == value
			return index
		index++
	return null
pick=(arr, remove=false)->
	if arr? and arr.length > 0
		index = irand(0, arr.length-1)
		value = arr[index]
		if remove
			arr.splice(index, 1)
		return value
	return null
remove=(arr, thing)->
	if arr? and arr.length > 0
		k = 0
		for a in arr
			if a == thing
				arr.splice(k, 1)
				return true
			k++
	return false
centerDistribution=(center, gap, count)->
	r = []
	if count <= 0 =>
	else if count == 1
		r.push(center)
	else
		totalLength = (count - 1) * gap
		x0 = center - totalLength * 0.5
		x1 = center + totalLength * 0.5
		for a til count
			t = a / (count-1.0)
			r.push(lerp(x0, x1, t))
	r
pointInside=(px, py, x, y, ex, ey)->
	dx = Math.abs(px - x)
	dy = Math.abs(py - y)
	r = false
	if dx <= ex and dy <= ey
		r = true
	r
# naive partitions... assumes enough elements for the splits
partitionsByCount=(array,counts)->
	arrs = []
	k = 0
	for a in counts
		part = []
		for til a
			part.push(array[k++])
		arrs.push(part)
	arrs
afind=(array,key,value)->
	for a in array
		if a[key]==value
			return a
# creates an array of random points, scattered within sections of percentage sectionSpan.
sprinkle=(count, a, b, sectionSpan=0)->
	r = null
	if count > 0 
		span = b - a
		sectionWidth = span / count
		centerx = a + sectionWidth * 0.5
		pad = sectionWidth * 0.5 * sectionSpan
		r = []
		for til count
			r.push frand(centerx - pad, centerx + pad)
			centerx += sectionWidth
	r
# others
setvar=(arr,varr,value)!-> for a in arr => a[varr] = value
#goInvisible=(arr)!-> for a in arr => a.visible = false
#doforeach=(arr, func)!-> for a in arr => a[func]!
overlappingActors=(other)->
	r = null
	if other?
		for a in actors
			if a != other and !a.dead and other.overlaps(a) =>
				#r = [] if !r?
				r ?= []
				r.push(a)
	r
killActorsByClass=(className)!->
	for a in actors
		if a.className == className
			a.dead = true
actorsByClass=(className)->
	r = []
	for a in actors
		if a.className == className
			r.push a
	r
numActorsByClass=(className)->
	c = 0
	for a in actors
		if a.className == className and !a.dead
			c++
	c

print=(x)!-> console.log x
dispatch=(func,data,className=null)!->
	for a in actors
		if !className? or className == a.className
			a[func]?(data)
loadImage=(name, ext="png")!->
	phaser.load.image(name, name + "." + ext)
addSprite=(name, x=0, y=0, anchorx=0.5, anchory=0.5, scale=1.0)->
	r = phaser.add.sprite(x, y, name)
	r.anchor.x = anchorx
	r.anchor.y = anchory
	r.scale.x = scale
	r.scale.y = scale
	r
remove=(arr, thing)->
	if arr? and arr.length > 0
		k = 0
		for a in arr
			if a == thing
				arr.splice(k, 1)
				return true
			k++
	return false
#------------------------------------------------------
class Boot
	->
	preload:!->
		g.dev_desktop = phaser.device.desktop
		if g.dev_pretendMobile
			g.dev_desktop = false
		print ("desktop: " + g.dev_desktop)
		g.dev_mobile = !g.dev_desktop
		g.dev_android = phaser.device.android
		g.dev_ios = phaser.device.ios
		
		print "starting Boot"
		loadImage("load")
		loadImage("creds")
		loadImage("epacegames")
		loadImage("playbutton")
		loadImage("scribing")
		loadImage("spin")
		loadImage("downloads")
		loadImage("download")
		loadImage("youtube")
	create:!->
		phaser.stage.disableVisibilityChange = true
		phaser.time.desiredFps = g.fps
		phaser.scale.pageAlignVertically = true
		phaser.scale.pageAlignHorizontally = true
		phaser.state.start("Preload")
#------------------------------------------------------
class Preload
	->
	preload:!->
		print "starting Preload"
		@load = addSprite("load", g.cx, g.cy)
		@creds = addSprite("creds", 5, 5, 0, 0, 0.8)
		@epacegames = addSprite("epacegames", g.width - 5, g.height - 5, 1, 1)
		@scribing = addSprite("scribing", g.cx, g.cy + 140)
		@spin = addSprite("spin", g.cx, g.cy + 180)
		
		@downloads = addSprite("downloads")
		@button_youtube = addSprite("youtube")
		@button_youtube.x = 80 - 40
		@button_youtube.y = g.height - 100
		@button_download = addSprite("download")
		@button_download.x = 85 - 40
		@button_download.y = g.height - 30
		@downloads.x = 200 - 40
		@downloads.y = g.height - 80
		
		@button_download.inputEnabled = true
		@button_download.input.useHandCursor = true
		@button_youtube.inputEnabled = true
		@button_youtube.input.useHandCursor = true
		
		@button_youtube.events.onInputDown.add(@onclickyoutube, this)
		@button_youtube.events.onInputOver.add(@onhoveryoutube, this)
		@button_youtube.events.onInputOut.add(@onoutyoutube, this)
		
		@button_download.events.onInputDown.add(@onclickdownload, this)
		@button_download.events.onInputOver.add(@onhoverdownload, this)
		@button_download.events.onInputOut.add(@onoutdownload, this)
		
		gcache!
	onclickyoutube:!->
		window.open("http://www.epacegames.com/ld36/video.html", "_blank");
	onhoveryoutube:!->
		@button_youtube.scale.x = 1.1
		@button_youtube.scale.y = 1.1
	onoutyoutube:!->
		@button_youtube.scale.x = 1.0
		@button_youtube.scale.y = 1.0
	onclickdownload:!->
		window.open("http://www.epacegames.com/files/ld36src.zip", "_blank");
	onhoverdownload:!->
		@button_download.scale.x = 1.1
		@button_download.scale.y = 1.1
	onoutdownload:!->
		@button_download.scale.x = 1.0
		@button_download.scale.y = 1.0
	update:!->
		@spin.angle += 600 * g.dt
	onplay:!->
		#print "onplay"
		phaser.state.start("Game")
	create:!->
		@spin.visible = false
		@scribing.visible = false
		if g.dev_playbutton
			@play = addSprite("playbutton", g.cx, g.cy + 250)
			@play.inputEnabled = true
			@play.input.useHandCursor = true
			@play.events.onInputDown.add(@onplay, this)
		else
			@onplay!
		print "create"
#------------------------------------------------------
class Game
	->
	preload:!->
		print "starting Game"
	update:!-> gupdate!
	create:!-> gcreate!
#------------------------------------------------------
class Actor
	->
		@className = @constructor.name
		@group = phaser.add.group!
		@containers = null
		actors.push this
		@worldpos = new Phaser.Point(0,0)
		@ex = 0
		@ey = 0
		@dead = false
		@keep = false
	createImage:(name, x=0, y=0, anchorx=0.0, anchory=0.0)->
		img = phaser.add.sprite(0, 0, name)
		if img?
			img.x = x
			img.y = y
			img.anchor.x = anchorx
			img.anchor.y = anchory
			@group?.add(img)
		else print "bad img " + name
		img
	newEvent:(func, data, delay)!->
		if !delay? => print "null newEvent " + func
		e = {}
		e.nextTrigger = gtime + delay
		e.func = func
		e.triggered = false
		e.data = data
		@events ?= []
		@events.push(e)
	checkEvents:!->
		if @events?
			deadones = null
			for a in @events
				if a?
					if a.nextTrigger < gtime
						@[a.func](a.data)
						a.triggered = true
						if !deadones? => deadones = []
						deadones.push(a)
			if deadones?
				@events = reject (.triggered), @events
	die:!-> @dead = true
	onActorDie:(other)!->
	addToContainer:(c)!->
		@containers ?= []
		@containers.push(c)
		c.push(@)
	addToLayer:(layer)!->
		@layer?.remove(@group)
		@layer = layer
		@layer?.add(@group, true) if @group?
	cleanup:!->
		@group.destroy(true)
		if @containers?
			for a in @containers
				b = remove(a, @)
#------------------------------------------------------	
class BG extends Actor
	->
		super!
		@className = @constructor.name
		@keep = true
		@createImage("canvas")
		@addToLayer(layerBack)
#------------------------------------------------------	
class Catapult extends Actor
	->
		super!
		@className = @constructor.name
		@base = @createImage("cata1")
		#@wheel = @createImage("cata2")
		#@wheel.x = 30
		#@wheel.y = 30
		#@stick = @createImage("cata3")
		@meter = null
		@thing = @createImage("cata4")
		@thing.x = 110
		@thing.y = 90
		@thing.anchor.x = 460.0 / 600.0
		@thing.anchor.y = 180.0 / 300.0
		@worldpos.x = g.cx
		@worldpos.y = g.ground - 50
		@startAngle = 40
		@lowangle = -13
		@avel = 0
		@launched = false
		playAA(aa.wood)
		@a = @startAngle
		@tick!
		@addToLayer(layerGame)
	launch:!->
		playAA(aa.catapult)
		#@thing.visible = false
		tasks.next!
		@thing.angle = @lowangle
		@la = lerp(30, 600, @meter.t)
		@launched = true
		@t = @meter.t
		@meter?.die!
		@meter = null
		tasks.canFail = true
	fling:!->
		@flinged = true
		@thing.visible = false
		@thing2 = @createImage("cata5")
		@thing2.angle = 50
		@thing2.x = @thing.x
		@thing2.y = @thing.y
		@thing2.anchor.x = @thing.anchor.x
		@thing2.anchor.y = @thing.anchor.y
		wingman := new WingMan
		parachute := new Parachute
		new Pointer(wingman, parachute)
		# https://www.wolframalpha.com/input/?i=max(x%5E3,+0.2)+from+0+to+1 asdfasdfasdfasdf asdkfjas asdflkjasd d
		t = @t * @t * @t
		t = 0.2 if t < 0.2
		wingman.fling(500 * t * 0.7, -2300 * t * 0.7)
		# 1400, -1500
	tick:!->
		if @launched
			@thing.angle += @la * g.dt
			if @thing.angle > 50
				@thing.angle = 50
				@fling! if !@flinged
		else
			@avel -= 300 * g.dt
			@a += @avel * g.dt
			if @a < @lowangle and @avel < 0
				@a = @lowangle
				@avel *= -0.5
			@thing.angle = @a
			
			if g.keys.a.isDown or key1.down
				@meter ?= new CataMeter
				if g.keys.f.isDown or key2.down
					@launch!
			else
				@meter?.die!
				@meter = null
		@group.x = @worldpos.x
		@group.y = @worldpos.y
	cleanup:!->
		super!
		print "cleanup"
		@meter?.die!
#------------------------------------------------------
class CataMeter extends Actor
	->
		super!
		@className = @constructor.name
		@base = @createImage("meter", 0, 0, 0.5, 0.5)
		@swirl = @createImage("swirl", 0, 0, 0.5, 0.5)
		@t = frand(0, 1)
		@s = rsign!
		@arrow = @createImage("arrow", 130, -70, 0.5, 0.5)
		@group.x = g.cx + 100
		@group.y = g.cy - 200
		@sc = 0
		@addToLayer(layerUI)
		@tick!
	tick:!->
		@t += @s * g.dt * 3
		if @t > 1.0
			@t = 1.0
			@s = -1
		if @t < 0.0
			@t = 0.0
			@s = 1
		w = 260
		@sc += g.dt * 5
		@sc = 1 if @sc > 1
		s = lerp(0.5, 1, @sc)
		@group.scale.x = s
		@group.scale.y = s
		@swirl.x = -w * 0.5 + @t * w
		@arrow.y = -70 + Math.sin(gtime * 40) * 10
		#@swirl.y = 30
#------------------------------------------------------
class WingMan extends Actor
	->
		super!
		@className = @constructor.name
		@worldpos.x = g.cx - 160
		@worldpos.y = g.ground - 240
		@createImage("torso", 0, 0, 0.5, 0.5)
		# visually right wing facing vman
		e = 0.05
		f = 0.5
		wx = 26
		wy = -72
		@vx = 0
		@vy = 0
		@flap1t = 1
		@flap2t = 1
		@wing1 = @createImage("wing1", wx, wy, e, f)
		# left
		@wing2 = @createImage("wing2", -wx, wy, 1.0 - e, f)
		@nextFlap1 = 0
		@nextFlap2 = 0
		@addToLayer(layerGame)
		@tick!
	fling:(vx, vy)!->
		@vx = vx
		@vy = vy
		@tick!
		camera.snapTo(@)
	flapT:(t)!->
		void
	# better to make a class for wings or make this more generic, but no time
	flapLeft:!->
		if @nextFlap1 < gtime
			@nextFlap1 = gtime + 0.2
			@flap1t = 0
			@vx -= frand(80, 120) * 0.4
			@vy -= frand(50, 60) * 0.4
			playAA(aa.flaps)
	flapRight:!->
		if @nextFlap2 < gtime
			@nextFlap2 = gtime + 0.2
			@flap2t = 0
			@vx += frand(80, 120) * 0.4
			@vy -= frand(50, 60) * 0.4
			playAA(aa.flaps)
	tick:!->
		tasks.fail! if @worldpos.y > 5000
		#flapT = (Math.sin(gtime * 3.0) + 1.0) * 0.5
		#@wing1.angle = lerp(-30, 30, flapT)
		#@wing2.angle = - @wing1.angle
		if g.keys.a.tapped or key1.tapped => @flapRight!
		if g.keys.f.tapped or key2.tapped => @flapLeft!
		# 
		@flap1t += g.dt * 3
		@flap1t = 1 if @flap1t > 1.0
		t = @flap1t * @flap1t
		@wing1.angle = - lerp(30, -30, Math.sin(t * g.pi))
		# 
		@flap2t += g.dt * 3
		@flap2t = 1 if @flap2t > 1.0
		t = @flap2t * @flap2t
		@wing2.angle = lerp(30, -30, Math.sin(t * g.pi))
		#print @worldpos.x + " | " + @worldpos.y
		@vy += 120 * g.dt
		@worldpos.x += @vx * g.dt
		@worldpos.y += @vy * g.dt
		if parachute?
			vx = parachute.worldpos.x - @worldpos.x
			vy = parachute.worldpos.y - @worldpos.y
			l = vlength(vx, vy)
			if l < 200
				@die!
				tasks.next!
				parachute.die!
				cata?.group.visible = false
				whee := new WheeParachute
				brush := new Brush
				new Pointer(whee, brush)
				camera.snapTo(whee)
		drot = Math.atan2(@vy, @vx) + g.pi * 0.5
		@group.rotation = @group.rotation + (drot - @group.rotation) * 0.3
		@group.x = @worldpos.x
		@group.y = @worldpos.y
#------------------------------------------------------
# yup... copying and pasting all this shit bc no time
# to modify class WingMan...
class WingMan2 extends Actor
	->
		super!
		@className = @constructor.name
		@worldpos.x = door.worldpos.x
		@worldpos.y = door.worldpos.y - 50
		@createImage("torso", 0, 0, 0.5, 0.5)
		# visually right wing facing vman
		e = 0.05
		f = 0.5
		wx = 26
		wy = -72
		@vx = 0
		@vy = -500
		@flap1t = 1
		@flap2t = 1
		@wing1 = @createImage("wing1", wx, wy, e, f)
		# left
		@wing2 = @createImage("wing2", -wx, wy, 1.0 - e, f)
		@nextFlap1 = 0
		@nextFlap2 = 0
		@addToLayer(layerGame)
		@tick!
	fling:(vx, vy)!->
		@vx = vx
		@vy = vy
		@tick!
		camera.snapTo(@)
	flapT:(t)!->
		void
	# better to make a class for wings or make this more generic, but no time
	flapLeft:!->
		if @nextFlap1 < gtime
			@nextFlap1 = gtime + 0.2
			@flap1t = 0
			@vx -= frand(80, 120) * 0.4
			@vy -= frand(50, 60) * 0.4
			playAA(aa.flaps)
	flapRight:!->
		if @nextFlap2 < gtime
			@nextFlap2 = gtime + 0.2
			@flap2t = 0
			@vx += frand(80, 120) * 0.4
			@vy -= frand(50, 60) * 0.4
			playAA(aa.flaps)
	tick:!->
		#flapT = (Math.sin(gtime * 3.0) + 1.0) * 0.5
		#@wing1.angle = lerp(-30, 30, flapT)
		#@wing2.angle = - @wing1.angle
		if g.keys.a.tapped or key1.tapped => @flapRight!
		if g.keys.f.tapped or key2.tapped => @flapLeft!
		# 
		@flap1t += g.dt * 3
		@flap1t = 1 if @flap1t > 1.0
		t = @flap1t * @flap1t
		@wing1.angle = - lerp(30, -30, Math.sin(t * g.pi))
		# 
		@flap2t += g.dt * 3
		@flap2t = 1 if @flap2t > 1.0
		t = @flap2t * @flap2t
		@wing2.angle = lerp(30, -30, Math.sin(t * g.pi))
		#print @worldpos.x + " | " + @worldpos.y
		@vy += 120 * g.dt
		@worldpos.x += @vx * g.dt
		@worldpos.y += @vy * g.dt
		if mich?
			vx = mich.worldpos.x - @worldpos.x
			vy = mich.worldpos.y - @worldpos.y
			l = vlength(vx, vy)
			if l < 200
				void
				#@die!
				#mich.die!
				#cata?.group.visible = false
				#whee := new WheeParachute
				#brush := new Brush
				#new Pointer(whee, brush)
				#camera.snapTo(whee)
				
		drot = Math.atan2(@vy, @vx) + g.pi * 0.5
		@group.rotation = @group.rotation + (drot - @group.rotation) * 0.3
		@group.x = @worldpos.x
		@group.y = @worldpos.y
#------------------------------------------------------
class Brush extends Actor
	->
		super!
		@className = @constructor.name
		@createImage("brush", 0, 0, 0.5, 0.2)
		@worldpos.x = 2000 * 0.66666 + frand(-600, 600)
		@worldpos.y = -4000 * 0.66666 * 0.4
		if g.dev_closerParachute
			@worldpos.x *= 0.7
			@worldpos.y *= 0.75
		@addToLayer(layerGame)
	tick:!->
		if whee?
			vx = whee.worldpos.x - @worldpos.x
			vy = whee.worldpos.y - @worldpos.y
			l = vlength(vx, vy)
			if l < 200
				playAA(aa.hit)
				tasks.next!
				tank := new Tank
				cata?.group.visible = false
				@die!
				new Pointer(whee, tank)
		@group.x = @worldpos.x
		@group.y = @worldpos.y
#------------------------------------------------------
class Drone extends Actor
	->
		super!
		@className = @constructor.name
		@worldpos.x = mich.worldpos.x
		@worldpos.y = mich.worldpos.y
		@heli1 = @createImage("heli1", 0, 0, 0.5, 0.5)
		@heli2 = @createImage("heli2", 0, 0, 0.5, 0.5)
		@heli2.visible = false
		#print @worldpos.x + "," + @worldpos.y
		@nextSwap = 0
		@vx = 0
		@vy = 0
		@n = 0.6
		@tick!
		@desty = @worldpos.y - 5000
		camera.snapTo(@)
		camera.offx = 0
		camera.offy = 0
		@addToLayer(layerGame)
		@reached = false
	tick:!->
		tasks.fail! if @worldpos.y > 5000
		#@vy += 200 * g.dt
		#if @nextSwap < gtime
		#	@nextSwap = gtime + @n
		#	@heli1.visible = !@heli1.visible
		#	@heli2.visible = !@heli1.visible
		if g.keys.a.tapped or g.keys.f.tapped or key1.tapped or key2.tapped
			@n -= 0.05
			if @n < 0.1 => @n = 0.1
			@vy -= 40
			@heli1.visible = !@heli1.visible
			@heli2.visible = !@heli1.visible
			playAA(aa.flaps)
		@vy += 300 * g.dt
		#@worldpos.x += @vx * g.dt
		@worldpos.y += @vy * g.dt
		#print @worldpos.y
		@group.x = @worldpos.x
		@group.y = @worldpos.y
		#print @worldpos.y
		if !@reached
			if @worldpos.y < @desty
				@reached = true
				# win!
				new Heaven
				tasks.next!
				key1.goNotCrazy!
				key2.goNotCrazy!
				reset.die!
				key1.die!
				key2.die!
				@die!
			#	new Reached
#------------------------------------------------------
class Reached extends Actor
	->
		for a in actors => a.die! if !a.keep
		key1.group.visible = false
		key2.group.visible = false
		super!
		@createImage("heaven")
		@group.x = g.cx
		@group.y = g.cy
		@addToLayer(layerUI)
#------------------------------------------------------
class Pointer extends Actor
	(src, dest, right=false, up=false)->
		super!
		@className = @constructor.name
		@right = right
		@up = up
		@src = src
		@dest = dest
		@createImage("circlearrow")
		@addToLayer(layerUI)
	tick:!->
		if @dest? and @src?
			if @right
				ax = @dest.worldpos.x - @src.worldpos.x
				l = Math.abs(ax)
				if l > 300
					@group.x = g.cx + 300
					@group.y = g.cy - 50
					@group.rotation = g.pi * 0.5
					@group.visible = true
				else
					@group.visible = false
			if @up
				ax = @dest.worldpos.x - @src.worldpos.x
				l = Math.abs(ax)
				if l > 300
					@group.x = g.cx
					@group.y = g.cy - 300
					@group.rotation = g.pi * 2
					@group.visible = true
				else
					@group.visible = false
			else
				ax = @dest.worldpos.x - @src.worldpos.x
				ay = @dest.worldpos.y - @src.worldpos.y
				l = vlength(ax, ay)
				if l > 300
					@group.visible = true
					ax /= l
					ay /= l
					@group.x = g.cx + ax * 300
					@group.y = g.cy + ay * 300
					@group.rotation = Math.atan2(ay, ax) + g.pi * 0.5
				else
					@group.visible = false
	onActorDie:(a)!->
		@die! if a == @src or a == @dest
#------------------------------------------------------
class Puff extends Actor
	(x,y,vx=0,vy=0)->
		super!
		@className = @constructor.name
		@img = @createImage(pick(puffImages), 0, 0, 0.5, 0.5, 1.0)
		@worldpos.x = x
		@worldpos.y = y
		@vx = vx
		@vy = vy
		@avel = frand(20, 200) * rsign!
		@age = 0
		@maxage = frand(0.75, 1.5)
		@addToLayer(layerGame)
		@tick!
	tick:!->
		t = saturate(@age / @maxage)
		@die! if t >= 1
		scale = lerp(1.0, 0.6, t)
		@group.scale.x = scale
		@group.scale.y = scale
		@group.alpha = lerp(1.0, 0, t)
		@group.x += @vx * g.dt
		@group.y += @vy * g.dt
		@group.angle += @avel * g.dt
		@group.x = @worldpos.x
		@group.y = @worldpos.y
		@age += g.dt
#------------------------------------------------------
class Sky extends Actor
	->
		super!
		@keep = true
		@className = @constructor.name
		#@nclouds = 20
		@ex = 2500
		@ey = 1500
		#@clouds = []
		#for a til @nclouds 
		#	@clouds[a] = @createImage(pick(cloudImages), 0, 0, 0.5, 0.5, 1.0)
		@img = phaser.add.tileSprite(0, 0, 1280, 720, "sky")
		@img.alpha = 0.5
		@group?.add(@img)
		#@addToLayer(layerUI)
	tick:!->
		#for a in @clouds =>
		#	dx = Math.abs(camera.worldCenterX - a.x)
		#	dy = Math.abs(camera.worldCenterY - a.y)
		#	if dx > @ex or @dy > @ey
		#		a.x = camera.worldCenterX + frand(g.cx * 1.25) * rsign!
		#		a.y = camera.worldCenterY + frand(g.cy * 1.25) * rsign!
		@img.tilePosition.x = -camera.worldCenterX
		@img.tilePosition.y = -camera.worldCenterY
	recreate:!->
		#for a in @clouds =>
		#	a.x = camera.worldCenterX
		#	a.y = camera.worldCenterY
	onResetWorld:!-> @recreate!
#------------------------------------------------------
class Door extends Actor
	->
		super!
		@className = @constructor.name
		@base = @createImage("door1")
		@worldpos.x = tank.worldpos.x + 1500
		#@worldpos.x = tank.worldpos.x + 100
		@react = false
		@worldpos.y = tank.worldpos.y - 550
		@addToLayer(layerGame)
		@tick!
	tick:!->
		if tank? and !@react
			# 280
			#print tank.worldpos.x + "," + @worldpos.x
			#if tank.worldpos.x > @worldpos.x
			#	print tank.speed
			if tank.worldpos.x > @worldpos.x
				@base.visible = false
				camera.snapTo(@)
				camera.offy = 400
				playAA(aa.crash)
				#if true or tank.speed > 250
				print "tank speed was " + tank.speed
				print "needed " + speedToBreakDown
				if g.dev_fragileDoor or tank.speed > speedToBreakDown
					print "busted down door"
					@base.visible = false
					@newimg = @createImage("door3")
					@newEvent("next", null, 1.0)
					tasks.next!
				else
					tasks.fail!
					speedToBreakDown := speedToBreakDown - 60
					print "FAILED to bust open door"
					@newimg = @createImage("door2")
					@newEvent("fail", null, 1.0)
				#tank.speed = 0
				key1.goNotCrazy!
				key2.goNotCrazy!
				#tank.die!
				#tank.group.visible = false
				#tank := null
				tank.die!
				tank := null
				@react = true
		@group.x = @worldpos.x
		@group.y = @worldpos.y
	next:!->
		@die!
		wingman2 := new WingMan2
		mich := new Mich
		camera.offy = 0
		new Pointer(wingman2, mich)
		camera.snapTo(wingman2)
		tank?.die?!
		tank := null
	fail:!->
		void
#------------------------------------------------------
class Tasks extends Actor
	->
		super!
		@className = @constructor.name
		@keep = true
		@set(1)
		@failT = 1
		@group.x = 20
		@group.y = 20
		@failed = false
		@canFail = true
		@addToLayer(layerUI)
		#@fail!
	set:(n)!->
		@curr = n
		@img?.destroy!
		@img = @createImage("list" + n)
	next:!->
		@set(@curr+1)
		playAA(aa.scratch)
	fail:!->
		if !@failed and @canFail
			playAA(aa.hit)
			@failImage ?= @createImage("fail", 160, 100, 0.5, 0.5)
			@failT = 0
			@failed = true
			key1?.group.visible = false
			key2?.group.visible = false
			reset?.group.visible = false
			#@newEvent("finishFailing", null, 1.0)
			print "failed"
			@canFail = false
			# nasty recursive bug otherwise
			resetWorldDelay!
	#finishFailing:!->
		#print "finish failing"
		#resetWorld!
	tick:!->
		@failT = saturate(@failT + g.dt * 5)
		s = lerp(1.5, 1, @failT)
		@failImage?.scale.x = s
		@failImage?.scale.y = s
	onResetWorld:!->
		@set(1)
		@failImage?.destroy!
		@failed = false
		key1?.group.visible = true
		key2?.group.visible = true
		reset?.group.visible = true
#------------------------------------------------------
class Reset extends Actor
	->
		super!
		@className = @constructor.name
		@keep = true
		@reset = @createImage("reset", 0, 0, 0.5, 0.5, 1)
		@group.x = g.width - 70
		@group.y = 50
		@reset.inputEnabled = true
		@reset.input.useHandCursor = true
		@reset.events.onInputDown.add(@onclick, this)
		@reset.events.onInputOver.add(@onhover, this)
		@reset.events.onInputOut.add(@onhoverout, this)
		@addToLayer(layerUI)
	onhover:!->
		@reset.scale.x = 1.1
		@reset.scale.y = 1.1
	onhoverout:!->
		@reset.scale.x = 1.0
		@reset.scale.y = 1.0
	onclick:!->
		resetWorld!
#------------------------------------------------------
#class DaVinciDrone extends Actor
#	->
#		super!
#		@heli1 = @createImage("heli1")
#		@heli2 = @createImage("heli2")
#		@s = 0
#		key1.goCrazy!
#		key2.goCrazy!
#		@vx = 0
#		@vy = 0
#		@worldpos.x = mich.worldpos.x
#		@worldpos.y = mich.worldpos.y
#		@addToLayer(layerGame)
#	tick:!->
#		@vy += 200 * g.dt
#		@vx = 0
#		@worldpos.x += @vx * g.dt
#		@worldpos.y += @vy * g.dt
#		@s += 5.0 * g.dt
#		if @s > 8.0
#			@s = 8.0
#		s = Math.sin(gtime * @s)
#		if s < 0
#			@heli1.visible = true
#		#if g.keys.a.tapped or g.keys.f.tapped
#		#	@vy -= -100 * g.dt
#		@heli2.visible = !@heli1.visible
#------------------------------------------------------
class Mich extends Actor
	->
		super!
		@worldpos.x = door.worldpos.x + 900
		@worldpos.y = door.worldpos.y - 5000 + frand(-300, 300)
		@base = @createImage("mich1", 0.5, 0.2)
		@bubble = @createImage("bubble")
		@bubble.visible = false
		@addToLayer(layerGame)
		@snapped = false
		@tick!
	tick:!->
		if wingman2? and !@snapped
			vx = wingman2.worldpos.x - @worldpos.x
			vy = wingman2.worldpos.y - @worldpos.y
			len = vlength(vx, vy)
			if len < 250 =>
				tasks.next!
				key1.goNotCrazy!
				key2.goNotCrazy!
				wingman2.die!
				wingman2 := null
				@bubble.visible = true
				playAA(aa.hmm)
				@newEvent("bless", null, 4.5)
				@bubble.x = -300
				@bubble.y = -280
				camera.snapTo(@)
				camera.offx = 0
				camera.offy = 0
				#@group.visible = false
				@snapped = true
		@group.x = @worldpos.x
		@group.y = @worldpos.y
	bless:!->
		drone := new Drone
		tasks.next!
		key1.goCrazy!
		key2.goCrazy!
		#heaven := new Heaven
		#new Pointer(drone, heaven)
#------------------------------------------------------
class Heaven extends Actor
	->
		super!
		@className = @constructor.name
		#@worldpos.x = drone.worldpos.x
		#@worldpos.y = -4000 * 0.66666
		playAA(aa.sing1)
		@createImage("heaven", 0, 0, 0.5, 0.5, 1)
		@group.x = g.cx
		@group.y = g.cy
		@addToLayer(layerUI)
	tick:!->
#------------------------------------------------------
class Parachute extends Actor
	->
		super!
		@className = @constructor.name
		@worldpos.x = 2000 * 0.66666
		@worldpos.y = -4000 * 0.66666
		if g.dev_closerParachute
			@worldpos.x *= 0.7
			@worldpos.y *= 0.75
		@worldpos.y -= 800
		@createImage("parachute")
		@addToLayer(layerGame)
	tick:!->
		@group.x = @worldpos.x
		@group.y = @worldpos.y
#------------------------------------------------------
class Tank extends Actor
	->
		super!
		@className = @constructor.name
		@base = @createImage("tank1", 0, 0, 0.5, 0.5)
		@boarded = false
		@speed = 0
		@worldpos.x = 2000 * 0.66666 + frand(-300, 300)
		@worldpos.y = g.ground
		@bob = 1
		if g.dev_closerParachute
			@worldpos.x *= 0.7
			@worldpos.y *= 0.75
		@addToLayer(layerGame)
		@tick!
	board:!->
		@boarded = true
		tasks.next!
		door := new Door
		camera.snapTo(@)
		key1.goCrazy!
		key2.goCrazy!
		new Pointer(@, door, true)
		@newEvent("putLidOn", null, 0.33333)
	putLidOn:!->
		@base.visible = false
		@lid = @createImage("tank2", 0, 0, 0.5, 0.5)
	tick:!->
		if @boarded
			if g.keys.a.tapped or g.keys.f.tapped or key1.tapped or key2.tapped
				new Puff(@worldpos.x + frand(-150,25), @worldpos.y + frand(-50,50), 0, 0)
				playAA(aa.bubbles)
				@speed += 3
				@bob = 0
			@worldpos.x += @speed * g.dt
		if whee? and !@boarded
			vx = whee.worldpos.x - @worldpos.x
			vy = whee.worldpos.y - @worldpos.y
			#print "vx " + vx
			#print "vy " + vy
			len = vlength(vx, vy)
			if len < 200 =>
				whee.die!
				whee := null
				@board!
		#print @speed
		@bob += g.dt * 6
		@bob = saturate(@bob)
		@lid?.angle = lerp(0, 6, Math.sin(@bob * g.pi))
		#
		@group.x = @worldpos.x
		@group.y = @worldpos.y
#------------------------------------------------------
class WheeParachute extends Actor
	->#
		super!
		@className = @constructor.name
		@worldpos.x = 2000 * 0.66666
		@worldpos.y = -4000 * 0.66666
		if g.dev_closerParachute
			@worldpos.x *= 0.7
			@worldpos.y *= 0.75
		@createImage("whee", 0, 0, 0.5, 0.5)
		@velx = 0
		@vely = 0
		@addToLayer(layerGame)
	tick:!->
		tasks.fail! if @worldpos.y > 5000
		@vely += 120 * g.dt
		if g.keys.a.isDown or key1.down
			@velx -= 300 * g.dt
		if g.keys.f.isDown or key2.down
			@velx += 300 * g.dt
		c = 30
		dang = clamp(@velx / 100.0, -c, c)
		@group.angle = @group.angle + (dang - @group.angle) * 0.3
		@worldpos.x += @velx * g.dt
		@worldpos.y += @vely * g.dt
		@group.x = @worldpos.x
		@group.y = @worldpos.y
#------------------------------------------------------
class Key extends Actor
	(x, image, letter)->
		super!
		@className = @constructor.name
		@keep = true
		@letter = letter
		@down = false
		@downFrames = 0
		@tapped = false
		@nextDownUp = 0
		if g.dev_desktop
			@img = @createImage(image, 0, 0, 0.5, 0.5)
		else
			@img = @createImage("button", 0, 0, 0.5, 0.5)
			@img.inputEnabled = true
			@img.input.useHandCursor = true
			@img.events.onInputDown.add(@ondown, this)
			@img.events.onInputUp.add(@onup, this)
		@crazy = false
		@arrow = @createImage("arrow", 0, 0, 0.5, 0.5)
		@arrow.x = -10
		@arrow.y = -70
		@group.x = x
		@group.y = g.height - 80
		@tick!
		#@goCrazy!
		@addToLayer(layerUI)
	ondown:!->
		@down = true
		@tapped = @downFrames <= 0
		@nextDownUp = gtime + 0.1
	onup:!->
		@down = false
		@downFrames = 0
		@tapped = false
	goCrazy:!->
		@arrow.visible = true
		@crazy = true
	goNotCrazy:!->
		@arrow.visible = false
		@crazy = false
	tick:!->
		if @down => @downFrames++
		if @downFrames > 1 => @tapped = false
		#print "down" + @down
		#if @nextDownUp < gtime
		#	@down = false
		if @letter.isDown or @down =>
			@group.scale.x = 1.0
			@group.scale.y = 1.0
		else
			@group.scale.x = 0.9
			@group.scale.y = 0.9
		if @crazy
			@arrow.y = -70 + Math.sin(gtime * 40) * 7
	onResetWorld:!->
		@goNotCrazy!
#------------------------------------------------------
class Camera extends Actor
	->
		super!
		@className = @constructor.name
		@keep = true
		# world space offsets
		@offx = 0
		@offy = 0
		@worldCenterX = 0
		@worldCenterY = 0
	snapTo:(thing, instant=false)!->
		@snapThing = thing
		if instant
			layerGame.x = @snapX!
			layerGame.y = @snapY!
	snapX:-> - (@snapThing.worldpos.x + @offx) + g.cx
	snapY:-> - (@snapThing.worldpos.y + @offy) + g.cy
	onResetWorld:!->
		@offx = 0
		@offy = 0
	onActorDie:(a)!->
		if a == @snapThing
			@snapThing = null
	tick:!->
		if @snapThing?
			layerGame.x = layerGame.x + (@snapX! - layerGame.x) * 0.5
			layerGame.y = layerGame.y + (@snapY! - layerGame.y) * 0.5
		@worldCenterX = -layerGame.x + g.cx
		@worldCenterY = -layerGame.y + g.cy
#------------------------------------------------------
playTrack=(track)!->
	if g.dev_music and track?
		if currMusicTrack != track
			currMusicTrack?.stop!
			currMusicTrack := track
			currMusicTrack.play(null, 0, musicVolume!, true, true)
musicVolume=-> (1.0)
playAA=(thing, volume=1)!->
	if g.dev_soundfx and thing?
		if thing.length? and thing.length > 0
			playAA(pick(thing), volume)
		else
			thing.play(null, 0, volume, false)
gcache=!->
	if g.dev_desktop
		loadImage("key1")
		loadImage("key2")
	else
		loadImage("button")
	loadImage("canvas")
	loadImage("cata1")
	#loadImage("cata2")
	#loadImage("cata3")
	loadImage("cata4")
	loadImage("cata5")
	loadImage("meter")
	loadImage("swirl")
	loadImage("torso")
	loadImage("wing1")
	loadImage("wing2")
	loadImage("arrow")
	loadImage("circlearrow")
	loadImage("parachute")
	loadImage("whee")
	loadImage("brush")
	loadImage("tank1")
	loadImage("tank2")
	loadImage("door1")
	loadImage("door2")
	loadImage("door3")
	loadImage("mich1")
	loadImage("bubble")
	loadImage("heli1")
	loadImage("heli2")
	loadImage("reset")
	loadImage("heaven")
	for a from 1 to 11 => 
		puffImages[a] = "puff" + a
		loadImage(puffImages[a])
	#for a from 1 to 5 => 
	#	cloudImages[a] = "cloud" + a
	#	loadImage(cloudImages[a])
	for a from 1 to 9 => loadImage("list" + a)
	loadImage("fail")
	loadImage("sky")
	loadImage("reset")
	for a,b of aa
		phaser.load.audio(a, a + ".mp3")
	for a,b of music
		phaser.load.audio(a, a + ".mp3")
gcreate=!->
	g.keys = []
	g.keys.a = phaser.input.keyboard.addKey(Phaser.Keyboard.A)
	g.keys.f = phaser.input.keyboard.addKey(Phaser.Keyboard.F)
	g.keys.esc = phaser.input.keyboard.addKey(Phaser.Keyboard.ESC)
	layerBack := phaser.add.group!
	layerGame := phaser.add.group!
	layerUI := phaser.add.group!
	camera := new Camera
	new Sky
	reset := new Reset
	tasks := new Tasks
	key1 := new Key(100, "key1", g.keys.a)
	key2 := new Key(g.width - 100, "key2", g.keys.f)
	new BG
	for a,b of aa
		aa[a] = phaser.add.audio(a)
	for a,b of music
		music[a] = phaser.add.audio(a)
	finalizeAA!
	resetWorld!
	playAA(aa.hit)
	playTrack(music.song_20160828_140724_215)
updateActors=!->
	for a in actors => a.checkEvents?!
	for a in actors => a.tick?!
	deadones = [a for a in actors when a.dead]
	for a in deadones
		a.cleanup?!
		dispatch("onActorDie", a)
	# this bugs if .cleanup sets other .dead=true
	#actors := reject (.dead), actors
	for a in deadones => remove(actors, a)
gupdate=!->
	if nextResetWorld > 0 and nextResetWorld < gtime
		nextResetWorld := 0
		resetWorld!
	for a,b of g.keys
		if b.isDown
			b.frames++
		else
			b.frames = 0
		b.tapped = (b.frames == 1)
	gtime := gtime + g.dt
	updateActors!
	if g.keys.esc.tapped
		resetWorld!
resetWorldDelay=(delay)!->
	nextResetWorld := gtime + 1.5
resetWorld=!->
	cata := null
	wingman := null
	wingman2 := null
	mich := null
	parachute := null
	whee := null
	brush := null
	tank := null
	door := null
	drone := null
	heaven := null
	updateActors!
	for a in actors => a.die! if !a.keep
	if g.dev_startFromCatapult
		cata := new Catapult
		camera.snapTo(cata, true)
	else
		#whee := new WheeParachute
		#camera.snapTo(whee, true)
		#new Door
		tank := new Tank
		tank.board!
		#mich := new Mich
	camera.offx = 0
	camera.offy = 0
	dispatch("onResetWorld")
	# !!!
	#new WingMan
phaser = new Phaser.Game(g.width, g.height, Phaser.CANVAS, '')
phaser.state.add("Boot", Boot)
phaser.state.add("Preload", Preload)
phaser.state.add("Game", Game)
phaser.state.start("Boot")
























































