1
0
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:
John 2021-11-24 12:17:07 -06:00
parent 3f4b42821b
commit af9b18243e
14 changed files with 304 additions and 211 deletions

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=3 format=2] [gd_scene load_steps=3 format=2]
[ext_resource path="res://script/game/Gameplay/Board.gd" type="Script" id=1] [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"] [node name="Board" type="Node2D"]
position = Vector2( 36, 36 ) position = Vector2( 36, 36 )

View File

@ -29,7 +29,7 @@ margin_right = 88.0
margin_bottom = 20.0 margin_bottom = 20.0
text = "Forfeit" text = "Forfeit"
[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."] [node name="Forfeit Confirmation" type="ConfirmationDialog" parent="."]
anchor_left = 0.5 anchor_left = 0.5
anchor_top = 0.5 anchor_top = 0.5
anchor_right = 0.5 anchor_right = 0.5
@ -54,6 +54,13 @@ margin_right = 43.4668
margin_bottom = 26.1478 margin_bottom = 26.1478
text = "Win" 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="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="button_down" from="Button" to="." method="_on_Button_button_down"]
[connection signal="confirmed" from="Connection Error" to="." method="_on_Connection_Error_confirmed"]

View File

@ -10,25 +10,12 @@ font_data = ExtResource( 2 )
[node name="Victory" type="Control"] [node name="Victory" type="Control"]
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
mouse_filter = 2
script = ExtResource( 1 ) script = ExtResource( 1 )
__meta__ = { __meta__ = {
"_edit_use_anchors_": false "_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="."] [node name="exit_to_main" type="Button" parent="."]
margin_left = 541.0 margin_left = 541.0
margin_top = 327.85 margin_top = 327.85
@ -47,6 +34,20 @@ margin_right = 63.2202
margin_bottom = 357.41 margin_bottom = 357.41
text = "Restart" 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="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="pressed" from="exit_to_main" to="." method="_on_Button_pressed"]
[connection signal="button_down" from="Button2" to="." method="_on_restart_button_down"] [connection signal="button_down" from="Button2" to="." method="_on_restart_button_down"]

View File

@ -93,9 +93,19 @@ text = "Connect to Game"
[node name="Connected Options" type="VBoxContainer" parent="Lobby Options"] [node name="Connected Options" type="VBoxContainer" parent="Lobby Options"]
visible = false visible = false
margin_top = 84.0 margin_top = 36.0
margin_right = 123.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"] [node name="Disconnect Button" type="Button" parent="Lobby Options/Connected Options"]
margin_right = 123.0 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/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/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/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/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"] [connection signal="pressed" from="Lobby Options/Exit Lobby Button" to="." method="_on_Exit_Lobby_pressed"]

View File

@ -60,7 +60,7 @@ func _on_scene_start(scene):
var instance var instance
#print ("_on_scene_start(",scene,")") #print ("_on_scene_start(",scene,")")
match scene: match scene:
"Singleplayer": "Gameplay":
instance = Game.instance() instance = Game.instance()
add_child (instance) add_child (instance)
return true return true

View File

@ -10,6 +10,7 @@ enum {MISS = -1, READY = 0, HIT = 1, SUNK = 2, LOST = 3}
var bottom_board:Array # Player board var bottom_board:Array # Player board
var top_board:Array # Opponent board var top_board:Array # Opponent board
var ships = [] # list of Ships var ships = [] # list of Ships
var ship_data = [] # Data used to generate ships
var ship_count = 0 # number of 'active' (un-sunk) ships var ship_count = 0 # number of 'active' (un-sunk) ships
# a board is square. This is its side length # a board is square. This is its side length
@ -33,50 +34,55 @@ func hit(pos):
var res = MISS var res = MISS
# Get the ship-metadata for that location # Get the ship-metadata for that location
var ship = bottom_board[pos.x][pos.y] var ship = bottom_board[pos.x][pos.y]
# If there's a ship there, which exists, and hasn't been hit, # If the ship's already been hit here, don't bother beating it again
if ship and ship[0] > NO_SHIP and ship[1] == READY: if ship[1] != READY:
# Hit the ship, and store whether HIT or SUNK return ship[1]
if ship[0] > NO_SHIP:
# Decide whether HIT or SUNK
res = ships[ship[0]].hit(pos) 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 ship sunk,
if res == SUNK: if res == SUNK:
# remove it from the count # remove it from the count
ship_count -= 1 ship_count -= 1
# If no ships left, # If we have no more ships left, we LOST
if ship_count == 0: 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 return res
# fire: Store the results of firing on an opponent # fire: Store the results of firing on an opponent
# pos: board position fired on # pos: board position fired on
# res: result of firing on the opponent # res: result of firing on the opponent
func fire(pos, res): 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 top_board[pos.x][pos.y] = res
return true return res
return false else:
return top_board[pos.x][pos.y]
# Place a ship on the board at board-space coordinates # Place a ship on the board at board-space coordinates
func place_ship(in_position, in_size, in_orientation, in_variant = 0): 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() var ship = Ship.instance()
ship._init(in_position, in_size, in_orientation, in_variant) ship._init(in_position, in_size, in_orientation, in_variant)
# Mark the ship on the board
for pos in ship.get_extent(): for pos in ship.get_extent():
bottom_board[pos.x][pos.y] = [ships.size(), READY] bottom_board[pos.x][pos.y] = [ships.size(), READY]
# Add the ship to the ships array, and keep count
ships.append(ship) ships.append(ship)
ship_count += 1 ship_count += 1
# Add the ship to the scene tree
add_child(ship) add_child(ship)
# Not sure why this is necessary yet
func get_bottom_board(): func query_bottom(pos):
return bottom_board return bottom_board[pos.x][pos.y]
func query_top(pos):
return top_board[pos.x][pos.y]
# Get the number of live ships # Get the number of live ships
func get_ship_count(): func get_ship_count():
@ -84,16 +90,16 @@ func get_ship_count():
# _init: Constructor # _init: Constructor
func _init(): func _init():
# Initialize the bottom_board to a 10x10 array # Initialize the bottom_board to a len*len array
for _row in range(board_len): for x in board_len:
bottom_board.append([]) bottom_board.append([])
for column in bottom_board: for y in board_len:
column.resize(10) bottom_board[x].append([NO_SHIP, READY])
# Initialize the top_board to a 10x10 array # Initialize the top_board to a len*len array
for _row in range(board_len): for x in board_len:
top_board.append([]) top_board.append([])
for column in top_board: for y in board_len:
column.resize(board_len) top_board[x].append(READY)
# worldspace_to_boardspace: convert a Vector2 in world-space to board-space # worldspace_to_boardspace: convert a Vector2 in world-space to board-space
func worldspace_to_boardspace(coordinate:Vector2): func worldspace_to_boardspace(coordinate:Vector2):

View File

@ -1,19 +1,20 @@
extends Control extends Control
# Signal to pass the fire location back to parent
signal fire_at
var atlas = preload("res://assets/game/HitMissAtlas.png") var atlas = preload("res://assets/game/HitMissAtlas.png")
var sprites = [] var sprites = []
var hits = [] var hits
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
func _ready(): func _ready():
print("Fire: _ready()")
for x in 10:
for y in 10:
texture(Vector2(x,y))
pass # Replace with function body. 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(): func _on_Fire_pressed():
var crosshair = get_node("Crosshair") var crosshair = get_node("Crosshair")
# Check if the crosshair is in a valid position # 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) var crosshair_pos = crosshair.world_to_board_space(crosshair.position)
if(hits[crosshair_pos.x][crosshair_pos.y] == 0): if(hits[crosshair_pos.x][crosshair_pos.y] == 0):
# fires at position # fires at position
print("Fire at position: ", crosshair_pos)
emit_signal("fire_at", crosshair_pos) emit_signal("fire_at", crosshair_pos)
# Close the Firing menu
queue_free()
return return
#if invalid position popup appears #if invalid position popup appears
var dialog = get_node("FireDialog") var dialog = get_node("FireDialog")
@ -32,7 +30,7 @@ func _on_Fire_pressed():
func _on_FireDialog_confirmed(): func _on_FireDialog_confirmed():
get_node("Crosshair").visible = true get_node("Crosshair").visible = true
const OFFSET = Vector2(18, 18) const OFFSET = Vector2(18, 18)
func texture(index): func texture(index):
@ -52,7 +50,4 @@ func texture(index):
var sprite = Sprite.new() var sprite = Sprite.new()
sprite.texture = t sprite.texture = t
sprite.position = Vector2(index.x, index.y) * textureSize + OFFSET sprite.position = Vector2(index.x, index.y) * textureSize + OFFSET
printt(t.get_height(), t.get_width())
$board_blue.add_child(sprite) $board_blue.add_child(sprite)

View File

@ -1,11 +1,18 @@
extends Control extends Control
# warning-ignore-all:unused_signal # warning-ignore-all:unused_signal
# warning-ignore-all:return_value_discarded
enum {MISS = -1, READY = 0, HIT = 1, SUNK = 2, LOST = 3}
# Signals # Signals
signal fire # fire(position) signal fire # fire(position)
signal hit # hit (state: see Miss/Ready/Hit/Sunk enum in Board.gd) signal hit # hit (state): see Miss/Ready/Hit/Sunk enum in Board.gd)
signal win # win (): sent when opponent player lost signal miss
signal loss
signal forfeit
signal game_ready
# Path to Player class, for instantiating new Players in code # Path to Player class, for instantiating new Players in code
var Player = preload("res://scenes/Game/Player.tscn") 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 # Array of instances of the Player class; stores the Players
var player var player
var players_ready = [] var players_ready = []
# turn counter
var turn = 0
# winner
var winner = 0
# Every game is a multiplayer game, even the ones that aren't. # Every game is a multiplayer game, even the ones that aren't.
# We're taking the Minecraft approach, baby # 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. # Called when the node enters the scene tree for the first time.
func _ready(): func _ready():
get_node("ConfirmationDialog").get_ok().text = "Yes" get_node("Forfeit Confirmation").get_ok().text = "Yes"
get_node("ConfirmationDialog").get_cancel().text = "No" get_node("Forfeit Confirmation").get_cancel().text = "No"
get_node("ConfirmationDialog").get_ok().rect_min_size.x = 100 get_node("Forfeit Confirmation").get_ok().rect_min_size.x = 100
get_node("ConfirmationDialog").get_cancel().rect_min_size.x = 100 get_node("Forfeit Confirmation").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 if Net.connected:
Net.connect("disconnected", self, "connection_error")
Net.connect("incoming", self, "_on_Net_incoming")
pass pass
game_setup() game_setup()
# Function used to keep track of which players are ready # Function used to keep track of which players are ready
# TODO: Change this to keep track of ready states only func player_ready(sender):
func player_ready(): print("player_ready(%s), %d" % [sender, players_ready.size()])
players_ready.append(Net.get_network_id()) players_ready.append(sender)
pass if (players_ready.size() >= Net.peer_info.size()):
emit_signal("game_ready")
# Member functions: # Member functions:
# game_start: starts the game # game_setup: starts the game
sync func game_setup(): sync func game_setup():
# If there's no server connected, create one # If there's no server connected, create one
if not Net.connected: if not Net.connected:
# TODO: Create a fake peer who we can automate, for single-player mode # TODO: Create a fake peer who we can automate, for single-player mode
Net.start_host() Net.start_host()
network_id = Net.get_network_id() 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 pass
# game_start: Runs on host. Controls the game. # state_win: The winning state. If you reach here, someone's won.
func game_start(): # ships: The opponent's ship data, so that their board can be shown
var state = "P1_fire" remote func state_win(ships):
# Make sure we're the server victory_screen(ships)
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
pass pass
func fire_on(id, pos:Vector2): # play_hit_sound: Play a hit sound depending on the severity of the hit
# REQUEST fire on opponent # value: Lost/Sunk/Hit/Miss
Net.send(id, ["fire", pos], Net.REQUEST) sync func play_hit_sound(value):
# Wait for REPLY 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) # mark: Update the local player's hit/miss board when opponent replies
func mark(res):
func _on_win(): return player.mark(res)
pass
# _on_Net_incoming: Handle mail.
func _on_Net_incoming(mail): func _on_Net_incoming(mail):
print ("mail: ", mail, mail.size())
if mail.size() == 3: if mail.size() == 3:
var sender = mail[0] print ("mail: ", mail, mail.size())
var sender = int(mail[0])
var message = mail[1] var message = mail[1]
var mailtype = mail[2] var mailtype = int(mail[2])
printt(sender, message, mailtype)
match 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: Net.REPLY:
print ("got REPLY")
# message is a REPLY (return value) # message is a REPLY (return value)
match message[0]: match message[0]:
# on "fire": fire(result)
"fire": "fire":
emit_signal("hit", message[1]) hit(message[1])
# Return value of # on "hit": mark(state)
"hit": "hit":
mark(message[1])
"forfeit":
pass pass
pass
Net.READY: Net.READY:
print ("got READY")
# Add player to the ready array # Add player to the ready array
players_ready.append(sender) player_ready(sender)
pass _:
print ("got ", mailtype)
# _on_player_ready: Player Ready signal handler # _on_player_ready: Player Ready signal handler
func _on_player_ready(): func _on_player_ready():
print ("_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 # victory_screen: display the victory screen
func victory_screen(): func victory_screen(ships, winner = true):
# TODO: Create the victory screen, fill it with knowledge if winner:
pass # Hide the buttons
get_node("Bittons").hide()
# display_turn: display which turn it is on the screen # Create a new Victory screen
func display_turn(): var victory = Victory.instance()
# TODO: Update the turn display, if there is one? # Give it the ships received from the opponent
pass 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 # _on_Forfeit_pressed: Handle forfeit button press
func _on_Forfeit_pressed(): func _on_Forfeit_pressed():
AudioBus.emit_signal("button_clicked") AudioBus.emit_signal("button_clicked")
get_node("ConfirmationDialog").popup() get_node("Forfeit Confirmation").popup_centered()
# end: end the Game # end: end the Game
func end(): sync func end():
queue_free() queue_free()
func connection_error():
get_node("Connection Error").popup_centered()
# _on_Button_button_down: Handle win button press # _on_Button_button_down: Handle win button press
# TODO: This isn't a thing any more # TODO: This isn't a thing any more
func _on_Button_button_down(): func _on_Button_button_down():
@ -159,6 +192,13 @@ func _on_Button_button_down():
add_child(victory) add_child(victory)
victory.connect("exit_main", self, "end") 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() end()
func _on_Connection_Error_confirmed():
# End the game
queue_free()

View File

@ -12,15 +12,13 @@ var Setup = preload("res://scenes/Game/Setup.tscn")
var Fire = preload("res://scenes/Game/Fire.tscn") var Fire = preload("res://scenes/Game/Fire.tscn")
# Members # Members
var pid # Player ID
var board # Board var board # Board
var fire_pos = Vector2(-1,-1)
var fire_at_position # Position to fire at var target = Vector2(-1,-1)
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
func _ready(): func _ready():
# Set the player ID according to which network peer ID we are pass
pid = int(name)
func set_up_begin(): func set_up_begin():
var setup = Setup.instance() var setup = Setup.instance()
@ -32,22 +30,23 @@ func set_up_begin():
# hit: Called when opponent fires on us. # hit: Called when opponent fires on us.
# Update internal state, and return hit/miss/sunk # Update internal state, and return hit/miss/sunk
func hit(pos): func hit(pos):
target = pos
var res = board.hit(pos) var res = board.hit(pos)
return res return res
# mark: Called when the opponent returns hit/miss/sunk # mark: Called when the opponent returns hit/miss/sunk
# Update internal state, return ack/nak # Update internal state, return ack/nak
func mark(pos, value): func mark(value):
# Mark the position on the top board # Mark the position on the top board
board.fire(pos, value) return board.fire(fire_pos, value)
# place_ship: called when ships are placed. # place_ship: called when ships are placed.
# forwards Ship locations to the Board, so that it may construct a ship # forwards Ship locations to the Board, so that it may construct a ship
# ship: a list of ship properties {position, orientation, size, variant} # ship: a list of ship properties {position, orientation, size, variant}
func place_ship(pos, size, orientation, 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 # Places each ship onto the board
# ships: a list of lists of ship properties [[position, orientation, size, variant], ...] # ships: a list of lists of ship properties [[position, orientation, size, variant], ...]
func set_up(ships): 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 # Initiates the player's turn, and blocks until the player selects a location to fire upon
# returns: fire = [player id, target coordinates] # returns: fire = [player id, target coordinates]
func turn_start(): func turn_start():
print("turn_start")
var fire = Fire.instance() var fire = Fire.instance()
fire.hits = board.top_board
add_child(fire) add_child(fire)
var pos = yield(fire, "fire_at") fire_pos = yield(fire, "fire_at")
return pos fire.queue_free()
return fire_pos
# getBoard: returns the player's board # getBoard: returns the player's board
# returns: board # returns: board
func getBoard(): func board_query(boardname):
return board match boardname:
"top":
return board.query_top (fire_pos)
"bottom":
return board.quert_bottom (target)
# forfeit: ends game for player # forfeit: ends game for player
# Sinks all ships # Sinks all ships
@ -86,7 +90,3 @@ func forfeit():
# getShipCount: get the number of ships the player has left alive # getShipCount: get the number of ships the player has left alive
func getShipCount(): func getShipCount():
return board.get_ship_count() return board.get_ship_count()
func _on_fire_at(pos):
fire_at_position = pos

View File

@ -59,6 +59,8 @@ func hit(pos):
# Sink the ship. # Sink the ship.
set_sunk() set_sunk()
res = SUNK res = SUNK
# Update graphics
update()
return res return res
# update: (re)calculates extents and textures # update: (re)calculates extents and textures

View File

@ -1,15 +1,18 @@
extends Control extends Control
signal exit_main # Path to Board class, for instantiating new Boards in code
# Declare member variables here. Examples: var Board = preload("res://scenes/Game/Board.tscn")
# var a = 2
# var b = "text"
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
func _ready(): func _ready():
pass # Replace with function body. 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. # Called every frame. 'delta' is the elapsed time since the previous frame.
#func _process(delta): #func _process(delta):
@ -17,11 +20,11 @@ func _ready():
func _on_restart_button_down(): func _on_restart_button_down():
AudioBus.emit_signal("button_clicked") AudioBus.emit_signal("button_clicked")
#MessageBus.emit_signal("change_scene", "Multiplayer") MessageBus.emit_signal("change_scene", "Multiplayer")
pass # Replace with function body. MessageBus.emit_signal("kill_scene", "Game")
# returns player(s) back to main menu # returns player(s) back to main menu
func _on_exit_to_main_button_down(): func _on_exit_to_main_button_down():
AudioBus.emit_signal("button_clicked") AudioBus.emit_signal("button_clicked")
#MessageBus.emit_signal("change_scene", "Title") MessageBus.emit_signal("return_to_title")
emit_signal("exit_main")

View File

@ -1,8 +1,10 @@
extends Control extends Control
# Ignore discarded return values # 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 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 # 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): func set_IP_Address_text(show):
# Print the IP address and port # Print the IP address and port
if show: 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: else:
ip.text = "" ip_address.text = ""
func _ready(): func _ready():
Net.connect("peers_updated", self, "_on_peers_updated") Net.connect("peers_updated", self, "_on_peers_updated")
Net.connect("disconnected", self, "_on_Net_disconnected") Net.connect("disconnected", self, "_on_Net_disconnected")
name_popup.get_node("Name Entry").text = Net.get_hostname()
_on_peers_updated() _on_peers_updated()
pass pass
func show_Connected_Options(show): func show_Connected_Options(show, host = false):
# Hide the host and connect buttons # [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 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 get_node("Lobby Options/Connected Options").visible = show
# Buttons # Buttons
# Host Button: Host a game # Host Button: Host a game
# Hides the connect button # Hides the connect button
func _on_Host_Button_pressed(): func _on_Host_Button_pressed():
# Make noise
AudioBus.emit_signal("button_clicked")
# Show "Connected Options" # Show "Connected Options"
show_Connected_Options(true) show_Connected_Options(true, true)
# Show the host IP address # Show the host IP address
set_IP_Address_text(true) set_IP_Address_text(true)
# Begin hosting # Begin hosting
@ -48,6 +55,8 @@ func _on_Host_Button_pressed():
# Disconnect from (or stop hosting) a game # Disconnect from (or stop hosting) a game
# Shows the host/connect buttons # Shows the host/connect buttons
func _on_Disconnect_Button_pressed(): func _on_Disconnect_Button_pressed():
# Make noise
AudioBus.emit_signal("button_clicked")
# Disconnect # Disconnect
Net.disconnect_host() Net.disconnect_host()
# Hide "Connected Options" # Hide "Connected Options"
@ -55,6 +64,13 @@ func _on_Disconnect_Button_pressed():
# Hide the host IP address # Hide the host IP address
set_IP_Address_text(false) 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(): func _on_Net_disconnected():
# Hide "Connected Options" # Hide "Connected Options"
show_Connected_Options(false) show_Connected_Options(false)
@ -62,28 +78,20 @@ func _on_Net_disconnected():
set_IP_Address_text(false) set_IP_Address_text(false)
func _on_Change_Name_Button_pressed(): func _on_Change_Name_Button_pressed():
# Make noise
AudioBus.emit_signal("button_clicked")
# Show the Change Name dialogue # Show the Change Name dialogue
get_node("Change Name").popup_centered() get_node("Change Name").popup_centered()
pass
func _on_Connect_Button_pressed(): func _on_Connect_Button_pressed():
# Make noise
AudioBus.emit_signal("button_clicked")
# Show the Connect to Game dialogue # Show the Connect to Game dialogue
get_node("Connect to Game").popup_centered() 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(): func _on_Exit_Lobby_pressed():
# Make noise
AudioBus.emit_signal("button_clicked")
# Disconnect # Disconnect
if Net.connected: if Net.connected:
Net.disconnect_host() Net.disconnect_host()
@ -92,6 +100,8 @@ func _on_Exit_Lobby_pressed():
func _on_IP_and_Port_Entry_text_entered(text): func _on_IP_and_Port_Entry_text_entered(text):
# Make noise
AudioBus.emit_signal("button_clicked")
# Split it into IP and Port segments # Split it into IP and Port segments
var ip_port = text.split(":") var ip_port = text.split(":")
# If text exists and contains valid IP address # 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"
show_Connected_Options(true) show_Connected_Options(true)
# Hide the popup # Hide the popup
find_node("Connect to Game").hide() game_popup.hide()
func _on_Name_Entry_text_entered(text): func _on_Name_Entry_text_entered(text):
# Change the name # Make noise
Net.change_name(text) AudioBus.emit_signal("button_clicked")
# Hide the popup # Check the length of the name
find_node("Change Name").hide() 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()

View File

@ -8,9 +8,11 @@ const LOCALHOST = "127.0.0.1"
# Enums, used for mail types # Enums, used for mail types
# Mail types: # Mail types:
# 1: REQUEST: Message is a request for information # 0: REQUEST: Message is a request for information
# 0: REPLY: Message is a reply # 1: REPLY: Message is a reply
enum {REPLY, REQUEST, READY, ACK} # 2: READY: Message is "ready"
# 3: ACK: Message is an acknowledgement
enum {REQUEST, REPLY, READY, ACK}
# Signals # Signals
# incoming(mail): Sent when there's an incoming message # 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: The message received from the sender (implicitly JSON-decoded by JSONRPC)
# mail_type: Type of mail (see "Mail Types" enum above) # mail_type: Type of mail (see "Mail Types" enum above)
remote func receive(mail): 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 # Get the sender's ID and force letter to be properly addressed
mail[0] = get_tree().get_rpc_sender_id() mail[0] = get_tree().get_rpc_sender_id()
# Add the mail to the inbox (so it can be read back later if necessary # 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: Variant of a json-encodable type (non-Object) to send
# mail_type: Type of mail (see "Mail Types" enum above) # mail_type: Type of mail (see "Mail Types" enum above)
func send(id, mail, mail_type = REPLY): func send(id, mail, mail_type = REPLY):
print_debug("send: %d, %s, %d" % [id, mail, mail_type])
# Make the recipient receive the mail # Make the recipient receive the mail
rpc_id(id, "receive", to_json([-1, mail, mail_type])) 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): func connect_host(ip = LOCALHOST, port = DEFAULT_PORT):
get_hostname() get_hostname()
var peer = NetworkedMultiplayerENet.new() var peer = NetworkedMultiplayerENet.new()
var ret = peer.create_client(ip, port) var ret = peer.create_client(ip, int(port))
get_tree().network_peer = peer get_tree().network_peer = peer
return ret return ret
@ -106,9 +113,10 @@ func disconnect_host():
func change_name(name): func change_name(name):
# Change name locally # Change name locally
local_info["name"] = name local_info["name"] = name
# Send updated info info to all peers # If connected, update peers
rpc("register_peer", local_info) if connected:
pass # Send updated info info to all peers
rpc("register_peer", local_info)
# Helper Functions # Helper Functions
# get_hostname: Asks the host machine to provide its hostname, # get_hostname: Asks the host machine to provide its hostname,
@ -130,7 +138,7 @@ func get_ip():
pass pass
func _ready(): func _ready():
var _trash var _trash
_trash = get_tree().connect("network_peer_connected", self, "_peer_connected" ) _trash = get_tree().connect("network_peer_connected", self, "_peer_connected" )
_trash = get_tree().connect("network_peer_disconnected", self, "_peer_disconnected") _trash = get_tree().connect("network_peer_disconnected", self, "_peer_disconnected")
_trash = get_tree().connect("connected_to_server", self, "_host_connected" ) _trash = get_tree().connect("connected_to_server", self, "_host_connected" )

View File

@ -12,7 +12,7 @@ func _ready():
func _on_Singleplayer_pressed(): func _on_Singleplayer_pressed():
AudioBus.emit_signal("button_clicked") AudioBus.emit_signal("button_clicked")
MessageBus.emit_signal("change_scene", "Singleplayer") MessageBus.emit_signal("change_scene", "Gameplay")
queue_free() queue_free()
func _on_Multiplayer_pressed(): func _on_Multiplayer_pressed():