Browse Source

Added a simple multiplayer test

Juan Linietsky 9 years ago
parent
commit
d9c91b93d9

+ 1 - 1
2d/rubegoldberg/engine.cfg

@@ -6,5 +6,5 @@ icon="res://icon.png"
 
 
 [physics_2d]
 [physics_2d]
 
 
+default_linear_damp=0.01
 default_gravity=500
 default_gravity=500
-default_density=0.01

+ 1 - 0
misc/window_management/observer/observer.gd

@@ -62,6 +62,7 @@ func _input(event):
 	if(event.type == InputEvent.MOUSE_MOTION):
 	if(event.type == InputEvent.MOUSE_MOTION):
 		r_pos = event.relative_pos
 		r_pos = event.relative_pos
 	
 	
+	
 	if(impulse(event, "ui_cancel")):
 	if(impulse(event, "ui_cancel")):
 		if(state == STATE_GRAB):
 		if(state == STATE_GRAB):
 			Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
 			Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)

BIN
misc/window_management/window_management.scn


+ 35 - 0
networking/simple_multiplayer/bomb.gd

@@ -0,0 +1,35 @@
+
+extends Area2D
+
+# member variables here, example:
+# var a=2
+# var b="textvar"
+
+var in_area = []
+var owner
+#called from the animation
+func explode():
+	if (not is_network_master()):
+		#but will call explosion only on master
+		return
+	
+	for p in in_area:
+		if (p.has_method("exploded")):
+			p.rpc("exploded",owner) #exploded has a master keyword, so it will only be received by the master
+		
+func done():
+	queue_free()
+	
+func _ready():
+	# Called every time the node is added to the scene.
+	# Initialization here
+	pass
+
+func _on_bomb_body_enter( body ):
+	if (not body in in_area):
+		in_area.append(body)
+
+
+func _on_bomb_body_exit( body ):
+	in_area.erase(body)
+	

+ 156 - 0
networking/simple_multiplayer/bomb.tscn

