Update cubio.gd

Update cubio.gd

a little wrong only.

extends KinematicBody

# member variables here, example:
# var a=2
# var b="textvar"

var g = -9.8
var vel = Vector3()
const MAX_SPEED = 5
const JUMP_SPEED = 7
const ACCEL= 2
const DEACCEL= 4
const MAX_SLOPE_ANGLE = 30

func _fixed_process(delta):

	var dir = Vector3() #where does the player intend to walk to
	var cam_xform = get_node("target/camera").get_global_transform()

	if (Input.is_action_pressed("move_forward")):
		dir+=-cam_xform.basis[2]
	if (Input.is_action_pressed("move_backwards")):
		dir+=cam_xform.basis[2]
	if (Input.is_action_pressed("move_left")):
		dir+=-cam_xform.basis[0]
	if (Input.is_action_pressed("move_right")):
		dir+=cam_xform.basis[0]

	dir.y=0
	dir=dir.normalized()

	vel.y+=delta*g

	var hvel = vel
	hvel.y=0

	var target = dir*MAX_SPEED
	var accel
	if (dir.dot(hvel) >0):
		accel=ACCEL
	else:
		accel=DEACCEL

	hvel = hvel.linear_interpolate(target,accel*delta)

	vel.x=hvel.x;
	vel.z=hvel.z

	var motion = vel*delta
	motion=move(vel*delta)

	var on_floor = false
	var original_vel = vel

	var floor_velocity=Vector3()

	var attempts=4

	while(is_colliding() and attempts):
		var n=get_collision_normal()

		if ( rad2deg(acos(n.dot( Vector3(0,1,0)))) < MAX_SLOPE_ANGLE ):
				#if angle to the "up" vectors is < angle tolerance
				#char is on floor
				floor_velocity=get_collider_velocity()
				on_floor=true

		motion = n.slide(motion)
		vel = n.slide(vel)
		if (original_vel.dot(vel) > 0):
			#do not allow to slide towads the opposite direction we were coming from
			motion=move(motion)
			if (motion.length()<0.001):
				break
		attempts-=1

	if (on_floor and floor_velocity!=Vector3()):
			move(floor_velocity*delta)

	if (on_floor and Input.is_action_pressed("jump")):
		vel.y=JUMP_SPEED

	var crid = get_node("../elevator1").get_rid()
#	print(crid," : ",PS.body_get_state(crid,PS.BODY_STATE_TRANSFORM))

func _ready():
	# Initalization here
	set_fixed_process(true)
	pass

func _on_tcube_body_enter( body ):
	get_node("../ty").show()
	pass # replace with function body

extends KinematicBody

# member variables here, example:
# var a=2
# var b="textvar"

var g = -9.8
var vel = Vector3()
const MAX_SPEED = 5
const JUMP_SPEED = 7
const ACCEL= 2
const DEACCEL= 4
const MAX_SLOPE_ANGLE = 30

func _fixed_process(delta):

	var dir = Vector3() #where does the player intend to walk to
	var cam_xform = get_node("target/camera").get_global_transform()

	if (Input.is_action_pressed("move_forward")):
		dir+=-cam_xform.basis[2]
	if (Input.is_action_pressed("move_backwards")):
		dir+=cam_xform.basis[2]
	if (Input.is_action_pressed("move_left")):
		dir+=-cam_xform.basis[0]
	if (Input.is_action_pressed("move_right")):
		dir+=cam_xform.basis[0]

	dir.y=0
	dir=dir.normalized()

	vel.y+=delta*g

	var hvel = vel
	hvel.y=0

	var target = dir*MAX_SPEED
	var accel
	if (dir.dot(hvel) >0):
		accel=ACCEL
	else:
		accel=DEACCEL

	hvel = hvel.linear_interpolate(target,accel*delta)

	vel.x=hvel.x;
	vel.z=hvel.z

	var motion = vel*delta
	motion=move(vel*delta)

	var on_floor = false
	var original_vel = vel

	var floor_velocity=Vector2()

	var attempts=4

	while(is_colliding() and attempts):
		var n=get_collision_normal()

		if ( rad2deg(acos(n.dot( Vector3(0,1,0)))) < MAX_SLOPE_ANGLE ):
				#if angle to the "up" vectors is < angle tolerance
				#char is on floor
				floor_velocity=get_collider_velocity()
				on_floor=true

		motion = n.slide(motion)
		vel = n.slide(vel)
		if (original_vel.dot(vel) > 0):
			#do not allow to slide towads the opposite direction we were coming from
			motion=move(motion)
			if (motion.length()<0.001):
				break
		attempts-=1

	if (on_floor and floor_velocity!=Vector3()):
			move(floor_velocity*delta)

	if (on_floor and Input.is_action_pressed("jump")):
		vel.y=JUMP_SPEED

	var crid = get_node("../elevator1").get_rid()
#	print(crid," : ",PS.body_get_state(crid,PS.BODY_STATE_TRANSFORM))

func _ready():
	# Initalization here
	set_fixed_process(true)
	pass

func _on_tcube_body_enter( body ):
	get_node("../ty").show()
	pass # replace with function body


sfx node disabled

sfx node disabled

extends CanvasLayer

export (NodePath) var collected_text #= get_node("hudframe/items_label/score_display")
export (NodePath) var score_text #final_score_text = get_node("hudframe/finalscore_label/final_scoredisplay")
export (NodePath) var highscore_text #= get_node("hudframe/highscore_label/highscore_display")
export (NodePath) var sound_off_button #= get_node("hudframe/sound_off")

onready var animations = get_node("animations")

func _on_met_yan():
#	get_node("sfx").play("card_unlock")
	animations.play("yan_unlock_anim")

func _ready():
	var yan = get_parent().find_node("Yan")
	print (yan.get_name())
	if yan:
		print("Yan is present")
		yan.connect("met_yan", self,"_on_met_yan")
		animations.play("yan_unlock_anim")
		print("working")

	update_scores()
	game.connect("scores_changed", self, "update_scores")
	update_sound_hud()

# Update scores
func update_scores():
	get_node(collected_text).set_text(str(game.items_collected))
	get_node(score_text).set_text(str(game.score))
	get_node(highscore_text).set_text(str(game.high_score))

func _on_go_to_menu_pressed():
	transition.fade_to("res://src/screens/menu/menu.tscn")

# Toggles music on/off while keeping the stored volume that may have been set elsewhere
func _on_sound_off_pressed():
	# Turns off music completely, or returns it back to normal
	if(global.music.enabled):
		AudioServer.set_stream_global_volume_scale(0)
	else:
		AudioServer.set_stream_global_volume_scale(global.music.volume)

	# Toggle bool
	global.music.enabled = !global.music.enabled

	# Update sound HUD
	update_sound_hud()

# Updates sound HUD
func update_sound_hud():
	if(global.music.enabled):
		get_node(sound_off_button).set_pressed(true) # res://src/screens/hud/sound_on.png
	else:
		get_node(sound_off_button).set_pressed(false) # res://src/screens/hud/sound_off.png

extends CanvasLayer

export (NodePath) var collected_text #= get_node("hudframe/items_label/score_display")
export (NodePath) var score_text #final_score_text = get_node("hudframe/finalscore_label/final_scoredisplay")
export (NodePath) var highscore_text #= get_node("hudframe/highscore_label/highscore_display")
export (NodePath) var sound_off_button #= get_node("hudframe/sound_off")

onready var animations = get_node("animations")

func _on_met_yan():
	get_node("sfx").play("card_unlock")
	animations.play("yan_unlock_anim")

func _ready():
	var yan = get_parent().find_node("Yan")
	print (yan.get_name())
	if yan:
		print("Yan is present")
		yan.connect("met_yan", self,"_on_met_yan")
		animations.play("yan_unlock_anim")
		print("working")

	update_scores()
	game.connect("scores_changed", self, "update_scores")
	update_sound_hud()

# Update scores
func update_scores():
	get_node(collected_text).set_text(str(game.items_collected))
	get_node(score_text).set_text(str(game.score))
	get_node(highscore_text).set_text(str(game.high_score))

func _on_go_to_menu_pressed():
	transition.fade_to("res://src/screens/menu/menu.tscn")

# Toggles music on/off while keeping the stored volume that may have been set elsewhere
func _on_sound_off_pressed():
	# Turns off music completely, or returns it back to normal
	if(global.music.enabled):
		AudioServer.set_stream_global_volume_scale(0)
	else:
		AudioServer.set_stream_global_volume_scale(global.music.volume)

	# Toggle bool
	global.music.enabled = !global.music.enabled

	# Update sound HUD
	update_sound_hud()

# Updates sound HUD
func update_sound_hud():
	if(global.music.enabled):
		get_node(sound_off_button).set_pressed(true) # res://src/screens/hud/sound_on.png
	else:
		get_node(sound_off_button).set_pressed(false) # res://src/screens/hud/sound_off.png


Mouse control mode (semi broken)

Mouse control mode (semi broken)

# PLAYER class
# Moves n shoots

extends Node2D

const SPEED = 420 / 1.42
const ACCEL = 3000

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

const MAX_HEALTH = 100
var health = MAX_HEALTH

var inv_time = 0

var col = Color(1,1,1)

func _ready():
	set_process(true)
	set_process_input(true)

func _input(event):
	# Input method switching
	if event.type == InputEvent.KEY:
		if event.scancode == KEY_M && event.pressed == true:
			use_mouse = !use_mouse

func _process(delta):
	# Input gathering
	var input_vec = Vector2(0,0)
	# Keyboard
	if !use_mouse:
		if Input.is_key_pressed(KEY_LEFT):
			input_vec += Vector2(-1,0)
		if Input.is_key_pressed(KEY_RIGHT):
			input_vec += Vector2(1,0)
		if Input.is_key_pressed(KEY_UP):
			input_vec += Vector2(0,-1)
		if Input.is_key_pressed(KEY_DOWN):
			input_vec += Vector2(0,1)
	# Mouse
	else:
		var mouse_pos = get_viewport().get_mouse_pos()

		var relative_pos = mouse_pos - get_global_pos()

		if relative_pos.length() > velocity.length():
			input_vec = relative_pos.normalized()

	# Normal movement
	if !polar_controls:
		var target_velocity = input_vec.normalized() * SPEED

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

		velocity += towards_target
		translate(velocity * delta)

	#TODO: Polar movement
				#if polar_controls:
				#angle += (delta / dist) * SPEED

				#if polar_controls:
				#angle -= (delta / dist) * SPEED

				#if polar_controls:
				#dist -= delta * SPEED
				#if dist < 0.1: dist = 0.1

				#if polar_controls:
				#dist += delta * SPEED

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

	var center = Vector2(720/2,720/2)
	var towards_center =  (center - get_pos()).normalized()

	# Rotate towards center
	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)

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

	# Timers
	if inv_time > 0:
		inv_time -= delta

	if fire_timer > 0:
		fire_timer -= delta

	if shield_cooldown > 0:
		shield_cooldown -= delta

	# Shooting
	var shooting = false
	if use_mouse:
		shooting = Input.is_mouse_button_pressed(BUTTON_LEFT)
	else:
		shooting = Input.is_key_pressed(KEY_SPACE) || Input.is_key_pressed(KEY_F)

	if shooting && 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

	# Shield
	var shielding = false
	if use_mouse:
		shielding = Input.is_mouse_button_pressed(BUTTON_RIGHT)
	else:
		shielding = Input.is_key_pressed(KEY_D)

	if shielding && 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

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
const ACCEL = 3000

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

const MAX_HEALTH = 100
var health = MAX_HEALTH

var inv_time = 0

var col = Color(1,1,1)

