Adjust child_blocks when moving

Adjust child_blocks when moving

extends Node2D

################################################################################
### Vars

# Gameplay
onready var tilemap   = self.get_node("TileMap")
enum TILES {TILE_EMPTY, TILE_WALL}

export(NodePath) var parent_block_path = null
export(Vector2)  var position_on_parent = Vector2(0, 0)
export(bool)     var is_player = false

var parent_block = null
var child_blocks = []

# TODO: calculate?
var size = 5

# Movement animation
var is_moving = false
var move_vector = Vector2(0.0, 0.0)
var previous_parent = null
var previous_position_on_parent = self.position_on_parent
export var move_duration = 0.5
var current_move_time = 0

# Drawing
onready var viewport  = self.get_node("SelfViewport")
onready var self_rect = self.get_self_rect()

################################################################################
### Setup

func _ready():
	# Add to block group
	self.add_to_group("blocks")

	# Setup viewport
	viewport.set_world_2d(self.get_world_2d())
	viewport.set_rect(self.self_rect)
	#viewport.set_size_override(true, self.self_rect.size)
	var transform = Matrix32(0, -self.self_rect.pos)
	viewport.set_canvas_transform(transform)

	self.set_fixed_process(true)
	self.set_process_input(true) # TODO: do input once in Level

################################################################################
### Parents and Siblings

func find_children():
	var all_blocks = get_tree().get_nodes_in_group("blocks")
	var child_blocks = []
	for b in all_blocks:
		if b.parent_block == self:
			child_blocks.append(b)
	return child_blocks

func set_child_blocks():
	self.child_blocks = self.find_children()

func find_parent_block():
	self.parent_block = self.get_node(self.parent_block_path)
	self.previous_parent = self.parent_block

################################################################################
### Block position
# Represents a square in the world

class BlockPosition:
	var block = null
	var position = Vector2(0, 0)

	func _init(b, p):
		self.block    = b
		self.position = p

	func get_tile_id():
		return self.block.tilemap.get_cell(self.position.x, self.position.y)

	func adjacent(direction):
		return adjacent_recurse(direction, self.block)

	func direction_to_vect(direction):
		if direction == "left":
			return Vector2(-1, 0)
		elif direction == "right":
			return Vector2(1, 0)
		elif direction == "up":
			return Vector2(0, -1)
		elif direction == "down":
			return Vector2(0, 1)

	func adjacent_recurse(direction, original):
		var newpos = self.position + self.direction_to_vect(direction)

		if newpos.x < 0:
			newpos.x = self.block.size - 1
		elif newpos.x >= self.block.size:
			newpos.x = 0
		elif newpos.y < 0:
			newpos.y = self.block.size - 1
		elif newpos.y >= self.block.size:
			newpos.y = 0
		else:
			return get_script().new(self.block, newpos)

		# We left the block

		if self.block.parent_block == original:
			return null

		var newsquare = self.block.own_position().adjacent_recurse(direction, original)

		if newsquare == null:
			return null

		var newblock = newsquare.block_at()

		if newblock == null:
			return null

		return get_script().new(newblock, newpos)

	func is_underlying_walkable():
		return self.get_tile_id() == TILE_EMPTY

	func block_at():
		for b in self.block.child_blocks:
			if b.position_on_parent == self.position:
				return b
		return null

	func is_empty():
		if not self.is_underlying_walkable():
			return false

		if not self.block_at() == null:
			return false

		return true

func own_position():
	return BlockPosition.new(parent_block, position_on_parent)

func adjacent_blocks():
	return adjacent_blocks_with_displacement().keys()

func adjacent_blocks_with_displacement():
	var adjacents = {}
	var a = null

	a = self.own_position().adjacent("left")
	if not a == null:
		var b = a.block_at()
		if not b == null:
			adjacents[b] = Vector2(-1, 0)

	a = self.own_position().adjacent("right")
	if not a == null:
		var b = a.block_at()
		if not b == null:
			adjacents[b] = Vector2(1, 0)

	a = self.own_position().adjacent("up")
	if not a == null:
		var b = a.block_at()
		if not b == null:
			adjacents[b] = Vector2(0, -1)

	a = self.own_position().adjacent("down")
	if not a == null:
		var b = a.block_at()
		if not b == null:
			adjacents[b] = Vector2(0, 1)

	return adjacents

################################################################################
### Drawing

func get_self_rect():
	var pos = tilemap.get_global_pos()
	# TODO: scale?
	var s = self.size * tilemap.get_cell_size()
	return Rect2(pos, s)

func draw_self_on(block, position):
	var texture = self.viewport.get_render_target_texture()

	var to_parent_transform = block.get_global_transform() * self.get_global_transform().affine_inverse()

	var tile_size = self.tilemap.get_cell_size() # TODO: self?
	var drawrect = Rect2(position, tile_size)

	self.draw_set_transform_matrix(to_parent_transform)
	self.draw_texture_rect(texture, drawrect, false)

func _draw():
	if self.is_moving:
		var t = easing_function(self.current_move_time / self.move_duration)

		var newpos = self.parent_block.tilemap.map_to_world(self.position_on_parent)
		var oldpos = self.parent_block.tilemap.map_to_world(self.position_on_parent - self.move_vector)

		var pos = interpolate(t, oldpos, newpos)
		draw_self_on(self.parent_block, pos)

		if not self.previous_parent == self.parent_block:
			var newpos = self.previous_parent.tilemap.map_to_world(self.previous_position_on_parent + self.move_vector)
			var oldpos = self.previous_parent.tilemap.map_to_world(self.previous_position_on_parent)

			var pos = interpolate(t, oldpos, newpos)
			draw_self_on(self.previous_parent, pos)
	else:
		var worldcoords = self.parent_block.tilemap.map_to_world(self.position_on_parent)
		draw_self_on(self.parent_block, worldcoords)

func interpolate(t, x1, x2):
	return (1-t)*x1 + t*x2

func easing_function(t):
	var u0 = 0
	var u1 = 0.2
	var u2 = 0.95
	var u3 = 1
	return u0 * (1-t*t*t) + 3*u1*(1-t*t)*t + 3*u2*(1-t)*t*t + u3*t*t*t*t

func _fixed_process(delta):
	if self.is_moving:
		self.current_move_time += delta
		if self.current_move_time > self.move_duration:
			self.is_moving = false
			self.current_move_time = 0

		self.update()

################################################################################
### Movement

func try_move(direction):
	if self.is_moving:
		return

	var a = self.own_position().adjacent(direction)
	if a == null:
		return

	if a.is_empty():
		do_move(direction, a)

func do_move(direction, new_square):
	self.is_moving = true

	# TODO: silly
	self.move_vector = self.own_position().direction_to_vect(direction)

	self.previous_parent = self.parent_block
	self.previous_position_on_parent = self.position_on_parent

	self.parent_block = new_square.block
	self.position_on_parent = new_square.position

	if not self.parent_block == self.previous_parent:
		self.previous_parent.child_blocks.erase(self)
		self.parent_block.children.append(self)

func _input(event):
	if not self.is_player:
		return

	if event.is_action_pressed("move_left"):
		self.try_move("left")

	if event.is_action_pressed("move_right"):
		self.try_move("right")

	if event.is_action_pressed("move_up"):
		self.try_move("up")

	if event.is_action_pressed("move_down"):
		self.try_move("down")

extends Node2D

################################################################################
### Vars

# Gameplay
onready var tilemap   = self.get_node("TileMap")
enum TILES {TILE_EMPTY, TILE_WALL}

export(NodePath) var parent_block_path = null
export(Vector2)  var position_on_parent = Vector2(0, 0)
export(bool)     var is_player = false

var parent_block = null
var child_blocks = []

# TODO: calculate?
var size = 5

# Movement animation
var is_moving = false
var move_vector = Vector2(0.0, 0.0)
var previous_parent = null
var previous_position_on_parent = self.position_on_parent
export var move_duration = 0.5
var current_move_time = 0

# Drawing
onready var viewport  = self.get_node("SelfViewport")
onready var self_rect = self.get_self_rect()

################################################################################
### Setup

func _ready():
	# Add to block group
	self.add_to_group("blocks")

	# Setup viewport
	viewport.set_world_2d(self.get_world_2d())
	viewport.set_rect(self.self_rect)
	#viewport.set_size_override(true, self.self_rect.size)
	var transform = Matrix32(0, -self.self_rect.pos)
	viewport.set_canvas_transform(transform)

	self.set_fixed_process(true)
	self.set_process_input(true) # TODO: do input once in Level

################################################################################
### Parents and Siblings

func find_children():
	var all_blocks = get_tree().get_nodes_in_group("blocks")
	var child_blocks = []
	for b in all_blocks:
		if b.parent_block == self:
			child_blocks.append(b)
	return child_blocks

func set_child_blocks():
	self.child_blocks = self.find_children()

func find_parent_block():
	self.parent_block = self.get_node(self.parent_block_path)
	self.previous_parent = self.parent_block

################################################################################
### Block position
# Represents a square in the world