@@ -0,0 +1,156 @@
+[gd_scene load_steps=8 format=1]
+
+[ext_resource path="res://bomb.gd" type="Script" id=1]
+[ext_resource path="res://brickfloor.png" type="Texture" id=2]
+[ext_resource path="res://explosion.png" type="Texture" id=3]
+
+[sub_resource type="RectangleShape2D" id=1]
+
+custom_solver_bias = 0.0
+extents = Vector2( 8, 96 )
+
+[sub_resource type="RectangleShape2D" id=2]
+
+custom_solver_bias = 0.0
+extents = Vector2( 96, 8 )
+
+[sub_resource type="ColorRamp" id=3]
+
+offsets = FloatArray( 0, 0.0233918, 0.80117, 1 )
+colors = ColorArray( 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0.922156, 0, 0, 0, 0 )
+
+[sub_resource type="Animation" id=4]
+
+resource/name = "anim"
+length = 4.0
+loop = false
+step = 0.1
+tracks/0/type = "value"
+tracks/0/path = NodePath("sprite:modulate")
+tracks/0/interp = 1
+tracks/0/imported = false
+tracks/0/keys = { "times":FloatArray( 0, 0.4, 0.6, 0.8, 1.1, 1.3, 1.5, 1.8, 1.9, 2, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 3 ), "transitions":FloatArray( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), "update":0, "values":[ Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 1 ), Color( 8, 8, 8, 1 ), Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 1 ), Color( 8, 8, 8, 1 ), Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 1 ), Color( 8, 8, 8, 1 ), Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 1 ), Color( 8, 8, 8, 1 ), Color( 1, 1, 1, 1 ), Color( 8, 8, 8, 1 ), Color( 1, 1, 1, 1 ), Color( 8, 8, 8, 1 ), Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 0 ) ] }
+tracks/1/type = "value"
+tracks/1/path = NodePath("explosion1:config/emitting")
+tracks/1/interp = 1
+tracks/1/imported = false
+tracks/1/keys = { "times":FloatArray( 2.8 ), "transitions":FloatArray( 1 ), "update":1, "values":[ true ] }
+tracks/2/type = "value"
+tracks/2/path = NodePath("explosion2:config/emitting")
+tracks/2/interp = 1
+tracks/2/imported = false
+tracks/2/keys = { "times":FloatArray( 2.8 ), "transitions":FloatArray( 1 ), "update":1, "values":[ true ] }
+tracks/3/type = "method"
+tracks/3/path = NodePath(".")
+tracks/3/interp = 1
+tracks/3/imported = false
+tracks/3/keys = { "times":FloatArray( 2.8, 3.4 ), "transitions":FloatArray( 1, 1 ), "values":[ { "args":[  ], "method":"explode" }, { "args":[  ], "method":"done" } ] }
+
+[node name="bomb" type="Area2D"]
+
+input/pickable = true
+shapes/0/shape = SubResource( 1 )
+shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 )
+shapes/0/trigger = false
+shapes/1/shape = SubResource( 2 )
+shapes/1/transform = Matrix32( 1, 0, 0, 1, 0, 0 )
+shapes/1/trigger = false
+gravity_vec = Vector2( 0, 1 )
+gravity = 98.0
+linear_damp = 0.1
+angular_damp = 1.0
+script/script = ExtResource( 1 )
+
+[node name="sprite" type="Sprite" parent="."]
+
+transform/pos = Vector2( -2.92606, -2.92606 )
+texture = ExtResource( 2 )
+region = true
+region_rect = Rect2( 144, 0, 48, 48 )
+
+[node name="shape1" type="CollisionShape2D" parent="."]
+
+shape = SubResource( 1 )
+trigger = false
+_update_shape_index = 0
+
+[node name="shape2" type="CollisionShape2D" parent="."]
+
+shape = SubResource( 2 )
+trigger = false
+_update_shape_index = 1
+
+[node name="explosion1" type="Particles2D" parent="."]
+
+config/amount = 16
+config/lifetime = 0.4
+config/emit_timeout = 0.4
+config/emitting = false
+config/half_extents = Vector2( 80, 14 )
+config/explosiveness = 0.2
+config/texture = ExtResource( 3 )
+params/direction = 0.0
+params/spread = 10.0
+params/linear_velocity = 0.0
+params/spin_velocity = 3.0
+params/orbit_velocity = 0.0
+params/gravity_direction = 0.0
+params/gravity_strength = 0.0
+params/radial_accel = 0.0
+params/tangential_accel = 0.0
+params/damping = 0.0
+params/initial_angle = 358.292
+params/initial_size = 0.8
+params/final_size = 1.0
+params/hue_variation = 0.0
+params/anim_speed_scale = 1.0
+params/anim_initial_pos = 0.0
+randomness/spin_velocity = 1.0
+randomness/initial_angle = 1.0
+color/color_ramp = SubResource( 3 )
+
+[node name="explosion2" type="Particles2D" parent="."]
+
+config/amount = 16
+config/lifetime = 0.4
+config/emit_timeout = 0.4
+config/emitting = false
+config/half_extents = Vector2( 14, 80 )
+config/explosiveness = 0.2
+config/texture = ExtResource( 3 )
+params/direction = 0.0
+params/spread = 10.0
+params/linear_velocity = 0.0
+params/spin_velocity = 3.0
+params/orbit_velocity = 0.0
+params/gravity_direction = 0.0
+params/gravity_strength = 0.0
+params/radial_accel = 0.0
+params/tangential_accel = 0.0
+params/damping = 0.0
+params/initial_angle = 358.292
+params/initial_size = 0.8
+params/final_size = 1.0
+params/hue_variation = 0.0
+params/anim_speed_scale = 1.0
+params/anim_initial_pos = 0.0
+randomness/spin_velocity = 1.0
+randomness/initial_angle = 1.0
+color/color_ramp = SubResource( 3 )
+
+[node name="anim" type="AnimationPlayer" parent="."]
+
+playback/process_mode = 1
+playback/default_blend_time = 0.0
+root/root = NodePath("..")
+anims/anim = SubResource( 4 )
+playback/active = true
+playback/speed = 1.0
+blend_times = [  ]
+autoplay = "anim"
+
+[connection signal="body_enter" from="." to="." method="_on_bomb_body_enter"]
+
+[connection signal="body_exit" from="." to="." method="_on_bomb_body_exit"]
+
+

BIN
networking/simple_multiplayer/brickfloor.png


BIN
networking/simple_multiplayer/charwalk.png


+ 21 - 0
networking/simple_multiplayer/engine.cfg

@@ -0,0 +1,21 @@
+[application]
+
+name="Multiplayer Bomber"
+main_scene="res://lobby.tscn"
+
+[autoload]
+
+gamestate="*res://gamestate.gd"
+
+[image_loader]
+
+filter=false
+gen_mipmaps=false
+
+[input]
+
+move_left=[key(Left)]
+move_right=[key(Right)]
+move_up=[key(Up)]
+move_down=[key(Down)]
+set_bomb=[key(Space)]

BIN
networking/simple_multiplayer/explosion.png


+ 182 - 0
networking/simple_multiplayer/gamestate.gd

