1
0
mirror of https://github.com/JohnBreaux/Boat-Battle.git synced 2025-02-04 12:28:35 +00:00

Compare commits

...

10 Commits

Author SHA1 Message Date
JohnBreaux
1beacb95e4
Change name for uniformity, add CC-BY Notice 2021-12-07 20:08:29 -06:00
55a12dceea Create the Boat Battle Player's Guide, to instruct new players on the ways of Boat Battle. 2021-12-06 14:40:34 -06:00
68009d1c39 Improve interactions between Game, Victory, and Lobby.
Game:
- Show victory screen on opponent, including ships
- Return to Lobby on forfeit/connection error

Victory:
- Display win/lose status
- Request that Game return to lobby, rather than doing it manually
- Disconnect from network when returning to main menu
2021-12-06 13:16:49 -06:00
99a54a021f Victory: Enable play-again button, organize menu in a more aesthetically pleasing way, fix text font weight/color to not die on light theme. 2021-12-06 13:12:21 -06:00
77d6b25f58 Lobby: Load Net state on Lobby start, and show appropriate menus. 2021-12-06 13:07:53 -06:00
4a2d577e9c Title: Change multiplayer game mode text to better suit the multiplayer-only nature of the game 2021-12-06 13:05:30 -06:00
df7c76806d Build: Enable building for Linux/X11. 2021-12-06 13:04:24 -06:00
49bda9cb9a Debug: Check for presence of file before loading. Fixes annoying break to debugger. 2021-12-06 13:02:11 -06:00
d8c5d0548f Net: Fix off-by-one in number of players allowed to connect (was 3, now 2; Godot does not include the 'server'.) 2021-12-06 12:57:06 -06:00
JohnBreaux
dbd7e0392c
Update README.md
Add information about the game
2021-11-28 19:12:23 -06:00
10 changed files with 220 additions and 61 deletions

34
How to Play.md Normal file
View File

@ -0,0 +1,34 @@
# Boat Battle - Player's Guide
Boat Battle is a limited information game where two opposing players compete to sink each others' ships.
### Starting a Multiplayer Game
1. From the Title Screen, select "Start" to bring you to the Lobby.
2. Select "Host Game".
3. Give the other player your IP address, shown on screen, and wait for them to join.
4. When everyone's joined, click Start Game to start the game!
### Joining a Multiplayer Game
1. From the Title Screen, select "Start" to bring you to the Lobby.
2. Select "Join Game".
3. In the "Join Game" text-entry box, type the host's IP address, and press the Enter or Return key
4. Wait for the host to start the game!
### Playing the Game
1. Place your ships:
- Move your mouse over the center of each ship, marked by a pin
- Click and drag it onto free spaces on the board
- Right click the pin to rotate it!
- Be wary when rotating ships: if they bump into each other, one will get knocked off the board!
2. Confirm your ship placement:
- Once your ships are all on the board, press the "Confirm Placement" button to lock them in place
3. When it's your turn to play, Fire!
- When your turn starts, the Fire menu pops up.
- Move your mouse to a space on the board, and click a spot to place the cross-hair
- To move the cross-hair, click a new space on the board.
- With the cross-hair placed, click the "FIRE" button to fire on your opponent
- You'll either see a gratifying explosion, or a sad, sad sploosh.
4. When you win, or lose, you'll be greeted with your opponent's ships, so you can see just how close to annihilating them you got!

View File

@ -1 +1,13 @@
# Group12 # Group12
## Boat Battle
### The classic pen and paper game brought to life
Enjoy minutes of fun and engaging multiplayer strategy gameplay in this recreation of the classic pen and paper Boat Battling game.
Made using Godot Engine v3.3.4
With music by Kevin MacLeod:
"Captain Scurvy" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0 License
http://creativecommons.org/licenses/by/4.0/

View File

@ -39,3 +39,28 @@ application/product_name="Ship Battle"
application/file_description="A game made by four very tired students." application/file_description="A game made by four very tired students."
application/copyright="2021 Group 12 Industries" application/copyright="2021 Group 12 Industries"
application/trademarks="" application/trademarks=""
[preset.1]
name="Linux/X11"
platform="Linux/X11"
runnable=true
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../../Boat Battle.x86_64"
script_export_mode=1
script_encryption_key=""
[preset.1.options]
custom_template/debug=""
custom_template/release=""
binary_format/64_bits=true
binary_format/embed_pck=true
texture_format/bptc=true
texture_format/s3tc=true
texture_format/etc=false
texture_format/etc2=false
texture_format/no_bptc_fallbacks=true

