mirror of
				https://github.com/JohnBreaux/Boat-Battle.git
				synced 2025-02-04 12:28:35 +00:00 
			
		
		
		
	Ship Battle: Draw the rest of the freaking owl
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| [gd_scene load_steps=3 format=2] | ||||
|  | ||||
| [ext_resource path="res://script/game/Gameplay/Board.gd" type="Script" id=1] | ||||
| [ext_resource path="res://assets/game/board_dark.png" type="Texture" id=2] | ||||
| [ext_resource path="res://assets/game/board_blue.png" type="Texture" id=2] | ||||
|  | ||||
| [node name="Board" type="Node2D"] | ||||
| position = Vector2( 36, 36 ) | ||||
|   | ||||
| @@ -29,7 +29,7 @@ margin_right = 88.0 | ||||
| margin_bottom = 20.0 | ||||
| text = "Forfeit" | ||||
|  | ||||
| [node name="ConfirmationDialog" type="ConfirmationDialog" parent="."] | ||||
| [node name="Forfeit Confirmation" type="ConfirmationDialog" parent="."] | ||||
| anchor_left = 0.5 | ||||
| anchor_top = 0.5 | ||||
| anchor_right = 0.5 | ||||
| @@ -54,6 +54,13 @@ margin_right = 43.4668 | ||||
| margin_bottom = 26.1478 | ||||
| text = "Win" | ||||
|  | ||||
| [node name="Connection Error" type="AcceptDialog" parent="."] | ||||
| margin_right = 230.0 | ||||
| margin_bottom = 58.0 | ||||
| window_title = "Connection Error" | ||||
| dialog_text = "Host disconnected unexpectedly." | ||||
|  | ||||
| [connection signal="pressed" from="Buttons/Forfeit" to="." method="_on_Forfeit_pressed"] | ||||
| [connection signal="confirmed" from="ConfirmationDialog" to="." method="_on_ConfirmationDialog_confirmed"] | ||||
| [connection signal="confirmed" from="Forfeit Confirmation" to="." method="_on_Forfeit_Confirmation_confirmed"] | ||||
| [connection signal="button_down" from="Button" to="." method="_on_Button_button_down"] | ||||
| [connection signal="confirmed" from="Connection Error" to="." method="_on_Connection_Error_confirmed"] | ||||
|   | ||||
| @@ -10,25 +10,12 @@ font_data = ExtResource( 2 ) | ||||
| [node name="Victory" type="Control"] | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| mouse_filter = 2 | ||||
| script = ExtResource( 1 ) | ||||
| __meta__ = { | ||||
| "_edit_use_anchors_": false | ||||
| } | ||||
|  | ||||
| [node name="Label" type="Label" parent="."] | ||||
| margin_left = 118.32 | ||||
| margin_top = 44.5109 | ||||
| margin_right = 260.32 | ||||
| margin_bottom = 84.5109 | ||||
| size_flags_vertical = 0 | ||||
| custom_fonts/font = SubResource( 1 ) | ||||
| text = "Victory" | ||||
| align = 1 | ||||
| valign = 1 | ||||
| __meta__ = { | ||||
| "_edit_use_anchors_": false | ||||
| } | ||||
|  | ||||
| [node name="exit_to_main" type="Button" parent="."] | ||||
| margin_left = 541.0 | ||||
| margin_top = 327.85 | ||||
| @@ -47,6 +34,20 @@ margin_right = 63.2202 | ||||
| margin_bottom = 357.41 | ||||
| text = "Restart" | ||||
|  | ||||
| [node name="Victory" type="Label" parent="."] | ||||
| margin_left = 380.0 | ||||
| margin_top = 44.5109 | ||||
| margin_right = 260.32 | ||||
| margin_bottom = 84.5109 | ||||
| size_flags_vertical = 0 | ||||
| custom_fonts/font = SubResource( 1 ) | ||||
| text = "Victory" | ||||
| align = 1 | ||||
| valign = 1 | ||||
| __meta__ = { | ||||
| "_edit_use_anchors_": false | ||||
| } | ||||
|  | ||||
| [connection signal="button_down" from="exit_to_main" to="." method="_on_exit_to_main_button_down"] | ||||
| [connection signal="pressed" from="exit_to_main" to="." method="_on_Button_pressed"] | ||||
| [connection signal="button_down" from="Button2" to="." method="_on_restart_button_down"] | ||||
|   | ||||
| @@ -93,9 +93,19 @@ text = "Connect to Game" | ||||
|  | ||||
| [node name="Connected Options" type="VBoxContainer" parent="Lobby Options"] | ||||
| visible = false | ||||
| margin_top = 84.0 | ||||
| margin_top = 36.0 | ||||
| margin_right = 123.0 | ||||
| margin_bottom = 104.0 | ||||
| margin_bottom = 56.0 | ||||
|  | ||||
| [node name="Host Options" type="VBoxContainer" parent="Lobby Options/Connected Options"] | ||||
| visible = false | ||||
| margin_right = 123.0 | ||||
| margin_bottom = 20.0 | ||||
|  | ||||
| [node name="Start Game" type="Button" parent="Lobby Options/Connected Options/Host Options"] | ||||
| margin_right = 123.0 | ||||
| margin_bottom = 20.0 | ||||
| text = "Start Game" | ||||
|  | ||||
| [node name="Disconnect Button" type="Button" parent="Lobby Options/Connected Options"] | ||||
| margin_right = 123.0 | ||||
| @@ -162,6 +172,7 @@ text = "127.0.0.1" | ||||
|  | ||||
| [connection signal="pressed" from="Lobby Options/Host or Connect/Host Button" to="." method="_on_Host_Button_pressed"] | ||||
| [connection signal="pressed" from="Lobby Options/Host or Connect/Connect Button" to="." method="_on_Connect_Button_pressed"] | ||||
| [connection signal="pressed" from="Lobby Options/Connected Options/Host Options/Start Game" to="." method="_on_Start_Game_pressed"] | ||||
| [connection signal="pressed" from="Lobby Options/Connected Options/Disconnect Button" to="." method="_on_Disconnect_Button_pressed"] | ||||
| [connection signal="pressed" from="Lobby Options/Change Name Button" to="." method="_on_Change_Name_Button_pressed"] | ||||
| [connection signal="pressed" from="Lobby Options/Exit Lobby Button" to="." method="_on_Exit_Lobby_pressed"] | ||||
|   | ||||
| @@ -60,7 +60,7 @@ func _on_scene_start(scene): | ||||
| 	var instance | ||||
| 	#print ("_on_scene_start(",scene,")") | ||||
| 	match scene: | ||||
| 		"Singleplayer":  | ||||
| 		"Gameplay":  | ||||
| 			instance = Game.instance() | ||||
| 			add_child (instance) | ||||
| 			return true | ||||
|   | ||||
| @@ -10,6 +10,7 @@ enum  {MISS = -1, READY = 0, HIT = 1, SUNK = 2, LOST = 3} | ||||
| var bottom_board:Array # Player board | ||||
| var top_board:Array    # Opponent board | ||||
| var ships = []         # list of Ships | ||||
| var ship_data = []     # Data used to generate ships | ||||
| var ship_count = 0     # number of 'active' (un-sunk) ships | ||||
|  | ||||
| # a board is square. This is its side length | ||||
| @@ -33,50 +34,55 @@ func hit(pos): | ||||
| 	var res = MISS | ||||
| 	# Get the ship-metadata for that location | ||||
| 	var ship = bottom_board[pos.x][pos.y] | ||||
| 	# If there's a ship there, which exists, and hasn't been hit, | ||||
| 	if ship and ship[0] > NO_SHIP and ship[1] == READY: | ||||
| 		# Hit the ship, and store whether HIT or SUNK | ||||
| 	# If the ship's already been hit here, don't bother beating it again | ||||
| 	if ship[1] != READY: | ||||
| 		return ship[1] | ||||
| 	if ship[0] > NO_SHIP: | ||||
| 		# Decide whether HIT or SUNK | ||||
| 		res = ships[ship[0]].hit(pos) | ||||
| 		# TODO: display KABOOM | ||||
| 		# Update the ship | ||||
| 		ships[ship[0]].update() | ||||
| 		# Mark the ship as hit | ||||
| 		ship[1] = HIT | ||||
| 	else: | ||||
| 		# Mark that position as a miss, with no ship | ||||
| 		bottom_board[pos.x][pos.y] = [NO_SHIP, MISS] | ||||
| 	# If ship sunk, | ||||
| 	if res == SUNK: | ||||
| 		# remove it from the count | ||||
| 		ship_count -= 1 | ||||
| 	# If no ships left, | ||||
| 	# If we have no more ships left, we LOST | ||||
| 	if ship_count == 0: | ||||
| 		# Game has been lost | ||||
| 		res = LOST; | ||||
| 		res = LOST | ||||
| 	# Record the result on the board, and return it | ||||
| 	ship[1] = res | ||||
| 	return res | ||||
|  | ||||
| # fire: Store the results of firing on an opponent | ||||
| #   pos: board position fired on | ||||
| #   res: result of firing on the opponent | ||||
| func fire(pos, res): | ||||
| 	if top_board[pos.x][pos.y] == null: | ||||
| 	if top_board[pos.x][pos.y] == READY: | ||||
| 		top_board[pos.x][pos.y] = res | ||||
| 		return true | ||||
| 	return false | ||||
| 		return res | ||||
| 	else: | ||||
| 		return top_board[pos.x][pos.y] | ||||
|  | ||||
| # Place a ship on the board at board-space coordinates | ||||
| func place_ship(in_position, in_size, in_orientation, in_variant = 0): | ||||
| 	# Save the ship data | ||||
| 	ship_data.append([in_position, in_size, in_orientation]) | ||||
| 	# Create a new Ship, and give it some data | ||||
| 	var ship = Ship.instance() | ||||
| 	ship._init(in_position, in_size, in_orientation, in_variant) | ||||
| 	# Mark the ship on the board | ||||
| 	for pos in ship.get_extent(): | ||||
| 		bottom_board[pos.x][pos.y] = [ships.size(), READY] | ||||
| 	# Add the ship to the ships array, and keep count | ||||
| 	ships.append(ship) | ||||
| 	ship_count += 1 | ||||
| 	# Add the ship to the scene tree | ||||
| 	add_child(ship) | ||||
|  | ||||
| # Not sure why this is necessary yet | ||||
| func get_bottom_board(): | ||||
| 	return bottom_board | ||||
|  | ||||
| func query_bottom(pos): | ||||
| 	return bottom_board[pos.x][pos.y] | ||||
|  | ||||
| func query_top(pos): | ||||
| 	return top_board[pos.x][pos.y] | ||||
|  | ||||
| # Get the number of live ships | ||||
| func get_ship_count(): | ||||
| @@ -84,16 +90,16 @@ func get_ship_count(): | ||||
|  | ||||
| # _init: Constructor | ||||
| func _init(): | ||||
| 	# Initialize the bottom_board to a 10x10 array | ||||
| 	for _row in range(board_len): | ||||
| 	# Initialize the bottom_board to a len*len array | ||||
| 	for x in board_len: | ||||
| 		bottom_board.append([]) | ||||
| 	for column in bottom_board: | ||||
| 		column.resize(10) | ||||
| 	# Initialize the top_board to a 10x10 array | ||||
| 	for _row in range(board_len): | ||||
| 		for y in board_len: | ||||
| 			bottom_board[x].append([NO_SHIP, READY]) | ||||
| 	# Initialize the top_board to a len*len array | ||||
| 	for x in board_len: | ||||
| 		top_board.append([]) | ||||
| 	for column in top_board: | ||||
| 		column.resize(board_len) | ||||
| 		for y in board_len: | ||||
| 			top_board[x].append(READY) | ||||
|  | ||||
| #   worldspace_to_boardspace: convert a Vector2 in world-space to board-space | ||||
| func worldspace_to_boardspace(coordinate:Vector2): | ||||
|   | ||||
| @@ -1,19 +1,20 @@ | ||||
| extends Control | ||||
|  | ||||
| # Signal to pass the fire location back to parent | ||||
| signal fire_at | ||||
|  | ||||
| var atlas = preload("res://assets/game/HitMissAtlas.png") | ||||
| var sprites = [] | ||||
| var hits = [] | ||||
| var hits | ||||
|  | ||||
| # Called when the node enters the scene tree for the first time. | ||||
| func _ready(): | ||||
| 	print("Fire: _ready()") | ||||
| 	for x in 10: | ||||
| 		for y in 10: | ||||
| 			texture(Vector2(x,y)) | ||||
| 	pass # Replace with function body. | ||||
|  | ||||
| # Signal to pass the fire location back to yet-unknown nodes | ||||
| signal fire_at | ||||
|  | ||||
| func _init(topBoard): | ||||
| 	hits = topBoard | ||||
|  | ||||
| func _on_Fire_pressed(): | ||||
| 	var crosshair = get_node("Crosshair") | ||||
| 	# Check if the crosshair is in a valid position | ||||
| @@ -21,10 +22,7 @@ func _on_Fire_pressed(): | ||||
| 		var crosshair_pos = crosshair.world_to_board_space(crosshair.position) | ||||
| 		if(hits[crosshair_pos.x][crosshair_pos.y] == 0): | ||||
| 			# fires at position | ||||
| 			print("Fire at position: ", crosshair_pos) | ||||
| 			emit_signal("fire_at", crosshair_pos) | ||||
| 			# Close the Firing menu | ||||
| 			queue_free() | ||||
| 			return | ||||
| 	#if invalid position popup appears | ||||
| 	var dialog = get_node("FireDialog") | ||||
| @@ -52,7 +50,4 @@ func texture(index): | ||||
| 		var sprite = Sprite.new() | ||||
| 		sprite.texture = t | ||||
| 		sprite.position = Vector2(index.x, index.y) * textureSize + OFFSET | ||||
| 		 | ||||
| 		printt(t.get_height(), t.get_width()) | ||||
| 		 | ||||
| 		$board_blue.add_child(sprite) | ||||
|   | ||||
| @@ -1,11 +1,18 @@ | ||||
| extends Control | ||||
|  | ||||
| # warning-ignore-all:unused_signal | ||||
| # warning-ignore-all:return_value_discarded | ||||
|  | ||||
| enum  {MISS = -1, READY = 0, HIT = 1, SUNK = 2, LOST = 3} | ||||
|  | ||||
| # Signals | ||||
| signal fire # fire(position) | ||||
| signal hit  # hit (state: see Miss/Ready/Hit/Sunk enum in Board.gd) | ||||
| signal win  # win (): sent when opponent player lost | ||||
| signal hit  # hit (state): see Miss/Ready/Hit/Sunk enum in Board.gd) | ||||
| signal miss | ||||
| signal loss | ||||
| signal forfeit | ||||
|  | ||||
| signal game_ready | ||||
|  | ||||
| # Path to Player class, for instantiating new Players in code | ||||
| var Player = preload("res://scenes/Game/Player.tscn") | ||||
| @@ -16,10 +23,6 @@ var Victory = preload("res://scenes/Game/Victory.tscn") | ||||
| # Array of instances of the Player class; stores the Players | ||||
| var player | ||||
| var players_ready = [] | ||||
| # turn counter | ||||
| var turn = 0 | ||||
| # winner | ||||
| var winner = 0 | ||||
|  | ||||
| # Every game is a multiplayer game, even the ones that aren't. | ||||
| # We're taking the Minecraft approach, baby | ||||
| @@ -28,129 +31,159 @@ var network_id | ||||
| # Called when the node enters the scene tree for the first time. | ||||
| func _ready(): | ||||
|  | ||||
| 	get_node("ConfirmationDialog").get_ok().text = "Yes" | ||||
| 	get_node("ConfirmationDialog").get_cancel().text = "No" | ||||
| 	get_node("ConfirmationDialog").get_ok().rect_min_size.x = 100 | ||||
| 	get_node("ConfirmationDialog").get_cancel().rect_min_size.x = 100 | ||||
| 	if multiplayer: | ||||
| 		# TODO: Spawn a lobby where people can either connect to a peer or create a server | ||||
| 	get_node("Forfeit Confirmation").get_ok().text = "Yes" | ||||
| 	get_node("Forfeit Confirmation").get_cancel().text = "No" | ||||
| 	get_node("Forfeit Confirmation").get_ok().rect_min_size.x = 100 | ||||
| 	get_node("Forfeit Confirmation").get_cancel().rect_min_size.x = 100 | ||||
|  | ||||
| 	if Net.connected: | ||||
| 		Net.connect("disconnected", self, "connection_error") | ||||
| 		Net.connect("incoming",     self, "_on_Net_incoming") | ||||
| 		pass | ||||
| 	game_setup() | ||||
|  | ||||
| # Function used to keep track of which players are ready | ||||
| # TODO: Change this to keep track of ready states only | ||||
| func player_ready(): | ||||
| 	players_ready.append(Net.get_network_id()) | ||||
| 	pass | ||||
| func player_ready(sender): | ||||
| 	print("player_ready(%s), %d" % [sender, players_ready.size()]) | ||||
| 	players_ready.append(sender) | ||||
| 	if (players_ready.size() >= Net.peer_info.size()): | ||||
| 		emit_signal("game_ready") | ||||
|  | ||||
| # Member functions: | ||||
| #   game_start: starts the game | ||||
| #   game_setup: starts the game | ||||
| sync func game_setup(): | ||||
| 	# If there's no server connected, create one | ||||
| 	if not Net.connected: | ||||
| 		# TODO: Create a fake peer who we can automate, for single-player mode | ||||
| 		Net.start_host() | ||||
| 	network_id = Net.get_network_id() | ||||
| 	player = Player.instance() | ||||
| 	player.connect("player_ready", self, "_on_player_ready") | ||||
| 	add_child(player) | ||||
| 	player.set_up_begin() | ||||
| 	yield(self, "game_ready") | ||||
| 	if Net.hosting: | ||||
| 		state_fire() | ||||
|  | ||||
| #   state_fire: The firing state. Displays fire menu, then notifies opponent. | ||||
| remote func state_fire(): | ||||
| 	var pos = player.turn_start() | ||||
| 	if pos is GDScriptFunctionState: | ||||
| 		pos = yield(pos, "completed") | ||||
| 	rpc("state_check", pos) | ||||
|  | ||||
| #   state_check: The checking state. Branches out to the other states. | ||||
| #     pos: Position which the opponent is trying to fire upon | ||||
| remote func state_check(pos): | ||||
| 	var res = player.hit(pos) | ||||
| 	# Tell the opponent | ||||
| 	Net.send(0, ["hit", res], Net.REPLY) | ||||
| 	rpc("play_hit_sound", res) | ||||
| 	match res: | ||||
| 		LOST: | ||||
| 			# the other player wins | ||||
| 			rpc("state_win", player.board.ship_data) | ||||
| 			victory_screen(null, false) | ||||
| 		SUNK, HIT: | ||||
| 			# Hit | ||||
| 			rpc("state_fire") | ||||
| 		MISS: | ||||
| 			# Our turn to fire | ||||
| 			state_fire() | ||||
| 	pass | ||||
|  | ||||
| #   game_start: Runs on host. Controls the game. | ||||
| func game_start(): | ||||
| 	var state = "P1_fire" | ||||
| 	# Make sure we're the server | ||||
| 	while true: | ||||
| 		match state: | ||||
| 			"P1_fire": | ||||
| 				# Tell local player to fire | ||||
|  | ||||
| 				# Wait for result | ||||
|  | ||||
| 				# Send fire REQUEST to P2 | ||||
| 				pass | ||||
| 			"P2_check": | ||||
| 				# Wait for hit | ||||
| 				var ret = yield(self, "hit") | ||||
| 				# Record the hit | ||||
|  | ||||
| 				# | ||||
| 				pass | ||||
| 			"P2_fire": | ||||
| 				pass | ||||
| 			"P1_check": | ||||
| 				# Check if | ||||
| 				pass | ||||
| 			"P1_win": | ||||
| 				pass | ||||
| 			"P2_win": | ||||
| 				pass | ||||
| #   state_win: The winning state. If you reach here, someone's won. | ||||
| #     ships: The opponent's ship data, so that their board can be shown | ||||
| remote func state_win(ships): | ||||
| 	victory_screen(ships) | ||||
| 	pass | ||||
|  | ||||
| func fire_on(id, pos:Vector2): | ||||
| 	# REQUEST fire on opponent | ||||
| 	Net.send(id, ["fire", pos], Net.REQUEST) | ||||
| 	# Wait for REPLY | ||||
| #   play_hit_sound: Play a hit sound depending on the severity of the hit | ||||
| #     value: Lost/Sunk/Hit/Miss | ||||
| sync func play_hit_sound(value): | ||||
| 	match value: | ||||
| 		LOST, SUNK: | ||||
| 			AudioBus.emit_signal("ship_sunk") | ||||
| 		HIT: | ||||
| 			AudioBus.emit_signal("ship_hit") | ||||
| 		MISS: | ||||
| 			AudioBus.emit_signal("ship_missed") | ||||
|  | ||||
| func return_hit(id, ship_status): | ||||
| #   hit: Update the local player's board when the opponent fires | ||||
| #     pos: Opponent's target | ||||
| func hit(pos): | ||||
| 	pos = Vector2(pos[0], pos[1]) | ||||
| 	var res = player.hit(pos) | ||||
| 	return res | ||||
|  | ||||
| 	Net.send(id, ["hit", ship_status], Net.REPLY) | ||||
|  | ||||
| func _on_win(): | ||||
| 	pass | ||||
| #   mark: Update the local player's hit/miss board when opponent replies | ||||
| func mark(res): | ||||
| 	return player.mark(res) | ||||
|  | ||||
| #   _on_Net_incoming: Handle mail. | ||||
| func _on_Net_incoming(mail): | ||||
| 	print ("mail: ", mail, mail.size()) | ||||
| 	if mail.size() == 3: | ||||
| 		var sender   = mail[0] | ||||
| 		print ("mail: ", mail, mail.size()) | ||||
| 		var sender   = int(mail[0]) | ||||
| 		var message  = mail[1] | ||||
| 		var mailtype = mail[2] | ||||
| 		var mailtype = int(mail[2]) | ||||
| 		printt(sender, message, mailtype) | ||||
| 		match mailtype: | ||||
| 			# if message is a REQUEST (to perform an action) | ||||
| 			Net.REQUEST: | ||||
| 				match message[0]: | ||||
| 					# Opponent asks for player.fire() | ||||
| 					"fire": | ||||
| 						emit_signal("fire", message[1]) | ||||
| 					# Opponent asks for hit(pos) | ||||
| 					"hit": | ||||
| 						pass | ||||
| 					_: | ||||
| 						pass | ||||
| 			Net.REPLY: | ||||
| 				print ("got REPLY") | ||||
| 					# message is a REPLY (return value) | ||||
| 				match message[0]: | ||||
| 					# on "fire": fire(result) | ||||
| 					"fire": | ||||
| 						emit_signal("hit", message[1]) | ||||
| 					# Return value of | ||||
| 						hit(message[1]) | ||||
| 					# on "hit": mark(state) | ||||
| 					"hit": | ||||
| 						mark(message[1]) | ||||
| 					"forfeit": | ||||
| 						pass | ||||
| 				pass | ||||
| 			Net.READY: | ||||
| 				print ("got READY") | ||||
| 				# Add player to the ready array | ||||
| 				players_ready.append(sender) | ||||
| 				pass | ||||
| 				player_ready(sender) | ||||
| 			_: | ||||
| 				print ("got ", mailtype) | ||||
|  | ||||
| #   _on_player_ready: Player Ready signal handler | ||||
| func _on_player_ready(): | ||||
| 	print ("_on_player_ready") | ||||
| 	Net.send(1, [], Net.READY) | ||||
| 	Net.send(0, [], Net.READY) | ||||
| 	player_ready(Net.get_network_id()) | ||||
|  | ||||
| #   victory_screen: display the victory screen | ||||
| func victory_screen(): | ||||
| 	# TODO: Create the victory screen, fill it with knowledge | ||||
| 	pass | ||||
|  | ||||
| #   display_turn: display which turn it is on the screen | ||||
| func display_turn(): | ||||
| 	# TODO: Update the turn display, if there is one? | ||||
| 	pass | ||||
| func victory_screen(ships, winner = true): | ||||
| 	if winner: | ||||
| 		# Hide the buttons | ||||
| 		get_node("Bittons").hide() | ||||
| 		# Create a new Victory screen | ||||
| 		var victory = Victory.instance() | ||||
| 		# Give it the ships received from the opponent | ||||
| 		victory.reveal_ships(ships) | ||||
| 		# Print a nice message to stdout | ||||
| 		print("You won!") | ||||
| 		# Add victory to the scene tree | ||||
| 		add_child(victory) | ||||
| 	else: | ||||
| 		end() | ||||
|  | ||||
| #   _on_Forfeit_pressed: Handle forfeit button press | ||||
| func _on_Forfeit_pressed(): | ||||
| 	AudioBus.emit_signal("button_clicked") | ||||
| 	get_node("ConfirmationDialog").popup() | ||||
| 	get_node("Forfeit Confirmation").popup_centered() | ||||
|  | ||||
| #   end: end the Game | ||||
| func end(): | ||||
| sync func end(): | ||||
| 	queue_free() | ||||
|  | ||||
|  | ||||
| func connection_error(): | ||||
| 	get_node("Connection Error").popup_centered() | ||||
|  | ||||
| #   _on_Button_button_down: Handle win button press | ||||
| #   TODO: This isn't a thing any more | ||||
| func _on_Button_button_down(): | ||||
| @@ -159,6 +192,13 @@ func _on_Button_button_down(): | ||||
| 	add_child(victory) | ||||
| 	victory.connect("exit_main", self, "end") | ||||
|  | ||||
| func _on_ConfirmationDialog_confirmed(): | ||||
| func _on_Forfeit_Confirmation_confirmed(): | ||||
| 	if Net.connected: | ||||
| 		# Send forfeit request to all users | ||||
| 		rpc("end") | ||||
| 	end() | ||||
|  | ||||
| func _on_Connection_Error_confirmed(): | ||||
| 	# End the game | ||||
| 	queue_free() | ||||
|  | ||||
|   | ||||
| @@ -12,15 +12,13 @@ var Setup = preload("res://scenes/Game/Setup.tscn") | ||||
| var Fire  = preload("res://scenes/Game/Fire.tscn") | ||||
|  | ||||
| # Members | ||||
| var pid   # Player ID | ||||
| var board # Board | ||||
|  | ||||
| var fire_at_position # Position to fire at | ||||
| var fire_pos = Vector2(-1,-1) | ||||
| var target = Vector2(-1,-1) | ||||
|  | ||||
| # Called when the node enters the scene tree for the first time. | ||||
| func _ready(): | ||||
| 	# Set the player ID according to which network peer ID we are | ||||
| 	pid = int(name) | ||||
| 	pass | ||||
|  | ||||
| func set_up_begin(): | ||||
| 	var setup = Setup.instance() | ||||
| @@ -32,22 +30,23 @@ func set_up_begin(): | ||||
| #   hit: Called when opponent fires on us. | ||||
| #     Update internal state, and return hit/miss/sunk | ||||
| func hit(pos): | ||||
| 	target = pos | ||||
| 	var res = board.hit(pos) | ||||
| 	return res | ||||
|  | ||||
| #   mark: Called when the opponent returns hit/miss/sunk | ||||
| #     Update internal state, return ack/nak | ||||
| func mark(pos, value): | ||||
| func mark(value): | ||||
| 	# Mark the position on the top board | ||||
| 	board.fire(pos, value) | ||||
| 	return board.fire(fire_pos, value) | ||||
|  | ||||
| #   place_ship: called when ships are placed. | ||||
| #     forwards Ship locations to the Board, so that it may construct a ship | ||||
| #     ship: a list of ship properties {position, orientation, size, variant} | ||||
| func place_ship(pos, size, orientation, variant): | ||||
| 	board.place_ship(pos, size, orientation, variant) | ||||
| 	return board.place_ship(pos, size, orientation, variant) | ||||
|  | ||||
| #   setup: set up the board given the placed ship locations | ||||
| #   set_up: set up the board given the placed ship locations | ||||
| #     Places each ship onto the board | ||||
| #     ships: a list of lists of ship properties [[position, orientation, size, variant], ...] | ||||
| func set_up(ships): | ||||
| @@ -62,17 +61,22 @@ func set_up(ships): | ||||
| #     Initiates the player's turn, and blocks until the player selects a location to fire upon | ||||
| #     returns: fire = [player id, target coordinates] | ||||
| func turn_start(): | ||||
| 	print("turn_start") | ||||
| 	var fire = Fire.instance() | ||||
| 	 | ||||
| 	fire.hits = board.top_board | ||||
| 	add_child(fire) | ||||
| 	var pos = yield(fire, "fire_at") | ||||
| 	return pos | ||||
| 	fire_pos = yield(fire, "fire_at") | ||||
| 	fire.queue_free() | ||||
| 	return fire_pos | ||||
|  | ||||
|  | ||||
| #   getBoard: returns the player's board | ||||
| #     returns: board | ||||
| func getBoard(): | ||||
| 	return board | ||||
| func board_query(boardname): | ||||
| 	match boardname: | ||||
| 		"top": | ||||
| 			return board.query_top  (fire_pos) | ||||
| 		"bottom": | ||||
| 			return board.quert_bottom (target) | ||||
|  | ||||
| #   forfeit: ends game for player | ||||
| #     Sinks all ships | ||||
| @@ -86,7 +90,3 @@ func forfeit(): | ||||
| #   getShipCount: get the number of ships the player has left alive | ||||
| func getShipCount(): | ||||
| 	return board.get_ship_count() | ||||
|  | ||||
|  | ||||
| func _on_fire_at(pos): | ||||
| 	fire_at_position = pos | ||||
|   | ||||
| @@ -59,6 +59,8 @@ func hit(pos): | ||||
| 		# Sink the ship. | ||||
| 		set_sunk() | ||||
| 		res = SUNK | ||||
| 	# Update graphics | ||||
| 	update() | ||||
| 	return res | ||||
|  | ||||
| # update: (re)calculates extents and textures | ||||
|   | ||||
| @@ -1,15 +1,18 @@ | ||||
| extends Control | ||||
|  | ||||
| signal exit_main | ||||
| # Declare member variables here. Examples: | ||||
| # var a = 2 | ||||
| # var b = "text" | ||||
|  | ||||
| # Path to Board class, for instantiating new Boards in code | ||||
| var Board = preload("res://scenes/Game/Board.tscn") | ||||
|  | ||||
| # Called when the node enters the scene tree for the first time. | ||||
| func _ready(): | ||||
| 	pass # Replace with function body. | ||||
|  | ||||
| # Reveal a list of ships | ||||
| func reveal_ships(ships:Array): | ||||
| 	var board = Board.instance() | ||||
| 	add_child(board); | ||||
| 	for ship in ships: | ||||
| 		board.callv("place_ship", ship) | ||||
|  | ||||
| # Called every frame. 'delta' is the elapsed time since the previous frame. | ||||
| #func _process(delta): | ||||
| @@ -17,11 +20,11 @@ func _ready(): | ||||
|  | ||||
| func _on_restart_button_down(): | ||||
| 	AudioBus.emit_signal("button_clicked") | ||||
| 	#MessageBus.emit_signal("change_scene", "Multiplayer") | ||||
| 	pass # Replace with function body. | ||||
| 	MessageBus.emit_signal("change_scene", "Multiplayer") | ||||
| 	MessageBus.emit_signal("kill_scene", "Game") | ||||
|  | ||||
|  | ||||
| # returns player(s) back to main menu | ||||
| func _on_exit_to_main_button_down(): | ||||
| 	AudioBus.emit_signal("button_clicked") | ||||
| 	#MessageBus.emit_signal("change_scene", "Title") | ||||
| 	emit_signal("exit_main") | ||||
| 	MessageBus.emit_signal("return_to_title") | ||||
|   | ||||
| @@ -1,8 +1,10 @@ | ||||
| extends Control | ||||
| # Ignore discarded return values | ||||
| # warning-ignore:return_value_discarded | ||||
| # warning-ignore-all:return_value_discarded | ||||
| onready var player_list = find_node("Player List") | ||||
| onready var ip = find_node("IP Address") | ||||
| onready var ip_address  = find_node("IP Address") | ||||
| onready var name_popup  = find_node("Change Name") | ||||
| onready var game_popup  = find_node("Connect to Game") | ||||
|  | ||||
| # TODO: Write a function to update Player List with the list of attached players | ||||
|  | ||||
| @@ -17,28 +19,33 @@ func _on_peers_updated(): | ||||
| func set_IP_Address_text(show): | ||||
| 	# Print the IP address and port | ||||
| 	if show: | ||||
| 		ip.text = "IP: %s\nPort: %s" % [Net.get_ip(), Net.DEFAULT_PORT] | ||||
| 		ip_address.text = "IP: %s\nPort: %s" % [Net.get_ip(), Net.DEFAULT_PORT] | ||||
| 	else: | ||||
| 		ip.text = "" | ||||
| 		ip_address.text = "" | ||||
|  | ||||
| func _ready(): | ||||
| 	Net.connect("peers_updated", self, "_on_peers_updated") | ||||
| 	Net.connect("disconnected",  self, "_on_Net_disconnected") | ||||
| 	name_popup.get_node("Name Entry").text = Net.get_hostname() | ||||
| 	_on_peers_updated() | ||||
| 	pass | ||||
|  | ||||
| func show_Connected_Options(show): | ||||
| 	# Hide the host and connect buttons | ||||
| func show_Connected_Options(show, host = false): | ||||
| 	# [Hide]/Show the host options | ||||
| 	get_node("Lobby Options/Connected Options/Host Options").visible = host | ||||
| 	# [Hide]/Show the host and connect buttons | ||||
| 	get_node("Lobby Options/Host or Connect").visible = !show | ||||
| 	# Show the host options | ||||
| 	# [Show]/Hide the host options | ||||
| 	get_node("Lobby Options/Connected Options").visible = show | ||||
|  | ||||
| # Buttons | ||||
| #   Host Button: Host a game | ||||
| #     Hides the connect button | ||||
| func _on_Host_Button_pressed(): | ||||
| 	# Make noise | ||||
| 	AudioBus.emit_signal("button_clicked") | ||||
| 	# Show "Connected Options" | ||||
| 	show_Connected_Options(true) | ||||
| 	show_Connected_Options(true, true) | ||||
| 	# Show the host IP address | ||||
| 	set_IP_Address_text(true) | ||||
| 	# Begin hosting | ||||
| @@ -48,6 +55,8 @@ func _on_Host_Button_pressed(): | ||||
| #     Disconnect from (or stop hosting) a game | ||||
| #     Shows the host/connect buttons | ||||
| func _on_Disconnect_Button_pressed(): | ||||
| 	# Make noise | ||||
| 	AudioBus.emit_signal("button_clicked") | ||||
| 	# Disconnect | ||||
| 	Net.disconnect_host() | ||||
| 	# Hide "Connected Options" | ||||
| @@ -55,6 +64,13 @@ func _on_Disconnect_Button_pressed(): | ||||
| 	# Hide the host IP address | ||||
| 	set_IP_Address_text(false) | ||||
|  | ||||
| func _on_Start_Game_pressed(): | ||||
| 	# If there are enough players for a game | ||||
| 	if Net.peer_info.size() >= 2: | ||||
| 		# Start the game for all players | ||||
| 		rpc("start_game") | ||||
| 	pass # Replace with function body. | ||||
|  | ||||
| func _on_Net_disconnected(): | ||||
| 	# Hide "Connected Options" | ||||
| 	show_Connected_Options(false) | ||||
| @@ -62,28 +78,20 @@ func _on_Net_disconnected(): | ||||
| 	set_IP_Address_text(false) | ||||
|  | ||||
| func _on_Change_Name_Button_pressed(): | ||||
| 	# Make noise | ||||
| 	AudioBus.emit_signal("button_clicked") | ||||
| 	# Show the Change Name dialogue | ||||
| 	get_node("Change Name").popup_centered() | ||||
| 	pass | ||||
|  | ||||
| func _on_Connect_Button_pressed(): | ||||
| 	# Make noise | ||||
| 	AudioBus.emit_signal("button_clicked") | ||||
| 	# Show the Connect to Game dialogue | ||||
| 	get_node("Connect to Game").popup_centered() | ||||
| 	pass | ||||
|  | ||||
| func _on_Connect_to_Game_confirmed(): | ||||
| 	# Get the IP and port specified by the player | ||||
| 	var ipbox = find_node("IP and Port Entry") | ||||
| 	# Split it into IP and Port segments | ||||
| 	var ip_port = ipbox.text.split(":") | ||||
| 	# If text exists and contains valid IP address | ||||
| 	if ip_port.size() > 0 and ip_port[0].is_valid_ip_address(): | ||||
| 		# Connect to host | ||||
| 		Net.callv("connect_host", ip_port) | ||||
| 		# Show "Connected Options" | ||||
| 		show_Connected_Options(true) | ||||
|  | ||||
| func _on_Exit_Lobby_pressed(): | ||||
| 	# Make noise | ||||
| 	AudioBus.emit_signal("button_clicked") | ||||
| 	# Disconnect | ||||
| 	if Net.connected: | ||||
| 		Net.disconnect_host() | ||||
| @@ -92,6 +100,8 @@ func _on_Exit_Lobby_pressed(): | ||||
|  | ||||
|  | ||||
| func _on_IP_and_Port_Entry_text_entered(text): | ||||
| 	# Make noise | ||||
| 	AudioBus.emit_signal("button_clicked") | ||||
| 	# Split it into IP and Port segments | ||||
| 	var ip_port = text.split(":") | ||||
| 	# If text exists and contains valid IP address | ||||
| @@ -102,11 +112,21 @@ func _on_IP_and_Port_Entry_text_entered(text): | ||||
| 			# Show "Connected Options" | ||||
| 			show_Connected_Options(true) | ||||
| 			# Hide the popup | ||||
| 			find_node("Connect to Game").hide() | ||||
| 			game_popup.hide() | ||||
|  | ||||
|  | ||||
| func _on_Name_Entry_text_entered(text): | ||||
| 	# Change the name | ||||
| 	Net.change_name(text) | ||||
| 	# Hide the popup | ||||
| 	find_node("Change Name").hide() | ||||
| 	# Make noise | ||||
| 	AudioBus.emit_signal("button_clicked") | ||||
| 	# Check the length of the name | ||||
| 	if text.length() < 18: | ||||
| 		# Change the name | ||||
| 		Net.change_name(text) | ||||
| 		# Hide the popup | ||||
| 		name_popup.hide() | ||||
|  | ||||
| sync func start_game(): | ||||
| 	MessageBus.emit_signal("change_scene", "Gameplay") | ||||
| 	queue_free() | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -8,9 +8,11 @@ const LOCALHOST = "127.0.0.1" | ||||
|  | ||||
| # Enums, used for mail types | ||||
| #   Mail types: | ||||
| #     1: REQUEST: Message is a request for information | ||||
| #     0: REPLY: Message is a reply | ||||
| enum {REPLY, REQUEST, READY, ACK} | ||||
| #     0: REQUEST: Message is a request for information | ||||
| #     1: REPLY: Message is a reply | ||||
| #     2: READY: Message is "ready" | ||||
| #     3: ACK:   Message is an acknowledgement | ||||
| enum {REQUEST, REPLY, READY, ACK} | ||||
|  | ||||
| # Signals | ||||
| #   incoming(mail): Sent when there's an incoming message | ||||
| @@ -36,6 +38,10 @@ var local_info = {"name": ""} | ||||
| #     mail: The message received from the sender (implicitly JSON-decoded by JSONRPC) | ||||
| #     mail_type: Type of mail (see "Mail Types" enum above) | ||||
| remote func receive(mail): | ||||
| 	print_debug("recv: %s" % mail) | ||||
| 	# Unpack the mail | ||||
| 	# Uses json parser of unknown stability, how fun | ||||
| 	mail = parse_json(mail) | ||||
| 	# Get the sender's ID and force letter to be properly addressed | ||||
| 	mail[0] = get_tree().get_rpc_sender_id() | ||||
| 	# Add the mail to the inbox (so it can be read back later if necessary | ||||
| @@ -48,6 +54,7 @@ remote func receive(mail): | ||||
| #     mail: Variant of a json-encodable type (non-Object) to send | ||||
| #     mail_type: Type of mail (see "Mail Types" enum above) | ||||
| func send(id, mail, mail_type = REPLY): | ||||
| 	print_debug("send: %d, %s, %d" % [id, mail, mail_type]) | ||||
| 	# Make the recipient receive the mail | ||||
| 	rpc_id(id, "receive", to_json([-1, mail, mail_type])) | ||||
|  | ||||
| @@ -81,7 +88,7 @@ func accept_guests(accept:bool): | ||||
| func connect_host(ip = LOCALHOST, port = DEFAULT_PORT): | ||||
| 	get_hostname() | ||||
| 	var peer = NetworkedMultiplayerENet.new() | ||||
| 	var ret = peer.create_client(ip, port) | ||||
| 	var ret = peer.create_client(ip, int(port)) | ||||
| 	get_tree().network_peer = peer | ||||
| 	return ret | ||||
|  | ||||
| @@ -106,9 +113,10 @@ func disconnect_host(): | ||||
| func change_name(name): | ||||
| 	# Change name locally | ||||
| 	local_info["name"] = name | ||||
| 	# Send updated info info to all peers | ||||
| 	rpc("register_peer", local_info) | ||||
| 	pass | ||||
| 	# If connected, update peers | ||||
| 	if connected: | ||||
| 		# Send updated info info to all peers | ||||
| 		rpc("register_peer", local_info) | ||||
|  | ||||
| # Helper Functions | ||||
| #   get_hostname: Asks the host machine to provide its hostname, | ||||
|   | ||||
| @@ -12,7 +12,7 @@ func _ready(): | ||||
|  | ||||
| func _on_Singleplayer_pressed(): | ||||
| 	AudioBus.emit_signal("button_clicked") | ||||
| 	MessageBus.emit_signal("change_scene", "Singleplayer") | ||||
| 	MessageBus.emit_signal("change_scene", "Gameplay") | ||||
| 	queue_free() | ||||
|  | ||||
| func _on_Multiplayer_pressed(): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user