delayed wall jumps

delayed wall jumps

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

var velocity
var movement_vector = [0, 0]
var controller_vector = [0, 0]

var AXIS_THRESHOLD = 0.15

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

var stun_duration = 0.15
var stun_level = 0

var can_jump = true
var is_in_air = false
var is_on_wall = false
var wall_vector = Vector2(0, 0)

var tombstone_template #= preload("res://scenes/particles/thumbstone.xscn")

var FLOOR_FRICTION = 25
var MOVEMENT_SPEED_CAP_LAND = 10
var MOVEMENT_SPEED_CAP_AIR = 12
var GRAVITY = 30
var COLLIDING_FALL = 0.1
var JUMP_SPEED = 20
var AIR_CONTROL = 0.8

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

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

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

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

func modify_position(delta):
    var x = self.apply_axis_threshold(self.controller_vector[0])
    var y = self.apply_axis_threshold(self.controller_vector[1])

    var current_motion = Vector2(self.movement_vector[0], self.movement_vector[1])
    var motion_delta = Vector2(x, y) * self.velocity * delta

    if self.is_in_air:
        motion_delta = motion_delta * self.AIR_CONTROL

    motion_delta.y = motion_delta.y + self.GRAVITY * delta

    current_motion = current_motion + motion_delta

    var speed_cap = self.MOVEMENT_SPEED_CAP_LAND
    if self.is_in_air:
        speed_cap = self.MOVEMENT_SPEED_CAP_AIR
    if abs(current_motion.x) > speed_cap:
        if current_motion.x < 0:
            current_motion.x = -speed_cap
        else:
            current_motion.x = speed_cap

    if abs(current_motion.y) > speed_cap:
        if current_motion.y < 0:
            current_motion.y = -speed_cap
        else:
            current_motion.y = speed_cap

    if current_motion == Vector2(0, 0) && not self.is_in_air:
        return

    if not self.is_in_air && self.is_jumping && self.can_jump:
        current_motion.y = -self.JUMP_SPEED

        if self.is_on_wall:
            current_motion.x = self.wall_vector.x * self.JUMP_SPEED
            self.can_jump = false
            self.bag.timers.set_timeout(1, self, 'enable_jump')

    current_motion = self.apply_friction(current_motion, delta)

    self.avatar.move(current_motion)
    if (self.avatar.is_colliding()):
        var normal = self.avatar.get_collision_normal()
        if (normal.x == 1 || normal.x == -1) && abs(normal.y) < 0.01:
            self.is_on_wall = true
            self.wall_vector = normal
        else:
            self.is_on_wall = false

        var n = self.avatar.get_collision_normal()
        current_motion = n.slide(current_motion)
        if not self.is_in_air:
            current_motion.y = self.COLLIDING_FALL
        self.is_in_air = false
        self.avatar.move(current_motion)
        self.handle_collision(self.avatar.get_collider())
    else:
        self.is_in_air = true
        self.is_on_wall = false
    self.flip(self.controller_vector[0])
    self.movement_vector[0] = current_motion.x
    self.movement_vector[1] = current_motion.y

func handle_collision(collider):
    return

func apply_friction(current_motion, delta):
    if abs(current_motion.x) < self.FLOOR_FRICTION * delta:
        current_motion.x = 0
    else:
        if current_motion.x > 0:
            current_motion.x = current_motion.x - self.FLOOR_FRICTION * delta
        else:
            current_motion.x = current_motion.x + self.FLOOR_FRICTION * delta

    return current_motion

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

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

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

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

func reset_movement():
    self.movement_vector = [0, 0]
    #self.avatar.set_opacity(1)
    #self.animations.play('idle')

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

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

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

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

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

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

func die():
    self.spawn_tombstone()
    .die()

func spawn_tombstone():
    var tombstone = self.tombstone_template.instance()
    self.bag.game_state.current_cell.add_persistent_object(tombstone, self.get_pos())

func enable_jump():
    self.can_jump = true

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

var velocity
var movement_vector = [0, 0]
var controller_vector = [0, 0]

var AXIS_THRESHOLD = 0.15

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

var stun_duration = 0.15
var stun_level = 0

var is_in_air = false
var is_on_wall = false
var wall_vector = Vector2(0, 0)

var tombstone_template #= preload("res://scenes/particles/thumbstone.xscn")

var FLOOR_FRICTION = 25
var MOVEMENT_SPEED_CAP_LAND = 10
var MOVEMENT_SPEED_CAP_AIR = 12
var GRAVITY = 30
var COLLIDING_FALL = 0.1
var JUMP_SPEED = 20
var AIR_CONTROL = 0.8

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

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

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

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

func modify_position(delta):
    var x = self.apply_axis_threshold(self.controller_vector[0])
    var y = self.apply_axis_threshold(self.controller_vector[1])

    var current_motion = Vector2(self.movement_vector[0], self.movement_vector[1])
    var motion_delta = Vector2(x, y) * self.velocity * delta

    if self.is_in_air:
        motion_delta = motion_delta * self.AIR_CONTROL

    motion_delta.y = motion_delta.y + self.GRAVITY * delta

    current_motion = current_motion + motion_delta

    var speed_cap = self.MOVEMENT_SPEED_CAP_LAND
    if self.is_in_air:
        speed_cap = self.MOVEMENT_SPEED_CAP_AIR
    if abs(current_motion.x) > speed_cap:
        if current_motion.x < 0:
            current_motion.x = -speed_cap
        else:
            current_motion.x = speed_cap

    if abs(current_motion.y) > speed_cap:
        if current_motion.y < 0:
            current_motion.y = -speed_cap
        else:
            current_motion.y = speed_cap

    if current_motion == Vector2(0, 0) && not self.is_in_air:
        return

    if not self.is_in_air && self.is_jumping:
        current_motion.y = -self.JUMP_SPEED

        if self.is_on_wall:
            current_motion.x = self.wall_vector.x * self.JUMP_SPEED

    current_motion = self.apply_friction(current_motion, delta)

    self.avatar.move(current_motion)
    if (self.avatar.is_colliding()):
        var normal = self.avatar.get_collision_normal()
        if (normal.x == 1 || normal.x == -1) && abs(normal.y) < 0.01:
            self.is_on_wall = true
            self.wall_vector = normal
        else:
            self.is_on_wall = false

        var n = self.avatar.get_collision_normal()
        current_motion = n.slide(current_motion)
        if not self.is_in_air:
            current_motion.y = self.COLLIDING_FALL
        self.is_in_air = false
        self.avatar.move(current_motion)
        self.handle_collision(self.avatar.get_collider())
    else:
        self.is_in_air = true
        self.is_on_wall = false
    self.flip(self.controller_vector[0])
    self.movement_vector[0] = current_motion.x
    self.movement_vector[1] = current_motion.y

func handle_collision(collider):
    return

func apply_friction(current_motion, delta):
    if abs(current_motion.x) < self.FLOOR_FRICTION * delta:
        current_motion.x = 0
    else:
        if current_motion.x > 0:
            current_motion.x = current_motion.x - self.FLOOR_FRICTION * delta
        else:
            current_motion.x = current_motion.x + self.FLOOR_FRICTION * delta

    return current_motion

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

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

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

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

func reset_movement():
    self.movement_vector = [0, 0]
    #self.avatar.set_opacity(1)
    #self.animations.play('idle')

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

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

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

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

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

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

func die():
    self.spawn_tombstone()
    .die()

func spawn_tombstone():
    var tombstone = self.tombstone_template.instance()
    self.bag.game_state.current_cell.add_persistent_object(tombstone, self.get_pos())


Automatic brake/reverse

Automatic brake/reverse

