clean-up

clean-up

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 add_block(b):
	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_parent().get_parent().get_node( "Camera" )
	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
		add_block(block.toNode())

# 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")

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 add_block(b):
	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")
	if block_node.get_script() == preload("Blocks/PairedBlock.gd"):
		print("PAIRED RM")
	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_parent().get_parent().get_node( "Camera" )
	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
		add_block(block.toNode())

# 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")


can now return to main menu from the host menu

# 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()

				gotoPuzzle()

				remotePuzzle = root.get_node("Puzzle")
				#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

				gotoPuzzle()

				remotePuzzle = root.get_node("Puzzle")
				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...")
		gotoMenu()
	elif !isHost and !isNetwork:
		stream.disconnect()
		print("Halting connection request...")
	elif !isHost and isNetwork:
		#connected to server already
		stream.disconnect()
		print("Disconnecting from server...")
		gotoMenu()

	#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)

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
		#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 puzzle = root.get_node( "Puzzle" )
		var pos = dataArray[1]
		puzzle.otherPuzzle.get_node("GridView/GridMan").forceClickBlock(pos)

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(pos):
	if !isNetwork:
		print("Error sending start packet: not connected!")
		return
	connection.put_var([REMOTE_BLOCK_UPDATE, pos])

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])

func gotoMenu():
	root.get_child( root.get_child_count() - 1 ).queue_free()
	root.add_child( load("res://menus.scn") )

func gotoPuzzle():
	var guiMan = preload("GUIManager.gd").new()
	guiMan.gotoPuzzleScene(root, true) # network = true

# 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()

				gotoPuzzle()

				remotePuzzle = root.get_node("Puzzle")
				#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

				gotoPuzzle()

				remotePuzzle = root.get_node("Puzzle")
				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)

	gotoMenu()

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 puzzle = root.get_node( "Puzzle" )
		var pos = dataArray[1]
		puzzle.otherPuzzle.get_node("GridView/GridMan").forceClickBlock(pos)

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(pos):
	if !isNetwork:
		print("Error sending start packet: not connected!")
		return
	connection.put_var([REMOTE_BLOCK_UPDATE, pos])

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 gotoMenu():
	root.get_child( root.get_child_count() - 1 ).queue_free()
	root.add_child( load("res://menus.scn") )

func gotoPuzzle():
	var guiMan = preload("GUIManager.gd").new()
	guiMan.gotoPuzzleScene(root, true) # network = true


add delay for falling and jumping behaviours

extends Node

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

onready var player_sprites = get_node("sprites")
onready var interaction_area = get_node("interaction_area")

const WALK_SPEED = 200
const JUMP_SPEED = 300
const RUN_SPEED = 500
const CLIMB_SPEED = 100
const GROUND_FRICTION_MULTIPLIER = 0.7
const GROUND_ACCELERATION_MULTIPLIER = 0.2
const AIR_FRICTION_MULTIPLIER = 0.95
const AIR_DIRECTION_MULTIPLIER = 0.15
const JUMP_TIME_MAX = 0.2
const FALLING_JUMP_DELAY = 3

var jumping = false
var jump_time = 0
var falling_time = 0

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

func _ready():
	set_process(true)

func _integrate_forces(state):
	if fsm == IDLE:
		integrate_idle(state)
	elif fsm == WALKING:
		integrate_walking(state)
	elif fsm == RUNNING:
		integrate_running(state)
	elif fsm == JUMPING:
		integrate_jumping(state)
	elif fsm == FALLING:
		integrate_falling(state)
	elif fsm == CLIMBING:
		integrate_climbing(state)

func integrate_idle(state):
	player_sprites.play("stand")
	var velocity = state.get_linear_velocity()
	velocity.x *= GROUND_FRICTION_MULTIPLIER
	state.set_linear_velocity(velocity)

func integrate_walking(state):
	player_sprites.play("walk")
	var velocity = state.get_linear_velocity()

	if abs(velocity.x) > WALK_SPEED:
		if Input.is_action_pressed("move_left"):
			player_sprites.set_flip_h(true)
		elif Input.is_action_pressed("move_right"):
			player_sprites.set_flip_h(false)
		velocity.x = clamp(velocity.x * (1 - GROUND_ACCELERATION_MULTIPLIER), -RUN_SPEED, RUN_SPEED)
	else:
		if Input.is_action_pressed("move_left"):
			velocity.x -= WALK_SPEED * GROUND_ACCELERATION_MULTIPLIER
			player_sprites.set_flip_h(true)
		elif Input.is_action_pressed("move_right"):
			velocity.x += WALK_SPEED * GROUND_ACCELERATION_MULTIPLIER
			player_sprites.set_flip_h(false)

		velocity.x = clamp(velocity.x, -WALK_SPEED, WALK_SPEED)

	state.set_linear_velocity(velocity)

func integrate_running(state):
	player_sprites.play("walk")
	var velocity = state.get_linear_velocity()

	if Input.is_action_pressed("move_left"):
		velocity.x -= RUN_SPEED * GROUND_ACCELERATION_MULTIPLIER
		player_sprites.set_flip_h(true)
	elif Input.is_action_pressed("move_right"):
		velocity.x += RUN_SPEED * GROUND_ACCELERATION_MULTIPLIER
		player_sprites.set_flip_h(false)

	velocity.x = clamp(velocity.x, -RUN_SPEED, RUN_SPEED)

	state.set_linear_velocity(velocity)

func integrate_falling(state):
	player_sprites.play("jump")
	var velocity = state.get_linear_velocity()

	if velocity.x > -WALK_SPEED && Input.is_action_pressed("move_left"):
		player_sprites.set_flip_h(true)
		velocity.x -= WALK_SPEED * AIR_DIRECTION_MULTIPLIER
	elif velocity.x < WALK_SPEED && Input.is_action_pressed("move_right"):
		player_sprites.set_flip_h(false)
		velocity.x += WALK_SPEED * AIR_DIRECTION_MULTIPLIER
	elif !Input.is_action_pressed("move_left") && !Input.is_action_pressed("move_right"):
		velocity.x *= AIR_FRICTION_MULTIPLIER

	velocity.x = clamp(velocity.x, -RUN_SPEED, RUN_SPEED)
	state.set_linear_velocity(velocity)

func integrate_jumping(state):
	player_sprites.play("jump")
	state.set_linear_velocity(Vector2(state.get_linear_velocity().x,-JUMP_SPEED))

func integrate_climbing(state):
	player_sprites.play("jump")
	var velocity = state.get_linear_velocity()

	if Input.is_action_pressed("move_up"):
		velocity.y = -CLIMB_SPEED
	elif Input.is_action_pressed("move_down"):
		velocity.y = CLIMB_SPEED
	else:
		velocity.y = 0

	if Input.is_action_pressed("move_left"):
		velocity.x = - WALK_SPEED * GROUND_ACCELERATION_MULTIPLIER
		player_sprites.set_flip_h(true)
	elif Input.is_action_pressed("move_right"):
		velocity.x = WALK_SPEED * GROUND_ACCELERATION_MULTIPLIER
		player_sprites.set_flip_h(false)
	else:
		velocity.x = 0

	state.set_linear_velocity(velocity)