func _ready():
	set_process(true)

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

	target_velocity = target_velocity.normalized() * SPEED

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

	velocity += towards_target
	translate(velocity * delta)

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

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

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

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

	if inv_time > 0:
		inv_time -= delta

	if fire_timer > 0:
		fire_timer -= delta

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

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

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

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

	if shield_cooldown > 0:
		shield_cooldown -= delta

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

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

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

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

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

func _on_RegularPolygon_area_exit( area ):
	pass


Fix textures locations for rewards

Fix textures locations for rewards

extends Node2D

signal taken

onready var anim = get_node("anim")

func _ready():
	# randomize items texture and animation
	var sprite = get_node("sprite")
	var tex_num = randi()%11+1
	var seek = randi()%4+1
	var tex_name = ("res://src/objects/rewards/" + str(tex_num) + ".tex")
	var tex = load(tex_name)
	anim.seek(seek, false)
	sprite.set_texture(tex)

func take():
	get_node("sfx").play("chime")
	anim.play("taken")
	yield(anim, "finished")
	emit_signal("taken")
	queue_free()

extends Node2D

signal taken

onready var anim = get_node("anim")

func _ready():
	# randomize items texture and animation
	var sprite = get_node("sprite")
	var tex_num = randi()%11+1
	var seek = randi()%4+1
	var tex_name = ("res://res/objects/rewards/" + str(tex_num) + ".tex")
	var tex = load(tex_name)
	anim.seek(seek, false)
	sprite.set_texture(tex)

func take():
	get_node("sfx").play("chime")
	anim.play("taken")
	yield(anim, "finished")
	emit_signal("taken")
	queue_free()


made a change

made a change

doot doot

extends Node

onready var ui = get_node("ui")
onready var game = get_node("ui/Control/VBoxContainer/HBoxContainer/Control/Viewport/game")

# prints cool to the console
func cool():
	print('cool')

# does some rad thing
func rad():
	print("wowzer!")

# All temp stuff for console

func _ready():
	set_process_input(true)

func _input(event):
	if event.is_action_pressed("console"):
		get_node("WindowDialog").popup()
		get_node("WindowDialog/VBoxContainer/LineEdit").grab_focus()

# stupid shit for console (hit ` [tilde])
func _on_LineEdit_text_entered( text ):
	var console = get_node("WindowDialog/VBoxContainer/Label")
	var ret = "Error"
	var a = text.split('.')
	if a != null and a.size() > 1:
		var target = find_node(a[0])
		if target != null:
			var b = a[1].split('(')
			if b != null and b.size() > 0:
				var function = b[0]
				var s = text.find('(')
				if s != -1:
					var args = text.right(s + 1)
					args = args.split(')')
					if args.size() > 0:
						args = args[0].split(',')
						if args.size() == 0:
							if target.has_method(function):
								ret = target.call(function)
						elif args.size() == 1:
							if target.has_method(function):
								ret = target.call(function, args[0])
						elif args.size() == 2:
							if target.has_method(function):
								ret = target.call(function, args[0], args[1])
						elif args.size() == 3:
							if target.has_method(function):
								ret = target.call(function, args[0], args[1], args[2])
						elif args.size() == 4:
							if target.has_method(function):
								ret = target.call(function, args[0], args[1], args[2], args[4])
	if ret == null:
		ret = "null"
	console.set_text(ret + '\n'+ console.get_text())
	get_node("WindowDialog/VBoxContainer/LineEdit").clear()
func _on_LineEdit_text_changed( text ):
	if text == '`':
		get_node("WindowDialog/VBoxContainer/LineEdit").clear()

extends Node

onready var ui = get_node("ui")
onready var game = get_node("ui/Control/VBoxContainer/HBoxContainer/Control/Viewport/game")

# prints cool to the console
func cool():
	print('cool')

# All temp stuff for console

func _ready():
	set_process_input(true)

func _input(event):
	if event.is_action_pressed("console"):
		get_node("WindowDialog").popup()
		get_node("WindowDialog/VBoxContainer/LineEdit").grab_focus()

# stupid shit for console (hit ` [tilde])
func _on_LineEdit_text_entered( text ):
	var console = get_node("WindowDialog/VBoxContainer/Label")
	var ret = "Error"
	var a = text.split('.')
	if a != null and a.size() > 1:
		var target = find_node(a[0])
		if target != null:
			var b = a[1].split('(')
			if b != null and b.size() > 0:
				var function = b[0]
				var s = text.find('(')
				if s != -1:
					var args = text.right(s + 1)
					args = args.split(')')
					if args.size() > 0:
						args = args[0].split(',')
						if args.size() == 0:
							if target.has_method(function):
								ret = target.call(function)
						elif args.size() == 1:
							if target.has_method(function):
								ret = target.call(function, args[0])
						elif args.size() == 2:
							if target.has_method(function):
								ret = target.call(function, args[0], args[1])
						elif args.size() == 3:
							if target.has_method(function):
								ret = target.call(function, args[0], args[1], args[2])
						elif args.size() == 4:
							if target.has_method(function):
								ret = target.call(function, args[0], args[1], args[2], args[4])
	if ret == null:
		ret = "null"
	console.set_text(ret + '\n'+ console.get_text())
	get_node("WindowDialog/VBoxContainer/LineEdit").clear()
func _on_LineEdit_text_changed( text ):
	if text == '`':
		get_node("WindowDialog/VBoxContainer/LineEdit").clear()


made nameToNodeName a static function

made nameToNodeName a static function

extends RigidBody

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

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

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

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

func setBlockLayer(n):
	blockLayer = n
	return self

func getBlockLayer():
	return blockLayer

func setSelected(sel):
	selected = sel
	return self

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

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

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

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

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

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

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

	return tweenNode

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

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

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

extends RigidBody

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

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

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

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

func setBlockLayer(n):
	blockLayer = n
	return self

func getBlockLayer():
	return blockLayer

func setSelected(sel):
	selected = sel
	return self

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

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

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

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

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

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

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

	return tweenNode

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

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

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


Switch the random functions to be static.

#
#
#

static func rand_int_range(from, to, storage, key):
	var delta = to - from
	return from + (rand_int(storage, key) % delta)

static func rand_float_range(from, to, storage, key):
	var delta = to - from
	return from + (rand_float(storage, key) * delta)

static func rand_float(storage, key):
	# Random float value from 0 to 1
	return min(float(rand_int(storage, key)) / 2147483647.0, 1.0)

static func shuffle(list, storage, key):
	# Randomly shuffle the items within the list,
	# and update the storage seed.  Returns the passed-in list.
	var i
	for i in range(0, list.size() - 1):
		list[i] = rand_int_range(i + 1, list.size(), storage, key)
	return list

static func rand_int(storage, key):
	# Random number from 0 to 2147483647
	var seed_val = null
	if storage != null and storage.has(key):
		seed_val = storage[key]
	else:
		seed_val = randf()
	var rv = rand_seed(seed_val)
	if storage != null:
		storage[key] = rv[1]
	return rv[0]

#
#
#

func rand_int_range(from, to, storage, key):
	var delta = to - from
	return from + (rand_int(storage, key) % delta)

func rand_float_range(from, to, storage, key):
	var delta = to - from
	return from + (rand_float(storage, key) * delta)

func rand_float(storage, key):
	# Random float value from 0 to 1
	return min(float(rand_int(storage, key)) / 2147483647.0, 1.0)

func shuffle(list, storage, key):
	# Randomly shuffle the items within the list,
	# and update the storage seed.  Returns the passed-in list.
	var i
	for i in range(0, list.size() - 1):
		list[i] = rand_int_range(i + 1, list.size(), storage, key)
	return list

func rand_int(storage, key):
	# Random number from 0 to 2147483647
	var seed_val = null
	if storage != null and storage.has(key):
		seed_val = storage[key]
	else:
		seed_val = randf()
	var rv = rand_seed(seed_val)
	if storage != null:
		storage[key] = rv[1]
	return rv[0]


added working Viewport code

added working Viewport code

extends Spatial

# proposed script manager:
# var PuzzleManScript = get_node("Globals").get("PuzzleManScript")

var PuzzleManScript = preload( "res://scripts/PuzzleManager.gd" )
var PuzzleScn = preload("res://puzzle.scn")
var puzzleMan
var mainPuzzle = true

# Called for initialization
func _ready():
	puzzleMan = PuzzleManScript.new()
	var puzzle = puzzleMan.generatePuzzle( 2, puzzleMan.DIFF_EASY )

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

	# 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
		var b = block.toNode()
		gridMan.shape[b.blockPos] = b
		gridMan.add_block(b)
		gridMan.get_child(block.name) \
			.set_translation(block.blockPos * 2 )

	if mainPuzzle:
		var p = PuzzleScn.instance()
		p.mainPuzzle = false
		p.set_scale(Vector3(0.5, 0.5, 0.5))
		p.set_translation(Vector3(20, 0, 0))

		var v = Viewport.new()
		v.set_as_render_target(true)
		v.set_world(p.get_world())
		v.set_rect(Rect2(0, 0, 100, 100))
		v.set_physics_object_picking(false)
		v.add_child(p)
		add_child(v)

extends Spatial

# proposed script manager:
# var PuzzleManScript = get_node("Globals").get("PuzzleManScript")

var PuzzleManScript = preload( "res://scripts/PuzzleManager.gd" )
var puzzleMan

# Called for initialization
func _ready():
	puzzleMan = PuzzleManScript.new()
	var puzzle = puzzleMan.generatePuzzle( 2, puzzleMan.DIFF_EASY )

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

	# 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
		var b = block.toNode()
		gridMan.shape[b.blockPos] = b
		gridMan.add_block(b)
		gridMan.get_child(block.name) \
			.set_translation(block.blockPos * 2 )


default config values

default config values

# Saves the config settings to a file.
static func saveConfig( config ):
	var file = File.new()
	file.open( "config.cfg", File.WRITE )

	if file.is_open():
		file.store_var( config.name )
		file.store_var( config.soundvolume )
		file.store_var( config.musicvolume )
		file.store_var( config.portnumber )
		file.close()

# Loads the config settings from a file.
static func loadConfig():
	var file = File.new()
	file.open( "config.cfg", File.READ )

	var config = {}

	if file.is_open():
		config.name = file.get_var()
		config.soundvolume = file.get_var()
		config.musicvolume = file.get_var()
		config.portnumber = file.get_var()
		file.close()
	else:
		config.name = "Testificate"
		config.soundvolume = 0.5
		config.musicvolume = 0.5
		config.portnumber = 54321

	return config

# Saves a puzzle to the file given.
static func savePuzzle( name, puzzle ):
	print( puzzle.puzzleName )
	var file = File.new()
	file.open( name, File.WRITE )

	var di = puzzle.toDict()

	if file.is_open():
		file.store_var( di )
		file.close()

# Loads a puzzle for the file given.
static func loadPuzzle( name ):
	var PuzzleManScript = preload( "res://scripts/PuzzleManager.gd" )

	var file = File.new()
	var puzzleMan = PuzzleManScript.new()
	var puzzle = puzzleMan.Puzzle.new()
	puzzle.puzzleMan = puzzleMan
	file.open( name, File.READ )

	if file.is_open():
		puzzle.fromDict( file.get_var() )
		file.close()

	return puzzle

# Saves the config settings to a file.
static func saveConfig( config ):
	var file = File.new()
	file.open( "config.cfg", File.WRITE )

	if file.is_open():
		file.store_var( config.name )
		file.store_var( config.soundvolume )
		file.store_var( config.musicvolume )
		file.store_var( config.portnumber )
		file.close()