@@ -0,0 +1,182 @@
+
+extends Node
+
+#default game port
+const DEFAULT_PORT = 10567
+
+#name for my player
+var player_name = "The Warrior"
+
+#names for remote players in id:name format
+var players = {}
+
+#signals to let lobby GUI know what's going on
+signal player_list_changed()
+signal connection_failed()
+signal connection_succeeded()
+signal game_ended()
+signal game_error(what)
+
+# callback from SceneTree
+func _player_connected(id):
+	#this is not used, because _connected_ok is called for clients on success and will do the job.
+	pass 
+	
+# callback from SceneTree
+func _player_disconnected(id):
+
+	if (get_tree().is_network_server()):
+		if (has_node("/root/world")): # game is in progress
+			emit_signal("game_error","Player "+players[id]+" disconnected")
+			end_game()
+		else: #game is not in progress
+			#if we are the server, send to the new dude all the already registered playes
+			unregister_player(id)
+			for p_id in players:
+				#erase in the server
+				rpc_id( p_id, "unregister_player", id)
+			
+					
+# callback from SceneTree, only for clients (not server)
+func _connected_ok():
+	#Registration of a client beings here, Tell everyone that we are here
+	rpc( "register_player", get_tree().get_network_unique_id(), player_name )
+	emit_signal("connection_succeeded")
+
+# callback from SceneTree, only for clients (not server)	
+func _server_disconnected():
+	emit_signal("game_error","Server disconnected")
+	end_game()
+
+# callback from SceneTree, only for clients (not server)	
+func _connected_fail():
+	get_tree().set_network_peer(null) #remove peer
+	emit_signal("connection_failed")	
+
+# lobby management functions
+remote func register_player(id, name):
+	
+	if (get_tree().is_network_server()):
+		#if we are the server, let everyone know about the new players
+		rpc_id( id, "register_player", 1, player_name ) # send myself to new dude
+		for p_id in players: #then, for each remoe player
+			rpc_id( id, "register_player", p_id, players[p_id] ) # send player to new dude
+			rpc_id( p_id, "register_player", id, name ) # send new dude to player
+			
+	players[id]=name
+					
+	emit_signal("player_list_changed")
+
+remote func unregister_player(id):
+	players.erase(id)
+	emit_signal("player_list_changed")
+
+remote func pre_start_game(spawn_points):
+#change scene	
+	var world = load("res://world.tscn").instance()
+	get_tree().get_root().add_child(world)
+
+	get_tree().get_root().get_node("lobby").hide()
+	
+	var player_scene = load("res://player.tscn")
+	
+	for p in spawn_points:	
+		var spawn_pos = world.get_node("spawn_points/"+str(spawn_points[p])).get_pos()	
+		var player = player_scene.instance()
+			
+		player.set_name( str(p) ) #use unique ID as node name
+		player.set_pos(spawn_pos)
+		
+		
+		if (p == get_tree().get_network_unique_id() ):
+			# if node for this peer id, set master
+			player.set_network_mode( NETWORK_MODE_MASTER )
+			player.set_player_name( player_name )
+		else:
+			# otherwise set slave
+			player.set_network_mode( NETWORK_MODE_SLAVE )
+			player.set_player_name( players[p] )
+
+		world.get_node("players").add_child(player)
+		
+	#set up score
+	world.get_node("score").add_player(get_tree().get_network_unique_id(),player_name)
+	for pn in players:
+		world.get_node("score").add_player(pn,players[pn])
+	
+	if (not get_tree().is_network_server()):
+		rpc("ready_to_start", get_tree().get_network_unique_id() )
+	elif players.size()==0:
+		post_start_game()
+	
+	
+remote func post_start_game():
+	get_tree().set_pause(false) #unpause and unleash the game!
+	
+var players_ready = []
+
+remote func ready_to_start(id):
+	
+	assert( get_tree().is_network_server() )
+	
+	if (not id in players_ready):
+		players_ready.append(id)
+		
+	if (players_ready.size() == players.size()):
+		for p in players:
+			rpc( "post_start_game" )
+	
+		post_start_game()
+		
+func host_game( name ):
+	player_name=name
+	var host = NetworkedMultiplayerENet.new()
+	host.create_server(DEFAULT_PORT,4)
+	get_tree().set_network_peer(host)
+	
+func join_game(ip, name):
+	player_name=name
+	var host = NetworkedMultiplayerENet.new()
+	host.create_client(ip,DEFAULT_PORT)
+	get_tree().set_network_peer(host)
+
+func get_player_list():
+	return players.values()
+
+func get_player_name():
+	return player_name
+
+func begin_game():
+	assert ( get_tree().is_network_server() )
+	
+	#create a dictionary with peer id and respective spawn points, could be improved by randomizing
+	var spawn_points={}
+	spawn_points[1]=0 #server in spawn point 0
+	var spawn_point_idx = 1
+	for p in players:
+		spawn_points[p]=spawn_point_idx
+		spawn_point_idx+=1
+	#call to pre-start game with the spawn points
+	for p in players:
+		rpc( "pre_start_game", spawn_points )
+		
+	pre_start_game( spawn_points )
+
+func end_game():
+	if (has_node("/root/world")): # game is in progress
+		#end it
+		get_node("/root/world").queue_free()
+		
+	emit_signal("game_ended")
+	players.clear()
+	get_tree().set_network_peer( null ) #end networking
+
+func _ready():
+	get_tree().connect("network_peer_connected",self,"_player_connected")
+	get_tree().connect("network_peer_disconnected",self,"_player_disconnected")
+	get_tree().connect("connected_to_server",self,"_connected_ok")
+	get_tree().connect("connection_failed",self,"_connected_fail")
+	get_tree().connect("server_disconnected",self,"_server_disconnected")
+	
+
+

+ 85 - 0
networking/simple_multiplayer/lobby.gd

