Clean up comments for Network.gd

Clean up comments for Network.gd

# Core networking

extends Node

const REMOTE_FINISH = 0
const REMOTE_START = 1
const REMOTE_QUIT = 2
const REMOTE_BLOCK = 4
const REMOTE_BLOCK_TRANSFORM = 5
const REMOTE_BLOCK_UPDATE = 6

var port = 54321
var root
var isNetwork
var isHost
var isClient = false
var server
var client
var connection
var proxy

var remotePuzzle

#Store the peer stream used by both the lcient and server
var stream

func _ready():
	isNetwork = false
	isHost = false
	var label = get_tree().get_root().get_node("GUIManager/OptionsMenu/Panel/PortField/LineEdit")
	if not label == null:
		label.set_text(str(port))

func setPort(pt):
	port = pt;

func connectTo(ip, pt):
	isClient = true #just to tell if it's doing something :S
	connection = PacketPeerStream.new()
	stream = StreamPeerTCP.new()
	connection.set_stream_peer(stream)
	stream.connect(ip, pt);

	if stream.get_status() == stream.STATUS_CONNECTED or stream.get_status() == stream.STATUS_CONNECTING:
		print("Connecting to " + ip + ":" + str(port));
		proxy.set_process(true)
		#leave is_network as false to indicate we're still waiting for connection

func host(pt):
	server = TCP_Server.new()
	#connection = PacketPeerStream.new()
	isHost = true
	print("Starting listening server on port " + str(pt))
	if server.listen(pt) == 0:
		proxy.set_process(true)
		print("Listening...")
	else:
		print("Failed to start server on port " + str(pt));

func _process(delta):
	if (isHost):
		#server processing stuff
		#use isNetwork to determine if we're still listening
		if !isNetwork:
			if server.is_connection_available():
				stream = server.take_connection()
				connection = PacketPeerStream.new()
				connection.set_stream_peer(stream)
				print("Connecting with player...")
				isNetwork = true
				server.stop()
				changeScene("res://puzzle.scn")
				remotePuzzle = root.get_node("Spatial")
				#MAKE CALL TO PUZZLE SELECTER
		else: #not listening anymore, have a client
			#do quick check to make sure we're still
			#connected
			if !stream.is_connected():
				print("Lost Connection!");
				isNetwork = false
				proxy.set_process(false)
				#QUIT GAME
				return
			#check if we have any data
			if connection.get_available_packet_count() > 0:
				#have to be careful about more than 1 packet per frame
				for i in range(connection.get_available_packet_count()):
					var dataArray = connection.get_var()
					ProcessServerData(dataArray)

	else:
		#client processing stuff
		if !isNetwork:
			#Still waiting for connection confirmed
			if stream.get_status() == stream.STATUS_CONNECTED:
				print("Connection established!")
				isNetwork = true
				changeScene("res://puzzle.scn")
				remotePuzzle = root.get_node("Spatial")
				return
			if stream.get_status() == stream.STATUS_NONE or stream.get_status() == stream.STATUS_ERROR:
				print("Error establishing connection!")
				proxy.set_process(false)
				return
				#stop running process loop, cause we have no connection
		else:
			#connecton established and confirmed. Do regular data processing
			#check if we have any data
			if connection.get_available_packet_count() > 0:
				for i in range(connection.get_available_packet_count()):
					var dataArray = connection.get_var()
					ProcessServerData(dataArray)
					#Call the server process script cuase it's peer to peer and we have the same functions!

func disconnect():
	#need to do lots of things! If the server is open and listening, but has no connection just stop listening!
	if isHost and !isNetwork:
		#this means we're waiting for connection still
		server.stop()
		print("closing server listener!")
	elif isHost and isNetwork:
		#have connections. Need to send them stop packet, and close connection
		connection.put_var([REMOTE_QUIT])
		stream.disconnect()
		print("Closing connection to remote player...")
	elif !isHost and !isNetwork:
		stream.disconnect()
		print("Halting connection request...")
	elif !isHost and isNetwork:
		#connected to server already
		stream.disconnect()
		print("Disconnecting from server...")

	#no matter where we wre before disconnect, set network to false and return to beginning screen!
	isNetwork = false;
	isHost = false;
	isClient = false;

	proxy.set_process(false)

	changeScene("res://menus.scn")

func ProcessServerData(dataArray):
	#have an array of data. First element should be identifying int
	var ID = dataArray[0]
	print(ID)
	#no switch statement. I'm crying right now while i type this. My fingers are bleeding
	if ID == REMOTE_START:
		#do start something or something whut
		print("remote_stuff")
	elif ID == REMOTE_FINISH:
		#again, do ending stuff
		print("remote_finish: " + str(dataArray[1]))
	elif ID == REMOTE_QUIT:
		#omg those jerks!
		print("remote_quit")
		disconnect()
	elif ID == REMOTE_BLOCK:
		#get their block ifnormation!
		print("remote_block")
	elif ID == REMOTE_BLOCK_TRANSFORM:
		#sent block information
		print("block TRANSFORM!!!!!!!!!!!")
		#var scale = dataArray[1]
		var translation = dataArray[1]
		#remotePuzzle.otherPuzzle.set_scale(scale)
		remotePuzzle.otherPuzzle.set_transform(Transform( translation ))
		remotePuzzle.otherPuzzle.set_translation(Vector3(20, 12, -40))
	elif ID == REMOTE_BLOCK_UPDATE:
		#sent an updated block pair
		print("block update")
		var gridMan = root.get_node( "Spatial/GridView/GridMan" )
		var pos1 = dataArray[1]
		var pos2 = dataArray[2]
		gridMan.remove_block(pos1)
		gridMan.remove_block(pos2)

func sendStart():
	if !isNetwork:
		print("Error sending start packet: not connected!")
		return
	var er = connection.put_var([REMOTE_START])
	print("Sending start and got [" + str(er) + "]")

func sendBlockUpdate(pos1, pos2):
	if !isNetwork:
		print("Error sending start packet: not connected!")
		return
	connection.put_var([REMOTE_BLOCK_UPDATE, pos1, pos2])

func sendFinish(score):
	if !isNetwork:
		print("Error sending finish packet: not connected!")
		return
	connection.put_var([REMOTE_FINISH, score])

func sendQuit():
	if !isNetwork:
		print("Error sending finish packet: not connected!")
		return
	connection.put_var([REMOTE_QUIT])

func sendTransform(translation):
	if !isNetwork:
		print("Cannot send transformation over an unitialized network!")
		return
	connection.put_var([REMOTE_BLOCK_TRANSFORM, translation])
	print("it got here")

func changeScene(scene):
	root.get_child( root.get_child_count() - 1 ).queue_free()
	root.add_child( ResourceLoader.load( scene ).instance() )

extends Node

const REMOTE_FINISH = 0
const REMOTE_START = 1
const REMOTE_QUIT = 2
const REMOTE_BLOCK = 4
const REMOTE_BLOCK_TRANSFORM = 5
const REMOTE_BLOCK_UPDATE = 6

var port = 54321
var root
var isNetwork
var isHost
var isClient = false
var server
var client
var connection
var proxy

var remotePuzzle

#Store the peer stream used by both the lcient and server
var stream

func _ready():
	isNetwork = false
	isHost = false
	var label = get_tree().get_root().get_node("GUIManager/OptionsMenu/Panel/PortField/LineEdit")
	if not label == null:
		label.set_text(str(port))

func setPort(pt):
	port = pt;

func connectTo(ip, pt):
	isClient = true #just to tell if it's doing something :S
	connection = PacketPeerStream.new()
	stream = StreamPeerTCP.new()
	connection.set_stream_peer(stream)
	stream.connect(ip, pt);

	if stream.get_status() == stream.STATUS_CONNECTED or stream.get_status() == stream.STATUS_CONNECTING:
		print("Connecting to " + ip + ":" + str(port));
		proxy.set_process(true)
		#leave is_network as false to indicate we're still waiting for connection

func host(pt):
	server = TCP_Server.new()
	#connection = PacketPeerStream.new()
	isHost = true
	print("Starting listening server on port " + str(pt))
	if server.listen(pt) == 0:
		proxy.set_process(true)
		print("Listening...")
	else:
		print("Failed to start server on port " + str(pt));

func _process(delta):
	#print("Spam")
	#self.omg()
	if (isHost):
		#server processing stuff
		#use isNetwork to determine if we're still listening
		if !isNetwork:
			if server.is_connection_available():
				stream = server.take_connection()
				connection = PacketPeerStream.new()
				connection.set_stream_peer(stream)
				print("Connecting with player...")
				isNetwork = true
				server.stop()
				changeScene("res://puzzle.scn")
				remotePuzzle = root.get_node("Spatial")
				#MAKE CALL TO PUZZLE SELECTER
		else: #not listening anymore, have a client
			#do quick check to make sure we're still
			#connected
			if !stream.is_connected():
				print("Lost Connection!");
				isNetwork = false
				proxy.set_process(false)
				#QUIT GAME
				return
			#check if we have any data
			if connection.get_available_packet_count() > 0:
				#have to be careful about more than 1 packet per frame
				for i in range(connection.get_available_packet_count()):
					var dataArray = connection.get_var()
					ProcessServerData(dataArray)

	else:
		#client processing stuff
		if !isNetwork:
			#Still waiting for connection confirmed
			if stream.get_status() == stream.STATUS_CONNECTED:
				print("Connection established!")
				isNetwork = true
				changeScene("res://puzzle.scn")
				remotePuzzle = root.get_node("Spatial")
				return
			if stream.get_status() == stream.STATUS_NONE or stream.get_status() == stream.STATUS_ERROR:
				print("Error establishing connection!")
				proxy.set_process(false)
				return
				#stop running process loop, cause we have no connection
		else:
			#connecton established and confirmed. Do regular data processing
			#check if we have any data
			if connection.get_available_packet_count() > 0:
				for i in range(connection.get_available_packet_count()):
					var dataArray = connection.get_var()
					ProcessServerData(dataArray)
					#Call the server process script cuase it's peer to peer and we have the same functions!

func disconnect():
	#need to do lots of things! If the server is open and listening, but has no connection just stop listening!
	if isHost and !isNetwork:
		#this means we're waiting for connection still
		server.stop()
		print("closing server listener!")
	elif isHost and isNetwork:
		#have connections. Need to send them stop packet, and close connection
		connection.put_var([REMOTE_QUIT])
		stream.disconnect()
		print("Closing connection to remote player...")
	elif !isHost and !isNetwork:
		stream.disconnect()
		print("Halting connection request...")
	elif !isHost and isNetwork:
		#connected to server already
		stream.disconnect()
		print("Disconnecting from server...")

	#no matter where we wre before disconnect, set network to false and return to beginning screen!
	isNetwork = false;
	isHost = false;
	isClient = false;

	proxy.set_process(false)

	changeScene("res://menus.scn")

func ProcessServerData(dataArray):
	#have an array of data. First element should be identifying int
	var ID = dataArray[0]
	print(ID)
	#no swithc statement. I'm crying right now while i type this. My fingers are bleeding
	if ID == REMOTE_START:
		#do start something or something whut
		print("remote_stuff")
	elif ID == REMOTE_FINISH:
		#again, do ending stuff
		print("remote_finish: " + str(dataArray[1]))
	elif ID == REMOTE_QUIT:
		#omg those jerks!
		print("remote_quit")
		disconnect()
	elif ID == REMOTE_BLOCK:
		#get their block ifnormation!
		print("remote_block")
	elif ID == REMOTE_BLOCK_TRANSFORM:
		#sent block information
		print("block TRANSFORM!!!!!!!!!!!")
		#var scale = dataArray[1]
		var translation = dataArray[1]
		#remotePuzzle.otherPuzzle.set_scale(scale)
		remotePuzzle.otherPuzzle.set_transform(Transform( translation ))
		remotePuzzle.otherPuzzle.set_translation(Vector3(20, 12, -40))
	elif ID == REMOTE_BLOCK_UPDATE:
		#sent an updated block pair
		print("block update")
		var gridMan = root.get_node( "Spatial/GridView/GridMan" )
		var pos1 = dataArray[1]
		var pos2 = dataArray[2]
		gridMan.remove_block(pos1)
		gridMan.remove_block(pos2)