func _process(delta):
	var can_climb = false

	var overlapping_areas = interaction_area.get_overlapping_areas()

	for area in overlapping_areas:
		if can_climb:
			break
		can_climb = area extends climb_area

	if fsm == IDLE:
		if Input.is_action_pressed("jump"):
			fsm = JUMPING
			print("idle -> jump")
		elif Input.is_action_pressed("move_left") || Input.is_action_pressed("move_right"):
			fsm = WALKING
			print("idle -> walk")
		elif can_climb && ((Input.is_action_pressed("move_up") && !test_motion(Vector2(0,-5))) || (Input.is_action_pressed("move_down") && !test_motion(Vector2(0,5)))):
			#check if can climb and if thera are obstacles where we want to climb
			fsm = CLIMBING
			print("idle -> climb")
		elif !test_motion(Vector2(0,5)):
			fsm = FALLING
			print("idle -> fall")

	elif fsm == WALKING:
		if !Input.is_action_pressed("move_left") && !Input.is_action_pressed("move_right"):
			fsm = IDLE
			print("walk -> idle")
		elif Input.is_action_pressed("run"):
			fsm = RUNNING
			print("walk -> run")
		elif Input.is_action_pressed("jump"):
			fsm = JUMPING
			print("walk -> jump")
		elif can_climb && ((Input.is_action_pressed("move_up") && !test_motion(Vector2(0,-5))) || (Input.is_action_pressed("move_down") && !test_motion(Vector2(0,5)))):
			fsm = CLIMBING
			print("walk -> climb")
		elif !test_motion(Vector2(0,5)):
			fsm = FALLING
			print("walk -> fall")

	elif fsm == RUNNING:
		if !Input.is_action_pressed("run"):
			fsm = WALKING
			print("run -> walk")
		elif !Input.is_action_pressed("move_left") && !Input.is_action_pressed("move_right"):
			fsm = IDLE
			print("run -> idle")
		elif Input.is_action_pressed("jump"):
			fsm = JUMPING
			print("run -> jump")
		elif can_climb && ((Input.is_action_pressed("move_up") && !test_motion(Vector2(0,-5))) || (Input.is_action_pressed("move_down") && !test_motion(Vector2(0,5)))):
			#check if can climb and if thera are obstacles where we want to climb
			fsm = CLIMBING
			print("run -> climb")
		elif !test_motion(Vector2(0,5)):
			fsm = FALLING
			print("run -> fall")

	elif fsm == CLIMBING:
		if !can_climb:
			fsm = FALLING
			print("climb -> fall")
		elif Input.is_action_pressed("jump"):
			fsm = JUMPING
			print("climb -> jump")
		elif test_motion(Vector2(0,5)):
			fsm = IDLE
			print("climb -> idle")

	elif fsm == FALLING:
		falling_time += delta
		if can_climb && ((Input.is_action_pressed("move_up") && !test_motion(Vector2(0,-5))) || (Input.is_action_pressed("move_down") && !test_motion(Vector2(0,5)))):
			#check if can climb and if thera are obstacles where we want to climb
			fsm = CLIMBING
			falling_time = 0
			print("fall -> climb")
		elif test_motion(Vector2(0,5)):
			fsm = IDLE
			falling_time = 0
			print("fall -> idle")
		elif Input.is_action_pressed("jump") && falling_time < FALLING_JUMP_DELAY:
			fsm = JUMPING
			print("fall -> jump")

	elif fsm == JUMPING:
		jump_time += delta
		if !Input.is_action_pressed("jump") || jump_time > JUMP_TIME_MAX:
			fsm = FALLING
			jump_time = 0
			falling_time = FALLING_JUMP_DELAY
			print("jump -> fall")

extends Node

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

onready var player_sprites = get_node("sprites")
onready var interaction_area = get_node("interaction_area")

const WALK_SPEED = 200
const JUMP_SPEED = 400
const RUN_SPEED = 500
const CLIMB_SPEED = 100
const GROUND_FRICTION_MULTIPLIER = 0.7
const GROUND_ACCELERATION_MULTIPLIER = 0.2
const AIR_FRICTION_MULTIPLIER = 0.95
const AIR_DIRECTION_MULTIPLIER = 0.15

var jumping = false

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

func _ready():
	set_process(true)

func _integrate_forces(state):
	if fsm == IDLE:
		integrate_idle(state)
	elif fsm == WALKING:
		integrate_walking(state)
	elif fsm == RUNNING:
		integrate_running(state)
	elif fsm == JUMPING:
		integrate_jumping(state)
	elif fsm == FALLING:
		integrate_falling(state)
	elif fsm == CLIMBING:
		integrate_climbing(state)

func integrate_idle(state):
	player_sprites.play("stand")
	var velocity = state.get_linear_velocity()
	velocity.x *= GROUND_FRICTION_MULTIPLIER
	state.set_linear_velocity(velocity)

func integrate_walking(state):
	player_sprites.play("walk")
	var velocity = state.get_linear_velocity()

	if abs(velocity.x) > WALK_SPEED:
		if Input.is_action_pressed("move_left"):
			player_sprites.set_flip_h(true)
		elif Input.is_action_pressed("move_right"):
			player_sprites.set_flip_h(false)
		velocity.x = clamp(velocity.x * (1 - GROUND_ACCELERATION_MULTIPLIER), -RUN_SPEED, RUN_SPEED)
	else:
		if Input.is_action_pressed("move_left"):
			velocity.x -= WALK_SPEED * GROUND_ACCELERATION_MULTIPLIER
			player_sprites.set_flip_h(true)
		elif Input.is_action_pressed("move_right"):
			velocity.x += WALK_SPEED * GROUND_ACCELERATION_MULTIPLIER
			player_sprites.set_flip_h(false)

		velocity.x = clamp(velocity.x, -WALK_SPEED, WALK_SPEED)

	state.set_linear_velocity(velocity)

func integrate_running(state):
	player_sprites.play("walk")
	var velocity = state.get_linear_velocity()

	if Input.is_action_pressed("move_left"):
		velocity.x -= RUN_SPEED * GROUND_ACCELERATION_MULTIPLIER
		player_sprites.set_flip_h(true)
	elif Input.is_action_pressed("move_right"):
		velocity.x += RUN_SPEED * GROUND_ACCELERATION_MULTIPLIER
		player_sprites.set_flip_h(false)

	velocity.x = clamp(velocity.x, -RUN_SPEED, RUN_SPEED)

	state.set_linear_velocity(velocity)

func integrate_falling(state):
	player_sprites.play("jump")
	var velocity = state.get_linear_velocity()

	if velocity.x > -WALK_SPEED && Input.is_action_pressed("move_left"):
		player_sprites.set_flip_h(true)
		velocity.x -= WALK_SPEED * AIR_DIRECTION_MULTIPLIER
	elif velocity.x < WALK_SPEED && Input.is_action_pressed("move_right"):
		player_sprites.set_flip_h(false)
		velocity.x += WALK_SPEED * AIR_DIRECTION_MULTIPLIER
	elif !Input.is_action_pressed("move_left") && !Input.is_action_pressed("move_right"):
		velocity.x *= AIR_FRICTION_MULTIPLIER

	velocity.x = clamp(velocity.x, -RUN_SPEED, RUN_SPEED)
	state.set_linear_velocity(velocity)

func integrate_jumping(state):
	player_sprites.play("jump")
	state.set_linear_velocity(Vector2(state.get_linear_velocity().x,-JUMP_SPEED))

func integrate_climbing(state):
	player_sprites.play("jump")
	var velocity = state.get_linear_velocity()

	if Input.is_action_pressed("move_up"):
		velocity.y = -CLIMB_SPEED
	elif Input.is_action_pressed("move_down"):
		velocity.y = CLIMB_SPEED
	else:
		velocity.y = 0

	if Input.is_action_pressed("move_left"):
		velocity.x = - WALK_SPEED * GROUND_ACCELERATION_MULTIPLIER
		player_sprites.set_flip_h(true)
	elif Input.is_action_pressed("move_right"):
		velocity.x = WALK_SPEED * GROUND_ACCELERATION_MULTIPLIER
		player_sprites.set_flip_h(false)
	else:
		velocity.x = 0

	state.set_linear_velocity(velocity)