Value fwd_mps converts global velocity into a velocity vector which is rotated to cars local coordinate system (in this example x-axis).
Then if key "ui_down" is pressed and the fwd_mps value is greater or equal to -1 (which seems to be very good spot and represents car still moving forward but nearly standing still) the car reverses (negative engine_force_value). If this condition isn't met (which means the car is moving forward) then the car brakes.

Tested and working on Godot 3.1 Beta 3.

Suggestions:
In this case, if you want something more realistic, the -engine_force_value can be much lower because no car reverses in same speed as it goes forward but for the sake of demo project I left it as it is.

Created for my own project with help of user wombatstampede from godotdevelopers.org/forum

extends VehicleBody

# Member variables
const STEER_SPEED = 1
const STEER_LIMIT = 0.4

var steer_angle = 0
var steer_target = 0

export var engine_force_value = 40

func _physics_process(delta):
	var fwd_mps = transform.basis.xform_inv(linear_velocity).x

	if Input.is_action_pressed("ui_left"):
		steer_target = STEER_LIMIT
	elif Input.is_action_pressed("ui_right"):
		steer_target = -STEER_LIMIT
	else:
		steer_target = 0

	if Input.is_action_pressed("ui_up"):
		engine_force = engine_force_value
	else:
		engine_force = 0

	if Input.is_action_pressed("ui_down"):
		if (fwd_mps >= -1):
			engine_force = -engine_force_value
		else:
			brake = 1
	else:
		brake = 0.0

	if steer_target < steer_angle:
		steer_angle -= STEER_SPEED * delta
		if steer_target > steer_angle:
			steer_angle = steer_target
	elif steer_target > steer_angle:
		steer_angle += STEER_SPEED * delta
		if steer_target < steer_angle:
			steer_angle = steer_target

	steering = steer_angle

extends VehicleBody

# Member variables
const STEER_SPEED = 1
const STEER_LIMIT = 0.4

var steer_angle = 0
var steer_target = 0

export var engine_force_value = 40

func _physics_process(delta):
	if Input.is_action_pressed("ui_left"):
		steer_target = STEER_LIMIT
	elif Input.is_action_pressed("ui_right"):
		steer_target = -STEER_LIMIT
	else:
		steer_target = 0

	if Input.is_action_pressed("ui_up"):
		engine_force = engine_force_value
	else:
		engine_force = 0

	if Input.is_action_pressed("ui_down"):
		brake = 1
	else:
		brake = 0.0

	if steer_target < steer_angle:
		steer_angle -= STEER_SPEED * delta
		if steer_target > steer_angle:
			steer_angle = steer_target
	elif steer_target > steer_angle:
		steer_angle += STEER_SPEED * delta
		if steer_target < steer_angle:
			steer_angle = steer_target

	steering = steer_angle


rozwiazany problem z przekazywaniem gry jako parametr, poczatki gui w oknie gry


Fix query.

Fix query.

extends Panel

onready var content_text = get_node( "content/text" )
onready var content_image = get_node( "content/image" )
onready var option_buttons = get_node( "option_buttons" )

var pages_list = []
const MAX_PAGES_NUMBER = 5
var current_pages_count = 0

var right_option = -1

func _input( event ):
	if event.type == InputEvent.KEY:
		if event.pressed and event.scancode == KEY_BACKSPACE:
			finish()

func start():
	Input.set_mouse_mode( Input.MOUSE_MODE_VISIBLE )
	get_tree().set_pause( true )
	show()
	set_process_input( true )
	current_pages_count = 0
	randomize()
	next_page()

func finish():
	hide()
	get_tree().set_pause( false )
	Input.set_mouse_mode( Input.MOUSE_MODE_CAPTURED )
	set_process_input( false )

func load_data( data_id ):
	pages_list.clear()
	var database = PSQLDatabase.new()
	database.connect_server( "localhost", "gamedb", "postgres", "postgres" )
	pages_list = database.select( "public.pages", "id,text,options,image", \
	                              "WHERE type = '" + data_id + "' ORDER BY id" )
	print( pages_list )
	var image_file = File.new()
	for page_idx in range( pages_list.size() ):
		if pages_list[ page_idx ].has( "image" ):
			var image_buffer = pages_list[ page_idx ][ "image" ]
			image_file.open( "cache.png", File.WRITE )
			image_file.store_buffer( image_buffer )
			image_file.close()
			pages_list[ page_idx ][ "image" ] = ImageTexture.new()
			pages_list[ page_idx ][ "image" ].load( "cache.png" )
		else:
			pages_list[ page_idx ][ "image" ] = null
		print( pages_list[ page_idx ] )

func next_page():
	current_pages_count += 1
	var pages_number = min( pages_list.size(), MAX_PAGES_NUMBER )
	if current_pages_count > pages_number:
		finish()
		return
	var page_idx = current_pages_count - 1
	var page = pages_list[ page_idx ]
	print( page )
	content_text.set_text( page[ "text" ] )
	content_image.set_texture( page[ "image" ] )
	option_buttons.clear()
	right_option = -1
	var option_entries = page[ "options" ].split( ",", false )
	for entry_idx in range( option_entries.size() ):
		var entry = option_entries[ entry_idx ]
		if entry.find( "*" ) == 0:
		  right_option= entry_idx
		  entry = entry.substr( 1, entry.length() - 1 )
		option_buttons.add_button( entry )

func _on_buttons_button_selected( button_idx ):
	print( "selected button " + str( button_idx ) + ": " + str( button_idx == right_option ) )

func _on_next_button_pressed():
	print( "next page" )
	next_page()

extends Panel

onready var content_text = get_node( "content/text" )
onready var content_image = get_node( "content/image" )
onready var option_buttons = get_node( "option_buttons" )

var pages_list = []
const MAX_PAGES_NUMBER = 5
var current_pages_count = 0

var right_option = -1

func _input( event ):
	if event.type == InputEvent.KEY:
		if event.pressed and event.scancode == KEY_BACKSPACE:
			finish()

func start():
	Input.set_mouse_mode( Input.MOUSE_MODE_VISIBLE )
	get_tree().set_pause( true )
	show()
	set_process_input( true )
	current_pages_count = 0
	randomize()
	next_page()

func finish():
	hide()
	get_tree().set_pause( false )
	Input.set_mouse_mode( Input.MOUSE_MODE_CAPTURED )
	set_process_input( false )

func load_data( data_id ):
	pages_list.clear()
	var database = PSQLDatabase.new()
	database.connect_server( "localhost", "gamedb", "postgres", "postgres" )
	pages_list = database.select( "public.pages", "id,text,options,image", "type = '" + data_id + "' ORDER BY id" )
	print( pages_list )
	var image_file = File.new()
	for page_idx in range( pages_list.size() ):
		if pages_list[ page_idx ].has( "image" ):
			var image_buffer = pages_list[ page_idx ][ "image" ]
			image_file.open( "cache.png", File.WRITE )
			image_file.store_buffer( image_buffer )
			image_file.close()
			pages_list[ page_idx ][ "image" ] = ImageTexture.new()
			pages_list[ page_idx ][ "image" ].load( "cache.png" )
		else:
			pages_list[ page_idx ][ "image" ] = null
		print( pages_list[ page_idx ] )

func next_page():
	current_pages_count += 1
	var pages_number = min( pages_list.size(), MAX_PAGES_NUMBER )
	if current_pages_count > pages_number:
		finish()
		return
	var page_idx = current_pages_count - 1
	var page = pages_list[ page_idx ]
	print( page )
	content_text.set_text( page[ "text" ] )
	content_image.set_texture( page[ "image" ] )
	option_buttons.clear()
	right_option = -1
	var option_entries = page[ "options" ].split( ",", false )
	for entry_idx in range( option_entries.size() ):
		var entry = option_entries[ entry_idx ]
		if entry.find( "*" ) == 0:
		  right_option= entry_idx
		  entry = entry.substr( 1, entry.length() - 1 )
		option_buttons.add_button( entry )