@@ -0,0 +1,85 @@
+
+extends Control
+
+const DEFAULT_PORT = 10567
+
+func _ready():
+	# Called every time the node is added to the scene.
+	# Initialization here
+	gamestate.connect("connection_failed",self,"_on_connection_failed")
+	gamestate.connect("connection_succeeded",self,"_on_connection_success")
+	gamestate.connect("player_list_changed",self,"refresh_lobby")
+	gamestate.connect("game_ended",self,"_on_game_ended")
+	gamestate.connect("game_error",self,"_on_game_error")
+	
+
+	pass
+
+
+func _on_host_pressed():
+	if (get_node("connect/name").get_text()==""):
+		get_node("connect/error_label").set_text("Invalid name!")
+		return
+	
+
+	get_node("connect").hide()	
+	get_node("players").show()
+	get_node("connect/error_label").set_text("")
+	
+	var name = get_node("connect/name").get_text()
+	gamestate.host_game( name )
+	refresh_lobby()
+
+
+func _on_join_pressed():
+	if (get_node("connect/name").get_text()==""):
+		get_node("connect/error_label").set_text("Invalid name!")
+		return
+
+	var ip = get_node("connect/ip").get_text()
+	if (not ip.is_valid_ip_address()):
+		get_node("connect/error_label").set_text("Invalid IPv4 Address!")
+		return
+
+	get_node("connect/error_label").set_text("")
+	get_node("connect/host").set_disabled(true)
+	get_node("connect/join").set_disabled(true)
+	get_node("players/start").set_disabled(true)
+	
+	var name = get_node("connect/name").get_text()
+	
+	gamestate.join_game(ip,name)
+	refresh_lobby()
+
+func _on_connection_success():
+	get_node("connect").hide()	
+	get_node("players").show()
+
+func _on_connection_failed():
+	get_node("connect/host").set_disabled(false)
+	get_node("connect/join").set_disabled(false)
+	get_node("connect/error_label").set_text("Connection Failed")
+
+func _on_game_ended():
+	show()
+	get_node("connect").show()	
+	get_node("players").hide()
+	get_node("connect/host").set_disabled(false)
+	get_node("connect/join").set_disabled(false)
+	
+func _on_game_error(errtxt):
+	get_node("error").set_text(errtxt)
+	get_node("error").popup_centered_minsize()
+
+func refresh_lobby():
+	var players = gamestate.get_player_list()
+	players.sort()
+	get_node("players/list").clear()
+	get_node("players/list").add_item(gamestate.get_player_name()+" (You)")
+	for p in players:
+		get_node("players/list").add_item(p)
+		
+	get_node("players/start").set_disabled( not get_tree().is_network_server() )
+
+func _on_start_pressed():
+	gamestate.begin_game()

+ 215 - 0
networking/simple_multiplayer/lobby.tscn

@@ -0,0 +1,215 @@
+[gd_scene load_steps=2 format=1]
+
+[ext_resource path="res://lobby.gd" type="Script" id=1]
+
+[node name="lobby" type="Control"]
+
+anchor/right = 1
+anchor/bottom = 1
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 0.0
+margin/top = 0.0
+margin/right = 0.0
+margin/bottom = 0.0
+script/script = ExtResource( 1 )
+
+[node name="players" type="Panel" parent="."]
+
+visibility/visible = false
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 393.0
+margin/top = 113.0
+margin/right = 645.0
+margin/bottom = 468.0
+
+[node name="label" type="Label" parent="players"]
+
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 26.0
+margin/top = 18.0
+margin/right = 142.0
+margin/bottom = 32.0
+text = "Awaiting Players..."
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[node name="start" type="Button" parent="players"]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 68.0
+margin/top = 307.0
+margin/right = 193.0
+margin/bottom = 336.0
+toggle_mode = false
+enabled_focus_mode = 2
+shortcut = null
+text = "START!"
+flat = false
+
+[node name="list" type="ItemList" parent="players"]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 25.0
+margin/top = 37.0
+margin/right = 229.0
+margin/bottom = 296.0
+
+[node name="connect" type="Panel" parent="."]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 370.0
+margin/top = 157.0
+margin/right = 647.0
+margin/bottom = 324.0
+
+[node name="name_label" type="Label" parent="connect"]
+
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 14.0
+margin/top = 11.0
+margin/right = 56.0
+margin/bottom = 25.0
+text = "Name:"
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[node name="name" type="LineEdit" parent="connect"]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 17.0
+margin/top = 30.0
+margin/right = 173.0
+margin/bottom = 54.0
+text = "The Warrior"
+placeholder/alpha = 0.6
+focus_mode = 2
+caret/caret_blink = false
+caret/caret_blink_speed = 0.65
+
+[node name="ip_label" type="Label" parent="connect"]
+
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 15.0
+margin/top = 66.0
+margin/right = 57.0
+margin/bottom = 80.0
+text = "IP:"
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[node name="ip" type="LineEdit" parent="connect"]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 17.0
+margin/top = 85.0
+margin/right = 173.0
+margin/bottom = 109.0
+text = "127.0.0.1"
+placeholder/alpha = 0.6
+focus_mode = 2
+caret/caret_blink = false
+caret/caret_blink_speed = 0.65
+
+[node name="host" type="Button" parent="connect"]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 181.0
+margin/top = 31.0
+margin/right = 246.0
+margin/bottom = 51.0
+toggle_mode = false
+enabled_focus_mode = 2
+shortcut = null
+text = "Host"
+flat = false
+
+[node name="join" type="Button" parent="connect"]
+
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 181.0
+margin/top = 87.0
+margin/right = 246.0
+margin/bottom = 107.0
+toggle_mode = false
+enabled_focus_mode = 2
+shortcut = null
+text = "Join"
+flat = false
+
+[node name="error_label" type="Label" parent="connect"]
+
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = 15.0
+margin/top = 125.0
+margin/right = 257.0
+margin/bottom = 139.0
+custom_colors/font_color = Color( 0.820312, 0.291595, 0.291595, 1 )
+align = 1
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+[node name="error" type="AcceptDialog" parent="."]
+
+visibility/visible = false
+focus/ignore_mouse = false
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 2
+margin/left = 0.0
+margin/top = 0.0
+margin/right = 55.0
+margin/bottom = 58.0
+popup/exclusive = false
+window/title = "Alert!"
+dialog/hide_on_ok = true
+
+[connection signal="pressed" from="players/start" to="." method="_on_start_pressed"]
+
+[connection signal="pressed" from="connect/host" to="." method="_on_host_pressed"]
+
+[connection signal="pressed" from="connect/join" to="." method="_on_join_pressed"]
+
+