class BlockPosition:
	var block = null
	var position = Vector2(0, 0)

	func _init(b, p):
		self.block    = b
		self.position = p

	func get_tile_id():
		return self.block.tilemap.get_cell(self.position.x, self.position.y)

	func adjacent(direction):
		return adjacent_recurse(direction, self.block)

	func direction_to_vect(direction):
		if direction == "left":
			return Vector2(-1, 0)
		elif direction == "right":
			return Vector2(1, 0)
		elif direction == "up":
			return Vector2(0, -1)
		elif direction == "down":
			return Vector2(0, 1)

	func adjacent_recurse(direction, original):
		var newpos = self.position + self.direction_to_vect(direction)

		if newpos.x < 0:
			newpos.x = self.block.size - 1
		elif newpos.x >= self.block.size:
			newpos.x = 0
		elif newpos.y < 0:
			newpos.y = self.block.size - 1
		elif newpos.y >= self.block.size:
			newpos.y = 0
		else:
			return get_script().new(self.block, newpos)

		# We left the block

		if self.block.parent_block == original:
			return null

		var newsquare = self.block.own_position().adjacent_recurse(direction, original)

		if newsquare == null:
			return null

		var newblock = newsquare.block_at()

		if newblock == null:
			return null

		return get_script().new(newblock, newpos)

	func is_underlying_walkable():
		return self.get_tile_id() == TILE_EMPTY

	func block_at():
		for b in self.block.child_blocks:
			if b.position_on_parent == self.position:
				return b
		return null

	func is_empty():
		if not self.is_underlying_walkable():
			return false

		if not self.block_at() == null:
			return false

		return true

func own_position():
	return BlockPosition.new(parent_block, position_on_parent)

func adjacent_blocks():
	return adjacent_blocks_with_displacement().keys()

func adjacent_blocks_with_displacement():
	var adjacents = {}
	var a = null

	a = self.own_position().adjacent("left")
	if not a == null:
		var b = a.block_at()
		if not b == null:
			adjacents[b] = Vector2(-1, 0)

	a = self.own_position().adjacent("right")
	if not a == null:
		var b = a.block_at()
		if not b == null:
			adjacents[b] = Vector2(1, 0)

	a = self.own_position().adjacent("up")
	if not a == null:
		var b = a.block_at()
		if not b == null:
			adjacents[b] = Vector2(0, -1)

	a = self.own_position().adjacent("down")
	if not a == null:
		var b = a.block_at()
		if not b == null:
			adjacents[b] = Vector2(0, 1)

	return adjacents

################################################################################
### Drawing

func get_self_rect():
	var pos = tilemap.get_global_pos()
	# TODO: scale?
	var s = self.size * tilemap.get_cell_size()
	return Rect2(pos, s)

func draw_self_on(block, position):
	var texture = self.viewport.get_render_target_texture()

	var to_parent_transform = block.get_global_transform() * self.get_global_transform().affine_inverse()

	var tile_size = self.tilemap.get_cell_size() # TODO: self?
	var drawrect = Rect2(position, tile_size)

	self.draw_set_transform_matrix(to_parent_transform)
	self.draw_texture_rect(texture, drawrect, false)

func _draw():
	if self.is_moving:
		var t = easing_function(self.current_move_time / self.move_duration)

		var newpos = self.parent_block.tilemap.map_to_world(self.position_on_parent)
		var oldpos = self.parent_block.tilemap.map_to_world(self.position_on_parent - self.move_vector)

		var pos = interpolate(t, oldpos, newpos)
		draw_self_on(self.parent_block, pos)

		if not self.previous_parent == self.parent_block:
			var newpos = self.previous_parent.tilemap.map_to_world(self.previous_position_on_parent + self.move_vector)
			var oldpos = self.previous_parent.tilemap.map_to_world(self.previous_position_on_parent)

			var pos = interpolate(t, oldpos, newpos)
			draw_self_on(self.previous_parent, pos)
	else:
		var worldcoords = self.parent_block.tilemap.map_to_world(self.position_on_parent)
		draw_self_on(self.parent_block, worldcoords)

func interpolate(t, x1, x2):
	return (1-t)*x1 + t*x2

func easing_function(t):
	var u0 = 0
	var u1 = 0.2
	var u2 = 0.95
	var u3 = 1
	return u0 * (1-t*t*t) + 3*u1*(1-t*t)*t + 3*u2*(1-t)*t*t + u3*t*t*t*t

func _fixed_process(delta):
	if self.is_moving:
		self.current_move_time += delta
		if self.current_move_time > self.move_duration:
			self.is_moving = false
			self.current_move_time = 0
			self.previous_parent = self.parent_block
			self.previous_position_on_parent = self.position_on_parent

		self.update()

################################################################################
### Movement

func try_move(direction):
	if self.is_moving:
		return

	var a = self.own_position().adjacent(direction)
	if a == null:
		return

	if a.is_empty():
		do_move(direction, a)

func do_move(direction, new_square):
	self.is_moving = true

	# TODO: silly
	self.move_vector = self.own_position().direction_to_vect(direction)

	# TODO! Possibly update child blocks
	self.parent_block = new_square.block
	self.position_on_parent = new_square.position

func _input(event):
	if not self.is_player:
		return

	if event.is_action_pressed("move_left"):
		self.try_move("left")

	if event.is_action_pressed("move_right"):
		self.try_move("right")

	if event.is_action_pressed("move_up"):
		self.try_move("up")

	if event.is_action_pressed("move_down"):
		self.try_move("down")


Fix MIDI devices scanning

Fix MIDI devices scanning

extends Node

onready var rtl = $HBoxContainer/Features
onready var mono_test = $MonoTest

# Returns a human-readable string from a date and time, date, or time dictionary.
func datetime_to_string(date):
	if (
		date.has("year")
		and date.has("month")
		and date.has("day")
		and date.has("hour")
		and date.has("minute")
		and date.has("second")
	):
		# Date and time.
		return "{year}-{month}-{day} {hour}:{minute}:{second}".format({
			year = str(date.year).pad_zeros(2),
			month = str(date.month).pad_zeros(2),
			day = str(date.day).pad_zeros(2),
			hour = str(date.hour).pad_zeros(2),
			minute = str(date.minute).pad_zeros(2),
			second = str(date.second).pad_zeros(2),
		})
	elif date.has("year") and date.has("month") and date.has("day"):
		# Date only.
		return "{year}-{month}-{day}".format({
			year = str(date.year).pad_zeros(2),
			month = str(date.month).pad_zeros(2),
			day = str(date.day).pad_zeros(2),
		})
	else:
		# Time only.
		return "{hour}:{minute}:{second}".format({
			hour = str(date.hour).pad_zeros(2),
			minute = str(date.minute).pad_zeros(2),
			second = str(date.second).pad_zeros(2),
		})

func scan_midi_devices():
	OS.open_midi_inputs()
	var devices = OS.get_connected_midi_inputs().join(", ")
	OS.close_midi_inputs()
	return devices

func add_header(header):
	rtl.append_bbcode("\n[b][u][color=#6df]{header}[/color][/u][/b]\n".format({
		header = header,
	}))

func add_line(key, value):
	rtl.append_bbcode("[b]{key}:[/b] {value}\n".format({
		key = key,
		value = value if str(value) != "" else "[color=#8fff](empty)[/color]",
	}))

func _ready():
	add_header("Audio")
	var audio_drivers = PoolStringArray()
	for i in OS.get_audio_driver_count():
		audio_drivers.push_back(OS.get_audio_driver_name(i))
	add_line("Available drivers", audio_drivers.join(", "))
	add_line("MIDI inputs", scan_midi_devices())

	add_header("Date")
	add_line("Date and time (local)", datetime_to_string(OS.get_datetime()))
	add_line("Date and time (UTC)", datetime_to_string(OS.get_datetime(true)))
	add_line("Date (local)", datetime_to_string(OS.get_date()))
	add_line("Date (UTC)", datetime_to_string(OS.get_date(true)))
	add_line("Time (local)", datetime_to_string(OS.get_time()))
	add_line("Time (UTC)", datetime_to_string(OS.get_time(true)))
	add_line("Timezone", OS.get_time_zone_info())
	add_line("System time (milliseconds)", OS.get_system_time_msecs())
	add_line("System time (seconds)", OS.get_system_time_secs())
	add_line("UNIX time", OS.get_unix_time())

	add_header("Display")
	add_line("Screen count", OS.get_screen_count())
	add_line("DPI", OS.get_screen_dpi())
	add_line("Startup screen position", OS.get_screen_position())
	add_line("Startup screen size", OS.get_screen_size())
	add_line("Safe area rectangle", OS.get_window_safe_area())
	add_line("Screen orientation", [
		"Landscape",
		"Portrait",
		"Landscape (reverse)",
		"Portrait (reverse)",
		"Landscape (defined by sensor)",
		"Portrait (defined by sensor)",
		"Defined by sensor",
	][OS.screen_orientation])

	add_header("Engine")
	add_line("Command-line arguments", str(OS.get_cmdline_args()))
	add_line("Is debug build", OS.is_debug_build())
	add_line("Executable path", OS.get_executable_path())
	add_line("User data directory", OS.get_user_data_dir())
	add_line("Filesystem is persistent", OS.is_userfs_persistent())

	add_header("Environment")
	add_line("Value of `PATH`", OS.get_environment("PATH"))
	add_line("Value of `path`", OS.get_environment("path"))

	add_header("Hardware")
	add_line("Model name", OS.get_model_name())
	add_line("Processor count", OS.get_processor_count())
	add_line("Device unique ID", OS.get_unique_id())
	add_line("Video adapter name", VisualServer.get_video_adapter_name())
	add_line("Video adapter vendor", VisualServer.get_video_adapter_vendor())

	add_header("Input")
	add_line("Latin keyboard variant", OS.get_latin_keyboard_variant())
	add_line("Device has touch screen", OS.has_touchscreen_ui_hint())
	add_line("Device has virtual keyboard", OS.has_virtual_keyboard())
	add_line("Virtual keyboard height", OS.get_virtual_keyboard_height())

	add_header("Localization")
	add_line("Locale", OS.get_locale())

	add_header("Mobile")
	add_line("Granted permissions", OS.get_granted_permissions())

	add_header("Mono (C#)")
	var mono_enabled = ResourceLoader.exists("res://MonoTest.cs")
	add_line("Mono module enabled", "Yes" if mono_enabled else "No")
	if mono_enabled:
		mono_test.set_script(load("res://MonoTest.cs"))
		add_line("Operating System", mono_test.OperatingSystem())
		add_line("Platform Type", mono_test.PlatformType())

	add_header("Software")
	add_line("OS name", OS.get_name())
	add_line("Process ID", OS.get_process_id())

	add_header("System directories")
	add_line("Desktop", OS.get_system_dir(OS.SYSTEM_DIR_DESKTOP))
	add_line("DCIM", OS.get_system_dir(OS.SYSTEM_DIR_DCIM))
	add_line("Documents", OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS))
	add_line("Downloads", OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS))
	add_line("Movies", OS.get_system_dir(OS.SYSTEM_DIR_MOVIES))
	add_line("Music", OS.get_system_dir(OS.SYSTEM_DIR_MUSIC))
	add_line("Pictures", OS.get_system_dir(OS.SYSTEM_DIR_PICTURES))
	add_line("Ringtones", OS.get_system_dir(OS.SYSTEM_DIR_RINGTONES))

	add_header("Video")
	var video_drivers = PoolStringArray()
	for i in OS.get_video_driver_count():
		video_drivers.push_back(OS.get_video_driver_name(i))
	add_line("Available drivers", video_drivers.join(", "))
	add_line("Current driver", OS.get_video_driver_name(OS.get_current_video_driver()))