func _process(delta):
	var can_climb = false

	var overlapping_areas = interaction_area.get_overlapping_areas()

	for area in overlapping_areas:
		if can_climb:
			break
		can_climb = area extends climb_area

	if fsm == IDLE:
		if Input.is_action_pressed("jump"):
			fsm = JUMPING
			print("idle -> jump")
		elif Input.is_action_pressed("move_left") || Input.is_action_pressed("move_right"):
			fsm = WALKING
			print("idle -> walk")
		elif can_climb && ((Input.is_action_pressed("move_up") && !test_motion(Vector2(0,-5))) || (Input.is_action_pressed("move_down") && !test_motion(Vector2(0,5)))):
			#check if can climb and if thera are obstacles where we want to climb
			fsm = CLIMBING
			print("idle -> climb")
		elif !test_motion(Vector2(0,5)):
			fsm = FALLING
			print("idle -> fall")

	elif fsm == WALKING:
		if !Input.is_action_pressed("move_left") && !Input.is_action_pressed("move_right"):
			fsm = IDLE
			print("walk -> idle")
		elif Input.is_action_pressed("run"):
			fsm = RUNNING
			print("walk -> run")
		elif Input.is_action_pressed("jump"):
			fsm = JUMPING
			print("walk -> jump")
		elif can_climb && ((Input.is_action_pressed("move_up") && !test_motion(Vector2(0,-5))) || (Input.is_action_pressed("move_down") && !test_motion(Vector2(0,5)))):
			fsm = CLIMBING
			print("walk -> climb")
		elif !test_motion(Vector2(0,5)):
			fsm = FALLING
			print("walk -> fall")

	elif fsm == RUNNING:
		if !Input.is_action_pressed("run"):
			fsm = WALKING
			print("run -> walk")
		elif !Input.is_action_pressed("move_left") && !Input.is_action_pressed("move_right"):
			fsm = IDLE
			print("run -> idle")
		elif Input.is_action_pressed("jump"):
			fsm = JUMPING
			print("run -> jump")
		elif can_climb && ((Input.is_action_pressed("move_up") && !test_motion(Vector2(0,-5))) || (Input.is_action_pressed("move_down") && !test_motion(Vector2(0,5)))):
			#check if can climb and if thera are obstacles where we want to climb
			fsm = CLIMBING
			print("run -> climb")
		elif !test_motion(Vector2(0,5)):
			fsm = FALLING
			print("run -> fall")

	elif fsm == CLIMBING:
		if !can_climb:
			fsm = FALLING
			print("climb -> fall")
		elif Input.is_action_pressed("jump"):
			fsm = JUMPING
			print("climb -> jump")
		elif test_motion(Vector2(0,5)):
			fsm = IDLE
			print("climb -> idle")

	elif fsm == FALLING:
		if can_climb && ((Input.is_action_pressed("move_up") && !test_motion(Vector2(0,-5))) || (Input.is_action_pressed("move_down") && !test_motion(Vector2(0,5)))):
			#check if can climb and if thera are obstacles where we want to climb
			fsm = CLIMBING
			print("fall -> climb")
		elif test_motion(Vector2(0,5)):
			fsm = IDLE
			print("fall -> idle")

	elif fsm == JUMPING:
		if !Input.is_action_pressed("jump"):
			fsm = FALLING
			print("jump -> fall")


Better ship movement indication on hud

Better ship movement indication on hud

extends RigidBody2D

var ROTATE_STEP = 6
var ROTATE_THRESHOLD = 6.28

var ACCELERATION = 300
var BOOST = 1500
var BOOST_FUEL = 6

var GRAVITY_FACTOR = 100000000000
var MAX_FUEL = 100

var rotation = 0
var current_acceleration = Vector2(0, 0)
var current_gravity = Vector2(0, 0)

var engines = []

var fuel = 100

var do_rotate = false
var rotate_factor = 1
var accelerate = false
var accelerate_factor = 1
var boost = false

func _integrate_forces(s):
    var lv = s.get_linear_velocity()
    var step = s.get_step()
    self.current_acceleration = lv

    var engines_on = {"Main" : false, "Left": false, "Right" : false}

    if self.do_rotate:
        self.rotation = self.rotation + self.ROTATE_STEP * step * self.rotate_factor
        if self.rotation > self.ROTATE_THRESHOLD:
            self.rotation = self.rotation - self.ROTATE_THRESHOLD
        if self.rotation < 0:
            self.rotation = self.rotation + self.ROTATE_THRESHOLD
        if self.rotate_factor < 0:
            engines_on["Left"] = true
        elif self.rotate_factor > 0:
            engines_on["Right"] = true
    self.set_rot(self.rotation)

    var acceleration_vector = Vector2(0,-1).rotated(self.rotation)

    if self.accelerate and self.fuel > step * 10:
        lv = lv + acceleration_vector * self.ACCELERATION * step * self.accelerate_factor
        engines_on["Main"] = true
        self.fuel = self.fuel - step * 10
    elif self.boost and self.fuel > step * 10 * self.BOOST_FUEL:
        lv = lv + acceleration_vector * self.BOOST * step
        engines_on["Main"] = true
        engines_on["Left"] = true
        engines_on["Right"] = true
        self.fuel = self.fuel - step * 10 * self.BOOST_FUEL

    self.current_gravity = s.get_total_gravity() * step * self.GRAVITY_FACTOR
    lv += self.current_gravity
    self.__engines_start(engines_on)
    s.set_linear_velocity(lv)

func __engines_start(engines):
    for engine in engines:
        if engines[engine] == true:
            self.engines[engine].set_emitting(true)

func _ready():
    self.set_mode(self.MODE_CHARACTER)
    var engines = self.get_node("Engines")
    self.engines = {
        "Main" : engines.get_node("Main"),
        "Left" : engines.get_node("Left"),
        "Right" : engines.get_node("Right"),
    }

extends RigidBody2D

var ROTATE_STEP = 6
var ROTATE_THRESHOLD = 6.28

var ACCELERATION = 300
var BOOST = 1500
var BOOST_FUEL = 6

var GRAVITY_FACTOR = 100000000000
var MAX_FUEL = 100

var rotation = 0
var current_acceleration = Vector2(0, 0)
var current_gravity = Vector2(0, 0)

var engines = []

var fuel = 100

var do_rotate = false
var rotate_factor = 1
var accelerate = false
var accelerate_factor = 1
var boost = false

func _integrate_forces(s):
    var lv = s.get_linear_velocity()
    var step = s.get_step()

    var engines_on = {"Main" : false, "Left": false, "Right" : false}

    if self.do_rotate:
        self.rotation = self.rotation + self.ROTATE_STEP * step * self.rotate_factor
        if self.rotation > self.ROTATE_THRESHOLD:
            self.rotation = self.rotation - self.ROTATE_THRESHOLD
        if self.rotation < 0:
            self.rotation = self.rotation + self.ROTATE_THRESHOLD
        if self.rotate_factor < 0:
            engines_on["Left"] = true
        elif self.rotate_factor > 0:
            engines_on["Right"] = true
    self.set_rot(self.rotation)

    var acceleration_vector = Vector2(0,-1).rotated(self.rotation)

    if self.accelerate and self.fuel > step * 10:
        lv = lv + acceleration_vector * self.ACCELERATION * step * self.accelerate_factor
        engines_on["Main"] = true
        self.fuel = self.fuel - step * 10
    elif self.boost and self.fuel > step * 10 * self.BOOST_FUEL:
        lv = lv + acceleration_vector * self.BOOST * step
        engines_on["Main"] = true
        engines_on["Left"] = true
        engines_on["Right"] = true
        self.fuel = self.fuel - step * 10 * self.BOOST_FUEL
    #elif self.fuel < 10:
    #    self.fuel = self.fuel + step * 20
    #    if self.fuel > 10:
    #        self.fuel = 10

    self.current_gravity = s.get_total_gravity() * step * self.GRAVITY_FACTOR
    lv += self.current_gravity
    self.current_acceleration = lv
    self.__engines_start(engines_on)
    s.set_linear_velocity(lv)