View File

@ -4,50 +4,95 @@
[ext_resource path="res://assets/font/Minecraft.ttf" type="DynamicFontData" id=2] [ext_resource path="res://assets/font/Minecraft.ttf" type="DynamicFontData" id=2]
[sub_resource type="DynamicFont" id=1] [sub_resource type="DynamicFont" id=1]
size = 40 size = 48
outline_size = 2
outline_color = Color( 0, 0, 0, 1 )
use_mipmaps = true
extra_spacing_char = 2
font_data = ExtResource( 2 ) 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
margin_top = -0.471924
margin_bottom = -0.471924
mouse_filter = 2 mouse_filter = 2
script = ExtResource( 1 ) script = ExtResource( 1 )
__meta__ = { __meta__ = {
"_edit_use_anchors_": false "_edit_use_anchors_": false
} }
[node name="exit_to_main" type="Button" parent="."] [node name="Board Placeholder" type="Control" parent="."]
margin_left = 541.0 margin_left = 18.0
margin_top = 327.85 margin_top = 18.0
margin_right = 636.0 margin_right = 342.0
margin_bottom = 353.85 margin_bottom = 342.0
text = "Exit to Main"
__meta__ = { __meta__ = {
"_edit_use_anchors_": false "_edit_use_anchors_": false
} }
[node name="Button2" type="Button" parent="."] [node name="Center Buttons" type="CenterContainer" parent="."]
visible = false margin_left = 342.0
margin_left = 2.22023 margin_top = 180.0
margin_top = 337.41 margin_right = 622.0
margin_right = 63.2202 margin_bottom = 342.0
margin_bottom = 357.41 grow_horizontal = 2
text = "Restart" grow_vertical = 2
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Victory" type="Label" parent="."] [node name="Buttons" type="VBoxContainer" parent="Center Buttons"]
margin_left = 380.0 margin_left = 60.0
margin_top = 44.5109 margin_top = 57.0
margin_right = 260.32 margin_right = 220.0
margin_bottom = 84.5109 margin_bottom = 105.0
size_flags_vertical = 0 rect_min_size = Vector2( 160, 0 )
custom_constants/separation = 8
[node name="Restart" type="Button" parent="Center Buttons/Buttons"]
margin_right = 160.0
margin_bottom = 20.0
rect_min_size = Vector2( 160, 0 )
text = "Play Again"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Exit to Title" type="Button" parent="Center Buttons/Buttons"]
margin_top = 28.0
margin_right = 160.0
margin_bottom = 48.0
text = "Return to Title"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Center Text" type="CenterContainer" parent="."]
margin_left = 342.0
margin_top = 18.0
margin_right = 622.0
margin_bottom = 180.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Text" type="Label" parent="Center Text"]
margin_left = 50.0
margin_top = 57.0
margin_right = 230.0
margin_bottom = 105.0
size_flags_stretch_ratio = 0.0
custom_fonts/font = SubResource( 1 ) custom_fonts/font = SubResource( 1 )
custom_colors/font_color = Color( 1, 1, 1, 1 )
custom_colors/font_outline_modulate = Color( 0, 0, 0, 1 )
custom_colors/font_color_shadow = Color( 0, 0, 0, 1 )
custom_constants/shadow_offset_x = 0
custom_constants/shadow_offset_y = 6
text = "Victory" text = "Victory"
align = 1
valign = 1
__meta__ = { __meta__ = {
"_edit_use_anchors_": false "_edit_use_anchors_": false
} }
[connection signal="button_down" from="exit_to_main" to="." method="_on_exit_to_main_button_down"] [connection signal="pressed" from="Center Buttons/Buttons/Restart" to="." method="_on_Restart_pressed"]
[connection signal="pressed" from="exit_to_main" to="." method="_on_Button_pressed"] [connection signal="pressed" from="Center Buttons/Buttons/Exit to Title" to="." method="_on_Exit_to_Title_pressed"]
[connection signal="button_down" from="Button2" to="." method="_on_restart_button_down"]

View File