func sendStart():
	if !isNetwork:
		print("Error sending start packet: not connected!")
		return
	var er = connection.put_var([REMOTE_START])
	print("Sending start and got [" + str(er) + "]")

func sendBlockUpdate(pos1, pos2):
	if !isNetwork:
		print("Error sending start packet: not connected!")
		return
	connection.put_var([REMOTE_BLOCK_UPDATE, pos1, pos2])

func sendFinish(score):
	if !isNetwork:
		print("Error sending finish packet: not connected!")
		return
	connection.put_var([REMOTE_FINISH, score])

func sendQuit():
	if !isNetwork:
		print("Error sending finish packet: not connected!")
		return
	connection.put_var([REMOTE_QUIT])

func sendTransform(translation):
	if !isNetwork:
		print("Cannot send transformation over an unitialized network!")
		return
	connection.put_var([REMOTE_BLOCK_TRANSFORM, translation])
	print("it got here")

func changeScene(scene):
	root.get_child( root.get_child_count() - 1 ).queue_free()
	#root.get_child( 0).queue_free()
	root.add_child( ResourceLoader.load( scene ).instance() )


call return after deleting self. very important. GODOT BUG, CAUSED SEGFAULT.

extends RigidBody

var name_int
var name
var pairName
var selected = false
var blockPos
var blockLayer
var blockType
const far_away_corner = Vector3(110, 110, 110)

var editor

static func nameToNodeName(n):
	return "block" + str(n)

func setName(n):
	name_int = n
	name = nameToNodeName(n)
	set_name(nameToNodeName(n))
	return self

func setPairName(n):
	pairName = nameToNodeName(n)
	return self

func setBlockLayer(n):
	blockLayer = n
	return self

func getBlockLayer():
	return blockLayer

func setBlockType( blockT ):
	blockType = blockT
	return self

func getBlockType():
	return blockType

func setSelected(sel):
	selected = sel
	return self

func forceClick():
	activate()
	get_parent().clickBlock( name )

func addNeighbor(editor, click_normal):
	var t = get_parent().get_parent().get_transform().inverse()
	var b = editor.newPickledBlock().setBlockPos(blockPos + t * click_normal)
	get_parent().add_block(b.toNode())

# catch clicks/taps
func _input_event( camera, ev, click_pos, click_normal, shape_idx ):
	if ((ev.type==InputEvent.MOUSE_BUTTON and ev.button_index==BUTTON_LEFT)
	or (ev.type==InputEvent.SCREEN_TOUCH)):
		if (get_parent().get_parent().active and ev.is_pressed()):
			if editor != null:
				if editor.shouldAddNeighbor():
					addNeighbor(editor, click_normal)
					return
				if editor.shouldReplaceSelf():
					addNeighbor(editor, 0 * click_normal)
					remove_with_pop(self, null)
					return
				if editor.shouldRemoveSelf():
					remove_with_pop(self, null)
					return
			else:
				forceClick()

# returns this block's pairNode or null
func pairActivate():
	selected = true

	# is my pair Nil?
	if pairName == null or get_parent() == null or not get_parent().has_node(pairName):
		scaleTweenNode(0.9, 0.2, Tween.TRANS_EXPO).start()
		return null

	# get my pair node
	var pairNode = get_parent().get_node(pairName)

	# enlarge if the other guy's unselected
	if not pairNode.selected:
		scaleTweenNode(1.1, 0.2, Tween.TRANS_EXPO).start()
	return pairNode

# returns a tween node that uniformly changes the object's scale
# call start() on the returned object
func scaleTweenNode(scale, time=1, transType=Tween.TRANS_BOUNCE):
	var tweenNode = newTweenNode()
	tweenNode.interpolate_method( self, "set_scale", \
		self.get_scale(), Vector3(scale, scale, scale), \
		time, transType, Tween.EASE_OUT )

	return tweenNode

# adds a tween transition node to the tree
func newTweenNode():
	var tween = Tween.new()
	add_child(tween)
	return tween

func remove_with_pop(node, key=null):
	get_parent().samplePlayer.play("deraj_pop_sound")
	get_parent().remove_block(self)

func _ready():
	editor = get_tree().get_root().get_node("EditorSpatial")
	set_ray_pickable(true)

extends RigidBody

var name_int
var name
var pairName
var selected = false
var blockPos
var blockLayer
var blockType
const far_away_corner = Vector3(110, 110, 110)

var editor

static func nameToNodeName(n):
	return "block" + str(n)

func setName(n):
	name_int = n
	name = nameToNodeName(n)
	set_name(nameToNodeName(n))
	return self

func setPairName(n):
	pairName = nameToNodeName(n)
	return self

func setBlockLayer(n):
	blockLayer = n
	return self

func getBlockLayer():
	return blockLayer

func setBlockType( blockT ):
	blockType = blockT
	return self

func getBlockType():
	return blockType

func setSelected(sel):
	selected = sel
	return self

func forceClick():
	activate()
	get_parent().clickBlock( name )

func addNeighbor(editor, click_normal):
	var t = get_parent().get_parent().get_transform().inverse()
	var b = editor.newPickledBlock().setBlockPos(blockPos + t * click_normal)
	get_parent().add_block(b.toNode())

# catch clicks/taps
func _input_event( camera, ev, click_pos, click_normal, shape_idx ):
	if ((ev.type==InputEvent.MOUSE_BUTTON and ev.button_index==BUTTON_LEFT)
	or (ev.type==InputEvent.SCREEN_TOUCH)):
		if (get_parent().get_parent().active and ev.is_pressed()):
			if editor != null:
				if editor.shouldAddNeighbor():
					addNeighbor(editor, click_normal)
				if editor.shouldReplaceSelf():
					addNeighbor(editor, 0 * click_normal)
					remove_with_pop(self, null)
				if editor.shouldRemoveSelf():
					remove_with_pop(self, null)
			else:
				forceClick()

# returns this block's pairNode or null
func pairActivate():
	selected = true

	# is my pair Nil?
	if pairName == null or get_parent() == null or not get_parent().has_node(pairName):
		scaleTweenNode(0.9, 0.2, Tween.TRANS_EXPO).start()
		return null

	# get my pair node
	var pairNode = get_parent().get_node(pairName)

	# enlarge if the other guy's unselected
	if not pairNode.selected:
		scaleTweenNode(1.1, 0.2, Tween.TRANS_EXPO).start()
	return pairNode

# returns a tween node that uniformly changes the object's scale
# call start() on the returned object
func scaleTweenNode(scale, time=1, transType=Tween.TRANS_BOUNCE):
	var tweenNode = newTweenNode()
	tweenNode.interpolate_method( self, "set_scale", \
		self.get_scale(), Vector3(scale, scale, scale), \
		time, transType, Tween.EASE_OUT )

	return tweenNode

# adds a tween transition node to the tree
func newTweenNode():
	var tween = Tween.new()
	add_child(tween)
	return tween

func remove_with_pop(node, key=null):
	get_parent().samplePlayer.play("deraj_pop_sound")
	get_parent().remove_block(self)

func _ready():
	editor = get_tree().get_root().get_node("EditorSpatial")
	set_ray_pickable(true)


Keep track of block layer in AbstractBlock

extends RigidBody

var name
var pairName
var selected = false
var blockPos
var blockLayer
const far_away_corner = Vector3(80, 80, 80)

func nameToNodeName(n):
	return "block" + str(n)

func setName(n):
	name = nameToNodeName(n)
	set_name(nameToNodeName(n))
	return self

func setPairName(n):
	pairName = nameToNodeName(n)
	return self

func setBlockLayer(n):
	blockLayer = n
	return self

func getBlockLayer():
	return blockLayer

# catch clicks/taps
func _input_event( camera, ev, click_pos, click_normal, shape_idx ):
	if ((ev.type==InputEvent.MOUSE_BUTTON and ev.button_index==BUTTON_LEFT)
	or (ev.type==InputEvent.SCREEN_TOUCH)):
		activate(ev, click_pos, click_normal)

# returns this block's pairNode or null
func pairActivate(ev, click_pos, click_normal):
	selected = true

	# is my pair Nil?
	if pairName == null or get_parent() == null or not get_parent().has_node(str(pairName)):
		scaleTweenNode(0.9, 0.2, Tween.TRANS_EXPO).start()
		return null

	# get my pair node
	var pairNode = get_parent().get_node(pairName)

	# enlarge if the other guy's unselected
	if not pairNode.selected:
		scaleTweenNode(1.1, 0.2, Tween.TRANS_EXPO).start()
	return pairNode

# returns a tween node that uniformly changes the object's scale
# call start() on the returned object
func scaleTweenNode(scale, time=1, transType=Tween.TRANS_BOUNCE):
	var tweenNode = newTweenNode()
	tweenNode.interpolate_method( self, "set_scale", \
		self.get_scale(), Vector3(scale, scale, scale), \
		time, transType, Tween.EASE_OUT )

	return tweenNode

# adds a tween transition node to the tree
func newTweenNode():
	var tween = Tween.new()
	add_child(tween)
	return tween

func remove_with_pop(node, key):
	get_parent().samplePlayer.play("deraj_pop_sound")
	get_parent().remove_block(self)

func _ready():
	set_ray_pickable(true)

extends RigidBody

var name
var pairName
var selected = false
var blockPos
const far_away_corner = Vector3(80, 80, 80)

func nameToNodeName(n):
	return "block" + str(n)

func setName(n):
	name = nameToNodeName(n)
	set_name(nameToNodeName(n))
	return self

func setPairName(n):
	pairName = nameToNodeName(n)
	return self

# catch clicks/taps
func _input_event( camera, ev, click_pos, click_normal, shape_idx ):
	if ((ev.type==InputEvent.MOUSE_BUTTON and ev.button_index==BUTTON_LEFT)
	or (ev.type==InputEvent.SCREEN_TOUCH)):
		activate(ev, click_pos, click_normal)

# returns this block's pairNode or null
func pairActivate(ev, click_pos, click_normal):
	selected = true

	# is my pair Nil?
	if pairName == null or get_parent() == null or not get_parent().has_node(str(pairName)):
		scaleTweenNode(0.9, 0.2, Tween.TRANS_EXPO).start()
		return null

	# get my pair node
	var pairNode = get_parent().get_node(pairName)

	# enlarge if the other guy's unselected
	if not pairNode.selected:
		scaleTweenNode(1.1, 0.2, Tween.TRANS_EXPO).start()
	return pairNode

# returns a tween node that uniformly changes the object's scale
# call start() on the returned object
func scaleTweenNode(scale, time=1, transType=Tween.TRANS_BOUNCE):
	var tweenNode = newTweenNode()
	tweenNode.interpolate_method( self, "set_scale", \
		self.get_scale(), Vector3(scale, scale, scale), \
		time, transType, Tween.EASE_OUT )

	return tweenNode

# adds a tween transition node to the tree
func newTweenNode():
	var tween = Tween.new()
	add_child(tween)
	return tween

func remove_with_pop(node, key):
	get_parent().samplePlayer.play("deraj_pop_sound")
	get_parent().remove_block(self)

func _ready():
	set_ray_pickable(true)


Use get_current_scene() instead of hack

Use get_current_scene() instead of hack

extends Node

# Member variables
var current_scene = null

func goto_scene(path):
	# This function will usually be called from a signal callback,
	# or some other function from the running scene.
	# Deleting the current scene at this point might be
	# a bad idea, because it may be inside of a callback or function of it.
	# The worst case will be a crash or unexpected behavior.

	# The way around this is deferring the load to a later time, when
	# it is ensured that no code from the current scene is running:

	call_deferred("_deferred_goto_scene",path)

func _deferred_goto_scene(path):
	# Immediately free the current scene,
	# there is no risk here.
	current_scene.free()

	# Load new scene
	var s = ResourceLoader.load(path)

	# Instance the new scene
	current_scene = s.instance()

	# Add it to the active scene, as child of root
	get_tree().get_root().add_child(current_scene)

func _ready():
	# Get the current scene at the time of initialization
	current_scene = get_tree().get_current_scene()

extends Node

# Member variables
var current_scene = null

func goto_scene(path):
	# This function will usually be called from a signal callback,
	# or some other function from the running scene.
	# Deleting the current scene at this point might be
	# a bad idea, because it may be inside of a callback or function of it.
	# The worst case will be a crash or unexpected behavior.

	# The way around this is deferring the load to a later time, when
	# it is ensured that no code from the current scene is running:

	call_deferred("_deferred_goto_scene",path)