BIN
networking/simple_multiplayer/montserrat.otf


+ 103 - 0
networking/simple_multiplayer/player.gd

@@ -0,0 +1,103 @@
+
+extends KinematicBody2D
+
+const MOTION_SPEED = 90.0
+
+slave var slave_pos = Vector2()
+slave var slave_motion = Vector2()
+
+export var stunned=false
+
+#use sync because it will be called everywhere
+sync func setup_bomb(name,pos,by_who):
+	var bomb = preload("res://bomb.tscn").instance()
+	bomb.set_name( name ) #ensure unique name for the bomb
+	bomb.set_pos( pos )	
+	bomb.owner=by_who
+	get_node("../..").add_child(bomb)
+	
+var current_anim=""
+var prev_bombing=false
+var bomb_index=0
+
+func _fixed_process(delta):
+	
+	var motion = Vector2()
+	
+	if ( is_network_master() ):
+		
+		if (Input.is_action_pressed("move_left")):
+			motion+=Vector2(-1, 0)
+		if (Input.is_action_pressed("move_right")):
+			motion+=Vector2( 1, 0)
+		if (Input.is_action_pressed("move_up")):
+			motion+=Vector2( 0,-1)
+		if (Input.is_action_pressed("move_down")):
+			motion+=Vector2( 0, 1)
+
+		var bombing = Input.is_action_pressed("set_bomb")
+		
+		if (stunned):
+			bombing=false
+			motion=Vector2()
+
+		if (bombing and not prev_bombing):
+			var bomb_name = get_name() + str(bomb_index)
+			var bomb_pos = get_pos()
+			rpc("setup_bomb",bomb_name, bomb_pos, get_tree().get_network_unique_id() )
+				
+		prev_bombing=bombing			
+		motion*=delta
+		
+			
+		rset("slave_motion",motion)
+		rset("slave_pos",get_pos())
+	else:
+		set_pos(slave_pos)
+		motion = slave_motion
+	
+	var new_anim="standing"
+	if (motion.y<0):
+		new_anim="walk_up"
+	elif (motion.y>0):
+		new_anim="walk_down"
+	elif (motion.x<0):
+		new_anim="walk_left"
+	elif (motion.x>0):
+		new_anim="walk_right"
+		
+	if (stunned):
+		new_anim="stunned"
+		
+	if (new_anim!=current_anim):
+		current_anim=new_anim
+		get_node("anim").play(current_anim)
+		
+	
+	var remainder = move( motion * MOTION_SPEED )
+	
+	if (is_colliding()):
+		#slide through walls
+		move( get_collision_normal().slide( remainder ) )
+	
+	if ( not is_network_master() ):
+		slave_pos = get_pos() # to avoid jitter
+	
+slave func stun():
+	stunned=true
+	
+master func exploded(by_who):
+	if (stunned):
+		return
+	stun()
+	rpc("stun")
+func set_player_name(name):
+	get_node("Label").set_text(name)
+
+func _ready():
+	stunned=false
+	slave_pos=get_pos()
+	set_fixed_process(true)
+	pass
+
+

+ 158 - 0
networking/simple_multiplayer/player.tscn