@ -42,7 +42,7 @@ text = "Single Player"
[node name="Multiplayer" type="Button" parent="VBoxContainer"] [node name="Multiplayer" type="Button" parent="VBoxContainer"]
margin_right = 160.0 margin_right = 160.0
margin_bottom = 24.0 margin_bottom = 24.0
text = "Multiplayer" text = "Start"
[node name="Options" type="Button" parent="VBoxContainer"] [node name="Options" type="Button" parent="VBoxContainer"]
margin_top = 28.0 margin_top = 28.0

View File

@ -357,14 +357,27 @@ func listify_string(string):
res = string.split(' ', true, 0) res = string.split(' ', true, 0)
return res return res
# file_exists: checks if a file exists at path
# params: path: string denoting the path to a file
# returns: bool denoting file's presence at path
func file_exists(path):
var D = Directory.new()
return D.file_exists(path)
# Commands. All commands take in a parameter called command, # Commands. All commands take in a parameter called command,
# which contains a partially tokenized command # which contains a partially tokenized command
# start: Loads scene from res://scenes/*.tscn by filename, and starts it # start: Loads scene from res://scenes/*.tscn by filename, and starts it
func command_start (command): func command_start (command):
if command.size() > 1: if command.size() > 1:
var pack = load("res://scenes/%s.tscn" % command[1]) var path = "res://scenes/%s.tscn" % command[1]
get_pwn().add_child(pack.instance()); var pack = load(path) if file_exists(path) else null
debug_print_line("started '%s'\n" % command[1]) # Check if the resource was opened
if pack:
get_pwn().add_child(pack.instance());
debug_print_line("started '%s'\n" % command[1])
else:
debug_print_line("Path not found: %s\n" % "res://scenes/%s.tscn" % command[1])
else: else:
debug_print_line(get_usage(command[0])) debug_print_line(get_usage(command[0]))
@ -374,7 +387,7 @@ func command_kill (command):
var node = get_pwn().find_node(command[1], false, false) var node = get_pwn().find_node(command[1], false, false)
if node: if node:
if String(node.get_path()).match("*Debug*"): if String(node.get_path()).match("*Debug*"):
debug_print_line("YOU DIDN'T SAY THE MAGIC WORD!\n") debug_print_line("I'm sorry, Dave. I'm afraid I can't do that.\n")
else: else:
node.queue_free() node.queue_free()
debug_print_line("%s killed\n" % command[1]) debug_print_line("%s killed\n" % command[1])

View File

@ -25,7 +25,6 @@ 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("Forfeit Confirmation").get_ok().text = "Yes" get_node("Forfeit Confirmation").get_ok().text = "Yes"
get_node("Forfeit Confirmation").get_cancel().text = "No" get_node("Forfeit Confirmation").get_cancel().text = "No"
get_node("Forfeit Confirmation").get_ok().rect_min_size.x = 100 get_node("Forfeit Confirmation").get_ok().rect_min_size.x = 100
@ -78,7 +77,6 @@ remote func state_check(pos):
LOST: LOST:
# the other player wins # the other player wins
rpc("state_win", player.board.ship_data) rpc("state_win", player.board.ship_data)
victory_screen(null, false)
SUNK, HIT: SUNK, HIT:
# Hit # Hit
rpc("state_fire") rpc("state_fire")
@ -87,11 +85,19 @@ remote func state_check(pos):
state_fire() state_fire()
pass pass
# state_win: The winning state. If you reach here, someone's won. # state_win: The winning state. If you reach here, you've won.
# ships: The opponent's ship data, so that their board can be shown # ships: The opponent's ship data, so that their board can be shown
remote func state_win(ships): remote func state_win(ships):
victory_screen(ships) # Send ships back to the opponent:
pass rpc("state_lose", player.board.ship_data)
# Show the victory screen
victory_screen(ships, true)
# state_lose: The losing state. If you reach here, you've lost.
# ships: The opponent's ship data, so that their board can be shown
remote func state_lose(ships):
# Show the not-victory screen
victory_screen(ships, false)
# play_hit_sound: Play a hit sound depending on the severity of the hit # play_hit_sound: Play a hit sound depending on the severity of the hit
# value: Lost/Sunk/Hit/Miss # value: Lost/Sunk/Hit/Miss
@ -149,17 +155,22 @@ func _on_player_ready():
# victory_screen: display the victory screen # victory_screen: display the victory screen
func victory_screen(ships, winner = true): func victory_screen(ships, winner = true):
if winner: # Stop listening for opponent disconnections
# Hide the buttons Net.disconnect("disconnected", self, "connection_error")
get_node("Buttons").hide() # Hide the buttons
# Create a new Victory screen get_node("Buttons").hide()
var victory = Victory.instance() # Create a new Victory screen
# Give it the ships received from the opponent var victory = Victory.instance()
# Allow victory to end the game
victory.connect("end_game", self, "end")
# Tell victory whether we've won or lost
victory.set_win(winner)
# If we were given ships to display
if ships:
# Give victory the ships
victory.reveal_ships(ships) victory.reveal_ships(ships)
# Add victory to the scene tree # Add victory to the scene tree
add_child(victory) 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():
@ -168,6 +179,8 @@ func _on_Forfeit_pressed():
# end: end the Game # end: end the Game
sync func end(): sync func end():
# Return to the lobby
MessageBus.emit_signal("change_scene", "Multiplayer")
queue_free() queue_free()
@ -186,9 +199,10 @@ func _on_Forfeit_Confirmation_confirmed():
if Net.connected: if Net.connected:
# Send forfeit request to all users # Send forfeit request to all users
rpc("end") rpc("end")
end() else:
end()
func _on_Connection_Error_confirmed(): func _on_Connection_Error_confirmed():
# End the game # End the game
queue_free() end()