func _deferred_goto_scene(path):
	# Immediately free the current scene,
	# there is no risk here.
	current_scene.free()

	# Load new scene
	var s = ResourceLoader.load(path)

	# Instance the new scene
	current_scene = s.instance()

	# Add it to the active scene, as child of root
	get_tree().get_root().add_child(current_scene)

func _ready():
	# Get the current scene, the first time.
	# It is always the last child of root,
	# after the autoloaded nodes.

	var root = get_tree().get_root()
	current_scene = root.get_child(root.get_child_count() - 1)


Player movement inertia

Player movement inertia

# PLAYER class
# Moves n shoots

extends Node2D

const SPEED = 420 / 1.42
const ACCEL = 3000

export var polar_controls = false
var angle = 0
var dist = 200
var shield_cooldown = 0
var fire_timer = 0
var velocity = Vector2(0,0)

const MAX_HEALTH = 100
var health = MAX_HEALTH

var inv_time = 0

var col = Color(1,1,1)

func _ready():
	set_process(true)

func _process(delta):
	var target_velocity = Vector2(0,0)
	if Input.is_key_pressed(KEY_LEFT):
		if polar_controls:
			angle += (delta / dist) * SPEED
		else:
			target_velocity += Vector2(-1,0)
	if Input.is_key_pressed(KEY_RIGHT):
		if polar_controls:
			angle -= (delta / dist) * SPEED
		else:
			target_velocity += Vector2(1,0)
	if Input.is_key_pressed(KEY_UP):
		if polar_controls:
			dist -= delta * SPEED
			if dist < 0.1: dist = 0.1
		else:
			target_velocity += Vector2(0,-1)
	if Input.is_key_pressed(KEY_DOWN):
		if polar_controls:
			dist += delta * SPEED
		else:
			target_velocity += Vector2(0,1)

	target_velocity = target_velocity.normalized() * SPEED

	var towards_target = target_velocity - velocity
	var dist_to_target = towards_target.length()
	towards_target = towards_target.normalized()
	towards_target *= min(ACCEL * delta, dist_to_target)

	velocity += towards_target
	translate(velocity * delta)

	var center = Vector2(720/2,720/2)

	if polar_controls:
		set_pos(center + Vector2(cos(angle),sin(angle)) * dist)

	var towards_center =  (center - get_pos()).normalized()

	col = col.linear_interpolate(Color(1,1,1),delta * 10)
	get_node("RegularPolygon/Polygon2D").set_color(col)

	if inv_time > 0:
		inv_time -= delta

	if fire_timer > 0:
		fire_timer -= delta

	if (Input.is_key_pressed(KEY_SPACE) or Input.is_key_pressed(KEY_F)) and fire_timer <= 0:
		var bullet = preload("res://objects/Bullet.tscn").instance()
		bullet.set_pos(get_pos())
		get_tree().get_root().get_node("Game/SFX").set_default_pitch_scale(rand_range(0.9,1.1))
		get_tree().get_root().get_node("Game/SFX").play("Laser_Shoot14")
		#bullet.get_node("RegularPolygon").size = 1
		bullet.damage = 1

		var dir = atan2(towards_center.y,towards_center.x)
		var len = 600

		var spread = 0.05
		var rot = rand_range(-spread,spread)
		var final = dir + rot

		bullet.velocity = Vector2(cos(final),sin(final)) * len
		get_tree().get_root().add_child(bullet,false)
		fire_timer = 0.05

	if shield_cooldown > 0:
		shield_cooldown -= delta

	if Input.is_key_pressed(KEY_D) and shield_cooldown <= 0:
		var shield = preload("res://objects/PlayerShield.tscn").instance()
		shield.node_to_follow = get_path()
		get_tree().get_root().add_child(shield)
		shield.set_global_pos(get_global_pos())
		shield_cooldown = 6

	set_rot(atan2(towards_center.x,towards_center.y) - PI/2)

	# Limit position to circle
	if center.distance_to(get_pos()) > 720/2:
		set_pos((get_pos() - center).normalized() * 720/2 + center)

func _on_RegularPolygon_area_enter( area ):
	if area.get_groups().has("damage_player"):
		var bullet = area.get_parent()
		bullet.queue_free()
		var dmgtotake = bullet.damage * 2
		lose_health(dmgtotake)
	if area.get_groups().has("damage_player_solid") && area.get_parent().enabled:
		lose_health(100)

func lose_health(hp):
	if inv_time <= 0:
		health -= hp
		inv_time = 1
		col = Color(1,0,0)
		get_parent().score_mult = 1
		get_parent().score_mult_timer = 0
		if health <= 0:
			for i in range(0,8):
				var explosion_instance = preload("res://objects/Explosion.tscn").instance()
				explosion_instance.get_node("RegularPolygon").size = get_node("RegularPolygon").size
				explosion_instance.get_node("RegularPolygon/Polygon2D").set_color(get_node("RegularPolygon/Polygon2D").get_color())
				# explosion_instance.velocity = Vector2(0,0)
				get_tree().get_root().add_child(explosion_instance)
				explosion_instance.set_global_pos(get_global_pos())
			OS.set_time_scale(0.02)
			queue_free()

func _on_RegularPolygon_area_exit( area ):
	pass

# PLAYER class
# Moves n shoots

extends Node2D

const SPEED = 420 / 1.42

export var polar_controls = false
var angle = 0
var dist = 200
var shield_cooldown = 0
var fire_timer = 0

const MAX_HEALTH = 100
var health = MAX_HEALTH

var inv_time = 0

var col = Color(1,1,1)

func _ready():
	set_process(true)

func _process(delta):
	if Input.is_key_pressed(KEY_LEFT):
		if polar_controls:
			angle += (delta / dist) * SPEED
		else:
			translate(Vector2(-delta,0) * SPEED)
	if Input.is_key_pressed(KEY_RIGHT):
		if polar_controls:
			angle -= (delta / dist) * SPEED
		else:
			translate(Vector2(delta,0) * SPEED)
	if Input.is_key_pressed(KEY_UP):
		if polar_controls:
			dist -= delta * SPEED
			if dist < 0.1: dist = 0.1
		else:
			translate(Vector2(0,-delta) * SPEED)
	if Input.is_key_pressed(KEY_DOWN):
		if polar_controls:
			dist += delta * SPEED
		else:
			translate(Vector2(0,delta) * SPEED)

	var center = Vector2(720/2,720/2)

	if polar_controls:
		set_pos(center + Vector2(cos(angle),sin(angle)) * dist)

	var towards_center =  (center - get_pos()).normalized()

	col = col.linear_interpolate(Color(1,1,1),delta * 10)
	get_node("RegularPolygon/Polygon2D").set_color(col)

	if inv_time > 0:
		inv_time -= delta

	if fire_timer > 0:
		fire_timer -= delta

	if (Input.is_key_pressed(KEY_SPACE) or Input.is_key_pressed(KEY_F)) and fire_timer <= 0:
		var bullet = preload("res://objects/Bullet.tscn").instance()
		bullet.set_pos(get_pos())
		get_tree().get_root().get_node("Game/SFX").set_default_pitch_scale(rand_range(0.9,1.1))
		get_tree().get_root().get_node("Game/SFX").play("Laser_Shoot14")
		#bullet.get_node("RegularPolygon").size = 1
		bullet.damage = 1

		var dir = atan2(towards_center.y,towards_center.x)
		var len = 600

		var spread = 0.05
		var rot = rand_range(-spread,spread)
		var final = dir + rot

		bullet.velocity = Vector2(cos(final),sin(final)) * len
		get_tree().get_root().add_child(bullet,false)
		fire_timer = 0.05

	if shield_cooldown > 0:
		shield_cooldown -= delta

	if Input.is_key_pressed(KEY_D) and shield_cooldown <= 0:
		var shield = preload("res://objects/PlayerShield.tscn").instance()
		shield.node_to_follow = get_path()
		get_tree().get_root().add_child(shield)
		shield.set_global_pos(get_global_pos())
		shield_cooldown = 6

	set_rot(atan2(towards_center.x,towards_center.y) - PI/2)

	# Limit position to circle
	if center.distance_to(get_pos()) > 720/2:
		set_pos((get_pos() - center).normalized() * 720/2 + center)

func _on_RegularPolygon_area_enter( area ):
	if area.get_groups().has("damage_player"):
		var bullet = area.get_parent()
		bullet.queue_free()
		var dmgtotake = bullet.damage * 2
		lose_health(dmgtotake)
	if area.get_groups().has("damage_player_solid") && area.get_parent().enabled:
		lose_health(100)

func lose_health(hp):
	if inv_time <= 0:
		health -= hp
		inv_time = 1
		col = Color(1,0,0)
		get_parent().score_mult = 1
		get_parent().score_mult_timer = 0
		if health <= 0:
			for i in range(0,8):
				var explosion_instance = preload("res://objects/Explosion.tscn").instance()
				explosion_instance.get_node("RegularPolygon").size = get_node("RegularPolygon").size
				explosion_instance.get_node("RegularPolygon/Polygon2D").set_color(get_node("RegularPolygon/Polygon2D").get_color())
				# explosion_instance.velocity = Vector2(0,0)
				get_tree().get_root().add_child(explosion_instance)
				explosion_instance.set_global_pos(get_global_pos())
			OS.set_time_scale(0.02)
			queue_free()

func _on_RegularPolygon_area_exit( area ):
	pass


Doge rampage

Doge rampage

extends "res://scripts/enemies/abstract_boss.gd"

const IRRITATED_TIMER = 2

var irritated = false
var calm = true
var rampage_hp_threshold = 10

var body_angry
var body_moving

var path_points = [
    Vector2(17, 2),
    Vector2(17, 8),
    Vector2(5, 8),
]
var path_points_global = []
var target_point = null
var current_point = 0

func _init(bag).(bag):
    self.avatar = preload("res://scenes/enemies/doge_prime.xscn").instance()
    self.body_part_head = self.avatar.get_node('body')
    self.body_part_body = self.avatar.get_node('body')
    self.body_part_footer = self.avatar.get_node('body')
    self.animations = self.avatar.get_node('body_animations')

    self.body_angry = self.avatar.get_node('body2')
    self.body_moving = self.avatar.get_node('body3')

    self.velocity = 250
    self.attack_range = 75
    self.aggro_range = 75
    self.attack_strength = 3
    self.attack_cooldown = 3
    self.max_hp = 25
    self.hp = 25
    self.rampage_hp_threshold = 10
    self.score = 200

    self.stun_duration = 0.4
    self.mass = 30

    self.calm = true
    self.irritated = false

    self.phase_hp_thresholds = [
        [25, 2],
    ]

    for point in self.path_points:
        self.path_points_global.push_back(self.bag.room_loader.translate_position(point))

func process_ai():
    var distance
    var direction = Vector2(0, 0)

    self.target = null
    if not self.calm:
        for player in self.bag.players.players:
            if player.is_playing && player.is_alive:
                distance = self.calculate_distance_to_object(player)
                if distance < self.aggro_range:
                    if self.target != null:
                        if self.calculate_distance_to_object(self.target) > distance:
                            self.target = player
                    else:
                        self.target = player
                    break

    if self.calm and not self.irritated:
        for player in self.bag.players.players:
            if player.is_playing && player.is_alive:
                distance = self.calculate_distance_to_object(player)
                if distance < self.aggro_range:
                    self.irritate()
                    break

    if self.target != null:
        distance = self.calculate_distance_to_object(self.target)
        if not self.target.is_alive || distance > self.aggro_range:
            self.target = null
        elif distance < self.attack_range and not self.is_attack_on_cooldown:
            self.attack()

    if self.target_point != null:
        distance = self.calculate_distance(self.target_point)
        if distance < 5:
            self.target_point = null
            self.calm_down()
        else:
            direction = self.cast_movement_vector(self.target_point)

    self.movement_vector[0] = direction.x
    self.movement_vector[1] = direction.y

func enrage():
    self.is_invulnerable = true
    self.calm = false
    self.pick_next_point()
    self.body_angry.hide()
    self.body_moving.show()
    self.body_part_body.hide()