func _on_buttons_button_selected( button_idx ):
	print( "selected button " + str( button_idx ) + ": " + str( button_idx == right_option ) )

func _on_next_button_pressed():
	print( "next page" )
	next_page()


Backing up from join-game will now disconnect any join requests

# 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

# network variables
var network

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

	Globals.set("Network", load("res://scripts/Network.gd").new())
	network = Globals.get("Network")

	#get_tree().get_root().add_child(network)
	network.root = get_tree().get_root()

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

# TODO save to ConfigFile, load this on beginning
func _on_Options_pressed():
	Menu_Main.guiOut()
	Menu_Options.guiIn()
	timer = 0.0
	menuOn = MENU_OPTIONS
	get_tree().get_root().get_node("GUIManager/OptionsMenu/Panel/PortField/LineEdit").set_text(str(network.port))

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

func _on_SaveQuit_pressed():
	# Add save options here.
	var field = get_tree().get_root().get_node("GUIManager/OptionsMenu/Panel/PortField/LineEdit")
	get_node("/root/Network").setPort(field.get_text())
	network.setPort(field.get_text())
	_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
	network.disconnect()

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

	if !network.isHost and !network.isNetwork:
		print("calling!")
		network.host(network.port)
		get_tree().get_root().get_node("GUIManager/HostGame/Panel/Waiting").set_text("Waiting for player to join on port " + str(network.port) + "...")

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
	network.disconnect()

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

func _on_Join_pressed():
	var IPPanel = get_tree().get_root().get_node("GUIManager/JoinGame/Panel/IPAddress")
	var ip = IPPanel.get_text();

	if (ip.empty()):
		return

	if !network.isClient:
		network.connectTo(ip, network.port)

# 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

# network variables
var network

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

	Globals.set("Network", load("res://scripts/Network.gd").new())
	network = Globals.get("Network")

	#get_tree().get_root().add_child(network)
	network.root = get_tree().get_root()

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

# TODO save to ConfigFile, load this on beginning
func _on_Options_pressed():
	Menu_Main.guiOut()
	Menu_Options.guiIn()
	timer = 0.0
	menuOn = MENU_OPTIONS
	get_tree().get_root().get_node("GUIManager/OptionsMenu/Panel/PortField/LineEdit").set_text(str(network.port))

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

func _on_SaveQuit_pressed():
	# Add save options here.
	var field = get_tree().get_root().get_node("GUIManager/OptionsMenu/Panel/PortField/LineEdit")
	get_node("/root/Network").setPort(field.get_text())
	network.setPort(field.get_text())
	_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
	network.disconnect()

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

	if !network.isHost and !network.isNetwork:
		print("calling!")
		network.host(network.port)
		get_tree().get_root().get_node("GUIManager/HostGame/Panel/Waiting").set_text("Waiting for player to join on port " + str(network.port) + "...")

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

func _on_Join_pressed():
	var IPPanel = get_tree().get_root().get_node("GUIManager/JoinGame/Panel/IPAddress")
	var ip = IPPanel.get_text();

	if (ip.empty()):
		return

	if !network.isClient:
		network.connectTo(ip, network.port)


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


load button works now

load button works now

# 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

# network variables
var network

# State variables.
var menuOn
var splashTween

# Timers.
var timer
var initialWait  = 0.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

# Main Menu Theme
var samplePlayer = StreamPlayer.new()
var songs = [load("res://main_theme_antris.ogg")]

# Loader dialog
var saveDir
var fileDialog

func skipTitle(skip):
	if skip:
		initialWait  = 0.01
		splashTimer  = 0.01
		warningTimer = 0.01

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

	Globals.set("Network", load("res://scripts/Network.gd").new())
	network = Globals.get("Network")

	var scn = load("res://networkProxy.scn")
	add_child(scn.instance())
	network.root = get_tree().get_root()

	# Load the config.
	var config = preload( "res://scripts/DataManager.gd" ).new().loadConfig()

	get_node("OptionsMenu/Panel/OnlineName/LineEdit").set_text( config.name )
	get_node("OptionsMenu/Panel/SoundVolume/SoundSlider").set_value( config.soundvolume )
	get_node("OptionsMenu/Panel/MusicVolume/MusicSlider").set_value( config.musicvolume )

	network.port = config.portnumber

	# Prepare puzzle loader dialog
	saveDir = OS.get_data_dir() + "/PuzzleSaves"
	fileDialog = preload("Editor.gd").initLoadSaveDialog(self, get_tree(), saveDir)

	# Main Menu Theme
	add_child(samplePlayer)
	samplePlayer.set_stream(songs[0])
	samplePlayer.play()

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

# TODO save to ConfigFile, load this on beginning
func _on_Options_pressed():
	Menu_Main.guiOut()
	Menu_Options.guiIn()
	timer = 0.0
	menuOn = MENU_OPTIONS
	get_node("OptionsMenu/Panel/PortField/LineEdit").set_text(str(network.port))

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

func _on_SaveQuit_pressed():
	# Save the options.

	var config = { name = get_node("OptionsMenu/Panel/OnlineName/LineEdit").get_text()
				  , soundvolume = get_node("OptionsMenu/Panel/SoundVolume/SoundSlider").get_value()
				  , musicvolume = get_node("OptionsMenu/Panel/MusicVolume/MusicSlider").get_value()
				  , portnumber = get_node("OptionsMenu/Panel/PortField/LineEdit").get_text()
				  }

	preload( "res://scripts/DataManager.gd" ).new().saveConfig( config )

	var field = get_node("OptionsMenu/Panel/PortField/LineEdit")
	network.setPort(field.get_text())
	network.setPort(field.get_text())
	_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
	network.disconnect()

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

	if !network.isHost and !network.isNetwork:
		print("calling!")
		network.host(network.port)
		get_node("HostGame/Panel/Waiting").set_text("Waiting for player to join on port " + str(network.port) + "...")

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
	if (network.isClient):
		network.disconnect()

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

	var p = ResourceLoader.load( "res://puzzle.scn" ).instance()
	p.generateRandom = false
	p.get_node("GridView/GridMan").set_puzzle(puzzle)
	root.add_child( p )
	p.get_node("GridView/GridMan").setupCam()

func _on_Join_pressed():
	var IPPanel = get_node("JoinGame/Panel/IPAddress")
	var ip = IPPanel.get_text();

	if (ip.empty()):
		return

	if !network.isClient:
		network.connectTo(ip, network.port)

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

func _on_LoadPuzzle_pressed():
	preload("Editor.gd").showLoadDialog(fileDialog, self)

# called by the fileDialog
func puzzleLoad():
	var f = fileDialog.get_current_path()
	if f == null or f == "":
		return
	print("LOADING FROM ", f)
	_on_RandomPuzzle_pressed(preload("DataManager.gd").loadPuzzle( f ))

# 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

# network variables
var network

# State variables.
var menuOn
var splashTween

# Timers.
var timer
var initialWait  = 0.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

# Main Menu Theme
var samplePlayer = StreamPlayer.new()
var songs = [load("res://main_theme_antris.ogg")]

