Made game field scalable

Made game field scalable

extends Node2D
var mainArray = []
const BALL_SIZE = 64
var ballObj = load("res://objects/ball.tscn")
var ballPressed = false
var ballPressedName = ''
var shiftPressed = Vector2(0,0)

var gameSize = Vector2(6,8)
const COLORS = ['r', 'g', 'b', 'p', 'y', 'o']

func setGameSize(gs):
	gameSize = gs

func generateMainArray():
	mainArray = []
	for i in range(gameSize.x):
		for j in range(gameSize.y):
			mainArray.append(str(COLORS[i] + str(j)))
	mainArray[mainArray.size() - 1] = ''

func _fixed_process(delta):
	var mouse = get_viewport().get_mouse_pos()
	var ballPressedPos = findBallByName(ballPressedName)
	if ballPressed:
		var mouseOnGrid = Vector2(0,0)
		var onGrid = Vector2(0,0)

		mouseOnGrid.x = int((mouse.x - ballPressedPos.x) / 64) - 1
		mouseOnGrid.y = int((mouse.y - 32 - ballPressedPos.y) / 64)

		onGrid.x = (int((mouse.x - ballPressedPos.x) / 64) - 1) - ballPressedPos.x
		onGrid.y = int((mouse.y - 32 - ballPressedPos.y) / 64) - ballPressedPos.y

		if onGrid.x != 0:
			var ballsToShift = []

			for i in range(gameSize.x):
				var b = get_node("balls/b" + mainArray[i * gameSize.y + ballPressedPos.y])
				ballsToShift.append(b)
			for i in range(ballsToShift.size()):

				var b = get_node("balls/b" + mainArray[mainArray.size() - 1])
				if ballPressedPos.y < gameSize.y:
					b = get_node("balls/b" + mainArray[i * gameSize.y + ballPressedPos.y])
				var pos = b.get_pos()
				pos.x = i * 64 + mouseOnGrid.x * 64 - ballPressedPos.x * 64

				if pos.x > 64 * gameSize.x - 32:
					pos.x -= 64 * gameSize.x
				elif pos.x < 0:
					pos.x += 64 * gameSize.x
				b.set_pos(pos)
			shiftPressed = Vector2(ballPressedPos.y, onGrid.x)

func _ready():
	randomize()

	set_process_input(true)
	set_fixed_process(true)