func calm_down():
    self.is_invulnerable = false
    self.calm = true
    self.irritated = false
    self.body_angry.hide()
    self.body_moving.hide()
    self.body_part_body.show()

    var timer = randi() % 3
    timer += 2

    if self.hp > self.rampage_hp_threshold:
        self.bag.timers.set_timeout(timer, self, 'irritate')
    else:
        self.irritate()

func irritate():
    if self.irritated:
        return

    self.irritated = true
    self.body_part_body.hide()
    self.body_angry.show()
    self.body_moving.hide()
    self.bag.timers.set_timeout(self.IRRITATED_TIMER, self, 'enrage')

func phase2():
    return

func pick_next_point():
    var next_point = self.current_point

    while (next_point == self.current_point):
        next_point = randi() % self.path_points_global.size()

    self.current_point = next_point
    self.target_point = self.path_points_global[next_point]

func push_back(enemy):
    if not self.irritated:
        self.irritate()

func flip_body_parts(flip_flag):
    .flip_body_parts(flip_flag)
    self.body_angry.set_flip_h(flip_flag)
    self.body_moving.set_flip_h(flip_flag)

extends "res://scripts/enemies/abstract_boss.gd"

const IRRITATED_TIMER = 2

var irritated = false
var calm = true

var body_angry
var body_moving

var path_points = [
    Vector2(17, 2),
    Vector2(17, 8),
    Vector2(5, 8),
]
var path_points_global = []
var target_point = null
var current_point = 0

func _init(bag).(bag):
    self.avatar = preload("res://scenes/enemies/doge_prime.xscn").instance()
    self.body_part_head = self.avatar.get_node('body')
    self.body_part_body = self.avatar.get_node('body')
    self.body_part_footer = self.avatar.get_node('body')
    self.animations = self.avatar.get_node('body_animations')

    self.body_angry = self.avatar.get_node('body2')
    self.body_moving = self.avatar.get_node('body3')

    self.velocity = 250
    self.attack_range = 75
    self.aggro_range = 75
    self.attack_strength = 3
    self.attack_cooldown = 3
    self.max_hp = 25
    self.hp = 25
    self.score = 200

    self.stun_duration = 0.4
    self.mass = 30

    self.calm = true
    self.irritated = false

    self.phase_hp_thresholds = [
        [25, 2],
    ]

    for point in self.path_points:
        self.path_points_global.push_back(self.bag.room_loader.translate_position(point))

func process_ai():
    var distance
    var direction = Vector2(0, 0)

    self.target = null
    if not self.calm:
        for player in self.bag.players.players:
            if player.is_playing && player.is_alive:
                distance = self.calculate_distance_to_object(player)
                if distance < self.aggro_range:
                    if self.target != null:
                        if self.calculate_distance_to_object(self.target) > distance:
                            self.target = player
                    else:
                        self.target = player
                    break

    if self.calm and not self.irritated:
        for player in self.bag.players.players:
            if player.is_playing && player.is_alive:
                distance = self.calculate_distance_to_object(player)
                if distance < self.aggro_range:
                    self.irritate()
                    break

    if self.target != null:
        distance = self.calculate_distance_to_object(self.target)
        if not self.target.is_alive || distance > self.aggro_range:
            self.target = null
        elif distance < self.attack_range and not self.is_attack_on_cooldown:
            self.attack()

    if self.target_point != null:
        distance = self.calculate_distance(self.target_point)
        if distance < 5:
            self.target_point = null
            self.calm_down()
        else:
            direction = self.cast_movement_vector(self.target_point)

    self.movement_vector[0] = direction.x
    self.movement_vector[1] = direction.y

func enrage():
    self.is_invulnerable = true
    self.calm = false
    self.pick_next_point()
    self.body_angry.hide()
    self.body_moving.show()
    self.body_part_body.hide()

func calm_down():
    self.is_invulnerable = false
    self.calm = true
    self.irritated = false
    self.body_angry.hide()
    self.body_moving.hide()
    self.body_part_body.show()

    var timer = randi() % 3
    timer += 2

    self.bag.timers.set_timeout(timer, self, 'irritate')

func irritate():
    if self.irritated:
        return

    self.irritated = true
    self.body_part_body.hide()
    self.body_angry.show()
    self.body_moving.hide()
    self.bag.timers.set_timeout(self.IRRITATED_TIMER, self, 'enrage')

func phase2():
    return

func pick_next_point():
    var next_point = self.current_point

    while (next_point == self.current_point):
        next_point = randi() % self.path_points_global.size()

    self.current_point = next_point
    self.target_point = self.path_points_global[next_point]

func push_back(enemy):
    if not self.irritated:
        self.irritate()

func flip_body_parts(flip_flag):
    .flip_body_parts(flip_flag)
    self.body_angry.set_flip_h(flip_flag)
    self.body_moving.set_flip_h(flip_flag)


remove cheese file

remove cheese file

extends RigidBody2D
# selects random chees omnomnom

var body

func _ready():
    self.set_mode(MODE_RIGID)
    self.body = self.get_node('body')
    self.body.set_frame(randi() % self.body.get_vframes())
    pass


added blockLayer variable

added blockLayer variable

extends RigidBody

var name
var pairName
var selected = false
var blockPos
var blockLayer
const far_away_corner = Vector3(80, 80, 80)

func nameToNodeName(n):
	return "block" + str(n)

func setName(n):
	name = nameToNodeName(n)
	set_name(nameToNodeName(n))
	return self

func setPairName(n):
	pairName = nameToNodeName(n)
	return self

func setBlockLayer(n):
	blockLayer = n
	return self

func getBlockLayer():
	return blockLayer

func setSelected(sel):
	selected = sel
	return self

# catch clicks/taps
func _input_event( camera, ev, click_pos, click_normal, shape_idx ):
	if ((ev.type==InputEvent.MOUSE_BUTTON and ev.button_index==BUTTON_LEFT)
	or (ev.type==InputEvent.SCREEN_TOUCH)):
		if (get_parent().get_parent().active and ev.is_pressed()):
			activate(ev, click_pos, click_normal)

			#now check if that was the second block we picked. If it was, we want to
			#unselect the blocks again
			var gridView = get_parent().get_parent()
			if (gridView.offClick):
				print("omg")
				gridView.offClick = false
				gridView.addSelected(name)
				gridView.clearSelection()
			else:
				print("lulz")
				gridView.offClick = true;
				gridView.addSelected(name)

# returns this block's pairNode or null
func pairActivate(ev, click_pos, click_normal):
	selected = true

	# is my pair Nil?
	if pairName == null or get_parent() == null or not get_parent().has_node(str(pairName)):
		scaleTweenNode(0.9, 0.2, Tween.TRANS_EXPO).start()
		return null

	# get my pair node
	var pairNode = get_parent().get_node(pairName)

	# enlarge if the other guy's unselected
	if not pairNode.selected:
		scaleTweenNode(1.1, 0.2, Tween.TRANS_EXPO).start()
	return pairNode

# returns a tween node that uniformly changes the object's scale
# call start() on the returned object
func scaleTweenNode(scale, time=1, transType=Tween.TRANS_BOUNCE):
	var tweenNode = newTweenNode()
	tweenNode.interpolate_method( self, "set_scale", \
		self.get_scale(), Vector3(scale, scale, scale), \
		time, transType, Tween.EASE_OUT )

	return tweenNode

# adds a tween transition node to the tree
func newTweenNode():
	var tween = Tween.new()
	add_child(tween)
	return tween

func remove_with_pop(node, key):
	get_parent().samplePlayer.play("deraj_pop_sound")
	get_parent().remove_block(self)

func _ready():
	set_ray_pickable(true)
	#var img;
	#img.load("res://textures/Block_" + textureName + ".png")

extends RigidBody

var name
var pairName
var selected = false
var blockPos
const far_away_corner = Vector3(80, 80, 80)

func nameToNodeName(n):
	return "block" + str(n)

func setName(n):
	name = nameToNodeName(n)
	set_name(nameToNodeName(n))
	return self

func setPairName(n):
	pairName = nameToNodeName(n)
	return self

func setBlockLayer(n):
	blockLayer = n
	return self

func getBlockLayer():
	return blockLayer

func setSelected(sel):
	selected = sel
	return self

# catch clicks/taps
func _input_event( camera, ev, click_pos, click_normal, shape_idx ):
	if ((ev.type==InputEvent.MOUSE_BUTTON and ev.button_index==BUTTON_LEFT)
	or (ev.type==InputEvent.SCREEN_TOUCH)):
		if (get_parent().get_parent().active and ev.is_pressed()):
			activate(ev, click_pos, click_normal)

			#now check if that was the second block we picked. If it was, we want to
			#unselect the blocks again
			var gridView = get_parent().get_parent()
			if (gridView.offClick):
				print("omg")
				gridView.offClick = false
				gridView.addSelected(name)
				gridView.clearSelection()
			else:
				print("lulz")
				gridView.offClick = true;
				gridView.addSelected(name)

# returns this block's pairNode or null
func pairActivate(ev, click_pos, click_normal):
	selected = true

	# is my pair Nil?
	if pairName == null or get_parent() == null or not get_parent().has_node(str(pairName)):
		scaleTweenNode(0.9, 0.2, Tween.TRANS_EXPO).start()
		return null

	# get my pair node
	var pairNode = get_parent().get_node(pairName)

	# enlarge if the other guy's unselected
	if not pairNode.selected:
		scaleTweenNode(1.1, 0.2, Tween.TRANS_EXPO).start()
	return pairNode

# returns a tween node that uniformly changes the object's scale
# call start() on the returned object
func scaleTweenNode(scale, time=1, transType=Tween.TRANS_BOUNCE):
	var tweenNode = newTweenNode()
	tweenNode.interpolate_method( self, "set_scale", \
		self.get_scale(), Vector3(scale, scale, scale), \
		time, transType, Tween.EASE_OUT )

	return tweenNode

# adds a tween transition node to the tree
func newTweenNode():
	var tween = Tween.new()
	add_child(tween)
	return tween

func remove_with_pop(node, key):
	get_parent().samplePlayer.play("deraj_pop_sound")
	get_parent().remove_block(self)

func _ready():
	set_ray_pickable(true)
	#var img;
	#img.load("res://textures/Block_" + textureName + ".png")


proper game over screen

proper game over screen

extends Spatial

# Is there a better way to do this?
const BLOCK_LASER	= 0
const BLOCK_WILD	= 1
const BLOCK_PAIR	= 2
const BLOCK_GOAL	= 3
const BLOCK_BLOCK   = 4

# Shape dictionary to access blocks quickly by position.
var shape = {}

# Block selection handling.
var offClick = false
var selectedBlocks = []

# Puzzle vars.
var puzzle

# Beam stuff.
const beamScn = preload( "res://blocks/block.scn" )
const Beam = preload("res://scripts/Blocks/Beam.gd")

# sounds
var samplePlayer = SamplePlayer.new()

func addPickledBlock(block):
	# TODO add to puzzle
	var b = block.toNode()

	print ("ADDED")
	shape[b.blockPos] = b

	# TODO  any special treatment for the wild blocks?
	# TODO  keep track of puzzle.lasers ? How to?

	# keep track of puzzle.pairCounts
	var layer = calcBlockLayerVec(b.blockPos)
	if b.getBlockType() == 2:
		while puzzle.pairCount.size() <= layer:
			puzzle.pairCount.append(0)
		puzzle.pairCount[layer] += 0.5

	add_child(b)

func get_block(pos):
	if shape.has(pos):
		return shape[pos]
	else:
		return null

# unused key argument needed for the tween_complete signal
func remove_block(block_node, key=null):
	if block_node == null:
		return
	print ("REMOVED")

	shape[block_node.blockPos] = null
	for child in block_node.get_children():
		block_node.remove_and_delete_child(child)
	remove_and_delete_child(block_node)

# Sets the puzzle for this GridMan.
func set_puzzle(puzz):
	# delete all current nodes
	for pos in shape:
		remove_block(shape[pos])

	# Store the puzzle.
	puzzle = puzz

	# Set the camera range to be relative to the layer count.
	var cam = get_tree().get_root().get_node( "Spatial" ).get_node( "Camera" )
	print( cam )
	var totalSize = ( puzzle.puzzleLayers * 2 + 1 )
	cam.distance.val = 4.5 * totalSize
	cam.distance.min_ = 3 * totalSize
	cam.distance.max_ = 10 * totalSize
	cam.recalculate_camera()

		# # I can do my own counting!
	# needed for adding blocks in the editor
	puzzle.pairCount = []
	for block in puzzle.blocks:
		# Create a block node, add it to the tree
		addPickledBlock(block)