func skipTitle(skip):
	if skip:
		initialWait  = 0.01
		splashTimer  = 0.01
		warningTimer = 0.01

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

	Globals.set("Network", load("res://scripts/Network.gd").new())
	network = Globals.get("Network")

	var scn = load("res://networkProxy.scn")
	add_child(scn.instance())
	network.root = get_tree().get_root()

	# Load the config.
	var config = preload( "res://scripts/DataManager.gd" ).new().loadConfig()

	get_node("OptionsMenu/Panel/OnlineName/LineEdit").set_text( config.name )
	get_node("OptionsMenu/Panel/SoundVolume/SoundSlider").set_value( config.soundvolume )
	get_node("OptionsMenu/Panel/MusicVolume/MusicSlider").set_value( config.musicvolume )

	network.port = config.portnumber

	# Main Menu Theme
	add_child(samplePlayer)
	samplePlayer.set_stream(songs[0])
	samplePlayer.play()

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

# TODO save to ConfigFile, load this on beginning
func _on_Options_pressed():
	Menu_Main.guiOut()
	Menu_Options.guiIn()
	timer = 0.0
	menuOn = MENU_OPTIONS
	get_node("OptionsMenu/Panel/PortField/LineEdit").set_text(str(network.port))

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

func _on_SaveQuit_pressed():
	# Save the options.

	var config = { name = get_node("OptionsMenu/Panel/OnlineName/LineEdit").get_text()
				  , soundvolume = get_node("OptionsMenu/Panel/SoundVolume/SoundSlider").get_value()
				  , musicvolume = get_node("OptionsMenu/Panel/MusicVolume/MusicSlider").get_value()
				  , portnumber = get_node("OptionsMenu/Panel/PortField/LineEdit").get_text()
				  }

	preload( "res://scripts/DataManager.gd" ).new().saveConfig( config )

	var field = get_node("OptionsMenu/Panel/PortField/LineEdit")
	network.setPort(field.get_text())
	network.setPort(field.get_text())
	_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
	network.disconnect()

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

	if !network.isHost and !network.isNetwork:
		print("calling!")
		network.host(network.port)
		get_node("HostGame/Panel/Waiting").set_text("Waiting for player to join on port " + str(network.port) + "...")

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
	if (network.isClient):
		network.disconnect()

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://puzzleView.scn" ).instance() )
	root.add_child( ResourceLoader.load( "res://puzzle.scn" ).instance() )

func _on_Join_pressed():
	var IPPanel = get_node("JoinGame/Panel/IPAddress")
	var ip = IPPanel.get_text();

	if (ip.empty()):
		return

	if !network.isClient:
		network.connectTo(ip, network.port)

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


Add check against gridvie wnot being the acgive grid view

# global working variables
var turn = Vector2( 0.0, 0.0 )            # the amount the mouse has turned
var mouseposlast = Input.get_mouse_pos()   # the mouses last position

# global tweakable parameters
var orbitrate = 10        # the rate the camera orbits the target when the mouse is moved
var active = true
var offClick = false
var selectedBlocks = []

func _ready():
	# Initalization here
	set_process_input(true)      # process user input events here

# called to handle a user input event
func _input(ev):
	if (not active):
		return
	# If the mouse has been moved
	if (ev.type==InputEvent.SCREEN_DRAG or (ev.type==InputEvent.MOUSE_MOTION and ev.button_mask == 1)):
		# calculate the delta change from the last mouse movement
		var mousedelta = (mouseposlast - ev.pos)
		# scale the mouse delta to a useful value
		turn = mousedelta / orbitrate
		# perform the rotation
		var currentTransform = get_transform().basis
		var dtime = get_process_delta_time()
		currentTransform = Matrix3( Vector3(0, 1, 0), PI * turn.x * dtime ) * currentTransform
		currentTransform = Matrix3( Vector3(1, 0, 0), PI * turn.y * dtime ) * currentTransform
		set_transform( Transform( currentTransform ) )

		# record the last position of the mousedelta

	if (ev.type==InputEvent.SCREEN_DRAG or ev.type==InputEvent.MOUSE_MOTION or ev.type==InputEvent.JOYSTICK_MOTION or ev.type==InputEvent.SCREEN_TOUCH):
		mouseposlast = ev.pos
	# Android does not like this: Input.get_mouse_pos()

func clearSelection():
	for bl in selectedBlocks:
		var blo = get_node("GridMan/" + bl)
		print("this bothers Hugo")
		blo.setSelected(false)
		blo.scaleTweenNode(1.0).start()
	selectedBlocks = []

func addSelected(bl):
	selectedBlocks.append(bl)

# global working variables
var turn = Vector2( 0.0, 0.0 )            # the amount the mouse has turned
var mouseposlast = Input.get_mouse_pos()   # the mouses last position

# global tweakable parameters
var orbitrate = 10        # the rate the camera orbits the target when the mouse is moved
var active = true
var offClick = false
var selectedBlocks = []

func _ready():
	# Initalization here
	set_process_input(true)      # process user input events here

# called to handle a user input event
func _input(ev):
	# If the mouse has been moved
	if (ev.type==InputEvent.SCREEN_DRAG or (ev.type==InputEvent.MOUSE_MOTION and ev.button_mask == 1)):
		# calculate the delta change from the last mouse movement
		var mousedelta = (mouseposlast - ev.pos)
		# scale the mouse delta to a useful value
		turn = mousedelta / orbitrate
		# perform the rotation
		var currentTransform = get_transform().basis
		var dtime = get_process_delta_time()
		currentTransform = Matrix3( Vector3(0, 1, 0), PI * turn.x * dtime ) * currentTransform
		currentTransform = Matrix3( Vector3(1, 0, 0), PI * turn.y * dtime ) * currentTransform
		set_transform( Transform( currentTransform ) )

		# record the last position of the mousedelta

	if (ev.type==InputEvent.SCREEN_DRAG or ev.type==InputEvent.MOUSE_MOTION or ev.type==InputEvent.JOYSTICK_MOTION or ev.type==InputEvent.SCREEN_TOUCH):
		mouseposlast = ev.pos
	# Android does not like this: Input.get_mouse_pos()

func clearSelection():
	for bl in selectedBlocks:
		var blo = get_node("GridMan/" + bl)
		print("this bothers Hugo")
		blo.setSelected(false)
		blo.scaleTweenNode(1.0).start()
	selectedBlocks = []

func addSelected(bl):
	selectedBlocks.append(bl)


Minor fix.

Minor fix.

Fix.

# 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

# network variables
var network

# State variables.
var menuOn
var splashTween

# Timers.
var timer
var initialWait  = 0.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

# Main Menu Theme
var samplePlayer = StreamPlayer.new()
var songs = [load("res://main_theme_antris.ogg")]

# Loader dialog
var saveDir
var fileDialog

func skipTitle(skip):
	if skip:
		initialWait  = 0.01
		splashTimer  = 0.01
		warningTimer = 0.01

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

	Globals.set("Network", load("res://scripts/Network.gd").new())
	network = Globals.get("Network")

	var scn = load("res://networkProxy.scn")
	add_child(scn.instance())
	network.root = get_tree().get_root()

	# Load the config.
	var config = preload( "res://scripts/DataManager.gd" ).new().loadConfig()

	get_node("OptionsMenu/Panel/OnlineName/LineEdit").set_text( config.name )
	get_node("OptionsMenu/Panel/SoundVolume/SoundSlider").set_value( config.soundvolume )
	get_node("OptionsMenu/Panel/MusicVolume/MusicSlider").set_value( config.musicvolume )

	network.port = config.portnumber

	# Prepare puzzle loader dialog
	saveDir = OS.get_data_dir() + "/PuzzleSaves"
	fileDialog = preload("Editor.gd").initLoadSaveDialog(self, get_tree(), saveDir)

	# Main Menu Theme
	Globals.set("StreamPlayer", samplePlayer)
	add_child(samplePlayer)
	samplePlayer.set_stream(songs[0])
	samplePlayer.play()

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

