Skip to content

Commit

Permalink
Bump (#1)
Browse files Browse the repository at this point in the history
* fix inventory in diablo_like example (OctoD#21)

Co-authored-by: sergwest <ksendzov.sd@gmail.com>

* chore: updates gitattributes

* chore: adds import file

* feat: adds hooks methods in Interaction (OctoD#22)

feat: adds on_before_interaction_end
feat: adds on_before_interaction_start
chore: updates sot-like example with draggable object

* chore: adds a line at the end so godot stop complaining

* Fix icon paths for attribute/ability resources/nodes (OctoD#24)

* fix icon paths missing "attributes_and_abilities"
* add available icons to ability nodes/resources

* fix abilitycontainer _ready missing preloaded abilities (OctoD#25)

* fix: fixes OctoD#26

* fix: adds condition

* chore(demo): adds example on how to use tags_to_display

* fix: closes OctoD#23 (OctoD#28)

* feat: adds _on_interaction_started and _on_interaction_ended calls (OctoD#29)

this occurs when an interactable_area is interacted and if the methods are implemented

(look at sot-like example)

* Feat/2d-and-3d-point-and-click-controllers (OctoD#30)

* feat: adds point_and_click_2d

* feat: adds PointAndClick3D node

* fix: fixes typo in tags_updated signal parameter name

* chore(docs): adds docs

* feat: adds radial menu (OctoD#31)

* chore: removes commented duped line

* feat: adds RadialMenuContainer icon

* chore: updates readme

* feat: adds initial camera shake implementation (OctoD#33)

* fix: closes OctoD#34. Thank you @Hairic95

* feat: adds SlideShow node for your game intros (OctoD#35)

* chore: migrates from 4.0 to 4.1

* fix: fixes test

* fix: fixes esc menu button

* chore: updates IDEAS.md

* feat: adds get_attributes_dict method

* Feat/turn-based-nodes (OctoD#36)

* feat: adds turn based nodes

* Update README.md (OctoD#42)

* fix: closes OctoD#39 (OctoD#41)

* chore: updates docs

* chore: adds requirement for reproj

---------

Co-authored-by: Sergwest <39670193+AFK1@users.noreply.github.com>
Co-authored-by: sergwest <ksendzov.sd@gmail.com>
Co-authored-by: octod <iamoctod@gmail.com>
Co-authored-by: OctoD <OctoD@users.noreply.github.com>
Co-authored-by: Daniel Bernard <daniel.john.bernard@gmail.com>
  • Loading branch information
6 people committed Oct 17, 2023
1 parent b6db627 commit f5ac792
Show file tree
Hide file tree
Showing 97 changed files with 2,404 additions and 156 deletions.
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
.github export-ignore
.gitignore export-ignore
.gut_editor_config.json export-ignore
.gut_editor_shortcuts.cfg export-ignore
addons/gut export-ignore
CHANGELOG.md export-ignore
CODE_OF_CONDUCT.md export-ignore
docs export-ignore
examples export-ignore
icon.svg export-ignore
icon.svg.import export-ignore
icons.sketch export-ignore
IDEAS.md export-ignore
LICENCE export-ignore
README.md export-ignore
test export-ignore
Expand Down
3 changes: 3 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ If applicable, add screenshots to help explain your problem.

**Additional context**
Add any other context about the problem here.

**Reproducible project**
Attach a reproducibile project. Otherwise I'll look into the issue when I can.
4 changes: 2 additions & 2 deletions .gut_editor_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@
"post_run_script": "",
"pre_run_script": "",
"prefix": "test_",
"selected": null,
"selected": "test_gameplay_attribute_map.gd",
"should_exit": true,
"should_exit_on_success": false,
"should_maximize": false,
"show_help": false,
"suffix": ".gd",
"tests": [],
"unit_test_name": null
"unit_test_name": "test_get_attributes_dict"
}
9 changes: 9 additions & 0 deletions IDEAS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,12 @@

- User interface basic controls for inventories, ability systems, character attributes.
- Global tag system with custom inspector editors so you can use global tags to tweak items, abilities and inventory/equipment.
- Global editor for
- items
- abilities
- attributes
- inventories
- equipment

this editor loads all resources by type, let you edit them and save them back to disk.
You can also filter by tags and search by name.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Godot Gameplay Systems (formerly godot gameplay attributes) is a set of nodes an
- [⚔️ GGS ⚔️](#️-ggs-️)
- [Install](#install)
- [How does it work?](#how-does-it-work)
- [Networking Attributes, Effects and Abilities](#networking-attributes-effects-and-abilities)
- [Networking and multiplayer](#networking-attributes-effects-and-abilities)
- [Contribution](#contribution)
- [Licence](#licence)

Expand All @@ -16,6 +16,8 @@ Clone this repo, copy the `addons` folder content inside your project's `addons`

Enable this plugin by going to `project settings/plugins`.

> Important: reload the project after activating the plugin
Enjoy!

## How does it work?
Expand All @@ -26,7 +28,7 @@ I am making also small "demos" to demonstrate how things work. You can check the

`Esc` key will always return you to the main menu.

# Networking Attributes, Effects and Abilities
# Networking and multiplayer

Did not test too much, but abilities, effects and attributes could be replicated using the `MultiplayerSynchronizer` node provided by godot4.x.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
## [br][Ability] resources can be activated, cancelled or finished under some circumstances and are used to apply
## [GameplayEffect] resources to [GameplayAttributeMap] instances.

@icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer.svg")

class_name AbilityContainer extends Node

Expand Down Expand Up @@ -38,7 +39,7 @@ signal cooldown_ended(ability: Ability)
## Emitted when an ability cooldown started
signal cooldown_started(ability: Ability)
## Emitted when tags are updated
signal tags_updated(updaated_tags: Array[String], previous_tags: Array[String])
signal tags_updated(updated_tags: Array[String], previous_tags: Array[String])


@export_category("Abilities")
Expand Down Expand Up @@ -107,7 +108,7 @@ func _get_cooldown_timer(ability: Ability) -> Timer:
## Handles the [signal Ability.activated] signal
## [br]It's called internally by the current [AbilityContainer], so you should not call it.
func _handle_ability_activated(ability: Ability, activation_event: ActivationEvent) -> void:
if not active:
if not _is_eligible_for_operation(activation_event):
return

_handle_lifecycle_tagging(LifeCycle.Activated, ability)
Expand All @@ -130,7 +131,7 @@ func _handle_ability_activated(ability: Ability, activation_event: ActivationEve
## Handles the [signal Ability.blocked] signal.
## [br]It's called internally by the current [AbilityContainer], so you should not call it.
func _handle_ability_blocked(ability: Ability, activation_event: ActivationEvent) -> void:
if not active:
if not _is_eligible_for_operation(activation_event):
return

ability_blocked.emit(ability, activation_event)
Expand All @@ -140,7 +141,7 @@ func _handle_ability_blocked(ability: Ability, activation_event: ActivationEvent
## Handles the [signal Ability.cancelled] signal
## [br]It's called internally by the current [AbilityContainer], so you should not call it.
func _handle_ability_cancelled(ability: Ability, activation_event: ActivationEvent) -> void:
if not active:
if not _is_eligible_for_operation(activation_event):
return

_handle_lifecycle_tagging(LifeCycle.Cancelled, ability)
Expand All @@ -151,7 +152,7 @@ func _handle_ability_cancelled(ability: Ability, activation_event: ActivationEve
## Handles the [signal Ability.ended] signal
## [br]It's called internally by the current [AbilityContainer], so you should not call it.
func _handle_ability_ended(ability: Ability, activation_event: ActivationEvent) -> void:
if not active:
if not _is_eligible_for_operation(activation_event):
return

_handle_lifecycle_tagging(LifeCycle.Ended, ability)
Expand Down Expand Up @@ -192,13 +193,15 @@ func _handle_lifecycle_tagging(lifecycle: LifeCycle, ability: Ability) -> void:
return


## Returns [code]true[/code] if the [AbilityContainer] can process and [ActivationEvent], [code]false[/code] otherwise.
func _is_eligible_for_operation(activation_event: ActivationEvent) -> bool:
return activation_event.ability_container == self and active


## The [method Node._ready] override
func _ready() -> void:
gameplay_attribute_map = get_node(gameplay_attribute_map_path)

for i in abilities.size():
var ability = abilities[i - 1]
grant(ability)
grant_all_abilities()


## Activates a single [Ability] calling [method Ability.try_activate].
Expand Down Expand Up @@ -351,18 +354,19 @@ func filter_abilities(predicate: Callable, includes_ungranted = false) -> Array[

## Gives an [Ability] at runtime
## If the [Ability] has already been granted, it will be ignored silently
func grant(ability: Ability) -> void:
## [br]Returns [code]true[/code] if the ability has been granted, [code]false[/code] otherwise
func grant(ability: Ability) -> bool:
# It's not active, maybe the owner is dead or on holiday
if not active:
return
return false

# Obviously skip granting if ability is null
if ability == null:
return
return false

# Skips if cannot be granted
if not can_grant(ability):
return
return false

# Removes from abilities array if it's there. This avoids duplication which could lead to bugs.
var ability_index = abilities.find(ability)
Expand Down Expand Up @@ -394,6 +398,28 @@ func grant(ability: Ability) -> void:
# Emits grant signal, so UI/parent nodes can do stuff with it
ability_granted.emit(ability)

# Returns true, so the caller knows the ability has been granted
return true


## Grants many [Ability] at runtime
## If an [Ability] is granted, it is removed from the [member AbilityContainer.abilities] array and added to the [member AbilityContainer.granted_abilities] array.
## If an [Ability] has already been granted, it will be ignored silently
## [br]Returns [code]int[/code] the number of abilities granted
func grant_all_abilities() -> int:
var granted = 0
var cursor = -1

for i in abilities.size():
var ability = abilities[cursor]

if grant(ability):
granted += 1
else:
cursor -= 1

return granted


## Returns [code]true[/code] if has an [Ability] which satisfies the [Callable] predicate, [code]false[/code] otherwise
func has_ability(predicate: Callable, includes_ungranted = false) -> bool:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,14 @@ func get_attribute_by_name(attribute_name: String) -> AttributeSpec:

return null


## Gets all attributes as a dictionary
## [br]The dictionary keys are the attribute names and the values are the current buffed value of the attribute.
func get_attributes_dict() -> Dictionary:
var keys = _attributes_dict.keys()
var out = {} as Dictionary

for key in keys:
var attr = get_attribute_by_name(key)
out[key] = attr.current_buffed_value
return out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@icon("res://addons/godot_gameplay_systems/assets/GameplayEffect.svg")
@icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect.svg")
@tool
class_name GameplayEffect extends Node

Expand Down
40 changes: 20 additions & 20 deletions addons/godot_gameplay_systems/attributes_and_abilities/plugin.gd
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ const EFFECTED_AREA2D = "EffectedArea2D"
const EFFECTED_AREA3D = "EffectedArea3D"
const STOP_EFFECT_IF0_RESOURCE_NAME = "StopEffectIfAttributeIs0"

const attribute_spec_script = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/objects/attribute_spec.gd")
const ability_container_resource = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/nodes/ability_container.gd")
const activation_event_script = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/objects/activation_event.gd")
const attributes_table_resource = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute_table.gd")
const attribute_resource = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute.gd")
const attribute_effect_resource = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute_effect.gd")
const attribute_effect_condition_resource = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute_effect_condition.gd")
const effected_area2d = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/nodes/effected_area2d.gd")
const effected_area3d = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/nodes/effected_area3d.gd")
const gameplay_effect = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/nodes/gameplay_effect.gd")
const gameplay_attribute_map = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/nodes/gameplay_attribute_map.gd")
const stop_effect_if0_resource = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/resources/stop_effect_if_0.gd")
const attribute_spec_script = preload("./objects/attribute_spec.gd")
const ability_container_resource = preload("./nodes/ability_container.gd")
const activation_event_script = preload("./objects/activation_event.gd")
const attributes_table_resource = preload("./resources/attribute_table.gd")
const attribute_resource = preload("./resources/attribute.gd")
const attribute_effect_resource = preload("./resources/attribute_effect.gd")
const attribute_effect_condition_resource = preload("./resources/attribute_effect_condition.gd")
const effected_area2d = preload("./nodes/effected_area2d.gd")
const effected_area3d = preload("./nodes/effected_area3d.gd")
const gameplay_effect = preload("./nodes/gameplay_effect.gd")
const gameplay_attribute_map = preload("./nodes/gameplay_attribute_map.gd")
const stop_effect_if0_resource = preload("./resources/stop_effect_if_0.gd")

const attribute_inspector_plugin_script = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/gameplay_attribute_map_inspector_plugin.gd")
const effect_inspector_plugin_script = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/gameplay_effect_inspector_plugin.gd")
const attribute_inspector_plugin_script = preload("./inspector/gameplay_attribute_map_inspector_plugin.gd")
const effect_inspector_plugin_script = preload("./inspector/gameplay_effect_inspector_plugin.gd")


var attribute_inspector_plugin: EditorInspectorPlugin
Expand All @@ -39,17 +39,17 @@ func _enter_tree():
add_custom_type(ACTIVATION_EVENT_NAME, "RefCounted", activation_event_script, null)
add_custom_type(ATTRIBUTE_SPEC_NAME, "RefCounted", attribute_spec_script, null)

add_custom_type(ATTRIBUTE_RESOURCE_NAME, "Resource", attribute_resource, preload("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute@0.15x.png"))
add_custom_type(ATTRIBUTE_TABLE_RESOURCE_NAME, "Resource", attributes_table_resource, preload("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable@0.15x.png"))
add_custom_type(ATTRIBUTE_EFFECT_RESOURCE_NAME, "Resource", attribute_effect_resource, preload("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect@0.15x.png"))
add_custom_type(ATTRIBUTE_RESOURCE_NAME, "Resource", attribute_resource, preload("./assets/Attribute@0.15x.png"))
add_custom_type(ATTRIBUTE_TABLE_RESOURCE_NAME, "Resource", attributes_table_resource, preload("./assets/AttributeTable@0.15x.png"))
add_custom_type(ATTRIBUTE_EFFECT_RESOURCE_NAME, "Resource", attribute_effect_resource, preload("./assets/GameplayEffect@0.15x.png"))
add_custom_type(ATTRIBUTE_EFFECT_CONDITION_RESOURCE_NAME, "Resource", attribute_effect_condition_resource, null)
add_custom_type(GAMEPLAY_ATTRIBUTE_MAP_NAME, "Node", gameplay_attribute_map, preload("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap@0.15x.png"))
add_custom_type(GAMEPLAY_EFFECT_NAME, "Node", gameplay_effect, preload("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect@0.15x.png"))
add_custom_type(GAMEPLAY_ATTRIBUTE_MAP_NAME, "Node", gameplay_attribute_map, preload("./assets/GameplayAttributeMap@0.15x.png"))
add_custom_type(GAMEPLAY_EFFECT_NAME, "Node", gameplay_effect, preload("./assets/GameplayEffect@0.15x.png"))
add_custom_type(EFFECTED_AREA2D, "Area2D", effected_area2d, null)
add_custom_type(EFFECTED_AREA3D, "Area3D", effected_area3d, null)
add_custom_type(STOP_EFFECT_IF0_RESOURCE_NAME, ATTRIBUTE_EFFECT_CONDITION_RESOURCE_NAME, stop_effect_if0_resource, null)

add_custom_type(ABILITY_CONTAINER_NAME, "Node", ability_container_resource, preload("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer@0.15x.png"))
add_custom_type(ABILITY_CONTAINER_NAME, "Node", ability_container_resource, preload("./assets/AbilityContainer@0.15x.png"))

attribute_inspector_plugin = attribute_inspector_plugin_script.new()
effect_inspector_plugin = effect_inspector_plugin_script.new()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability.svg")
class_name Ability extends Resource

## Represents an ability or skill
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@icon("res://addons/godot_gameplay_systems/assets/AttributeEffect.svg")
@icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect.svg")

class_name AttributeEffect extends Resource

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@icon("res://addons/godot_gameplay_systems/assets/AttributeTable.svg")
@icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable.svg")

class_name AttributeTable extends Resource

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,84 @@ func test_activate_many() -> void:
b.activate_many(true)

assert_signal_emit_count(b, "ability_activated", 2, "ability_activated called twice")


func test_granting_issue_23() -> void:
# refers to issue #23 https://github.com/OctoD/godot-gameplay-systems/issues/23

var ability000 = Ability000.new("000")
var ability001 = Ability000.new("001")
var ability002 = Ability000.new("002")
var ability003 = Ability000.new("003")
var ability004 = Ability000.new("004")
var ability005 = Ability000.new("005")

ability001.grant_tags_required.append("cannot be granted at all")
ability004.grant_tags_required.append("cannot be granted at all")

var container = _make([ability000, ability001, ability002, ability003, ability004, ability005])

container.grant(ability000)
container.grant(ability001)
container.grant(ability002)
container.grant(ability003)
container.grant(ability004)
container.grant(ability005)

assert_eq(container.granted_abilities.has(ability000), true, "ability000 should have been granted")
assert_eq(container.granted_abilities.has(ability002), true, "ability002 should have been granted")
assert_eq(container.granted_abilities.has(ability001), false, "ability001 should not have been granted")
assert_eq(container.granted_abilities.has(ability003), true, "ability003 should have been granted")
assert_eq(container.granted_abilities.has(ability004), false, "ability004 should not have been granted")
assert_eq(container.granted_abilities.has(ability005), true, "ability005 should have been granted")

assert_eq(container.abilities.has(ability000), false, "ability000 should not be there since it's granted")
assert_eq(container.abilities.has(ability002), false, "ability002 should not be there since it's granted")
assert_eq(container.abilities.has(ability001), true, "ability001 should be there since it's not granted")
assert_eq(container.abilities.has(ability003), false, "ability003 should be there since it's not granted")
assert_eq(container.abilities.has(ability004), true, "ability004 should be there since it's not granted")
assert_eq(container.abilities.has(ability005), false, "ability005 should not be there since it's granted")


# it should reach this point to avoid that possible bug about not granting abilities at all if one is not grantable


class _test_Enemy_hairic95 extends Node2D:
var ability_container: AbilityContainer

func _init(ability: Ability000) -> void:
ability_container = AbilityContainer.new()
add_child(ability_container)
ability_container.grant(ability)


func fire() -> void:
ability_container.activate_many()


func test_hairic95_issue() -> void:
var ability000 = Ability000.new("000")

ability000.tags_activation.append("started")

var enemy000 = _test_Enemy_hairic95.new(ability000)
var enemy001 = _test_Enemy_hairic95.new(ability000)
var enemy002 = _test_Enemy_hairic95.new(ability000)
var enemies = [enemy000, enemy001, enemy002]

add_child_autofree(enemy000)
add_child_autofree(enemy001)
add_child_autofree(enemy002)

enemy000.get("fire").call()

assert_eq(enemy000.ability_container.tags.has("started"), true, "Ability container should have the tag")
assert_eq(enemy001.ability_container.tags.has("started"), false, "Ability container should have the tag")
assert_eq(enemy002.ability_container.tags.has("started"), false, "Ability container should have the tag")

for e in enemies:
e.get("fire").call()

assert_eq(enemy000.ability_container.tags.has("started"), true, "Ability container should have the tag")
assert_eq(enemy001.ability_container.tags.has("started"), true, "Ability container should have the tag")
assert_eq(enemy002.ability_container.tags.has("started"), true, "Ability container should have the tag")
Loading

0 comments on commit f5ac792

Please sign in to comment.