func __engines_start(engines):
    for engine in engines:
        if engines[engine] == true:
            self.engines[engine].set_emitting(true)

func _ready():
    self.set_mode(self.MODE_CHARACTER)
    var engines = self.get_node("Engines")
    self.engines = {
        "Main" : engines.get_node("Main"),
        "Left" : engines.get_node("Left"),
        "Right" : engines.get_node("Right"),
    }


fixed node names

fixed node names

# 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()

				gotoPuzzle()

				remotePuzzle = root.get_node("Puzzle")
				#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

				gotoPuzzle()

				remotePuzzle = root.get_node("Puzzle")
				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)

	gotoMenu()

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 puzzle = root.get_node( "Puzzle" )
		var pos = dataArray[1]
		puzzle.otherPuzzle.get_node("GridView/GridMan").forceClickBlock(pos)

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(pos):
	if !isNetwork:
		print("Error sending start packet: not connected!")
		return
	connection.put_var([REMOTE_BLOCK_UPDATE, pos])

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 gotoMenu():
	root.get_child( root.get_child_count() - 1 ).queue_free()
	root.add_child( load("res://menus.scn") )

func gotoPuzzle():
	var guiMan = preload("GUIManager.gd").new()
	guiMan.gotoPuzzleScene(root, true) # network = true

# 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 puzzle = root.get_node( "Spatial" )
		var pos = dataArray[1]
		puzzle.otherPuzzle.get_node("GridView/GridMan").forceClickBlock(pos)

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(pos):
	if !isNetwork:
		print("Error sending start packet: not connected!")
		return
	connection.put_var([REMOTE_BLOCK_UPDATE, pos])

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() )


Removed print statement from Abstract Block

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=null):
	var network = Globals.get("Network")
	if (not network == null and get_node("../../../../Puzzle").mainPuzzle and network.isNetwork):
			network.sendBlockUpdate(blockPos)

	if click_normal != null and editor != null:
		if editor.shouldAddNeighbor():
			addNeighbor(editor, click_normal)
			return
		if editor.shouldReplaceSelf() or editor.shouldRemoveSelf():
			# lasers cannot be removed by the editor
			if blockType == get_parent().get_parent().get_parent().puzzleMan.BLOCK_LASER:
				return

			# maybe replace self
			if editor.shouldReplaceSelf():
				addNeighbor(editor, 0 * click_normal)
			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 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 request_remove(node=null, key=null):
	var n = scaleTweenNode(0.001, 0.25, Tween.TRANS_QUART)
	n.start()
	n.connect("tween_complete", 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=null):
	var network = Globals.get("Network")
	if (not network == null and get_node("../../../../Puzzle").mainPuzzle and network.isNetwork):
			network.sendBlockUpdate(blockPos)
			print("sending click!")

	if click_normal != null and editor != null:
		if editor.shouldAddNeighbor():
			addNeighbor(editor, click_normal)
			return
		if editor.shouldReplaceSelf() or editor.shouldRemoveSelf():
			# lasers cannot be removed by the editor
			if blockType == get_parent().get_parent().get_parent().puzzleMan.BLOCK_LASER:
				return

			# maybe replace self
			if editor.shouldReplaceSelf():
				addNeighbor(editor, 0 * click_normal)
			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 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 request_remove(node=null, key=null):
	var n = scaleTweenNode(0.001, 0.25, Tween.TRANS_QUART)
	n.start()
	n.connect("tween_complete", 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)


added failing test. commented out setup/teardown functions

extends "res://scripts/gut.gd".Test

var puzzleManScript = load("res://scripts/PuzzleManager.gd")
var abstractScript = load("res://scripts/Blocks/AbstractBlock.gd")

# func setup():
# 	gut.p("ran setup", 2)
#
# func teardown():
# 	gut.p("ran teardown")

func test_unpickled_paired_block():
	var pMan = puzzleManScript.new()
	var pickle = pMan.PickledBlock.new()
	var aB = abstractScript
	pickle.setName(1) \
		.setBlockClass(pMan.BLOCK_PAIR) \
		.setPairName("TestPickledBlock_BUDDY") \
		.setTextureName("Blue")
	var node = pickle.toNode()
	gut.assert_eq(node.name, aB.nameToNodeName(pickle.name), "toName(Pickled name) = node name")
	gut.assert_eq(node.pairName,  aB.nameToNodeName(pickle.pairName), "toName(Pickled pair name) = node pair name")
	gut.assert_eq(node.textureName, pickle.textureName, "Pickled texture = node texture")

	# test AbstractBlock.pairActivate
	gut.assert_eq(node.selected, false, "Abstract.activate starts as false")
	var pairNode = node.pairActivate(null, null, null)
	gut.assert_eq(pairNode, null, "pairActivate returns null for invalid pair")
	gut.assert_eq(node.selected, true, "Abstract.activate is true after activation")

func test_unpickled_laser_block():
	var pMan = puzzleManScript.new()
	var pickle = pMan.PickledBlock.new()
	var aB = abstractScript
	pickle.setName(0) \
		.setBlockClass(pMan.BLOCK_LASER) \
		.setPairName("TestPickledBlock_BUDDY") \
		.setLaserExtent(Vector3(1,1,1)) \
		.setTextureName("Blue")
	var node = pickle.toNode()
	gut.assert_eq(node.name, aB.nameToNodeName(pickle.name), "toName(Pickled name) = node name")
	gut.assert_eq(node.pairName,  aB.nameToNodeName(pickle.pairName), "toName(Pickled pair name) = node pair name")
	gut.assert_eq(node.laserExtent, pickle.laserExtent, "Pickled laser extent = node laser extent")

func test_puzzleMan_generatePuzzle():
	var pMan = puzzleManScript.new()
	for layers in [0,1,2,5]:
		for diff in [pMan.DIFF_HARD, pMan.DIFF_MEDIUM, pMan.DIFF_EASY]:
			var puzzle = pMan.generatePuzzle(layers, diff)
			gut.assert_eq(puzzle.puzzleLayers, layers, "puzzleMan layers = layers. difficulty=" + str(diff))
			gut.assert_eq(float(puzzle.blocks.size()), pow(layers * 2 + 1,3) - 1, "puzzleMan blocks.size = (layers*2 + 1)**3 - 1. difficulty=" + str(diff))
			gut.assert_eq(puzzle.solvePuzzle(), true, "Puzzle solveable")

func test_network():
	# test start
	# test transmit blocks
	# test end
	gut.assert_eq(false, true, "should fail")
	pass

extends "res://scripts/gut.gd".Test

var puzzleManScript = load("res://scripts/PuzzleManager.gd")
var abstractScript = load("res://scripts/Blocks/AbstractBlock.gd")

func setup():
	gut.p("ran setup", 2)

func teardown():
	gut.p("ran teardown")

func test_unpickled_paired_block():
	var pMan = puzzleManScript.new()
	var pickle = pMan.PickledBlock.new()
	var aB = abstractScript
	pickle.setName(1) \
		.setBlockClass(pMan.BLOCK_PAIR) \
		.setPairName("TestPickledBlock_BUDDY") \
		.setTextureName("Blue")
	var node = pickle.toNode()
	gut.assert_eq(node.name, aB.nameToNodeName(pickle.name), "toName(Pickled name) = node name")
	gut.assert_eq(node.pairName,  aB.nameToNodeName(pickle.pairName), "toName(Pickled pair name) = node pair name")
	gut.assert_eq(node.textureName, pickle.textureName, "Pickled texture = node texture")

	# test AbstractBlock.pairActivate
	gut.assert_eq(node.selected, false, "Abstract.activate starts as false")
	var pairNode = node.pairActivate(null, null, null)
	gut.assert_eq(pairNode, null, "pairActivate returns null for invalid pair")
	gut.assert_eq(node.selected, true, "Abstract.activate is true after activation")