# TODO save to ConfigFile, load this on beginning
func _on_Options_pressed():
	Menu_Main.guiOut()
	Menu_Options.guiIn()
	timer = 0.0
	menuOn = MENU_OPTIONS
	get_node("OptionsMenu/Panel/PortField/LineEdit").set_text(str(network.port))

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

func _on_SaveQuit_pressed():
	# Save the options.

	var config = { name = get_node("OptionsMenu/Panel/OnlineName/LineEdit").get_text()
				  , soundvolume = get_node("OptionsMenu/Panel/SoundVolume/SoundSlider").get_value()
				  , musicvolume = get_node("OptionsMenu/Panel/MusicVolume/MusicSlider").get_value()
				  , portnumber = get_node("OptionsMenu/Panel/PortField/LineEdit").get_text()
				  }

	preload( "res://scripts/DataManager.gd" ).new().saveConfig( config )

	var field = get_node("OptionsMenu/Panel/PortField/LineEdit")
	network.setPort(field.get_text())
	network.setPort(field.get_text())
	_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
	network.disconnect()

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

	if !network.isHost and !network.isNetwork:
		print("calling!")
		network.host(network.port)
		get_node("HostGame/Panel/Waiting").set_text("Waiting for player to join on port " + str(network.port) + "...")

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
	if (network.isClient):
		network.disconnect()

func _on_RandomPuzzle_pressed(puzzle=null):
	gotoPuzzleScene(get_tree().get_root(), false, puzzle)

static func gotoPuzzleScene(root, networked=false, puzzle=null):
	var p = load( "res://puzzle.scn" ).instance()
	p.mainPuzzle = networked

	if puzzle != null:
		p.generateRandom = false
		p.get_node("GridView/GridMan").set_puzzle(puzzle)
		p.get_node("GridView/GridMan").setupCam()

	# called on idle time
	goto_scene(root.get_tree(), [load("res://puzzleView.scn").instance(), p])

static func goto_scene(tree, scenes, freeAll=false):
	var root = tree.get_root()

	if freeAll:
		for child in root.get_children():
			child.queue_free()
	else:
		root.get_child( root.get_child_count() - 1 ).queue_free()
	for scn in scenes:
		scn.set_owner(root)
		root.call_deferred("add_child", scn )

func _on_Join_pressed():
	var IPPanel = get_node("JoinGame/Panel/IPAddress")
	var ip = IPPanel.get_text();

	if (ip.empty()):
		return

	if !network.isClient:
		network.connectTo(ip, network.port)

func _on_Editor_pressed():
	# called on idle time
	goto_scene(get_tree(), [load("res://editor.scn").instance()])

func _on_LoadPuzzle_pressed():
	preload("Editor.gd").showLoadDialog(fileDialog, self)

# called by the fileDialog
func puzzleLoad():
	var f = fileDialog.get_current_path()
	if f == null or f == "":
		return
	print("LOADING FROM ", f)
	_on_RandomPuzzle_pressed(preload("DataManager.gd").loadPuzzle( f ))

# 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

# network variables
var network

# State variables.
var menuOn
var splashTween

# Timers.
var timer
var initialWait  = 0.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

# Main Menu Theme
var samplePlayer = StreamPlayer.new()
var songs = [load("res://main_theme_antris.ogg")]

# Loader dialog
var saveDir
var fileDialog

func skipTitle(skip):
	if skip:
		initialWait  = 0.01
		splashTimer  = 0.01
		warningTimer = 0.01

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

	Globals.set("Network", load("res://scripts/Network.gd").new())
	network = Globals.get("Network")

	var scn = load("res://networkProxy.scn")
	add_child(scn.instance())
	network.root = get_tree().get_root()

	# Load the config.
	var config = preload( "res://scripts/DataManager.gd" ).new().loadConfig()

	get_node("OptionsMenu/Panel/OnlineName/LineEdit").set_text( config.name )
	get_node("OptionsMenu/Panel/SoundVolume/SoundSlider").set_value( config.soundvolume )
	get_node("OptionsMenu/Panel/MusicVolume/MusicSlider").set_value( config.musicvolume )

	network.port = config.portnumber

	# Prepare puzzle loader dialog
	saveDir = OS.get_data_dir() + "/PuzzleSaves"
	fileDialog = preload("Editor.gd").initLoadSaveDialog(self, get_tree(), saveDir)

	# Main Menu Theme
	Globals.set("StreamPlayer", samplePlayer)
	add_child(samplePlayer)
	samplePlayer.set_stream(songs[0])
	samplePlayer.play()

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

# TODO save to ConfigFile, load this on beginning
func _on_Options_pressed():
	Menu_Main.guiOut()
	Menu_Options.guiIn()
	timer = 0.0
	menuOn = MENU_OPTIONS
	get_node("OptionsMenu/Panel/PortField/LineEdit").set_text(str(network.port))

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

func _on_SaveQuit_pressed():
	# Save the options.

	var config = { name = get_node("OptionsMenu/Panel/OnlineName/LineEdit").get_text()
				  , soundvolume = get_node("OptionsMenu/Panel/SoundVolume/SoundSlider").get_value()
				  , musicvolume = get_node("OptionsMenu/Panel/MusicVolume/MusicSlider").get_value()
				  , portnumber = get_node("OptionsMenu/Panel/PortField/LineEdit").get_text()
				  }

	preload( "res://scripts/DataManager.gd" ).new().saveConfig( config )

	var field = get_node("OptionsMenu/Panel/PortField/LineEdit")
	network.setPort(field.get_text())
	network.setPort(field.get_text())
	_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
	network.disconnect()

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

	if !network.isHost and !network.isNetwork:
		print("calling!")
		network.host(network.port)
		get_node("HostGame/Panel/Waiting").set_text("Waiting for player to join on port " + str(network.port) + "...")

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
	if (network.isClient):
		network.disconnect()

func _on_RandomPuzzle_pressed(puzzle=null):
	gotoPuzzleScene(get_tree().get_root(), false, puzzle)

static func gotoPuzzleScene(root, networked=false, puzzle=null):
	var p = load( "res://puzzle.scn" ).instance()
	p.mainPuzzle = networked

	if puzzle != null:
		p.generateRandom = false
		p.get_node("GridView/GridMan").set_puzzle(puzzle)
		p.get_node("GridView/GridMan").setupCam()

	# called on idle time
	goto_scene(root.get_tree(), [load("res://puzzleView.scn").instance(), p])

static func goto_scene(tree, scenes, freeAll=false):
	var root = tree.get_root()
	root.print_tree()
	if freeAll:
		for child in root.get_children():
			child.queue_free()
	else:
		root.get_child( root.get_child_count() - 1 ).queue_free()
	for scn in scenes:
		scn.set_owner(root)
		root.call_deferred("add_child", scn )

func _on_Join_pressed():
	var IPPanel = get_node("JoinGame/Panel/IPAddress")
	var ip = IPPanel.get_text();

	if (ip.empty()):
		return

	if !network.isClient:
		network.connectTo(ip, network.port)

func _on_Editor_pressed():
	# called on idle time
	goto_scene(get_tree(), [load("res://editor.scn").instance()])

func _on_LoadPuzzle_pressed():
	preload("Editor.gd").showLoadDialog(fileDialog, self)

# called by the fileDialog
func puzzleLoad():
	var f = fileDialog.get_current_path()
	if f == null or f == "":
		return
	print("LOADING FROM ", f)
	_on_RandomPuzzle_pressed(preload("DataManager.gd").loadPuzzle( f ))


Fade black background too using alpha