extends Node

onready var rtl = $HBoxContainer/Features
onready var mono_test = $MonoTest

# Returns a human-readable string from a date and time, date, or time dictionary.
func datetime_to_string(date):
	if (
		date.has("year")
		and date.has("month")
		and date.has("day")
		and date.has("hour")
		and date.has("minute")
		and date.has("second")
	):
		# Date and time.
		return "{year}-{month}-{day} {hour}:{minute}:{second}".format({
			year = str(date.year).pad_zeros(2),
			month = str(date.month).pad_zeros(2),
			day = str(date.day).pad_zeros(2),
			hour = str(date.hour).pad_zeros(2),
			minute = str(date.minute).pad_zeros(2),
			second = str(date.second).pad_zeros(2),
		})
	elif date.has("year") and date.has("month") and date.has("day"):
		# Date only.
		return "{year}-{month}-{day}".format({
			year = str(date.year).pad_zeros(2),
			month = str(date.month).pad_zeros(2),
			day = str(date.day).pad_zeros(2),
		})
	else:
		# Time only.
		return "{hour}:{minute}:{second}".format({
			hour = str(date.hour).pad_zeros(2),
			minute = str(date.minute).pad_zeros(2),
			second = str(date.second).pad_zeros(2),
		})

func add_header(header):
	rtl.append_bbcode("\n[b][u][color=#6df]{header}[/color][/u][/b]\n".format({
		header = header,
	}))

func add_line(key, value):
	rtl.append_bbcode("[b]{key}:[/b] {value}\n".format({
		key = key,
		value = value if str(value) != "" else "[color=#8fff](empty)[/color]",
	}))

func _ready():
	add_header("Audio")
	var audio_drivers = PoolStringArray()
	for i in OS.get_audio_driver_count():
		audio_drivers.push_back(OS.get_audio_driver_name(i))
	add_line("Available drivers", audio_drivers.join(", "))
	add_line("MIDI inputs", OS.get_connected_midi_inputs().join(", "))

	add_header("Date")
	add_line("Date and time (local)", datetime_to_string(OS.get_datetime()))
	add_line("Date and time (UTC)", datetime_to_string(OS.get_datetime(true)))
	add_line("Date (local)", datetime_to_string(OS.get_date()))
	add_line("Date (UTC)", datetime_to_string(OS.get_date(true)))
	add_line("Time (local)", datetime_to_string(OS.get_time()))
	add_line("Time (UTC)", datetime_to_string(OS.get_time(true)))
	add_line("Timezone", OS.get_time_zone_info())
	add_line("System time (milliseconds)", OS.get_system_time_msecs())
	add_line("System time (seconds)", OS.get_system_time_secs())
	add_line("UNIX time", OS.get_unix_time())

	add_header("Display")
	add_line("Screen count", OS.get_screen_count())
	add_line("DPI", OS.get_screen_dpi())
	add_line("Startup screen position", OS.get_screen_position())
	add_line("Startup screen size", OS.get_screen_size())
	add_line("Safe area rectangle", OS.get_window_safe_area())
	add_line("Screen orientation", [
		"Landscape",
		"Portrait",
		"Landscape (reverse)",
		"Portrait (reverse)",
		"Landscape (defined by sensor)",
		"Portrait (defined by sensor)",
		"Defined by sensor",
	][OS.screen_orientation])

	add_header("Engine")
	add_line("Command-line arguments", str(OS.get_cmdline_args()))
	add_line("Is debug build", OS.is_debug_build())
	add_line("Executable path", OS.get_executable_path())
	add_line("User data directory", OS.get_user_data_dir())
	add_line("Filesystem is persistent", OS.is_userfs_persistent())

	add_header("Environment")
	add_line("Value of `PATH`", OS.get_environment("PATH"))
	add_line("Value of `path`", OS.get_environment("path"))

	add_header("Hardware")
	add_line("Model name", OS.get_model_name())
	add_line("Processor count", OS.get_processor_count())
	add_line("Device unique ID", OS.get_unique_id())
	add_line("Video adapter name", VisualServer.get_video_adapter_name())
	add_line("Video adapter vendor", VisualServer.get_video_adapter_vendor())

	add_header("Input")
	add_line("Latin keyboard variant", OS.get_latin_keyboard_variant())
	add_line("Device has touch screen", OS.has_touchscreen_ui_hint())
	add_line("Device has virtual keyboard", OS.has_virtual_keyboard())
	add_line("Virtual keyboard height", OS.get_virtual_keyboard_height())

	add_header("Localization")
	add_line("Locale", OS.get_locale())

	add_header("Mobile")
	add_line("Granted permissions", OS.get_granted_permissions())

	add_header("Mono (C#)")
	var mono_enabled = ResourceLoader.exists("res://MonoTest.cs")
	add_line("Mono module enabled", "Yes" if mono_enabled else "No")
	if mono_enabled:
		mono_test.set_script(load("res://MonoTest.cs"))
		add_line("Operating System", mono_test.OperatingSystem())
		add_line("Platform Type", mono_test.PlatformType())

	add_header("Software")
	add_line("OS name", OS.get_name())
	add_line("Process ID", OS.get_process_id())

	add_header("System directories")
	add_line("Desktop", OS.get_system_dir(OS.SYSTEM_DIR_DESKTOP))
	add_line("DCIM", OS.get_system_dir(OS.SYSTEM_DIR_DCIM))
	add_line("Documents", OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS))
	add_line("Downloads", OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS))
	add_line("Movies", OS.get_system_dir(OS.SYSTEM_DIR_MOVIES))
	add_line("Music", OS.get_system_dir(OS.SYSTEM_DIR_MUSIC))
	add_line("Pictures", OS.get_system_dir(OS.SYSTEM_DIR_PICTURES))
	add_line("Ringtones", OS.get_system_dir(OS.SYSTEM_DIR_RINGTONES))

	add_header("Video")
	var video_drivers = PoolStringArray()
	for i in OS.get_video_driver_count():
		video_drivers.push_back(OS.get_video_driver_name(i))
	add_line("Available drivers", video_drivers.join(", "))
	add_line("Current driver", OS.get_video_driver_name(OS.get_current_video_driver()))


Added different platforms

Added different platforms

var bag

var segments = []
var last_visited_segment = -1

var SCREEN_WIDTH = Globals.get("display/width")
var SCREEN_HEIGHT = Globals.get("display/height")
var SCREEN_MARGIN = 200

var SEGMENT_SIZE = 60
var SEGMENT_LINE_HEIGHT = 36

var WALL_HEIGHT = 39
var WALL_HALF_WIDTH = 36

var platforms = {
    'grass' : [
        { 'width' : 100, 'template' : preload('res://scenes/map/platform_grass_big.xscn') },
        { 'width' : 100, 'template' : preload('res://scenes/map/platform_grass_medium.xscn') },
        { 'width' : 100, 'template' : preload('res://scenes/map/platform_grass_small.xscn') }
    ],
    'ice' : [
        { 'width' : 100, 'template' : preload('res://scenes/map/platform_ice_big.xscn') },
        { 'width' : 100, 'template' : preload('res://scenes/map/platform_ice_medium.xscn') },
        { 'width' : 100, 'template' : preload('res://scenes/map/platform_ice_small.xscn') }
    ]
}