func test_unpickled_laser_block():
	var pMan = puzzleManScript.new()
	var pickle = pMan.PickledBlock.new()
	var aB = abstractScript
	pickle.setName(0) \
		.setBlockClass(pMan.BLOCK_LASER) \
		.setPairName("TestPickledBlock_BUDDY") \
		.setLaserExtent(Vector3(1,1,1)) \
		.setTextureName("Blue")
	var node = pickle.toNode()
	gut.assert_eq(node.name, aB.nameToNodeName(pickle.name), "toName(Pickled name) = node name")
	gut.assert_eq(node.pairName,  aB.nameToNodeName(pickle.pairName), "toName(Pickled pair name) = node pair name")
	gut.assert_eq(node.laserExtent, pickle.laserExtent, "Pickled laser extent = node laser extent")

func test_puzzleMan_generatePuzzle():
	var pMan = puzzleManScript.new()
	for layers in [0,1,2,5]:
		for diff in [pMan.DIFF_HARD, pMan.DIFF_MEDIUM, pMan.DIFF_EASY]:
			var puzzle = pMan.generatePuzzle(layers, diff)
			gut.assert_eq(puzzle.puzzleLayers, layers, "puzzleMan layers = layers. difficulty=" + str(diff))
			gut.assert_eq(float(puzzle.blocks.size()), pow(layers * 2 + 1,3) - 1, "puzzleMan blocks.size = (layers*2 + 1)**3 - 1. difficulty=" + str(diff))
			gut.assert_eq(puzzle.solvePuzzle(), true, "Puzzle solveable")

func test_network():
	# test start
	# test transmit blocks
	# test end
	gut.assert_eq(true, true, "TRUE")
	pass


Avoid duplicate path of the samething.

Avoid duplicate path of the samething.

extends HBoxContainer

const mask = preload("res://mask/mask.gd")
const types = mask.types
const type_textures = mask.type_textures

var current_mask = ""
var previous_mask = ""

onready var tree = get_tree()
onready var player = tree.get_nodes_in_group("player")[0]
onready var _sprite = get_node("Sprite")

func _ready():
	set_process(true)

func _process(delta):
	if not player == null:
		current_mask = player.get_current_mask()
		if current_mask != previous_mask:
			previous_mask = current_mask
			if type_textures.has(current_mask):
				_sprite.set_texture(type_textures[ current_mask ])

extends HBoxContainer

const types = preload("res://mask/mask.gd").types
const type_textures = preload("res://mask/mask.gd").type_textures

var current_mask = ""
var previous_mask = ""

onready var tree = get_tree()
onready var player = tree.get_nodes_in_group("player")[0]
onready var _sprite = get_node("Sprite")

func _ready():
	set_process(true)

func _process(delta):
	if not player == null:
		current_mask = player.get_current_mask()
		if current_mask != previous_mask:
			previous_mask = current_mask
			if type_textures.has(current_mask):
				_sprite.set_texture(type_textures[ current_mask ])


Donate Opens webpage instead.

Donate Opens webpage instead.

extends Node

#This is used by global.gd for changing scenes.
var currentScene = null
var currentDifficulty = 1
var currentName = "RIP"
var currentScore = "0"
var currentTime = "12:00"
var errorHighArray = false
var enteringOS = false
var enteringMenu = false
var enteringRound = false
var enteringDonate = false

#Highscores that are saved to disk.
#  highArray[difficulty][rank][stat] = value
#  difficulty: range(SIZE_DIFFICULTY)
#  rank:       range(SIZE_RANK)
#  stat:       STAT_NAME, STAT_SCORE, STAT_TIME, STAT_NEW
const SIZE_DIFFICULTY = 4
const SIZE_RANK = 10
const SIZE_STAT = 4

const STAT_NAME = 0
const STAT_SCORE = 1
const STAT_TIME = 2
const STAT_NEW = 3
var highArray = []

func _ready():
	get_scene().set_auto_accept_quit(false) #Enables: _notification(what) to recieve MainLoop.NOTIFICATION_WM_QUIT_REQUEST
	randomize()
	var root = get_scene().get_root()
	currentScene = root.get_child(root.get_child_count() - 1)
	loadHighScore()

func _notification(what):
	if (what==MainLoop.NOTIFICATION_WM_QUIT_REQUEST):
		get_scene().quit() #default behavior

func loadHighScore():
	#this function is to load the high scores for viewing.
	var f = File.new()
	if f.file_exists("user://highScores.zombie"):
		var err = f.open_encrypted_with_pass("user://highScores.zombie", File.READ, OS.get_unique_ID())
		if err:
			print("LOAD ERROR: " + str(err))
			errorHighArray = true
		else:
			highArray = f.get_var()
	else:
		print("LOAD ERROR: highScores.zombie does not exist.")
		errorHighArray = true
	if (typeof(highArray) != TYPE_ARRAY):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID01")
		errorHighArray = true
	if (highArray.size() + 1 == SIZE_DIFFICULTY):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID02")
		errorHighArray = true
	if (typeof(highArray[0]) != TYPE_ARRAY):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID03")
		errorHighArray = true
	if (highArray[0].size() + 1 == SIZE_RANK):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID04")
		errorHighArray = true
	if (typeof(highArray[0][0]) != TYPE_ARRAY):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID05")
		errorHighArray = true
	if (highArray[0][0].size() + 1 == SIZE_STAT):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID06")
		errorHighArray = true
	if (typeof(highArray[0][0][STAT_NAME]) != TYPE_STRING):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID07")
		errorHighArray = true
	if (typeof(highArray[0][0][STAT_SCORE]) != TYPE_STRING):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID08")
		errorHighArray = true
	if (typeof(highArray[0][0][STAT_TIME]) != TYPE_STRING):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID09")
		errorHighArray = true
	if (typeof(highArray[0][0][STAT_NEW]) != TYPE_BOOL):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID10")
		errorHighArray = true
	if errorHighArray:
		clearHighScore()
	for difficulty in range(SIZE_DIFFICULTY):
		for rank in range(SIZE_RANK):
			highArray[difficulty][rank][STAT_NEW] = false
	f.close()

func clearHighScore():
	#
	# TODO: Add prompt to delete all highscores and create empty list!
	#
	#Highscores that are saved to disk.
	#  highArray[difficulty][rank][stat] = value
	#  difficulty: range(SIZE_DIFFICULTY)
	#  rank:       range(SIZE_RANK)
	#  stat:       STAT_NAME, STAT_SCORE, STAT_TIME, STAT_NEW
	highArray = []
	for difficulty in range(SIZE_DIFFICULTY):
		highArray.append([])
		for rank in range(SIZE_RANK):
			highArray[difficulty].append([])
			highArray[difficulty][rank].append("RIP")
			highArray[difficulty][rank].append("0")
			highArray[difficulty][rank].append("12:00")
			highArray[difficulty][rank].append(false)
	#highDifficulty.sort()
	errorHighArray = false
	saveHighScore()

func saveHighScore():
	#This function is to save the high scores.
	if !errorHighArray:
		var f = File.new()
		var err = f.open_encrypted_with_pass("user://highScores.zombie", File.WRITE, OS.get_unique_ID())
		if err:
			print("SAVE ERROR: " + str(err))
			#
			# TODO: Add prompt to try and save again!
			#
		else:
			f.seek(0)
			f.store_var(highArray)
			print("SAVE DONE")
		f.close()