Fade black background too using alpha

extends Node

# Gameplay

var block_size = 5

# Drawing
var background_fade = Color(0.5, 0.5, 0.5, 0.5)
var player_highlight = Color(0.5, 0, 0)

# Animation

var camera_lerp = 1.5
var camera_zoom_lerp = 2.0
var move_duration = 0.5

extends Node

# Gameplay

var block_size = 5

# Drawing
var background_fade = Color(0.5, 0.5, 0.5)
var player_highlight = Color(0.5, 0, 0)

# Animation

var camera_lerp = 1.5
var camera_zoom_lerp = 2.0
var move_duration = 0.5


Adds animation tests.

Adds animation tests.

extends ColorRect

const GOLDEN_GLTF_SCENE: PackedScene = preload("res://gltf/45545-relax-name-sanitization.gltf")

## These constants are the prefix part of the node names used in the golden glTF file.
# Prefix for "basic" nodes, no import hints, no animation, etc...
const GLTF_PREFIX_BASIC: String = "Basic_"

# Prefix for nodes that have an import hint.
const GLTF_PREFIX_HINTED: String = "Hinted_"

# Prefix for nodes that have an associated animation.
const GLTF_PREFIX_ANIMATED: String = "Animated_"

# The suffix for animations (all animations in the golden glTF file use the Blender default suffix
# "Action" and the looping import hint "-loop".
const GLTF_SUFFIX_ANIMATION: String = "Action-loop"

## These constants are the infix part of the node names used in the golden glTF file.
const GLTF_ALPHANUMERIC: String = "Alphanumeric123"
# The last three symbols are:
# https://en.wiktionary.org/wiki/%E3%82%B4#Japanese
# https://en.wiktionary.org/wiki/%E3%83%89#Japanese
# https://en.wiktionary.org/wiki/%E3%83%84#Japanese
const GLTF_KATAKANA: String = "Katakana_ゴドツ"
# The last two symbols are:
# https://emojipedia.org/grinning-face
# https://emojipedia.org/robot/
const GLTF_EMOJI: String = "Emoji_😀🤖"
const GLTF_ALLOWED_SYMBOLS: String = "AllowedSymbols_!#$%^&*()-=[]{}';<>,~"
const GLTF_DISALLOWED_NONCLASHING: String = "Disallowed_Non-clashing_.:@\"/"
const GLTF_DISALLOWED_CLASHING_1: String = "Disallowed_Clashing_."
const GLTF_DISALLOWED_CLASHING_2: String = "Disallowed_Clashing_@"
const GLTF_DISALLOWED_CLASHING_DEEP_TREE_1: String = "Disallowed_Clashing_Deep_Tree_@"
const GLTF_DISALLOWED_CLASHING_DEEP_TREE_2: String = "Disallowed_Clashing_Deep_Tree_@@"
const GLTF_DISALLOWED_CLASHING_DEEP_TREE_3: String = "Disallowed_Clashing_Deep_Tree_@@@"

var output: BoxContainer
var test_scene: Node
var test_animation_player: AnimationPlayer

func _ready():
	output = $ScrollContainer/OutputWindow
	test_scene = GOLDEN_GLTF_SCENE.instance()
	test_animation_player = test_scene.get_node_or_null("AnimationPlayer")
	if not test_animation_player:
		_add_line("ERROR: imported glTF scene has no AnimationPlayer node!", Color.orangered)
		print("FAIL: imported glTF scene has no AnimationPlayer so no tests will be run.")
		return

	var passed_tests: Array = []
	var failed_tests: Array = []

	for method_name in _find_tests():
		_add_test_header(method_name)
		var result: bool = self.call(method_name)
		_add_line(" ")
		if result:
			passed_tests.push_back(method_name)
		else:
			failed_tests.push_back(method_name)

	if failed_tests:
		print("FAIL: %d failed tests:" % len(failed_tests))
		for test in failed_tests:
			print("\t%s" % test)
		print("")
		print("Animations:")
		for animation_name in test_animation_player.get_animation_list():
			print("\t%s" % animation_name)
	else:
		print("PASS: Passed all %d tests" % len(passed_tests))

func _find_tests() -> Array:
	var ret: Array = []
	for method in get_method_list():
		var method_name: String = method.get("name")
		if method_name.begins_with("_test_"):
			ret.push_back(method_name)
	ret.sort()
	return ret

func _add_test_header(method_name: String) -> void:
	_add_line(method_name, Color.royalblue)

func _add_result(succeeded: bool, message: String) -> void:
	var prefix: String = "  [OK] " if succeeded else "  [FAIL] "
	_add_line(prefix + message, Color.forestgreen if succeeded else Color.orangered)

func _add_line(text: String, font_color: Color = Color.black) -> void:
	var line: Label = Label.new()
	line.text = text
	line.add_theme_font_size_override("font_size", 22)
	line.add_theme_color_override("font_color", font_color)
	output.add_child(line)

func _check_basic(original: String, expected: String) -> bool:
	var node: Node = test_scene.get_node_or_null(expected)
	if not node:
		_add_result(
			false,
			"Missing expected Godot node '%s' for glTF node '%s'" % [expected, original])
		return false
	_add_result(true, "Found expected node '%s'" % expected)
	return true

func _test_basic_alphanumeric_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_ALPHANUMERIC
	var expected: String = original
	return _check_basic(original, expected)

func _test_basic_allowed_symbols_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_ALLOWED_SYMBOLS
	var expected: String = original
	return _check_basic(original, expected)

func _test_basic_katakana_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_KATAKANA
	var expected: String = original
	return _check_basic(original, expected)

func _test_basic_emoji_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_EMOJI
	var expected: String = original
	return _check_basic(original, expected)

func _test_basic_disallowed_characters_are_stripped() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_NONCLASHING
	var expected: String = GLTF_PREFIX_BASIC + "Disallowed_Non-clashing_"
	return _check_basic(original, expected)

func _test_basic_disallowed_clashing_nodes_are_uniquified() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_1
	var expected: String = GLTF_PREFIX_BASIC + "Disallowed_Clashing_"
	if not _check_basic(original, expected):
		return false

	original = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_2
	expected = GLTF_PREFIX_BASIC + "Disallowed_Clashing_2"
	return _check_basic(original, expected)

func _test_basic_disallowed_nested_nodes_are_uniquified() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_DEEP_TREE_1
	var expected_root: String = GLTF_PREFIX_BASIC + "Disallowed_Clashing_Deep_Tree_"
	if not _check_basic(original, expected_root):
		return false

	original = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_DEEP_TREE_2
	var expected_mid = expected_root + "/" + GLTF_PREFIX_BASIC + "Disallowed_Clashing_Deep_Tree_2"
	if not _check_basic(original, expected_mid):
		return false

	original = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_DEEP_TREE_3
	var expected_leaf = expected_mid + "/" + GLTF_PREFIX_BASIC + "Disallowed_Clashing_Deep_Tree_3"
	return _check_basic(original, expected_leaf)

# Verifies that a node exists with the expected name and that it has a StaticBody3D child.
func _check_hinted_col(original: String, expected: String) -> bool:
	var node: Node = test_scene.get_node_or_null(expected)
	if not node:
		_add_result(
			false,
			"Missing expected Godot node '%s' for glTF node '%s'" % [expected, original])
		return false

	var collision_body: StaticBody3D = node.get_node_or_null("static_collision")
	if not collision_body:
		_add_result(
			false,
			"Missing 'static_collision' child for Godot node '%s' (glTF '%s')" % [
				expected, original])
		return false

	# This test assumes that if the static_collision node was created the -col hint was properly
	# handled (as that behavior should be tested elsewhere).
	_add_result(true, "Expected node '%s' contains a static_collision StaticBody3D" % expected)
	return true