var left_wall_template = preload('res://scenes/map/wall_left.xscn')
var right_wall_template = preload('res://scenes/map/wall_right.xscn')
var separator_template = preload('res://scenes/map/base_platform.xscn')
var playable_width = 0

func _init_bag(bag):
    self.bag = bag
    self.playable_width = self.SCREEN_WIDTH - (self.SCREEN_MARGIN * 2)
    self.reset_map()

func reset_map():
    for segment in self.segments:
        self.destroy_unused_segment(segment)
    self.segments.clear()

func generate_next_map_segment():
    var next_segment_index = self.segments.size()

    var new_segment = []

    new_segment = self.add_walls(new_segment)
    new_segment = self.add_separator(new_segment)
    new_segment = self.add_platforms(new_segment, next_segment_index)

    self.segments.append(new_segment)
    self.apply_segment(next_segment_index)
    self.destroy_unused_segment_by_id(next_segment_index - 3)

func apply_segment(id):
    var segment_offset = id * self.SEGMENT_SIZE * self.SEGMENT_LINE_HEIGHT
    for object in self.segments[id]:
        self.bag.action_controller.attach_object(object.object)
        object.object.set_global_pos(Vector2(object.x, -object.y - segment_offset))

func destroy_unused_segment_by_id(id):
    if id < 0:
        return

    self.destroy_unused_segment(self.segments[id])

func destroy_unused_segment(segment):
    for object in segment:
        self.bag.action_controller.detach_object(object.object)
        object.object.queue_free()

    segment.clear()

func add_segment_object(segment, x, y, object):
    segment.append({
        'x' : x,
        'y' : y,
        'object' : object
    })

    return segment

func add_separator(segment):
    var separator_platform = self.separator_template.instance()
    segment = self.add_segment_object(segment, -10, self.SEGMENT_LINE_HEIGHT, separator_platform)
    return segment

func add_walls(segment):
    var height = 0
    var left_wall
    var right_wall
    var wall_offset
    var real_height

    while height < self.SEGMENT_SIZE:
        left_wall = self.left_wall_template.instance()
        right_wall = self.right_wall_template.instance()

        wall_offset = self.SCREEN_MARGIN - self.WALL_HALF_WIDTH
        real_height = (height + self.WALL_HEIGHT / 2) * self.SEGMENT_LINE_HEIGHT
        segment = self.add_segment_object(segment, wall_offset, real_height, left_wall)
        segment = self.add_segment_object(segment, wall_offset + self.playable_width + self.WALL_HALF_WIDTH * 2, real_height, right_wall)

        height = height + self.WALL_HEIGHT
    return segment

func update_segments(player_height):
    var segment_height = self.SEGMENT_SIZE * self.SEGMENT_LINE_HEIGHT
    var overflow = player_height % segment_height

    var segment_num = (player_height - overflow) / segment_height

    if overflow > 100 && segment_num > self.last_visited_segment:
        self.last_visited_segment = segment_num
        self.generate_next_map_segment()

func add_platforms(segment, index):
    var iterator = 2
    var last_iterator = 0

    var templates
    if index < 3:
        templates = self.platforms.grass
    else:
        templates = self.platforms.ice

    randomize()

    while iterator < self.SEGMENT_SIZE - 2:
        segment = self.generate_single_platform(segment, iterator, templates)

        last_iterator = iterator

        if randi() % 10 == 0:
            iterator = iterator + 5
        else:
            iterator = iterator + 4

    if last_iterator < self.SEGMENT_SIZE - 4:
        segment = self.generate_single_platform(segment, self.SEGMENT_SIZE - 2, templates)

    return segment

func generate_single_platform(segment, iterator, templates):
    var template = templates[randi() % templates.size()]
    var new_platform = template.template.instance()

    var horizontal_position = randi() % (self.playable_width - template.width)
    horizontal_position = horizontal_position + self.SCREEN_MARGIN + int(template.width / 2)

    segment = self.add_segment_object(segment, horizontal_position, (iterator + 1) * self.SEGMENT_LINE_HEIGHT, new_platform)

    return segment

var bag

var segments = []
var last_visited_segment = -1

var SCREEN_WIDTH = Globals.get("display/width")
var SCREEN_HEIGHT = Globals.get("display/height")
var SCREEN_MARGIN = 200

var SEGMENT_SIZE = 60
var SEGMENT_LINE_HEIGHT = 36

var WALL_HEIGHT = 39
var WALL_HALF_WIDTH = 36

var platforms = [
    { 'width' : 100, 'template' : preload('res://scenes/map/platform_grass.xscn') }
]

var left_wall_template = preload('res://scenes/map/wall_left.xscn')
var right_wall_template = preload('res://scenes/map/wall_right.xscn')
var separator_template = preload('res://scenes/map/base_platform.xscn')
var playable_width = 0

func _init_bag(bag):
    self.bag = bag
    self.playable_width = self.SCREEN_WIDTH - (self.SCREEN_MARGIN * 2)
    self.reset_map()

func reset_map():
    for segment in self.segments:
        self.destroy_unused_segment(segment)
    self.segments.clear()

func generate_next_map_segment():
    var next_segment_index = self.segments.size()

    var new_segment = []

    new_segment = self.add_walls(new_segment)
    new_segment = self.add_separator(new_segment)
    new_segment = self.add_platforms(new_segment)

    self.segments.append(new_segment)
    self.apply_segment(next_segment_index)
    self.destroy_unused_segment_by_id(next_segment_index - 3)

func apply_segment(id):
    var segment_offset = id * self.SEGMENT_SIZE * self.SEGMENT_LINE_HEIGHT
    for object in self.segments[id]:
        self.bag.action_controller.attach_object(object.object)
        object.object.set_global_pos(Vector2(object.x, -object.y - segment_offset))

func destroy_unused_segment_by_id(id):
    if id < 0:
        return

    self.destroy_unused_segment(self.segments[id])

func destroy_unused_segment(segment):
    for object in segment:
        self.bag.action_controller.detach_object(object.object)
        object.object.queue_free()

    segment.clear()

func add_segment_object(segment, x, y, object):
    segment.append({
        'x' : x,
        'y' : y,
        'object' : object
    })

    return segment

func add_separator(segment):
    var separator_platform = self.separator_template.instance()
    segment = self.add_segment_object(segment, -10, self.SEGMENT_LINE_HEIGHT, separator_platform)
    return segment

func add_walls(segment):
    var height = 0
    var left_wall
    var right_wall
    var wall_offset
    var real_height

    while height < self.SEGMENT_SIZE:
        left_wall = self.left_wall_template.instance()
        right_wall = self.right_wall_template.instance()

        wall_offset = self.SCREEN_MARGIN - self.WALL_HALF_WIDTH
        real_height = (height + self.WALL_HEIGHT / 2) * self.SEGMENT_LINE_HEIGHT
        segment = self.add_segment_object(segment, wall_offset, real_height, left_wall)
        segment = self.add_segment_object(segment, wall_offset + self.playable_width + self.WALL_HALF_WIDTH * 2, real_height, right_wall)

        height = height + self.WALL_HEIGHT
    return segment

func update_segments(player_height):
    var segment_height = self.SEGMENT_SIZE * self.SEGMENT_LINE_HEIGHT
    var overflow = player_height % segment_height

    var segment_num = (player_height - overflow) / segment_height

    if overflow > 100 && segment_num > self.last_visited_segment:
        self.last_visited_segment = segment_num
        self.generate_next_map_segment()

func add_platforms(segment):
    var iterator = 2
    var last_iterator = 0

    randomize()

    while iterator < self.SEGMENT_SIZE - 2:
        segment = self.generate_single_platform(segment, iterator)

        last_iterator = iterator

        if randi() % 10 == 0:
            iterator = iterator + 5
        else:
            iterator = iterator + 4

    if last_iterator < self.SEGMENT_SIZE - 4:
        segment = self.generate_single_platform(segment, self.SEGMENT_SIZE - 2)

    return segment

func generate_single_platform(segment, iterator):
    var template = self.platforms[randi() % self.platforms.size()]
    var new_platform = template.template.instance()

    var horizontal_position = randi() % (self.playable_width - template.width)
    horizontal_position = horizontal_position + self.SCREEN_MARGIN + int(template.width / 2)

    segment = self.add_segment_object(segment, horizontal_position, (iterator + 1) * self.SEGMENT_LINE_HEIGHT, new_platform)

    return segment


add temporary 2 keyboard buttons controls using page_up and page_down buttons

extends KinematicBody2D

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

#### VARIABLES ####

## Export

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

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

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

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

## FSM

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

## PHYSICS

const GRAVITY = 800
const WALK_SPEED = 150
const RUN_SPEED = 350
const JUMP_SPEED = 70
const CLIMB_SPEED = Vector2(70, 130)

var velocity = Vector2()

const MAX_JUMP_TIME = 0.1
var jumping_time = 0

#### ONREADY ####

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

func _ready():
	set_fixed_process(true)
	set_process_input(true)

#### METHODS ####

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

func _input(event):
	update_controls(event)