@@ -0,0 +1,158 @@
+[gd_scene load_steps=12 format=1]
+
+[ext_resource path="res://player.gd" type="Script" id=1]
+[ext_resource path="res://charwalk.png" type="Texture" id=2]
+[ext_resource path="res://montserrat.otf" type="DynamicFontData" id=3]
+
+[sub_resource type="CircleShape2D" id=1]
+
+custom_solver_bias = 0.0
+radius = 20.0
+
+[sub_resource type="Animation" id=2]
+
+resource/name = "standing"
+length = 0.8
+loop = true
+step = 0.1
+tracks/0/type = "value"
+tracks/0/path = NodePath("Sprite:frame")
+tracks/0/interp = 1
+tracks/0/imported = false
+tracks/0/keys = { "times":FloatArray( 0, 0.2, 0.4, 0.6 ), "transitions":FloatArray( 1, 1, 1, 1 ), "update":1, "values":[ 0, 4, 8, 12 ] }
+
+[sub_resource type="Animation" id=3]
+
+resource/name = "stunned"
+length = 1.2
+loop = false
+step = 0.1
+tracks/0/type = "value"
+tracks/0/path = NodePath("Sprite:frame")
+tracks/0/interp = 1
+tracks/0/imported = false
+tracks/0/keys = { "times":FloatArray( 0 ), "transitions":FloatArray( 1 ), "update":1, "values":[ 0 ] }
+tracks/1/type = "value"
+tracks/1/path = NodePath("Sprite:transform/rot")
+tracks/1/interp = 1
+tracks/1/imported = false
+tracks/1/keys = { "times":FloatArray( 0, 1, 1.1 ), "transitions":FloatArray( 1, 0, 1 ), "update":0, "values":[ 0.0, 720.0, 0.0 ] }
+tracks/2/type = "value"
+tracks/2/path = NodePath(".:stunned")
+tracks/2/interp = 1
+tracks/2/imported = false
+tracks/2/keys = { "times":FloatArray( 1 ), "transitions":FloatArray( 1 ), "update":1, "values":[ false ] }
+
+[sub_resource type="Animation" id=4]
+
+length = 0.8
+loop = true
+step = 0.1
+tracks/0/type = "value"
+tracks/0/path = NodePath("Sprite:frame")
+tracks/0/interp = 1
+tracks/0/imported = false
+tracks/0/keys = { "times":FloatArray( 0, 0.2, 0.4, 0.6 ), "transitions":FloatArray( 1, 1, 1, 1 ), "update":1, "values":[ 0, 4, 8, 12 ] }
+
+[sub_resource type="Animation" id=5]
+
+length = 0.8
+loop = true
+step = 0.2
+tracks/0/type = "value"
+tracks/0/path = NodePath("Sprite:frame")
+tracks/0/interp = 1
+tracks/0/imported = false
+tracks/0/keys = { "times":FloatArray( 0, 0.2, 0.4, 0.6 ), "transitions":FloatArray( 1, 1, 1, 1 ), "update":1, "values":[ 1, 5, 9, 13 ] }
+
+[sub_resource type="Animation" id=6]
+
+length = 0.8
+loop = true
+step = 0.2
+tracks/0/type = "value"
+tracks/0/path = NodePath("Sprite:frame")
+tracks/0/interp = 1
+tracks/0/imported = false
+tracks/0/keys = { "times":FloatArray( 0, 0.2, 0.4, 0.6 ), "transitions":FloatArray( 1, 1, 1, 1 ), "update":1, "values":[ 3, 7, 11, 15 ] }
+
+[sub_resource type="Animation" id=7]
+
+length = 0.8
+loop = true
+step = 0.2
+tracks/0/type = "value"
+tracks/0/path = NodePath("Sprite:frame")
+tracks/0/interp = 1
+tracks/0/imported = false
+tracks/0/keys = { "times":FloatArray( 0, 0.2, 0.4, 0.6 ), "transitions":FloatArray( 1, 1, 1, 1 ), "update":1, "values":[ 2, 6, 10, 14 ] }
+
+[sub_resource type="DynamicFont" id=8]
+
+font/size = 14
+font/use_mipmaps = false
+font/use_filter = false
+font/font = ExtResource( 3 )
+
+[node name="player" type="KinematicBody2D"]
+
+input/pickable = false
+shapes/0/shape = SubResource( 1 )
+shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 )
+shapes/0/trigger = false
+collision/layers = 1
+collision/mask = 1
+collision/margin = 0.08
+script/script = ExtResource( 1 )
+stunned = false
+
+[node name="Sprite" type="Sprite" parent="."]
+
+transform/pos = Vector2( 0.0750351, 6.23615 )
+texture = ExtResource( 2 )
+offset = Vector2( -0.0750351, -6.23615 )
+vframes = 4
+hframes = 4
+
+[node name="shape" type="CollisionShape2D" parent="."]
+
+shape = SubResource( 1 )
+trigger = false
+_update_shape_index = 0
+
+[node name="anim" type="AnimationPlayer" parent="."]
+
+playback/process_mode = 1
+playback/default_blend_time = 0.0
+root/root = NodePath("..")
+anims/standing = SubResource( 2 )
+anims/stunned = SubResource( 3 )
+anims/walk_down = SubResource( 4 )
+anims/walk_left = SubResource( 5 )
+anims/walk_right = SubResource( 6 )
+anims/walk_up = SubResource( 7 )
+next/walk_down = ""
+playback/active = true
+playback/speed = 1.0
+blend_times = [  ]
+autoplay = ""
+
+[node name="Label" type="Label" parent="."]
+
+visibility/opacity = 0.7
+focus/ignore_mouse = true
+focus/stop_mouse = true
+size_flags/horizontal = 2
+size_flags/vertical = 0
+margin/left = -82.0
+margin/top = -35.0
+margin/right = 85.0
+margin/bottom = -14.0
+custom_fonts/font = SubResource( 8 )
+text = "Player 1"
+align = 1
+percent_visible = 1.0
+lines_skipped = 0
+max_lines_visible = -1
+
+