# Clears any selected blocks. WE SHOULD FIX THIS, THERE CAN ONLY BE ONE BLOCK SELECTED AT ANY ONE TIME, NO NEED FOR AN ARRAY!
func clearSelection():
	for bl in selectedBlocks:
		var blo = get_node( bl )
		blo.setSelected(false)
		blo.scaleTweenNode(1.0).start()
	selectedBlocks = []

# Add the selected block to the selected list. WE SHOULD FIX THIS, IT ONLY NEEDS TO STORE IT, NOT AN ARRAY!
func addSelected(bl):
	selectedBlocks.append(bl)

# Used for the multiplayer mode to force click a block on their side.
func forceClickBlock( pos ):
	shape[pos].forceClick()

func clickBlock( name ):
	#now check if that was the second block we picked. If it was, we want to
	#unselect the blocks again
	if (offClick):
		offClick = false
		addSelected(name)
		clearSelection()
	else:
		offClick = true;
		addSelected(name)

# Calculates the layer that a block is on.
# COPY OF FUNCTION IN PUZZLEMAN, IS THERE A BETTER WAY TO DO THIS?!
func calcBlockLayer( x, y, z ):
	return max( max( abs( x ), abs( y ) ), abs( z ) )
func calcBlockLayerVec( pos ):
	return max( max( abs( pos.x ), abs( pos.y ) ), abs( pos.z ) )

# Handles keeping track of pairs being removed.
func popPair( pos ):
	var blayer = calcBlockLayerVec( pos )
	puzzle.pairCount[blayer] -= 1

	if( puzzle.pairCount[blayer] == 0 ):
		print("LAYER CLEARED")
		if blayer == 1:
			print( "GAME OVER!" )
			var pauseMenu = get_tree().get_root().get_node( "Spatial" ).get_node( "Camera" ).pauseMenu
			pauseMenu.set_text("GAME OVER\nSCORE:1,000,000")
			# TODO set timeout!!!
			pauseMenu.popup_centered()

		for b in shape:
			if not ( shape[b] == null ):
				if calcBlockLayerVec( b ) == blayer:
					if shape[b].getBlockType() == BLOCK_LASER:
						shape[b].forceActivate()

		# Fire beams.
		var beamNum = 0
		for l in range( puzzle.lasers.size() ):
			if calcBlockLayerVec( puzzle.lasers[l][0] ) == blayer:
				# Firin mah lazerz!
				var beam = beamScn.instance()

				beam.set_name( str(blayer) + "_beam_" + str(beamNum) )
				beamNum += 1
				beam.set_script( Beam )

				add_child( beam )
				beam.fire( puzzle.lasers[l][0], puzzle.lasers[l][1] )

				samplePlayer.play( "soundslikewillem_hitting_slinky" )

func _init():
	# Load sound
	samplePlayer.set_voice_count(10)
	samplePlayer.set_sample_library(ResourceLoader.load("new_samplelibrary.xml"))
	print("GridMan initialized")

extends Spatial

# Is there a better way to do this?
const BLOCK_LASER	= 0
const BLOCK_WILD	= 1
const BLOCK_PAIR	= 2
const BLOCK_GOAL	= 3
const BLOCK_BLOCK   = 4

# Shape dictionary to access blocks quickly by position.
var shape = {}

# Block selection handling.
var offClick = false
var selectedBlocks = []

# Puzzle vars.
var puzzle

# Beam stuff.
const beamScn = preload( "res://blocks/block.scn" )
const Beam = preload("res://scripts/Blocks/Beam.gd")

# sounds
var samplePlayer = SamplePlayer.new()

func addPickledBlock(block):
	# TODO add to puzzle
	var b = block.toNode()

	print ("ADDED")
	shape[b.blockPos] = b

	# TODO  any special treatment for the wild blocks?
	# TODO  keep track of puzzle.lasers ? How to?

	# keep track of puzzle.pairCounts
	var layer = calcBlockLayerVec(b.blockPos)
	if b.getBlockType() == 2:
		while puzzle.pairCount.size() <= layer:
			puzzle.pairCount.append(0)
		puzzle.pairCount[layer] += 0.5

	add_child(b)

func get_block(pos):
	if shape.has(pos):
		return shape[pos]
	else:
		return null

# unused key argument needed for the tween_complete signal
func remove_block(block_node, key=null):
	if block_node == null:
		return
	print ("REMOVED")

	shape[block_node.blockPos] = null
	for child in block_node.get_children():
		block_node.remove_and_delete_child(child)
	remove_and_delete_child(block_node)

# Sets the puzzle for this GridMan.
func set_puzzle(puzz):
	# delete all current nodes
	for pos in shape:
		remove_block(shape[pos])

	# Store the puzzle.
	puzzle = puzz

	# Set the camera range to be relative to the layer count.
	var cam = get_tree().get_root().get_node( "Spatial" ).get_node( "Camera" )
	print( cam )
	var totalSize = ( puzzle.puzzleLayers * 2 + 1 )
	cam.distance.val = 4.5 * totalSize
	cam.distance.min_ = 3 * totalSize
	cam.distance.max_ = 10 * totalSize
	cam.recalculate_camera()

		# # I can do my own counting!
	# needed for adding blocks in the editor
	puzzle.pairCount = []
	for block in puzzle.blocks:
		# Create a block node, add it to the tree
		addPickledBlock(block)

# Clears any selected blocks. WE SHOULD FIX THIS, THERE CAN ONLY BE ONE BLOCK SELECTED AT ANY ONE TIME, NO NEED FOR AN ARRAY!
func clearSelection():
	for bl in selectedBlocks:
		var blo = get_node( bl )
		blo.setSelected(false)
		blo.scaleTweenNode(1.0).start()
	selectedBlocks = []

# Add the selected block to the selected list. WE SHOULD FIX THIS, IT ONLY NEEDS TO STORE IT, NOT AN ARRAY!
func addSelected(bl):
	selectedBlocks.append(bl)

# Used for the multiplayer mode to force click a block on their side.
func forceClickBlock( pos ):
	shape[pos].forceClick()

func clickBlock( name ):
	#now check if that was the second block we picked. If it was, we want to
	#unselect the blocks again
	if (offClick):
		offClick = false
		addSelected(name)
		clearSelection()
	else:
		offClick = true;
		addSelected(name)

# Calculates the layer that a block is on.
# COPY OF FUNCTION IN PUZZLEMAN, IS THERE A BETTER WAY TO DO THIS?!
func calcBlockLayer( x, y, z ):
	return max( max( abs( x ), abs( y ) ), abs( z ) )
func calcBlockLayerVec( pos ):
	return max( max( abs( pos.x ), abs( pos.y ) ), abs( pos.z ) )

# Handles keeping track of pairs being removed.
func popPair( pos ):
	var blayer = calcBlockLayerVec( pos )
	puzzle.pairCount[blayer] -= 1

	if( puzzle.pairCount[blayer] == 0 ):
		print("LAYER CLEARED")
		if blayer == 1:
			print( "GAME OVER!" )

		for b in shape:
			if not ( shape[b] == null ):
				if calcBlockLayerVec( b ) == blayer:
					if shape[b].getBlockType() == BLOCK_LASER:
						shape[b].forceActivate()

		# Fire beams.
		var beamNum = 0
		for l in range( puzzle.lasers.size() ):
			if calcBlockLayerVec( puzzle.lasers[l][0] ) == blayer:
				# Firin mah lazerz!
				var beam = beamScn.instance()

				beam.set_name( str(blayer) + "_beam_" + str(beamNum) )
				beamNum += 1
				beam.set_script( Beam )

				add_child( beam )
				beam.fire( puzzle.lasers[l][0], puzzle.lasers[l][1] )

				samplePlayer.play( "soundslikewillem_hitting_slinky" )

func _init():
	# Load sound
	samplePlayer.set_voice_count(10)
	samplePlayer.set_sample_library(ResourceLoader.load("new_samplelibrary.xml"))
	print("GridMan initialized")


tests if editor is available before requesting it

extends RigidBody

var name_int
var name
var pairName
var selected = false
var blockPos
var blockLayer
var blockType
const far_away_corner = Vector3(110, 110, 110)

var editor

static func nameToNodeName(n):
	return "block" + str(n)

func setName(n):
	name_int = n
	name = nameToNodeName(n)
	set_name(nameToNodeName(n))
	return self

func setPairName(n):
	pairName = nameToNodeName(n)
	return self

func setBlockLayer(n):
	blockLayer = n
	return self

func getBlockLayer():
	return blockLayer

func setBlockType( blockT ):
	blockType = blockT
	return self

func getBlockType():
	return blockType

func setSelected(sel):
	selected = sel
	return self

func forceClick(click_normal):
	if editor != null:
		if editor.shouldAddNeighbor():
			addNeighbor(editor, click_normal)
			return
		if editor.shouldReplaceSelf():
			addNeighbor(editor, 0 * click_normal)
		if editor.shouldReplaceSelf() or editor.shouldRemoveSelf():
			if pairName != null:
				var pair = get_parent().get_node(pairName)
				if pair != null:
					pair.request_remove()
			request_remove()
			return
	else:
		activate()
		get_parent().clickBlock( name )

func addNeighbor(editor, click_normal):
	var t = get_parent().get_parent().get_transform().inverse()
	editor.addBlock(blockPos + t * click_normal)

# catch clicks/taps
func _input_event( camera, ev, click_pos, click_normal, shape_idx ):
	if ((ev.type==InputEvent.MOUSE_BUTTON and ev.button_index==BUTTON_LEFT)
	or (ev.type==InputEvent.SCREEN_TOUCH)):
		if (get_parent().get_parent().active and ev.is_pressed()):
			forceClick(click_normal)

# returns this block's pairNode or null
func pairActivate():
	selected = true

	# is my pair Nil?
	if pairName == null or get_parent() == null or not get_parent().has_node(pairName):
		scaleTweenNode(0.9, 0.2, Tween.TRANS_EXPO).start()
		return null

	# get my pair node
	var pairNode = get_parent().get_node(pairName)

	# enlarge if the other guy's unselected
	if not pairNode.selected:
		scaleTweenNode(1.1, 0.2, Tween.TRANS_EXPO).start()
	return pairNode

# returns a tween node that uniformly changes the object's scale
# call start() on the returned object
func scaleTweenNode(scale, time=1, transType=Tween.TRANS_BOUNCE):
	var tweenNode = newTweenNode()
	tweenNode.interpolate_method( self, "set_scale", \
		self.get_scale(), Vector3(scale, scale, scale), \
		time, transType, Tween.EASE_OUT )

	return tweenNode

# adds a tween transition node to the tree
func newTweenNode():
	var tween = Tween.new()
	add_child(tween)
	return tween

func request_remove(node=null, key=null):
	get_parent().remove_block(self)

func _ready():
	if get_tree().get_root().has_node("EditorSpatial"):
		editor = get_tree().get_root().get_node("EditorSpatial")
	set_ray_pickable(true)

extends RigidBody

var name_int
var name
var pairName
var selected = false
var blockPos
var blockLayer
var blockType
const far_away_corner = Vector3(110, 110, 110)

var editor

static func nameToNodeName(n):
	return "block" + str(n)

func setName(n):
	name_int = n
	name = nameToNodeName(n)
	set_name(nameToNodeName(n))
	return self

func setPairName(n):
	pairName = nameToNodeName(n)
	return self

func setBlockLayer(n):
	blockLayer = n
	return self

func getBlockLayer():
	return blockLayer

func setBlockType( blockT ):
	blockType = blockT
	return self

func getBlockType():
	return blockType

func setSelected(sel):
	selected = sel
	return self

func forceClick(click_normal):
	if editor != null:
		if editor.shouldAddNeighbor():
			addNeighbor(editor, click_normal)
			return
		if editor.shouldReplaceSelf():
			addNeighbor(editor, 0 * click_normal)
		if editor.shouldReplaceSelf() or editor.shouldRemoveSelf():
			if pairName != null:
				var pair = get_parent().get_node(pairName)
				if pair != null:
					pair.request_remove()
			request_remove()
			return
	else:
		activate()
		get_parent().clickBlock( name )