func updateHighScore(score, time):
	for difficultyTemp in range(SIZE_DIFFICULTY):
		for rankTemp in range(SIZE_RANK):
			highArray[difficultyTemp][rankTemp][STAT_NEW] = false

	if score <= 0:
		enterHighScore()
	elif time <= 0:
		enterHighScore()
	else:
		currentName = "RIP"
		currentScore = str(floor(score))
		currentTime = timeString(time)
		var i = highArray[currentDifficulty]
		i[SIZE_RANK - 1][STAT_NAME] = currentName
		i[SIZE_RANK - 1][STAT_SCORE] = currentScore
		i[SIZE_RANK - 1][STAT_TIME] = currentTime
		i[SIZE_RANK - 1][STAT_NEW] = true

		#sorting highArray:
		i.sort_custom(get_node("/root/global"), "sortLogic")

		#Only if the new score was sorted into the top 9!:
		if i[SIZE_RANK - 1][STAT_NEW] == false:
			enterGetName()
		else:
			enterHighScore()

func updateHighScorePart2():
	#The last index of highArray is never displayed and is overridden with any new score before sorting.

	#Highscores that are saved to disk.
	#  highArray[difficulty][rank][stat] = value
	#  difficulty: range(SIZE_DIFFICULTY)
	#  rank:       range(SIZE_RANK)
	#  stat:       STAT_NAME, STAT_SCORE, STAT_TIME, STAT_NEW

	#highArray[currentDifficulty][SIZE_RANK - 1][STAT_NAME] = currentName WILL THROW AN ERROR MESSAGE OF "INVALID GET INDREX '0' (on base: 'int')" INCORRECTLY USING "var i = highArray[currentDifficulty]" as a workaround.
	print("updateHighScorePart2 " + currentTime + " " + currentName + " " + currentScore)
	for rankTemp in range(SIZE_RANK):
		if highArray[currentDifficulty][rankTemp][STAT_NEW] == true:
			highArray[currentDifficulty][rankTemp][STAT_NAME] = currentName
	saveHighScore()
	enterHighScore()

func sortLogic(first, second):
	if second[STAT_SCORE] < first[STAT_SCORE]:
		return true
	else:
		return false

func timeString(time):
	time = floor(time)
	if time < 10:
		return "12:0" + str(time)
	elif time < 60:
		return "12:" + str(time)
	elif time % 60 < 10:
		return str(floor(time / 60)) + ":0" + str(time % 60)
	else:
		return str(floor(time / 60)) + ":" + str(time % 60)

func enterOS():
	get_scene().quit()

func enterGetName():
	#Make sure you call get_node("/root/global").currentName = "INITAlS" AND get_node("/root/global").updateHighScorePart2()
	if !errorHighArray:
		var s = ResourceLoader.load("res://scene/getName.xscn")
		currentScene.queue_free()
		currentScene = s.instance()
		get_scene().get_root().add_child(currentScene)

func enterHighScore():
	#Only display the top 9 of each difficulty.
	if !errorHighArray:
		var s = ResourceLoader.load("res://scene/highScore.xscn")
		currentScene.queue_free()
		currentScene = s.instance()
		get_scene().get_root().add_child(currentScene)

func enterDonate():
	OS.shell_open("http://www.google.com")
	#var s = ResourceLoader.load("res://scene/donate.xscn")
	#currentScene.queue_free()
	#currentScene = s.instance()
	#get_scene().get_root().add_child(currentScene)
	enteringDonate = false

func enterRound(difficulty):
	currentDifficulty = difficulty #currentDifficulty should be accessed by zombiesGo.gd

	var s = ResourceLoader.load("res://scene/zombiesGo.xscn")
	currentScene.queue_free()
	currentScene = s.instance()
	get_scene().get_root().add_child(currentScene)
	enteringRound = false

func enterMenu():
	var s = ResourceLoader.load("res://scene/menu.xscn")
	currentScene.queue_free()
	currentScene = s.instance()
	get_scene().get_root().add_child(currentScene)
	enteringMenu = false

extends Node

#This is used by global.gd for changing scenes.
var currentScene = null
var currentDifficulty = 1
var currentName = "RIP"
var currentScore = "0"
var currentTime = "12:00"
var errorHighArray = false
var enteringOS = false
var enteringMenu = false
var enteringRound = false
var enteringDonate = false

#Highscores that are saved to disk.
#  highArray[difficulty][rank][stat] = value
#  difficulty: range(SIZE_DIFFICULTY)
#  rank:       range(SIZE_RANK)
#  stat:       STAT_NAME, STAT_SCORE, STAT_TIME, STAT_NEW
const SIZE_DIFFICULTY = 4
const SIZE_RANK = 10
const SIZE_STAT = 4

const STAT_NAME = 0
const STAT_SCORE = 1
const STAT_TIME = 2
const STAT_NEW = 3
var highArray = []

func _ready():
	get_scene().set_auto_accept_quit(false) #Enables: _notification(what) to recieve MainLoop.NOTIFICATION_WM_QUIT_REQUEST
	randomize()
	var root = get_scene().get_root()
	currentScene = root.get_child(root.get_child_count() - 1)
	loadHighScore()

func _notification(what):
	if (what==MainLoop.NOTIFICATION_WM_QUIT_REQUEST):
		get_scene().quit() #default behavior

func loadHighScore():
	#this function is to load the high scores for viewing.
	var f = File.new()
	if f.file_exists("user://highScores.zombie"):
		var err = f.open_encrypted_with_pass("user://highScores.zombie", File.READ, OS.get_unique_ID())
		if err:
			print("LOAD ERROR: " + str(err))
			errorHighArray = true
		else:
			highArray = f.get_var()
	else:
		print("LOAD ERROR: highScores.zombie does not exist.")
		errorHighArray = true
	if (typeof(highArray) != TYPE_ARRAY):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID01")
		errorHighArray = true
	if (highArray.size() + 1 == SIZE_DIFFICULTY):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID02")
		errorHighArray = true
	if (typeof(highArray[0]) != TYPE_ARRAY):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID03")
		errorHighArray = true
	if (highArray[0].size() + 1 == SIZE_RANK):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID04")
		errorHighArray = true
	if (typeof(highArray[0][0]) != TYPE_ARRAY):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID05")
		errorHighArray = true
	if (highArray[0][0].size() + 1 == SIZE_STAT):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID06")
		errorHighArray = true
	if (typeof(highArray[0][0][STAT_NAME]) != TYPE_STRING):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID07")
		errorHighArray = true
	if (typeof(highArray[0][0][STAT_SCORE]) != TYPE_STRING):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID08")
		errorHighArray = true
	if (typeof(highArray[0][0][STAT_TIME]) != TYPE_STRING):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID09")
		errorHighArray = true
	if (typeof(highArray[0][0][STAT_NEW]) != TYPE_BOOL):
		print("LOAD ERROR: highScores.zombie is using an outdated or incorrect format. ID10")
		errorHighArray = true
	if errorHighArray:
		clearHighScore()
	for difficulty in range(SIZE_DIFFICULTY):
		for rank in range(SIZE_RANK):
			highArray[difficulty][rank][STAT_NEW] = false
	f.close()

func clearHighScore():
	#
	# TODO: Add prompt to delete all highscores and create empty list!
	#
	#Highscores that are saved to disk.
	#  highArray[difficulty][rank][stat] = value
	#  difficulty: range(SIZE_DIFFICULTY)
	#  rank:       range(SIZE_RANK)
	#  stat:       STAT_NAME, STAT_SCORE, STAT_TIME, STAT_NEW
	highArray = []
	for difficulty in range(SIZE_DIFFICULTY):
		highArray.append([])
		for rank in range(SIZE_RANK):
			highArray[difficulty].append([])
			highArray[difficulty][rank].append("RIP")
			highArray[difficulty][rank].append("0")
			highArray[difficulty][rank].append("12:00")
			highArray[difficulty][rank].append(false)
	#highDifficulty.sort()
	errorHighArray = false
	saveHighScore()