View File

@ -3,6 +3,9 @@ extends Control
# Path to Board class, for instantiating new Boards in code # Path to Board class, for instantiating new Boards in code
var Board = preload("res://scenes/Game/Board.tscn") var Board = preload("res://scenes/Game/Board.tscn")
# Sidnals
# request to return to lobby
signal end_game
# 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.
@ -14,17 +17,26 @@ func reveal_ships(ships:Array):
for ship in ships: for ship in ships:
board.callv("place_ship", ship) board.callv("place_ship", ship)
func set_win(won:bool):
var Text = find_node("Text")
if won:
Text.text = "You win!"
else:
Text.text = "You lose"
# 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):
# pass # pass
func _on_restart_button_down(): func _on_Restart_pressed():
AudioBus.emit_signal("button_clicked") AudioBus.emit_signal("button_clicked")
MessageBus.emit_signal("change_scene", "Multiplayer") emit_signal("end_game")
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_Title_pressed():
AudioBus.emit_signal("button_clicked") AudioBus.emit_signal("button_clicked")
# Disconnect from peer
Net.disconnect_host()
# Force return to title
MessageBus.emit_signal("return_to_title") MessageBus.emit_signal("return_to_title")

View File

@ -27,9 +27,19 @@ func set_IP_Address_text(show):
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")
# Let the player name default to hostname
name_popup.get_node("Name Entry").text = Net.get_hostname() name_popup.get_node("Name Entry").text = Net.get_hostname()
# Update the peers list
_on_peers_updated() _on_peers_updated()
pass # Set the keyboard-control focus to the first valid focus
find_next_valid_focus().grab_focus()
# Resume a connection, if coming to this screen from a connected state (i.e. "restart gane"
if Net.hosting:
# Show the host IP address
set_IP_Address_text(true)
if Net.connected:
# Show "Connected Options"
show_Connected_Options()
func show_Connected_Options(): func show_Connected_Options():
# [Hide]/Show the host options # [Hide]/Show the host options

View File

@ -62,8 +62,8 @@ func send(id, mail, mail_type = REPLY):
# Host # Host
# start_host: Host the game # start_host: Host the game
# port: TCP port # port: TCP port
# max_players: Largest number of players allowed to connect at a time # max_players: Largest number of players allowed to connect at a time (the host does not count)
func start_host(port = DEFAULT_PORT, max_players = 2): func start_host(port = DEFAULT_PORT, max_players = 1):
get_hostname() get_hostname()
peer_info[1] = local_info peer_info[1] = local_info
# Notify that peer list has updated # Notify that peer list has updated
@ -150,16 +150,10 @@ func _ready():
func _peer_connected(id): func _peer_connected(id):
# Send peer info to remote peer # Send peer info to remote peer
rpc_id(id, "register_peer", local_info) rpc_id(id, "register_peer", local_info)
if hosting and peer_info.size() >= 2:
pass
pass
func _peer_disconnected(id): func _peer_disconnected(id):
# Unregister the peer locally # Unregister the peer locally
unregister_peer(id) unregister_peer(id)
if hosting and peer_info.size() < 2:
pass
pass
func _host_connected(): func _host_connected():