# Loads the config settings from a file.
static func loadConfig():
	var file = File.new()
	file.open( "config.cfg", File.READ )

	var config = {}

	if file.is_open():
		config.name = file.get_var()
		config.soundvolume = file.get_var()
		config.musicvolume = file.get_var()
		config.portnumber = file.get_var()
		file.close()

	return config

# Saves a puzzle to the file given.
static func savePuzzle( name, puzzle ):
	print( puzzle.puzzleName )
	var file = File.new()
	file.open( name, File.WRITE )

	var di = puzzle.toDict()

	if file.is_open():
		file.store_var( di )
		file.close()

# Loads a puzzle for the file given.
static func loadPuzzle( name ):
	var PuzzleManScript = preload( "res://scripts/PuzzleManager.gd" )

	var file = File.new()
	var puzzleMan = PuzzleManScript.new()
	var puzzle = puzzleMan.Puzzle.new()
	puzzle.puzzleMan = puzzleMan
	file.open( name, File.READ )

	if file.is_open():
		puzzle.fromDict( file.get_var() )
		file.close()

	return puzzle


commented out print statements

commented out print statements

const DIFF_EASY		= 0
const DIFF_MEDIUM	= 1
const DIFF_HARD		= 2

const BLOCK_LASER	= 0
const BLOCK_WILD	= 1
const BLOCK_PAIR	= 2
const BLOCK_GOAL	= 3
const BLOCK_BLOCK   = 4

const blockColors = ["Blue", "Orange", "Red", "Yellow", "Purple", "Green"]
const wildColors = ["WildBlue", "WildOrange", "WildRed", "WildYellow", "WildPurple", "WildGreen"]

# Preload paired blocks
const blockScn = preload( "res://blocks/block.scn" )
const blockScripts = [ preload( "Blocks/LaserBlock.gd" )
			   	      , preload( "Blocks/LaserBlock.gd" )
			   	      , preload( "Blocks/PairedBlock.gd" )
			   	      , preload( "Blocks/LaserBlock.gd" )
			   	      ]
const aBlock = preload( "Blocks/AbstractBlock.gd" )

# Hash map of all possible positions
var shape = {}

# Stores a puzzle in a convenient class.
class Puzzle:
	var puzzleName
	var puzzleLayers
	var blocks = []
	var lasers = []

	var puzzleMan

	# Converts a puzzle to a dictionary.
	func toDict():
		var blockArr = []
		for b in range( blocks.size() ):
			blockArr.append( blocks[b].toDict() )

		var di = { pN = puzzleName
		 	     , pL = puzzleLayers
				 , bL = blockArr
			     }
		return di

	# Converts a dictionary to a puzzle.
	func fromDict( di ):
		puzzleName = di.pN
		puzzleLayers = di.pL
		for b in range( di.bL.size() ):
			var nb = puzzleMan.PickledBlock.new()
			nb.fromDict( di.bL[b] )
			blocks.append( nb )
		return

	# Determines if a puzzle is solveable.
	func solvePuzzle():
		# Simply use the solvePuzzleSteps function and return the solveable part.
		var ps = self.solvePuzzleSteps()
		return ps.solveable

	# Determines if a puzzle is solveable and returns the steps needed to solve it.
	func solvePuzzleSteps():
		var puzzleSteps = PuzzleSteps.new()
		puzzleSteps.solveable = true

		# SOLVER

		return puzzleSteps

	# Holds all of the steps needed to solve a puzzle.
	class PuzzleSteps:
		var solveable

# Randomly shuffle an array.
func shuffleArray( arr ):
	for i in range( arr.size() - 1, 1, -1 ):
		var swapVal = randi() % (i + 1)
		var temp = arr[swapVal]
		arr[swapVal] = arr[i]
		arr[i] = temp

# calculate the layer; move into a method so that it can be used elsewhere
func calcBlockLayer( x, y, z ):
	return max( max( abs( x ), abs( y ) ), abs( z ) )

# Determines the block type based on puzzle size and difficulty.
func getBlockType( difficulty, x, y, z ):
	# Determine if this is the goal block.
	if x == 0 and y == 0 and z == 0:
		return BLOCK_GOAL

	# Determine the layer this block is on.
	var layer = calcBlockLayer( x, y, z )

	# Determine how many blocks are on the outer part of the layer.
	var layerCount = 0
	if abs( x ) == layer:
		layerCount += 1
	if abs( y ) == layer:
		layerCount += 1
	if abs( z ) == layer:
		layerCount += 1

	# Determine if this block is a laser.
	if difficulty == DIFF_EASY or difficulty == DIFF_MEDIUM:
		if layerCount == 3:
			return BLOCK_LASER

	if difficulty == DIFF_HARD:
		if abs( x ) == abs( z ) and y == 0:
			return BLOCK_LASER

	# Determine if this block is a wild block.
	if difficulty == DIFF_EASY:
		if layerCount == 2:
			return BLOCK_WILD

	if difficulty == DIFF_MEDIUM:
		if layerCount == 2:
			if y == layer || y == -layer:
				return BLOCK_WILD

	if difficulty == DIFF_HARD:
		if layerCount == 1:
			if y == 0:
				return BLOCK_WILD

	# Otherwise it's a normal block.
	return BLOCK_PAIR

# Generates a solveable puzzle.
func generatePuzzle( layers, difficulty ):
	var blockID = 0
	var puzzle = Puzzle.new()
	puzzle.puzzleName = "RANDOM PUZZLE"
	puzzle.puzzleLayers = layers

	# Create all possible positions.
	var layeredblocks = []
	for l in range( 0, layers + 1 ):
		layeredblocks.append( [] )
	for x in range( -layers, layers + 1 ):
		for y in range( -layers, layers + 1 ):
			for z in range( -layers, layers + 1 ):
				layeredblocks[calcBlockLayer( x, y, z )].append(Vector3(x,y,z))
				shape[Vector3(x,y,z)] = null

	# Generate laser lines.
	for l in range( 1, layers + 1 ):
		if difficulty == DIFF_EASY:
			for lx in [ -l, l ]:
				for lz in [ -l, l ]:
					puzzle.lasers.append( [Vector3( lx, lx, lz ), Vector3( lx + lx*-1, lx, lz )] )
					puzzle.lasers.append( [Vector3( lx, lx, lz ), Vector3( lx, lx + lx*-1, lz )] )
					puzzle.lasers.append( [Vector3( lx, lx, lz ), Vector3( lx, lx, lz + lz*-1 )] )

	for l in puzzle.lasers:
		pass # print( l )

	# print( "LASERS ", puzzle.lasers.size() )

	# Assign block types based on position.
	for l in range( 0, layers + 1 ):
		# Randomize the pairs.
		shuffleArray( layeredblocks[l] )
		# print( "NUM ", layeredblocks[l].size() )
		var prevBlock = null
		var even = false
		var prevLaser = null
		var laserEven = false
		for pos in layeredblocks[l]:
			var x = pos.x
			var y = pos.y
			var z = pos.z

			var t = getBlockType( difficulty, x, y, z )

			if t == BLOCK_GOAL:
				continue

			var b = PickledBlock.new()
			b.blockPos = pos
			b.name = blockID

			blockID += 1
			puzzle.blocks.append( b )

			if t == BLOCK_LASER:
				b.setBlockClass(BLOCK_LASER)
				if laserEven:
					b.setPairName(prevLaser.name) \
					.setLaserExtent(prevLaser.blockPos - b.blockPos)

					prevLaser.setPairName(b.name) \
					.setLaserExtent(b.blockPos - prevLaser.blockPos)
				laserEven = not laserEven
				prevLaser = b
				continue

			if t == BLOCK_WILD:
				b.setBlockClass(BLOCK_PAIR) \
					.setTextureName(wildColors[randi() % wildColors.size()])
				continue

			if even:
				var randColor = blockColors[randi() % blockColors.size()]
				b.setBlockClass(BLOCK_PAIR) \
					.setPairName(prevBlock.name) \
					.setTextureName(randColor)

				prevBlock.setBlockClass(BLOCK_PAIR) \
					.setPairName(b.name) \
					.setTextureName(randColor)
			even = not even
			prevBlock = b

	return puzzle

class PickledBlock:
	var name
	var blockClass = BLOCK_PAIR
	var pairName
	var textureName = "Red"
	var blockPos
	var laserExtent

	func setName(n):
		name = n
		return self

	func setPairName(n):
		pairName = n
		return self

	func setLaserExtent(n):
		laserExtent = n
		return self

	func setBlockClass(c):
		blockClass = c
		return self

	func setTextureName(t):
		textureName = t
		return self

	func toString():
		return str(name) + ": " + str(blockClass)

	func toNode():
		# instantiate a block scene, assign the appropriate script to it
		var n = blockScn.instance()
		n.set_script(blockScripts[blockClass])

		# configure block node
		n.setName(str(name)).setTexture()
		n.blockPos = blockPos
		if blockClass == BLOCK_PAIR:
			n.setPairName(pairName).setTexture(textureName)

		if blockClass == BLOCK_LASER:
			n.setPairName(pairName).setExtent(laserExtent)
		return n

	# Convert this object to a dictionary.
	func toDict():
		var di = { n = name
		 	     , bC = blockClass
			     , pN = pairName
			     , tN = textureName
			     , bP = blockPos
			     , lE = laserExtent
			     }
		return di

	# Make this object from a dictionary.
	func fromDict( di ):
		name = di.n
		blockClass = di.bC
		pairName = di.pN
		textureName = di.tN
		blockPos = di.bP
		laserExtent = di.lE

const DIFF_EASY		= 0
const DIFF_MEDIUM	= 1
const DIFF_HARD		= 2

const BLOCK_LASER	= 0
const BLOCK_WILD	= 1
const BLOCK_PAIR	= 2
const BLOCK_GOAL	= 3
const BLOCK_BLOCK   = 4

const blockColors = ["Blue", "Orange", "Red", "Yellow", "Purple", "Green"]
const wildColors = ["WildBlue", "WildOrange", "WildRed", "WildYellow", "WildPurple", "WildGreen"]

# Preload paired blocks
const blockScn = preload( "res://blocks/block.scn" )
const blockScripts = [ preload( "Blocks/LaserBlock.gd" )
			   	      , preload( "Blocks/LaserBlock.gd" )
			   	      , preload( "Blocks/PairedBlock.gd" )
			   	      , preload( "Blocks/LaserBlock.gd" )
			   	      ]
const aBlock = preload( "Blocks/AbstractBlock.gd" )

# Hash map of all possible positions
var shape = {}

# Stores a puzzle in a convenient class.
class Puzzle:
	var puzzleName
	var puzzleLayers
	var blocks = []
	var lasers = []

	var puzzleMan

	# Converts a puzzle to a dictionary.
	func toDict():
		var blockArr = []
		for b in range( blocks.size() ):
			blockArr.append( blocks[b].toDict() )

		var di = { pN = puzzleName
		 	     , pL = puzzleLayers
				 , bL = blockArr
			     }
		return di

	# Converts a dictionary to a puzzle.
	func fromDict( di ):
		puzzleName = di.pN
		puzzleLayers = di.pL
		for b in range( di.bL.size() ):
			var nb = puzzleMan.PickledBlock.new()
			nb.fromDict( di.bL[b] )
			blocks.append( nb )
		return

	# Determines if a puzzle is solveable.
	func solvePuzzle():
		# Simply use the solvePuzzleSteps function and return the solveable part.
		var ps = self.solvePuzzleSteps()
		return ps.solveable

	# Determines if a puzzle is solveable and returns the steps needed to solve it.
	func solvePuzzleSteps():
		var puzzleSteps = PuzzleSteps.new()
		puzzleSteps.solveable = true

		# SOLVER

		return puzzleSteps

	# Holds all of the steps needed to solve a puzzle.
	class PuzzleSteps:
		var solveable