func saveHighScore():
	#This function is to save the high scores.
	if !errorHighArray:
		var f = File.new()
		var err = f.open_encrypted_with_pass("user://highScores.zombie", File.WRITE, OS.get_unique_ID())
		if err:
			print("SAVE ERROR: " + str(err))
			#
			# TODO: Add prompt to try and save again!
			#
		else:
			f.seek(0)
			f.store_var(highArray)
			print("SAVE DONE")
		f.close()

func updateHighScore(score, time):
	for difficultyTemp in range(SIZE_DIFFICULTY):
		for rankTemp in range(SIZE_RANK):
			highArray[difficultyTemp][rankTemp][STAT_NEW] = false

	if score <= 0:
		enterHighScore()
	elif time <= 0:
		enterHighScore()
	else:
		currentName = "RIP"
		currentScore = str(floor(score))
		currentTime = timeString(time)
		var i = highArray[currentDifficulty]
		i[SIZE_RANK - 1][STAT_NAME] = currentName
		i[SIZE_RANK - 1][STAT_SCORE] = currentScore
		i[SIZE_RANK - 1][STAT_TIME] = currentTime
		i[SIZE_RANK - 1][STAT_NEW] = true

		#sorting highArray:
		i.sort_custom(get_node("/root/global"), "sortLogic")

		#Only if the new score was sorted into the top 9!:
		if i[SIZE_RANK - 1][STAT_NEW] == false:
			enterGetName()
		else:
			enterHighScore()

func updateHighScorePart2():
	#The last index of highArray is never displayed and is overridden with any new score before sorting.

	#Highscores that are saved to disk.
	#  highArray[difficulty][rank][stat] = value
	#  difficulty: range(SIZE_DIFFICULTY)
	#  rank:       range(SIZE_RANK)
	#  stat:       STAT_NAME, STAT_SCORE, STAT_TIME, STAT_NEW

	#highArray[currentDifficulty][SIZE_RANK - 1][STAT_NAME] = currentName WILL THROW AN ERROR MESSAGE OF "INVALID GET INDREX '0' (on base: 'int')" INCORRECTLY USING "var i = highArray[currentDifficulty]" as a workaround.
	print("updateHighScorePart2 " + currentTime + " " + currentName + " " + currentScore)
	for rankTemp in range(SIZE_RANK):
		if highArray[currentDifficulty][rankTemp][STAT_NEW] == true:
			highArray[currentDifficulty][rankTemp][STAT_NAME] = currentName
	saveHighScore()
	enterHighScore()

func sortLogic(first, second):
	if second[STAT_SCORE] < first[STAT_SCORE]:
		return true
	else:
		return false

func timeString(time):
	time = floor(time)
	if time < 10:
		return "12:0" + str(time)
	elif time < 60:
		return "12:" + str(time)
	elif time % 60 < 10:
		return str(floor(time / 60)) + ":0" + str(time % 60)
	else:
		return str(floor(time / 60)) + ":" + str(time % 60)

func enterOS():
	get_scene().quit()

func enterGetName():
	#Make sure you call get_node("/root/global").currentName = "INITAlS" AND get_node("/root/global").updateHighScorePart2()
	if !errorHighArray:
		var s = ResourceLoader.load("res://scene/getName.xscn")
		currentScene.queue_free()
		currentScene = s.instance()
		get_scene().get_root().add_child(currentScene)

func enterHighScore():
	#Only display the top 9 of each difficulty.
	if !errorHighArray:
		var s = ResourceLoader.load("res://scene/highScore.xscn")
		currentScene.queue_free()
		currentScene = s.instance()
		get_scene().get_root().add_child(currentScene)

func enterDonate():
	var s = ResourceLoader.load("res://scene/donate.xscn")
	currentScene.queue_free()
	currentScene = s.instance()
	get_scene().get_root().add_child(currentScene)
	enteringDonate = false

func enterRound(difficulty):
	currentDifficulty = difficulty #currentDifficulty should be accessed by zombiesGo.gd

	var s = ResourceLoader.load("res://scene/zombiesGo.xscn")
	currentScene.queue_free()
	currentScene = s.instance()
	get_scene().get_root().add_child(currentScene)
	enteringRound = false

func enterMenu():
	var s = ResourceLoader.load("res://scene/menu.xscn")
	currentScene.queue_free()
	currentScene = s.instance()
	get_scene().get_root().add_child(currentScene)
	enteringMenu = false


Convert radian to degree with exits method in Godot Engine

extends Node2D

const MAX_PARTICLE_POOLING = 6
const MASK_TYPES = preload("res://mask/mask.gd").types
const PARTICLES = {
	"use_mask" : preload("res://particles/use_mask_particle.tscn"),
	"hit_totem" : preload("res://particles/hit_totem_particle.tscn")
}

var pooling_particles = {
	"use_mask" : [],
	"hit_totem" : []
}

onready var tree = get_tree()
onready var root = tree.get_root()
onready var players = tree.get_nodes_in_group("player")

func _ready():
	_initialize()
	set_process(true)

func _process(delta):
	if not players.empty():
		if players[ 0 ].get_is_using_mask():
			if players[ 0 ].get_current_using_mask() == MASK_TYPES[ 0 ]:
				var params = {
					Particles2D.PARAM_TANGENTIAL_ACCEL : players[ 0 ].get_move_direction().x * 30
				}
				spawn("use_mask", players[ 0 ].get_global_pos(), params)
		if players[ 0 ].is_used_dig_mask:
			var params = {
				Particles2D.PARAM_TANGENTIAL_ACCEL : players[ 0 ].get_move_direction().x * 30
			}
			spawn("use_mask", players[ 0 ].get_using_mask_pos(), params)
			players[ 0 ].is_used_dig_mask = false
		if players[ 0 ].get_is_hit_totem():
			var pos = players[ 0 ].get_player_to_hit_totem_pos().normalized()
			var angle_radian = atan2(pos.x, pos.y)
			var angle_degree = rad2deg(angle_radian)
			var params = {
				Particles2D.PARAM_DIRECTION : angle_degree
			}
			spawn("hit_totem", players[ 0 ].get_hit_totem_pos(), params)
			players[ 0 ].set_is_hit_totem(false)

func _initialize():
	var use_mask_instance = PARTICLES[ "use_mask" ].instance()
	var hit_totem_instance = PARTICLES[ "hit_totem" ].instance()
	for index in range(MAX_PARTICLE_POOLING):
		pooling_particles[ "use_mask" ].append(use_mask_instance.duplicate())
		pooling_particles[ "hit_totem" ].append(hit_totem_instance.duplicate())
		add_child(pooling_particles[ "use_mask" ][ index ])
		add_child(pooling_particles[ "hit_totem" ][ index ])

func spawn(particle_name, emit_pos, params):
	if not players.empty():
		if pooling_particles.keys().has(particle_name):
			for particle in pooling_particles[ particle_name ]:
				if not particle.is_emitting():
					for index in range(params.keys().size()):
						particle.set_param(params.keys()[ index ], params.values()[ index ])
					particle.set_global_pos(emit_pos)
					particle.set_emitting(true)
					break

extends Node2D

const MAX_PARTICLE_POOLING = 6
const MASK_TYPES = preload("res://mask/mask.gd").types
const PARTICLES = {
	"use_mask" : preload("res://particles/use_mask_particle.tscn"),
	"hit_totem" : preload("res://particles/hit_totem_particle.tscn")
}

var pooling_particles = {
	"use_mask" : [],
	"hit_totem" : []
}

onready var tree = get_tree()
onready var root = tree.get_root()
onready var players = tree.get_nodes_in_group("player")

func _ready():
	_initialize()
	set_process(true)