# PHYSICS

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

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

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

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

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

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

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

	velocity.y -= JUMP_SPEED

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

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

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

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

	move_and_slide(direction * CLIMB_SPEED)

	velocity.y = 0
	velocity.x = 0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# Interactions

func update_controls(event):
	# update control_x_pressed state

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

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

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

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

func die():
	emit_signal("die")
	#get_tree().get_root().get_node("root").restart()

func exit():
	emit_signal("exit")
	#get_tree().get_root().get_node("root").show_menu()

extends KinematicBody2D

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

#### VARIABLES ####

## Export

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

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

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

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

## FSM

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

## PHYSICS

const GRAVITY = 800
const WALK_SPEED = 150
const RUN_SPEED = 350
const JUMP_SPEED = 70
const CLIMB_SPEED = Vector2(70, 130)

var velocity = Vector2()

const MAX_JUMP_TIME = 0.1
var jumping_time = 0

#### ONREADY ####

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

func _ready():
	set_fixed_process(true)
	set_process_input(true)

#### METHODS ####

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

func _input(event):
	update_controls(event)

# PHYSICS

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

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

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

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

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

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

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

	velocity.y -= JUMP_SPEED

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

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

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

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

	move_and_slide(direction * CLIMB_SPEED)

	velocity.y = 0
	velocity.x = 0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# Interactions

func update_controls(event):
	# update control_x_pressed state

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

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

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

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

func die():
	emit_signal("die")
	#get_tree().get_root().get_node("root").restart()

func exit():
	emit_signal("exit")
	#get_tree().get_root().get_node("root").show_menu()


Deleted unused variable

Deleted unused variable

Deleted the var GRAVITY because it is unused. The gravity is used at line 237, but it's gotten from the Physics2DDirectBodyState parameter.

extends RigidBody2D

# Character Demo, written by Juan Linietsky.
#
# Implementation of a 2D Character controller.
# This implementation uses the physics engine for
# controlling a character, in a very similar way
# than a 3D character controller would be implemented.
#
# Using the physics engine for this has the main
# advantages:
# -Easy to write.
# -Interaction with other physics-based objects is free
# -Only have to deal with the object linear velocity, not position
# -All collision/area framework available
#
# But also has the following disadvantages:
#
# -Objects may bounce a little bit sometimes
# -Going up ramps sends the chracter flying up, small hack is needed.
# -A ray collider is needed to avoid sliding down on ramps and
#   undesiderd bumps, small steps and rare numerical precision errors.
#   (another alternative may be to turn on friction when the character is not moving).
# -Friction cant be used, so floor velocity must be considered
#  for moving platforms.

var anim=""
var siding_left=false
var jumping=false
var stopping_jump=false
var shooting=false

var WALK_ACCEL = 800.0
var WALK_DEACCEL= 800.0
var WALK_MAX_VELOCITY= 200.0
var AIR_ACCEL = 200.0
var AIR_DEACCEL= 200.0
var JUMP_VELOCITY=460
var STOP_JUMP_FORCE=900.0

var MAX_FLOOR_AIRBORNE_TIME = 0.15

var airborne_time=1e20
var shoot_time=1e20

var MAX_SHOOT_POSE_TIME = 0.3

var bullet = preload("res://bullet.xml")

var floor_h_velocity=0.0
var enemy

func _integrate_forces(s):

	var lv = s.get_linear_velocity()
	var step = s.get_step()

	var new_anim=anim
	var new_siding_left=siding_left

	# Get the controls
	var move_left = Input.is_action_pressed("move_left")
	var move_right = Input.is_action_pressed("move_right")
	var jump = Input.is_action_pressed("jump")
	var shoot = Input.is_action_pressed("shoot")
	var spawn = Input.is_action_pressed("spawn")

	if spawn:
		var e = enemy.instance()
		var p = get_pos()
		p.y = p.y - 100
		e.set_pos(p)
		get_parent().add_child(e)

	#deapply prev floor velocity
	lv.x-=floor_h_velocity
	floor_h_velocity=0.0

	# Find the floor (a contact with upwards facing collision normal)
	var found_floor=false
	var floor_index=-1

	for x in range(s.get_contact_count()):

		var ci = s.get_contact_local_normal(x)
		if (ci.dot(Vector2(0,-1))>0.6):
			found_floor=true
			floor_index=x

	# A good idea when impementing characters of all kinds,
	# Compensates for physics imprecission, as well as human
	# reaction delay.

	if (shoot and not shooting):
		shoot_time=0
		var bi = bullet.instance()
		var ss
		if (siding_left):
			ss=-1.0
		else:
			ss=1.0
		var pos = get_pos() + get_node("bullet_shoot").get_pos()*Vector2(ss,1.0)

		bi.set_pos(pos)
		get_parent().add_child(bi)

		bi.set_linear_velocity( Vector2(800.0*ss,-80) )
		get_node("sprite/smoke").set_emitting(true)
		get_node("sound").play("shoot")
		PS2D.body_add_collision_exception(bi.get_rid(),get_rid()) # make bullet and this not collide

	else:
		shoot_time+=step

	if (found_floor):
		airborne_time=0.0
	else:
		airborne_time+=step #time it spent in the air

	var on_floor = airborne_time < MAX_FLOOR_AIRBORNE_TIME

	# Process jump
	if (jumping):
		if (lv.y>0):
			#set off the jumping flag if going down
			jumping=false
		elif (not jump):
			stopping_jump=true

		if (stopping_jump):
			lv.y+=STOP_JUMP_FORCE*step

	if (on_floor):

		# Process logic when character is on floor

		if (move_left and not move_right):
			if (lv.x > -WALK_MAX_VELOCITY):
				lv.x-=WALK_ACCEL*step
		elif (move_right and not move_left):
			if (lv.x < WALK_MAX_VELOCITY):
				lv.x+=WALK_ACCEL*step
		else:
			var xv = abs(lv.x)
			xv-=WALK_DEACCEL*step
			if (xv<0):
				xv=0
			lv.x=sign(lv.x)*xv

		#Check jump
		if (not jumping and jump):
			lv.y=-JUMP_VELOCITY
			jumping=true
			stopping_jump=false
			get_node("sound").play("jump")

		#check siding

		if (lv.x < 0 and move_left):
			new_siding_left=true
		elif (lv.x > 0 and move_right):
			new_siding_left=false
		if (jumping):
			new_anim="jumping"
		elif (abs(lv.x)<0.1):
			if (shoot_time<MAX_SHOOT_POSE_TIME):
				new_anim="idle_weapon"
			else:
				new_anim="idle"
		else:
			if (shoot_time<MAX_SHOOT_POSE_TIME):
				new_anim="run_weapon"
			else:
				new_anim="run"
	else:

		# Process logic when the character is in the air

		if (move_left and not move_right):
			if (lv.x > -WALK_MAX_VELOCITY):
				lv.x-=AIR_ACCEL*step
		elif (move_right and not move_left):
			if (lv.x < WALK_MAX_VELOCITY):
				lv.x+=AIR_ACCEL*step
		else:
			var xv = abs(lv.x)
			xv-=AIR_DEACCEL*step
			if (xv<0):
				xv=0
			lv.x=sign(lv.x)*xv

		if (lv.y<0):
			if (shoot_time<MAX_SHOOT_POSE_TIME):
				new_anim="jumping_weapon"
			else:
				new_anim="jumping"
		else:
			if (shoot_time<MAX_SHOOT_POSE_TIME):
				new_anim="falling_weapon"
			else:
				new_anim="falling"

	#Update siding

	if (new_siding_left!=siding_left):
		if (new_siding_left):
			get_node("sprite").set_scale( Vector2(-1,1) )
		else:
			get_node("sprite").set_scale( Vector2(1,1) )

		siding_left=new_siding_left

	#Change animation
	if (new_anim!=anim):
		anim=new_anim
		get_node("anim").play(anim)

	shooting=shoot

 	# Apply floor velocity
	if (found_floor):
		floor_h_velocity=s.get_contact_collider_velocity_at_pos(floor_index).x
		lv.x+=floor_h_velocity

	#Finally, apply gravity and set back the linear velocity
	lv+=s.get_total_gravity()*step
	s.set_linear_velocity(lv)

func _ready():
	# Initalization here

#	if !Globals.has_singleton("Facebook"):
#        	return
#	var Facebook = Globals.get_singleton("Facebook")
#	var link = Globals.get("facebook/link")
#	var icon = Globals.get("facebook/icon")
#	var msg = "I just sneezed on your wall! Beat my score and Stop the Running nose!"
#	var title = "I just sneezed on your wall!"
#	Facebook.post("feed", msg, title, link, icon)
	enemy = ResourceLoader.load("res://enemy.xml")
	pass

extends RigidBody2D

# Character Demo, written by Juan Linietsky.
#
# Implementation of a 2D Character controller.
# This implementation uses the physics engine for
# controlling a character, in a very similar way
# than a 3D character controller would be implemented.
#
# Using the physics engine for this has the main
# advantages:
# -Easy to write.
# -Interaction with other physics-based objects is free
# -Only have to deal with the object linear velocity, not position
# -All collision/area framework available
#
# But also has the following disadvantages:
#
# -Objects may bounce a little bit sometimes
# -Going up ramps sends the chracter flying up, small hack is needed.
# -A ray collider is needed to avoid sliding down on ramps and
#   undesiderd bumps, small steps and rare numerical precision errors.
#   (another alternative may be to turn on friction when the character is not moving).
# -Friction cant be used, so floor velocity must be considered
#  for moving platforms.

