Skip to content

Commit

Permalink
Feat/turn-based-nodes (#36)
Browse files Browse the repository at this point in the history
* feat: adds turn based nodes
  • Loading branch information
OctoD authored Sep 11, 2023
1 parent 5f3ef26 commit 63ae822
Show file tree
Hide file tree
Showing 14 changed files with 512 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ var can_interact: bool:
if not tags.has(tag):
return false

return focused_interactable != null
if focused_interactable != null:
return tags_blocking_interaction.size() == 0 and tags_required_to_interact.size() == 0

return false
## Is the interacting owner. Usually a [CharacterBody2D] or [CharacterBody3D].
var interacting_owner: Node:
get:
Expand Down
5 changes: 5 additions & 0 deletions addons/godot_gameplay_systems/plugin.gd
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const extended_character_nodes_script = preload("res://addons/godot_gameplay_sys
const inventory_system_script = preload("res://addons/godot_gameplay_systems/inventory_system/plugin.gd")
const interactables_script = preload("res://addons/godot_gameplay_systems/interactables/plugin.gd")
const slideshow_script = preload("res://addons/godot_gameplay_systems/slideshow/plugin.gd")
const turn_based_script = preload("res://addons/godot_gameplay_systems/turn_based/plugin.gd")


var attributes_and_abilities_plugin: EditorPlugin
Expand All @@ -15,6 +16,7 @@ var extended_character_nodes: EditorPlugin
var inventory_system: EditorPlugin
var interactables: EditorPlugin
var slideshow: EditorPlugin
var turn_based: EditorPlugin


func _init() -> void:
Expand All @@ -24,6 +26,7 @@ func _init() -> void:
inventory_system = inventory_system_script.new()
interactables = interactables_script.new()
slideshow = slideshow_script.new()
turn_based = turn_based_script.new()


func _enter_tree():
Expand All @@ -33,6 +36,7 @@ func _enter_tree():
inventory_system._enter_tree()
interactables._enter_tree()
slideshow._enter_tree()
turn_based._enter_tree()


func _exit_tree():
Expand All @@ -42,3 +46,4 @@ func _exit_tree():
inventory_system._exit_tree()
interactables._exit_tree()
slideshow._exit_tree()
turn_based._exit_tree()
36 changes: 36 additions & 0 deletions addons/godot_gameplay_systems/turn_based/autoloads/turn_manager.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
extends Node


## The [TurnBasedGame] node.
var turn_based_game: TurnBasedGame = null


## Called when the node enters the scene tree for the first time.
func _ready() -> void:
get_turn_based_game()


## Ends the current turn sequence.
func end_turn_sequence() -> void:
if get_turn_based_game():
get_turn_based_game().end_turn_sequence()


## Gets the [TurnBasedGame] node.
func get_turn_based_game() -> TurnBasedGame:
if turn_based_game:
return turn_based_game

for child in get_tree().get_nodes_in_group("ggs.turnbased"):
if child is TurnBasedGame:
turn_based_game = child
return turn_based_game

return null


## Starts a new turn sequence (aka, starts the turn based mode).
func start_turn_sequence() -> void:
if get_turn_based_game():
get_turn_based_game().start_turn_sequence()

124 changes: 124 additions & 0 deletions addons/godot_gameplay_systems/turn_based/nodes/TurnBasedGame.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
class_name TurnBasedGame extends Node


## Emitted when a turn changes.
signal turn_changed(manager: TurnBasedGame)
## Emitted when the turn based game starts.
signal turn_game_started()
## Emitted when the turn based game stops.
signal turn_game_stopped()
## Emitted when a subscriber is added.
signal subscriber_added(subscriber: TurnSubscriber)
## Emitted when a subscriber is removed.
signal subscriber_removed(subscriber: TurnSubscriber)


## The current turn.
var current_turn: int = 0
## The current turn subscriber.
var current_turn_subscriber: TurnSubscriber:
get:
if subscribers.size() == 0:
return null

return subscribers[current_turn]
## The turn subscribers.
var subscribers: Array[TurnSubscriber] = []


func _ready() -> void:
add_to_group("ggs.turnbased")


func _sort_subscribers() -> void:
subscribers.sort_custom(func (a, b):
return a.priority < b.priority
)


## Called when a [TurnSubscriber] is subscribed.
## [br]This is a virtual method
func _subscriber_added(sub: TurnSubscriber) -> void:
pass


## Called when a [TurnSubscriber] is unsubscribed.
## [br]This is a virtual method
func _subscriber_removed(sub: TurnSubscriber) -> void:
pass


## Adds a [TurnSubscriber]
func add_subscriber(sub: TurnSubscriber) -> bool:
if subscribers.has(sub):
return false

subscribers.append(sub)
subscriber_added.emit(sub)

_sort_subscribers()

return true


## Ends the current turn
func end_turn_sequence() -> void:
if subscribers.size() == 0:
return

subscribers[current_turn].end_turn()

current_turn += 1

if current_turn >= subscribers.size():
current_turn = 0

subscribers[current_turn].turn_started.emit()
subscribers[current_turn]._turn_started()

turn_game_stopped.emit()


## Calls next turn
func next_turn() -> void:
var subscribers_count := subscribers.size()

if current_turn < subscribers_count:
subscribers[current_turn].turn_ended.emit()
subscribers[current_turn]._turn_ended()

current_turn += 1

if current_turn >= subscribers_count:
current_turn = 0

subscribers[current_turn].turn_started.emit()
subscribers[current_turn]._turn_started()


## Removes a [TurnSubscriber]
func remove_subscriber(sub: TurnSubscriber) -> bool:
if not subscribers.has(sub):
return false

var sub_turn_index = subscribers.find(func (x): return x == sub)

subscribers.remove_at(sub_turn_index)
subscriber_removed.emit(sub)

_sort_subscribers()

return true


## Starts the turn based game.
func start_turn_sequence() -> void:
if subscribers.size() == 0:
return

current_turn = 0

subscribers[current_turn]._turn_started()
subscribers[current_turn].turn_started.emit()

turn_game_started.emit()
54 changes: 54 additions & 0 deletions addons/godot_gameplay_systems/turn_based/nodes/TurnSubscriber.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
class_name TurnSubscriber extends Node

## A turn based participant
##
## It represents a scene which can partecipate to a turn based game


## Emitted when this subscriber turn ends
signal turn_ended()
## Emitted when this subscriber turn starts
signal turn_started()
## Emitter when the turn round ended,
signal turn_round_ended()


@export_group("Turn based game")
## Priority of this subscriber. The higher the priority, the sooner the turn starts.
@export var priority: int = 0


func _enter_tree() -> void:
if TurnManager.get_turn_based_game() != null:
TurnManager.get_turn_based_game().add_subscriber(self)


func _exit_tree() -> void:
if TurnManager.get_turn_based_game() != null:
TurnManager.get_turn_based_game().remove_subscriber(self)


func _ready() -> void:
add_to_group("ggs.turnbased")

if TurnManager.get_turn_based_game() != null:
TurnManager.get_turn_based_game().add_subscriber(self)


## Called when the turn round ended
## [br]It's a virtual method, it can be overrided
func _turn_ended() -> void:
pass


## Called when the turn round started
## [br]It's a virtual method, it can be overrided
func _turn_started() -> void:
pass


## Ends the turn of this subscriber
func end_turn() -> void:
if TurnManager.get_turn_based_game() != null:
TurnManager.get_turn_based_game().next_turn()

18 changes: 18 additions & 0 deletions addons/godot_gameplay_systems/turn_based/plugin.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
extends EditorPlugin


const TurnBasedGameScript = preload("res://addons/godot_gameplay_systems/turn_based/nodes/TurnBasedGame.gd")
const TurnSubscriberScript = preload("res://addons/godot_gameplay_systems/turn_based/nodes/TurnSubscriber.gd")


func _enter_tree() -> void:
add_autoload_singleton("TurnManager", "res://addons/godot_gameplay_systems/turn_based/autoloads/turn_manager.gd")
add_custom_type("TurnBasedGame", "Node", TurnBasedGameScript, null)
add_custom_type("TurnSubscriber", "Node", TurnSubscriberScript, null)


func _exit_tree() -> void:
remove_autoload_singleton("TurnManager")
remove_custom_type("TurnBasedGame")
remove_custom_type("TurnSubscriber")

30 changes: 30 additions & 0 deletions docs/turn-based-nodes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Turn based nodes
================

Turn based nodes are nodes that can be used to create turn based games.

To start adding functionalities to your game, you can add a `TurnBasedGame` node to your scene. This node should be at the highest level of your hierarchy. It will be used to manage the turns properly.

Then you should add a `TurnSubscriber` to each node which should be notified when a turn starts or ends.

This node has a priority property. The nodes with the highest priority will start their turns first. If two nodes have the same priority, the one that was added first will start its turn first.

To get the `TurnBasedGame` node, you have to use the singleton `TurnManager`.

This has a method which returns the `TurnBasedGame` node. You can use it like this:

```gdscript
# To start a turn sequence
var turn_based_game = TurnManager.get_turn_based_game()
turn_based_game.start_turn_sequence();
# To end a turn sequence
turn_based_game.end_turn_sequence();
```

Each `TurnSubscriber` can terminate it's own turn by calling their `end_turn` method.

```gdscript
# To end a turn
self.end_turn();
```
6 changes: 6 additions & 0 deletions examples/examples_menu.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ text = "SOT-like (sail management)"
script = ExtResource("3_jxaps")
scene_to_load = "res://examples/sot_like/sot_game.tscn"

[node name="TurnBasedTopDown" type="Button" parent="ExamplesMenu/ExampleButtons"]
layout_mode = 2
text = "SOT-like (sail management)"
script = ExtResource("3_jxaps")
scene_to_load = "res://examples/turn_based_top_down/turn_based_top_down.tscn"

[node name="HSeparator" type="HSeparator" parent="ExamplesMenu"]
custom_minimum_size = Vector2(2.08165e-12, 50)
layout_mode = 2
Expand Down
30 changes: 30 additions & 0 deletions examples/turn_based_top_down/turn_based_player.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
extends CharacterBody3D


@export var turn_priority: int = 0
@onready var point_and_click_3d: PointAndClick3D = $PointAndClick3D
@onready var turn_subscriber: TurnSubscriber = $TurnSubscriber
@onready var current_turn_indicator: MeshInstance3D = $CurrentTurnIndicator


func _ready() -> void:
turn_subscriber.priority = turn_priority
current_turn_indicator.visible = false

set_process_input(false)

turn_subscriber.turn_started.connect(func ():
set_process_input(true)
current_turn_indicator.visible = true
)

turn_subscriber.turn_ended.connect(func ():
set_process_input(false)
current_turn_indicator.visible = false
)


func _input(event: InputEvent) -> void:
if event.is_action_pressed("diablo_like_move_to"):
point_and_click_3d.set_new_movement_position()

Loading

0 comments on commit 63ae822

Please sign in to comment.