# Randomly shuffle an array.
func shuffleArray( arr ):
	for i in range( arr.size() - 1, 1, -1 ):
		# works great! print( "SWAP" )
		var swapVal = randi() % (i + 1)
		var temp = arr[swapVal]
		arr[swapVal] = arr[i]
		arr[i] = temp

# calculate the layer; move into a method so that it can be used elsewhere
func calcBlockLayer( x, y, z ):
	return max( max( abs( x ), abs( y ) ), abs( z ) )

# Determines the block type based on puzzle size and difficulty.
func getBlockType( difficulty, x, y, z ):
	# Determine if this is the goal block.
	if x == 0 and y == 0 and z == 0:
		return BLOCK_GOAL

	# Determine the layer this block is on.
	var layer = calcBlockLayer( x, y, z )

	# Determine how many blocks are on the outer part of the layer.
	var layerCount = 0
	if abs( x ) == layer:
		layerCount += 1
	if abs( y ) == layer:
		layerCount += 1
	if abs( z ) == layer:
		layerCount += 1

	# Determine if this block is a laser.
	if difficulty == DIFF_EASY or difficulty == DIFF_MEDIUM:
		if layerCount == 3:
			return BLOCK_LASER

	if difficulty == DIFF_HARD:
		if abs( x ) == abs( z ) and y == 0:
			return BLOCK_LASER

	# Determine if this block is a wild block.
	if difficulty == DIFF_EASY:
		if layerCount == 2:
			return BLOCK_WILD

	if difficulty == DIFF_MEDIUM:
		if layerCount == 2:
			if y == layer || y == -layer:
				return BLOCK_WILD

	if difficulty == DIFF_HARD:
		if layerCount == 1:
			if y == 0:
				return BLOCK_WILD

	# Otherwise it's a normal block.
	return BLOCK_PAIR

# Generates a solveable puzzle.
func generatePuzzle( layers, difficulty ):
	var blockID = 0
	var puzzle = Puzzle.new()
	puzzle.puzzleName = "RANDOM PUZZLE"
	puzzle.puzzleLayers = layers

	# Create all possible positions.
	var layeredblocks = []
	for l in range( 0, layers + 1 ):
		layeredblocks.append( [] )
	for x in range( -layers, layers + 1 ):
		for y in range( -layers, layers + 1 ):
			for z in range( -layers, layers + 1 ):
				layeredblocks[calcBlockLayer( x, y, z )].append(Vector3(x,y,z))
				shape[Vector3(x,y,z)] = null

	# Generate laser lines.
	for l in range( 1, layers + 1 ):
		if difficulty == DIFF_EASY:
			for lx in [ -l, l ]:
				for lz in [ -l, l ]:
					puzzle.lasers.append( [Vector3( lx, lx, lz ), Vector3( lx + lx*-1, lx, lz )] )
					puzzle.lasers.append( [Vector3( lx, lx, lz ), Vector3( lx, lx + lx*-1, lz )] )
					puzzle.lasers.append( [Vector3( lx, lx, lz ), Vector3( lx, lx, lz + lz*-1 )] )

	for l in puzzle.lasers:
		print( l )

	print( "LASERS ", puzzle.lasers.size() )

	# Assign block types based on position.
	for l in range( 0, layers + 1 ):
		# Randomize the pairs.
		shuffleArray( layeredblocks[l] )
		print( "NUM ", layeredblocks[l].size() )
		var prevBlock = null
		var even = false
		var prevLaser = null
		var laserEven = false
		for pos in layeredblocks[l]:
			var x = pos.x
			var y = pos.y
			var z = pos.z

			var t = getBlockType( difficulty, x, y, z )

			if t == BLOCK_GOAL:
				continue

			var b = PickledBlock.new()
			b.blockPos = pos
			b.name = blockID

			blockID += 1
			puzzle.blocks.append( b )

			if t == BLOCK_LASER:
				b.setBlockClass(BLOCK_LASER)
				if laserEven:
					b.setPairName(prevLaser.name) \
					.setLaserExtent(prevLaser.blockPos - b.blockPos)

					prevLaser.setPairName(b.name) \
					.setLaserExtent(b.blockPos - prevLaser.blockPos)
				laserEven = not laserEven
				prevLaser = b
				continue

			if t == BLOCK_WILD:
				b.setBlockClass(BLOCK_PAIR) \
					.setTextureName(wildColors[randi() % wildColors.size()])
				continue

			if even:
				var randColor = blockColors[randi() % blockColors.size()]
				b.setBlockClass(BLOCK_PAIR) \
					.setPairName(prevBlock.name) \
					.setTextureName(randColor)

				prevBlock.setBlockClass(BLOCK_PAIR) \
					.setPairName(b.name) \
					.setTextureName(randColor)
			even = not even
			prevBlock = b

	return puzzle

class PickledBlock:
	var name
	var blockClass = BLOCK_PAIR
	var pairName
	var textureName = "Red"
	var blockPos
	var laserExtent

	func setName(n):
		name = n
		return self

	func setPairName(n):
		pairName = n
		return self

	func setLaserExtent(n):
		laserExtent = n
		return self

	func setBlockClass(c):
		blockClass = c
		return self

	func setTextureName(t):
		textureName = t
		return self

	func toString():
		return str(name) + ": " + str(blockClass)

	func toNode():
		# instantiate a block scene, assign the appropriate script to it
		var n = blockScn.instance()
		n.set_script(blockScripts[blockClass])

		# configure block node
		n.setName(str(name)).setTexture()
		n.blockPos = blockPos
		if blockClass == BLOCK_PAIR:
			n.setPairName(pairName).setTexture(textureName)

		if blockClass == BLOCK_LASER:
			n.setPairName(pairName).setExtent(laserExtent)
		return n

	# Convert this object to a dictionary.
	func toDict():
		var di = { n = name
		 	     , bC = blockClass
			     , pN = pairName
			     , tN = textureName
			     , bP = blockPos
			     , lE = laserExtent
			     }
		return di

	# Make this object from a dictionary.
	func fromDict( di ):
		name = di.n
		blockClass = di.bC
		pairName = di.pN
		textureName = di.tN
		blockPos = di.bP
		laserExtent = di.lE


Change network port

Change network port

extends Node

var connection_id

var SERVER_IP = "127.0.0.1"
#var SERVER_IP = "82.149.113.37"
#var SERVER_IP = "192.168.0.10"
var SERVER_PORT = 8910
var MAX_PLAYERS = 512

# Signals to let lobby GUI know what's going on

#  when a player has been added, or removed
signal peer_list_changed(player_pool_size, networkid, add_or_remove) # server only signal

# when the game is switched to client mode
signal start_client()
# when the game is switched to server mode
signal start_server()
# when the game is switched to offline mode
signal start_offline()

# when external ip address has been determined
signal determined_external_ip(ip)

# when the client is connected to the server
signal connected_as_client()

#signal connection_failed()
#signal connection_succeeded()
#signal game_ended()
#signal game_error(what)

# Connect all functions
func _ready():

	get_tree().connect("network_peer_connected", self, "network_peer_connected")
	get_tree().connect("network_peer_disconnected", self, "network_peer_disconnected")
	#get_tree().connect("peer_connected", self, "peer_connected")
	#get_tree().connect("peer_disconnected", self, "peer_disconnected")
	get_tree().connect("connected_to_server", self, "connected_to_server")
	get_tree().connect("connection_failed", self, "connection_failed")
	#get_tree().connect("connection_succeeded", self, "connection_succeeded")

	get_tree().connect("server_disconnected", self, "server_disconnected")

	set_physics_process(false)

	init_local_information()

# methods to check current status

func is_network_activated():
	return get_tree().has_meta("network_peer")

func is_offline():
	return not is_network_activated()

func is_server():
	if is_network_activated() and get_tree().is_network_server():
		return true
	else :
		return false

func is_master(node):
	if is_network_activated() and node.is_network_master():
		return true
	else :
		return false

func is_slave(node):
	if is_network_activated() and not node.is_network_master():
		return true
	else :
		return false

# send updates to other clients
func _physics_process(delta):
	_single_process()

func _single_process():
	if (get_tree().has_meta("network_peer") and get_tree().is_network_server()):
		#print ("sending network update..")
		for group in object_locator.objects_registered:
			for obj in object_locator.objects_registered[group]:
				if obj.is_network_master():
					obj.rpc_unreliable("set_network_update", obj.get_network_update())

# Player info, associate ID to data
var player_info = {}
# Info we send to other players
var my_info = { }

var players_done = []

func resolved_external_address(result, response_code, headers, body, params = []):
	var response = http.on_request_completed(result, response_code, headers, body, params)
	if response and response.body:
		my_info['ip'].append(response.body)
		# my_info['ip'] += ','+ response.body
		print("added external IP to my info: ", my_info.ip)
		emit_signal("determined_external_ip", my_info.ip)

func resolve_external_address():
	http.send("http://ip.icb.at", "", "", "resolved_external_address", self)

func init_local_information():

	# get all local adresses
	var ip_all = IP.get_local_addresses()
	var ip_valid = []
	# only add valid ones (no localhost, no ipv6, no ms fallback
	for ip in ip_all:
		print("Found local IP: ", ip)
		if ip.find(':') != -1: continue
		elif ip.substr(0,4) == '127.': continue
		elif ip.substr(0,4) == '169.': continue
		else: ip_valid.append(ip)

#	my_info["ip"] = PoolStringArray(ip_valid).join(",")
	my_info["ip"] = ip_valid
	call_deferred("resolve_external_address")

	my_info["os"] = OS.get_name()
	my_info["device"] = OS.get_model_name()
	var username = OS.get_user_data_dir()

	# remove everything before first slash
	var pos = username.find("/")
	username = username.substr(pos+1, username.length() - pos)
	# remove /Users/ and /home/
	username = username.replacen("Users/", "").replacen("home/", "")
	# remove everything after next slash
	if username.find("/") > 0:
		username = username.substr(0, username.find("/"))
	my_info["username"] = username

	print("my infos: ", my_info)

# Will go unused, not useful here
func network_peer_connected(id):
	print ("network_peer_connected")
	emit_signal("peer_list_changed", player_info.size(), id, 1)

func network_peer_disconnected(id):
	print ("network_peer_disconnected")
	# Erase player from info
	player_info.erase(id)
	emit_signal("peer_list_changed", player_info.size(), id, -1)

func connected_to_server():
	print ("connected_to_server")
	emit_signal("connected_as_client")

	# Only called on clients, not server. Send my ID and info to all the other peers
	rpc("register_player", get_tree().get_network_unique_id(), my_info)

# Server kicked us, show error and abort
func server_disconnected():
	print ("server_disconnected")
	get_tree().set_meta("network_peer", null)
	emit_signal("start_offline")

# Could not even connect to server, abort
func connection_failed():
	print ("connection_failed")
	get_tree().set_meta("network_peer", null)
	emit_signal("start_offline")

func connection_succeeded():
	print ("connection_succeeded")
	#get_tree().set_meta("network_peer", null)
	#emit_signal("start_offline")

func disconnect_game():
	print ("disconnect_game")

	var peer = get_tree().get_meta("network_peer")
	if peer:
		peer.close_connection()

	get_tree().set_meta("network_peer", null)
	emit_signal("start_offline")

func split_path(path):
	# /root/game/objects/Asteroid2
	# > objects
	var parts = String(path).split("/", false)
	return parts[2]