var anim=""
var siding_left=false
var jumping=false
var stopping_jump=false
var shooting=false

var WALK_ACCEL = 800.0
var WALK_DEACCEL= 800.0
var WALK_MAX_VELOCITY= 200.0
var GRAVITY = 700.0
var AIR_ACCEL = 200.0
var AIR_DEACCEL= 200.0
var JUMP_VELOCITY=460
var STOP_JUMP_FORCE=900.0

var MAX_FLOOR_AIRBORNE_TIME = 0.15

var airborne_time=1e20
var shoot_time=1e20

var MAX_SHOOT_POSE_TIME = 0.3

var bullet = preload("res://bullet.xml")

var floor_h_velocity=0.0
var enemy

func _integrate_forces(s):

	var lv = s.get_linear_velocity()
	var step = s.get_step()

	var new_anim=anim
	var new_siding_left=siding_left

	# Get the controls
	var move_left = Input.is_action_pressed("move_left")
	var move_right = Input.is_action_pressed("move_right")
	var jump = Input.is_action_pressed("jump")
	var shoot = Input.is_action_pressed("shoot")
	var spawn = Input.is_action_pressed("spawn")

	if spawn:
		var e = enemy.instance()
		var p = get_pos()
		p.y = p.y - 100
		e.set_pos(p)
		get_parent().add_child(e)

	#deapply prev floor velocity
	lv.x-=floor_h_velocity
	floor_h_velocity=0.0

	# Find the floor (a contact with upwards facing collision normal)
	var found_floor=false
	var floor_index=-1

	for x in range(s.get_contact_count()):

		var ci = s.get_contact_local_normal(x)
		if (ci.dot(Vector2(0,-1))>0.6):
			found_floor=true
			floor_index=x

	# A good idea when impementing characters of all kinds,
	# Compensates for physics imprecission, as well as human
	# reaction delay.

	if (shoot and not shooting):
		shoot_time=0
		var bi = bullet.instance()
		var ss
		if (siding_left):
			ss=-1.0
		else:
			ss=1.0
		var pos = get_pos() + get_node("bullet_shoot").get_pos()*Vector2(ss,1.0)

		bi.set_pos(pos)
		get_parent().add_child(bi)

		bi.set_linear_velocity( Vector2(800.0*ss,-80) )
		get_node("sprite/smoke").set_emitting(true)
		get_node("sound").play("shoot")
		PS2D.body_add_collision_exception(bi.get_rid(),get_rid()) # make bullet and this not collide

	else:
		shoot_time+=step

	if (found_floor):
		airborne_time=0.0
	else:
		airborne_time+=step #time it spent in the air

	var on_floor = airborne_time < MAX_FLOOR_AIRBORNE_TIME

	# Process jump
	if (jumping):
		if (lv.y>0):
			#set off the jumping flag if going down
			jumping=false
		elif (not jump):
			stopping_jump=true

		if (stopping_jump):
			lv.y+=STOP_JUMP_FORCE*step

	if (on_floor):

		# Process logic when character is on floor

		if (move_left and not move_right):
			if (lv.x > -WALK_MAX_VELOCITY):
				lv.x-=WALK_ACCEL*step
		elif (move_right and not move_left):
			if (lv.x < WALK_MAX_VELOCITY):
				lv.x+=WALK_ACCEL*step
		else:
			var xv = abs(lv.x)
			xv-=WALK_DEACCEL*step
			if (xv<0):
				xv=0
			lv.x=sign(lv.x)*xv

		#Check jump
		if (not jumping and jump):
			lv.y=-JUMP_VELOCITY
			jumping=true
			stopping_jump=false
			get_node("sound").play("jump")

		#check siding

		if (lv.x < 0 and move_left):
			new_siding_left=true
		elif (lv.x > 0 and move_right):
			new_siding_left=false
		if (jumping):
			new_anim="jumping"
		elif (abs(lv.x)<0.1):
			if (shoot_time<MAX_SHOOT_POSE_TIME):
				new_anim="idle_weapon"
			else:
				new_anim="idle"
		else:
			if (shoot_time<MAX_SHOOT_POSE_TIME):
				new_anim="run_weapon"
			else:
				new_anim="run"
	else:

		# Process logic when the character is in the air

		if (move_left and not move_right):
			if (lv.x > -WALK_MAX_VELOCITY):
				lv.x-=AIR_ACCEL*step
		elif (move_right and not move_left):
			if (lv.x < WALK_MAX_VELOCITY):
				lv.x+=AIR_ACCEL*step
		else:
			var xv = abs(lv.x)
			xv-=AIR_DEACCEL*step
			if (xv<0):
				xv=0
			lv.x=sign(lv.x)*xv

		if (lv.y<0):
			if (shoot_time<MAX_SHOOT_POSE_TIME):
				new_anim="jumping_weapon"
			else:
				new_anim="jumping"
		else:
			if (shoot_time<MAX_SHOOT_POSE_TIME):
				new_anim="falling_weapon"
			else:
				new_anim="falling"

	#Update siding

	if (new_siding_left!=siding_left):
		if (new_siding_left):
			get_node("sprite").set_scale( Vector2(-1,1) )
		else:
			get_node("sprite").set_scale( Vector2(1,1) )

		siding_left=new_siding_left

	#Change animation
	if (new_anim!=anim):
		anim=new_anim
		get_node("anim").play(anim)

	shooting=shoot

 	# Apply floor velocity
	if (found_floor):
		floor_h_velocity=s.get_contact_collider_velocity_at_pos(floor_index).x
		lv.x+=floor_h_velocity

	#Finally, apply gravity and set back the linear velocity
	lv+=s.get_total_gravity()*step
	s.set_linear_velocity(lv)

func _ready():
	# Initalization here

#	if !Globals.has_singleton("Facebook"):
#        	return
#	var Facebook = Globals.get_singleton("Facebook")
#	var link = Globals.get("facebook/link")
#	var icon = Globals.get("facebook/icon")
#	var msg = "I just sneezed on your wall! Beat my score and Stop the Running nose!"
#	var title = "I just sneezed on your wall!"
#	Facebook.post("feed", msg, title, link, icon)
	enemy = ResourceLoader.load("res://enemy.xml")
	pass


uses optionbutton now. action selection now works

extends Spatial

var puzzleScn = preload("res://puzzle.scn")
var puzzle
var puzzleMan

var gridMan
var prevBlock = null

var fileDialog = load("res://fileDialog.scn").instance()
var gui = [
	["status", Label.new()], # plz keep me as first element, referenced as gui[0] later
	["save_pzl", Button.new()],
	["load_pzl", Button.new()],
	["remove_layer", Button.new()],
	["random_layer", Button.new()],
	# THESE SHOULD BE Options instead of left, label, right
	["class_toggle", {
		optionButt=OptionButton.new(),
		values=["LZR", "WILD", "PAIR", "GOAL"],
		value="PAIR"
	}],
	["color_toggle", {
		optionButt=OptionButton.new(),
		values=preload("res://scripts/PuzzleManager.gd").blockColors,
		value="Red"
	}],
	["action_toggle", {
		optionButt=OptionButton.new(),
		values=["Add", "Remove", "Replace"],
		value="Add"
	}],
	["test_pzl", Button.new()],
]
var action_ix = gui.size() - 1 - 1
var color_ix = gui.size() - 1 - 2
var class_ix = gui.size() - 1 - 3

func shouldAddNeighbor():
	return gui[action_ix][1].value == "Add"

func shouldReplaceSelf():
	return gui[action_ix][1].value == "Replace"

func shouldRemoveSelf():
	return gui[action_ix][1].value == "Remove"

func newPickledBlock():
	var b = puzzleMan.PickledBlock.new()\
		.setName(gridMan.shape.keys().size())\
	if prevBlock != null:
		b.setPairName(prevBlock.name)
		gridMan.get_node(prevBlock.toNode().name).setPairName(b.name)
		prevBlock = null
		gui[0][1].set_text("STATUS: ERROR, ADD PAIR")
	else:
		prevBlock = b
		gui[0][1].set_text("STATUS: NOMINAL")
	return b

func showFileDialog(loadInstead=false):
	get_tree().set_pause(true)

	if loadInstead:
		fileDialog.connect("confirmed", self, "puzzleLoad")
		fileDialog.set_mode(FileDialog.MODE_OPEN_FILE)
	else:
		fileDialog.connect("confirmed", self, "puzzleSave")
		fileDialog.set_mode(FileDialog.MODE_SAVE_FILE)

	fileDialog.popup_centered()

func puzzleSave():
	print("SAVING TO ", fileDialog.get_current_file() )
	get_tree().set_pause(false)

func puzzleLoad():
	print("LOADING FROM ", fileDialog.get_current_file() )
	get_tree().set_pause(false)

func changeValue(ix, togg):
	togg.value = togg.values[ix]