func addNeighbor(editor, click_normal):
	var t = get_parent().get_parent().get_transform().inverse()
	editor.addBlock(blockPos + t * click_normal)

# catch clicks/taps
func _input_event( camera, ev, click_pos, click_normal, shape_idx ):
	if ((ev.type==InputEvent.MOUSE_BUTTON and ev.button_index==BUTTON_LEFT)
	or (ev.type==InputEvent.SCREEN_TOUCH)):
		if (get_parent().get_parent().active and ev.is_pressed()):
			forceClick(click_normal)

# returns this block's pairNode or null
func pairActivate():
	selected = true

	# is my pair Nil?
	if pairName == null or get_parent() == null or not get_parent().has_node(pairName):
		scaleTweenNode(0.9, 0.2, Tween.TRANS_EXPO).start()
		return null

	# get my pair node
	var pairNode = get_parent().get_node(pairName)

	# enlarge if the other guy's unselected
	if not pairNode.selected:
		scaleTweenNode(1.1, 0.2, Tween.TRANS_EXPO).start()
	return pairNode

# returns a tween node that uniformly changes the object's scale
# call start() on the returned object
func scaleTweenNode(scale, time=1, transType=Tween.TRANS_BOUNCE):
	var tweenNode = newTweenNode()
	tweenNode.interpolate_method( self, "set_scale", \
		self.get_scale(), Vector3(scale, scale, scale), \
		time, transType, Tween.EASE_OUT )

	return tweenNode

# adds a tween transition node to the tree
func newTweenNode():
	var tween = Tween.new()
	add_child(tween)
	return tween

func request_remove(node=null, key=null):
	get_parent().remove_block(self)

func _ready():
	editor = get_tree().get_root().get_node("EditorSpatial")
	set_ray_pickable(true)


tweak buttbutt speeds

tweak buttbutt speeds

extends KinematicBody2D

#### SIGNALS ####
signal exit
signal die
signal controls_changed

#### VARIABLES ####

## Export

# traces the fsm changes
export (bool) var debug = false

# default moves available
export(String, "move_up", "move_down", "move_left", "move_right", "run", "jump") var control_1 = "move_right"
export(String, "move_up", "move_down", "move_left", "move_right", "run", "jump") var control_2 = "move_left"

# controls are pressed
var control_1_pressed = false
var control_2_pressed = false

# keep finger id for touchscreen
var touchscreen_right = -1
var touchscreen_left = -1

## FSM

const IDLE = 0
const WALKING = 1
const RUNNING = 2
const JUMPING = 3
const FALLING = 4
const CLIMBING = 5
# current state
var fsm = IDLE

## PHYSICS

const GRAVITY = 1200
const WALK_SPEED = 180
const RUN_SPEED = 350
const JUMP_SPEED = 80
const CLIMB_SPEED = Vector2(120, 200)

var velocity = Vector2(0, 0)

const MAX_JUMP_TIME = 0.11
var jumping_time = 0

#### ONREADY ####

onready var interaction_area = get_node("interaction_area")
onready var climb_area = preload("res://sources/scripts/levels/climb_area.gd")
onready var initial_pos = get_pos()

func _ready():
	set_fixed_process(true)
	set_process_input(true)
	print(get_pos(), " start")

func reset():
	set_pos(initial_pos)
	velocity = Vector2(0,0)
	fsm = IDLE

#### METHODS ####

func _fixed_process(delta):
	decide_fsm(delta)
	update_physics(delta)

func _input(event):
	update_controls(event)

# PHYSICS

func update_physics(delta):
	# first update the velocity with gravity
	update_gravity(delta)

	# next update the velocity according to the fsm state
	if fsm == IDLE:
		update_idle(delta)
	elif fsm == WALKING:
		update_walking(delta)
	elif fsm == RUNNING:
		update_running(delta)
	elif fsm == JUMPING:
		update_jumping(delta)
	elif fsm == FALLING:
		update_falling(delta)
	elif fsm == CLIMBING:
		update_climbing(delta)

	# finally integrate the velocity into actual motion
	integrate_motion(delta)

func update_idle(delta):
	velocity.y = 0
	velocity.x = lerp(velocity.x, 0, 0.1)

func update_walking(delta):
	if is_on_ground() :
		velocity.y = 0
		if available_action_pressed("move_left") && available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, 0, 0.2)
		elif available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -WALK_SPEED, 0.2)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, WALK_SPEED, 0.2)

func update_running(delta):
	if is_on_ground() :
		velocity.y = 0
		if available_action_pressed("move_left") && available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, 0, 0.2)
		elif available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -RUN_SPEED, 0.2)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, RUN_SPEED, 0.2)

func update_jumping(delta):
	jumping_time += delta
	if abs(velocity.x) < RUN_SPEED && available_action_pressed("run"):
		if available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -RUN_SPEED, 0.3)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, RUN_SPEED, 0.3)
	elif abs(velocity.x) < WALK_SPEED && !available_action_pressed("run"):
		if available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -WALK_SPEED, 0.3)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, WALK_SPEED, 0.3)

	velocity.y -= JUMP_SPEED

func update_falling(delta):
	if abs(velocity.x) < RUN_SPEED && available_action_pressed("run") && (available_action_pressed("move_left") || available_action_pressed("move_right")):
		if available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -RUN_SPEED, 0.1)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, RUN_SPEED, 0.1)
	elif abs(velocity.x) < WALK_SPEED && !available_action_pressed("run") && (available_action_pressed("move_left") || available_action_pressed("move_right")):
		if available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -WALK_SPEED, 0.2)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, WALK_SPEED, 0.2)
	else:
		velocity.x = lerp(velocity.x, 0, 0.02)

func update_climbing(delta):
	var direction = Vector2()

	if available_action_pressed("move_up") && !reached_top():
		direction.y -= 1
	if available_action_pressed("move_down"):
		direction.y += 1

	if available_action_pressed("move_left"):
		direction.x -= 1
	if available_action_pressed("move_right"):
		direction.x += 1

	move_and_slide(direction * CLIMB_SPEED)

	velocity.y = 0
	velocity.x = 0

func update_gravity(delta):
	#velocity.y += delta * GRAVITY
	velocity.y = lerp(velocity.y, GRAVITY, 0.015)

func integrate_motion(delta):
	var motion = velocity * delta
	move_and_slide(velocity)

	if is_colliding():
		var n = get_collision_normal()
		motion = n.slide(motion)
		velocity = n.slide(velocity)
		move(motion)

func is_on_ground():
	 return test_move(get_transform(), Vector2(0, 1))

func reset_jump():
	jumping_time = 0
	if control_1 == "jump" && control_1_pressed:
		control_1_pressed = false
	if control_2 == "jump" && control_2_pressed:
		control_2_pressed = false
# FSM

func decide_fsm(delta):
	if fsm == IDLE:
		decide_fsm_idle(delta)
	elif fsm == WALKING:
		decide_fsm_walking(delta)
	elif fsm == RUNNING:
		decide_fsm_running(delta)
	elif fsm == JUMPING:
		decide_fsm_jumping(delta)
	elif fsm == FALLING:
		decide_fsm_falling(delta)
	elif fsm == CLIMBING:
		decide_fsm_climbing(delta)

func decide_fsm_idle(delta):
	if !is_on_ground():
		change_state(FALLING)
	elif available_action_pressed("jump"):
		change_state(JUMPING)
	elif can_climb() && !reached_top() && (available_action_pressed("move_up") || available_action_pressed("move_down")):
		change_state(CLIMBING)
	elif available_action_pressed("move_left") || available_action_pressed("move_right") :
		if available_action_pressed("run"):
			change_state(RUNNING)
		else:
			change_state(WALKING)

func decide_fsm_walking(delta):
	if !is_on_ground():
		change_state(FALLING)
	elif available_action_pressed("jump"):
		change_state(JUMPING)
	elif can_climb() && !reached_top() && (available_action_pressed("move_up") || available_action_pressed("move_down")):
		change_state(CLIMBING)
	elif available_action_pressed("run"):
		change_state(RUNNING)
	elif !available_action_pressed("move_left") && !available_action_pressed("move_right") :
		change_state(IDLE)

func decide_fsm_running(delta):
	if !is_on_ground():
		change_state(FALLING)
	elif available_action_pressed("jump"):
		change_state(JUMPING)
	elif can_climb() && !reached_top() && (available_action_pressed("move_up") || available_action_pressed("move_down")):
		change_state(CLIMBING)
	elif !available_action_pressed("move_left") && !available_action_pressed("move_right") :
		change_state(IDLE)
	elif !available_action_pressed("run"):
		change_state(WALKING)

func decide_fsm_jumping(delta):
	if jumping_time > MAX_JUMP_TIME || !available_action_pressed("jump"):
		change_state(FALLING)
		reset_jump()

func decide_fsm_falling(delta):
	if is_on_ground():
		if available_action_pressed("move_left") || available_action_pressed("move_right"):
			change_state(WALKING)
		else :
			change_state(IDLE)
	elif can_climb() && !reached_top() && (available_action_pressed("move_up") || available_action_pressed("move_down")):
		change_state(CLIMBING)

func decide_fsm_climbing(delta):
	if available_action_pressed("jump"):
		change_state(FALLING)
	elif !can_climb():
		change_state(FALLING)

func change_state(id):
	if debug:
		print(get_fsm_name(fsm), " -> ", get_fsm_name(id))
	fsm = id

func can_climb():
	for area in interaction_area.get_overlapping_areas():
		if area extends climb_area:
			return true
	return false

func reached_top():
	for area in interaction_area.get_overlapping_areas():
		if area extends climb_area && !area.is_on_top(interaction_area):
			return false
	return true

func get_fsm_name(id = -1):
	if id == -1:
		id = fsm
	if id == IDLE:
		return "idle"
	elif id == WALKING:
		return "walking"
	elif id == RUNNING:
		return "running"
	elif id == JUMPING:
		return "jumping"
	elif id == FALLING:
		return "falling"
	elif id == CLIMBING:
		return "climbing"

# Interactions

func update_controls(event):
	# update control_x_pressed state

	# touchscreen event
	if event.type == InputEvent.SCREEN_TOUCH:
		# just pressed the screen
		if event.pressed:
			# checks if bottom part of the screen
			if event.pos.y > 200:
				# checks if left part of the screen and no finger already in this part
				if event.pos.x <= 1024 / 2 && touchscreen_left == -1:
					touchscreen_left = event.index
					control_1_pressed = true
				# checks if right part of the screen and no finger already in this part
				elif event.pos.x > 1024 / 2 && touchscreen_right == -1:
					touchscreen_right = event.index
					control_2_pressed = true
		# just released a finger
		else:
			# if the finger released was on the right part
			if event.index == touchscreen_right:
				touchscreen_right = -1
				control_2_pressed = false
			# if the finger released was on the left part
			elif event.index == touchscreen_left:
				touchscreen_left = -1
				control_1_pressed = false

	# Keyboard event
	elif event.type == InputEvent.KEY && !event.is_echo():
		if event.is_action(control_1):
			control_1_pressed = event.pressed
		if event.is_action(control_2):
			control_2_pressed = event.pressed
		if event.is_action("ui_page_up"):
			control_1_pressed = event.pressed
		if event.is_action("ui_page_down"):
			control_2_pressed = event.pressed

func available_action_pressed(action):
	if debug:
		return Input.is_action_pressed(action)
	else:
		return (control_1_pressed && control_1 == action) || (control_2_pressed && control_2 == action)

func change_controls(control_1, control_2):
	if self.control_1 != control_1:
		control_1_pressed = false
		self.control_1 = control_1
	if self.control_2 != control_2:
		control_2_pressed = false
		self.control_2 = control_2
	emit_signal("controls_changed", self)

func release_controls():
	control_1_pressed = false
	control_2_pressed = false

func die():
	emit_signal("die")

func exit():
	emit_signal("exit")

extends KinematicBody2D

#### SIGNALS ####
signal exit
signal die
signal controls_changed

#### VARIABLES ####

## Export

# traces the fsm changes
export (bool) var debug = false

# default moves available
export(String, "move_up", "move_down", "move_left", "move_right", "run", "jump") var control_1 = "move_right"
export(String, "move_up", "move_down", "move_left", "move_right", "run", "jump") var control_2 = "move_left"