func _test_hinted_alphanumeric_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_HINTED + GLTF_ALPHANUMERIC
	var expected: String = original
	return _check_hinted_col(original, expected)

func _test_hinted_allowed_symbols_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_HINTED + GLTF_ALLOWED_SYMBOLS
	var expected: String = original
	return _check_hinted_col(original, expected)

func _test_hinted_katakana_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_HINTED + GLTF_KATAKANA
	var expected: String = original
	return _check_hinted_col(original, expected)

func _test_hinted_emoji_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_HINTED + GLTF_EMOJI
	var expected: String = original
	return _check_hinted_col(original, expected)

func _test_hinted_disallowed_characters_are_stripped() -> bool:
	var original: String = GLTF_PREFIX_HINTED + GLTF_DISALLOWED_NONCLASHING
	var expected: String = GLTF_PREFIX_HINTED + "Disallowed_Non-clashing_"
	return _check_hinted_col(original, expected)

func _test_hinted_disallowed_clashing_nodes_are_uniquified() -> bool:
	var original: String = GLTF_PREFIX_HINTED + GLTF_DISALLOWED_CLASHING_1
	var expected: String = GLTF_PREFIX_HINTED + "Disallowed_Clashing_"
	if not _check_hinted_col(original, expected):
		return false

	original = GLTF_PREFIX_HINTED + GLTF_DISALLOWED_CLASHING_2
	expected = GLTF_PREFIX_HINTED + "Disallowed_Clashing_2"
	return _check_hinted_col(original, expected)

func _test_hinted_disallowed_nested_nodes_are_uniquified() -> bool:
	var original: String = GLTF_PREFIX_HINTED + GLTF_DISALLOWED_CLASHING_DEEP_TREE_1
	var expected_root: String = GLTF_PREFIX_HINTED + "Disallowed_Clashing_Deep_Tree_"
	if not _check_hinted_col(original, expected_root):
		return false

	original = GLTF_PREFIX_HINTED + GLTF_DISALLOWED_CLASHING_DEEP_TREE_2
	var expected_mid = expected_root + "/" + GLTF_PREFIX_HINTED + "Disallowed_Clashing_Deep_Tree_2"
	if not _check_hinted_col(original, expected_mid):
		return false

	original = GLTF_PREFIX_HINTED + GLTF_DISALLOWED_CLASHING_DEEP_TREE_3
	var expected_leaf = expected_mid + "/" + GLTF_PREFIX_HINTED + "Disallowed_Clashing_Deep_Tree_3"
	return _check_hinted_col(original, expected_leaf)

# Verifies that a node exists with the expected name and that an associated animation exists.
func _check_animated(original: String, expected: String) -> bool:
	var node: Node = test_scene.get_node_or_null(expected)
	if not node:
		_add_result(
			false,
			"Missing expected Godot node '%s' for glTF node '%s'" % [expected, original])
		return false

	var expected_animation: String = expected + GLTF_SUFFIX_ANIMATION
	var animation: Animation = test_animation_player.get_animation(expected_animation)
	if not animation:
		_add_result(
			false,
			"Missing expected animation '%s' for Godot node '%s' (glTF '%s')" % [
				expected_animation, expected, original])
		return false

	var track_count: int = animation.get_track_count()
	if track_count != 1:
		_add_result(
			false,
			"Track count %d != 1 on animation '%s' for Godot node '%s' (glTF '%s')" % [
				track_count, expected_animation, expected, original])
		return false

	var track_type: int = animation.track_get_type(0)
	if track_type != Animation.TYPE_TRANSFORM:
		_add_result(
			false,
			"Unexpected track type %d on animation '%s' for Godot node '%s' (glTF '%s')" % [
				track_type, expected_animation, expected, original])
		return false

	var track_path: NodePath = animation.track_get_path(0)
	var expected_track_path = NodePath(expected)
	if track_path != expected_track_path:
		_add_result(
			false,
			("Incorrect track_path '%s' (expected '%s') on animation '%s' for Godot node '%s' \
				 (glTF '%s')" % [
					track_path, expected_track_path, expected_animation, expected, original]))
		return false

	# This test assumes that if the static_collision node was created the -col hint was properly
	# handled (as that behavior should be tested elsewhere).
	_add_result(true, "Expected node '%s' has an associated animation" % expected)
	return true

func _test_animated_alphanumeric_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_ANIMATED + GLTF_ALPHANUMERIC
	var expected: String = original
	return _check_animated(original, expected)

func _test_animated_allowed_symbols_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_ANIMATED + GLTF_ALLOWED_SYMBOLS
	var expected: String = original
	return _check_animated(original, expected)

func _test_animated_katakana_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_ANIMATED + GLTF_KATAKANA
	var expected: String = original
	return _check_animated(original, expected)

func _test_animated_emoji_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_ANIMATED + GLTF_EMOJI
	var expected: String = original
	return _check_animated(original, expected)

func _test_animated_disallowed_characters_are_stripped() -> bool:
	var original: String = GLTF_PREFIX_ANIMATED + GLTF_DISALLOWED_NONCLASHING
	var expected: String = GLTF_PREFIX_ANIMATED + "Disallowed_Non-clashing_"
	return _check_animated(original, expected)

func _test_animated_disallowed_clashing_nodes_are_uniquified() -> bool:
	var original: String = GLTF_PREFIX_ANIMATED + GLTF_DISALLOWED_CLASHING_1
	var expected: String = GLTF_PREFIX_ANIMATED + "Disallowed_Clashing_"
	if not _check_animated(original, expected):
		return false

	original = GLTF_PREFIX_ANIMATED + GLTF_DISALLOWED_CLASHING_2
	expected = GLTF_PREFIX_ANIMATED + "Disallowed_Clashing_2"
	return _check_animated(original, expected)

func _test_animated_disallowed_nested_nodes_are_uniquified() -> bool:
	var original: String = GLTF_PREFIX_ANIMATED + GLTF_DISALLOWED_CLASHING_DEEP_TREE_1
	var expected_root: String = GLTF_PREFIX_ANIMATED + "Disallowed_Clashing_Deep_Tree_"
	if not _check_animated(original, expected_root):
		return false

	original = GLTF_PREFIX_ANIMATED + GLTF_DISALLOWED_CLASHING_DEEP_TREE_2
	var expected_mid = (expected_root + "/" +
		GLTF_PREFIX_ANIMATED + "Disallowed_Clashing_Deep_Tree_2")
	if not _check_animated(original, expected_mid):
		return false

	original = GLTF_PREFIX_ANIMATED + GLTF_DISALLOWED_CLASHING_DEEP_TREE_3
	var expected_leaf = (expected_mid + "/" +
		GLTF_PREFIX_ANIMATED + "Disallowed_Clashing_Deep_Tree_3")
	return _check_animated(original, expected_leaf)

extends ColorRect

const GOLDEN_GLTF_SCENE: PackedScene = preload("res://gltf/45545-relax-name-sanitization.gltf")

## These constants are the prefix part of the node names used in the golden glTF file.
# Prefix for "basic" nodes, no import hints, no animation, etc...
const GLTF_PREFIX_BASIC: String = "Basic_"

# Prefix for nodes that have an import hint.
const GLTF_PREFIX_HINTED: String = "Hinted_"

# Prefix for nodes that have an associated animation.
const GLTF_PREFIX_ANIMATED: String = "Animated_"