#remote func register_player(id, info):
remote func register_player(id, info):
	print ("register_player ", id , ' info ', info )
	# Store the info
	player_info[id] = info
	# If I'm the server, let the new guy know about existing players
	if (get_tree().is_network_server()):
		# Send my info to new player
		rpc_id(id, "register_player", 1, my_info)

		emit_signal("peer_list_changed", player_info.size(), id, -1)

		# go through all game objects, and spawn them accordingly on the new peer
		for group in object_locator.objects_registered:
			for obj in object_locator.objects_registered[group]:
				get_node(global.scene_tree_game).rpc_id(id, "spawn_object", obj.scene_path, split_path(obj.get_path()))

		# Send the info of existing players
		# no in use
		for peer_id in player_info:
			rpc_id(id, "register_player", peer_id, player_info[peer_id])

		# Call function to update lobby UI here
		# called on all peers
		rpc("pre_configure_game")

remote func pre_configure_game():
	print ("pre_configure_game")

	# game is paused for now
	get_tree().set_pause(true) # Pre-pause
	# The rest is the same as in the code in the previous section (look above)
	var selfPeerID = get_tree().get_network_unique_id()

	# Tell server (remember, server is always ID=1) that this peer is done pre-configuring
	rpc_id(1, "done_preconfiguring", selfPeerID)

# waits until all clients sent "done"
remote func done_preconfiguring(who):
	print ("done_preconfiguring")

	# Here is some checks you can do, as example
	assert(get_tree().is_network_server())
	assert(who in player_info) # Exists
	assert(not who in players_done) # Was not added yet

	players_done.append(who)

	if (players_done.size() == player_info.size()):
		rpc("post_configure_game")

# .. then continues the game
remote func post_configure_game():
	print ("post_configure_game")
	get_tree().set_pause(false)
	# Game starts now!

#
func start_server():
	print ("starting server...")
	var peer = NetworkedMultiplayerENet.new()
	var status = peer.create_server(SERVER_PORT, MAX_PLAYERS)

	print ("status: ", status)

	if status == 0:

		get_tree().set_network_peer(peer)
		get_tree().set_meta("network_peer", peer)

		set_physics_process(true)

		emit_signal("start_server")
		http.send("https://dev.pauschenwein.net/spiel4/lobby.php",
			{"server": "start"}, my_info, "registered_server", self)

	else :
		print ("Starting Server failed: ", status)
		peer.close_connection()

func start_client():
	print ("connect to server..")
	var peer = NetworkedMultiplayerENet.new()
	peer.create_client(SERVER_IP, SERVER_PORT)
	get_tree().set_network_peer(peer)
	get_tree().set_meta("network_peer", peer)

	set_physics_process(false)

	# clearing is done via signals now - because cool
	emit_signal("start_client")

func registered_server(result, response_code, headers, body, params = []):
	print("registered_server: ", result)

	var response = http.on_request_completed(result, response_code, headers, body, params)
	if response and response.body:
		print("response Body: ", response.body)

extends Node

var connection_id

var SERVER_IP = "127.0.0.1"
var SERVER_PORT = 32112
var MAX_PLAYERS = 512

# Signals to let lobby GUI know what's going on

#  when a player has been added, or removed
signal peer_list_changed(player_pool_size, networkid, add_or_remove) # server only signal

# when the game is switched to client mode
signal start_client()
# when the game is switched to server mode
signal start_server()
# when the game is switched to offline mode
signal start_offline()

# when external ip address has been determined
signal determined_external_ip(ip)

# when the client is connected to the server
signal connected_as_client()

#signal connection_failed()
#signal connection_succeeded()
#signal game_ended()
#signal game_error(what)

# Connect all functions
func _ready():

	get_tree().connect("network_peer_connected", self, "network_peer_connected")
	get_tree().connect("network_peer_disconnected", self, "network_peer_disconnected")
	#get_tree().connect("peer_connected", self, "peer_connected")
	#get_tree().connect("peer_disconnected", self, "peer_disconnected")
	get_tree().connect("connected_to_server", self, "connected_to_server")
	get_tree().connect("connection_failed", self, "connection_failed")
	#get_tree().connect("connection_succeeded", self, "connection_succeeded")

	get_tree().connect("server_disconnected", self, "server_disconnected")

	set_physics_process(false)

	init_local_information()

# methods to check current status

func is_network_activated():
	return get_tree().has_meta("network_peer")

func is_offline():
	return not is_network_activated()

func is_server():
	if is_network_activated() and get_tree().is_network_server():
		return true
	else :
		return false

func is_master(node):
	if is_network_activated() and node.is_network_master():
		return true
	else :
		return false

func is_slave(node):
	if is_network_activated() and not node.is_network_master():
		return true
	else :
		return false

# send updates to other clients
func _physics_process(delta):
	_single_process()

func _single_process():
	if (get_tree().has_meta("network_peer") and get_tree().is_network_server()):
		#print ("sending network update..")
		for group in object_locator.objects_registered:
			for obj in object_locator.objects_registered[group]:
				if obj.is_network_master():
					obj.rpc_unreliable("set_network_update", obj.get_network_update())

# Player info, associate ID to data
var player_info = {}
# Info we send to other players
var my_info = { }

var players_done = []

func resolved_external_address(result, response_code, headers, body, params = []):
	var response = http.on_request_completed(result, response_code, headers, body, params)
	if response and response.body:
		my_info['ip'].append(response.body)
		# my_info['ip'] += ','+ response.body
		print("added external IP to my info: ", my_info.ip)
		emit_signal("determined_external_ip", my_info.ip)

func resolve_external_address():
	http.send("http://ip.icb.at", "", "", "resolved_external_address", self)

func init_local_information():

	# get all local adresses
	var ip_all = IP.get_local_addresses()
	var ip_valid = []
	# only add valid ones (no localhost, no ipv6, no ms fallback
	for ip in ip_all:
		if ip.find(':') != -1: continue
		elif ip.substr(0,4) == '127.': continue
		elif ip.substr(0,4) == '169.': continue
		else: ip_valid.append(ip)

#	my_info["ip"] = PoolStringArray(ip_valid).join(",")
	my_info["ip"] = ip_valid
	call_deferred("resolve_external_address")

	my_info["os"] = OS.get_name()
	my_info["device"] = OS.get_model_name()
	var username = OS.get_user_data_dir()

	# remove everything before first slash
	var pos = username.find("/")
	username = username.substr(pos+1, username.length() - pos)
	# remove /Users/ and /home/
	username = username.replacen("Users/", "").replacen("home/", "")
	# remove everything after next slash
	if username.find("/") > 0:
		username = username.substr(0, username.find("/"))
	my_info["username"] = username

	print("my infos: ", my_info)

# Will go unused, not useful here
func network_peer_connected(id):
	print ("network_peer_connected")
	emit_signal("peer_list_changed", player_info.size(), id, 1)

func network_peer_disconnected(id):
	print ("network_peer_disconnected")
	# Erase player from info
	player_info.erase(id)
	emit_signal("peer_list_changed", player_info.size(), id, -1)

func connected_to_server():
	print ("connected_to_server")
	emit_signal("connected_as_client")

	# Only called on clients, not server. Send my ID and info to all the other peers
	rpc("register_player", get_tree().get_network_unique_id(), my_info)

# Server kicked us, show error and abort
func server_disconnected():
	print ("server_disconnected")
	get_tree().set_meta("network_peer", null)
	emit_signal("start_offline")

# Could not even connect to server, abort
func connection_failed():
	print ("connection_failed")
	get_tree().set_meta("network_peer", null)
	emit_signal("start_offline")

func connection_succeeded():
	print ("connection_succeeded")
	#get_tree().set_meta("network_peer", null)
	#emit_signal("start_offline")

func disconnect_game():
	print ("disconnect_game")

	var peer = get_tree().get_meta("network_peer")
	if peer:
		peer.close_connection()

	get_tree().set_meta("network_peer", null)
	emit_signal("start_offline")

func split_path(path):
	# /root/game/objects/Asteroid2
	# > objects
	var parts = String(path).split("/", false)
	return parts[2]

#remote func register_player(id, info):
remote func register_player(id, info):
	print ("register_player ", id , ' info ', info )
	# Store the info
	player_info[id] = info
	# If I'm the server, let the new guy know about existing players
	if (get_tree().is_network_server()):
		# Send my info to new player
		rpc_id(id, "register_player", 1, my_info)

		emit_signal("peer_list_changed", player_info.size(), id, -1)

		# go through all game objects, and spawn them accordingly on the new peer
		for group in object_locator.objects_registered:
			for obj in object_locator.objects_registered[group]:
				get_node(global.scene_tree_game).rpc_id(id, "spawn_object", obj.scene_path, split_path(obj.get_path()))

		# Send the info of existing players
		# no in use
		for peer_id in player_info:
			rpc_id(id, "register_player", peer_id, player_info[peer_id])

		# Call function to update lobby UI here
		# called on all peers
		rpc("pre_configure_game")

remote func pre_configure_game():
	print ("pre_configure_game")

	# game is paused for now
	get_tree().set_pause(true) # Pre-pause
	# The rest is the same as in the code in the previous section (look above)
	var selfPeerID = get_tree().get_network_unique_id()

	# Tell server (remember, server is always ID=1) that this peer is done pre-configuring
	rpc_id(1, "done_preconfiguring", selfPeerID)

# waits until all clients sent "done"
remote func done_preconfiguring(who):
	print ("done_preconfiguring")

	# Here is some checks you can do, as example
	assert(get_tree().is_network_server())
	assert(who in player_info) # Exists
	assert(not who in players_done) # Was not added yet

	players_done.append(who)

	if (players_done.size() == player_info.size()):
		rpc("post_configure_game")

# .. then continues the game
remote func post_configure_game():
	print ("post_configure_game")
	get_tree().set_pause(false)
	# Game starts now!

#
func start_server():
	print ("starting server...")
	var peer = NetworkedMultiplayerENet.new()
	var status = peer.create_server(SERVER_PORT, MAX_PLAYERS)

	print ("status: ", status)

	if status == 0:

		get_tree().set_network_peer(peer)
		get_tree().set_meta("network_peer", peer)

		set_physics_process(true)

		emit_signal("start_server")
		http.send("https://dev.pauschenwein.net/spiel4/lobby.php",
			{"server": "start"}, my_info, "registered_server", self)

	else :
		print ("Starting Server failed: ", status)
		peer.close_connection()

func start_client():
	print ("connect to server..")
	var peer = NetworkedMultiplayerENet.new()
	peer.create_client(SERVER_IP, SERVER_PORT)
	get_tree().set_network_peer(peer)
	get_tree().set_meta("network_peer", peer)

	set_physics_process(false)

	# clearing is done via signals now - because cool
	emit_signal("start_client")

func registered_server(result, response_code, headers, body, params = []):
	print("registered_server: ", result)

	var response = http.on_request_completed(result, response_code, headers, body, params)
	if response and response.body:
		print("response Body: ", response.body)


Update GUIManager.gd

Update GUIManager.gd

Added splash skip and comments.

# This script manages all of the GUI elements.
# It switches between the states of the GUI allowing
# the user to start games, edit the options or connect
# to a multiplayer game.

extends Node

# State variables.
var menuOn
var splashTween

# Timers.
var timer
var initialWait = .5
var splashTimer = 2.0
var warningTimer = 5.0

# GUI pieces.
var Splash_Team5
var Splash_Warning
var Menu_Main
var Menu_Chooser
var Menu_MP
var Menu_HostGame
var Menu_JoinGame
var Menu_Options