func _ready():
	puzzle = puzzleScn.instance()
	gridMan = puzzle.get_node("GridView/GridMan")
	puzzle.mainPuzzle = false

	# hide and disable timer
	puzzle.time.on = false;
	puzzle.time.val = ''

	puzzle.set_as_toplevel(true)

	add_child(puzzle)

	print(puzzle.get_tree() == get_tree())
	puzzleMan = puzzle.puzzleMan

	set_process_input(true)

	var theme = preload("res://themes/MainTheme.thm")

	fileDialog.set_title("Select Puzzle Filename")
	fileDialog.set_access(FileDialog.ACCESS_USERDATA)
	fileDialog.set_current_dir("PuzzleSaves")
	fileDialog.add_filter("*.pzl ; Anttris Puzzle")

	# MainTheme leaks on bottom
	var dialogTheme = Theme.new()
	dialogTheme.copy_default_theme()
	fileDialog.set_theme(dialogTheme)

	# work even if game is paused
	fileDialog.set_pause_mode(PAUSE_MODE_PROCESS)

	# unpause if user cancels
	fileDialog.connect("popup_hide", get_tree(), "set_pause", [false])
	fileDialog.hide()
	add_child(fileDialog)

	var y = 0
	for control in gui:
		y += 45
		if control[0].rfind('_toggle') > 0:
			var togg = control[1]
			var e = togg.optionButt
			e.set_pos(Vector2(10, y))
			e.set_theme(theme)
			add_child(e)

			# index of items needed
			for i in range(togg.values.size()):
				e.add_item(togg.values[i], i)
				e.connect("item_selected", self, "changeValue", [togg])
				# e.add_icon_item(togg.values[i], i)

		else:
			var e = control[1]
			e.set_pos(Vector2(10, y))
			e.set_theme(theme)
			if e extends Button:
				e.set_text(control[0])
			if control[0] == 'save_pzl' :
				e.connect('pressed', self, 'showFileDialog')
			if control[0] == 'load_pzl' :
				e.connect('pressed', self, 'showFileDialog', [true])
			if control[0] == 'status':
				control[1].set_text('STATUS: NOMINAL')
			add_child(e)

extends Spatial

var puzzleScn = preload("res://puzzle.scn")
var puzzle
var puzzleMan

var gridMan
var prevBlock = null

var fileDialog = load("res://fileDialog.scn").instance()
var gui = [
	["status", Label.new()], # plz keep me as first element, referenced as gui[0] later
	["save_pzl", Button.new()],
	["load_pzl", Button.new()],
	["remove_layer", Button.new()],
	["random_layer", Button.new()],
	# THESE SHOULD BE Options instead of left, label, right
	["class_toggle", {
		left=Button.new(),
		right=Button.new(),
		label=Label.new(),
		values=["LZR", "WILD", "PAIR", "GOAL"],
		value="PAIR"
	}],
	["color_toggle", {
		left=Button.new(),
		right=Button.new(),
		label=Label.new(),
		values=preload("res://scripts/PuzzleManager.gd").blockColors,
		value="Red"
	}],
	["action_toggle", {
		left=Button.new(),
		right=Button.new(),
		label=Label.new(),
		values=["Add", "Remove", "Replace"],
		value="Add"
	}],
	["test_pzl", Button.new()],
]

func shouldAddNeighbor():
	return true

func shouldReplaceSelf():
	return false

func shouldRemoveSelf():
	return false

func newPickledBlock():
	var b = puzzleMan.PickledBlock.new()\
		.setName(gridMan.shape.keys().size())\
	if prevBlock != null:
		b.setPairName(prevBlock.name)
		gridMan.get_node(prevBlock.toNode().name).setPairName(b.name)
		prevBlock = null
		gui[0][1].set_text("STATUS: ERROR, ADD PAIR")
	else:
		prevBlock = b
		gui[0][1].set_text("STATUS: NOMINAL")
	return b

func updateToggle(togg, increase):
	if increase:
		var next_ix = togg.values.find(togg.value) + 1
		if (next_ix >= togg.values.size()):
			next_ix = 0
		togg.value = togg.values[next_ix]
	else:
		var next_ix = togg.values.find(togg.value) - 1
		if (next_ix < 0):
			next_ix = togg.values.size() - 1
		togg.value =  togg.values[next_ix]
	togg.label.set_text(togg.value)

func showFileDialog(loadInstead=false):
	get_tree().set_pause(true)

	if loadInstead:
		fileDialog.connect("confirmed", self, "puzzleLoad")
		fileDialog.set_mode(FileDialog.MODE_OPEN_FILE)
	else:
		fileDialog.connect("confirmed", self, "puzzleSave")
		fileDialog.set_mode(FileDialog.MODE_SAVE_FILE)

	fileDialog.popup_centered()

func puzzleSave():
	print("SAVING TO ", fileDialog.get_current_file() )
	get_tree().set_pause(false)

func puzzleLoad():
	print("LOADING FROM ", fileDialog.get_current_file() )
	get_tree().set_pause(false)

func _ready():
	puzzle = puzzleScn.instance()
	gridMan = puzzle.get_node("GridView/GridMan")
	puzzle.mainPuzzle = false

	# hide and disable timer
	puzzle.time.on = false;
	puzzle.time.val = ''

	puzzle.set_as_toplevel(true)

	add_child(puzzle)

	print(puzzle.get_tree() == get_tree())
	puzzleMan = puzzle.puzzleMan

	set_process_input(true)

	var theme = preload("res://themes/MainTheme.thm")

	fileDialog.set_title("Select Puzzle Filename")
	fileDialog.set_access(FileDialog.ACCESS_USERDATA)
	fileDialog.set_current_dir("PuzzleSaves")
	fileDialog.add_filter("*.pzl ; Anttris Puzzle")

	# MainTheme leaks on bottom
	var dialogTheme = Theme.new()
	dialogTheme.copy_default_theme()
	fileDialog.set_theme(dialogTheme)

	# work even if game is paused
	fileDialog.set_pause_mode(PAUSE_MODE_PROCESS)

	# unpause if user cancels
	fileDialog.connect("popup_hide", get_tree(), "set_pause", [false])
	fileDialog.hide()
	add_child(fileDialog)

	var y = 0
	for control in gui:
		y += 45
		if control[0].rfind('_toggle') > 0:
			var togg = control[1]
			for cont in togg.keys():
				if not cont.begins_with('value'):
					var e = togg[cont]
					e.set_pos(Vector2(10, y))
					e.set_theme(theme)
					add_child(e)

			togg.left.set_text("<")
			togg.left.connect('pressed', self, 'updateToggle', [togg, false])

			togg.label.set_pos(Vector2(90, y + 4))
			togg.label.set_text(togg.value)

			togg.right.set_pos(Vector2(55, y))
			togg.right.set_text(">")
			togg.right.connect('pressed', self, 'updateToggle', [togg, true])

		else:
			var e = control[1]
			e.set_pos(Vector2(10, y))
			e.set_theme(theme)
			if e extends Button:
				e.set_text(control[0])
			if control[0] == 'save_pzl' :
				e.connect('pressed', self, 'showFileDialog')
			if control[0] == 'load_pzl' :
				e.connect('pressed', self, 'showFileDialog', [true])
			if control[0] == 'status':
				control[1].set_text('STATUS: NOMINAL')
			add_child(e)


fixed block addition

fixed block addition

extends Spatial

var puzzleScn = preload("res://puzzle.scn")
var puzzle
var puzzleMan

var gridMan
var prevBlockByColor = {} # keeps track of prevous color for pairs
var blockColors = preload("res://scripts/PuzzleManager.gd").blockColors
var id

var fileDialog = load("res://fileDialog.scn").instance()
var gui = [
	["status", Label.new()], # plz keep me as first element, referenced as gui[0] later
	["save_pzl", Button.new()],
	["load_pzl", Button.new()],
	["remove_layer", Button.new()],
	["random_layer", Button.new()],
	# THESE SHOULD BE Options instead of left, label, right
	["class_toggle", {
		optionButt=OptionButton.new(),
		values=["LZR", "WILD", "PAIR", "GOAL"],
		value="PAIR"
	}],
	["color_toggle", {
		optionButt=OptionButton.new(),
		values=blockColors,
		value="Blue"
	}],
	["action_toggle", {
		optionButt=OptionButton.new(),
		values=["Add", "Remove", "Replace"],
		value="Add"
	}],
	["test_pzl", Button.new()],
]
var action_ix = gui.size() - 1 - 1
var color_ix = gui.size() - 1 - 2
var class_ix = gui.size() - 1 - 3

# gross style, cannot , but clean enough
func shouldAddNeighbor():
	return gui[action_ix][1].value == "Add"

func shouldReplaceSelf():
	return gui[action_ix][1].value == "Replace"

func shouldRemoveSelf():
	return gui[action_ix][1].value == "Remove"

# called in AbstractBlock to add a new block
func newPickledBlock():
	var curColor = gui[color_ix][1].value
	var b = puzzleMan.PickledBlock.new() \
		.setName(id) \
		.setTextureName(curColor)
	var pb = prevBlockByColor[curColor]
	id += 1

	if pb != null:
		b.setPairName(pb.name)
		gridMan.get_node(pb.toNode().name).setPairName(b.name)
		prevBlockByColor[curColor] = null
		gui[0][1].set_text("STATUS: NOMINAL")
	else:
		# TODO SUPPORT GLYPHS HERE, SET PAIRWISE GLYPH
		prevBlockByColor[curColor] = b
		var missing = ""
		for k in prevBlockByColor.keys():
			if prevBlockByColor[k] != null:
				missing += k.to_upper() + " " # bad way of constructing strings
		gui[0][1].set_text("STATUS: ERROR, ADD PAIR " + missing)
	return b