#Ключовий масив, в котрому зберігаються дані про положення кольорових кульок
	mainArray = [ 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7',
				 'o1', 'o2', 'o3', 'o4', 'o5', 'o6', 'o7',
				 'y1', 'y2', 'y3', 'y4', 'y5', 'y6', 'y7',
				 'g1', 'g2', 'g3', 'g4', 'g5', 'g6', 'g7',
				 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7',
				 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', '']
#Створення кульок відповідно до ключового масиву
	generateMainArray()
	#shuffleBalls()
	updateBalls()

#Обробка подій (натискання клавіш)
func _input(event):
	if event.is_action_released("space"):
		shuffleBalls()
		updateBalls()
		ballPressed = false

	if event.is_action_released("LMB"):
		if shiftPressed.y != 0:
			shiftRow(shiftPressed.x, shiftPressed.y)
		else:
			if findBallByName('').x == findBallByName(ballPressedName).x and ballPressed:
				var sCol = findBallByName(ballPressedName).x
				cutCol(findBallByName(ballPressedName))
				updateBalls()
		ballPressed = false
		updateBalls()
		shiftPressed = Vector2(0,0)

#Створення однієї кульки за заданими параметрами
func createBall(name):
	var balls = get_node("balls")
	var ball = ballObj.instance()
	ball.set_name(name)
	balls.add_child(ball)
	var color = Color(0,0,0,1)
	if name != 'b':
		color = toColor(name[1])
	ball.setColor(color)

#Перефарбування кульок у відповідність до зазначених в ключовому масиві кольорів
func toColor(colorMark):
	var color = Color(1,0,1,1)
	if not colorMark: 			#Порожня комірка
		color = Color(1,1,0,1)
	elif colorMark == 'r':		#Червоні
		color = Color(1,0,0,1)
	elif colorMark == 'g':		#Зелені
		color = Color(0,0.6,0,1)
	elif colorMark == 'b':		#Сині
		color = Color(0,0,1,1)
	elif colorMark == 'o':		#Помаранчеві
		color = Color(1.0, 0.5, 0.0, 1.0)
	elif colorMark == 'y':		#Жовті
		color = Color(0.65, 0.65, 0.0, 1.0)
	elif colorMark == 'p':		#Фіолетові
		color = Color(0.65, 0.0, 0.65, 1.0)
	return color

#Зміщення рядка ліворуч або праворуч
func shiftRow(row, dir):
	var tempRow = []
	for i in range(gameSize.x):
		tempRow.append(mainArray[i * gameSize.y + row])
	var tail
	if dir < 0:
		for j in range(abs(dir)):
			tail = tempRow[0]
			for i in range(tempRow.size() - 1):
				tempRow[i] = tempRow[i+1]
			tempRow[tempRow.size() - 1] = tail
	elif dir > 0:
		for j in range(abs(dir)):
			tail = tempRow[tempRow.size() - 1]
			for i in range((tempRow.size() - 1), 0 , -1):
				tempRow[i] = tempRow[i - 1]
			tempRow[0] = tail
	for i in range(gameSize.x):
		mainArray[i * gameSize.y + row] = tempRow[i]

func findBallByName(name):
	var index = 0
	for b in mainArray:
		if b == name:
			var col = int(index / gameSize.y)
			var row = (index - col * gameSize.y)
			return(Vector2(col,row))
		index += 1

#Перемішати кульки
func shuffleBalls():
	var shuffledList = []
	var indexList = range(mainArray.size())
	for i in range(mainArray.size()):
		var x = randi()%indexList.size()
		shuffledList.append(mainArray[indexList[x]])
		indexList.remove(x)
	mainArray = shuffledList

#Обновлення позицій кульок
func updateBalls():
	var index = 0
	var table = []
	for b in mainArray:
		var col = int(index / gameSize.y)
		var row = (index - col * gameSize.y)
		var name = "b" + mainArray[index]
		if not get_node("balls/" + name):
			createBall(name)
		get_node("balls" + "/b" + mainArray[index]).set_pos(Vector2(col * (BALL_SIZE), row * (BALL_SIZE)))
		index += 1

#Обробка сигналів кнопок
func _signal_arrow(rowDir):
	shiftRow(abs(rowDir) - 1, sign(int(rowDir)))
	updateBalls()

func cutCol(ball):
	var column = []
	var i
	for b in range(gameSize.y):
		i = b + (ball.x * gameSize.y)
		column.append(mainArray[i])
	var empty = findBallByName('')

	var dir = 1
	if empty.y > ball.y:
		dir = -1
	i = empty.y
	while(i != ball.y):
		column[i] = column[i + dir]

		i += dir
	column[ball.y] = ''

	for b in range(gameSize.y):
		i = b + (ball.x * gameSize.y)
		mainArray[i] = column[b]

func _signal_ballClicked(name):
	if name != 'b':
		name = name[1] + name[2]
		ballPressedName = name
	else:
		ballPressedName = ''
	ballPressed = true

extends Node2D
var mainArray = []
const BALL_SIZE = 64
var ballObj = load("res://objects/ball.tscn")
var ballPressed = false
var ballPressedName = ''
var shiftPressed = Vector2(0,0)

func _fixed_process(delta):
	var mouse = get_viewport().get_mouse_pos()

	var ballPressedPos = findBallByName(ballPressedName)

	if ballPressed:

		var mouseOnGrid = Vector2(0,0)
		var onGrid = Vector2(0,0)

		mouseOnGrid.x = int((mouse.x - ballPressedPos.x) / 64) - 1
		mouseOnGrid.y = int((mouse.y - 32 - ballPressedPos.y) / 64)

		onGrid.x = (int((mouse.x - ballPressedPos.x) / 64) - 1) - ballPressedPos.x
		onGrid.y = int((mouse.y - 32 - ballPressedPos.y) / 64) - ballPressedPos.y

		if onGrid.x != 0:
			var ballsToShift = []
			if ballPressedPos.y > 6:
				ballsToShift.append(get_node("balls/b" + ballPressedName))
			else:
				for i in range(6):
					var b = get_node("balls/b" + mainArray[i * 7 + ballPressedPos.y])
					ballsToShift.append(b)
			for i in range(ballsToShift.size()):

				var b = get_node("balls/b" + mainArray[mainArray.size() - 1])
				if ballPressedPos.y < 7:
					b = get_node("balls/b" + mainArray[i * 7 + ballPressedPos.y])
				var pos = b.get_pos()
				pos.x = i * 64 + mouseOnGrid.x * 64 - ballPressedPos.x * 64
				if ballPressedPos.y > 6:
					pos.x = i * 64 + mouseOnGrid.x * 64
				if pos.x > 64 * 6 - 32:
					pos.x -= 64 * 6
				elif pos.x < 0:
					pos.x += 64 * 6
				b.set_pos(pos)
			shiftPressed = Vector2(ballPressedPos.y, onGrid.x)

func _ready():
	randomize()
	set_process_input(true)
	set_fixed_process(true)
#Ключовий масив, в котрому зберігаються дані про положення кольорових кульок
	mainArray = [ 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7',
				 'o1', 'o2', 'o3', 'o4', 'o5', 'o6', 'o7',
				 'y1', 'y2', 'y3', 'y4', 'y5', 'y6', 'y7',
				 'g1', 'g2', 'g3', 'g4', 'g5', 'g6', 'g7',
				 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7',
				 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', '']
#Створення кульок відповідно до ключового масиву
	shuffleBalls()
	updateBalls()

#Обробка подій (натискання клавіш)
func _input(event):
	if event.is_action_released("space"):
		shuffleBalls()
		updateBalls()
	if event.is_action_released("LMB"):

		if shiftPressed.y != 0:
			shiftRow(shiftPressed.x, shiftPressed.y)
		else:
			if findBallByName('').x == findBallByName(ballPressedName).x and ballPressed:

				var sCol = findBallByName(ballPressedName).x
				cutCol(findBallByName(ballPressedName))
				updateBalls()
		ballPressed = false
		updateBalls()
		shiftPressed = Vector2(0,0)

#Створення однієї кульки за заданими параметрами
func createBall(name):
	var balls = get_node("balls")
	var ball = ballObj.instance()
	ball.set_name(name)
	balls.add_child(ball)
	var color = Color(0,0,0,1)
	if name != 'b':
		color = toColor(name[1])
	ball.setColor(color)

#Перефарбування кульок у відповідність до зазначених в ключовому масиві кольорів
func toColor(colorMark):
	var color = Color(1,0,1,1)
	if not colorMark: 			#Порожня комірка
		color = Color(1,1,0,1)
	elif colorMark == 'r':		#Червоні
		color = Color(1,0,0,1)
	elif colorMark == 'g':		#Зелені
		color = Color(0,0.6,0,1)
	elif colorMark == 'b':		#Сині
		color = Color(0,0,1,1)
	elif colorMark == 'o':		#Помаранчеві
		color = Color(1.0, 0.5, 0.0, 1.0)
	elif colorMark == 'y':		#Жовті
		color = Color(0.65, 0.65, 0.0, 1.0)
	elif colorMark == 'p':		#Фіолетові
		color = Color(0.65, 0.0, 0.65, 1.0)
	return color

#Зміщення рядка ліворуч або праворуч
func shiftRow(row, dir):
	var tempRow = []
	for i in range(6):
		tempRow.append(mainArray[i * 7 + row])
	var tail
	if dir < 0:
		for j in range(abs(dir)):
			tail = tempRow[0]
			for i in range(tempRow.size() - 1):
				tempRow[i] = tempRow[i+1]
			tempRow[tempRow.size() - 1] = tail
	elif dir > 0:
		for j in range(abs(dir)):
			tail = tempRow[tempRow.size() - 1]
			for i in range((tempRow.size() - 1), 0 , -1):
				tempRow[i] = tempRow[i - 1]
			tempRow[0] = tail
	for i in range(6):
		mainArray[i * 7 + row] = tempRow[i]

func findBallByName(name):
	var index = 0
	for b in mainArray:
		if b == name:
			var col = int(index / 7)
			var row = (index - col * 7)
			return(Vector2(col,row))
		index += 1

#Перемішати кульки
func shuffleBalls():
	var shuffledList = []
	var indexList = range(mainArray.size())
	for i in range(mainArray.size()):
		var x = randi()%indexList.size()
		shuffledList.append(mainArray[indexList[x]])
		indexList.remove(x)
	mainArray = shuffledList

#Обновлення позицій кульок
func updateBalls():
	var index = 0
	var table = []
	for b in mainArray:
		var col = int(index / 7)
		var row = (index - col * 7)
		var name = "b" + mainArray[index]
		if not get_node("balls/" + name):
			createBall(name)
		get_node("balls" + "/b" + mainArray[index]).set_pos(Vector2(col * (BALL_SIZE), row * (BALL_SIZE)))
		index += 1

#Обробка сигналів кнопок
func _signal_arrow(rowDir):
	shiftRow(abs(rowDir) - 1, sign(int(rowDir)))
	updateBalls()

func cutCol(ball):
	var column = []
	var i
	for b in range(7):
		i = b + (ball.x * 7)
		column.append(mainArray[i])
	var empty = findBallByName('')

	var dir = 1
	if empty.y > ball.y:
		dir = -1
	i = empty.y
	while(i != ball.y):
		column[i] = column[i + dir]

		i += dir
	column[ball.y] = ''

	for b in range(7):
		i = b + (ball.x * 7)
		mainArray[i] = column[b]

func _signal_ballClicked(name):
	if name != 'b':
		name = name[1] + name[2]
		ballPressedName = name
	else:
		ballPressedName = ''
	ballPressed = true


Speak bug with timers

Speak bug with timers

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

const THRASH_AMOUNT = 2
var THRASH_SPAWN_TIME = 5
var VULNERABLE_TIME = 3.5
var RAINBOW_DELAY = 5
var thrash_template = 'fat_rat'

var shield_points = [
    Vector2(17, 3),
    Vector2(17, 8),
    Vector2(2, 8),
]
var shield_points_global = []

var shield
var angry_body = null
var rainbow_spawned = true
var rainbow = 'rainbow'

func _init(bag).(bag):
    self.avatar = preload("res://scenes/enemies/grumpy_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.angry_body = self.avatar.get_node('angry')
    self.shield = self.avatar.get_node('shield')
    self.hit_particles = self.avatar.get_node('hitparticles')
    self.speech_bubble = self.avatar.get_node('speech')

    self.velocity = 25
    self.attack_range = 50
    self.attack_strength = 4
    self.attack_cooldown = 2
    self.max_hp = 200
    self.hp = 200
    self.score = 200
    self.is_invulnerable = true

    self.stun_duration = 0.4
    self.mass = 3

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

    for point in self.shield_points:
        self.shield_points_global.push_back(self.bag.room_loader.translate_position(point))

func spawn(position):
    .spawn(position)
    self.randomize_movement()
    self.raise_shield()
    self.spawn_thrash()
    self.speak("NO", 2, true)

func phase2():
    self.body_part_body.hide()
    self.angry_body.show()
    self.THRASH_SPAWN_TIME = 2.5
    self.VULNERABLE_TIME = 2
    self.RAINBOW_DELAY = 2.5
    self.velocity = 50
    self.speak("I had fun once", 2, true)
    self.bag.timers.set_timeout(2.1, self, "bugged_speak")

func bugged_speak():
    self.speak("It was awful", 2, true)

func process_ai():
    var distance

    if self.target == null:
        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.target = player
                    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 not self.rainbow_spawned:
        self.rainbow_spawned = true
        self.bag.timers.set_timeout(self.RAINBOW_DELAY, self, 'spawn_rainbow')

func randomize_movement():
    if self.hp < 1:
        return

    var direction

    var x = randi() % 18 + 1
    var y = randi() % 8 + 1

    direction = self.bag.room_loader.translate_position(Vector2(x, y))
    direction = self.cast_movement_vector(direction)

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

func reset_movement():
    return

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

func external_stun(duration=null):
    if self.is_invulnerable:
        return

    .external_stun(duration)

func push_back(enemy):
    if self.is_invulnerable:
        return

    .push_back(enemy)

func schedule_thrash_spawn():
    self.bag.timers.set_timeout(self.THRASH_SPAWN_TIME, self, 'spawn_thrash')

func spawn_thrash():
    if self.hp < 1:
        return

    var position = self.avatar.get_global_pos()
    var new_guy

    for i in range(0, self.THRASH_AMOUNT):
        position = self.avatar.get_global_pos()
        position.x += pow(-1, i) * 30
        position.y += 30
        new_guy = self.bag.enemies.spawn_global(self.thrash_template, position)
        new_guy.is_attack_on_cooldown = true
        new_guy.has_tombstone = false
        new_guy.drop_chance = 0
        self.bag.timers.set_timeout(self.attack_cooldown, new_guy, "attack_cooled_down")
        new_guy.make_invulnerable(1)

    self.schedule_thrash_spawn()

func disable_shield():
    self.is_invulnerable = false
    self.shield.hide()
    self.bag.timers.set_timeout(self.VULNERABLE_TIME, self, 'raise_shield')

func raise_shield():
    self.animations.play('shield_on')
    self.bag.timers.set_timeout(0.5, self, 'raise_shield_anim_end')

func raise_shield_anim_end():
    self.is_invulnerable = true
    self.rainbow_spawned = false
    self.shield.show()

func spawn_rainbow():
    if self.hp < 1:
        return

    self.bag.enemies.spawn_global(self.rainbow, self.pick_next_point())

func pick_next_point():
    return self.shield_points_global[randi() % self.shield_points_global.size()]

func flip_body_parts(flip_flag):
    .flip_body_parts(flip_flag)
    self.angry_body.set_flip_h(flip_flag)

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

const THRASH_AMOUNT = 2
var THRASH_SPAWN_TIME = 5
var VULNERABLE_TIME = 3.5
var RAINBOW_DELAY = 5
var thrash_template = 'fat_rat'

var shield_points = [
    Vector2(17, 3),
    Vector2(17, 8),
    Vector2(2, 8),
]
var shield_points_global = []

var shield
var angry_body = null
var rainbow_spawned = true
var rainbow = 'rainbow'

func _init(bag).(bag):
    self.avatar = preload("res://scenes/enemies/grumpy_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.angry_body = self.avatar.get_node('angry')
    self.shield = self.avatar.get_node('shield')
    self.hit_particles = self.avatar.get_node('hitparticles')
    self.speech_bubble = self.avatar.get_node('speech')

    self.velocity = 25
    self.attack_range = 50
    self.attack_strength = 4
    self.attack_cooldown = 2
    self.max_hp = 200
    self.hp = 200
    self.score = 200
    self.is_invulnerable = true

    self.stun_duration = 0.4
    self.mass = 3

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

    for point in self.shield_points:
        self.shield_points_global.push_back(self.bag.room_loader.translate_position(point))

func spawn(position):
    .spawn(position)
    self.randomize_movement()
    self.raise_shield()
    self.spawn_thrash()
    self.speak("NO", 2, true)

func phase2():
    self.body_part_body.hide()
    self.angry_body.show()
    self.THRASH_SPAWN_TIME = 2.5
    self.VULNERABLE_TIME = 2
    self.RAINBOW_DELAY = 2.5
    self.velocity = 50
    self.speak("I had fun once", 2, true)
    self.bag.timers.set_timeout(2.1, self, "speak", ["It was awful", 2, true])

func process_ai():
    var distance

    if self.target == null:
        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.target = player
                    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 not self.rainbow_spawned:
        self.rainbow_spawned = true
        self.bag.timers.set_timeout(self.RAINBOW_DELAY, self, 'spawn_rainbow')

func randomize_movement():
    if self.hp < 1:
        return

    var direction

    var x = randi() % 18 + 1
    var y = randi() % 8 + 1

    direction = self.bag.room_loader.translate_position(Vector2(x, y))
    direction = self.cast_movement_vector(direction)

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

func reset_movement():
    return

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

func external_stun(duration=null):
    if self.is_invulnerable:
        return

    .external_stun(duration)

func push_back(enemy):
    if self.is_invulnerable:
        return

    .push_back(enemy)

func schedule_thrash_spawn():
    self.bag.timers.set_timeout(self.THRASH_SPAWN_TIME, self, 'spawn_thrash')

func spawn_thrash():
    if self.hp < 1:
        return

    var position = self.avatar.get_global_pos()
    var new_guy

    for i in range(0, self.THRASH_AMOUNT):
        position = self.avatar.get_global_pos()
        position.x += pow(-1, i) * 30
        position.y += 30
        new_guy = self.bag.enemies.spawn_global(self.thrash_template, position)
        new_guy.is_attack_on_cooldown = true
        new_guy.has_tombstone = false
        new_guy.drop_chance = 0
        self.bag.timers.set_timeout(self.attack_cooldown, new_guy, "attack_cooled_down")
        new_guy.make_invulnerable(1)

    self.schedule_thrash_spawn()

func disable_shield():
    self.is_invulnerable = false
    self.shield.hide()
    self.bag.timers.set_timeout(self.VULNERABLE_TIME, self, 'raise_shield')

func raise_shield():
    self.animations.play('shield_on')
    self.bag.timers.set_timeout(0.5, self, 'raise_shield_anim_end')

func raise_shield_anim_end():
    self.is_invulnerable = true
    self.rainbow_spawned = false
    self.shield.show()

func spawn_rainbow():
    if self.hp < 1:
        return

    self.bag.enemies.spawn_global(self.rainbow, self.pick_next_point())

func pick_next_point():
    return self.shield_points_global[randi() % self.shield_points_global.size()]

func flip_body_parts(flip_flag):
    .flip_body_parts(flip_flag)
    self.angry_body.set_flip_h(flip_flag)


Fixed Grumpy Prime stopping to spawn adds

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

const THRASH_AMOUNT = 2
var THRASH_SPAWN_TIME = 5
var VULNERABLE_TIME = 3.5
var RAINBOW_DELAY = 5
var thrash_template = 'fat_rat'

var shield_points = [
    Vector2(17, 3),
    Vector2(17, 8),
    Vector2(2, 8),
]
var shield_points_global = []

var shield
var angry_body = null
var rainbow_spawned = true
var rainbow = 'rainbow'

func _init(bag).(bag):
    self.avatar = preload("res://scenes/enemies/grumpy_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.angry_body = self.avatar.get_node('angry')
    self.shield = self.avatar.get_node('shield')

    self.velocity = 25
    self.attack_range = 50
    self.attack_strength = 4
    self.attack_cooldown = 2
    self.max_hp = 200
    self.hp = 200
    self.score = 200
    self.is_invulnerable = true

    self.stun_duration = 0.4
    self.mass = 3

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

    for point in self.shield_points:
        self.shield_points_global.push_back(self.bag.room_loader.translate_position(point))

func spawn(position):
    .spawn(position)
    self.randomize_movement()
    self.raise_shield()
    self.spawn_thrash()

func phase2():
    self.body_part_body.hide()
    self.angry_body.show()
    self.THRASH_SPAWN_TIME = 2.5
    self.VULNERABLE_TIME = 2
    self.RAINBOW_DELAY = 2.5
    self.velocity = 50

func process_ai():
    var distance

    if self.target == null:
        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.target = player
                    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 not self.rainbow_spawned:
        self.rainbow_spawned = true
        self.bag.timers.set_timeout(self.RAINBOW_DELAY, self, 'spawn_rainbow')

func randomize_movement():
    if self.hp < 1:
        return

    var direction

    var x = randi() % 18 + 1
    var y = randi() % 8 + 1

    direction = self.bag.room_loader.translate_position(Vector2(x, y))
    direction = self.cast_movement_vector(direction)

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

func reset_movement():
    return

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

func external_stun(duration=null):
    if self.is_invulnerable:
        return

    .external_stun(duration)

func push_back(enemy):
    if self.is_invulnerable:
        return

    .push_back(enemy)

func schedule_thrash_spawn():
    self.bag.timers.set_timeout(self.THRASH_SPAWN_TIME, self, 'spawn_thrash')

func spawn_thrash():
    if self.hp < 1:
        return

    var position = self.avatar.get_global_pos()
    var new_guy

    for i in range(0, self.THRASH_AMOUNT):
        position = self.avatar.get_global_pos()
        position.x += pow(-1, i) * 30
        position.y += 30
        new_guy = self.bag.enemies.spawn_global(self.thrash_template, position)
        new_guy.is_attack_on_cooldown = true
        self.bag.timers.set_timeout(self.attack_cooldown, new_guy, "attack_cooled_down")
        new_guy.make_invulnerable(1)

    self.schedule_thrash_spawn()

func disable_shield():
    self.is_invulnerable = false
    self.shield.hide()
    self.bag.timers.set_timeout(self.VULNERABLE_TIME, self, 'raise_shield')

func raise_shield():
    self.animations.play('shield_on')
    self.bag.timers.set_timeout(0.5, self, 'raise_shield_anim_end')

func raise_shield_anim_end():
    self.is_invulnerable = true
    self.rainbow_spawned = false
    self.shield.show()

func spawn_rainbow():
    if self.hp < 1:
        return

    self.bag.enemies.spawn_global(self.rainbow, self.pick_next_point())

func pick_next_point():
    return self.shield_points_global[randi() % self.shield_points_global.size()]

func flip_body_parts(flip_flag):
    .flip_body_parts(flip_flag)
    self.angry_body.set_flip_h(flip_flag)

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

const THRASH_AMOUNT = 2
var THRASH_SPAWN_TIME = 5
var VULNERABLE_TIME = 3.5
var RAINBOW_DELAY = 5
var thrash_template = 'fat_rat'

var shield_points = [
    Vector2(17, 3),
    Vector2(17, 8),
    Vector2(2, 8),
]
var shield_points_global = []

var shield
var angry_body = null
var rainbow_spawned = true
var rainbow = 'rainbow'

func _init(bag).(bag):
    self.avatar = preload("res://scenes/enemies/grumpy_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.angry_body = self.avatar.get_node('angry')
    self.shield = self.avatar.get_node('shield')

    self.velocity = 25
    self.attack_range = 50
    self.attack_strength = 4
    self.attack_cooldown = 2
    self.max_hp = 200
    self.hp = 200
    self.score = 200
    self.is_invulnerable = true

    self.stun_duration = 0.4
    self.mass = 3

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

    for point in self.shield_points:
        self.shield_points_global.push_back(self.bag.room_loader.translate_position(point))

func spawn(position):
    .spawn(position)
    self.randomize_movement()
    self.raise_shield()
    self.spawn_thrash()

func phase2():
    self.body_part_body.hide()
    self.angry_body.show()
    self.THRASH_SPAWN_TIME = 2.5
    self.VULNERABLE_TIME = 2
    self.RAINBOW_DELAY = 2.5
    self.velocity = 50

func process_ai():
    var distance

    if self.target == null:
        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.target = player
                    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 not self.rainbow_spawned:
        self.rainbow_spawned = true
        self.bag.timers.set_timeout(self.RAINBOW_DELAY, self, 'spawn_rainbow')

func randomize_movement():
    if self.hp < 1:
        return

    var direction

    var x = randi() % 18 + 1
    var y = randi() % 8 + 1

    direction = self.bag.room_loader.translate_position(Vector2(x, y))
    direction = self.cast_movement_vector(direction)

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

func reset_movement():
    return

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

func external_stun(duration=null):
    if self.is_invulnerable:
        return

    .external_stun(duration)

func push_back(enemy):
    if self.is_invulnerable:
        return

    .push_back(enemy)

func schedule_thrash_spawn():
    self.bag.timers.set_timeout(self.THRASH_SPAWN_TIME, self, 'spawn_thrash')

func spawn_thrash():
    if self.hp < 1 or not self.is_processing:
        return

    var position = self.avatar.get_global_pos()
    var new_guy

    for i in range(0, self.THRASH_AMOUNT):
        position = self.avatar.get_global_pos()
        position.x += pow(-1, i) * 30
        position.y += 30
        new_guy = self.bag.enemies.spawn_global(self.thrash_template, position)
        new_guy.is_attack_on_cooldown = true
        self.bag.timers.set_timeout(self.attack_cooldown, new_guy, "attack_cooled_down")
        new_guy.make_invulnerable(1)

    self.schedule_thrash_spawn()

func disable_shield():
    self.is_invulnerable = false
    self.shield.hide()
    self.bag.timers.set_timeout(self.VULNERABLE_TIME, self, 'raise_shield')

func raise_shield():
    self.animations.play('shield_on')
    self.bag.timers.set_timeout(0.5, self, 'raise_shield_anim_end')

func raise_shield_anim_end():
    self.is_invulnerable = true
    self.rainbow_spawned = false
    self.shield.show()

func spawn_rainbow():
    if self.hp < 1:
        return

    self.bag.enemies.spawn_global(self.rainbow, self.pick_next_point())

func pick_next_point():
    return self.shield_points_global[randi() % self.shield_points_global.size()]

func flip_body_parts(flip_flag):
    .flip_body_parts(flip_flag)
    self.angry_body.set_flip_h(flip_flag)


Fixed room loader

Fixed room loader

var bag
var tilemap
var room_max_size = Vector2(20, 12)
var side_offset = 3
var DOORS_CLOSED = 3
var DOORS_OPEN = 2

var spawns = {
    'initial0' : Vector2(8, 5),
    'initial1' : Vector2(9, 5),
    'initial2' : Vector2(8, 6),
    'initial3' : Vector2(9, 6),
    'north0' : Vector2(8, 1),
    'north1' : Vector2(9, 1),
    'north2' : Vector2(8, 2),
    'north3' : Vector2(9, 2),
    'south0' : Vector2(8, 9),
    'south1' : Vector2(9, 9),
    'south2' : Vector2(8, 8),
    'south3' : Vector2(9, 8),
    'east0' : Vector2(15, 5),
    'east1' : Vector2(14, 5),
    'east2' : Vector2(15, 6),
    'east3' : Vector2(14, 6),
    'west0' : Vector2(1, 5),
    'west1' : Vector2(2, 5),
    'west2' : Vector2(1, 6),
    'west3' : Vector2(2, 6),
}

var room_templates = {
    'start' : preload("res://scripts/map/rooms/start_room.gd"),
    'easy1' : preload("res://scripts/map/rooms/easy1_room.gd"),
    'easy2' : preload("res://scripts/map/rooms/easy2_room.gd"),
    'easy3' : preload("res://scripts/map/rooms/easy3_room.gd"),
    'easy4' : preload("res://scripts/map/rooms/easy4_room.gd"),
    'easy5' : preload("res://scripts/map/rooms/easy5_room.gd"),
    'easy6' : preload("res://scripts/map/rooms/easy6_room.gd"),
    'boss1' : preload("res://scripts/map/rooms/boss1_room.gd"),
    'boss2' : preload("res://scripts/map/rooms/boss2_room.gd"),
}

var difficulty_templates = [
    ['start'],
    ['easy1', 'easy2', 'easy4', 'easy5', 'easy6'],
    ['easy2', 'easy3', 'easy4', 'easy6'],
]

var difficulty_bosses = [
    [],
    ['boss1'],
    ['boss2'],
]

var door_definitions = {
    'north' : [
        [0, 0, 14, 21],
        [1, 0, 0, 19],
        [2, 0, 13, 20],
    ],
    'south' : [
        [0, 0, 16, 26],
        [1, 0, 0, 27],
        [2, 0, 15, 28],
    ],
    'east' : [
        [0, 0, 13, 17],
        [0, 1, 0, 22],
        [0, 2, 15, 24],
    ],
    'west' : [
        [0, 0, 14, 18],
        [0, 1, 0, 23],
        [0, 2, 16, 25],
    ],
}

func _init_bag(bag):
    self.bag = bag
    self.tilemap = self.bag.action_controller.tilemap

func load_room(cell):
    var template_name = cell.template_name
    var data
    self.clear_space()
    self.bag.game_state.current_room = self.room_templates[template_name].new()
    data = self.create_passages(self.bag.game_state.current_room.room, cell)
    self.apply_room_data(data)
    if self.bag.game_state.current_room.enemies.size() > 0 && not cell.clear:
        self.spawn_enemies(self.bag.game_state.current_room.enemies)
        self.close_doors()
    else:
        self.open_doors()
    self.bag.items.reset()
    if cell.items_loaded:
        self.load_previous_items(cell.items)
    else:
        self.spawn_items(self.bag.game_state.current_room.items, cell)
        cell.items_loaded = true

func create_passages(data, cell):
    if cell.north != null:
        data = self.open_passage(data, self.door_definitions['north'], 7, 0)
    if cell.south != null:
        data = self.open_passage(data, self.door_definitions['south'], 7, 10)
    if cell.east != null:
        data = self.open_passage(data, self.door_definitions['east'], 16, 4)
    if cell.west != null:
        data = self.open_passage(data, self.door_definitions['west'], 0, 4)
    return data

func open_passage(data, passage, x, y):
    for tile in passage:
        data[tile[1] + y][tile[0] + x] = tile[2]
    return data

func close_doors():
    self.bag.game_state.doors_open = false
    self.switch_doors(self.DOORS_CLOSED)

func open_doors():
    self.bag.game_state.doors_open = true
    self.switch_doors(self.DOORS_OPEN)

func switch_custom_doors(tile_index):
    for custom_door in self.bag.game_state.current_room.doors:
        self.apply_door(self.door_definitions[custom_door[2]], tile_index, custom_door[0], custom_door[1])

func switch_doors(tile_index):
    var cell = self.bag.game_state.current_cell
    if cell.north != null:
        self.apply_door(self.door_definitions['north'], tile_index, 7, 0)
    if cell.south != null:
        self.apply_door(self.door_definitions['south'], tile_index, 7, 10)
    if cell.east != null:
        self.apply_door(self.door_definitions['east'], tile_index, 16, 4)
    if cell.west != null:
        self.apply_door(self.door_definitions['west'], tile_index, 0, 4)
    self.switch_custom_doors(tile_index)

func apply_door(definition, tile_index, x, y):
    for tile in definition:
        self.tilemap.set_cell(tile[0] + self.side_offset + x, tile[1] + y, tile[tile_index])

func clear_space():
    for x in range(self.room_max_size.x):
        for y in range(self.room_max_size.y):
            self.tilemap.set_cell(x, y, -1)

func apply_room_data(data):
    var row
    for y in range(0, data.size()):
        row = data[y]
        for x in range(0, row.size()):
            self.tilemap.set_cell(x + self.side_offset, y, data[y][x])

func spawn_enemies(enemies):
    var position = Vector2(0, 0)
    self.bag.enemies.reset()
    for enemy_data in enemies:
        position.x = enemy_data[0] + self.side_offset
        position.y = enemy_data[1]
        self.bag.enemies.spawn(enemy_data[2], position)

func spawn_items(items, cell):
    var position = Vector2(0, 0)
    for item_data in items:
        position.x = item_data[0] + self.side_offset
        position.y = item_data[1]
        cell.add_item(self.bag.items.spawn(item_data[2], position))

func load_previous_items(items):
    for item in items:
        items[item].attach()
        self.bag.items.add_item(items[item])

func get_spawn_position(spawn_name):
    var position = self.bag.room_loader.spawns[spawn_name]
    position.x = position.x + self.side_offset
    return self.translate_position(position)

func translate_position(position):
    return self.tilemap.map_to_world(position)

var bag
var tilemap
var room_max_size = Vector2(20, 12)
var side_offset = 3
var DOORS_CLOSED = 3
var DOORS_OPEN = 2

var spawns = {
    'initial0' : Vector2(8, 5),
    'initial1' : Vector2(9, 5),
    'initial2' : Vector2(8, 6),
    'initial3' : Vector2(9, 6),
    'north0' : Vector2(8, 1),
    'north1' : Vector2(9, 1),
    'north2' : Vector2(8, 2),
    'north3' : Vector2(9, 2),
    'south0' : Vector2(8, 9),
    'south1' : Vector2(9, 9),
    'south2' : Vector2(8, 8),
    'south3' : Vector2(9, 8),
    'east0' : Vector2(15, 5),
    'east1' : Vector2(14, 5),
    'east2' : Vector2(15, 6),
    'east3' : Vector2(14, 6),
    'west0' : Vector2(1, 5),
    'west1' : Vector2(2, 5),
    'west2' : Vector2(1, 6),
    'west3' : Vector2(2, 6),
}

var room_templates = {
    'start' : preload("res://scripts/map/rooms/start_room.gd"),
    'easy1' : preload("res://scripts/map/rooms/easy1_room.gd"),
    'easy2' : preload("res://scripts/map/rooms/easy2_room.gd"),
    'easy3' : preload("res://scripts/map/rooms/easy3_room.gd"),
    'easy4' : preload("res://scripts/map/rooms/easy4_room.gd"),
    'easy5' : preload("res://scripts/map/rooms/easy5_room.gd"),
    'easy6' : preload("res://scripts/map/rooms/easy6_room.gd"),
    'boss1' : preload("res://scripts/map/rooms/boss1_room.gd"),
    'boss2' : preload("res://scripts/map/rooms/boss2_room.gd"),
}

var difficulty_templates = [
    ['start'],
    ['easy1', 'easy2', 'easy4', 'easy5', 'easy6'],
    ['easy2', 'easy3', 'easy4', 'easy6'],
]

var difficulty_bosses = [
    [],
    ['boss1']
    ['boss2']
]

var door_definitions = {
    'north' : [
        [0, 0, 14, 21],
        [1, 0, 0, 19],
        [2, 0, 13, 20],
    ],
    'south' : [
        [0, 0, 16, 26],
        [1, 0, 0, 27],
        [2, 0, 15, 28],
    ],
    'east' : [
        [0, 0, 13, 17],
        [0, 1, 0, 22],
        [0, 2, 15, 24],
    ],
    'west' : [
        [0, 0, 14, 18],
        [0, 1, 0, 23],
        [0, 2, 16, 25],
    ],
}

func _init_bag(bag):
    self.bag = bag
    self.tilemap = self.bag.action_controller.tilemap

func load_room(cell):
    var template_name = cell.template_name
    var data
    self.clear_space()
    self.bag.game_state.current_room = self.room_templates[template_name].new()
    data = self.create_passages(self.bag.game_state.current_room.room, cell)
    self.apply_room_data(data)
    if self.bag.game_state.current_room.enemies.size() > 0 && not cell.clear:
        self.spawn_enemies(self.bag.game_state.current_room.enemies)
        self.close_doors()
    else:
        self.open_doors()
    self.bag.items.reset()
    if cell.items_loaded:
        self.load_previous_items(cell.items)
    else:
        self.spawn_items(self.bag.game_state.current_room.items, cell)
        cell.items_loaded = true

func create_passages(data, cell):
    if cell.north != null:
        data = self.open_passage(data, self.door_definitions['north'], 7, 0)
    if cell.south != null:
        data = self.open_passage(data, self.door_definitions['south'], 7, 10)
    if cell.east != null:
        data = self.open_passage(data, self.door_definitions['east'], 16, 4)
    if cell.west != null:
        data = self.open_passage(data, self.door_definitions['west'], 0, 4)
    return data

func open_passage(data, passage, x, y):
    for tile in passage:
        data[tile[1] + y][tile[0] + x] = tile[2]
    return data

func close_doors():
    self.bag.game_state.doors_open = false
    self.switch_doors(self.DOORS_CLOSED)

func open_doors():
    self.bag.game_state.doors_open = true
    self.switch_doors(self.DOORS_OPEN)

func switch_custom_doors(tile_index):
    for custom_door in self.bag.game_state.current_room.doors:
        self.apply_door(self.door_definitions[custom_door[2]], tile_index, custom_door[0], custom_door[1])

func switch_doors(tile_index):
    var cell = self.bag.game_state.current_cell
    if cell.north != null:
        self.apply_door(self.door_definitions['north'], tile_index, 7, 0)
    if cell.south != null:
        self.apply_door(self.door_definitions['south'], tile_index, 7, 10)
    if cell.east != null:
        self.apply_door(self.door_definitions['east'], tile_index, 16, 4)
    if cell.west != null:
        self.apply_door(self.door_definitions['west'], tile_index, 0, 4)
    self.switch_custom_doors(tile_index)

func apply_door(definition, tile_index, x, y):
    for tile in definition:
        self.tilemap.set_cell(tile[0] + self.side_offset + x, tile[1] + y, tile[tile_index])

func clear_space():
    for x in range(self.room_max_size.x):
        for y in range(self.room_max_size.y):
            self.tilemap.set_cell(x, y, -1)

func apply_room_data(data):
    var row
    for y in range(0, data.size()):
        row = data[y]
        for x in range(0, row.size()):
            self.tilemap.set_cell(x + self.side_offset, y, data[y][x])

func spawn_enemies(enemies):
    var position = Vector2(0, 0)
    self.bag.enemies.reset()
    for enemy_data in enemies:
        position.x = enemy_data[0] + self.side_offset
        position.y = enemy_data[1]
        self.bag.enemies.spawn(enemy_data[2], position)

func spawn_items(items, cell):
    var position = Vector2(0, 0)
    for item_data in items:
        position.x = item_data[0] + self.side_offset
        position.y = item_data[1]
        cell.add_item(self.bag.items.spawn(item_data[2], position))

func load_previous_items(items):
    for item in items:
        items[item].attach()
        self.bag.items.add_item(items[item])

func get_spawn_position(spawn_name):
    var position = self.bag.room_loader.spawns[spawn_name]
    position.x = position.x + self.side_offset
    return self.translate_position(position)

func translate_position(position):
    return self.tilemap.map_to_world(position)


Move to puzzle.scn instead of network_test

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 remotePuzzle

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

func _ready():
	isNetwork = false
	isHost = false
	get_tree().get_root().get_node("GUIManager/OptionsMenu/Panel/PortField/LineEdit").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));
		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:
		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
				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!")
				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;

	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[2]
		remotePuzzle.otherPuzzle.set_scale(scale)
		remotePuzzle.otherPuzzle.set_translation(translation)
	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(scale, translation):
	if !isNetwork:
		print("Cannot send transformation over an unitialized network!")
		return
	connection.put_var([REMOTE_BLOCK_TRANSFORM, scale, translation])

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

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 remotePuzzle

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

func _ready():
	isNetwork = false
	isHost = false
	get_tree().get_root().get_node("GUIManager/OptionsMenu/Panel/PortField/LineEdit").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));
		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:
		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://network_test.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
				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://network_test.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!")
				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;

	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[2]
		remotePuzzle.otherPuzzle.set_scale(scale)
		remotePuzzle.otherPuzzle.set_translation(translation)
	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(scale, translation):
	if !isNetwork:
		print("Cannot send transformation over an unitialized network!")
		return
	connection.put_var([REMOTE_BLOCK_TRANSFORM, scale, translation])

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


networking moved

networking moved

extends "AbstractBlock.gd"

# 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

var textureName

func setTexture(textureName="Red"):
	var img = Image()
	self.textureName = textureName

	var mat = load("res://materials/block_" + textureName + ".mtl")

	self.get_node("MeshInstance").set_material_override(mat)
	return self

# Animate the removal of this block and its pair.
func popBlock( pairNode, justFly=false ):
	get_parent().samplePlayer.play("deraj_pop_sound_low")
	# fly away
	var tweenNode = newTweenNode()
	tweenNode.interpolate_method( self, "set_translation", \
		self.get_translation(), self.get_translation().normalized() * far_away_corner, \
		1, Tween.TRANS_CIRC, Tween.EASE_IN_OUT )

	# remove on animation end
	tweenNode.connect("tween_complete", self, "request_remove")

	tweenNode.start()
	# just one call to activate...
	if not justFly:
		pairNode.activate(true)
		get_parent().popPair( blockPos )

# THIS IS EVERYWHERE, ANY BETTER WAY TO HAVE FUNCTIONS BETWEEN SCRIPTS?
func calcBlockLayerVec( pos ):
	return max( max( abs( pos.x ), abs( pos.y ) ), abs( pos.z ) )

# fly away only if self.pairName is selected
func activate(justFly = false):
	var gridMan = get_parent()
	if gridMan.selectedBlocks.size() > 0:
		var selBlock = gridMan.get_node( gridMan.selectedBlocks[0] )

		if selBlock.getBlockType() == BLOCK_WILD:
			if selBlock.textureName == textureName:
				if calcBlockLayerVec( selBlock.blockPos ) == calcBlockLayerVec( blockPos ):
					gridMan.clearSelection()
					selBlock.popBlock()
					forceActivate()

					return

	var pairNode = pairActivate()
	if pairNode == null:
		return
	if pairNode.selected:
		popBlock( pairNode, justFly )

# Force the pair to activate.
func forceActivate():
	var pairNode = pairActivate()
	if pairNode == null:
		return

	popBlock( pairNode )

extends "AbstractBlock.gd"

# 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

var textureName

func setTexture(textureName="Red"):
	var img = Image()
	self.textureName = textureName

	var mat = load("res://materials/block_" + textureName + ".mtl")

	self.get_node("MeshInstance").set_material_override(mat)
	return self

# Animate the removal of this block and its pair.
func popBlock( pairNode, justFly=false ):
	get_parent().samplePlayer.play("deraj_pop_sound_low")
	# fly away
	var tweenNode = newTweenNode()
	tweenNode.interpolate_method( self, "set_translation", \
		self.get_translation(), self.get_translation().normalized() * far_away_corner, \
		1, Tween.TRANS_CIRC, Tween.EASE_IN_OUT )

	# remove on animation end
	tweenNode.connect("tween_complete", self, "request_remove")

	var network = Globals.get("Network")
	if (not network == null and get_parent().get_parent().active and network.isNetwork):
			network.sendBlockUpdate(blockPos)

	tweenNode.start()
	# just one call to activate...
	if not justFly:
		pairNode.activate(true)
		get_parent().popPair( blockPos )

# THIS IS EVERYWHERE, ANY BETTER WAY TO HAVE FUNCTIONS BETWEEN SCRIPTS?
func calcBlockLayerVec( pos ):
	return max( max( abs( pos.x ), abs( pos.y ) ), abs( pos.z ) )

# fly away only if self.pairName is selected
func activate(justFly = false):
	var gridMan = get_parent()
	if gridMan.selectedBlocks.size() > 0:
		var selBlock = gridMan.get_node( gridMan.selectedBlocks[0] )

		if selBlock.getBlockType() == BLOCK_WILD:
			if selBlock.textureName == textureName:
				if calcBlockLayerVec( selBlock.blockPos ) == calcBlockLayerVec( blockPos ):
					gridMan.clearSelection()
					selBlock.popBlock()
					forceActivate()

					return

	var pairNode = pairActivate()
	if pairNode == null:
		return
	if pairNode.selected:
		popBlock( pairNode, justFly )

# Force the pair to activate.
func forceActivate():
	var pairNode = pairActivate()
	if pairNode == null:
		return

	popBlock( pairNode )


Fix condition always true in navigation demo

extends Navigation2D

export(float) var CHARACTER_SPEED = 400.0
var path = []

# The 'click' event is a custom input action defined in
# Project > Project Settings > Input Map tab
func _input(event):
	if not event.is_action_pressed('click'):
		return
	_update_navigation_path($Character.position, get_local_mouse_position())

func _update_navigation_path(start_position, end_position):
	# get_simple_path is part of the Navigation2D class
	# it returns a PoolVector2Array of points that lead you from the
	# start_position to the end_position
	path = get_simple_path(start_position, end_position, true)
	# The first point is always the start_position
	# We don't need it in this example as it corresponds to the character's position
	path.remove(0)
	set_process(true)

func _process(delta):
	var walk_distance = CHARACTER_SPEED * delta
	move_along_path(walk_distance)

func move_along_path(distance):
	var last_point = $Character.position
	for index in range(path.size()):
		var distance_between_points = last_point.distance_to(path[0])
		# the position to move to falls between two points
		if distance <= distance_between_points and distance >= 0.0:
			$Character.position = last_point.linear_interpolate(path[0], distance / distance_between_points)
			break
		# the character reached the end of the path
		elif distance < 0.0:
			$Character.position = path[0]
			set_process(false)
			break
		distance -= distance_between_points
		last_point = path[0]
		path.remove(0)

extends Navigation2D

export(float) var CHARACTER_SPEED = 400.0
var path = []

# The 'click' event is a custom input action defined in
# Project > Project Settings > Input Map tab
func _input(event):
	if not event.is_action_pressed('click'):
		return
	_update_navigation_path($Character.position, get_local_mouse_position())

func _update_navigation_path(start_position, end_position):
	# get_simple_path is part of the Navigation2D class
	# it returns a PoolVector2Array of points that lead you from the
	# start_position to the end_position
	path = get_simple_path(start_position, end_position, true)
	# The first point is always the start_position
	# We don't need it in this example as it corresponds to the character's position
	path.remove(0)
	set_process(true)

func _process(delta):
	var walk_distance = CHARACTER_SPEED * delta
	move_along_path(walk_distance)

func move_along_path(distance):
	var last_point = $Character.position
	for index in range(path.size()):
		var distance_between_points = last_point.distance_to(path[0])
		# the position to move to falls between two points
		if distance <= distance_between_points:
			$Character.position = last_point.linear_interpolate(path[0], distance / distance_between_points)
			break
		# the character reached the end of the path
		elif distance < 0.0:
			$Character.position = path[0]
			set_process(false)
			break
		distance -= distance_between_points
		last_point = path[0]
		path.remove(0)


fixes to new window management API

fixes to new window management API

-needs testing on Linux
-needs testing on Windows
-NEED SOMEONE TO IMPLEMENT IT ON OSX!! PLEASE HELP!

extends Control

func _fixed_process(delta):

	var modetext = "Mode:\n"

	if(OS.is_fullscreen()):
		modetext += "Fullscreen\n"
	else:
		modetext += "Windowed\n"

	if(!OS.is_resizable()):
		modetext += "FixedSize\n"

	if(OS.is_minimized()):
		modetext += "Minimized\n"

	if(OS.is_maximized()):
		modetext += "Maximized\n"

	if(Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED):
		modetext += "MouseGrab\n"
		get_node("Label_MouseGrab_KeyInfo").show()
	else:
		get_node("Label_MouseGrab_KeyInfo").hide()

	get_node("Label_Mode").set_text(modetext)

	get_node("Label_Position").set_text( str("Position:\n", OS.get_window_position() ) )

	get_node("Label_Size").set_text(str("Size:\n", OS.get_window_size() ) )

	get_node("Label_MousePosition").set_text(str("Mouse Position:\n", Input.get_mouse_pos() ) )

	get_node("Label_Screen_Count").set_text( str("Screen_Count:\n", OS.get_screen_count() ) )

	get_node("Label_Screen_Current").set_text( str("Screen:\n", OS.get_current_screen() ) )

	get_node("Label_Screen0_Resolution").set_text( str("Screen0 Resolution:\n", OS.get_screen_size() ) )

	get_node("Label_Screen0_Position").set_text(str("Screen0 Position:\n",OS.get_screen_position() ) )

	if(OS.get_screen_count() > 1):
		get_node("Button_Screen0").show()
		get_node("Button_Screen1").show()
		get_node("Label_Screen1_Resolution").show()
		get_node("Label_Screen1_Position").show()
		get_node("Label_Screen1_Resolution").set_text( str("Screen1 Resolution:\n", OS.get_screen_size(1) ) )
		get_node("Label_Screen1_Position").set_text( str("Screen1 Position:\n", OS.get_screen_position(1) ) )
	else:
		get_node("Button_Screen0").hide()
		get_node("Button_Screen1").hide()
		get_node("Label_Screen1_Resolution").hide()
		get_node("Label_Screen1_Position").hide()

	get_node("Button_Fullscreen").set_pressed( OS.is_window_fullscreen() )
	get_node("Button_FixedSize").set_pressed( !OS.is_is_window_resizable() )
	get_node("Button_Minimized").set_pressed( OS.is_is_window_minimized() )
	get_node("Button_Maximized").set_pressed( OS.is_is_window_maximized() )
	get_node("Button_Mouse_Grab").set_pressed( Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED )

func check_wm_api():
	var s = ""
	if( !OS.has_method("get_screen_count") ):
		s += " - get_screen_count()\n"

	if( !OS.has_method("get_screen") ):
		s += " - get_screen()\n"

	if( !OS.has_method("set_screen") ):
		s += " - set_screen()\n"

	if( !OS.has_method("get_screen_position") ):
		s += " - get_screen_position()\n"

	if( !OS.has_method("get_screen_size") ):
		s += " - get_screen_size()\n"

	if( !OS.has_method("get_window_position") ):
		s += " - get_window_position()\n"

	if( !OS.has_method("set_window_position") ):
		s += " - set_window_position()\n"

	if( !OS.has_method("get_window_size") ):
		s += " - get_window_size()\n"

	if( !OS.has_method("set_window_size") ):
		s += " - set_window_size()\n"

	if( !OS.has_method("set_fullscreen") ):
		s += " - set_fullscreen()\n"

	if( !OS.has_method("is_fullscreen") ):
		s += " - is_fullscreen()\n"

	if( !OS.has_method("set_resizable") ):
		s += " - set_resizable()\n"

	if( !OS.has_method("is_resizable") ):
		s += " - is_resizable()\n"

	if( !OS.has_method("set_minimized") ):
		s += " - set_minimized()\n"

	if( !OS.has_method("is_minimized") ):
		s += " - is_minimized()\n"

	if( !OS.has_method("set_maximized") ):
		s += " - set_maximized()\n"

	if( !OS.has_method("is_maximized") ):
		s += " - is_maximized()\n"

	if( s.length() == 0 ):
		return true
	else:
		var text = get_node("ImplementationDialog/Text").get_text()
		get_node("ImplementationDialog/Text").set_text( text + s )
		get_node("ImplementationDialog").show()
		return false

func _ready():
	if( check_wm_api() ):
		set_fixed_process(true)

func _on_Button_MoveTo_pressed():
	OS.set_window_position( Vector2(100,100) )

func _on_Button_Resize_pressed():
	OS.set_window_size( Vector2(1024,768) )

func _on_Button_Screen0_pressed():
	OS.set_current_screen(0)

func _on_Button_Screen1_pressed():
	OS.set_current_screen(1)

func _on_Button_Fullscreen_pressed():
	if(OS.is_window_fullscreen()):
		OS.set_window_fullscreen(false)
	else:
		OS.set_window_fullscreen(true)

func _on_Button_FixedSize_pressed():
	if(OS.is_window_resizable()):
		OS.set_window_resizable(false)
	else:
		OS.set_window_resizable(true)

func _on_Button_Minimized_pressed():
	if(OS.is_window_minimized()):
		OS.set_window_minimized(false)
	else:
		OS.set_window_minimized(true)

func _on_Button_Maximized_pressed():
	if(OS.is_window_maximized()):
		OS.set_window_maximized(false)
	else:
		OS.set_window_maximized(true)

func _on_Button_Mouse_Grab_pressed():
	var observer = get_node("../Observer")
	observer.state = observer.STATE_GRAB

extends Control

func _fixed_process(delta):

	var modetext = "Mode:\n"

	if(OS.is_fullscreen()):
		modetext += "Fullscreen\n"
	else:
		modetext += "Windowed\n"

	if(!OS.is_resizable()):
		modetext += "FixedSize\n"

	if(OS.is_minimized()):
		modetext += "Minimized\n"

	if(OS.is_maximized()):
		modetext += "Maximized\n"

	if(Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED):
		modetext += "MouseGrab\n"
		get_node("Label_MouseGrab_KeyInfo").show()
	else:
		get_node("Label_MouseGrab_KeyInfo").hide()

	get_node("Label_Mode").set_text(modetext)

	get_node("Label_Position").set_text( str("Position:\n", OS.get_window_position() ) )

	get_node("Label_Size").set_text(str("Size:\n", OS.get_window_size() ) )

	get_node("Label_MousePosition").set_text(str("Mouse Position:\n", Input.get_mouse_pos() ) )

	get_node("Label_Screen_Count").set_text( str("Screen_Count:\n", OS.get_screen_count() ) )

	get_node("Label_Screen_Current").set_text( str("Screen:\n", OS.get_screen() ) )

	get_node("Label_Screen0_Resolution").set_text( str("Screen0 Resolution:\n", OS.get_screen_size() ) )

	get_node("Label_Screen0_Position").set_text(str("Screen0 Position:\n",OS.get_screen_position() ) )

	if(OS.get_screen_count() > 1):
		get_node("Button_Screen0").show()
		get_node("Button_Screen1").show()
		get_node("Label_Screen1_Resolution").show()
		get_node("Label_Screen1_Position").show()
		get_node("Label_Screen1_Resolution").set_text( str("Screen1 Resolution:\n", OS.get_screen_size(1) ) )
		get_node("Label_Screen1_Position").set_text( str("Screen1 Position:\n", OS.get_screen_position(1) ) )
	else:
		get_node("Button_Screen0").hide()
		get_node("Button_Screen1").hide()
		get_node("Label_Screen1_Resolution").hide()
		get_node("Label_Screen1_Position").hide()

	get_node("Button_Fullscreen").set_pressed( OS.is_fullscreen() )
	get_node("Button_FixedSize").set_pressed( !OS.is_resizable() )
	get_node("Button_Minimized").set_pressed( OS.is_minimized() )
	get_node("Button_Maximized").set_pressed( OS.is_maximized() )
	get_node("Button_Mouse_Grab").set_pressed( Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED )

func check_wm_api():
	var s = ""
	if( !OS.has_method("get_screen_count") ):
		s += " - get_screen_count()\n"

	if( !OS.has_method("get_screen") ):
		s += " - get_screen()\n"

	if( !OS.has_method("set_screen") ):
		s += " - set_screen()\n"

	if( !OS.has_method("get_screen_position") ):
		s += " - get_screen_position()\n"

	if( !OS.has_method("get_screen_size") ):
		s += " - get_screen_size()\n"

	if( !OS.has_method("get_window_position") ):
		s += " - get_window_position()\n"

	if( !OS.has_method("set_window_position") ):
		s += " - set_window_position()\n"

	if( !OS.has_method("get_window_size") ):
		s += " - get_window_size()\n"

	if( !OS.has_method("set_window_size") ):
		s += " - set_window_size()\n"

	if( !OS.has_method("set_fullscreen") ):
		s += " - set_fullscreen()\n"

	if( !OS.has_method("is_fullscreen") ):
		s += " - is_fullscreen()\n"

	if( !OS.has_method("set_resizable") ):
		s += " - set_resizable()\n"

	if( !OS.has_method("is_resizable") ):
		s += " - is_resizable()\n"

	if( !OS.has_method("set_minimized") ):
		s += " - set_minimized()\n"

	if( !OS.has_method("is_minimized") ):
		s += " - is_minimized()\n"

	if( !OS.has_method("set_maximized") ):
		s += " - set_maximized()\n"

	if( !OS.has_method("is_maximized") ):
		s += " - is_maximized()\n"

	if( s.length() == 0 ):
		return true
	else:
		var text = get_node("ImplementationDialog/Text").get_text()
		get_node("ImplementationDialog/Text").set_text( text + s )
		get_node("ImplementationDialog").show()
		return false

func _ready():
	if( check_wm_api() ):
		set_fixed_process(true)

func _on_Button_MoveTo_pressed():
	OS.set_window_position( Vector2(100,100) )

func _on_Button_Resize_pressed():
	OS.set_window_size( Vector2(1024,768) )

func _on_Button_Screen0_pressed():
	OS.set_screen(0)

func _on_Button_Screen1_pressed():
	OS.set_screen(1)

func _on_Button_Fullscreen_pressed():
	if(OS.is_fullscreen()):
		OS.set_fullscreen(false)
	else:
		OS.set_fullscreen(true)

func _on_Button_FixedSize_pressed():
	if(OS.is_resizable()):
		OS.set_resizable(false)
	else:
		OS.set_resizable(true)

func _on_Button_Minimized_pressed():
	if(OS.is_minimized()):
		OS.set_minimized(false)
	else:
		OS.set_minimized(true)

func _on_Button_Maximized_pressed():
	if(OS.is_maximized()):
		OS.set_maximized(false)
	else:
		OS.set_maximized(true)

func _on_Button_Mouse_Grab_pressed():
	var observer = get_node("../Observer")
	observer.state = observer.STATE_GRAB


Player damage color, invincibility timer

# 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

# 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

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

	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):
	health -= hp
	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


Fix simple multiplayer demo for 3+ players

Properly send newly connected dudes to old players

extends Node

# Default game port
const DEFAULT_PORT = 10567

# Max number of players
const MAX_PEERS = 12

# Name for my player
var player_name = "The Warrior"

# Names for remote players in id:name format
var players = {}

# Signals to let lobby GUI know what's going on
signal player_list_changed()
signal connection_failed()
signal connection_succeeded()
signal game_ended()
signal game_error(what)

# Callback from SceneTree
func _player_connected(id):
	# This is not used in this demo, because _connected_ok is called for clients
	# on success and will do the job.
	pass

# Callback from SceneTree
func _player_disconnected(id):
	if (get_tree().is_network_server()):
		if (has_node("/root/world")): # Game is in progress
			emit_signal("game_error", "Player " + players[id] + " disconnected")
			end_game()
		else: # Game is not in progress
			# If we are the server, send to the new dude all the already registered players
			unregister_player(id)
			for p_id in players:
				# Erase in the server
				rpc_id(p_id, "unregister_player", id)

# Callback from SceneTree, only for clients (not server)
func _connected_ok():
	# Registration of a client beings here, tell everyone that we are here
	rpc("register_player", get_tree().get_network_unique_id(), player_name)
	emit_signal("connection_succeeded")

# Callback from SceneTree, only for clients (not server)
func _server_disconnected():
	emit_signal("game_error", "Server disconnected")
	end_game()

# Callback from SceneTree, only for clients (not server)
func _connected_fail():
	get_tree().set_network_peer(null) # Remove peer
	emit_signal("connection_failed")

# Lobby management functions

remote func register_player(id, name):
	if (get_tree().is_network_server()):
		# If we are the server, let everyone know about the new player
		rpc_id(id, "register_player", 1, player_name) # Send myself to new dude
		for p_id in players: # Then, for each remote player
			rpc_id(id, "register_player", p_id, players[p_id]) # Send player to new dude
			rpc_id(p_id, "register_player", id, name) # Send new dude to player

	players[id] = name
	emit_signal("player_list_changed")

remote func unregister_player(id):
	players.erase(id)
	emit_signal("player_list_changed")

remote func pre_start_game(spawn_points):
	# Change scene
	var world = load("res://world.tscn").instance()
	get_tree().get_root().add_child(world)

	get_tree().get_root().get_node("lobby").hide()

	var player_scene = load("res://player.tscn")

	for p_id in spawn_points:
		var spawn_pos = world.get_node("spawn_points/" + str(spawn_points[p_id])).get_pos()
		var player = player_scene.instance()

		player.set_name(str(p_id)) # Use unique ID as node name
		player.set_pos(spawn_pos)

		if (p_id == get_tree().get_network_unique_id()):
			# If node for this peer id, set master
			player.set_network_mode(NETWORK_MODE_MASTER)
			player.set_player_name(player_name)
		else:
			# Otherwise set slave
			player.set_network_mode(NETWORK_MODE_SLAVE)
			player.set_player_name(players[p_id])

		world.get_node("players").add_child(player)

	# Set up score
	world.get_node("score").add_player(get_tree().get_network_unique_id(), player_name)
	for pn in players:
		world.get_node("score").add_player(pn, players[pn])

	if (not get_tree().is_network_server()):
		# Tell server we are ready to start
		rpc_id(1, "ready_to_start", get_tree().get_network_unique_id())
	elif players.size() == 0:
		post_start_game()

remote func post_start_game():
	get_tree().set_pause(false) # Unpause and unleash the game!

var players_ready = []

remote func ready_to_start(id):
	assert(get_tree().is_network_server())

	if (not id in players_ready):
		players_ready.append(id)

	if (players_ready.size() == players.size()):
		for p in players:
			rpc_id(p, "post_start_game")
		post_start_game()

func host_game(name):
	player_name = name
	var host = NetworkedMultiplayerENet.new()
	host.create_server(DEFAULT_PORT, MAX_PEERS)
	get_tree().set_network_peer(host)

func join_game(ip, name):
	player_name = name
	var host = NetworkedMultiplayerENet.new()
	host.create_client(ip, DEFAULT_PORT)
	get_tree().set_network_peer(host)

func get_player_list():
	return players.values()

func get_player_name():
	return player_name

func begin_game():
	assert(get_tree().is_network_server())

	# Create a dictionary with peer id and respective spawn points, could be improved by randomizing
	var spawn_points = {}
	spawn_points[1] = 0 # Server in spawn point 0
	var spawn_point_idx = 1
	for p in players:
		spawn_points[p] = spawn_point_idx
		spawn_point_idx += 1
	# Call to pre-start game with the spawn points
	for p in players:
		rpc_id(p, "pre_start_game", spawn_points)

	pre_start_game(spawn_points)

func end_game():
	if (has_node("/root/world")): # Game is in progress
		# End it
		get_node("/root/world").queue_free()

	emit_signal("game_ended")
	players.clear()
	get_tree().set_network_peer(null) # End networking

func _ready():
	get_tree().connect("network_peer_connected", self, "_player_connected")
	get_tree().connect("network_peer_disconnected", self,"_player_disconnected")
	get_tree().connect("connected_to_server", self, "_connected_ok")
	get_tree().connect("connection_failed", self, "_connected_fail")
	get_tree().connect("server_disconnected", self, "_server_disconnected")

extends Node

# Default game port
const DEFAULT_PORT = 10567

# Max number of players
const MAX_PEERS = 12

# Name for my player
var player_name = "The Warrior"

# Names for remote players in id:name format
var players = {}

# Signals to let lobby GUI know what's going on
signal player_list_changed()
signal connection_failed()
signal connection_succeeded()
signal game_ended()
signal game_error(what)

# Callback from SceneTree
func _player_connected(id):
	# This is not used in this demo, because _connected_ok is called for clients
	# on success and will do the job.
	pass

# Callback from SceneTree
func _player_disconnected(id):
	if (get_tree().is_network_server()):
		if (has_node("/root/world")): # Game is in progress
			emit_signal("game_error", "Player " + players[id] + " disconnected")
			end_game()
		else: # Game is not in progress
			# If we are the server, send to the new dude all the already registered players
			unregister_player(id)
			for p_id in players:
				# Erase in the server
				rpc_id(p_id, "unregister_player", id)

# Callback from SceneTree, only for clients (not server)
func _connected_ok():
	# Registration of a client beings here, tell everyone that we are here
	rpc("register_player", get_tree().get_network_unique_id(), player_name)
	emit_signal("connection_succeeded")

# Callback from SceneTree, only for clients (not server)
func _server_disconnected():
	emit_signal("game_error", "Server disconnected")
	end_game()

# Callback from SceneTree, only for clients (not server)
func _connected_fail():
	get_tree().set_network_peer(null) # Remove peer
	emit_signal("connection_failed")

# Lobby management functions

remote func register_player(id, name):
	if (get_tree().is_network_server()):
		# If we are the server, let everyone know about the new player
		rpc_id(id, "register_player", 1, player_name) # Send myself to new dude
		for p_id in players: # Then, for each remote player
			rpc_id(id, "register_player", p_id, players[p_id]) # Send player to new dude

	players[id] = name
	emit_signal("player_list_changed")

remote func unregister_player(id):
	players.erase(id)
	emit_signal("player_list_changed")

remote func pre_start_game(spawn_points):
	# Change scene
	var world = load("res://world.tscn").instance()
	get_tree().get_root().add_child(world)

	get_tree().get_root().get_node("lobby").hide()

	var player_scene = load("res://player.tscn")

	for p_id in spawn_points:
		var spawn_pos = world.get_node("spawn_points/" + str(spawn_points[p_id])).get_pos()
		var player = player_scene.instance()

		player.set_name(str(p_id)) # Use unique ID as node name
		player.set_pos(spawn_pos)

		if (p_id == get_tree().get_network_unique_id()):
			# If node for this peer id, set master
			player.set_network_mode(NETWORK_MODE_MASTER)
			player.set_player_name(player_name)
		else:
			# Otherwise set slave
			player.set_network_mode(NETWORK_MODE_SLAVE)
			player.set_player_name(players[p_id])

		world.get_node("players").add_child(player)

	# Set up score
	world.get_node("score").add_player(get_tree().get_network_unique_id(), player_name)
	for pn in players:
		world.get_node("score").add_player(pn, players[pn])

	if (not get_tree().is_network_server()):
		# Tell server we are ready to start
		rpc_id(1, "ready_to_start", get_tree().get_network_unique_id())
	elif players.size() == 0:
		post_start_game()

remote func post_start_game():
	get_tree().set_pause(false) # Unpause and unleash the game!

var players_ready = []

remote func ready_to_start(id):
	assert(get_tree().is_network_server())

	if (not id in players_ready):
		players_ready.append(id)

	if (players_ready.size() == players.size()):
		for p in players:
			rpc_id(p, "post_start_game")
		post_start_game()

func host_game(name):
	player_name = name
	var host = NetworkedMultiplayerENet.new()
	host.create_server(DEFAULT_PORT, MAX_PEERS)
	get_tree().set_network_peer(host)

func join_game(ip, name):
	player_name = name
	var host = NetworkedMultiplayerENet.new()
	host.create_client(ip, DEFAULT_PORT)
	get_tree().set_network_peer(host)

func get_player_list():
	return players.values()

func get_player_name():
	return player_name

func begin_game():
	assert(get_tree().is_network_server())

	# Create a dictionary with peer id and respective spawn points, could be improved by randomizing
	var spawn_points = {}
	spawn_points[1] = 0 # Server in spawn point 0
	var spawn_point_idx = 1
	for p in players:
		spawn_points[p] = spawn_point_idx
		spawn_point_idx += 1
	# Call to pre-start game with the spawn points
	for p in players:
		rpc_id(p, "pre_start_game", spawn_points)

	pre_start_game(spawn_points)

func end_game():
	if (has_node("/root/world")): # Game is in progress
		# End it
		get_node("/root/world").queue_free()

	emit_signal("game_ended")
	players.clear()
	get_tree().set_network_peer(null) # End networking

func _ready():
	get_tree().connect("network_peer_connected", self, "_player_connected")
	get_tree().connect("network_peer_disconnected", self,"_player_disconnected")
	get_tree().connect("connected_to_server", self, "_connected_ok")
	get_tree().connect("connection_failed", self, "_connected_fail")
	get_tree().connect("server_disconnected", self, "_server_disconnected")


Fixed small typos

Fixed small typos

extends HBoxContainer

const RECT_WIDTH = 20
const RECT_HEIGHT = 20

var rects_life = [
	Rect2(0, 0, RECT_WIDTH * 0, RECT_HEIGHT),
	Rect2(0, 0, RECT_WIDTH * 1, RECT_HEIGHT),
	Rect2(0, 0, RECT_WIDTH * 2, RECT_HEIGHT),
	Rect2(0, 0, RECT_WIDTH * 3, RECT_HEIGHT)
]

var current_life = 0
var current_rect_life = 0

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

func _ready():
	set_process(true)

func _process(delta):
	current_life = _health.get_current_life()
	current_rect_life = rects_life[ current_life ]
	_life_sprite.set_region_rect(current_rect_life)

extends HBoxContainer

const RECT_WIDTH = 20
const RECT_HEIGHT = 20

var rects_life = [
		Rect2(0, 0, RECT_WIDTH * 0, RECT_HEIGHT),
		Rect2(0, 0, RECT_WIDTH * 1, RECT_HEIGHT),
		Rect2(0, 0, RECT_WIDTH * 2, RECT_HEIGHT),
		Rect2(0, 0, RECT_WIDTH * 3, RECT_HEIGHT)
	]

var current_life = 0
var current_rect_life = 0

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

func _ready():
	set_process(true)

func _process(delta):
	current_life = _health.get_current_life()
	current_rect_life = rects_life[current_life]
	_life_sprite.set_region_rect(current_rect_life)


Added simple check to make sure networking is needed on creation of a puzzle scene

# 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 otherPuzzle
var mainPuzzle = true

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

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
	puzzleMan = PuzzleManScript.new()
	var puzzle = puzzleMan.generatePuzzle( 2, puzzleMan.DIFF_EASY )
	puzzle.puzzleMan = puzzleMan

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

	print("Generated ", puzzle.blocks.size(), " blocks." )

	print( "Saving..." )
	DataMan.savePuzzle( "TestPuzzle.pzl", puzzle )

	puzzle = 0

	puzzle = DataMan.loadPuzzle( "TestPuzzle.pzl" )

	var steps = puzzle.solvePuzzleSteps()
	print( "PUZZLE IS SOLVEABLE?: ", steps.solveable )

	addTimer()

	# Place the blocks in the puzzle.
	var gridMan = get_node( "GridView/GridMan" )
	gridMan.shape = puzzleMan.shape
	gridMan.set_puzzle(puzzle)
	for block in puzzle.blocks:
		# Create a block node, add it to the tree
		gridMan.add_block(block.toNode())

	# make a new puzzle, embed using Viewport
	if mainPuzzle:
		var p = PuzzleScn.instance()
		p.remove_and_delete_child(p.get_node("Lights"))
		p.get_node("GridView").active = false
		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_rect(Rect2(0, 0, 100, 100))
		v.set_physics_object_picking(false)
		get_node("Camera").add_child(p)
		v.add_child(p)
		add_child(c)
		c.add_child(v)
		otherPuzzle = p #for use with network
# child of control? easier input

# 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 otherPuzzle
var mainPuzzle = true

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

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
	puzzleMan = PuzzleManScript.new()
	var puzzle = puzzleMan.generatePuzzle( 2, puzzleMan.DIFF_EASY )
	puzzle.puzzleMan = puzzleMan

	#set up network stuffs
	add_child(load("res://networkProxy.scn").instance())
	Globals.get("Network").proxy.set_process(true)

	print("Generated ", puzzle.blocks.size(), " blocks." )

	print( "Saving..." )
	DataMan.savePuzzle( "TestPuzzle.pzl", puzzle )

	puzzle = 0

	puzzle = DataMan.loadPuzzle( "TestPuzzle.pzl" )

	var steps = puzzle.solvePuzzleSteps()
	print( "PUZZLE IS SOLVEABLE?: ", steps.solveable )

	addTimer()

	# Place the blocks in the puzzle.
	var gridMan = get_node( "GridView/GridMan" )
	gridMan.shape = puzzleMan.shape
	gridMan.set_puzzle(puzzle)
	for block in puzzle.blocks:
		# Create a block node, add it to the tree
		gridMan.add_block(block.toNode())

	# make a new puzzle, embed using Viewport
	if mainPuzzle:
		var p = PuzzleScn.instance()
		p.remove_and_delete_child(p.get_node("Lights"))
		p.get_node("GridView").active = false
		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_rect(Rect2(0, 0, 100, 100))
		v.set_physics_object_picking(false)
		get_node("Camera").add_child(p)
		v.add_child(p)
		add_child(c)
		c.add_child(v)
		otherPuzzle = p #for use with network
# child of control? easier input