# Menu state values. Used to switch between them.
var MENU_INIT			= 0
var MENU_SPLASHTEAM5	= 1
var MENU_SPLASHWARNING	= 2
var MENU_MAIN			= 3
var MENU_CHOOSER		= 4
var MENU_MP				= 5
var MENU_HOSTGAME		= 6
var MENU_JOINGAME		= 7
var MENU_OPTIONS		= 8

# Function to be called once for setup.
func _ready():
	# Initial setup.
	menuOn = MENU_INIT
	timer = 0.0
	set_process( true )
	set_process_input( true )

	# Setup the splash fader tween.
	splashTween = Tween.new()
	get_node( "SplashFader" ).add_child( splashTween )
	splashTween.interpolate_method( get_node( "SplashFader" ), "set_opacity", 1.0, 0.0, 1.0, Tween.TRANS_CUBIC, Tween.EASE_IN_OUT )

	# Gather all of the menus.
	Splash_Team5 = get_node( "SplashTeam5" )
	Splash_Warning = get_node( "SplashWarning" )
	Menu_Main = get_node( "MainMenu" )
	Menu_Chooser = get_node( "ChooserMenu" )
	Menu_MP = get_node( "Multiplayer" )
	Menu_HostGame = get_node( "HostGame" )
	Menu_JoinGame = get_node( "JoinGame" )
	Menu_Options = get_node( "OptionsMenu" )

# Function to update the GUI.
func _process( delta ):
	# Handle the initial wait.
	if( menuOn == MENU_INIT ):
		if( timer > initialWait ):
			menuOn = MENU_SPLASHTEAM5
			timer = 0.0
			Splash_Team5.guiIn()

	# Handle the Team5 splash screen.
	if( menuOn == MENU_SPLASHTEAM5 ):
		if( timer > splashTimer ):
			menuOn = MENU_SPLASHWARNING
			timer = 0.0
			Splash_Team5.guiOut()
			Splash_Warning.guiIn()

	# Handle the warning splash screen.
	if( menuOn == MENU_SPLASHWARNING ):
		if( timer > warningTimer ):
			menuOn = MENU_MAIN
			timer = 0.0
			Splash_Warning.guiOut()
			Menu_Main.guiIn()
			splashTween.start()

	# Increase the timer each frame.
	timer += delta

# Handle the input events.
func _input( ev ):
	# Handle skip splash screens.
	if( !ev.is_pressed() and ev.type==InputEvent.MOUSE_BUTTON and ev.button_index==BUTTON_LEFT ):
		if( menuOn == MENU_SPLASHTEAM5 || menuOn == MENU_SPLASHWARNING ):
			timer = 999.0;

	# Handle exit.
	if( ev.is_action( "ui_cancel" ) ):
		exit()

func exit():
	print( "Exiting" )
	OS.get_main_loop().quit()

func _on_Exit_pressed():
	exit()

func _on_Options_pressed():
	Menu_Main.guiOut()
	Menu_Options.guiIn()
	timer = 0.0
	menuOn = MENU_OPTIONS

func _on_Cancel_pressed():
	Menu_Options.guiOut()
	Menu_Main.guiIn()
	timer = 0.0
	menuOn = MENU_MAIN

func _on_SaveQuit_pressed():
	# Add save options here.
	_on_Cancel_pressed()

func _on_SP_pressed():
	Menu_Main.guiOut()
	Menu_Chooser.guiIn()
	timer = 0.0
	menuOn = MENU_CHOOSER

func _on_MainMenu_pressed():
	Menu_Chooser.guiOut()
	Menu_Main.guiIn()
	timer = 0.0
	menuOn = MENU_MAIN

func _on_MainMenuMP_pressed():
	Menu_MP.guiOut()
	Menu_Main.guiIn()
	timer = 0.0
	menuOn = MENU_MAIN

func _on_MP_pressed():
	Menu_Main.guiOut()
	Menu_MP.guiIn()
	timer = 0.0
	menuOn = MENU_MP

func _on_MainMenuHG_pressed():
	Menu_HostGame.guiOut()
	Menu_Main.guiIn()
	timer = 0.0
	menuOn = MENU_MAIN

func _on_HostGame_pressed():
	Menu_MP.guiOut()
	Menu_HostGame.guiIn()
	timer = 0.0
	menuOn = MENU_HOSTGAME

func _on_JoinGame_pressed():
	Menu_MP.guiOut()
	Menu_JoinGame.guiIn()
	timer = 0.0
	menuOn = MENU_JOINGAME

func _on_MainMenuJG_pressed():
	Menu_JoinGame.guiOut()
	Menu_Main.guiIn()
	timer = 0.0
	menuOn = MENU_MAIN

func _on_RandomPuzzle_pressed():
	var root = get_tree().get_root()
	root.get_child( root.get_child_count() - 1 ).queue_free()
	root.add_child( ResourceLoader.load( "res://puzzle.scn" ).instance() )

extends Node

var menuOn
var timer
var splashTween

var initialWait = .5
var splashTimer = 2.0
var warningTimer = 5.0

var Splash_Team5
var Splash_Warning
var Menu_Main
var Menu_Chooser
var Menu_MP
var Menu_HostGame
var Menu_JoinGame
var Menu_Options

var MENU_INIT			= 0
var MENU_SPLASHTEAM5	= 1
var MENU_SPLASHWARNING	= 2
var MENU_MAIN			= 3
var MENU_CHOOSER		= 4
var MENU_MP				= 5
var MENU_HOSTGAME		= 6
var MENU_JOINGAME		= 7
var MENU_OPTIONS		= 8

func _ready():
	# Initial setup.
	menuOn = MENU_INIT
	timer = 0.0
	set_process( true )
	set_process_input( true )

	# Setup the splash fader tween.
	splashTween = Tween.new()
	get_node( "SplashFader" ).add_child( splashTween )
	splashTween.interpolate_method( get_node( "SplashFader" ), "set_opacity", 1.0, 0.0, 1.0, Tween.TRANS_CUBIC, Tween.EASE_IN_OUT )

	# Gather all of the menus.
	Splash_Team5 = get_node( "SplashTeam5" )
	Splash_Warning = get_node( "SplashWarning" )
	Menu_Main = get_node( "MainMenu" )
	Menu_Chooser = get_node( "ChooserMenu" )
	Menu_MP = get_node( "Multiplayer" )
	Menu_HostGame = get_node( "HostGame" )
	Menu_JoinGame = get_node( "JoinGame" )
	Menu_Options = get_node( "OptionsMenu" )

func _process( delta ):
	# Handle the initial wait.
	if( menuOn == MENU_INIT ):
		if( timer > initialWait ):
			menuOn = MENU_SPLASHTEAM5
			timer = 0.0
			Splash_Team5.guiIn()

	# Handle the Team5 splash screen.
	if( menuOn == MENU_SPLASHTEAM5 ):
		if( timer > splashTimer ):
			menuOn = MENU_SPLASHWARNING
			timer = 0.0
			Splash_Team5.guiOut()
			Splash_Warning.guiIn()

	# Handle the warning splash screen.
	if( menuOn == MENU_SPLASHWARNING ):
		if( timer > warningTimer ):
			menuOn = MENU_MAIN
			timer = 0.0
			Splash_Warning.guiOut()
			Menu_Main.guiIn()
			splashTween.start()

	# Increase the timer each frame.
	timer += delta

func _input( ev ):
	# Handle exit.
	if( ev.is_action( "ui_cancel" ) ):
		exit()

func exit():
	print( "Exiting" )
	OS.get_main_loop().quit()

func _on_Exit_pressed():
	exit()

func _on_Options_pressed():
	Menu_Main.guiOut()
	Menu_Options.guiIn()
	timer = 0.0
	menuOn = MENU_OPTIONS

func _on_Cancel_pressed():
	Menu_Options.guiOut()
	Menu_Main.guiIn()
	timer = 0.0
	menuOn = MENU_MAIN

func _on_SaveQuit_pressed():
	# Add save options here.
	_on_Cancel_pressed()

func _on_SP_pressed():
	Menu_Main.guiOut()
	Menu_Chooser.guiIn()
	timer = 0.0
	menuOn = MENU_CHOOSER

func _on_MainMenu_pressed():
	Menu_Chooser.guiOut()
	Menu_Main.guiIn()
	timer = 0.0
	menuOn = MENU_MAIN

func _on_MainMenuMP_pressed():
	Menu_MP.guiOut()
	Menu_Main.guiIn()
	timer = 0.0
	menuOn = MENU_MAIN

func _on_MP_pressed():
	Menu_Main.guiOut()
	Menu_MP.guiIn()
	timer = 0.0
	menuOn = MENU_MP

func _on_MainMenuHG_pressed():
	Menu_HostGame.guiOut()
	Menu_Main.guiIn()
	timer = 0.0
	menuOn = MENU_MAIN

func _on_HostGame_pressed():
	Menu_MP.guiOut()
	Menu_HostGame.guiIn()
	timer = 0.0
	menuOn = MENU_HOSTGAME

func _on_JoinGame_pressed():
	Menu_MP.guiOut()
	Menu_JoinGame.guiIn()
	timer = 0.0
	menuOn = MENU_JOINGAME

func _on_MainMenuJG_pressed():
	Menu_JoinGame.guiOut()
	Menu_Main.guiIn()
	timer = 0.0
	menuOn = MENU_MAIN

func _on_RandomPuzzle_pressed():
	var root = get_tree().get_root()
	root.get_child( root.get_child_count() - 1 ).queue_free()
	root.add_child( ResourceLoader.load( "res://puzzle.scn" ).instance() )


Fix deep equality checking to use a "near" double comparison to allow for floating point errors.

# base test class

#extends Object

var _tests = []
var filename = "<unknown>"
var _results = null

func _init():
	var t
	for t in get_method_list():
		# print(t.to_json())
		if t["name"].begins_with("test_") && t["args"].size() <= 0:
			add(t["name"])

func class_setup():
	pass

func class_teardown():
	pass

func setup():
	pass

func teardown():
	pass

# ------------------------------------------------------------------------

func add(test_name):
	if test_name == null:
		return
	if typeof(test_name) == TYPE_STRING:
		if not(test_name in _tests):
			_tests.append(test_name)
	else:
		var t
		for t in test_name:
			if t != null && not(t in _tests):
				_tests.append(t)

func add_all(all_tests):
	add(all_tests)

func set_tests(test_names):
	_tests = []
	add(test_names)

func skip(test_name):
	if test_name == null:
		return
	if typeof(test_name) == TYPE_STRING:
		_tests.erase(test_name)
	else:
		var t
		for t in test_name:
			_tests.erase(test_name)

# ------------------------------------------------------------------------

func check_true(text, bool_val):
	if ! bool_val:
		if _results != null:
			_results.add_error(text)
		return false
	return true

func check_that(text, actual, matcher):
	return check_true(text + ": " + matcher.describe(actual), matcher.matches(actual))

func check(text = ""):
	return Checker.new(_results, text)

# ------------------------------------------------------------------------

# result_collector must implement these methods:
#   func start_suite(suite_name)
#   func end_suite()
#   func start_test(name)
#   func end_test()
#   func add_error(message)
#   func has_errors() (does the current suite have any errors?)

func run(result_collector):
	self._results = result_collector
	result_collector.start_suite(filename)
	class_setup()
	if result_collector.has_error():
		return

	var t
	for t in _tests:
		if has_method(t):
			run_test(t)
		else:
			result_collector.add_error("Setup Error: requested function does not exist: " + t)

	class_teardown()
	result_collector.end_suite()

func run_test(name):
	if _results != null:
		_results.start_test(name)
	setup()
	call(name)
	teardown()
	if _results != null:
		_results.end_test()

# -------------------------------------------------------------------------