+ 14 - 0
networking/simple_multiplayer/rock.gd

@@ -0,0 +1,14 @@
+
+extends KinematicBody2D
+
+#sent to everyone else
+slave func do_explosion():
+	get_node("anim").play("explode")
+
+#received by owner of the rock
+master func exploded(by_who):
+	rpc("do_explosion") #re-sent to slave rocks
+	get_node("../../score").rpc("increase_score",by_who)
+	do_explosion()
+	
+	

+ 101 - 0
networking/simple_multiplayer/rock.tscn

@@ -0,0 +1,101 @@
+[gd_scene load_steps=7 format=1]
+
+[ext_resource path="res://rock.gd" type="Script" id=1]
+[ext_resource path="res://brickfloor.png" type="Texture" id=2]
+[ext_resource path="res://rock_bit.png" type="Texture" id=3]
+
+[sub_resource type="RectangleShape2D" id=1]
+
+custom_solver_bias = 0.0
+extents = Vector2( 24, 24 )
+
+[sub_resource type="ColorRamp" id=2]
+
+offsets = FloatArray( 0, 0.860465, 1 )
+colors = ColorArray( 1, 1, 1, 1, 0.860465, 0.860465, 0.860465, 1, 1, 1, 1, 0 )
+
+[sub_resource type="Animation" id=3]
+
+resource/name = "explode"
+length = 1.0
+loop = false
+step = 0.1
+tracks/0/type = "value"
+tracks/0/path = NodePath("explosion:config/emitting")
+tracks/0/interp = 1
+tracks/0/imported = false
+tracks/0/keys = { "times":FloatArray( 0 ), "transitions":FloatArray( 1 ), "update":1, "values":[ true ] }
+tracks/1/type = "method"
+tracks/1/path = NodePath(".")
+tracks/1/interp = 1
+tracks/1/imported = false
+tracks/1/keys = { "times":FloatArray( 1 ), "transitions":FloatArray( 1 ), "values":[ { "args":[  ], "method":"queue_free" } ] }
+tracks/2/type = "value"
+tracks/2/path = NodePath("sprite:visibility/visible")
+tracks/2/interp = 1
+tracks/2/imported = false
+tracks/2/keys = { "times":FloatArray( 0 ), "transitions":FloatArray( 1 ), "update":1, "values":[ false ] }
+
+[node name="rock" type="KinematicBody2D"]
+
+input/pickable = false
+shapes/0/shape = SubResource( 1 )
+shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 )
+shapes/0/trigger = false
+collision/layers = 1
+collision/mask = 1
+collision/margin = 0.001
+script/script = ExtResource( 1 )
+
+[node name="sprite" type="Sprite" parent="."]
+
+texture = ExtResource( 2 )
+region = true
+region_rect = Rect2( 96, 0, 48, 48 )
+
+[node name="shape" type="CollisionShape2D" parent="."]
+
+shape = SubResource( 1 )
+trigger = false
+_update_shape_index = 0
+
+[node name="explosion" type="Particles2D" parent="."]
+
+config/amount = 32
+config/lifetime = 0.8
+config/emit_timeout = 0.8
+config/emitting = false
+config/half_extents = Vector2( 15, 15 )
+config/explosiveness = 0.1
+config/texture = ExtResource( 3 )
+params/direction = 0.0
+params/spread = 180.0
+params/linear_velocity = 100.0
+params/spin_velocity = 4.0
+params/orbit_velocity = 0.0
+params/gravity_direction = 0.0
+params/gravity_strength = 90.0
+params/radial_accel = 0.0
+params/tangential_accel = 0.0
+params/damping = 0.0
+params/initial_angle = 0.0
+params/initial_size = 2.0
+params/final_size = 2.0
+params/hue_variation = 0.0
+params/anim_speed_scale = 1.0
+params/anim_initial_pos = 0.0
+randomness/spin_velocity = 0.95
+color/color_ramp = SubResource( 2 )
+
+[node name="anim" type="AnimationPlayer" parent="."]
+
+playback/process_mode = 1
+playback/default_blend_time = 0.0
+root/root = NodePath("..")
+anims/explode = SubResource( 3 )
+playback/active = true
+playback/speed = 1.0
+blend_times = [  ]
+autoplay = ""
+
+

