Aims of this Tutorial
- Calculate health on client to show in info box.
- Find out the winner on the server and send it to the client.
- Have the client show the winner once the turn has ended.
Since we already wrote the code for calculating the damage players take, we simply need to copy that code onto the client. In order to get the health at the start of each turn however, we will get the server to tell us how much health each player has. This will help keep us in sync if there is a slight discrepancy. On the server we need to add the following code.
# Player.gd - send_all_plans(): end_health = self.health rpc("r_end_turn", time_of_death, end_translation, end_rotation, end_linear_velocity, end_rotational_velocity, end_health)
And then on the client we have:
# Player.gd - r_end_turn(): end_health = r_end_health # Player.gd - play_full_plan(): health = 100.0 # Player.gd - play_current_plan(): health = end_health
We now need to display this health on the info card. This is trivial since we made a function to update the data.
func display_data(player_obj : RigidBody) -> void: var text = "Name: " + player_obj.get_display_name() + "\n" text += "Position: " + get_human_readable_vector(player_obj.translation) + "\n" text += "Rotation: " + get_human_readable_vector(player_obj.rotation_degrees) + "\n" text += "Hull Integrity: " + String(player_obj.health) + "%\n" $InfoPanel/Label.text = text mouse_over_player = player_obj
Find out the Winner on the Server
In order to find out the winner, we can get every player to report their death to the arena, which will then check how many people have died and end the game appropriately. There will then be only one player left alive, who will be the winner. The players death function is now the following.
func die(time : float) -> void: is_alive = false if !has_died: if report_death_to_arena(): finish_game() has_died = true $CollisionShape.disabled = true time_of_death = time func report_death_to_arena() -> bool: return get_parent().register_death() func finish_game() -> void: get_parent().finish_game()
The arena will return true if there is only one player left, which will get the arena to end the game. Note the order in which we set
has_died. This is important because, when we find out the winner, we must not be
is_alive, but we cannot
has_died because then we would fail the first if statement. The complementary code on the arena is as follows.
func register_death() -> bool: deaths += 1 if deaths == len(players.keys()) -1: return true return false func finish_game() -> void: for player in players.values(): if player.is_alive: declare_winner(player) func declare_winner(player) -> void: var name = get_parent().name_dict[player.id] rpc("declare_winner", name)
Here we are checking our deaths counter (starts at 0), and if there are 1 less than the number of players, we have a winner. We then find the only player who is alive by looping through our dictionary, find their name, and announce it to all the clients.
If we were to simply announce the winner immediately, we would show the winner before we even play the plan. We therefore need to delay the announcement until after we have finished playing the plan. We need a new boolean to keep track of this for us,
was_plan_running, which lags behind
running_plan by one frame.
# Player.gd _physics_update(): if !running_plan: if is_local and was_running_plan: get_parent().show_winner() was_running_plan = false return was_running_plan = true
On the arena we have both the remote method the server calls and the
show_winner the local player calls.
remote func declare_winner(name : String) -> void: winner_name = "Victor: " + name $WinnerPanel/WinnerLabel.text = winner_name func show_winner() -> void: if winner_name != null: $WinnerPanel.visible = true
The default value of
winner_name is simply
null. This concludes this week's tutorial. Thank you all for sticking with me over this hiatus!