class Matcher:
	func matches(value):
		return false
	func describe(value):
		return ""
	func _as_str(value):
		if value == null:
			return "<null>"
		if typeof(value) == TYPE_DICTIONARY:
			return value.to_json()
		return "[" + str(value) + "]"
	func _is_list(value):
		# return typeof(value) == TYPE_ARRAY || typeof(value) == TYPE_INT_ARRAY || typeof(value) == TYPE_REAL_ARRAY || typeof(value) == TYPE_STRING_ARRAY
		# All the arrays are in this specific range.
		# This needs to be checked against future versions of Godot.
		var v = typeof(value)
		return v >= TYPE_ARRAY && v <= TYPE_COLOR_ARRAY

class IsMatcher:
	extends Matcher
	var val
	var _epsilon

	func _init(v, epsilon = 0.00001):
		val = v
		_epsilon = epsilon

	func matches(value):
		return _inner_match(val, value, [])

	func _inner_match(v1, v2, seen):
		seen.append(v2)
		# Check lists the same way
		if _is_list(v1) && _is_list(v2):
			if v1.size() != v2.size():
				return false
			var i
			for i in range(0, v1.size()):
				if v2[i] in seen:
					# This isn't a correct check; instead,
					# we need to ensure that the seen version of v2[i]
					# matches up with the corresponding seen version of v1[i]
					if v1[i] != v2[i]:
						return false
					# Else prevent infinite loop by just
					# saying it's right.
				elif ! _inner_match(v1[i], v2[i], seen):
					return false
			return true
		# Cannot perform a "==" if the types are different
		if typeof(v1) != typeof(v2):
			return false
		if v1 == v2:
			return true
		if typeof(v1) == TYPE_DICTIONARY:
			if v1.size() != v2.size():
				return false
			var k
			for k in v1:
				if not (k in v2):
					return false
				if v2[k] in seen:
					# This isn't a correct check; instead,
					# we need to ensure that the seen version of v2[i]
					# matches up with the corresponding seen version of v1[i]
					if v1[k] != v2[k]:
						return false
				elif ! _inner_match(v1[k], v2[k], seen):
					return false
			return true
		if typeof(v1) == TYPE_REAL:
			# Closeness
			return abs(float(v1) - v2) <= _epsilon
		# Any other type should have == match right.
		return false

	func describe(value):
		return "expected " + _as_str(val) + ", found " + _as_str(value)

static func is(value, epsilon = 0.00001):
	# "is" can be used to make a clear English-like sentence.
	# So, if the value is a matcher, then just use that matcher instead of
	# adding another layer around "is".
	if typeof(value) == TYPE_OBJECT && value.has_method("matches") && value.has_method("describe"):
		return value
	# Need to wrap the value in an is check.
	return IsMatcher.new(value, epsilon)

static func equals(value):
	return IsMatcher.new(value)

class NotMatcher:
	var val

	func _init(v):
		if typeof(v) == TYPE_OBJECT && v extends Matcher:
			val = v
		else:
			val = IsMatcher.new(v)

	func matches(value):
		return ! val.matches(value)

	func describe(value):
		return "expected not: " + val.describe(value)

static func is_not(value):
	return NotMatcher.new(value)

class BetweenMatcher:
	var lo
	var hi

	func _init(l, h):
		lo = float(l)
		hi = float(h)

	func matches(value):
		return value != null && float(value) >= lo && float(value) <= hi

	func describe(value):
		return "expected [" + str(lo) + ", " + str(hi) + "], found " + str(value)

static func between(lo, hi):
	return BetweenMatcher.new(lo, hi)

class NearMatcher:
	var _epsilon
	var _val

	func _init(val, epsilon = 0.00001):
		_epsilon = float(epsilon)
		_val = float(val)

	func matches(value):
		return abs(float(value) - _val) <= _epsilon

	func describe(value):
		return "expected " + str(_val) + " within " + str(_epsilon) + ", found " + str(value)

static func near(val, epsilon = 0.00001):
	return NearMatcher.new(val, epsilon)

class ContainsMatcher:
	extends Matcher
	var _val

	func _init(val):
		_val = val

	func matches(actual):
		if actual == null:
			return false
		if typeof(actual) == TYPE_STRING:
			# Expect a string to contain a sub-string
			return actual.find(_val) >= 0
		if _is_list(actual):
			if _is_list(_val):
				# each "val" must be in the actual list
				var v
				for v in _val:
					if ! (v in actual):
						return false
				return true
			return _val in actual
		if typeof(actual) == TYPE_RECT2:
			if typeof(_val) == TYPE_RECT2:
				return actual.encloses(_val)
			if typeof(_val) == TYPE_VECTOR2:
				return actual.has_point(_val)
			return false
		if typeof(actual) == TYPE_PLANE:
			if typeof(_val) == TYPE_VECTOR3:
				return actual.has_point(_val)
			return false
		if typeof(actual) == TYPE_DICTIONARY:
			if _is_list(_val):
				return actual.has_all(_val)
		 	return actual.has(_val)

		# These don't really make sense.

		# Object values should just be checked for equality.
		#if typeof(actual) == TYPE_OBJECT:
		#	return actual.get(_val) != null

		return false

	func describe(actual):
		return "expected " + _as_str(actual) + " to contain " + _as_str(_val)

static func contains(val):
	return ContainsMatcher.new(val)

class EmptyMatcher:
	extends Matcher

	func matches(actual):
		if _is_list(actual):
			return actual.size() <= 0
		if typeof(actual) == TYPE_DICTIONARY:
			return actual.keys().size() <= 0
		return false

	func describe(actual):
		if _is_list(actual):
			return "expected " + _as_str(actual) + " to be an empty list"
		if typeof(actual) == TYPE_DICTIONARY:
			return "expected " + _as_str(actual) + " to be an empty dictionary"
		return "expected " + _as_str(actual) + " to be an empty list or dictionary"

static func empty():
	return EmptyMatcher.new()

# ---------------------------------------------------------------------------

class Checker:
	var _text
	var _results

	func _init(results, text):
		_results = results
		_text = text

	func that(actual, matcher):
		var res
		var msg
		if typeof(matcher) == TYPE_BOOL:
			res = matcher
			msg = _text
		else:
			res = matcher.matches(actual)
			msg = _text + ": " + matcher.describe(actual)
		if ! res:
			if _results != null:
				_results.add_error(msg)
			return false
		return true

# base test class

#extends Object

var _tests = []
var filename = "<unknown>"
var _results = null

func _init():
	var t
	for t in get_method_list():
		# print(t.to_json())
		if t["name"].begins_with("test_") && t["args"].size() <= 0:
			add(t["name"])

func class_setup():
	pass

func class_teardown():
	pass

func setup():
	pass

func teardown():
	pass

# ------------------------------------------------------------------------

func add(test_name):
	if test_name == null:
		return
	if typeof(test_name) == TYPE_STRING:
		if not(test_name in _tests):
			_tests.append(test_name)
	else:
		var t
		for t in test_name:
			if t != null && not(t in _tests):
				_tests.append(t)

func add_all(all_tests):
	add(all_tests)

func set_tests(test_names):
	_tests = []
	add(test_names)

func skip(test_name):
	if test_name == null:
		return
	if typeof(test_name) == TYPE_STRING:
		_tests.erase(test_name)
	else:
		var t
		for t in test_name:
			_tests.erase(test_name)

# ------------------------------------------------------------------------

func check_true(text, bool_val):
	if ! bool_val:
		if _results != null:
			_results.add_error(text)
		return false
	return true

func check_that(text, actual, matcher):
	return check_true(text + ": " + matcher.describe(actual), matcher.matches(actual))

func check(text = ""):
	return Checker.new(_results, text)

# ------------------------------------------------------------------------

# result_collector must implement these methods:
#   func start_suite(suite_name)
#   func end_suite()
#   func start_test(name)
#   func end_test()
#   func add_error(message)
#   func has_errors() (does the current suite have any errors?)

func run(result_collector):
	self._results = result_collector
	result_collector.start_suite(filename)
	class_setup()
	if result_collector.has_error():
		return

	var t
	for t in _tests:
		if has_method(t):
			run_test(t)
		else:
			result_collector.add_error("Setup Error: requested function does not exist: " + t)

	class_teardown()
	result_collector.end_suite()

func run_test(name):
	if _results != null:
		_results.start_test(name)
	setup()
	call(name)
	teardown()
	if _results != null:
		_results.end_test()

# -------------------------------------------------------------------------

class Matcher:
	func matches(value):
		return false
	func describe(value):
		return ""
	func _as_str(value):
		if value == null:
			return "<null>"
		if typeof(value) == TYPE_DICTIONARY:
			return value.to_json()
		return "[" + str(value) + "]"
	func _is_list(value):
		# return typeof(value) == TYPE_ARRAY || typeof(value) == TYPE_INT_ARRAY || typeof(value) == TYPE_REAL_ARRAY || typeof(value) == TYPE_STRING_ARRAY
		# All the arrays are in this specific range.
		# This needs to be checked against future versions of Godot.
		var v = typeof(value)
		return v >= TYPE_ARRAY && v <= TYPE_COLOR_ARRAY

class IsMatcher:
	extends Matcher
	var val

	func _init(v):
		val = v

	func matches(value):
		return _inner_match(val, value, [])

	func _inner_match(v1, v2, seen):
		seen.append(v2)
		# Check lists the same way
		if _is_list(v1) && _is_list(v2):
			if v1.size() != v2.size():
				return false
			var i
			for i in range(0, v1.size()):
				if v2[i] in seen:
					# This isn't a correct check; instead,
					# we need to ensure that the seen version of v2[i]
					# matches up with the corresponding seen version of v1[i]
					if v1[i] != v2[i]:
						return false
					# Else prevent infinite loop by just
					# saying it's right.
				elif ! _inner_match(v1[i], v2[i], seen):
					return false
			return true
		# Cannot perform a "==" if the types are different
		if typeof(v1) != typeof(v2):
			return false
		if v1 == v2:
			return true
		if typeof(v1) == TYPE_DICTIONARY:
			if v1.size() != v2.size():
				return false
			var k
			for k in v1:
				if not (k in v2):
					return false
				if v2[k] in seen:
					# This isn't a correct check; instead,
					# we need to ensure that the seen version of v2[i]
					# matches up with the corresponding seen version of v1[i]
					if v1[k] != v2[k]:
						return false
				elif ! _inner_match(v1[k], v2[k], seen):
					return false
			return true
		if typeof(v1) == TYPE_REAL:
			# Closeness
			return abs(float(v1) - v2) <= 0.0000001
		# Any other type should have == match right.
		return false

	func describe(value):
		return "expected " + _as_str(val) + ", found " + _as_str(value)

static func is(value):
	# "is" can be used to make a clear English-like sentence.
	# So, if the value is a matcher, then just use that matcher instead of
	# adding another layer around "is".
	if typeof(value) == TYPE_OBJECT && value.has_method("matches") && value.has_method("describe"):
		return value
	# Need to wrap the value in an is check.
	return IsMatcher.new(value)

static func equals(value):
	return IsMatcher.new(value)

class NotMatcher:
	var val

	func _init(v):
		if typeof(v) == TYPE_OBJECT && v extends Matcher:
			val = v
		else:
			val = IsMatcher.new(v)

	func matches(value):
		return ! val.matches(value)

	func describe(value):
		return "expected not: " + val.describe(value)

static func is_not(value):
	return NotMatcher.new(value)

class BetweenMatcher:
	var lo
	var hi

	func _init(l, h):
		lo = float(l)
		hi = float(h)

	func matches(value):
		return value != null && float(value) >= lo && float(value) <= hi

	func describe(value):
		return "expected [" + str(lo) + ", " + str(hi) + "], found " + str(value)