func showFileDialog(loadInstead=false):
	get_tree().set_pause(true)

	if loadInstead:
		fileDialog.connect("confirmed", self, "puzzleLoad")
		fileDialog.set_mode(FileDialog.MODE_OPEN_FILE)
	else:
		fileDialog.connect("confirmed", self, "puzzleSave")
		fileDialog.set_mode(FileDialog.MODE_SAVE_FILE)

	fileDialog.popup_centered()

func puzzleSave():
	print("SAVING TO ", fileDialog.get_current_file() )
	get_tree().set_pause(false)

func puzzleLoad():
	print("LOADING FROM ", fileDialog.get_current_file() )
	get_tree().set_pause(false)

func changeValue(ix, togg):
	togg.value = togg.values[ix]

func _ready():
	for k in blockColors:
		prevBlockByColor[k] = null

	puzzle = puzzleScn.instance()
	gridMan = puzzle.get_node("GridView/GridMan")
	id = gridMan.shape.size()
	puzzle.mainPuzzle = false
	get_tree().get_root().add_child( preload( "res://puzzleView.scn" ).instance() )

	# hide and disable timer
	puzzle.time.on = false;
	puzzle.time.val = ''

	puzzle.set_as_toplevel(true)

	add_child(puzzle)

	puzzleMan = puzzle.puzzleMan

	set_process_input(true)

	var theme = preload("res://themes/MainTheme.thm")

	fileDialog.set_title("Select Puzzle Filename")
	fileDialog.set_access(FileDialog.ACCESS_USERDATA)
	fileDialog.set_current_dir("PuzzleSaves")
	fileDialog.add_filter("*.pzl ; Anttris Puzzle")

	# MainTheme leaks on bottom
	var dialogTheme = Theme.new()
	dialogTheme.copy_default_theme()
	fileDialog.set_theme(dialogTheme)

	# work even if game is paused
	fileDialog.set_pause_mode(PAUSE_MODE_PROCESS)

	# unpause if user cancels
	fileDialog.connect("popup_hide", get_tree(), "set_pause", [false])
	fileDialog.hide()
	add_child(fileDialog)

	var y = 0
	for control in gui:
		y += 45
		if control[0].rfind('_toggle') > 0:
			var togg = control[1]
			var e = togg.optionButt
			e.set_pos(Vector2(10, y))
			e.set_theme(theme)
			add_child(e)

			# index of items needed
			e.connect("item_selected", self, "changeValue", [togg])
			for i in range(togg.values.size()):
				e.add_item(togg.values[i], i)
				# e.add_icon_item(togg.values[i], i)

		else:
			var e = control[1]
			e.set_pos(Vector2(10, y))
			e.set_theme(theme)
			if e extends Button:
				e.set_text(control[0])
			if control[0] == 'save_pzl' :
				e.connect('pressed', self, 'showFileDialog')
			if control[0] == 'load_pzl' :
				e.connect('pressed', self, 'showFileDialog', [true])
			if control[0] == 'status':
				control[1].set_text('STATUS: NOMINAL')
			add_child(e)

extends Spatial

var puzzleScn = preload("res://puzzle.scn")
var puzzle
var puzzleMan

var gridMan
var prevBlockByColor = {} # keeps track of prevous color for pairs
var blockColors = preload("res://scripts/PuzzleManager.gd").blockColors

var fileDialog = load("res://fileDialog.scn").instance()
var gui = [
	["status", Label.new()], # plz keep me as first element, referenced as gui[0] later
	["save_pzl", Button.new()],
	["load_pzl", Button.new()],
	["remove_layer", Button.new()],
	["random_layer", Button.new()],
	# THESE SHOULD BE Options instead of left, label, right
	["class_toggle", {
		optionButt=OptionButton.new(),
		values=["LZR", "WILD", "PAIR", "GOAL"],
		value="PAIR"
	}],
	["color_toggle", {
		optionButt=OptionButton.new(),
		values=blockColors,
		value="Blue"
	}],
	["action_toggle", {
		optionButt=OptionButton.new(),
		values=["Add", "Remove", "Replace"],
		value="Add"
	}],
	["test_pzl", Button.new()],
]
var action_ix = gui.size() - 1 - 1
var color_ix = gui.size() - 1 - 2
var class_ix = gui.size() - 1 - 3

# gross style, cannot , but clean enough
func shouldAddNeighbor():
	return gui[action_ix][1].value == "Add"

func shouldReplaceSelf():
	return gui[action_ix][1].value == "Replace"

func shouldRemoveSelf():
	return gui[action_ix][1].value == "Remove"

func newPickledBlock():
	var curColor = gui[color_ix][1].value
	var b = puzzleMan.PickledBlock.new() \
		.setName(gridMan.shape.keys().size()) \
		.setTextureName(curColor)
	var pb = prevBlockByColor[curColor]

	if pb != null:
		b.setPairName(pb.name)
		gridMan.get_node(pb.toNode().name).setPairName(b.name)
		prevBlockByColor[curColor] = null
		gui[0][1].set_text("STATUS: NOMINAL")
	else:
		# TODO SUPPORT GLYPHS HERE, SET PAIRWISE GLYPH
		prevBlockByColor[curColor] = b
		var missing = ""
		for k in prevBlockByColor.keys():
			if prevBlockByColor[k] != null:
				missing += k.to_upper() + " " # bad way of constructing strings
		gui[0][1].set_text("STATUS: ERROR, ADD PAIR " + missing)
	return b

func showFileDialog(loadInstead=false):
	get_tree().set_pause(true)

	if loadInstead:
		fileDialog.connect("confirmed", self, "puzzleLoad")
		fileDialog.set_mode(FileDialog.MODE_OPEN_FILE)
	else:
		fileDialog.connect("confirmed", self, "puzzleSave")
		fileDialog.set_mode(FileDialog.MODE_SAVE_FILE)

	fileDialog.popup_centered()

func puzzleSave():
	print("SAVING TO ", fileDialog.get_current_file() )
	get_tree().set_pause(false)

func puzzleLoad():
	print("LOADING FROM ", fileDialog.get_current_file() )
	get_tree().set_pause(false)

func changeValue(ix, togg):
	togg.value = togg.values[ix]

func _ready():
	for k in blockColors:
		prevBlockByColor[k] = null
	get_tree().get_root().add_child( preload( "res://puzzleView.scn" ).instance() )

	puzzle = puzzleScn.instance()
	gridMan = puzzle.get_node("GridView/GridMan")
	puzzle.mainPuzzle = false

	# hide and disable timer
	puzzle.time.on = false;
	puzzle.time.val = ''

	puzzle.set_as_toplevel(true)

	add_child(puzzle)

	print(puzzle.get_tree() == get_tree())
	puzzleMan = puzzle.puzzleMan

	set_process_input(true)

	var theme = preload("res://themes/MainTheme.thm")

	fileDialog.set_title("Select Puzzle Filename")
	fileDialog.set_access(FileDialog.ACCESS_USERDATA)
	fileDialog.set_current_dir("PuzzleSaves")
	fileDialog.add_filter("*.pzl ; Anttris Puzzle")

	# MainTheme leaks on bottom
	var dialogTheme = Theme.new()
	dialogTheme.copy_default_theme()
	fileDialog.set_theme(dialogTheme)

	# work even if game is paused
	fileDialog.set_pause_mode(PAUSE_MODE_PROCESS)

	# unpause if user cancels
	fileDialog.connect("popup_hide", get_tree(), "set_pause", [false])
	fileDialog.hide()
	add_child(fileDialog)

	var y = 0
	for control in gui:
		y += 45
		if control[0].rfind('_toggle') > 0:
			var togg = control[1]
			var e = togg.optionButt
			e.set_pos(Vector2(10, y))
			e.set_theme(theme)
			add_child(e)

			# index of items needed
			for i in range(togg.values.size()):
				e.add_item(togg.values[i], i)
				e.connect("item_selected", self, "changeValue", [togg])
				# e.add_icon_item(togg.values[i], i)

		else:
			var e = control[1]
			e.set_pos(Vector2(10, y))
			e.set_theme(theme)
			if e extends Button:
				e.set_text(control[0])
			if control[0] == 'save_pzl' :
				e.connect('pressed', self, 'showFileDialog')
			if control[0] == 'load_pzl' :
				e.connect('pressed', self, 'showFileDialog', [true])
			if control[0] == 'status':
				control[1].set_text('STATUS: NOMINAL')
			add_child(e)


send puzzle over network. static time formatter

# Provides functionality for the puzzle itself

extends Spatial

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

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

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

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

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

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

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

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

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

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

		rand_seed(seed)

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

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

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

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

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

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

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

		otherPuzzle = p #for use with network

# Provides functionality for the puzzle itself

extends Spatial

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

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

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

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

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

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

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

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

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

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

		rand_seed(seed)

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

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

		var network = Globals.get("Network")
		if (not network == null and network.isNetwork):
			print("Sending puzzle")

	# make a new puzzle, embed using Viewport
	if mainPuzzle:
		network.sendStart(puzzle)

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

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

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

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

		otherPuzzle = p #for use with network