func _process(delta):
	if not players.empty():
		if players[ 0 ].get_is_using_mask():
			if players[ 0 ].get_current_using_mask() == MASK_TYPES[ 0 ]:
				var params = {
					Particles2D.PARAM_TANGENTIAL_ACCEL : players[ 0 ].get_move_direction().x * 30
				}
				spawn("use_mask", players[ 0 ].get_global_pos(), params)
		if players[ 0 ].is_used_dig_mask:
			var params = {
				Particles2D.PARAM_TANGENTIAL_ACCEL : players[ 0 ].get_move_direction().x * 30
			}
			spawn("use_mask", players[ 0 ].get_using_mask_pos(), params)
			players[ 0 ].is_used_dig_mask = false
		if players[ 0 ].get_is_hit_totem():
			var pos = players[ 0 ].get_player_to_hit_totem_pos().normalized()
			var angle_radian = atan2(pos.x, pos.y)
			var angle_degree = (angle_radian * 180) / PI
			var params = {
				Particles2D.PARAM_DIRECTION : angle_degree
			}
			spawn("hit_totem", players[ 0 ].get_hit_totem_pos(), params)
			players[ 0 ].set_is_hit_totem(false)

func _initialize():
	var use_mask_instance = PARTICLES[ "use_mask" ].instance()
	var hit_totem_instance = PARTICLES[ "hit_totem" ].instance()
	for index in range(MAX_PARTICLE_POOLING):
		pooling_particles[ "use_mask" ].append(use_mask_instance.duplicate())
		pooling_particles[ "hit_totem" ].append(hit_totem_instance.duplicate())
		add_child(pooling_particles[ "use_mask" ][ index ])
		add_child(pooling_particles[ "hit_totem" ][ index ])

func spawn(particle_name, emit_pos, params):
	if not players.empty():
		if pooling_particles.keys().has(particle_name):
			for particle in pooling_particles[ particle_name ]:
				if not particle.is_emitting():
					for index in range(params.keys().size()):
						particle.set_param(params.keys()[ index ], params.values()[ index ])
					particle.set_global_pos(emit_pos)
					particle.set_emitting(true)
					break


Enemy update

Enemy update

Enemy destroys themself if player enters them then defeats them.

extends KinematicBody2D

func _ready():
	set_fixed_process(true)
	pass

func _fixed_process(delta):
	if(get_node("/root/global").currentRunEnemy == true):
		self.queue_free()
	pass

func _on_playerCheck_body_enter( body ):
	if(body.is_in_group("playerRun")):
		get_node("/root/global").playerStartPosition = self.get_global_pos()
		get_node("/root/global").currentRunEnemy = true
		get_node("/root/global").setScene("res://Scenes/Dungeon_Fight.tscn")
	pass

extends KinematicBody2D

func _ready():
	set_fixed_process(true)
	pass

func _fixed_process(delta):
	pass

func _on_playerCheck_body_enter( body ):
	if(body.is_in_group("playerRun")):
		get_node("/root/global").playerStartPosition = self.get_global_pos()
#		get_node("/root/global").currentRunEnemy = self
		get_node("/root/global").setScene("res://Scenes/Dungeon_Fight.tscn")
	pass


Update Puzzle.gd

Update Puzzle.gd

# Provides functionality for the puzzle itself

extends Spatial

var DataMan = preload( "res://scripts/DataManager.gd" ).new()
var PuzzleManScript = preload( "res://scripts/PuzzleManager.gd" )
var PuzzleScn = preload("res://puzzle.scn")
var puzzleMan
var seed
var otherPuzzle
var generateRandom = true
var mainPuzzle = false

var time = {
		on = true,
		val = 0.0,
		label = Label.new(),
		tween = Tween.new() }

func addTimer():
	add_child(time.label)
	add_child(time.tween)
	time.label.set_pos(Vector2(15,15))

	time.label.set_theme(load("res://themes/MainTheme.thm"))
	time.tween.interpolate_method(time.label, "set_pos", \
			time.label.get_pos(), time.label.get_pos()*2, 0.5, \
			Tween.TRANS_BOUNCE, Tween.EASE_OUT)
	time.tween.start()
	time.label.set_text(str(time.val))

static func formatTime(t):
	var mins = floor(t / 60)
	var secs = fmod(floor(t), 60)
	var millis = floor((t - floor(t)) * 1000)
	return str(mins) + ":" + str(secs).pad_zeros(2) + ":" + str(millis).pad_zeros(3)

func _process(dTime):
	# start label tween on
	time.val += dTime
	time.label.set_text(formatTime(time.val))
	if fmod(time.val, 10) < 0.1:
		time.tween.seek(0.0)

# Called for initialization
func _ready():
	if time.on:
		set_process(true) # needed for time keeping
	addTimer()

	#set up network stuffs
	add_child(load("res://networkProxy.scn").instance())
	var Network = Globals.get("Network")
	if Network != null:
		Network.proxy.set_process(Network.isClient or Network.isHost)

	# generate puzzle
	puzzleMan = PuzzleManScript.new()
	var puzzle

	if generateRandom:
		seed = OS.get_unix_time() # unix time
		seed *= OS.get_ticks_msec() # initial time
		seed *= 1 + OS.get_time().second
		seed *= 1 + OS.get_date().weekday
		seed = abs(seed) % 7919 # 1000th prime

		rand_seed(seed)

		puzzle = puzzleMan.generatePuzzle( 2, puzzleMan.DIFF_HARD )
		puzzle.puzzleMan = puzzleMan
		var steps = puzzle.solvePuzzleSteps()
		print("Generated ", puzzle.shape.size(), " blocks." )
		print( "PUZZLE IS SOLVEABLE?: ", steps.solveable )

		var gridMan = get_node( "GridView/GridMan" )
		DataMan.savePuzzle("test.pzl", puzzle)
		var pCopy = DataMan.loadPuzzle("test.pzl")
		gridMan.set_puzzle(puzzle)

	# make a new puzzle, embed using Viewport
	if mainPuzzle:
		if (not Network == null and Network.isNetwork):
			print("Sending puzzle")
			Network.sendStart(puzzle)

		var p = PuzzleScn.instance()
		p.get_node("GridView").active = false
		p.get_node("GridView/GridMan").set_puzzle(puzzle)
		p.mainPuzzle = false
		p.set_scale(Vector3(0.5, 0.5, 0.5))
		p.set_translation(Vector3(10, 5, -20))

		var v = Viewport.new()
		var c = Control.new()

		v.set_world(p.get_world())
		v.set_render_target_to_screen_rect(Rect2(20,20,40,40))
		v.set_physics_object_picking(false)
		v.add_child(p)
		c.add_child(v)
		add_child(c)

		p.get_node("GridView").set_process_input(false)

		otherPuzzle = p #for use with network

	#music attempt
	var musicPlayer = Globals.get("StreamPlayer")
	var songs = [load("res://main_theme_antris.ogg")]
	add_child(StreamPlayer)
	add_child(musicPlayer)
	musicPlayer.set_stream(songs[0])
	musicPlayer.play()


Fix regex demo after godot commit e3e2f06

extends VBoxContainer

# Member variables
var regex = RegEx.new()

func update_expression(text):
	regex.compile(text)
	update_text()

func update_text():
	for child in $List.get_children():
		child.queue_free()
	if regex.is_valid():
		var matches = regex.search($Text.get_text())
		for result in matches.get_strings():
			var label = Label.new()
			label.text = result
			$List.add_child(label)

func _ready():
	$Text.set_text("They asked me \"What's going on \\\"in the manor\\\"?\"")
	update_expression($Expression.text)

extends VBoxContainer

# Member variables
var regex = RegEx.new()

func update_expression(text):
	regex.compile(text)
	update_text()

func update_text():
	for child in $List.get_children():
		child.queue_free()
	if regex.is_valid():
		var matches = regex.search($Text.get_text())
		for result in matches.get_group_array():
			var label = Label.new()
			label.text = result
			$List.add_child(label)

func _ready():
	$Text.set_text("They asked me \"What's going on \\\"in the manor\\\"?\"")
	update_expression($Expression.text)