static func between(lo, hi):
	return BetweenMatcher.new(lo, hi)

class NearMatcher:
	var _epsilon
	var _val

	func _init(val, epsilon = 0.00001):
		_epsilon = float(epsilon)
		_val = float(val)

	func matches(value):
		return abs(float(value) - _val) <= _epsilon

	func describe(value):
		return "expected " + str(_val) + " within " + str(_epsilon) + ", found " + str(value)

static func near(val, epsilon = 0.00001):
	return NearMatcher.new(val, epsilon)

class ContainsMatcher:
	extends Matcher
	var _val

	func _init(val):
		_val = val

	func matches(actual):
		if actual == null:
			return false
		if typeof(actual) == TYPE_STRING:
			# Expect a string to contain a sub-string
			return actual.find(_val) >= 0
		if _is_list(actual):
			if _is_list(_val):
				# each "val" must be in the actual list
				var v
				for v in _val:
					if ! (v in actual):
						return false
				return true
			return _val in actual
		if typeof(actual) == TYPE_RECT2:
			if typeof(_val) == TYPE_RECT2:
				return actual.encloses(_val)
			if typeof(_val) == TYPE_VECTOR2:
				return actual.has_point(_val)
			return false
		if typeof(actual) == TYPE_PLANE:
			if typeof(_val) == TYPE_VECTOR3:
				return actual.has_point(_val)
			return false
		if typeof(actual) == TYPE_DICTIONARY:
			if _is_list(_val):
				return actual.has_all(_val)
		 	return actual.has(_val)

		# These don't really make sense.

		# Object values should just be checked for equality.
		#if typeof(actual) == TYPE_OBJECT:
		#	return actual.get(_val) != null

		return false

	func describe(actual):
		return "expected " + _as_str(actual) + " to contain " + _as_str(_val)

static func contains(val):
	return ContainsMatcher.new(val)

class EmptyMatcher:
	extends Matcher

	func matches(actual):
		if _is_list(actual):
			return actual.size() <= 0
		if typeof(actual) == TYPE_DICTIONARY:
			return actual.keys().size() <= 0
		return false

	func describe(actual):
		if _is_list(actual):
			return "expected " + _as_str(actual) + " to be an empty list"
		if typeof(actual) == TYPE_DICTIONARY:
			return "expected " + _as_str(actual) + " to be an empty dictionary"
		return "expected " + _as_str(actual) + " to be an empty list or dictionary"

static func empty():
	return EmptyMatcher.new()

# ---------------------------------------------------------------------------

class Checker:
	var _text
	var _results

	func _init(results, text):
		_results = results
		_text = text

	func that(actual, matcher):
		var res
		var msg
		if typeof(matcher) == TYPE_BOOL:
			res = matcher
			msg = _text
		else:
			res = matcher.matches(actual)
			msg = _text + ": " + matcher.describe(actual)
		if ! res:
			if _results != null:
				_results.add_error(msg)
			return false
		return true


Fix for input_mapping: Fixed bug when you push a key before you press a button.

extends Control

# Note for the reader:
#
# This demo conveniently uses the same names for actions and for the container nodes
# that hold each remapping button. This allow to get back to the button based simply
# on the name of the corresponding action, but it might not be so simple in your project.
#
# A better approach for large-scale input remapping might be to do the connections between
# buttons and wait_for_input through the code, passing as arguments both the name of the
# action and the node, e.g.:
# button.connect("pressed", self, "wait_for_input", [ button, action ])

# Constants
const INPUT_ACTIONS = [ "move_up", "move_down", "move_left", "move_right", "jump" ]
const CONFIG_FILE = "user://input.cfg"

# Member variables
var action # To register the action the UI is currently handling
var button # Button node corresponding to the above action

# Load/save input mapping to a config file
# Changes done while testing the demo will be persistent, saved to CONFIG_FILE

func load_config():
	var config = ConfigFile.new()
	var err = config.load(CONFIG_FILE)
	if err: # Assuming that file is missing, generate default config
		for action_name in INPUT_ACTIONS:
			var action_list = InputMap.get_action_list(action_name)
			# There could be multiple actions in the list, but we save the first one by default
			var scancode = OS.get_scancode_string(action_list[0].scancode)
			config.set_value("input", action_name, scancode)
		config.save(CONFIG_FILE)
	else: # ConfigFile was properly loaded, initialize InputMap
		for action_name in config.get_section_keys("input"):
			# Get the key scancode corresponding to the saved human-readable string
			var scancode = OS.find_scancode_from_string(config.get_value("input", action_name))
			# Create a new event object based on the saved scancode
			var event = InputEventKey.new()
			event.scancode = scancode
			# Replace old action (key) events by the new one
			for old_event in InputMap.get_action_list(action_name):
				if old_event is InputEventKey:
					InputMap.action_erase_event(action_name, old_event)
			InputMap.action_add_event(action_name, event)

func save_to_config(section, key, value):
	"""Helper function to redefine a parameter in the settings file"""
	var config = ConfigFile.new()
	var err = config.load(CONFIG_FILE)
	if err:
		print("Error code when loading config file: ", err)
	else:
		config.set_value(section, key, value)
		config.save(CONFIG_FILE)

# Input management

func wait_for_input(action_bind):
	action = action_bind
	# See note at the beginning of the script
	button = get_node("bindings").get_node(action).get_node("Button")
	get_node("contextual_help").text = "Press a key to assign to the '" + action + "' action."
	set_process_input(true)

func _input(event):
	# Handle the first pressed key
	if event is InputEventKey:
		# Register the event as handled and stop polling
		get_tree().set_input_as_handled()
		set_process_input(false)
		# Reinitialise the contextual help label
		get_node("contextual_help").text = "Click a key binding to reassign it, or press the Cancel action."
		if not event.is_action("ui_cancel"):
			# Display the string corresponding to the pressed key
			var scancode = OS.get_scancode_string(event.scancode)
			button.text = scancode
			# Start by removing previously key binding(s)
			for old_event in InputMap.get_action_list(action):
				InputMap.action_erase_event(action, old_event)
			# Add the new key binding
			InputMap.action_add_event(action, event)
			save_to_config("input", action, scancode)

func _ready():
	# Load config if existing, if not it will be generated with default values
	load_config()
	# Initialise each button with the default key binding from InputMap
	for action in INPUT_ACTIONS:
		# We assume that the key binding that we want is the first one (0), if there are several
		var input_event = InputMap.get_action_list(action)[0]
		# See note at the beginning of the script
		var button = get_node("bindings").get_node(action).get_node("Button")
		button.text = OS.get_scancode_string(input_event.scancode)
		button.connect("pressed", self, "wait_for_input", [action])

	# Do not start processing input until a button is pressed
	set_process_input(false)


Fixed typo and standardised spacing

Fixed typo and standardised spacing

extends Node2D

#member variables here, example:
#var a=2
#var b="textvar"
const INITIAL_BALL_SPEED = 80
var ball_speed = INITIAL_BALL_SPEED
var screen_size = Vector2(640,400)
#default ball direction
var direction = Vector2(-1,0)
var pad_size = Vector2(8,32)
const PAD_SPEED = 150

func _process(delta):

	#get ball position and pad rectangles
	var ball_pos = get_node("ball").get_pos()
	var left_rect = Rect2( get_node("left").get_pos() - pad_size*0.5, pad_size )
	var right_rect = Rect2( get_node("right").get_pos() - pad_size*0.5, pad_size )

	#integrate new ball postion
	ball_pos+=direction*ball_speed*delta

	#flip when touching roof or floor
	if ( (ball_pos.y<0 and direction.y <0) or (ball_pos.y>screen_size.y and direction.y>0)):
		direction.y = -direction.y

	#flip, change direction and increase speed when touching pads
	if ( (left_rect.has_point(ball_pos) and direction.x < 0) or (right_rect.has_point(ball_pos) and direction.x > 0)):
		direction.x=-direction.x
		ball_speed*=1.1
		direction.y=randf()*2.0-1
		direction = direction.normalized()

	#check gameover
	if (ball_pos.x<0 or ball_pos.x>screen_size.x):
		ball_pos=screen_size*0.5
		ball_speed=INITIAL_BALL_SPEED
		direction=Vector2(-1,0)

	get_node("ball").set_pos(ball_pos)

	#move left pad
	var left_pos = get_node("left").get_pos()

	if (left_pos.y > 0 and Input.is_action_pressed("left_move_up")):
		left_pos.y+=-PAD_SPEED*delta
	if (left_pos.y < screen_size.y and Input.is_action_pressed("left_move_down")):
		left_pos.y+=PAD_SPEED*delta

	get_node("left").set_pos(left_pos)

	#move right pad
	var right_pos = get_node("right").get_pos()

	if (right_pos.y > 0 and Input.is_action_pressed("right_move_up")):
		right_pos.y+=-PAD_SPEED*delta
	if (right_pos.y < screen_size.y and Input.is_action_pressed("right_move_down")):
		right_pos.y+=PAD_SPEED*delta

	get_node("right").set_pos(right_pos)

func _ready():
	screen_size = get_viewport_rect().size #get actual size
	pad_size = get_node("left").get_texture().get_size()
	set_process(true)

extends Node2D

# member variables here, example:
# var a=2
# var b="textvar"
const INITIAL_BALL_SPEED = 80
var ball_speed = INITIAL_BALL_SPEED
var screen_size = Vector2(640,400)
#default ball direction
var direction = Vector2(-1,0)
var pad_size = Vector2(8,32)
const PAD_SPEED = 150

func _process(delta):

	# get ball positio and pad rectangles
	var ball_pos = get_node("ball").get_pos()
	var left_rect = Rect2( get_node("left").get_pos() - pad_size*0.5, pad_size )
	var right_rect = Rect2( get_node("right").get_pos() - pad_size*0.5, pad_size )

	#integrate new ball postion
	ball_pos+=direction*ball_speed*delta

	#flip when touching roof or floor
	if ( (ball_pos.y<0 and direction.y <0) or (ball_pos.y>screen_size.y and direction.y>0)):
		direction.y = -direction.y

	#flip, change direction and increase speed when touching pads
	if ( (left_rect.has_point(ball_pos) and direction.x < 0) or (right_rect.has_point(ball_pos) and direction.x > 0)):
		direction.x=-direction.x
		ball_speed*=1.1
		direction.y=randf()*2.0-1
		direction = direction.normalized()

	#check gameover
	if (ball_pos.x<0 or ball_pos.x>screen_size.x):
		ball_pos=screen_size*0.5
		ball_speed=INITIAL_BALL_SPEED
		direction=Vector2(-1,0)

	get_node("ball").set_pos(ball_pos)

	#move left pad
	var left_pos = get_node("left").get_pos()

	if (left_pos.y > 0 and Input.is_action_pressed("left_move_up")):
		left_pos.y+=-PAD_SPEED*delta
	if (left_pos.y < screen_size.y and Input.is_action_pressed("left_move_down")):
		left_pos.y+=PAD_SPEED*delta

	get_node("left").set_pos(left_pos)

	#move right pad
	var right_pos = get_node("right").get_pos()

	if (right_pos.y > 0 and Input.is_action_pressed("right_move_up")):
		right_pos.y+=-PAD_SPEED*delta
	if (right_pos.y < screen_size.y and Input.is_action_pressed("right_move_down")):
		right_pos.y+=PAD_SPEED*delta

	get_node("right").set_pos(right_pos)

func _ready():
	screen_size = get_viewport_rect().size # get actual size
	pad_size = get_node("left").get_texture().get_size()
	set_process(true)