# controls are pressed
var control_1_pressed = false
var control_2_pressed = false

# keep finger id for touchscreen
var touchscreen_right = -1
var touchscreen_left = -1

## FSM

const IDLE = 0
const WALKING = 1
const RUNNING = 2
const JUMPING = 3
const FALLING = 4
const CLIMBING = 5
# current state
var fsm = IDLE

## PHYSICS

const GRAVITY = 1200
const WALK_SPEED = 200
const RUN_SPEED = 350
const JUMP_SPEED = 70
const CLIMB_SPEED = Vector2(70, 130)

var velocity = Vector2(0, 0)

const MAX_JUMP_TIME = 0.11
var jumping_time = 0

#### ONREADY ####

onready var interaction_area = get_node("interaction_area")
onready var climb_area = preload("res://sources/scripts/levels/climb_area.gd")
onready var initial_pos = get_pos()

func _ready():
	set_fixed_process(true)
	set_process_input(true)
	print(get_pos(), " start")

func reset():
	set_pos(initial_pos)
	velocity = Vector2(0,0)
	fsm = IDLE

#### METHODS ####

func _fixed_process(delta):
	decide_fsm(delta)
	update_physics(delta)

func _input(event):
	update_controls(event)

# PHYSICS

func update_physics(delta):
	# first update the velocity with gravity
	update_gravity(delta)

	# next update the velocity according to the fsm state
	if fsm == IDLE:
		update_idle(delta)
	elif fsm == WALKING:
		update_walking(delta)
	elif fsm == RUNNING:
		update_running(delta)
	elif fsm == JUMPING:
		update_jumping(delta)
	elif fsm == FALLING:
		update_falling(delta)
	elif fsm == CLIMBING:
		update_climbing(delta)

	# finally integrate the velocity into actual motion
	integrate_motion(delta)

func update_idle(delta):
	velocity.y = 0
	velocity.x = lerp(velocity.x, 0, 0.1)

func update_walking(delta):
	if is_on_ground() :
		velocity.y = 0
		if available_action_pressed("move_left") && available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, 0, 0.2)
		elif available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -WALK_SPEED, 0.2)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, WALK_SPEED, 0.2)

func update_running(delta):
	if is_on_ground() :
		velocity.y = 0
		if available_action_pressed("move_left") && available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, 0, 0.2)
		elif available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -RUN_SPEED, 0.2)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, RUN_SPEED, 0.2)

func update_jumping(delta):
	jumping_time += delta
	if abs(velocity.x) < RUN_SPEED && available_action_pressed("run"):
		if available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -RUN_SPEED, 0.3)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, RUN_SPEED, 0.3)
	elif abs(velocity.x) < WALK_SPEED && !available_action_pressed("run"):
		if available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -WALK_SPEED, 0.3)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, WALK_SPEED, 0.3)

	velocity.y -= JUMP_SPEED

func update_falling(delta):
	if abs(velocity.x) < RUN_SPEED && available_action_pressed("run") && (available_action_pressed("move_left") || available_action_pressed("move_right")):
		if available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -RUN_SPEED, 0.1)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, RUN_SPEED, 0.1)
	elif abs(velocity.x) < WALK_SPEED && !available_action_pressed("run") && (available_action_pressed("move_left") || available_action_pressed("move_right")):
		if available_action_pressed("move_left"):
			velocity.x = lerp(velocity.x, -WALK_SPEED, 0.2)
		elif available_action_pressed("move_right"):
			velocity.x = lerp(velocity.x, WALK_SPEED, 0.2)
	else:
		velocity.x = lerp(velocity.x, 0, 0.02)

func update_climbing(delta):
	var direction = Vector2()

	if available_action_pressed("move_up") && !reached_top():
		direction.y -= 1
	if available_action_pressed("move_down"):
		direction.y += 1

	if available_action_pressed("move_left"):
		direction.x -= 1
	if available_action_pressed("move_right"):
		direction.x += 1

	move_and_slide(direction * CLIMB_SPEED)

	velocity.y = 0
	velocity.x = 0

func update_gravity(delta):
	#velocity.y += delta * GRAVITY
	velocity.y = lerp(velocity.y, GRAVITY, 0.015)

func integrate_motion(delta):
	var motion = velocity * delta
	move_and_slide(velocity)

	if is_colliding():
		var n = get_collision_normal()
		motion = n.slide(motion)
		velocity = n.slide(velocity)
		move(motion)

func is_on_ground():
	 return test_move(get_transform(), Vector2(0, 1))

func reset_jump():
	jumping_time = 0
	if control_1 == "jump" && control_1_pressed:
		control_1_pressed = false
	if control_2 == "jump" && control_2_pressed:
		control_2_pressed = false
# FSM

func decide_fsm(delta):
	if fsm == IDLE:
		decide_fsm_idle(delta)
	elif fsm == WALKING:
		decide_fsm_walking(delta)
	elif fsm == RUNNING:
		decide_fsm_running(delta)
	elif fsm == JUMPING:
		decide_fsm_jumping(delta)
	elif fsm == FALLING:
		decide_fsm_falling(delta)
	elif fsm == CLIMBING:
		decide_fsm_climbing(delta)

func decide_fsm_idle(delta):
	if !is_on_ground():
		change_state(FALLING)
	elif available_action_pressed("jump"):
		change_state(JUMPING)
	elif can_climb() && !reached_top() && (available_action_pressed("move_up") || available_action_pressed("move_down")):
		change_state(CLIMBING)
	elif available_action_pressed("move_left") || available_action_pressed("move_right") :
		if available_action_pressed("run"):
			change_state(RUNNING)
		else:
			change_state(WALKING)

func decide_fsm_walking(delta):
	if !is_on_ground():
		change_state(FALLING)
	elif available_action_pressed("jump"):
		change_state(JUMPING)
	elif can_climb() && !reached_top() && (available_action_pressed("move_up") || available_action_pressed("move_down")):
		change_state(CLIMBING)
	elif available_action_pressed("run"):
		change_state(RUNNING)
	elif !available_action_pressed("move_left") && !available_action_pressed("move_right") :
		change_state(IDLE)

func decide_fsm_running(delta):
	if !is_on_ground():
		change_state(FALLING)
	elif available_action_pressed("jump"):
		change_state(JUMPING)
	elif can_climb() && !reached_top() && (available_action_pressed("move_up") || available_action_pressed("move_down")):
		change_state(CLIMBING)
	elif !available_action_pressed("move_left") && !available_action_pressed("move_right") :
		change_state(IDLE)
	elif !available_action_pressed("run"):
		change_state(WALKING)

func decide_fsm_jumping(delta):
	if jumping_time > MAX_JUMP_TIME || !available_action_pressed("jump"):
		change_state(FALLING)
		reset_jump()

func decide_fsm_falling(delta):
	if is_on_ground():
		if available_action_pressed("move_left") || available_action_pressed("move_right"):
			change_state(WALKING)
		else :
			change_state(IDLE)
	elif can_climb() && !reached_top() && (available_action_pressed("move_up") || available_action_pressed("move_down")):
		change_state(CLIMBING)

func decide_fsm_climbing(delta):
	if available_action_pressed("jump"):
		change_state(FALLING)
	elif !can_climb():
		change_state(FALLING)

func change_state(id):
	if debug:
		print(get_fsm_name(fsm), " -> ", get_fsm_name(id))
	fsm = id

func can_climb():
	for area in interaction_area.get_overlapping_areas():
		if area extends climb_area:
			return true
	return false

func reached_top():
	for area in interaction_area.get_overlapping_areas():
		if area extends climb_area && !area.is_on_top(interaction_area):
			return false
	return true

func get_fsm_name(id = -1):
	if id == -1:
		id = fsm
	if id == IDLE:
		return "idle"
	elif id == WALKING:
		return "walking"
	elif id == RUNNING:
		return "running"
	elif id == JUMPING:
		return "jumping"
	elif id == FALLING:
		return "falling"
	elif id == CLIMBING:
		return "climbing"

# Interactions

func update_controls(event):
	# update control_x_pressed state

	# touchscreen event
	if event.type == InputEvent.SCREEN_TOUCH:
		# just pressed the screen
		if event.pressed:
			# checks if bottom part of the screen
			if event.pos.y > 200:
				# checks if left part of the screen and no finger already in this part
				if event.pos.x <= 1024 / 2 && touchscreen_left == -1:
					touchscreen_left = event.index
					control_1_pressed = true
				# checks if right part of the screen and no finger already in this part
				elif event.pos.x > 1024 / 2 && touchscreen_right == -1:
					touchscreen_right = event.index
					control_2_pressed = true
		# just released a finger
		else:
			# if the finger released was on the right part
			if event.index == touchscreen_right:
				touchscreen_right = -1
				control_2_pressed = false
			# if the finger released was on the left part
			elif event.index == touchscreen_left:
				touchscreen_left = -1
				control_1_pressed = false

	# Keyboard event
	elif event.type == InputEvent.KEY && !event.is_echo():
		if event.is_action(control_1):
			control_1_pressed = event.pressed
		if event.is_action(control_2):
			control_2_pressed = event.pressed
		if event.is_action("ui_page_up"):
			control_1_pressed = event.pressed
		if event.is_action("ui_page_down"):
			control_2_pressed = event.pressed

func available_action_pressed(action):
	if debug:
		return Input.is_action_pressed(action)
	else:
		return (control_1_pressed && control_1 == action) || (control_2_pressed && control_2 == action)

func change_controls(control_1, control_2):
	if self.control_1 != control_1:
		self.control_1 = control_1
		control_1_pressed = false
	if self.control_2 != control_2:
		self.control_2 = control_2
		control_2_pressed = false
	emit_signal("controls_changed", self)

func release_controls():
	control_1_pressed = false
	control_2_pressed = false

func die():
	emit_signal("die")

func exit():
	emit_signal("exit")


Fix jumps in scale and rotation when pinching

When pressing with two fingers at once and pinching,  an old coordinate contained in the curr_state dictionary gets added to the base_state dictionary, which causes unintended jumps in both scale and rotation. By clearing base_state the script functions as intended.

extends Control

export(NodePath) var target
export var min_scale = 0.1
export var max_scale = 3.0
export var one_finger_rot_x = true
export var one_finger_rot_y = true
export var two_fingers_rot_z = true
export var two_fingers_zoom = true

var base_state
var curr_state

var target_node

# We keep here a copy of the state before the number of fingers changed to avoid accumulation errors.
var base_xform

func _ready():
	base_state = {}
	curr_state = {}
	target_node = get_node(target)

func _gui_input(event):
	# We must start touching inside, but we can drag or unpress outside.
#	if not (event is InputEventScreenDrag or
#		(event is InputEventScreenTouch and (not event.pressed or get_global_rect().has_point(event.position)))):
#		return

	var finger_count = base_state.size()

	if finger_count == 0:
		# No fingers => Accept press.
		if event is InputEventScreenTouch:
			if event.pressed:
				# A finger started touching.

				base_state = {
					event.index: event.position,
				}

	elif finger_count == 1:
		# One finger => For rotating around X and Y.
		# Accept one more press, unpress or drag.
		if event is InputEventScreenTouch:
			if event.pressed:
				# One more finger started touching.

				# Reset the base state to the only current and the new fingers.
				base_state = {
					curr_state.keys()[0]: curr_state.values()[0],
					event.index: event.position,
				}
			else:
				if base_state.has(event.index):
					# Only touching finger released.

					base_state.clear()

		elif event is InputEventScreenDrag:
			if curr_state.has(event.index):
				# Touching finger dragged.
				var unit_drag = _px2unit(base_state[base_state.keys()[0]] - event.position)
				if one_finger_rot_x:
					target_node.global_rotate(Vector3.UP, deg2rad(180.0 * unit_drag.x))
				if one_finger_rot_y:
					target_node.global_rotate(Vector3.RIGHT, deg2rad(180.0 * unit_drag.y))
				# Since rotating around two axes, we have to reset the base constantly.
				curr_state[event.index] = event.position
				base_state[event.index] = event.position
				base_xform = target_node.get_transform()

	elif finger_count == 2:
		# Two fingers => To pinch-zoom and rotate around Z.
		# Accept unpress or drag.
		if event is InputEventScreenTouch:
			if not event.pressed and base_state.has(event.index):
				# Some known touching finger released.

				# Clear the base state
				base_state.clear()

		elif event is InputEventScreenDrag:
			if curr_state.has(event.index):
				# Some known touching finger dragged.
				curr_state[event.index] = event.position

				# Compute base and current inter-finger vectors.
				var base_segment = base_state[base_state.keys()[0]] - base_state[base_state.keys()[1]]
				var new_segment = curr_state[curr_state.keys()[0]] - curr_state[curr_state.keys()[1]]

				# Get the base scale from the base matrix.
				var base_scale = Vector3(base_xform.basis.x.x, base_xform.basis.y.y, base_xform.basis.z.z).length()

				if two_fingers_zoom:
					# Compute the new scale limiting it and taking into account the base scale.
					var new_scale = clamp(base_scale * (new_segment.length() / base_segment.length()), min_scale, max_scale) / base_scale
					target_node.set_transform(base_xform.scaled(new_scale * Vector3.ONE))
				else:
					target_node.set_transform(base_xform)

				if two_fingers_rot_z:
					# Apply rotation between base inter-finger vector and the current one.
					var rot = new_segment.angle_to(base_segment)
					target_node.global_rotate(Vector3.BACK, rot)

	# Finger count changed?
	if base_state.size() != finger_count:
		# Copy new base state to the current state.
		curr_state = {}
		for idx in base_state.keys():
			curr_state[idx] = base_state[idx]
		# Remember the base transform.
		base_xform = target_node.get_transform()