## These constants are the infix part of the node names used in the golden glTF file.
const GLTF_ALPHANUMERIC: String = "Alphanumeric123"
# The last three symbols are:
# https://en.wiktionary.org/wiki/%E3%82%B4#Japanese
# https://en.wiktionary.org/wiki/%E3%83%89#Japanese
# https://en.wiktionary.org/wiki/%E3%83%84#Japanese
const GLTF_KATAKANA: String = "Katakana_ゴドツ"
# The last two symbols are:
# https://emojipedia.org/grinning-face
# https://emojipedia.org/robot/
const GLTF_EMOJI: String = "Emoji_😀🤖"
const GLTF_ALLOWED_SYMBOLS: String = "AllowedSymbols_!#$%^&*()-=[]{}';<>,~"
const GLTF_DISALLOWED_NONCLASHING: String = "Disallowed_Non-clashing_.:@\"/"
const GLTF_DISALLOWED_CLASHING_1: String = "Disallowed_Clashing_."
const GLTF_DISALLOWED_CLASHING_2: String = "Disallowed_Clashing_@"
const GLTF_DISALLOWED_CLASHING_DEEP_TREE_1: String = "Disallowed_Clashing_Deep_Tree_@"
const GLTF_DISALLOWED_CLASHING_DEEP_TREE_2: String = "Disallowed_Clashing_Deep_Tree_@@"
const GLTF_DISALLOWED_CLASHING_DEEP_TREE_3: String = "Disallowed_Clashing_Deep_Tree_@@@"

var output: BoxContainer
var test_scene: Node

func _ready():
	output = $ScrollContainer/OutputWindow
	test_scene = GOLDEN_GLTF_SCENE.instance()

	var passed_tests: Array = []
	var failed_tests: Array = []

	for method_name in _find_tests():
		_add_test_header(method_name)
		var result: bool = self.call(method_name)
		_add_line(" ")
		if result:
			passed_tests.push_back(method_name)
		else:
			failed_tests.push_back(method_name)

	if failed_tests:
		print("%d failed tests:" % len(failed_tests))
		for test in failed_tests:
			print("\t%s" % test)
	else:
		print("Passed all %d tests" % len(passed_tests))

func _find_tests() -> Array:
	var ret: Array = []
	for method in get_method_list():
		var method_name: String = method.get("name")
		if method_name.begins_with("_test_"):
			ret.push_back(method_name)
	ret.sort()
	return ret

func _add_test_header(method_name: String) -> void:
	_add_line(method_name, Color.royalblue)

func _add_result(succeeded: bool, message: String) -> void:
	var prefix: String = "  [OK] " if succeeded else "  [FAIL] "
	_add_line(prefix + message, Color.forestgreen if succeeded else Color.orangered)

func _add_line(text: String, color: Color = Color.white) -> void:
	var line: Label = Label.new()
	line.text = text
	line.add_theme_font_size_override("font_size", 22)
	line.add_theme_color_override("font_color", color)
	output.add_child(line)

func _check_basic(original: String, expected: String) -> bool:
	var node: Node = test_scene.get_node_or_null(expected)
	if not node:
		_add_result(
			false,
			"Missing expected Godot node '%s' for glTF node '%s'" % [expected, original])
		return false
	_add_result(true, "Found expected node '%s'" % expected)
	return true

func _test_basic_alphanumeric_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_ALPHANUMERIC
	var expected: String = original
	return _check_basic(original, expected)

func _test_basic_allowed_symbols_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_ALLOWED_SYMBOLS
	var expected: String = original
	return _check_basic(original, expected)

func _test_basic_katakana_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_KATAKANA
	var expected: String = original
	return _check_basic(original, expected)

func _test_basic_emoji_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_EMOJI
	var expected: String = original
	return _check_basic(original, expected)

func _test_basic_disallowed_characters_are_stripped() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_NONCLASHING
	var expected: String = GLTF_PREFIX_BASIC + "Disallowed_Non-clashing_"
	return _check_basic(original, expected)

func _test_basic_disallowed_clashing_nodes_are_uniquified() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_1
	var expected: String = GLTF_PREFIX_BASIC + "Disallowed_Clashing_"
	if not _check_basic(original, expected):
		return false

	original = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_2
	expected = GLTF_PREFIX_BASIC + "Disallowed_Clashing_2"
	return _check_basic(original, expected)

func _test_basic_disallowed_nested_nodes_are_uniquified() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_DEEP_TREE_1
	var expected_root: String = GLTF_PREFIX_BASIC + "Disallowed_Clashing_Deep_Tree_"
	if not _check_basic(original, expected_root):
		return false

	original = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_DEEP_TREE_2
	var expected_mid = expected_root + "/" + GLTF_PREFIX_BASIC + "Disallowed_Clashing_Deep_Tree_2"
	if not _check_basic(original, expected_mid):
		return false

	original = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_DEEP_TREE_3
	var expected_leaf = expected_mid + "/" + GLTF_PREFIX_BASIC + "Disallowed_Clashing_Deep_Tree_3"
	return _check_basic(original, expected_leaf)

# Verifies that a node exists with the expected name and that it has a StaticBody3D child.
func _check_hinted_col(original: String, expected: String) -> bool:
	var node: Node = test_scene.get_node_or_null(expected)
	if not node:
		_add_result(
			false,
			"Missing expected Godot node '%s' for glTF node '%s'" % [expected, original])
		return false

	var collision_body: StaticBody3D = node.get_node_or_null("static_collision")
	if not collision_body:
		_add_result(
			false,
			"Missing 'static_collision' child for Godot node '%s' (glTF '%s')" % [
				expected, original])
		return false

	# This test assumes that if the static_collision node was created the -col hint was properly
	# handled (as that behavior should be tested elsewhere).
	_add_result(true, "Expected node '%s' contains a static_collision StaticBody3D" % expected)
	return true

func _test_hinted_alphanumeric_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_ALPHANUMERIC
	var expected: String = original
	return _check_basic(original, expected)

func _test_hinted_allowed_symbols_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_ALLOWED_SYMBOLS
	var expected: String = original
	return _check_hinted_col(original, expected)

func _test_hinted_katakana_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_KATAKANA
	var expected: String = original
	return _check_hinted_col(original, expected)

func _test_hinted_emoji_is_unchanged() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_EMOJI
	var expected: String = original
	return _check_hinted_col(original, expected)

func _test_hinted_disallowed_characters_are_stripped() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_NONCLASHING
	var expected: String = GLTF_PREFIX_BASIC + "Disallowed_Non-clashing_"
	return _check_hinted_col(original, expected)

func _test_hinted_disallowed_clashing_nodes_are_uniquified() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_1
	var expected: String = GLTF_PREFIX_BASIC + "Disallowed_Clashing_"
	if not _check_hinted_col(original, expected):
		return false

	original = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_2
	expected = GLTF_PREFIX_BASIC + "Disallowed_Clashing_2"
	return _check_hinted_col(original, expected)

func _test_hinted_disallowed_nested_nodes_are_uniquified() -> bool:
	var original: String = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_DEEP_TREE_1
	var expected_root: String = GLTF_PREFIX_BASIC + "Disallowed_Clashing_Deep_Tree_"
	if not _check_hinted_col(original, expected_root):
		return false

	original = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_DEEP_TREE_2
	var expected_mid = expected_root + "/" + GLTF_PREFIX_BASIC + "Disallowed_Clashing_Deep_Tree_2"
	if not _check_hinted_col(original, expected_mid):
		return false

	original = GLTF_PREFIX_BASIC + GLTF_DISALLOWED_CLASHING_DEEP_TREE_3
	var expected_leaf = expected_mid + "/" + GLTF_PREFIX_BASIC + "Disallowed_Clashing_Deep_Tree_3"
	return _check_hinted_col(original, expected_leaf)