BIN
networking/simple_multiplayer/rock_bit.png


+ 56 - 0
networking/simple_multiplayer/score.gd

@@ -0,0 +1,56 @@
+
+extends HBoxContainer
+
+# member variables here, example:
+# var a=2
+# var b="textvar"
+
+var player_labels={}
+
+func _process(delta):
+	
+	var rocks_left = get_node("../rocks").get_child_count()
+	if (rocks_left==0):
+		var winner_name = ""
+		var winner_score = 0
+		for p in player_labels:
+			if (player_labels[p].score > winner_score):
+				winner_score=player_labels[p].score
+				winner_name=player_labels[p].name
+				
+		get_node("../winner").set_text("THE WINNER IS:\n"+winner_name)
+		get_node("../winner").show()
+		
+	
+
+
+sync func increase_score(for_who):
+	assert( for_who in player_labels )
+	var pl = player_labels[for_who]
+	pl.score+=1
+	pl.label.set_text( pl.name+"\n"+str(pl.score) )
+
+	
+func add_player(id,name):
+	var l = Label.new()
+	l.set_align(Label.ALIGN_CENTER)
+	l.set_text(name+"\n"+"0")
+	l.set_h_size_flags(SIZE_EXPAND_FILL)
+	var font = DynamicFont.new()
+	font.set_size(18)
+	font.set_font_data( preload("res://montserrat.otf" ) )
+	l.add_font_override("font",font)
+	
+	add_child(l)
+	
+	player_labels[id]={ name=name, label=l, score=0 }
+	
+func _ready():
+	get_node("../winner").hide()
+	set_process(true)
+
+
+
+func _on_exit_game_pressed():
+	gamestate.end_game()
+

+ 45 - 0
networking/simple_multiplayer/tile_scene.tscn

@@ -0,0 +1,45 @@
+[gd_scene load_steps=3 format=1]
+
+[ext_resource path="res://brickfloor.png" type="Texture" id=1]
+
+[sub_resource type="RectangleShape2D" id=1]
+
+custom_solver_bias = 0.0
+extents = Vector2( 24, 24 )
+
+[node name="Node2D" type="Node2D"]
+
+[node name="wall" type="Sprite" parent="."]
+
+transform/pos = Vector2( 24, 24 )
+texture = ExtResource( 1 )
+region = true
+region_rect = Rect2( 0, 0, 48, 48 )
+
+[node name="col" type="StaticBody2D" parent="wall"]
+
+input/pickable = false
+shapes/0/shape = SubResource( 1 )
+shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 )
+shapes/0/trigger = false
+collision/layers = 1
+collision/mask = 1
+constant_linear_velocity = Vector2( 0, 0 )
+constant_angular_velocity = 0.0
+friction = 1.0
+bounce = 0.0
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="wall/col"]
+
+shape = SubResource( 1 )
+trigger = false
+_update_shape_index = 0
+
+[node name="floor" type="Sprite" parent="."]
+
+transform/pos = Vector2( 72, 24 )
+texture = ExtResource( 1 )
+region = true
+region_rect = Rect2( 48, 0, 48, 48 )
+
+

+ 28 - 0
networking/simple_multiplayer/tileset.tres

@@ -0,0 +1,28 @@
+[gd_resource type="TileSet" load_steps=3 format=1]
+
+[ext_resource path="res://brickfloor.png" type="Texture" id=1]
+
+[sub_resource type="RectangleShape2D" id=1]
+
+custom_solver_bias = 0.0
+extents = Vector2( 24, 24 )
+
+[resource]
+
+0/name = "wall"
+0/texture = ExtResource( 1 )
+0/tex_offset = Vector2( 0, 0 )
+0/region = Rect2( 0, 0, 48, 48 )
+0/occluder_offset = Vector2( 24, 24 )
+0/navigation_offset = Vector2( 24, 24 )
+0/shape_offset = Vector2( 24, 24 )
+0/shapes = [ SubResource( 1 ) ]
+1/name = "floor"
+1/texture = ExtResource( 1 )
+1/tex_offset = Vector2( 0, 0 )
+1/region = Rect2( 48, 0, 48, 48 )
+1/occluder_offset = Vector2( 24, 24 )
+1/navigation_offset = Vector2( 24, 24 )
+1/shape_offset = Vector2( 0, 0 )
+1/shapes = [  ]
+

File diff suppressed because it is too large
+ 32 - 0
networking/simple_multiplayer/world.tscn


+ 0 - 0
misc/udp_chat/chat.gd → networking/udp_chat/chat.gd


+ 0 - 0
misc/udp_chat/chat.scn → networking/udp_chat/chat.scn


+ 0 - 0
misc/udp_chat/engine.cfg → networking/udp_chat/engine.cfg


+ 0 - 0
misc/udp_chat/icon.png → networking/udp_chat/icon.png


Some files were not shown because too many files changed in this diff