# Converts a vector in pixels to a unitary magnitude,
# considering the number of pixels of the shorter axis is the unit.
func _px2unit(v):
	var shortest = min(get_size().x, get_size().y)
	return v * (1.0 / shortest)

extends Control

export(NodePath) var target
export var min_scale = 0.1
export var max_scale = 3.0
export var one_finger_rot_x = true
export var one_finger_rot_y = true
export var two_fingers_rot_z = true
export var two_fingers_zoom = true

var base_state
var curr_state

var target_node

# We keep here a copy of the state before the number of fingers changed to avoid accumulation errors.
var base_xform

func _ready():
	base_state = {}
	curr_state = {}
	target_node = get_node(target)

func _gui_input(event):
	# We must start touching inside, but we can drag or unpress outside.
#	if not (event is InputEventScreenDrag or
#		(event is InputEventScreenTouch and (not event.pressed or get_global_rect().has_point(event.position)))):
#		return

	var finger_count = base_state.size()

	if finger_count == 0:
		# No fingers => Accept press.
		if event is InputEventScreenTouch:
			if event.pressed:
				# A finger started touching.

				base_state = {
					event.index: event.position,
				}

	elif finger_count == 1:
		# One finger => For rotating around X and Y.
		# Accept one more press, unpress or drag.
		if event is InputEventScreenTouch:
			if event.pressed:
				# One more finger started touching.

				# Reset the base state to the only current and the new fingers.
				base_state = {
					curr_state.keys()[0]: curr_state.values()[0],
					event.index: event.position,
				}
			else:
				if base_state.has(event.index):
					# Only touching finger released.

					base_state.clear()

		elif event is InputEventScreenDrag:
			if curr_state.has(event.index):
				# Touching finger dragged.
				var unit_drag = _px2unit(base_state[base_state.keys()[0]] - event.position)
				if one_finger_rot_x:
					target_node.global_rotate(Vector3.UP, deg2rad(180.0 * unit_drag.x))
				if one_finger_rot_y:
					target_node.global_rotate(Vector3.RIGHT, deg2rad(180.0 * unit_drag.y))
				# Since rotating around two axes, we have to reset the base constantly.
				curr_state[event.index] = event.position
				base_state[event.index] = event.position
				base_xform = target_node.get_transform()

	elif finger_count == 2:
		# Two fingers => To pinch-zoom and rotate around Z.
		# Accept unpress or drag.
		if event is InputEventScreenTouch:
			if not event.pressed and base_state.has(event.index):
				# Some known touching finger released.

				# Remove released finger from the base state.
				base_state.erase(event.index)
				# Reset the base state to the now only toyching finger.
				base_state = {
					curr_state.keys()[0]: curr_state.values()[0],
				}

		elif event is InputEventScreenDrag:
			if curr_state.has(event.index):
				# Some known touching finger dragged.
				curr_state[event.index] = event.position

				# Compute base and current inter-finger vectors.
				var base_segment = base_state[base_state.keys()[0]] - base_state[base_state.keys()[1]]
				var new_segment = curr_state[curr_state.keys()[0]] - curr_state[curr_state.keys()[1]]

				# Get the base scale from the base matrix.
				var base_scale = Vector3(base_xform.basis.x.x, base_xform.basis.y.y, base_xform.basis.z.z).length()

				if two_fingers_zoom:
					# Compute the new scale limiting it and taking into account the base scale.
					var new_scale = clamp(base_scale * (new_segment.length() / base_segment.length()), min_scale, max_scale) / base_scale
					target_node.set_transform(base_xform.scaled(new_scale * Vector3.ONE))
				else:
					target_node.set_transform(base_xform)

				if two_fingers_rot_z:
					# Apply rotation between base inter-finger vector and the current one.
					var rot = new_segment.angle_to(base_segment)
					target_node.global_rotate(Vector3.BACK, rot)

	# Finger count changed?
	if base_state.size() != finger_count:
		# Copy new base state to the current state.
		curr_state = {}
		for idx in base_state.keys():
			curr_state[idx] = base_state[idx]
		# Remember the base transform.
		base_xform = target_node.get_transform()

# Converts a vector in pixels to a unitary magnitude,
# considering the number of pixels of the shorter axis is the unit.
func _px2unit(v):
	var shortest = min(get_size().x, get_size().y)
	return v * (1.0 / shortest)


pushback range nerf

pushback range nerf

extends "res://scripts/object.gd"

var velocity
var movement_vector = [0, 0]

var AXIS_THRESHOLD = 0.15

var body_part_head
var body_part_body
var body_part_footer
var animations
var hat = false

var stun_duration = 0.15
var stun_level = 0

func _init(bag).(bag):
    self.bag = bag

func spawn(position):
    .spawn(position)
    self.bag.processing.register(self)

func despawn():
    self.bag.processing.remove(self)
    .despawn()

func process(delta):
    self.modify_position(delta)

func modify_position(delta):
    var x = self.apply_axis_threshold(self.movement_vector[0])
    var y = self.apply_axis_threshold(self.movement_vector[1])
    var motion = Vector2(x, y) * self.velocity * delta
    self.avatar.move(motion)
    if (self.avatar.is_colliding()):
        var n = self.avatar.get_collision_normal()
        motion = n.slide(motion)
        self.avatar.move(motion)
    self.flip(self.movement_vector[0])

func apply_axis_threshold(axis_value):
    if abs(axis_value) < self.AXIS_THRESHOLD:
        return 0
    return axis_value

func flip(direction):
    if direction == 0:
        return

    var flip_sprite = false
    if direction > 0:
        flip_sprite = true

    self.body_part_head.set_flip_h(flip_sprite)
    self.body_part_body.set_flip_h(flip_sprite)
    self.body_part_footer.set_flip_h(flip_sprite)
    if self.hat:
        self.hat.set_flip_h(flip_sprite)

func reset_movement():
    self.movement_vector = [0, 0]

func push_back(enemy):
    var enemy_position = enemy.get_pos()
    var object_position = self.get_pos()

    var position_delta_x =  object_position.x - enemy_position.x
    var position_delta_y = object_position.y - enemy_position.y
    var force = pow(enemy.attack_strength, -1)

    var scale = force / self.calculate_distance(enemy_position) * 10

    self.avatar.move(Vector2(position_delta_x * scale, position_delta_y * scale))
    self.stun()

func stun(duration=null):
    if duration == null:
        duration = self.stun_duration
    self.is_processing = false
    self.stun_level = stun_level + 1
    self.bag.timers.set_timeout(duration, self, "remove_stun")

func remove_stun():
    self.stun_level = stun_level - 1
    if self.stun_level == 0:
        self.is_processing = true

extends "res://scripts/object.gd"

var velocity
var movement_vector = [0, 0]

var AXIS_THRESHOLD = 0.15

var body_part_head
var body_part_body
var body_part_footer
var animations
var hat = false

var stun_duration = 0.15
var stun_level = 0

func _init(bag).(bag):
    self.bag = bag

func spawn(position):
    .spawn(position)
    self.bag.processing.register(self)

func despawn():
    self.bag.processing.remove(self)
    .despawn()

func process(delta):
    self.modify_position(delta)

func modify_position(delta):
    var x = self.apply_axis_threshold(self.movement_vector[0])
    var y = self.apply_axis_threshold(self.movement_vector[1])
    var motion = Vector2(x, y) * self.velocity * delta
    self.avatar.move(motion)
    if (self.avatar.is_colliding()):
        var n = self.avatar.get_collision_normal()
        motion = n.slide(motion)
        self.avatar.move(motion)
    self.flip(self.movement_vector[0])

func apply_axis_threshold(axis_value):
    if abs(axis_value) < self.AXIS_THRESHOLD:
        return 0
    return axis_value

func flip(direction):
    if direction == 0:
        return

    var flip_sprite = false
    if direction > 0:
        flip_sprite = true

    self.body_part_head.set_flip_h(flip_sprite)
    self.body_part_body.set_flip_h(flip_sprite)
    self.body_part_footer.set_flip_h(flip_sprite)
    if self.hat:
        self.hat.set_flip_h(flip_sprite)

func reset_movement():
    self.movement_vector = [0, 0]

func push_back(enemy):
    var enemy_position = enemy.get_pos()
    var object_position = self.get_pos()

    var position_delta_x =  object_position.x - enemy_position.x
    var position_delta_y = object_position.y - enemy_position.y
    var force = pow(enemy.attack_strength, -1)

    var scale = force / self.calculate_distance(enemy_position) * 20 - mass / force

    self.avatar.move(Vector2(position_delta_x * scale, position_delta_y * scale))
    self.stun()

func stun(duration=null):
    if duration == null:
        duration = self.stun_duration
    self.is_processing = false
    self.stun_level = stun_level + 1
    self.bag.timers.set_timeout(duration, self, "remove_stun")

func remove_stun():
    self.stun_level = stun_level - 1
    if self.stun_level == 0:
        self.is_processing = true


Fix function typo

Fix function typo

tool
extends Control

# Minimum size the parent Control must have in order to trigger this Responsive Sizing
export(Vector2) var minimumParentSize = Vector2()

# Whether or not this Responsive Sizing is visible in the set size
export(bool) var isVisible = true

# Checks if this Responsive Sizing is eligible under the specified size
func isEligible(size):
	return size.width >= minimumParentSize.width && size.height >= minimumParentSize.height

# Applies this Responsive Sizing to the specified Control
func applyTo(control):
	if isVisible != control.is_visible():
		if isVisible:
			control.show()
		else:
			control.hide()

	control.set_h_size_flags(get_h_size_flags())
	control.set_v_size_flags(get_v_size_flags())
	control.set_scale(get_scale())
	for margin in [ MARGIN_BOTTOM, MARGIN_TOP, MARGIN_RIGHT, MARGIN_LEFT ]:
		control.set_anchor(margin, get_anchor(margin))
		control.set_margin(margin, get_margin(margin))

tool
extends Control

# Minimum size the parent Control must have in order to trigger this Responsive Sizing
export(Vector2) var minimumParentSize = Vector2()

# Whether or not this Responsive Sizing is visible in the set size
export(bool) var isVisible = true

# Checks if this Responsive Sizing is eligible under the specified size
func isEligible(size):
	return size.width >= minimumParentSize.width && size.height >= minimumParentSize.height

# Applies this Responsive Sizing to the specified Control
func applyTo(control):
	if isVisible != control.is_visible():
		if isVisible:
			control.show()
		else:
			control.hide()

	control.set_h_size_flags(get_h_size_flags())
	control.set_v_size_flags(get_v_size_flags())
	control.set_scale(get_scale())
	for margin in [ MARGIN_BOTTOM, MARGIN_TOP, MARGIN_RIGHT, MARGIN_LEFT ]:
		control.set_anchor(margin, get_anchor(margin))
		control.set_margin(margin, get_marginr(margin))