├── addons
└── godot_gameplay_systems
│ ├── inventory_system
│ ├── assets
│ │ ├── DropIcon.png
│ │ ├── ItemIcon.png
│ │ ├── DropNode2D.png
│ │ ├── DropNode3D.png
│ │ ├── DropGroupIcon.png
│ │ ├── DropTableIcon.png
│ │ ├── EquipmentIcon.png
│ │ ├── Equipped3DIcon.png
│ │ ├── InventoryIcon.png
│ │ ├── RadialMenuIcon.png
│ │ ├── DropIcon.png.import
│ │ ├── ItemIcon.png.import
│ │ ├── DropNode2D.png.import
│ │ ├── DropNode3D.png.import
│ │ ├── DropGroupIcon.png.import
│ │ ├── DropTableIcon.png.import
│ │ ├── EquipmentIcon.png.import
│ │ ├── InventoryIcon.png.import
│ │ ├── Equipped3DIcon.png.import
│ │ └── RadialMenuIcon.png.import
│ ├── nodes
│ │ ├── drop_3d.gd
│ │ ├── drop_2d.gd
│ │ ├── radial_menu_container.gd
│ │ ├── pickable_item_2d.gd
│ │ ├── pickable_item_3d.gd
│ │ ├── equipped_item_3d.gd
│ │ ├── drop.gd
│ │ └── inventory.gd
│ ├── resources
│ │ ├── drop_table.gd
│ │ ├── drop_group.gd
│ │ ├── equipment_slot.gd
│ │ └── item.gd
│ ├── objects
│ │ └── item_activation_event.gd
│ └── plugin.gd
│ ├── interactables
│ ├── assets
│ │ ├── Interaction2DIcon.png
│ │ ├── Interaction3DIcon.png
│ │ ├── InteractionIcon.png
│ │ ├── InteractableArea2D.png
│ │ ├── InteractableArea3D.png
│ │ ├── InteractionRayCast2DIcon.png
│ │ ├── InteractionRayCast3DIcon.png
│ │ ├── InteractionIcon.png.import
│ │ ├── Interaction2DIcon.png.import
│ │ ├── Interaction3DIcon.png.import
│ │ ├── InteractableArea2D.png.import
│ │ ├── InteractableArea3D.png.import
│ │ ├── InteractionRayCast2DIcon.png.import
│ │ └── InteractionRayCast3DIcon.png.import
│ ├── nodes
│ │ ├── 2d
│ │ │ ├── interactable_area_2d.gd
│ │ │ └── interaction_raycast_2d.gd
│ │ ├── 3d
│ │ │ ├── interactable_area_3d.gd
│ │ │ └── interaction_raycast_3d.gd
│ │ └── interaction_manager.gd
│ ├── plugin.gd
│ └── resources
│ │ └── interaction.gd
│ ├── attributes_and_abilities
│ ├── assets
│ │ ├── Ability@0.15x.png
│ │ ├── Attribute@0.15x.png
│ │ ├── TimedEffect@0.15x.png
│ │ ├── AttributeEffect@0.15x.png
│ │ ├── AttributeTable@0.15x.png
│ │ ├── GameplayAttributes.sketch
│ │ ├── GameplayEffect@0.15x.png
│ │ ├── AbilityContainer@0.15x.png
│ │ ├── GameplayAttributeMap@0.15x.png
│ │ ├── TimedEffectOneShot@0.15x.png
│ │ ├── Attribute.svg
│ │ ├── AttributeEffect.svg
│ │ ├── TimedEffect.svg
│ │ ├── Ability@0.15x.png.import
│ │ ├── Attribute@0.15x.png.import
│ │ ├── TimedEffect@0.15x.png.import
│ │ ├── AttributeTable@0.15x.png.import
│ │ ├── GameplayEffect@0.15x.png.import
│ │ ├── AbilityContainer@0.15x.png.import
│ │ ├── AttributeEffect@0.15x.png.import
│ │ ├── TimedEffectOneShot@0.15x.png.import
│ │ ├── GameplayAttributeMap@0.15x.png.import
│ │ ├── GameplayAttributeMap.svg
│ │ ├── Ability.svg.import
│ │ ├── Attribute.svg.import
│ │ ├── TimedEffect.svg.import
│ │ ├── AttributeTable.svg.import
│ │ ├── GameplayEffect.svg.import
│ │ ├── AttributeEffect.svg.import
│ │ ├── AbilityContainer.svg.import
│ │ ├── TimedEffectOneShot.svg.import
│ │ ├── GameplayAttributeMap.svg.import
│ │ ├── AttributeTable.svg
│ │ ├── TimedEffectOneShot.svg
│ │ ├── GameplayEffect.svg
│ │ ├── Ability.svg
│ │ └── AbilityContainer.svg
│ ├── resources
│ │ ├── stop_effect_if_0.gd
│ │ ├── attribute_table.gd
│ │ ├── attribute_effect_condition.gd
│ │ ├── attribute.gd
│ │ ├── stop_effect_at_value.gd
│ │ ├── attribute_effect.gd
│ │ └── ability.gd
│ ├── nodes
│ │ ├── gameplay_effect.gd
│ │ ├── effected_area2d.gd
│ │ ├── effected_area3d.gd
│ │ └── gameplay_attribute_map.gd
│ ├── debug
│ │ ├── abilities_debugger.tscn
│ │ └── abilities_debugger.gd
│ ├── inspector
│ │ ├── gameplay_effect_inspector_plugin.gd
│ │ ├── gameplay_attribute_map_inspector_plugin.gd
│ │ ├── components
│ │ │ ├── attribute_editor_row.gd
│ │ │ ├── attribute_editor_row.tscn
│ │ │ ├── attribute_effect_editor_row.tscn
│ │ │ └── attribute_effect_editor_row.gd
│ │ ├── attribute_editor.gd
│ │ └── attribute_effect_editor.gd
│ ├── objects
│ │ ├── activation_event.gd
│ │ └── attribute_spec.gd
│ └── plugin.gd
│ ├── camera_shake
│ ├── plugin.gd
│ └── nodes
│ │ └── camera_shake.gd
│ ├── plugin.cfg
│ ├── slideshow
│ ├── plugin.gd
│ └── slide_show.gd
│ ├── extended_character_nodes
│ ├── plugin.gd
│ └── nodes
│ │ ├── 2d
│ │ └── point_and_click_2d.gd
│ │ └── 3d
│ │ └── point_and_click_3d.gd
│ ├── turn_based
│ ├── plugin.gd
│ ├── autoloads
│ │ └── turn_manager.gd
│ └── nodes
│ │ ├── TurnSubscriber.gd
│ │ └── TurnBasedGame.gd
│ └── plugin.gd
├── LICENSE
└── project.godot
/addons/godot_gameplay_systems/inventory_system/assets/DropIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/DropIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/ItemIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/ItemIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/DropNode2D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/DropNode2D.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/DropNode3D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/DropNode3D.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/Interaction2DIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/Interaction2DIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/Interaction3DIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/Interaction3DIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/DropGroupIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/DropGroupIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/DropTableIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/DropTableIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/EquipmentIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/EquipmentIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/Equipped3DIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/Equipped3DIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/InventoryIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/InventoryIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/RadialMenuIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/RadialMenuIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/InteractableArea2D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/InteractableArea2D.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/InteractableArea3D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/InteractableArea3D.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability@0.15x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability@0.15x.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/InteractionRayCast2DIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/InteractionRayCast2DIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/InteractionRayCast3DIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/InteractionRayCast3DIcon.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute@0.15x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute@0.15x.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect@0.15x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect@0.15x.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect@0.15x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect@0.15x.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable@0.15x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable@0.15x.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributes.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributes.sketch
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect@0.15x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect@0.15x.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer@0.15x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer@0.15x.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap@0.15x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap@0.15x.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot@0.15x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot@0.15x.png
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/camera_shake/plugin.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends EditorPlugin
3 |
4 |
5 | func _enter_tree() -> void:
6 | add_custom_type("CameraShake", "Node", preload("./nodes/camera_shake.gd"), null)
7 |
8 |
9 | func _exit_tree() -> void:
10 | remove_custom_type("CameraShake")
11 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/nodes/2d/interactable_area_2d.gd:
--------------------------------------------------------------------------------
1 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractableArea2D.png")
2 | @tool
3 | class_name InteractableArea2D extends Area2D
4 |
5 | @export_category("Interaction")
6 | @export var interaction: Interaction = null
7 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/nodes/3d/interactable_area_3d.gd:
--------------------------------------------------------------------------------
1 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractableArea3D.png")
2 | @tool
3 | class_name InteractableArea3D extends Area3D
4 |
5 | @export_category("Interaction")
6 | @export var interaction: Interaction = null
7 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/plugin.cfg:
--------------------------------------------------------------------------------
1 | [plugin]
2 |
3 | name="GGS"
4 | description="Godot Gameplay Systems (formerly godot gameplay attributes) is a set of nodes and resources which speed up development of skills and attribute based gameplay mechanisms."
5 | author="OctoD"
6 | version="1.0.0"
7 | script="plugin.gd"
8 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/nodes/drop_3d.gd:
--------------------------------------------------------------------------------
1 | class_name Drop3D extends Node3D
2 |
3 | ## Is a drop zone in a 3D world
4 |
5 |
6 | # Emitted when items are dropped in this node.
7 | signal item_dropped(item: Item, node: Node3D)
8 |
9 |
10 | func _ready() -> void:
11 | add_to_group("ggs.drop-node")
12 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/nodes/drop_2d.gd:
--------------------------------------------------------------------------------
1 | class_name Drop2D extends Node2D
2 |
3 |
4 | ## Is a drop zone in a 2D world
5 |
6 |
7 | # Emitted when items are dropped in this node.
8 | signal item_dropped(item: Item, node: Node2D)
9 |
10 |
11 | func _ready() -> void:
12 | add_to_group("ggs.drop-node")
13 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/slideshow/plugin.gd:
--------------------------------------------------------------------------------
1 | extends EditorPlugin
2 |
3 |
4 | const slideshow_script = preload("./slide_show.gd")
5 |
6 |
7 | func _enter_tree() -> void:
8 | add_custom_type("SlideShow", "Node2D", slideshow_script, null)
9 |
10 |
11 | func _exit_tree() -> void:
12 | remove_custom_type("SlideShow")
13 |
14 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/extended_character_nodes/plugin.gd:
--------------------------------------------------------------------------------
1 | extends EditorPlugin
2 |
3 |
4 | func _enter_tree() -> void:
5 | add_custom_type("PointAndClick2D", "Node2D", preload("./nodes/2d/point_and_click_2d.gd"), null)
6 | add_custom_type("PointAndClick3D", "Node3D", preload("./nodes/3d/point_and_click_3d.gd"), null)
7 |
8 |
9 | func _exit_tree() -> void:
10 | remove_custom_type("PointAndClick2D")
11 | remove_custom_type("PointAndClick3D")
12 |
13 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/resources/stop_effect_if_0.gd:
--------------------------------------------------------------------------------
1 | class_name StopEffectIf0 extends AttributeEffectCondition
2 |
3 |
4 | func should_apply(attribute_effect: AttributeEffect, effect: GameplayEffect, gameplay_attributes: GameplayAttributeMap) -> bool:
5 | var attribute = gameplay_attributes.get_attribute_by_name(attribute_effect.attribute_name)
6 |
7 | if attribute:
8 | return attribute.current_value > 0
9 |
10 | return false
11 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/nodes/gameplay_effect.gd:
--------------------------------------------------------------------------------
1 | @icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect.svg")
2 | @tool
3 | class_name GameplayEffect extends Node
4 |
5 |
6 | @export_category("Effect")
7 | ## Is the [AttributeTable] resource
8 | @export var table: AttributeTable = null
9 | ## Is the array of [AttributeEffect] resources generated by the inspector
10 | @export var attributes_affected: Array[AttributeEffect] = []
11 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute_table.gd:
--------------------------------------------------------------------------------
1 | @icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable.svg")
2 |
3 | class_name AttributeTable extends Resource
4 |
5 | ## Represents a table of attributes
6 |
7 | @export_category("Attributes")
8 | ## The list of attributes
9 | @export var attributes : Array[String] = []
10 |
11 |
12 | func _init(p_attributes: Array[String] = []) -> void:
13 | attributes = p_attributes
14 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/debug/abilities_debugger.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://cxh5dyttaogko"]
2 |
3 | [ext_resource type="Script" path="res://addons/godot_gameplay_systems/attributes_and_abilities/debug/abilities_debugger.gd" id="1_lb3v4"]
4 |
5 | [node name="AbilitiesDebugger" type="BoxContainer"]
6 | offset_right = 1152.0
7 | script = ExtResource("1_lb3v4")
8 |
9 | [node name="Timer" type="Timer" parent="."]
10 | wait_time = 5.0
11 | autostart = true
12 |
13 | [node name="Labels" type="VBoxContainer" parent="."]
14 | layout_mode = 2
15 | size_flags_horizontal = 3
16 | size_flags_vertical = 3
17 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/turn_based/plugin.gd:
--------------------------------------------------------------------------------
1 | extends EditorPlugin
2 |
3 |
4 | const TurnBasedGameScript = preload("./nodes/TurnBasedGame.gd")
5 | const TurnSubscriberScript = preload("./nodes/TurnSubscriber.gd")
6 |
7 |
8 | func _enter_tree() -> void:
9 | add_autoload_singleton("TurnManager", "./autoloads/turn_manager.gd")
10 | add_custom_type("TurnBasedGame", "Node", TurnBasedGameScript, null)
11 | add_custom_type("TurnSubscriber", "Node", TurnSubscriberScript, null)
12 |
13 |
14 | func _exit_tree() -> void:
15 | remove_autoload_singleton("TurnManager")
16 | remove_custom_type("TurnBasedGame")
17 | remove_custom_type("TurnSubscriber")
18 |
19 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute_effect_condition.gd:
--------------------------------------------------------------------------------
1 | class_name AttributeEffectCondition extends Resource
2 |
3 |
4 | ## Calls it's own method when the bound attribute should change
5 | ##
6 | ## If the condition expressed is met, then the effect can be applied
7 | ## This kind of resource is very useful to stop infinite time-based effects
8 |
9 |
10 | ## Returns [code]true[/code] if the effect should continue to be applied, [code]false[/code] otherwise
11 | func should_apply(
12 | attribute_effect: AttributeEffect,
13 | gameplay_effect: GameplayEffect,
14 | gameplay_attribute_map: GameplayAttributeMap
15 | ) -> bool:
16 | return true
17 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute.gd:
--------------------------------------------------------------------------------
1 | class_name AttributeResource extends Resource
2 |
3 | @export_category("Attribute")
4 | ## Is the attribute name
5 | @export var attribute_name := ""
6 | ## Is the attribute minimum value
7 | @export var minimum_value := 0.0
8 | ## Is the attribute maximum value
9 | @export var maximum_value := 0.0
10 | ## Is the attribute initial value
11 | @export var current_value := 0.0
12 |
13 |
14 | func _init(p_attribute_name: String = "", p_minimum_value: float = 0.0, p_maximum_value: float = 0.0, p_current_value: float = 0.0) -> void:
15 | attribute_name = p_attribute_name
16 | minimum_value = p_minimum_value
17 | maximum_value = p_maximum_value
18 | current_value = p_current_value
19 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/inspector/gameplay_effect_inspector_plugin.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends EditorInspectorPlugin
3 |
4 |
5 | var attribute_editor = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_editor.gd")
6 | var attribute_effect_editor = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_effect_editor.gd")
7 |
8 |
9 | func _can_handle(object) -> bool:
10 | return object is GameplayEffect
11 |
12 |
13 | func _parse_property(object: Object, type, name: String, hint_type, hint_string: String, usage_flags, wide: bool) -> bool:
14 | if type == 28 and name == "attributes_affected":
15 | add_property_editor(name, attribute_effect_editor.new(), true)
16 | return true
17 | if type == 24 and name == "table":
18 | return true
19 | return false
20 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/resources/drop_table.gd:
--------------------------------------------------------------------------------
1 | class_name DropTable extends Resource
2 |
3 | ## Represents a drop table.
4 | ##
5 | ## It has several [DropGroup] pools. Each pool has a chance of dropping one [Item] based on it's [member DropGroup.drop_chance].
6 |
7 | @export_category("Drop")
8 | ## An array of [DropGroup]. Each pool can drop one item.
9 | @export var pools: Array[DropGroup] = []
10 |
11 |
12 | ## Drops an array of [Item]s.
13 | ## [br]It accepts an optional argument [code]drop_modifier[/code] which increases (or decreases if negative) drop chances for each [DropGroup] pool.
14 | ## [br]The greater the modifier, the higher the chances to drop [Item]s from the pools.
15 | func drop(drop_modifier: float = 0.0) -> Array[Item]:
16 | var out: Array[Item] = []
17 |
18 | for pool in pools:
19 | var maybe_item = pool.drop(drop_modifier)
20 |
21 | if maybe_item != null:
22 | out.append(maybe_item)
23 |
24 | return out
25 |
26 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/inspector/gameplay_attribute_map_inspector_plugin.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends EditorInspectorPlugin
3 |
4 |
5 | var attribute_editor = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_editor.gd")
6 | var attribute_effect_editor = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_effect_editor.gd")
7 |
8 |
9 | func _can_handle(object) -> bool:
10 | return object is GameplayAttributeMap
11 |
12 |
13 | func _parse_property(object: Object, type, name: String, hint_type, hint_string: String, usage_flags, wide: bool) -> bool:
14 | if type == 28 and name == "attributes":
15 | add_property_editor(name, attribute_editor.new(), true)
16 | return true
17 | if type == 28 and name == "attributes_affected":
18 | add_property_editor(name, attribute_effect_editor.new(), true)
19 | return true
20 | if type == 24 and name == "table":
21 | return true
22 | return false
23 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/turn_based/autoloads/turn_manager.gd:
--------------------------------------------------------------------------------
1 | extends Node
2 |
3 |
4 | ## The [TurnBasedGame] node.
5 | var turn_based_game: TurnBasedGame = null
6 |
7 |
8 | ## Called when the node enters the scene tree for the first time.
9 | func _ready() -> void:
10 | get_turn_based_game()
11 |
12 |
13 | ## Ends the current turn sequence.
14 | func end_turn_sequence() -> void:
15 | if get_turn_based_game():
16 | get_turn_based_game().end_turn_sequence()
17 |
18 |
19 | ## Gets the [TurnBasedGame] node.
20 | func get_turn_based_game() -> TurnBasedGame:
21 | if turn_based_game:
22 | return turn_based_game
23 |
24 | for child in get_tree().get_nodes_in_group("ggs.turnbased"):
25 | if child is TurnBasedGame:
26 | turn_based_game = child
27 | return turn_based_game
28 |
29 | return null
30 |
31 |
32 | ## Starts a new turn sequence (aka, starts the turn based mode).
33 | func start_turn_sequence() -> void:
34 | if get_turn_based_game():
35 | get_turn_based_game().start_turn_sequence()
36 |
37 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/DropIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bv0eepnnlr8rx"
6 | path="res://.godot/imported/DropIcon.png-5ab37537aade41394994437130bd02be.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/DropIcon.png"
14 | dest_files=["res://.godot/imported/DropIcon.png-5ab37537aade41394994437130bd02be.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/ItemIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://du6kdypiccr7d"
6 | path="res://.godot/imported/ItemIcon.png-e12a6e4cda0c5fff1bfec52b69b09882.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/ItemIcon.png"
14 | dest_files=["res://.godot/imported/ItemIcon.png-e12a6e4cda0c5fff1bfec52b69b09882.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/resources/stop_effect_at_value.gd:
--------------------------------------------------------------------------------
1 | class_name StopEffectAtValue extends AttributeEffectCondition
2 |
3 | @export_category("Rule")
4 | ## The value to stop at
5 | @export var value_to_stop_at: float = 0.0
6 | ## The value condition
7 | @export_enum("Equal", "Greater than", "Lesser than", "Greater Equal to", "Less Equal To") var operator = 0
8 |
9 |
10 | func should_apply(attribute_effect: AttributeEffect, effect: GameplayEffect, gameplay_attributes: GameplayAttributeMap) -> bool:
11 | var attribute = gameplay_attributes.get_attribute_by_name(attribute_effect.attribute_name)
12 |
13 | if attribute == null:
14 | return false
15 |
16 | match operator:
17 | 0: return attribute.current_value == value_to_stop_at
18 | 1: return attribute.current_value > value_to_stop_at
19 | 2: return attribute.current_value < value_to_stop_at
20 | 3: return attribute.current_value >= value_to_stop_at
21 | 4: return attribute.current_value <= value_to_stop_at
22 |
23 |
24 | return false
25 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/DropNode2D.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://b8tbsu1m5bjj5"
6 | path="res://.godot/imported/DropNode2D.png-505269c443c6f334a033e07591488b87.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/DropNode2D.png"
14 | dest_files=["res://.godot/imported/DropNode2D.png-505269c443c6f334a033e07591488b87.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/DropNode3D.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://c8b1c3dfii8cw"
6 | path="res://.godot/imported/DropNode3D.png-543cba8655b833053b0a70d8f6995260.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/DropNode3D.png"
14 | dest_files=["res://.godot/imported/DropNode3D.png-543cba8655b833053b0a70d8f6995260.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://b2gocrfty8qoa"
6 | path="res://.godot/imported/InteractionIcon.png-a371fc1d70b6e0b25ca8b6ba3e430f41.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png"
14 | dest_files=["res://.godot/imported/InteractionIcon.png-a371fc1d70b6e0b25ca8b6ba3e430f41.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/DropGroupIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://u02qmnlc1nk4"
6 | path="res://.godot/imported/DropGroupIcon.png-082ecee561eb77062736d4b1913eae84.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/DropGroupIcon.png"
14 | dest_files=["res://.godot/imported/DropGroupIcon.png-082ecee561eb77062736d4b1913eae84.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/DropTableIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bnlfgtgq5m3es"
6 | path="res://.godot/imported/DropTableIcon.png-dc6f72115d3a8fc32c80629bde52acf7.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/DropTableIcon.png"
14 | dest_files=["res://.godot/imported/DropTableIcon.png-dc6f72115d3a8fc32c80629bde52acf7.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/EquipmentIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://dsam1x2nnts1n"
6 | path="res://.godot/imported/EquipmentIcon.png-125ef11927f42a69bf3a8290bc401ce8.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/EquipmentIcon.png"
14 | dest_files=["res://.godot/imported/EquipmentIcon.png-125ef11927f42a69bf3a8290bc401ce8.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/InventoryIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://c0t3nn02g2aae"
6 | path="res://.godot/imported/InventoryIcon.png-3eabb97464864e508b14fd93db8fc3fb.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/InventoryIcon.png"
14 | dest_files=["res://.godot/imported/InventoryIcon.png-3eabb97464864e508b14fd93db8fc3fb.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/Equipped3DIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bmrsp6wmi5u2w"
6 | path="res://.godot/imported/Equipped3DIcon.png-cf3a3c5bcbecf44b4b7c648464a80b39.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/Equipped3DIcon.png"
14 | dest_files=["res://.godot/imported/Equipped3DIcon.png-cf3a3c5bcbecf44b4b7c648464a80b39.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/assets/RadialMenuIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cxfy3v83r43w1"
6 | path="res://.godot/imported/RadialMenuIcon.png-94dd769b061766a6aa27a71907d4a786.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/RadialMenuIcon.png"
14 | dest_files=["res://.godot/imported/RadialMenuIcon.png-94dd769b061766a6aa27a71907d4a786.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/Interaction2DIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://kckk1u3tv7fw"
6 | path="res://.godot/imported/Interaction2DIcon.png-23ca3edeaeabcacfdfb729c9f4985386.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/Interaction2DIcon.png"
14 | dest_files=["res://.godot/imported/Interaction2DIcon.png-23ca3edeaeabcacfdfb729c9f4985386.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/Interaction3DIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://0un3a4vtlxma"
6 | path="res://.godot/imported/Interaction3DIcon.png-bc4642cc3b89e7fa7b7356ec961e15cb.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/Interaction3DIcon.png"
14 | dest_files=["res://.godot/imported/Interaction3DIcon.png-bc4642cc3b89e7fa7b7356ec961e15cb.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability@0.15x.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://snrls3ve2xjq"
6 | path="res://.godot/imported/Ability@0.15x.png-b03d46ca8ad1e8c852ba988c59548ada.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability@0.15x.png"
14 | dest_files=["res://.godot/imported/Ability@0.15x.png-b03d46ca8ad1e8c852ba988c59548ada.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/InteractableArea2D.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cuotecpt76nmw"
6 | path="res://.godot/imported/InteractableArea2D.png-1439c320787289216c2f380ade2852b0.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/InteractableArea2D.png"
14 | dest_files=["res://.godot/imported/InteractableArea2D.png-1439c320787289216c2f380ade2852b0.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/InteractableArea3D.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://c76kg8ddxlos6"
6 | path="res://.godot/imported/InteractableArea3D.png-a5a8ac4d0da793dba97b4827e8e0165b.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/InteractableArea3D.png"
14 | dest_files=["res://.godot/imported/InteractableArea3D.png-a5a8ac4d0da793dba97b4827e8e0165b.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute@0.15x.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://mdi7o2crtoen"
6 | path="res://.godot/imported/Attribute@0.15x.png-0f5f51bdd0a39e19d0753428626a2d40.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute@0.15x.png"
14 | dest_files=["res://.godot/imported/Attribute@0.15x.png-0f5f51bdd0a39e19d0753428626a2d40.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect@0.15x.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bbd35kb6frop4"
6 | path="res://.godot/imported/TimedEffect@0.15x.png-1f83d729305101aeba11b6b93a23f59e.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect@0.15x.png"
14 | dest_files=["res://.godot/imported/TimedEffect@0.15x.png-1f83d729305101aeba11b6b93a23f59e.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/InteractionRayCast2DIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cv7yqsjuso70y"
6 | path="res://.godot/imported/InteractionRayCast2DIcon.png-d59ca4ade7a08b61362770f1a1e0f239.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/InteractionRayCast2DIcon.png"
14 | dest_files=["res://.godot/imported/InteractionRayCast2DIcon.png-d59ca4ade7a08b61362770f1a1e0f239.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/assets/InteractionRayCast3DIcon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://b8rnslakt6c41"
6 | path="res://.godot/imported/InteractionRayCast3DIcon.png-0a6b8a5be286279c55c50412ed88b36d.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/InteractionRayCast3DIcon.png"
14 | dest_files=["res://.godot/imported/InteractionRayCast3DIcon.png-0a6b8a5be286279c55c50412ed88b36d.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable@0.15x.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://djqr0krbf5bnc"
6 | path="res://.godot/imported/AttributeTable@0.15x.png-d256c289513bc255c1617d373c1c315f.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable@0.15x.png"
14 | dest_files=["res://.godot/imported/AttributeTable@0.15x.png-d256c289513bc255c1617d373c1c315f.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect@0.15x.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://ct6ls2s3ek5xy"
6 | path="res://.godot/imported/GameplayEffect@0.15x.png-522233c4cfda5c629c6801ea7eb9bcb3.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect@0.15x.png"
14 | dest_files=["res://.godot/imported/GameplayEffect@0.15x.png-522233c4cfda5c629c6801ea7eb9bcb3.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer@0.15x.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cm1prlnq3noon"
6 | path="res://.godot/imported/AbilityContainer@0.15x.png-dbf25c66b7ab7c8127c14a3ef31b5f9c.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer@0.15x.png"
14 | dest_files=["res://.godot/imported/AbilityContainer@0.15x.png-dbf25c66b7ab7c8127c14a3ef31b5f9c.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect@0.15x.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://dico0si16oo6u"
6 | path="res://.godot/imported/AttributeEffect@0.15x.png-0ff52db701b052fdb134a2b52898e627.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect@0.15x.png"
14 | dest_files=["res://.godot/imported/AttributeEffect@0.15x.png-0ff52db701b052fdb134a2b52898e627.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot@0.15x.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://qdrl51ys1mv7"
6 | path="res://.godot/imported/TimedEffectOneShot@0.15x.png-dec4305913ec60d3bdba7a1c17434a4e.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot@0.15x.png"
14 | dest_files=["res://.godot/imported/TimedEffectOneShot@0.15x.png-dec4305913ec60d3bdba7a1c17434a4e.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap@0.15x.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://2wdpq1f5p81h"
6 | path="res://.godot/imported/GameplayAttributeMap@0.15x.png-9773e9306c196855a875eb5d05f0adf6.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap@0.15x.png"
14 | dest_files=["res://.godot/imported/GameplayAttributeMap@0.15x.png-9773e9306c196855a875eb5d05f0adf6.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Paolo Roth
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://c2d8ar1vogsgq"
6 | path="res://.godot/imported/Ability.svg-3f9962c26262717fc95c0b689b1f9c2c.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability.svg"
14 | dest_files=["res://.godot/imported/Ability.svg-3f9962c26262717fc95c0b689b1f9c2c.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cp7geybwp22pi"
6 | path="res://.godot/imported/Attribute.svg-f7a91e28dba79614248d03df87724328.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute.svg"
14 | dest_files=["res://.godot/imported/Attribute.svg-f7a91e28dba79614248d03df87724328.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://b8osbt147xfdt"
6 | path="res://.godot/imported/TimedEffect.svg-7e0221bc7c621cbe2da09d683aeade15.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect.svg"
14 | dest_files=["res://.godot/imported/TimedEffect.svg-7e0221bc7c621cbe2da09d683aeade15.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cbhnaxdu8v24d"
6 | path="res://.godot/imported/AttributeTable.svg-5081b1f9f1075c6b197e825443a2b656.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable.svg"
14 | dest_files=["res://.godot/imported/AttributeTable.svg-5081b1f9f1075c6b197e825443a2b656.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bgfn647m855hg"
6 | path="res://.godot/imported/GameplayEffect.svg-2500388b0e2b1a8e049ad93ae746c2d3.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect.svg"
14 | dest_files=["res://.godot/imported/GameplayEffect.svg-2500388b0e2b1a8e049ad93ae746c2d3.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cmp3udx3jf0ut"
6 | path="res://.godot/imported/AttributeEffect.svg-59c4a27403293b505775db1a7d596d4c.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect.svg"
14 | dest_files=["res://.godot/imported/AttributeEffect.svg-59c4a27403293b505775db1a7d596d4c.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://dn54fmpqnjrlm"
6 | path="res://.godot/imported/AbilityContainer.svg-94a2d27656beadd249024abbd7dad512.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer.svg"
14 | dest_files=["res://.godot/imported/AbilityContainer.svg-94a2d27656beadd249024abbd7dad512.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bpcerra7enco5"
6 | path="res://.godot/imported/TimedEffectOneShot.svg-618699cade701d04ede8d12766983954.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot.svg"
14 | dest_files=["res://.godot/imported/TimedEffectOneShot.svg-618699cade701d04ede8d12766983954.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://dnu24g4ay5c6t"
6 | path="res://.godot/imported/GameplayAttributeMap.svg-a2ab7db92d9ac565eefeda7ab43c93c4.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap.svg"
14 | dest_files=["res://.godot/imported/GameplayAttributeMap.svg-a2ab7db92d9ac565eefeda7ab43c93c4.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/nodes/radial_menu_container.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends Container
3 |
4 |
5 | func _draw() -> void:
6 | var child_count = get_child_count()
7 | var pie_slice_radians = 2 * PI / child_count
8 | var child_index = 0
9 |
10 | for child in get_children():
11 | if child is Control:
12 | # move child to a pie slice starting from the center of the container
13 | var child_angle = child_index * pie_slice_radians
14 | var child_radius = min(size.x, size.y) / 2
15 | var child_position = Vector2(
16 | child_radius * cos(child_angle),
17 | child_radius * sin(child_angle)
18 | )
19 |
20 | child_position.x += size.x / 2 - child.size.x / 2
21 | child_position.y += size.y / 2 - child.size.y / 2
22 |
23 | child.set_position(child_position)
24 |
25 | child_index += 1
26 |
27 |
28 | func _enter_tree() -> void:
29 | child_entered_tree.connect(_handle_child_entered_tree)
30 | child_exiting_tree.connect(_handle_child_exiting_tree)
31 |
32 |
33 | func _handle_child_entered_tree(child: Node) -> void:
34 | _draw()
35 |
36 |
37 | func _handle_child_exiting_tree(child: Node) -> void:
38 | _draw()
39 |
40 |
41 | func _process(delta: float) -> void:
42 | if Engine.is_editor_hint():
43 | _draw()
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/plugin.gd:
--------------------------------------------------------------------------------
1 | extends EditorPlugin
2 |
3 |
4 | func _enter_tree() -> void:
5 | add_custom_type("InteractableArea2D", "Area2D",preload("./nodes/2d/interactable_area_2d.gd"),preload("./assets/InteractableArea2D.png"))
6 | add_custom_type("InteractableArea3D", "Area3D",preload("./nodes/3d/interactable_area_3d.gd"),preload("./assets/InteractableArea2D.png"))
7 | add_custom_type("InteractionRayCast2D", "RayCast2D",preload("./nodes/2d/interaction_raycast_2d.gd"),preload("./assets/InteractionRayCast2DIcon.png"))
8 | add_custom_type("InteractionRayCast3D", "RayCast3D",preload("./nodes/3d/interaction_raycast_3d.gd"),preload("./assets/InteractionRayCast3DIcon.png"))
9 | add_custom_type("InteractionManager", "Node",preload("./nodes/interaction_manager.gd"),preload("./assets/InteractionIcon.png"))
10 | add_custom_type("Interaction", "Resource",preload("./resources/interaction.gd"),preload("./assets/InteractionIcon.png"))
11 |
12 |
13 | func _exit_tree() -> void:
14 | remove_custom_type("Interaction")
15 | remove_custom_type("InteractionManager")
16 | remove_custom_type("InteractableArea2D")
17 | remove_custom_type("InteractableArea3D")
18 | remove_custom_type("InteractionRayCast2D")
19 | remove_custom_type("InteractionRayCast3D")
20 |
21 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/resources/drop_group.gd:
--------------------------------------------------------------------------------
1 | class_name DropGroup extends Resource
2 |
3 | ## Represents a drop rule.
4 | ##
5 | ##
6 |
7 | @export_category("Drop")
8 | ## Represents the pool of droppable [Item]s.
9 | @export var items_pool: Array[Item] = []
10 | ## Represents the chance of dropping one item from the [member DropGroup.items_pool] pool.
11 | ## [br]The higher the value, the higher the chances.
12 | @export_range(0.0, 100.0, 0.01) var drop_chance: float = 50.0
13 |
14 |
15 | ## Returns if the group can drop an item from the [member DropGroup.items_pool] pool.
16 | ## [br]It accepts an optional argument [code]drop_modifier[/code] which increases (or decreases if negative) drop chances.
17 | ## [br]The greater the modifier, the higher the chances to drop an [Item] from the pool.
18 | func can_be_dropped(modifier: float = 0.0) -> bool:
19 | return drop_chance + modifier >= randf_range(0.0, 100.0)
20 |
21 |
22 | ## Drops an [Item].
23 | ## [br]If the drop is unsuccessfull, it returns [code]null[/code].
24 | ## [br]It accepts an optional argument [code]drop_modifier[/code] which increases (or decreases if negative) drop chances.
25 | ## [br]The greater the modifier, the higher the chances to drop an [Item] from the pool.
26 | func drop(drop_modifier: float = 0.0) -> Item:
27 | if can_be_dropped(drop_modifier):
28 | return items_pool.pick_random()
29 |
30 | return null
31 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/nodes/2d/interaction_raycast_2d.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractionRayCast2DIcon.png")
3 | class_name InteractionRayCast2D extends RayCast2D
4 |
5 |
6 | ## Emitted when an [InteractableArea2D] is detected.
7 | signal interactable_detected(interactable: InteractableArea2D)
8 | signal interactable_focus_in(interactable: InteractableArea2D)
9 | signal interactable_focus_out(interactable: InteractableArea2D)
10 |
11 |
12 | @export_category("Interaction owner")
13 | @export_node_path("InteractionManager") var interaction_manager_path := NodePath()
14 |
15 |
16 | var manager: Node = null
17 | var interacting := false
18 | var previous_collider: Object = null
19 |
20 |
21 | func _get_configuration_warnings() -> PackedStringArray:
22 | if interaction_manager_path == null or interaction_manager_path.is_empty():
23 | return ["Tag manager path cannot be empty"]
24 | return []
25 |
26 |
27 | func _ready() -> void:
28 | collide_with_areas = true
29 | manager = get_node(interaction_manager_path)
30 |
31 |
32 | func _physics_process(delta: float) -> void:
33 | var collider = get_collider()
34 |
35 | if collider is InteractableArea2D:
36 | interactable_detected.emit(collider)
37 | interactable_focus_in.emit(collider)
38 | elif collider == null and previous_collider != null:
39 | interactable_focus_out.emit(previous_collider)
40 | previous_collider = null
41 |
42 | previous_collider = collider
43 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/nodes/3d/interaction_raycast_3d.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractionRayCast3DIcon.png")
3 | class_name InteractionRayCast3D extends RayCast3D
4 |
5 |
6 | ## Emitted when an [InteractableArea3D] is detected.
7 | signal interactable_detected(interactable: InteractableArea3D)
8 | signal interactable_focus_in(interactable: InteractableArea3D)
9 | signal interactable_focus_out(interactable: InteractableArea3D)
10 |
11 |
12 | @export_category("Interaction owner")
13 | @export_node_path("InteractionManager") var interaction_manager_path := NodePath()
14 |
15 |
16 | var manager: Node = null
17 | var interacting := false
18 | var previous_collider: Object = null
19 |
20 |
21 | func _get_configuration_warnings() -> PackedStringArray:
22 | if interaction_manager_path == null or interaction_manager_path.is_empty():
23 | return ["Tag manager path cannot be empty"]
24 | return []
25 |
26 |
27 | func _ready() -> void:
28 | collide_with_areas = true
29 | manager = get_node(interaction_manager_path)
30 |
31 |
32 | func _physics_process(delta: float) -> void:
33 | var collider = get_collider()
34 |
35 | if collider is InteractableArea3D:
36 | interactable_detected.emit(collider)
37 | interactable_focus_in.emit(collider)
38 | elif collider == null and previous_collider != null:
39 | interactable_focus_out.emit(previous_collider)
40 | previous_collider = null
41 |
42 | previous_collider = collider
43 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/turn_based/nodes/TurnSubscriber.gd:
--------------------------------------------------------------------------------
1 | class_name TurnSubscriber extends Node
2 |
3 | ## A turn based participant
4 | ##
5 | ## It represents a scene which can partecipate to a turn based game
6 |
7 |
8 | ## Emitted when this subscriber turn ends
9 | signal turn_ended()
10 | ## Emitted when this subscriber turn starts
11 | signal turn_started()
12 | ## Emitter when the turn round ended,
13 | signal turn_round_ended()
14 |
15 |
16 | @export_group("Turn based game")
17 | ## Priority of this subscriber. The higher the priority, the sooner the turn starts.
18 | @export var priority: int = 0
19 |
20 |
21 | func _enter_tree() -> void:
22 | if TurnManager.get_turn_based_game() != null:
23 | TurnManager.get_turn_based_game().add_subscriber(self)
24 |
25 |
26 | func _exit_tree() -> void:
27 | if TurnManager.get_turn_based_game() != null:
28 | TurnManager.get_turn_based_game().remove_subscriber(self)
29 |
30 |
31 | func _ready() -> void:
32 | add_to_group("ggs.turnbased")
33 |
34 | if TurnManager.get_turn_based_game() != null:
35 | TurnManager.get_turn_based_game().add_subscriber(self)
36 |
37 |
38 | ## Called when the turn round ended
39 | ## [br]It's a virtual method, it can be overrided
40 | func _turn_ended() -> void:
41 | pass
42 |
43 |
44 | ## Called when the turn round started
45 | ## [br]It's a virtual method, it can be overrided
46 | func _turn_started() -> void:
47 | pass
48 |
49 |
50 | ## Ends the turn of this subscriber
51 | func end_turn() -> void:
52 | if TurnManager.get_turn_based_game() != null:
53 | TurnManager.get_turn_based_game().next_turn()
54 |
55 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/objects/item_activation_event.gd:
--------------------------------------------------------------------------------
1 | class_name ItemActivationEvent extends RefCounted
2 |
3 | ## The activation type
4 | var activation_type: int = 0
5 | ## [code]true[/code] if owner is not [code]null[/code], [code]false[/code] otherwise.
6 | var has_owner: bool:
7 | get:
8 | return owner != null
9 | ## The inventory owner
10 | var owner: Node
11 | ## The equipment which emits the activation event or is bound to an [Inventory]. It could be [code]null[/code].
12 | var equipment: Equipment
13 | ## The inventory which emits the activation event or is bound to an [Equipment]. It could be [code]null[/code].
14 | var inventory: Inventory
15 |
16 |
17 | ## Builds the event
18 | func _init(_inventory_or_equipment: Variant, _activation_type: int = 0) -> void:
19 | var someone_really_called_me = false
20 |
21 | assert(_activation_type != null, "activation_type cannot be null")
22 |
23 | activation_type = _activation_type
24 |
25 | if _inventory_or_equipment is Inventory:
26 | inventory = _inventory_or_equipment
27 | equipment = inventory.equipment
28 | someone_really_called_me = true
29 |
30 | if not inventory.owner_path.is_empty():
31 | owner = inventory.get_node(inventory.owner_path)
32 |
33 | if _inventory_or_equipment is Equipment:
34 | equipment = _inventory_or_equipment
35 | inventory = equipment.inventory
36 | someone_really_called_me = true
37 |
38 | if not equipment.owner_path.is_empty():
39 | owner = equipment.get_node(equipment.owner_path)
40 |
41 | assert(someone_really_called_me, "an ItemActivationEvent can be called by an Equipment or Inventory node")
42 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_editor_row.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends VBoxContainer
3 |
4 |
5 | signal attribute_updated(key: String, value: float)
6 |
7 |
8 | @export_category("Attribute")
9 | @export var attribute: AttributeResource = null:
10 | get:
11 | return attribute
12 | set(value):
13 | attribute = value
14 | _update_ui()
15 |
16 |
17 | @onready var label: Label = $Label
18 | @onready var max_value: SpinBox = $GridContainer/MaximumValue/SpinBox
19 | @onready var min_value: SpinBox = $GridContainer/MinimumValue/SpinBox
20 | @onready var cur_value: SpinBox = $GridContainer/CurrentValue/SpinBox
21 |
22 |
23 | var _updating_ui = false
24 |
25 |
26 | func _init() -> void:
27 | pass
28 |
29 |
30 | func _ready() -> void:
31 | max_value.step = 0.01
32 | min_value.step = 0.01
33 | cur_value.step = 0.01
34 | max_value.value_changed.connect(func (value):
35 | if not _updating_ui:
36 | attribute_updated.emit("maximum_value", value)
37 | )
38 | min_value.value_changed.connect(func (value):
39 | if not _updating_ui:
40 | attribute_updated.emit("minimum_value", value)
41 | )
42 | cur_value.value_changed.connect(func (value):
43 | if not _updating_ui:
44 | attribute_updated.emit("current_value", value)
45 | )
46 |
47 | _update_ui()
48 |
49 |
50 | func _update_ui() -> void:
51 | if attribute != null:
52 | _updating_ui = true
53 | label.text = attribute.attribute_name
54 | max_value.value = attribute.maximum_value
55 | min_value.value = attribute.minimum_value
56 | cur_value.value = attribute.current_value
57 | _updating_ui = false
58 |
59 |
60 | func set_values(res: AttributeResource) -> void:
61 | await ready
62 | attribute = res
63 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/nodes/pickable_item_2d.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends Area2D
3 |
4 |
5 | ## It represents an [Item] which can be picked in a 2D world.
6 |
7 |
8 | @export_category("Item Pick")
9 | ## Automatically triggers a pick if the collision layer hits this area.
10 | @export var automatically_pick: bool = false
11 | ## The item to pick.
12 | @export var item: Item = null:
13 | get:
14 | return item
15 | set(value):
16 | item = value
17 | _render()
18 |
19 |
20 | ## A reference to the rendered [Item].
21 | var rendered_item: Node2D
22 |
23 |
24 | ## Handles the collision
25 | func _handle_collision(body: Node2D) -> void:
26 | if automatically_pick:
27 | if body.has_meta("ggsInventory"):
28 | pick(body.get_meta("ggsInventory", null))
29 | elif body.has_meta("ggsEquipment"):
30 | pick(body.get_meta("ggsEquipment", null))
31 |
32 |
33 | ## Renders the bound [Item] only if it has a valid [member Item.scene] member.
34 | func _render() -> void:
35 | if item and item.scene and item.scene.can_instantiate():
36 | if rendered_item:
37 | rendered_item.queue_free()
38 |
39 | rendered_item = item.scene.instantiate()
40 | add_child(rendered_item)
41 |
42 |
43 | func _ready() -> void:
44 | if not Engine.is_editor_hint():
45 | area_entered.connect(_handle_collision)
46 | body_entered.connect(_handle_collision)
47 |
48 |
49 | ## Triggers the pick mechanism programmatically.
50 | func pick(by: Node) -> void:
51 | assert(item != null, "item should not be null")
52 |
53 | if by is Inventory:
54 | if by.can_add(item):
55 | by.add_item(item)
56 | queue_free()
57 | elif by is Equipment:
58 | ## Adds tags because it "picked" the item.
59 | by.add_tags(item.tags_added_on_add)
60 |
61 | for slot in by.slots:
62 | if slot.can_equip(item):
63 | slot.equip(item)
64 |
65 | queue_free()
66 |
67 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/nodes/pickable_item_3d.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends Area3D
3 |
4 |
5 | ## It represents an [Item] which can be picked in a 3D world.
6 |
7 |
8 | @export_category("Item Pick")
9 | ## Automatically triggers a pick if the collision layer hits this area.
10 | @export var automatically_pick: bool = false
11 | ## The item to pick.
12 | @export var item: Item = null:
13 | get:
14 | return item
15 | set(value):
16 | item = value
17 | _render()
18 |
19 |
20 | ## A reference to the rendered [Item].
21 | var rendered_item: Node3D
22 |
23 |
24 | ## Handles the collision
25 | func _handle_collision(body: Node3D) -> void:
26 | if automatically_pick:
27 | if body.has_meta("ggsInventory"):
28 | pick(body.get_meta("ggsInventory", null))
29 | elif body.has_meta("ggsEquipment"):
30 | pick(body.get_meta("ggsEquipment", null))
31 |
32 |
33 | ## Renders the bound [Item] only if it has a valid [member Item.scene] member.
34 | func _render() -> void:
35 | if item and item.scene and item.scene.can_instantiate():
36 | if rendered_item:
37 | rendered_item.queue_free()
38 |
39 | rendered_item = item.scene.instantiate()
40 | add_child(rendered_item)
41 |
42 |
43 | func _ready() -> void:
44 | if not Engine.is_editor_hint():
45 | area_entered.connect(_handle_collision)
46 | body_entered.connect(_handle_collision)
47 |
48 |
49 | ## Triggers the pick mechanism programmatically.
50 | func pick(by: Node) -> void:
51 | assert(item != null, "item should not be null")
52 |
53 | if by is Inventory:
54 | if by.can_add(item):
55 | by.add_item(item)
56 | queue_free()
57 | elif by is Equipment:
58 | ## Adds tags because it "picked" the item.
59 | by.add_tags(item.tags_added_on_add)
60 |
61 | for slot in by.slots:
62 | if slot.can_equip(item):
63 | slot.equip(item)
64 |
65 | queue_free()
66 |
67 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/plugin.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends EditorPlugin
3 |
4 | const attributes_and_abilities_plugin_script = preload("./attributes_and_abilities/plugin.gd")
5 | const camera_shake_plugin_script = preload("./camera_shake/plugin.gd")
6 | const extended_character_nodes_script = preload("./extended_character_nodes/plugin.gd")
7 | const inventory_system_script = preload("./inventory_system/plugin.gd")
8 | const interactables_script = preload("./interactables/plugin.gd")
9 | const slideshow_script = preload("./slideshow/plugin.gd")
10 | const turn_based_script = preload("./turn_based/plugin.gd")
11 |
12 |
13 | var attributes_and_abilities_plugin: EditorPlugin
14 | var camera_shake_plugin: EditorPlugin
15 | var extended_character_nodes: EditorPlugin
16 | var inventory_system: EditorPlugin
17 | var interactables: EditorPlugin
18 | var slideshow: EditorPlugin
19 | var turn_based: EditorPlugin
20 |
21 |
22 | func _init() -> void:
23 | attributes_and_abilities_plugin = attributes_and_abilities_plugin_script.new()
24 | camera_shake_plugin = camera_shake_plugin_script.new()
25 | extended_character_nodes = extended_character_nodes_script.new()
26 | inventory_system = inventory_system_script.new()
27 | interactables = interactables_script.new()
28 | slideshow = slideshow_script.new()
29 | turn_based = turn_based_script.new()
30 |
31 |
32 | func _enter_tree():
33 | attributes_and_abilities_plugin._enter_tree()
34 | camera_shake_plugin._enter_tree()
35 | extended_character_nodes._enter_tree()
36 | inventory_system._enter_tree()
37 | interactables._enter_tree()
38 | slideshow._enter_tree()
39 | turn_based._enter_tree()
40 |
41 |
42 | func _exit_tree():
43 | attributes_and_abilities_plugin._exit_tree()
44 | camera_shake_plugin._exit_tree()
45 | extended_character_nodes._exit_tree()
46 | inventory_system._exit_tree()
47 | interactables._exit_tree()
48 | slideshow._exit_tree()
49 | turn_based._exit_tree()
50 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/nodes/effected_area2d.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | class_name EffectedArea2D extends Area2D
3 |
4 | ## An extension to base class with automatism for GameplayAttributeMap
5 | ##
6 | ## Applies [GameplayEffect]s automatically when a collision is triggered and [method should_apply_effect] returns [code]true[/code].
7 |
8 |
9 | @export_category("Gameplay Effect Handling")
10 | ## Removes the EffectedArea2D if [method should_apply_effect] returns [code]true[/code]
11 | @export var remove_self_on_apply := true
12 | ## Removes all child [GameplayEffect]s when colliding and when [method should_apply_effect] returns [code]true[/code]
13 | @export var remove_effects_on_apply := true
14 |
15 |
16 | ## Gets all child GameplayEffect nodes
17 | var effects: Array[GameplayEffect] = []:
18 | get:
19 | var _effects: Array[GameplayEffect] = []
20 |
21 | for child in get_children():
22 | if child is GameplayEffect:
23 | _effects.append(child)
24 |
25 | return _effects
26 |
27 |
28 | func _ready() -> void:
29 | if not Engine.is_editor_hint():
30 | area_entered.connect(func (body: Area2D):
31 | if should_apply_effect(body):
32 | for effect in effects:
33 | if remove_effects_on_apply:
34 | remove_child(effect)
35 | body.add_child(effect)
36 | else:
37 | body.add_child(effect.duplicate())
38 | if remove_self_on_apply:
39 | queue_free()
40 | )
41 |
42 | body_entered.connect(func (body: Node2D):
43 | if should_apply_effect(body):
44 | for effect in effects:
45 | if remove_effects_on_apply:
46 | remove_child(effect)
47 | body.add_child(effect)
48 | else:
49 | body.add_child(effect.duplicate())
50 | if remove_self_on_apply:
51 | queue_free()
52 | )
53 |
54 |
55 | ## Returns true if the effect should be applied to a certain node
56 | func should_apply_effect(node: Node2D) -> bool:
57 | return true
58 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/nodes/effected_area3d.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | class_name EffectedArea3D extends Area3D
3 |
4 | ## An extension to base class with automatism for GameplayAttributeMap
5 | ##
6 | ## Applies [GameplayEffect]s automatically when a collision is triggered and [method should_apply_effect] returns [code]true[/code].
7 |
8 |
9 | @export_category("Gameplay Effect Handling")
10 | ## Removes the EffectedArea2D if [method should_apply_effect] returns [code]true[/code]
11 | @export var remove_self_on_apply := true
12 | ## Removes all child [GameplayEffect]s when colliding and when [method should_apply_effect] returns [code]true[/code]
13 | @export var remove_effects_on_apply := true
14 |
15 |
16 | ## Gets all child GameplayEffect nodes
17 | var effects: Array[GameplayEffect] = []:
18 | get:
19 | var _effects: Array[GameplayEffect] = []
20 |
21 | for child in get_children():
22 | if child is GameplayEffect:
23 | _effects.append(child)
24 |
25 | return _effects
26 |
27 |
28 | func _ready() -> void:
29 | if not Engine.is_editor_hint():
30 | area_entered.connect(func (body: Area3D):
31 | if should_apply_effect(body):
32 | for effect in effects:
33 | if remove_effects_on_apply:
34 | remove_child(effect)
35 | body.add_child(effect)
36 | else:
37 | body.add_child(effect.duplicate())
38 |
39 | if remove_self_on_apply:
40 | queue_free()
41 | )
42 |
43 | body_entered.connect(func (body: Node3D):
44 | if should_apply_effect(body):
45 | for effect in effects:
46 | if remove_effects_on_apply:
47 | remove_child(effect)
48 | body.add_child(effect)
49 | else:
50 | body.add_child(effect.duplicate())
51 |
52 | if remove_self_on_apply:
53 | queue_free()
54 | )
55 |
56 |
57 | ## Returns true if the effect should be applied to a certain node
58 | func should_apply_effect(node: Node3D) -> bool:
59 | return true
60 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/objects/activation_event.gd:
--------------------------------------------------------------------------------
1 | ## Is the activation event object sent to an ability
2 | ##
3 | ## This class is used to pass down to an [Ability] all the informations needed for it's own execution.
4 |
5 | class_name ActivationEvent extends RefCounted
6 |
7 | ## It's the executing [AbilityContainer] which passed the [ActivationEvent] to an [Ability]
8 | var ability_container: AbilityContainer
9 | ## It's the [GameplayAttributeMap] bound to an [AbilityContainer]
10 | var attribute_map: GameplayAttributeMap
11 | ## Returns a [Node] as the owning character
12 | var character: Node:
13 | get:
14 | if has_attribute_map and attribute_map.owning_character != null and not attribute_map.owning_character.is_empty():
15 | return attribute_map.get_node(attribute_map.owning_character)
16 | return null
17 | ## [code]true[/code] if [member ActivationEvent.ability_container] is not [code]null[/code], [code]false[/code] otherwise.
18 | var has_ability_container: bool:
19 | get:
20 | return ability_container != null
21 | ## [code]true[/code] if [member ActivationEvent.attribute_map] is not [code]null[/code], [code]false[/code] otherwise.
22 | var has_attribute_map: bool:
23 | get:
24 | return attribute_map != null
25 | ## Returns if the [ActivationEvent] has been created with some tags
26 | var has_tags: bool:
27 | get:
28 | return tags.size() > 0
29 | ## These are the tags set when the [Ability] has been activated using [method Ability.try_activate]
30 | var tags: Array[String] = []
31 |
32 |
33 | func _init(_ability_container: AbilityContainer) -> void:
34 | ability_container = _ability_container
35 | attribute_map = _ability_container.gameplay_attribute_map
36 | tags = _ability_container.tags.duplicate()
37 |
38 |
39 | ## Gets an [AttributeSpec] attribute by it's name
40 | func get_attribute(attribute_name: String) -> AttributeSpec:
41 | if has_attribute_map:
42 | return attribute_map.get_attribute_by_name(attribute_name)
43 | return null
44 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/nodes/equipped_item_3d.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | class_name EquippedItem3D extends Node3D
3 |
4 |
5 | ## Displays the current equipped [Item] from a specific [Equipment] node.
6 |
7 |
8 | ## Emitted when an [Item] is displayed.
9 | signal item_displayed(item: Item)
10 | ## Emitted when an [Item] is hidden.
11 | signal item_hidden(item: Item)
12 |
13 |
14 | @export_category("Equipment")
15 | ## The path to the [Equipment] node
16 | @export_node_path("Equipment") var equipment_path: NodePath = NodePath():
17 | get:
18 | return equipment_path
19 | set(value):
20 | equipment_path = value
21 | update_configuration_warnings()
22 |
23 | @export_group("Tagging", "tags_")
24 | ## Displays an equipped [Item] only if both it has these the [member Item.tags] tags.
25 | @export var tags_to_display: Array[String] = []
26 |
27 |
28 | var current: Node3D = null
29 |
30 |
31 | func _init() -> void:
32 | if Engine.is_editor_hint():
33 | update_configuration_warnings()
34 |
35 |
36 | func _ready() -> void:
37 | assert(not equipment_path.is_empty(), "equipment_path cannot be empty")
38 |
39 | var equipment = get_node(equipment_path)
40 |
41 | equipment.equipped.connect(func (item: Item, _slot: EquipmentSlot):
42 | if tags_to_display.size() > 0:
43 | var has_tags = true
44 | for tag in tags_to_display:
45 | if not item.tags.has(tag):
46 | has_tags = false
47 | break
48 | if not has_tags:
49 | return
50 |
51 | if item.scene and item.scene.can_instantiate():
52 | current = item.scene.instantiate()
53 | add_child(current)
54 | )
55 |
56 | equipment.unequipped.connect(func (_item: Item, _slot: EquipmentSlot):
57 | if current:
58 | remove_child(current)
59 | current = null
60 | )
61 |
62 |
63 | func _get_configuration_warnings() -> PackedStringArray:
64 | var errors: Array[String] = []
65 |
66 | if equipment_path == null:
67 | errors.append("equipment_path cannot be null")
68 |
69 | if equipment_path != null and equipment_path.is_empty():
70 | errors.append("equipment_path cannot be empty")
71 |
72 | return PackedStringArray(errors)
73 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_editor_row.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3]
2 |
3 | [ext_resource type="Script" path="res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_editor_row.gd" id="1_ceu2s"]
4 |
5 | [node name="AttributeEditorRow" type="VBoxContainer"]
6 | offset_right = 1152.0
7 | offset_bottom = 101.0
8 | grow_horizontal = 2
9 | grow_vertical = 2
10 | size_flags_horizontal = 3
11 | script = ExtResource("1_ceu2s")
12 |
13 | [node name="Label" type="Label" parent="."]
14 | layout_mode = 2
15 | theme_type_variation = &"HeaderSmall"
16 | vertical_alignment = 1
17 |
18 | [node name="LabelSeparator" type="HSeparator" parent="."]
19 | layout_mode = 2
20 |
21 | [node name="GridContainer" type="GridContainer" parent="."]
22 | layout_mode = 2
23 | columns = 3
24 |
25 | [node name="MaximumValue" type="VBoxContainer" parent="GridContainer"]
26 | layout_mode = 2
27 | size_flags_horizontal = 3
28 |
29 | [node name="Label" type="Label" parent="GridContainer/MaximumValue"]
30 | layout_mode = 2
31 | size_flags_horizontal = 3
32 | text = "Maximum"
33 | clip_text = true
34 |
35 | [node name="SpinBox" type="SpinBox" parent="GridContainer/MaximumValue"]
36 | layout_mode = 2
37 | size_flags_horizontal = 3
38 | allow_greater = true
39 | allow_lesser = true
40 | alignment = 2
41 | select_all_on_focus = true
42 |
43 | [node name="MinimumValue" type="VBoxContainer" parent="GridContainer"]
44 | layout_mode = 2
45 | size_flags_horizontal = 3
46 |
47 | [node name="Label" type="Label" parent="GridContainer/MinimumValue"]
48 | layout_mode = 2
49 | size_flags_horizontal = 3
50 | text = "Minimum"
51 | clip_text = true
52 |
53 | [node name="SpinBox" type="SpinBox" parent="GridContainer/MinimumValue"]
54 | layout_mode = 2
55 | size_flags_horizontal = 3
56 | allow_greater = true
57 | allow_lesser = true
58 | alignment = 2
59 | select_all_on_focus = true
60 |
61 | [node name="CurrentValue" type="VBoxContainer" parent="GridContainer"]
62 | layout_mode = 2
63 | size_flags_horizontal = 3
64 |
65 | [node name="Label" type="Label" parent="GridContainer/CurrentValue"]
66 | layout_mode = 2
67 | size_flags_horizontal = 3
68 | text = "Current"
69 | clip_text = true
70 |
71 | [node name="SpinBox" type="SpinBox" parent="GridContainer/CurrentValue"]
72 | layout_mode = 2
73 | size_flags_horizontal = 3
74 | allow_greater = true
75 | allow_lesser = true
76 | alignment = 2
77 | select_all_on_focus = true
78 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/resources/equipment_slot.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | class_name EquipmentSlot extends Resource
3 |
4 |
5 | ## Represents an equipment slot.
6 |
7 |
8 | ## Emitted when an [Item] is equipped.
9 | ## [br]Note, this will always be emitted after the [signal EquipmentSlot.unequipped_item] signal if a previous [Item] was equipped.
10 | signal item_equipped(item: Item, slot: EquipmentSlot)
11 | ## Emitted if the [Item] passed to [method EquipmentSlot.equip] method is not acceptable.
12 | signal item_refused_to_equip(item: Item, slot: EquipmentSlot)
13 | ## Emitted when an [Item] is unequipped.
14 | signal item_unequipped(item: Item, slot: EquipmentSlot)
15 |
16 |
17 | @export_category("Slot")
18 | ## The name given to this slot.
19 | ## [br]It is useful to be queried by [method Equipment.activate_by_name] method.
20 | @export var name: String = ""
21 | ## A white-list [Array] of accepted [Item]s.
22 | ## [br]Note: if this array is empty, all [Item]s will be eligible to be equipped, so beware.
23 | @export var accepted_items: Array[Item] = []
24 | ## The current equipped [Item]
25 | @export var equipped: Item = null
26 |
27 |
28 | ## [br]Is [code]true[/code] if an [Item] is equipped, [code]false[/code] otherwise.
29 | var has_equipped_item: bool:
30 | get:
31 | return equipped != null
32 | ## [br]Is [code]true[/code] if no [Item] are equipped, [code]false[/code] otherwise.
33 | var is_free: bool:
34 | get:
35 | return equipped == null
36 |
37 |
38 | func _init(_accepted_items: Array[Item] = [], _equipped_item: Item = null) -> void:
39 | accepted_items = _accepted_items
40 |
41 | if can_equip(_equipped_item):
42 | equipped = _equipped_item
43 |
44 |
45 | ## Determines if the slot can accept an [Item]
46 | func can_equip(item: Item) -> bool:
47 | if item == null:
48 | return false
49 |
50 | if accepted_items.size() == 0:
51 | return true
52 |
53 | for accepted in accepted_items:
54 | if accepted.name == item.name:
55 | return true
56 |
57 | return false
58 |
59 |
60 | ## Equips an [Item]
61 | func equip(item: Item) -> void:
62 | if item == null:
63 | return
64 |
65 | if not can_equip(item):
66 | item_refused_to_equip.emit(item, self)
67 | return
68 |
69 | if has_equipped_item:
70 | item_unequipped.emit(equipped, self)
71 | equipped = null
72 |
73 | equipped = item
74 | item_equipped.emit(item, self)
75 |
76 |
77 | ## Unequips an [Item] if any.
78 | ## [br]If there is an equipped item, the signal [signal EquipmentSlot.item_unequipped] will be emitted.
79 | func unequip() -> void:
80 | if has_equipped_item:
81 | item_unequipped.emit(equipped, self)
82 | equipped = null
83 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_editor.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends EditorProperty
3 |
4 | var resource_input := EditorResourcePicker.new()
5 | var vbox_container := VBoxContainer.new()
6 | var scroll_container := VBoxContainer.new()
7 | var row_scene = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_editor_row.tscn")
8 |
9 | var updating := false
10 |
11 |
12 | func _init() -> void:
13 | resource_input.base_type = "AttributeTable"
14 |
15 | vbox_container.add_child(resource_input)
16 | vbox_container.add_child(scroll_container)
17 |
18 | add_child(vbox_container)
19 |
20 | resource_input.resource_changed.connect(func (res):
21 | get_edited_object().table = res
22 | if res != null:
23 | _draw_attributes_rows(res)
24 | else:
25 | _empty_attributes()
26 | _empty_scroll_container()
27 | )
28 | resource_input.resource_selected.connect(func (res, inspect):
29 | get_edited_object().table = res
30 | if res != null:
31 | _draw_attributes_rows(res)
32 | else:
33 | _empty_attributes()
34 | _empty_scroll_container()
35 | )
36 |
37 | property_changed.connect(func (property: StringName, value: Variant, field: StringName, changing: bool):
38 | if property == "attributes" and value == null:
39 | var target = get_edited_object()
40 | target.attributes = []
41 | target.table = null
42 | resource_input.edited_resource = null
43 | _empty_scroll_container()
44 | )
45 |
46 |
47 | func _draw_attributes_rows(resource: AttributeTable) -> void:
48 | _empty_scroll_container()
49 |
50 | if resource == null:
51 | return
52 |
53 | var index = 0
54 |
55 | for attribute in resource.attributes:
56 | scroll_container.add_child(_make_row(attribute, index))
57 | index += 1
58 |
59 |
60 | func _empty_attributes() -> void:
61 | get_edited_object().attributes = []
62 |
63 |
64 | func _empty_scroll_container() -> void:
65 | for child in scroll_container.get_children():
66 | scroll_container.remove_child(child)
67 |
68 |
69 | func _make_row(attribute_name: String, index: int) -> Control:
70 | var gameplay_attributes = get_edited_object() as GameplayAttributeMap
71 | var spec = gameplay_attributes._get_attribute_at(index)
72 | var row = row_scene.instantiate()
73 |
74 | spec.attribute_name = attribute_name
75 |
76 | row.attribute_updated.connect(func (key, value):
77 | gameplay_attributes._update_attribute(index, key, value)
78 | )
79 |
80 | row.set_values(spec)
81 |
82 | return row
83 |
84 |
85 | func _update_property() -> void:
86 | var target = get_edited_object()
87 | _draw_attributes_rows(target.table)
88 | resource_input.edited_resource = target.table
89 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/plugin.gd:
--------------------------------------------------------------------------------
1 | extends EditorPlugin
2 |
3 |
4 | const equipment_script = preload("./nodes/equipment.gd")
5 | const equipped_item_3d = preload("./nodes/equipped_item_3d.gd")
6 | const inventory_script = preload("./nodes/inventory.gd")
7 | const item_script = preload("./resources/item.gd")
8 | const item_activation_event_script = preload("./objects/item_activation_event.gd")
9 | const pickable_item_2d = preload("./nodes/pickable_item_2d.gd")
10 | const pickable_item_3d = preload("./nodes/pickable_item_3d.gd")
11 | const drop_group_script = preload("./resources/drop_group.gd")
12 | const drop_table_script = preload("./resources/drop_table.gd")
13 | const drop_script = preload("./nodes/drop.gd")
14 | const drop_2d_script = preload("./nodes/drop_2d.gd")
15 | const drop_3d_script = preload("./nodes/drop_3d.gd")
16 | const radial_menu_script = preload("./nodes/radial_menu_container.gd")
17 |
18 |
19 | func _enter_tree() -> void:
20 | add_custom_type("Item", "Resource", item_script, preload("./assets/ItemIcon.png"))
21 | add_custom_type("Drop", "Node", drop_script, preload("./assets/DropIcon.png"))
22 | add_custom_type("Drop2D", "Node2D", drop_2d_script, preload("./assets/DropNode2D.png"))
23 | add_custom_type("Drop3D", "Node3D", drop_3d_script, preload("./assets/DropNode3D.png"))
24 | add_custom_type("DropGroup", "Resource", drop_group_script, preload("./assets/DropGroupIcon.png"))
25 | add_custom_type("DropTable", "Resource", drop_table_script, preload("./assets/DropTableIcon.png"))
26 | add_custom_type("ItemActivationEvent", "RefCounted", item_activation_event_script, null)
27 | add_custom_type("Inventory", "Node", inventory_script, preload("./assets/InventoryIcon.png"))
28 | add_custom_type("Equipment", "Node", equipment_script, preload("./assets/EquipmentIcon.png"))
29 | add_custom_type("EquippedItem3D", "Node3D", equipped_item_3d, preload("./assets/Equipped3DIcon.png"))
30 | add_custom_type("PickableArea2D", "Area2D", pickable_item_2d, null)
31 | add_custom_type("PickableArea3D", "Area3D", pickable_item_3d, null)
32 | add_custom_type("RadialMenuContainer", "Container", radial_menu_script, preload("./assets/RadialMenuIcon.png"))
33 |
34 |
35 | func _exit_tree() -> void:
36 | remove_custom_type("RadialMenuContainer")
37 | remove_custom_type("PickableArea3D")
38 | remove_custom_type("PickableArea2D")
39 | remove_custom_type("EquippedItem3D")
40 | remove_custom_type("Equipment")
41 | remove_custom_type("Inventory")
42 | remove_custom_type("ItemActivationEvent")
43 | remove_custom_type("DropTable")
44 | remove_custom_type("DropGroup")
45 | remove_custom_type("Drop")
46 | remove_custom_type("Drop3D")
47 | remove_custom_type("Drop2D")
48 | remove_custom_type("Item")
49 |
50 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/debug/abilities_debugger.gd:
--------------------------------------------------------------------------------
1 | extends Control
2 |
3 | @export_category("Debug UI")
4 | @export_node_path("AbilityContainer") var ability_container_path := NodePath()
5 | @export var persist_messages_for := 15.0
6 |
7 |
8 | @onready var ability_container: AbilityContainer
9 | @onready var labels: VBoxContainer = $Labels
10 | @onready var timer: Timer = $Timer
11 |
12 |
13 | func _ready() -> void:
14 | ability_container = get_node(ability_container_path) as AbilityContainer
15 |
16 | timer.timeout.connect(func ():
17 | var children = labels.get_children()
18 |
19 | if children:
20 | labels.remove_child(children[0])
21 | )
22 |
23 | assert(ability_container != null, "ability_container is null")
24 |
25 | ability_container.ability_activated.connect(_handle_activated)
26 | ability_container.ability_blocked.connect(_handle_blocked)
27 | ability_container.ability_cancelled.connect(_handle_cancelled)
28 | ability_container.ability_ended.connect(_handle_ended)
29 | ability_container.ability_granted.connect(_handle_granted)
30 | ability_container.ability_revoked.connect(_handle_revoked)
31 | ability_container.cooldown_started.connect(_handle_cooldown_started)
32 | ability_container.cooldown_ended.connect(_handle_cooldown_ended)
33 |
34 |
35 | func _handle_activated(ability: Ability, e: ActivationEvent) -> void:
36 | _make_label("activated", ability, e)
37 |
38 |
39 | func _handle_blocked(ability: Ability, e: ActivationEvent) -> void:
40 | _make_label("blocked", ability, e)
41 |
42 |
43 | func _handle_cancelled(ability: Ability, e: ActivationEvent) -> void:
44 | _make_label("cancelled", ability, e)
45 |
46 |
47 | func _handle_cooldown_ended(ability: Ability) -> void:
48 | _make_label("cooldown_ended", ability, ActivationEvent.new(ability_container))
49 |
50 |
51 | func _handle_cooldown_started(ability: Ability) -> void:
52 | _make_label("cooldown_started", ability, ActivationEvent.new(ability_container))
53 |
54 |
55 | func _handle_ended(ability: Ability, e: ActivationEvent) -> void:
56 | _make_label("ended", ability, e)
57 |
58 |
59 | func _handle_granted(ability: Ability) -> void:
60 | _make_label("granted", ability, ActivationEvent.new(ability_container))
61 |
62 |
63 | func _handle_revoked(ability: Ability) -> void:
64 | _make_label("revoked", ability, ActivationEvent.new(ability_container))
65 |
66 |
67 | func _make_label(event: String, ability: Ability, e: ActivationEvent) -> void:
68 | var label = Label.new()
69 | var timer = Timer.new()
70 |
71 | timer.one_shot = true
72 | timer.wait_time = persist_messages_for
73 |
74 | label.text = "{0} is {1} with tags {2}".format({
75 | "0": ability.ui_name,
76 | "1": event,
77 | "2": str(e.tags)
78 | })
79 |
80 | labels.add_child(label)
81 |
82 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/resources/interaction.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png")
3 | class_name Interaction extends Resource
4 |
5 | ## Represents an interaction
6 |
7 | @export_category("Interaction")
8 |
9 | @export_group("Tags added on", "tags_added_on_")
10 | ## Tags added when an interaction starts
11 | @export var tags_added_on_start: Array[String] = []
12 | ## Tags added when an interaction is blocked
13 | @export var tags_added_on_block: Array[String] = []
14 | ## Tags added when an interaction is cancelled
15 | @export var tags_added_on_cancel: Array[String] = []
16 | ## Tags added when an interaction ends
17 | @export var tags_added_on_end: Array[String] = []
18 |
19 | @export_group("Tags required to", "tags_required_to_")
20 | ## Tags required to start an interaction
21 | @export var tags_required_to_start: Array[String] = []
22 | ## Tags required to block an interaction
23 | @export var tags_required_to_block: Array[String] = []
24 | ## Tags required to cancel an interaction
25 | @export var tags_required_to_cancel: Array[String] = []
26 | ## Tags required to end an interaction
27 | @export var tags_required_to_end: Array[String] = []
28 |
29 | @export_group("Tags removed on", "tags_removed_on_")
30 | ## Tags removed when an interaction starts
31 | @export var tags_removed_on_start: Array[String] = []
32 | ## Tags removed when an interaction is blocked
33 | @export var tags_removed_on_block: Array[String] = []
34 | ## Tags removed when an interaction is cancelled
35 | @export var tags_removed_on_cancel: Array[String] = []
36 | ## Tags removed when an interaction ends
37 | @export var tags_removed_on_end: Array[String] = []
38 |
39 |
40 | ## Invoked right before an interaction ends.
41 | ## [br]It's a virtual method, so it can be overridden in a script.
42 | func on_before_interaction_end(manager: InteractionManager, interacted_node: Node) -> void:
43 | pass
44 |
45 |
46 | ## Invoked right before an interaction starts.
47 | ## [br]It's a virtual method, so it can be overridden in a script.
48 | func on_before_interaction_start(manager: InteractionManager, interacted_node: Node) -> void:
49 | pass
50 |
51 |
52 | ## Invoked each time an input is received when the interaction is active.
53 | ## [br]It's a virtual method, so it can be overridden in a script.
54 | func handle_input(input: InputEvent, manager: InteractionManager, interacted_node: Node) -> void:
55 | pass
56 |
57 |
58 | ## Invoked inside the physics process if the interaction is active.
59 | ## [br]It's a virtual method, so it can be overridden in a script.
60 | func handle_physics_process(delta: float, manager: InteractionManager, interacted_node: Node) -> void:
61 | pass
62 |
63 |
64 | ## Invoked inside the process if the interaction is active.
65 | ## [br]It's a virtual method, so it can be overridden in a script.
66 | func handle_process(delta: float, manager: InteractionManager, interacted_node: Node) -> void:
67 | pass
68 |
69 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/extended_character_nodes/nodes/2d/point_and_click_2d.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | class_name PointAndClick2D extends Node2D
3 |
4 |
5 | signal new_position_marked(position: Vector2)
6 |
7 |
8 | @export_category("PointAndClick2D")
9 | ## Activates the [PointAndClick2D].
10 | @export var active: bool = true
11 | ## The movement speed
12 | @export var movement_speed: float = 100.0
13 | ## Is the [CharacterBody2D] to move.
14 | @export var character_2d: CharacterBody2D = null
15 | ## Is the [CharacterBody2D] child [NavigationAgent2D].
16 | ## Is used to determine the path in the world.
17 | @export var navigation_agent: NavigationAgent2D = null
18 | ## If set to [code]true[/code] the [code]PointAndClick2D[/code] will move the [CharacterBody2D] automatically.
19 | @export var automatic_movement: bool = true
20 |
21 |
22 | ## Returns the mouse position in a 2D world.
23 | var mouse_position: Vector2:
24 | get:
25 | return character_2d.get_global_mouse_position()
26 |
27 |
28 | ## Handles movement
29 | func _handle_velocity_computed(_velocity: Vector2) -> void:
30 | character_2d.velocity = _velocity
31 | character_2d.move_and_slide()
32 |
33 |
34 | ## The physics process function
35 | func _physics_process(delta: float) -> void:
36 | if not Engine.is_editor_hint() and automatic_movement:
37 | move_to_clicked_position()
38 |
39 |
40 | ## The ready function
41 | func _ready() -> void:
42 | if not Engine.is_editor_hint():
43 | navigation_agent.velocity_computed.connect(_handle_velocity_computed)
44 |
45 |
46 | ## Returns [code]true[/code] if the [CharacterBody2D] can move.
47 | func can_move() -> bool:
48 | if Engine.is_editor_hint():
49 | return false
50 |
51 | if navigation_agent == null:
52 | return false
53 |
54 | if character_2d == null:
55 | return false
56 |
57 | return active
58 |
59 |
60 | ## Returns [code]true[/code] if the [code]navigation_agent[/code] is not [code]null[/code] and the [code]navigation_agent[/code] has finished navigating.
61 | func is_navigation_finished() -> bool:
62 | if navigation_agent != null:
63 | return navigation_agent.is_navigation_finished()
64 | return false
65 |
66 |
67 | ## Moves the [code]character_2d[/code] to the clicked position with the given speed.
68 | func move_to_clicked_position() -> void:
69 | if can_move():
70 | var _velocity: Vector2 = (navigation_agent.get_next_path_position() - global_position).normalized() * movement_speed
71 |
72 | if navigation_agent.avoidance_enabled:
73 | navigation_agent.set_velocity(_velocity)
74 | else:
75 | _handle_velocity_computed(_velocity)
76 |
77 |
78 | ## Sets the [code]navigation_agent[/code] target position to current click position.
79 | func set_new_movement_position() -> void:
80 | if can_move():
81 | navigation_agent.target_position = mouse_position
82 | new_position_marked.emit(navigation_agent.target_position)
83 |
84 |
85 | ## Stops the [code]navigation_agent[/code] movement.
86 | func stop_movement() -> void:
87 | if can_move():
88 | navigation_agent.target_position = character_2d.position
89 |
90 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/nodes/drop.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | class_name Drop extends Node
3 |
4 | ## It is the node responsible for dropping an array of [Items].
5 | ##
6 |
7 | ## Emitted when some [Item]s have been dropped.
8 | signal items_dropped(items: Array[Item], drop_owner: Node)
9 |
10 |
11 | @export_category("Drop")
12 | ## The table used to drop items
13 | @export var drop_table: DropTable = null
14 | ## The path to the owning node. This will determine if the drop will be performed in a 2D or 3D world.
15 | ## [br]Note that the drop will occur at the same position of the owning node.
16 | @export_node_path("Node2D", "Node3D") var owning_node_path := NodePath()
17 |
18 |
19 | ## It could be either a [Node2D] or [Node3D]
20 | var owning_node: Variant:
21 | get:
22 | if owning_node_path != null and not owning_node_path.is_empty():
23 | return get_node(owning_node_path)
24 | return null
25 |
26 |
27 | ## Called when some [Item]s have been dropped.
28 | func _drop(items: Array[Item]) -> void:
29 | var instances: Array[Node] = []
30 | var _owning_node = owning_node
31 |
32 | if _owning_node == null:
33 | return
34 |
35 | for item in items:
36 | if item.scene and item.scene.can_instantiate():
37 | var instance = item.scene.instantiate()
38 | var nearest_drop = find_nearest_drop()
39 |
40 | instance.position = _owning_node.position
41 |
42 | if nearest_drop:
43 | nearest_drop.add_child(instance)
44 | nearest_drop.item_dropped.emit(item, instance)
45 |
46 |
47 | ## It gets the droppable [Item]s from the [DropTable].
48 | ## [br]It accepts an optional argument [code]drop_modifier[/code] which increases (or decreases if negative) drop chances for each [DropGroup] pool.
49 | ## [br]The greater the modifier, the higher the chances to drop an [Item] from the [member DropTable.pools].
50 | func drop_items(drop_modifier: float = 0.0) -> void:
51 | var items = drop_table.drop(drop_modifier)
52 |
53 | if items.size() > 0:
54 | # Who knows what happens in this method. Let's await it.
55 | await _drop(items)
56 |
57 | items_dropped.emit(items)
58 |
59 |
60 | ## Finds the nearest [Drop2D] or [Drop3D] node.
61 | ## [br]This could be useful in a world-partitioned scenario (like open world rpgs).
62 | func find_nearest_drop() -> Node:
63 | var owner_node = owning_node
64 |
65 | if owner_node == null:
66 | return null
67 |
68 | # gets all dropzones
69 | var dropzones = get_tree().get_nodes_in_group("ggs.drop-node").duplicate()
70 | var position = owner_node.position
71 | var is_2d = (owner_node as Node2D) != null
72 | var is_3d = (owner_node as Node3D) != null
73 |
74 | if dropzones.size() == 0:
75 | return null
76 |
77 |
78 | # the sorting function, we will take the nearest [Drop2D] or [Drop3D] node.
79 | dropzones.sort_custom(func (a: Variant, b: Variant) -> bool:
80 | if a is Node2D and b is Node2D and is_2d:
81 | return b.position.distance_to(position) > a.position.distance_to(position)
82 | elif a is Node3D and b is Node3D and is_3d:
83 | return b.position.distance_to(position) > a.position.distance_to(position)
84 |
85 | return false
86 | )
87 |
88 | return dropzones[0]
89 |
90 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/objects/attribute_spec.gd:
--------------------------------------------------------------------------------
1 | class_name AttributeSpec extends RefCounted
2 |
3 | ## Represents an attribute as a class
4 |
5 | ## Emitted when the instance has changed one of it's values
6 | signal changed(current: AttributeSpec)
7 | ## Emitted when the instance has changed it's minimum value
8 | signal minimum_value_changed(from: float, to: float)
9 | ## Emitted when the instance has changed it's maximum value
10 | signal maximum_value_changed(from: float, to: float)
11 |
12 | ## used internally to avoid the initial signals propagation
13 | var __setup := false
14 | ## It's the attribute name
15 | var attribute_name := ""
16 | ## It's the buff amount
17 | var buffing_value := 0.0
18 | ## It's the current attribute's value
19 | var current_value := 0.0:
20 | get:
21 | return current_value
22 | set(value):
23 | var previous_value = current_value
24 |
25 | if previous_value == value:
26 | return
27 |
28 | if maximum_value > minimum_value:
29 | current_value = clampf(value, minimum_value, maximum_value)
30 | elif maximum_value == minimum_value and maximum_value == 0.0:
31 | current_value = value
32 | else:
33 | if maximum_value == 0.0 and minimum_value > 0.0:
34 | current_value = clampf(value, value + 1, minimum_value)
35 | else:
36 | current_value = clampf(value, maximum_value, minimum_value)
37 | if not __setup:
38 | changed.emit(self)
39 | ## It's the maximum attribute's value. Keep 0 to mark it as infinite
40 | var maximum_value := 0.0:
41 | get:
42 | return maximum_value
43 | set(value):
44 | var previous_value = maximum_value
45 | maximum_value = value
46 |
47 | if not __setup:
48 | maximum_value_changed.emit(previous_value, value)
49 | ## It's the maximum attribute's value.
50 | var minimum_value := 0.0:
51 | get:
52 | return minimum_value
53 | set(value):
54 | var previous_value = minimum_value
55 | minimum_value = value
56 |
57 | if not __setup:
58 | minimum_value_changed.emit(previous_value, value)
59 | ## The calculated attribute value with the [member AttributeSpec.buffing_value].
60 | var current_buffed_value: float = 0.0:
61 | get:
62 | return current_value + buffing_value
63 |
64 |
65 | func apply_attribute_effect(attribute_effect: AttributeEffect) -> void:
66 | var value = attribute_effect.get_current_value()
67 |
68 | if attribute_effect.applies_as == 1:
69 | buffing_value += value
70 | else:
71 | if buffing_value > 0.0 and value < 0.0:
72 | var diff = buffing_value + value
73 |
74 | buffing_value = clampf(diff, 0, buffing_value)
75 | current_value += diff
76 | else:
77 | current_value += value
78 |
79 |
80 | ## Creates an instance of [AttributeSpec] starting from an [AttributeResource]
81 | static func from_attribute(attribute: AttributeResource) -> AttributeSpec:
82 | var instance = AttributeSpec.new()
83 | instance.__setup = true
84 | instance.attribute_name = attribute.attribute_name
85 | instance.maximum_value = attribute.maximum_value
86 | instance.minimum_value = attribute.minimum_value
87 | instance.current_value = attribute.current_value
88 | instance.__setup = false
89 | return instance
90 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/turn_based/nodes/TurnBasedGame.gd:
--------------------------------------------------------------------------------
1 | class_name TurnBasedGame extends Node
2 |
3 |
4 | ## Emitted when a turn changes.
5 | signal turn_changed(manager: TurnBasedGame)
6 | ## Emitted when the turn based game starts.
7 | signal turn_game_started()
8 | ## Emitted when the turn based game stops.
9 | signal turn_game_stopped()
10 | ## Emitted when a subscriber is added.
11 | signal subscriber_added(subscriber: TurnSubscriber)
12 | ## Emitted when a subscriber is removed.
13 | signal subscriber_removed(subscriber: TurnSubscriber)
14 |
15 |
16 | ## The current turn.
17 | var current_turn: int = 0
18 | ## The current turn subscriber.
19 | var current_turn_subscriber: TurnSubscriber:
20 | get:
21 | if subscribers.size() == 0:
22 | return null
23 |
24 | return subscribers[current_turn]
25 | ## The turn subscribers.
26 | var subscribers: Array[TurnSubscriber] = []
27 |
28 |
29 | func _ready() -> void:
30 | add_to_group("ggs.turnbased")
31 |
32 |
33 | func _sort_subscribers() -> void:
34 | subscribers.sort_custom(func (a, b):
35 | return a.priority < b.priority
36 | )
37 |
38 |
39 | ## Called when a [TurnSubscriber] is subscribed.
40 | ## [br]This is a virtual method
41 | func _subscriber_added(sub: TurnSubscriber) -> void:
42 | pass
43 |
44 |
45 | ## Called when a [TurnSubscriber] is unsubscribed.
46 | ## [br]This is a virtual method
47 | func _subscriber_removed(sub: TurnSubscriber) -> void:
48 | pass
49 |
50 |
51 | ## Adds a [TurnSubscriber]
52 | func add_subscriber(sub: TurnSubscriber) -> bool:
53 | if subscribers.has(sub):
54 | return false
55 |
56 | subscribers.append(sub)
57 | subscriber_added.emit(sub)
58 |
59 | _sort_subscribers()
60 |
61 | return true
62 |
63 |
64 | ## Ends the current turn
65 | func end_turn_sequence() -> void:
66 | if subscribers.size() == 0:
67 | return
68 |
69 | subscribers[current_turn].end_turn()
70 |
71 | current_turn = 0
72 |
73 | turn_game_stopped.emit()
74 |
75 |
76 | ## Calls next turn
77 | func next_turn() -> void:
78 | var subscribers_count := subscribers.size()
79 |
80 | if current_turn < subscribers_count:
81 | subscribers[current_turn].turn_ended.emit()
82 | subscribers[current_turn]._turn_ended()
83 |
84 | current_turn += 1
85 |
86 | if current_turn >= subscribers_count:
87 | current_turn = 0
88 |
89 | subscribers[current_turn].turn_started.emit()
90 | subscribers[current_turn]._turn_started()
91 |
92 |
93 | ## Removes a [TurnSubscriber]
94 | func remove_subscriber(sub: TurnSubscriber) -> bool:
95 | if not subscribers.has(sub):
96 | return false
97 |
98 | var sub_turn_index = subscribers.find(sub)
99 |
100 | subscribers.remove_at(sub_turn_index)
101 | subscriber_removed.emit(sub)
102 |
103 | _sort_subscribers()
104 |
105 | return true
106 |
107 |
108 | ## Starts the turn based game.
109 | func start_turn_sequence() -> void:
110 | if subscribers.size() == 0:
111 | return
112 |
113 | current_turn = 0
114 |
115 | subscribers[current_turn]._turn_started()
116 | subscribers[current_turn].turn_started.emit()
117 |
118 | turn_game_started.emit()
119 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_effect_editor.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends EditorProperty
3 |
4 | var add_row_button := Button.new()
5 | var scroll_container := VBoxContainer.new()
6 | var resource_input := EditorResourcePicker.new()
7 | var row_editor_scene = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_effect_editor_row.tscn")
8 | var vbox_container := VBoxContainer.new()
9 |
10 |
11 | func _draw_effects_row(table: AttributeTable, res: AttributeEffect, index: int) -> Control:
12 | var instance = row_editor_scene.instantiate()
13 |
14 | instance.set_values(table, res)
15 |
16 | instance.removed.connect(func ():
17 | var target = get_edited_object() as GameplayEffect
18 | target.attributes_affected.erase(res)
19 | _update_ui()
20 | )
21 |
22 | res.changed.connect(func ():
23 | var prop = get_edited_property()
24 | var target = get_edited_object()
25 |
26 | target[prop][index] = res;
27 | )
28 |
29 | return instance
30 |
31 |
32 | func _draw_effects_rows() -> void:
33 | var target = get_edited_object() as GameplayEffect
34 | var index = 0
35 |
36 | _empty_scroll_container()
37 |
38 | if target and target.attributes_affected:
39 | for eff in target.attributes_affected:
40 | scroll_container.add_child(_draw_effects_row(target.table, eff, index))
41 | index += 1
42 |
43 |
44 | func _handle_add_attribute_effect() -> void:
45 | var target = get_edited_object() as GameplayEffect
46 |
47 | target.attributes_affected.append(AttributeEffect.new())
48 |
49 | _update_ui()
50 |
51 |
52 | func _init() -> void:
53 | add_row_button.text = "Add effect"
54 | add_row_button.hide()
55 |
56 | resource_input.base_type = "AttributeTable"
57 |
58 | vbox_container.add_child(resource_input)
59 | vbox_container.add_child(scroll_container)
60 | vbox_container.add_child(add_row_button)
61 |
62 | add_row_button.pressed.connect(_handle_add_attribute_effect)
63 |
64 | resource_input.resource_changed.connect(func (res):
65 | _handle_attribute_table_changed(res)
66 | )
67 |
68 | resource_input.resource_selected.connect(func (res, inspector):
69 | _handle_attribute_table_changed(res)
70 | )
71 |
72 | add_child(vbox_container)
73 |
74 | _update_ui()
75 |
76 |
77 | func _empty_scroll_container() -> void:
78 | for child in scroll_container.get_children():
79 | scroll_container.remove_child(child)
80 |
81 |
82 | func _handle_attribute_table_changed(res: AttributeTable) -> void:
83 | get_edited_object().table = res
84 |
85 | if res:
86 | add_row_button.show()
87 | else:
88 | add_row_button.hide()
89 |
90 |
91 | func _update_property() -> void:
92 | var target = get_edited_object() as GameplayEffect
93 |
94 | resource_input.edited_resource = target.table
95 |
96 | _update_ui()
97 |
98 |
99 | func _update_ui() -> void:
100 | var target = get_edited_object()
101 |
102 | _empty_scroll_container()
103 | _draw_effects_rows()
104 |
105 | if target and target.table:
106 | add_row_button.show()
107 | else:
108 | add_row_button.hide()
109 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/extended_character_nodes/nodes/3d/point_and_click_3d.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | class_name PointAndClick3D extends Node3D
3 |
4 |
5 | ## Emitted when the player clicks a new position
6 | signal new_position_marked(position: Vector3)
7 |
8 |
9 | @export_category("PointAndClick3D")
10 | ## Activates the [PointAndClick3D].
11 | @export var active: bool = true
12 | ## The movement speed
13 | @export var movement_speed: float = 100.0
14 | ## Is the [CharacterBody3D] to move.
15 | @export var character_3d: CharacterBody3D = null
16 | ## Is the [CharacterBody3D] child [NavigationAgent3D].
17 | ## Is used to determine the path in the world.
18 | @export var navigation_agent: NavigationAgent3D = null
19 | ## If set to [code]true[/code] the [PointAndClick3D] will move the [CharacterBody3D] automatically.
20 | @export var automatic_movement: bool = true
21 |
22 | @export_flags_3d_physics var clickable_layer: int = 1
23 |
24 |
25 | ## Gets the current mouse position in the world.
26 | var mouse_position: Vector3:
27 | get:
28 | if character_3d == null:
29 | return Vector3.ZERO
30 |
31 | var camera = get_viewport().get_camera_3d()
32 | var mouse_position = get_viewport().get_mouse_position()
33 | var direct_space_state = get_world_3d().direct_space_state
34 | var pos = camera.project_position(mouse_position, 100.0)
35 | var raycast_origin = camera.project_ray_origin(mouse_position)
36 | var raycast_end = (camera.project_ray_normal(mouse_position) + raycast_origin) * 2000.0
37 | # var query = PhysicsRayQueryParameters3D.new().create(raycast_origin, raycast_end, clickable_layer, [character_3d])
38 | var query = PhysicsRayQueryParameters3D.new().create(raycast_origin, pos, clickable_layer, [character_3d])
39 | var raycast_array = direct_space_state.intersect_ray(query)
40 | var target_position = raycast_array.get("position", Vector3.ZERO)
41 |
42 | if target_position != Vector3.ZERO:
43 | return target_position
44 |
45 | return Vector3.ZERO
46 |
47 |
48 | func _handle_velocity_computed(_velocity: Vector3) -> void:
49 | character_3d.velocity = _velocity
50 | character_3d.move_and_slide()
51 |
52 |
53 | ## The physics process function
54 | func _physics_process(delta: float) -> void:
55 | if not Engine.is_editor_hint() and automatic_movement:
56 | move_to_clicked_position()
57 |
58 |
59 | ## The ready function
60 | func _ready() -> void:
61 | if not Engine.is_editor_hint():
62 | navigation_agent.velocity_computed.connect(_handle_velocity_computed)
63 |
64 |
65 | ## Returns [code]true[/code] if the navigation path's final position has been reached.
66 | func is_navigation_finished() -> bool:
67 | if navigation_agent != null:
68 | return navigation_agent.is_navigation_finished()
69 | return false
70 |
71 |
72 | ## Moves the [CharacterBody3D] to the clicked position.
73 | func move_to_clicked_position() -> void:
74 | if Engine.is_editor_hint():
75 | return
76 |
77 | if navigation_agent == null:
78 | return
79 |
80 | if navigation_agent.is_navigation_finished():
81 | return
82 |
83 | if not active:
84 | return
85 |
86 | var _velocity: Vector3 = (navigation_agent.get_next_path_position() - global_position).normalized() * movement_speed
87 |
88 | if navigation_agent.avoidance_enabled:
89 | navigation_agent.set_velocity(_velocity)
90 | else:
91 | _handle_velocity_computed(_velocity)
92 |
93 |
94 | ## Sets the [code]navigation_agent[/code] target position to current click position.
95 | func set_new_movement_position() -> void:
96 | if Engine.is_editor_hint():
97 | return
98 |
99 | if navigation_agent == null:
100 | return
101 |
102 | var target_position = mouse_position
103 |
104 | if target_position != Vector3.ZERO:
105 | navigation_agent.target_position = target_position
106 | new_position_marked.emit(target_position)
107 |
108 |
109 | ## Stops the [code]navigation_agent[/code] movement.
110 | func stop_movement() -> void:
111 | navigation_agent.target_position = character_3d.position
112 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/resources/item.gd:
--------------------------------------------------------------------------------
1 | @icon("res://addons/godot_gameplay_systems/inventory_system/assets/ItemIcon.png")
2 | @tool
3 | class_name Item extends Resource
4 |
5 |
6 | ## Represents an item in the world
7 |
8 |
9 | @export_category("Item")
10 | ## Name given to this item.
11 | ## [br]Use it just for your internal purpose, you should use a dictionary of name->i18n values for multilanguage purpose.
12 | @export var name: StringName = ""
13 | ## Determines if an [Item] can stack or not.
14 | @export var can_stack: bool = false
15 | ## If [member Item.can_stack] is [code]true[/code], it will decrease automatically it's own stack on activation.
16 | @export var decrease_stack_on_use: bool = false
17 | ## The rendered (2D or 3D) item into the game world, or in front of the camera.
18 | @export var scene: PackedScene = null
19 | ## Tags used to categorize this [Item].
20 | @export var tags: Array[String] = []
21 |
22 | @export_group("Stacking", "quantity_")
23 | ## Current quantity.
24 | @export var quantity_current: int = 1
25 | ## If [member Item.can_stack] is set to [code]true[/code].
26 | ## [br]Set it to [code]0[/code] for infinite stacking (e.g. currency and similar).
27 | @export var quantity_max: int = 1
28 |
29 | @export_group("Tags", "tags_")
30 | ## Tags added to [Inventory] and [Equipment] on [Item] activation.
31 | @export var tags_added_on_activation: Array[String] = []
32 | ## Tags added to [Inventory] on [Item] add.
33 | ## [br]Note, if some [Item]s are added to an [Inventory] through the editor, these tags will be added inside
34 | ## the [method Inventory._ready] method.
35 | @export var tags_added_on_add: Array[String] = []
36 | ## Tags added to [Equipment] when the [Item] is equipped.
37 | @export var tags_added_on_equip: Array[String] = []
38 | ## Tags added to [Inventory] and [Equipment] on [Item] remove.
39 | @export var tags_added_on_remove: Array[String] = []
40 | ## Tags added to [Equipment] owner when the [Item] is unequipped.
41 | @export var tags_added_on_unequip: Array[String] = []
42 | ## Requires an [Inventory] or [Equipment] to have all these tags before activating this [Item].
43 | @export var tags_required_to_activate: Array[String] = []
44 | ## Requires an [Inventory] to have all these tags before adding this [Item].
45 | @export var tags_required_to_add: Array[String] = []
46 | ## Requires an [Equipment] to have all these tags before equipping this [Item].
47 | @export var tags_required_to_equip: Array[String] = []
48 | ## Requires an [Inventory] to have all these tags before removing this [Item].
49 | @export var tags_required_to_remove: Array[String] = []
50 | ## Requires an [Equipment] to have all these tags before unequipping this [Item].
51 | @export var tags_required_to_unequip: Array[String] = []
52 | ## Tags removed from [Inventory] and [Equipment] on [Item] activation.
53 | @export var tags_removed_on_activation: Array[String] = []
54 | ## Tags removed from [Inventory] on [Item] add.
55 | @export var tags_removed_on_add: Array[String] = []
56 | ## Tags removed from the [Equipment] when the [Item] is equipped.
57 | @export var tags_removed_on_equip: Array[String] = []
58 | ## Tags removed from [Inventory] and [Equipment] on [Item] remove.
59 | @export var tags_removed_on_remove: Array[String] = []
60 | ## Tags removed from the [Equipment] when the [Item] is unequipped.
61 | @export var tags_removed_on_unequip: Array[String] = []
62 |
63 |
64 | ## A virtual method called when the item is activated. Override it to determine how the item behaves on activation.
65 | func _activate(_activation_event: ItemActivationEvent) -> void:
66 | pass
67 |
68 |
69 | ## Returns if the item has the right tags to be activated. Override it to determine if the item can be added to an [Inventory].
70 | func _can_activate(_activation_event: ItemActivationEvent) -> bool:
71 | return true
72 |
73 |
74 | ## A virtual method called when this item is equipped.
75 | func _equip(_equipment: Equipment, _equipment_slot: EquipmentSlot) -> void:
76 | pass
77 |
78 |
79 | ## A virtual method called when this item is unequipped.
80 | func _unequip(_equipment: Equipment, _equipment_slot: EquipmentSlot) -> void:
81 | pass
82 |
83 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute_effect.gd:
--------------------------------------------------------------------------------
1 | @icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect.svg")
2 |
3 | class_name AttributeEffect extends Resource
4 |
5 | ## An AttributeEffect is a resource which stores all effects mutation informations
6 | ##
7 | ## It is consumed once by a GameplayAttributeMap if it's lifetime is "oneshot", otherwise
8 | ## it will be consumed each [member AttributeEffect.apply_every_second] seconds for a maximum of [member AttributeEffect.max_applications] times
9 |
10 |
11 | enum {
12 | ## The effect will be applied once
13 | LIFETIME_ONE_SHOT = 0,
14 | ## The effect will be applied each tot seconds
15 | LIFETIME_TIME_BASED = 1,
16 | }
17 |
18 | @export_category("Effect life-time")
19 | ## The attribute effect life-time:[br]
20 | ## LIFETIME_ONE_SHOT [b]0[/b] is equal to a oneshot effect, aka applied once.[br]
21 | ## LIFETIME_TIME_BASED [b]1[/b] is equal to a time-based effect, applied each [member AttributeEffect.apply_every_second] seconds for a maximum of [member AttributeEffect.max_applications] times
22 | @export_enum("One-Shot", "Time-Based") var life_time = 0:
23 | get:
24 | return life_time
25 | set(value):
26 | life_time = value
27 | emit_changed()
28 | ## If [member life_time] is [b]1[/b], then the effect is applied each seconds as specified by this parameter
29 | @export var apply_every_second := 1.0:
30 | get:
31 | return apply_every_second
32 | set(value):
33 | apply_every_second = value
34 | emit_changed()
35 | ## If [member life_time] is [b]1[/b], then the effect is applied each seconds as specified by [member apply_every_second] for a maximum of times specified by this parameter
36 | @export var max_applications := 1:
37 | get:
38 | return max_applications
39 | set(value):
40 | max_applications = value
41 | emit_changed()
42 |
43 | @export_category("Attribute modifier")
44 | @export_enum("Value modification", "Value buff") var applies_as = 0:
45 | get:
46 | return applies_as
47 | set(value):
48 | applies_as = value
49 | emit_changed()
50 | ## The attribute name to mutate
51 | @export var attribute_name := "":
52 | get:
53 | return attribute_name
54 | set(value):
55 | attribute_name = value
56 | emit_changed()
57 | ## The minimum value.
58 | ## [br]Can be negative
59 | @export var minimum_value := 0.0:
60 | get:
61 | return minimum_value
62 | set(value):
63 | minimum_value = value
64 | emit_changed()
65 | ## The minimum value.
66 | ## [br]Can be negative
67 | @export var maximum_value := 0.0:
68 | get:
69 | return maximum_value
70 | set(value):
71 | maximum_value = value
72 | emit_changed()
73 | @export_group("Pipeline")
74 | ## If it is set, it will call [method AttributeEffectCondition.should_apply]
75 | @export var condition: Resource = null:
76 | get:
77 | return condition
78 | set(value):
79 | condition = value
80 | emit_changed()
81 |
82 |
83 | ## Gets the computed current value.[br]
84 | ##
85 | ## If both [member minimum_value] and [member maximum_value] are different from [code]0.0[/code] the resulting [code]float[/code] is a value between
86 | ## [br] - [member minimum_value] and [member maximum_value] if minimum_value is less than maximum
87 | ## [br] - [member maximum_value] and [member minimum_value] if minimum_value is greater than maximum
88 | func get_current_value() -> float:
89 | if minimum_value < maximum_value:
90 | return randf_range(minimum_value, maximum_value)
91 | elif minimum_value > maximum_value:
92 | return randf_range(maximum_value, minimum_value)
93 | else:
94 | return minimum_value
95 |
96 |
97 | ## Checks if the current attribute effect can be applied
98 | func should_apply(gameplay_effect: GameplayEffect, gameplay_attribute_map: GameplayAttributeMap) -> bool:
99 | if condition == null:
100 | return true
101 |
102 | if condition.has_method("should_apply"):
103 | return condition.should_apply(self, gameplay_effect, gameplay_attribute_map)
104 |
105 | printerr("condition has not a method should_apply(attribute_effect: AttributeEffect, effect: GameplayEffect, gameplay_attributes: GameplayAttributeMap) -> bool")
106 |
107 | return true
108 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/plugin.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends EditorPlugin
3 |
4 | const ATTRIBUTE_SPEC_NAME = "AttributeSpec"
5 | const ABILITY_CONTAINER_NAME = "AbilityContainer"
6 | const ACTIVATION_EVENT_NAME = "ActivationEvent"
7 | const ATTRIBUTE_RESOURCE_NAME = "AttributeResource"
8 | const ATTRIBUTE_TABLE_RESOURCE_NAME = "AttributeTableResource"
9 | const ATTRIBUTE_EFFECT_RESOURCE_NAME = "AttributeEffectResource"
10 | const ATTRIBUTE_EFFECT_CONDITION_RESOURCE_NAME = "AttributeEffectCondition"
11 | const GAMEPLAY_EFFECT_NAME = "GameplayEffect"
12 | const GAMEPLAY_ATTRIBUTE_MAP_NAME = "GameplayAttributeMap"
13 | const EFFECTED_AREA2D = "EffectedArea2D"
14 | const EFFECTED_AREA3D = "EffectedArea3D"
15 | const STOP_EFFECT_IF0_RESOURCE_NAME = "StopEffectIfAttributeIs0"
16 |
17 | const attribute_spec_script = preload("./objects/attribute_spec.gd")
18 | const ability_container_resource = preload("./nodes/ability_container.gd")
19 | const activation_event_script = preload("./objects/activation_event.gd")
20 | const attributes_table_resource = preload("./resources/attribute_table.gd")
21 | const attribute_resource = preload("./resources/attribute.gd")
22 | const attribute_effect_resource = preload("./resources/attribute_effect.gd")
23 | const attribute_effect_condition_resource = preload("./resources/attribute_effect_condition.gd")
24 | const effected_area2d = preload("./nodes/effected_area2d.gd")
25 | const effected_area3d = preload("./nodes/effected_area3d.gd")
26 | const gameplay_effect = preload("./nodes/gameplay_effect.gd")
27 | const gameplay_attribute_map = preload("./nodes/gameplay_attribute_map.gd")
28 | const stop_effect_if0_resource = preload("./resources/stop_effect_if_0.gd")
29 |
30 | const attribute_inspector_plugin_script = preload("./inspector/gameplay_attribute_map_inspector_plugin.gd")
31 | const effect_inspector_plugin_script = preload("./inspector/gameplay_effect_inspector_plugin.gd")
32 |
33 |
34 | var attribute_inspector_plugin: EditorInspectorPlugin
35 | var effect_inspector_plugin: EditorInspectorPlugin
36 |
37 |
38 | func _enter_tree():
39 | add_custom_type(ACTIVATION_EVENT_NAME, "RefCounted", activation_event_script, null)
40 | add_custom_type(ATTRIBUTE_SPEC_NAME, "RefCounted", attribute_spec_script, null)
41 |
42 | add_custom_type(ATTRIBUTE_RESOURCE_NAME, "Resource", attribute_resource, preload("./assets/Attribute@0.15x.png"))
43 | add_custom_type(ATTRIBUTE_TABLE_RESOURCE_NAME, "Resource", attributes_table_resource, preload("./assets/AttributeTable@0.15x.png"))
44 | add_custom_type(ATTRIBUTE_EFFECT_RESOURCE_NAME, "Resource", attribute_effect_resource, preload("./assets/GameplayEffect@0.15x.png"))
45 | add_custom_type(ATTRIBUTE_EFFECT_CONDITION_RESOURCE_NAME, "Resource", attribute_effect_condition_resource, null)
46 | add_custom_type(GAMEPLAY_ATTRIBUTE_MAP_NAME, "Node", gameplay_attribute_map, preload("./assets/GameplayAttributeMap@0.15x.png"))
47 | add_custom_type(GAMEPLAY_EFFECT_NAME, "Node", gameplay_effect, preload("./assets/GameplayEffect@0.15x.png"))
48 | add_custom_type(EFFECTED_AREA2D, "Area2D", effected_area2d, null)
49 | add_custom_type(EFFECTED_AREA3D, "Area3D", effected_area3d, null)
50 | add_custom_type(STOP_EFFECT_IF0_RESOURCE_NAME, ATTRIBUTE_EFFECT_CONDITION_RESOURCE_NAME, stop_effect_if0_resource, null)
51 |
52 | add_custom_type(ABILITY_CONTAINER_NAME, "Node", ability_container_resource, preload("./assets/AbilityContainer@0.15x.png"))
53 |
54 | attribute_inspector_plugin = attribute_inspector_plugin_script.new()
55 | effect_inspector_plugin = effect_inspector_plugin_script.new()
56 |
57 | add_inspector_plugin(attribute_inspector_plugin)
58 | add_inspector_plugin(effect_inspector_plugin)
59 |
60 |
61 | func _exit_tree():
62 | remove_custom_type(ATTRIBUTE_RESOURCE_NAME)
63 | remove_custom_type(ATTRIBUTE_TABLE_RESOURCE_NAME)
64 | remove_custom_type(ATTRIBUTE_EFFECT_RESOURCE_NAME)
65 | remove_custom_type(ATTRIBUTE_EFFECT_CONDITION_RESOURCE_NAME)
66 | remove_custom_type(GAMEPLAY_ATTRIBUTE_MAP_NAME)
67 | remove_custom_type(GAMEPLAY_EFFECT_NAME)
68 | remove_custom_type(EFFECTED_AREA2D)
69 | remove_custom_type(EFFECTED_AREA3D)
70 | remove_custom_type(STOP_EFFECT_IF0_RESOURCE_NAME)
71 |
72 | remove_custom_type(ABILITY_CONTAINER_NAME)
73 |
74 | remove_custom_type(ATTRIBUTE_SPEC_NAME)
75 | remove_custom_type(ACTIVATION_EVENT_NAME)
76 |
77 | remove_inspector_plugin(attribute_inspector_plugin)
78 | remove_inspector_plugin(effect_inspector_plugin)
79 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_effect_editor_row.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=4 format=3 uid="uid://caqvpv434u0tf"]
2 |
3 | [ext_resource type="Script" path="res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_effect_editor_row.gd" id="1_ntmni"]
4 | [ext_resource type="Texture2D" uid="uid://qdrl51ys1mv7" path="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot@0.15x.png" id="2_twl3n"]
5 | [ext_resource type="Texture2D" uid="uid://bbd35kb6frop4" path="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect@0.15x.png" id="3_iu2ar"]
6 |
7 | [node name="AttributeEffectRow" type="GridContainer"]
8 | offset_right = 262.0
9 | offset_bottom = 140.0
10 | script = ExtResource("1_ntmni")
11 |
12 | [node name="AppliesAs" type="BoxContainer" parent="."]
13 | layout_mode = 2
14 |
15 | [node name="OptionButton" type="OptionButton" parent="AppliesAs"]
16 | layout_mode = 2
17 | size_flags_horizontal = 3
18 | item_count = 2
19 | selected = 0
20 | popup/item_0/text = "Alters value directly"
21 | popup/item_0/id = 0
22 | popup/item_1/text = "Applies buff"
23 | popup/item_1/id = 1
24 |
25 | [node name="AttributeName" type="BoxContainer" parent="."]
26 | layout_mode = 2
27 |
28 | [node name="OptionButton" type="OptionButton" parent="AttributeName"]
29 | layout_mode = 2
30 | size_flags_horizontal = 3
31 |
32 | [node name="RemoveButton" type="Button" parent="AttributeName"]
33 | layout_mode = 2
34 | text = "Remove"
35 |
36 | [node name="MinimumValue" type="BoxContainer" parent="."]
37 | layout_mode = 2
38 |
39 | [node name="Label" type="Label" parent="MinimumValue"]
40 | layout_mode = 2
41 | size_flags_horizontal = 3
42 | text = "Minimum value applied"
43 | clip_text = true
44 |
45 | [node name="SpinBox" type="SpinBox" parent="MinimumValue"]
46 | layout_mode = 2
47 | size_flags_horizontal = 3
48 | allow_greater = true
49 | allow_lesser = true
50 | alignment = 2
51 | update_on_text_changed = true
52 | select_all_on_focus = true
53 |
54 | [node name="MaximumValue" type="BoxContainer" parent="."]
55 | layout_mode = 2
56 |
57 | [node name="Label" type="Label" parent="MaximumValue"]
58 | layout_mode = 2
59 | size_flags_horizontal = 3
60 | text = "Maximum value applied"
61 | clip_text = true
62 |
63 | [node name="SpinBox" type="SpinBox" parent="MaximumValue"]
64 | layout_mode = 2
65 | size_flags_horizontal = 3
66 | allow_greater = true
67 | allow_lesser = true
68 | alignment = 2
69 | update_on_text_changed = true
70 | select_all_on_focus = true
71 |
72 | [node name="LifeTime" type="BoxContainer" parent="."]
73 | layout_mode = 2
74 |
75 | [node name="Label" type="Label" parent="LifeTime"]
76 | layout_mode = 2
77 | size_flags_horizontal = 3
78 | text = "Life time type"
79 |
80 | [node name="OptionButton" type="OptionButton" parent="LifeTime"]
81 | layout_mode = 2
82 | size_flags_horizontal = 3
83 | alignment = 1
84 | item_count = 2
85 | selected = 0
86 | popup/item_0/text = "One Shot"
87 | popup/item_0/icon = ExtResource("2_twl3n")
88 | popup/item_0/id = 0
89 | popup/item_1/text = "Time-Based"
90 | popup/item_1/icon = ExtResource("3_iu2ar")
91 | popup/item_1/id = 1
92 |
93 | [node name="VBoxContainer" type="VBoxContainer" parent="."]
94 | visible = false
95 | layout_mode = 2
96 |
97 | [node name="Lifetime Setup" type="Label" parent="VBoxContainer"]
98 | custom_minimum_size = Vector2(2.08165e-12, 50)
99 | layout_mode = 2
100 | text = "Life time settings"
101 | horizontal_alignment = 1
102 | vertical_alignment = 1
103 |
104 | [node name="TimerSetupContainer" type="VBoxContainer" parent="VBoxContainer"]
105 | layout_mode = 2
106 | size_flags_horizontal = 3
107 |
108 | [node name="TimeoutLabel" type="Label" parent="VBoxContainer/TimerSetupContainer"]
109 | layout_mode = 2
110 | size_flags_horizontal = 3
111 | text = "Seconds"
112 |
113 | [node name="TimeoutSpinBox" type="SpinBox" parent="VBoxContainer/TimerSetupContainer"]
114 | layout_mode = 2
115 | size_flags_horizontal = 3
116 | allow_greater = true
117 | allow_lesser = true
118 | alignment = 2
119 | update_on_text_changed = true
120 | select_all_on_focus = true
121 |
122 | [node name="VSeparator" type="VSeparator" parent="VBoxContainer/TimerSetupContainer"]
123 | layout_mode = 2
124 | size_flags_horizontal = 3
125 |
126 | [node name="ApplicationCountLabel" type="Label" parent="VBoxContainer/TimerSetupContainer"]
127 | layout_mode = 2
128 | size_flags_horizontal = 3
129 | text = "Apply times (0 = infinite)"
130 |
131 | [node name="ApplicationCountSpinBox" type="SpinBox" parent="VBoxContainer/TimerSetupContainer"]
132 | layout_mode = 2
133 | size_flags_horizontal = 3
134 | allow_greater = true
135 | allow_lesser = true
136 | alignment = 2
137 | update_on_text_changed = true
138 | select_all_on_focus = true
139 |
140 | [node name="HSeparator" type="HSeparator" parent="."]
141 | custom_minimum_size = Vector2(2.08165e-12, 20)
142 | layout_mode = 2
143 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/slideshow/slide_show.gd:
--------------------------------------------------------------------------------
1 | class_name SlideShow extends Node2D
2 |
3 |
4 | ## The initial slideshow in a videogame
5 | ##
6 | ## This is made easy
7 |
8 |
9 | enum {
10 | SKIP_PREV = -1,
11 | SKIP_NEXT = +1,
12 | }
13 |
14 | ## Emitted when the slideshow is finished
15 | signal finished()
16 | ## Emitted when a slide is skipped
17 | signal slide_skipped(skip_direction: int)
18 |
19 | @export_category("Presentation settings")
20 | ## Starts the presentation automatically when ready
21 | @export var autoplay: bool = true
22 | ## How much long the slide is shown. It does not take in the [member SlideShow.slide_fade_duration] fadein/fadeout time.
23 | @export_range(1.0, 10.0, 0.1) var slide_duration: float = 6.0
24 | @export_range(0.0, 3.0) var slide_fade_duration: float = 1.0
25 |
26 | ## Current slide index.
27 | var current_slide: int = 0
28 | ## Is [code]true[/code] if there is a previous slide, [code]false[/code] otherwise.
29 | var has_prev: bool:
30 | get:
31 | return current_slide > 0 and slides.size() > 0
32 | ## Is [code]true[/code] if there is a next slide, [code]false[/code] otherwise.
33 | var has_next: bool:
34 | get:
35 | return current_slide < slides.size()
36 | ## If [code]true[/code] the slide is playing, [code]false[/code] otherwise.
37 | var playing: bool = true:
38 | get:
39 | return playing
40 | set(value):
41 | playing = value
42 |
43 | if value and autoplay:
44 | _handle_next_slide()
45 | var slides: Array[Node2D]:
46 | get:
47 | var _s = [] as Array[Node2D]
48 |
49 | for child in get_children():
50 | if child is Node2D:
51 | _s.append(child)
52 |
53 | return _s
54 |
55 |
56 | func _handle_slide_in(slide: Node2D) -> void:
57 | if slide.has_method("_slide_in"):
58 | slide.call("_slide_in")
59 |
60 |
61 | func _handle_slide_out(slide: Node2D) -> void:
62 | if slide.has_method("_slide_out"):
63 | slide.call("_slide_out")
64 |
65 |
66 | ## Forcefully
67 | func _forcefully_fade_current() -> Tween:
68 | var tween = create_tween()
69 | var slide = slides[current_slide] as Node2D
70 |
71 | tween.tween_property(slide, "modulate:a", 0.0, slide_fade_duration)
72 |
73 | return tween
74 |
75 |
76 | ## Handles next slide. Called internally, use [method SlideShow.skip_to_prev], [method SlideShow.skip_to_next], [method SlideShow.skip_to_nth] or [method SlideShow.skip_all]
77 | func _handle_next_slide(direction: int = SKIP_NEXT) -> void:
78 | if current_slide >= slides.size():
79 | finished.emit()
80 | else:
81 | var tween = create_tween()
82 | var slide = slides[current_slide] as Node2D
83 |
84 | if slide == null:
85 | printerr("This should NEVER happen, what have you done?")
86 | _handle_next_slide()
87 |
88 | _handle_slide_in(slide)
89 |
90 | tween.tween_property(slide, "modulate:a", 1.0, slide_fade_duration)
91 | tween.tween_interval(slide_duration - (slide_fade_duration * 2))
92 | tween.tween_property(slide, "modulate:a", 0.0, slide_fade_duration)
93 |
94 | tween.finished.connect(func ():
95 | _handle_slide_out(slide)
96 | current_slide += direction
97 | _handle_next_slide()
98 | )
99 |
100 | ## Ready fn
101 | func _ready() -> void:
102 | playing = autoplay
103 |
104 | for slide in slides:
105 | slide.modulate.a = 0.0
106 |
107 |
108 | ## Sets [member SlideShow.playing] to [code]true[/code]
109 | func play() -> void:
110 | current_slide = 0
111 | playing = true
112 |
113 |
114 | ## Skips all slides and the [signal SlideShow.finished] is emitted.
115 | ## [br]
116 | ## GG mate, we worked hard for this.
117 | func skip_all() -> void:
118 | skip_slide_to_nth(get_child_count() + 1)
119 |
120 |
121 | ## Skips to the next slide if any, otherwise the slideshow ends and the [signal SlideShow.finished] is emitted.
122 | func skip_slide_to_next() -> void:
123 | skip_slide_to_nth(current_slide + 1)
124 |
125 |
126 | ## Skips to a nth slide. If out of bound, the slideshow ends and the [signal SlideShow.finished] is emitted.
127 | func skip_slide_to_nth(slide_index: int) -> void:
128 | var direction = SKIP_NEXT if slide_index > current_slide else SKIP_PREV
129 | var inbound = slide_index >= 0 and slide_index <= slides.size()
130 |
131 | if not inbound:
132 | playing = false
133 | finished.emit()
134 | return
135 |
136 | if current_slide >= slides.size():
137 | finished.emit()
138 | else:
139 | var tween = create_tween()
140 | var slide = slides[current_slide] as Node2D
141 |
142 | slide_skipped.emit(direction)
143 |
144 | ## Forcefully fades out current slide. You asked for it, do not complain plis.
145 | tween.tween_property(slide, "modulate:a", 0.0, slide_fade_duration)
146 |
147 | tween.finished.connect(func ():
148 | current_slide += direction
149 | _handle_slide_out(slide)
150 | _handle_next_slide(direction)
151 | )
152 |
153 |
154 | ## Skips to the previous slide if any, otherwise the slideshow ends and the [signal SlideShow.finished] is emitted.
155 | func skip_slide_to_prev() -> void:
156 | skip_slide_to_nth(current_slide - 1)
157 |
158 |
159 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_effect_editor_row.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends GridContainer
3 |
4 |
5 | signal removed()
6 |
7 |
8 | @export_category("Effect")
9 | @export var attributes_table: AttributeTable = null:
10 | get:
11 | return attributes_table
12 | set(value):
13 | attributes_table = value
14 | _redraw()
15 | @export var attribute_effect: AttributeEffect = null:
16 | get:
17 | return attribute_effect
18 | set(value):
19 | attribute_effect = value
20 | _redraw()
21 |
22 | @onready var application_count_spinbox: SpinBox = $VBoxContainer/TimerSetupContainer/ApplicationCountSpinBox
23 | @onready var applies_as: OptionButton = $AppliesAs/OptionButton
24 | @onready var attribute_option_button: OptionButton = $AttributeName/OptionButton
25 | @onready var life_time_option_button: OptionButton = $LifeTime/OptionButton
26 | @onready var minimum_value_spinbox: SpinBox = $MinimumValue/SpinBox
27 | @onready var maximum_value_spinbox: SpinBox = $MaximumValue/SpinBox
28 | @onready var timer_setup_container: VBoxContainer = $VBoxContainer
29 | @onready var timer_spinbox: SpinBox = $VBoxContainer/TimerSetupContainer/TimeoutSpinBox
30 | @onready var remove_button: Button = $AttributeName/RemoveButton
31 |
32 |
33 | var condition_resource_input := EditorResourcePicker.new()
34 |
35 |
36 | func _init() -> void:
37 | pass
38 |
39 |
40 | func _inherit_from_resource() -> void:
41 | if attribute_effect != null and attributes_table != null:
42 | _populate_attribute_name_list()
43 | _select_attribute_name()
44 | applies_as.selected = attribute_effect.applies_as
45 |
46 | if applies_as.selected < 0:
47 | applies_as.selected = 0
48 |
49 | attribute_option_button.text = attribute_effect.attribute_name
50 | life_time_option_button.selected = attribute_effect.life_time
51 | minimum_value_spinbox.value = attribute_effect.minimum_value
52 | maximum_value_spinbox.value = attribute_effect.maximum_value
53 | timer_spinbox.value = attribute_effect.apply_every_second
54 | application_count_spinbox.value = attribute_effect.max_applications
55 | condition_resource_input.edited_resource = attribute_effect.condition
56 | _set_lifetime(attribute_effect.life_time)
57 |
58 |
59 | func _populate_attribute_name_list() -> void:
60 | if attribute_option_button:
61 | attribute_option_button.clear()
62 |
63 | if attributes_table:
64 | for attribute in attributes_table.attributes:
65 | attribute_option_button.add_item(attribute)
66 |
67 |
68 | func _ready() -> void:
69 | var condition_label = Label.new()
70 |
71 | condition_label.text = "Condition"
72 | condition_resource_input.base_type = "AttributeEffectCondition"
73 |
74 | add_child(condition_label)
75 | add_child(condition_resource_input)
76 |
77 | timer_setup_container.visible = false
78 |
79 | applies_as.item_selected.connect(func (index):
80 | attribute_effect.applies_as = index
81 | )
82 |
83 | application_count_spinbox.value_changed.connect(func (value):
84 | attribute_effect.max_applications = value
85 | )
86 |
87 | attribute_option_button.item_selected.connect(func (index):
88 | if attributes_table and attribute_effect:
89 | if attributes_table.attributes.size() > index:
90 | attribute_effect.attribute_name = attributes_table.attributes[index]
91 | )
92 |
93 | condition_resource_input.resource_changed.connect(func (resource):
94 | if resource:
95 | attribute_effect.condition = resource
96 | else:
97 | attribute_effect.condition = null
98 | )
99 | condition_resource_input.resource_selected.connect(func (resource, _i):
100 | if resource:
101 | attribute_effect.condition = resource
102 | else:
103 | attribute_effect.condition = null
104 | )
105 |
106 | life_time_option_button.item_selected.connect(func (id):
107 | if id is int:
108 | attribute_effect.life_time = id
109 | _set_lifetime(id)
110 | )
111 |
112 | minimum_value_spinbox.value_changed.connect(func (value):
113 | attribute_effect.minimum_value = value
114 | )
115 |
116 | maximum_value_spinbox.value_changed.connect(func (value):
117 | attribute_effect.maximum_value = value
118 | )
119 |
120 | remove_button.pressed.connect(func ():
121 | removed.emit()
122 | )
123 |
124 | timer_spinbox.value_changed.connect(func (value):
125 | attribute_effect.apply_every_second = value
126 | )
127 |
128 | _redraw()
129 |
130 |
131 | func _redraw() -> void:
132 | _populate_attribute_name_list()
133 | _inherit_from_resource()
134 | _select_attribute_name()
135 |
136 |
137 | func _select_attribute_name() -> void:
138 | if attributes_table and attribute_effect:
139 | var index = attributes_table.attributes.find(attribute_effect.attribute_name)
140 | attribute_option_button.selected = clampi(index, 0, attribute_option_button.item_count)
141 |
142 |
143 | func _set_lifetime(value: int) -> void:
144 | timer_setup_container.visible = value == 1
145 |
146 | if not timer_setup_container.visible:
147 | application_count_spinbox.value = 0
148 | timer_spinbox.value = 0.0
149 |
150 |
151 | func set_values(table: AttributeTable, effect: AttributeEffect) -> void:
152 | await ready
153 | attributes_table = table
154 | attribute_effect = effect
155 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/camera_shake/nodes/camera_shake.gd:
--------------------------------------------------------------------------------
1 | class_name CameraShake extends Node
2 |
3 |
4 | ## Emitted when the shake ends
5 | signal shake_ended()
6 | ## Emitted when the shake starts
7 | signal shake_started()
8 | ## Emitted when a shake occurs
9 | signal shaken(remaining_times: int)
10 |
11 |
12 | enum CameraContext {
13 | CAMERA_2D,
14 | CAMERA_3D,
15 | }
16 |
17 |
18 | @export_category("Camera shake")
19 | ## The path to the camera to shake
20 | @export_node_path("Camera2D", "Camera3D") var camera_path: NodePath = NodePath()
21 |
22 | @export_group("Minimum values", "min_")
23 | ## The minimum strength appliable to the shake
24 | @export var min_strength: float = 0.0
25 | ## How many seconds the camera will be shaked (min)
26 | @export var min_duration: float = 0.0
27 | ## How many times per second the camera will be shaked (min)
28 | @export var min_frequency: int = 0
29 |
30 | @export_group("Maximum values", "max_")
31 | ## The maximum strength appliable to the shake
32 | @export var max_strength: float = 50.0
33 | ## How many seconds the camera will be shaked (max)
34 | @export var max_duration: float = 5.0
35 | ## How many times per second the camera will be shaked (max)
36 | @export var max_frequency: int = 50
37 |
38 | ## The camera 2d/3d node
39 | var camera: Node
40 | ## The camera context
41 | var camera_context: CameraContext
42 | ## Current applied duration
43 | var duration: float = 0.0:
44 | get:
45 | return duration
46 | set(value):
47 | duration = clampf(value, min_duration, max_duration)
48 | ## Current applied frequency
49 | var frequency: int = 0:
50 | get:
51 | return frequency
52 | set(value):
53 | frequency = clampf(value, min_frequency, max_frequency)
54 | ## Previous used [Tween]. Is [code]null[/code] if the [member CameraShake.camera] never shook or the previous [Tween] finished it's own job.
55 | var previous_tween: Tween
56 | ## Current applied strength
57 | var strength: float = 0.0:
58 | get:
59 | return strength
60 | set(value):
61 | strength = clampf(value, min_strength, max_strength)
62 | ## Time remaining before the shake effect ends
63 | var time_remaining: float = 0.0:
64 | get:
65 | return duration / float(frequency)
66 | ## Returns the number of tweens to operate
67 | var tweens_range: Array:
68 | get:
69 | return range(0, duration * frequency)
70 |
71 | ## Applies the shake
72 | func _apply_shake() -> void:
73 | if camera_context == CameraContext.CAMERA_2D:
74 | _apply_shake_2d()
75 | elif camera_context == CameraContext.CAMERA_3D:
76 | _apply_shake_3d()
77 |
78 |
79 | ## Applies the shake using 2D context
80 | func _apply_shake_2d() -> void:
81 | var _camera = camera as Camera2D
82 |
83 | assert(_camera != null, "Camera (2D) should not be null at this point, check your code before shaking the camera again")
84 |
85 | if previous_tween:
86 | previous_tween.stop()
87 |
88 | previous_tween = create_tween()
89 | previous_tween.tween_property(_camera, "offset", Vector2(0.0, 0.0), time_remaining)
90 |
91 | print("duration: ", duration)
92 | print("time_remaining: ", time_remaining)
93 | print("duration * frequency: ", duration * frequency)
94 | print("tweens_range: ", tweens_range)
95 |
96 | for _n in tweens_range:
97 | previous_tween.tween_property(_camera, "offset", Vector2(_get_shake_value(), _get_shake_value()), time_remaining)
98 |
99 | previous_tween.tween_property(_camera, "offset", Vector2(0.0, 0.0), time_remaining)
100 | previous_tween.play()
101 |
102 | previous_tween.step_finished.connect(func (_x):
103 | previous_tween = null
104 | )
105 |
106 |
107 | ## Applies the shake using 3D context
108 | func _apply_shake_3d() -> void:
109 | var _camera = camera as Camera3D
110 |
111 | assert(_camera != null, "Camera (3D) should not be null at this point, check your code before shaking the camera again")
112 |
113 | if previous_tween != null:
114 | previous_tween.stop()
115 |
116 | previous_tween = create_tween()
117 | previous_tween.tween_property(_camera, "h_offset", 0.0, time_remaining)
118 | previous_tween.tween_property(_camera, "v_offset", 0.0, time_remaining)
119 |
120 | print("tweens_range: ", tweens_range)
121 | print("time_remaining: ", time_remaining)
122 | print("duration: ", duration)
123 | print("frequency: ", frequency)
124 |
125 | for _n in tweens_range:
126 | previous_tween.tween_property(_camera, "h_offset", _get_shake_value(), time_remaining)
127 | previous_tween.tween_property(_camera, "v_offset", _get_shake_value(), time_remaining)
128 |
129 | previous_tween.tween_property(_camera, "h_offset", 0.0, time_remaining)
130 | previous_tween.tween_property(_camera, "v_offset", 0.0, time_remaining)
131 | previous_tween.play()
132 |
133 |
134 | func _get_shake_value(rerolls_remaining: int = 5) -> float:
135 | var shake_value = randf_range(-1.0, 1.0) * strength
136 |
137 | # Avoiding stack overflows
138 | if max_strength == 0.0 and shake_value == 0.0:
139 | return 0.0
140 |
141 | # Rerolls
142 | if shake_value == 0.0 and rerolls_remaining > 0:
143 | return _get_shake_value(rerolls_remaining - 1)
144 |
145 | return shake_value
146 |
147 |
148 | func _ready() -> void:
149 | if camera_path.is_empty():
150 | if owner is Camera2D or owner is Camera3D:
151 | camera = owner
152 | else:
153 | camera = get_node(camera_path)
154 |
155 | assert(camera != null, "Camera is null. Set camera_path correctly.")
156 |
157 | ## Try to guess if the camera is a Camera2D
158 | if camera is Camera2D:
159 | camera_context = CameraContext.CAMERA_2D
160 | ## Try to guess if the camera is a Camera3D
161 | elif camera is Camera3D:
162 | camera_context = CameraContext.CAMERA_3D
163 |
164 |
165 | ## Resets shake 2D
166 | func _reset_from_shake_2d() -> void:
167 | camera.offset = Vector2.ZERO
168 |
169 |
170 | ## Resets shake 2D
171 | func _reset_from_shake_3d() -> void:
172 | camera.h_offset = 0.0
173 | camera.v_offset = 0.0
174 |
175 |
176 | ## Shakes current camera by the given strength, duration, and frequency.
177 | ## [br][code]strength[/code] defaults to [code]1.0[/code]
178 | ## [br][code]duration[/code] defaults to [code]1.0[/code]
179 | ## [br][code]frequency[/code] defaults to [code]5[/code]
180 | func shake(strength: float = 1.0, duration: float = 1.0, frequency: int = 5) -> void:
181 | self.strength = strength
182 | self.duration = duration
183 | self.frequency = frequency
184 |
185 | _apply_shake()
186 |
187 |
188 | ## Resets from shake
189 | func reset_from_shake() -> void:
190 | if camera is Camera2D:
191 | _reset_from_shake_2d()
192 | elif camera is Camera3D:
193 | _reset_from_shake_3d()
194 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/nodes/gameplay_attribute_map.gd:
--------------------------------------------------------------------------------
1 | @icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap.svg")
2 | @tool
3 | class_name GameplayAttributeMap extends Node
4 |
5 | ## A GameplayAttributeMap is the core node which makes the magic for you.[br]
6 | ##
7 | ## A GameplayAttributeMap stores and handles a set of attributes and effects for your gameplay mechanics.
8 |
9 |
10 | ## Emitted when an attribute changes.
11 | signal attribute_changed(attribute: AttributeSpec)
12 | ## Emitted each time an effect is applied to a specific AttributeSpec.
13 | signal attribute_effect_applied(attribute_effect: AttributeEffect, attribute: AttributeSpec)
14 | ## Emitted after an effect is has been applied for the last time to a specific AttributeSpec.
15 | signal attribute_effect_removed(attribute_effect: AttributeEffect, attribute: AttributeSpec)
16 | ## Emitted once a GameplayEffect has been applied.
17 | ## [br]
18 | ## The signal is called once, even if a time-based AttributeEffect is still going on.
19 | signal effect_applied(effect: GameplayEffect)
20 |
21 |
22 | @export_category("Owner")
23 | ## Is the path to the owning character
24 | @export_node_path var owning_character := NodePath()
25 |
26 |
27 | @export_category("Attributes")
28 | ## Is the array of Attribute resources generated by the inspector plugin.
29 | @export var attributes: Array[AttributeResource] = []
30 | ## Is the AttributeTable selected resource.
31 | @export var table: AttributeTable = null
32 |
33 |
34 | ## A dictionary whose keys are attributes names and values are [AttributeSpec] instances.
35 | var _attributes_dict: Dictionary = {}
36 | ## A dictionary whose keys are [Timer] id and values are integers representing how many times these timers got called.
37 | ## [br]Once a timer is stopped, those key/value pair should be erased.
38 | var _timeouts_count_dict: Dictionary = {}
39 | ## Is the list of child GameplayEffect nodes
40 | var effects: Array[GameplayEffect] = []:
41 | get:
42 | var _effects: Array[GameplayEffect] = []
43 |
44 | for child in get_children():
45 | if child is GameplayEffect:
46 | _effects.append(child)
47 |
48 | return _effects
49 |
50 |
51 | func _add_attribute_spec(spec: AttributeResource) -> void:
52 | if Engine.is_editor_hint():
53 | attributes.append(spec)
54 |
55 |
56 | func _apply_initial_effects() -> void:
57 | for effect in effects:
58 | apply_effect(effect)
59 |
60 |
61 | func _handle_character_child_entered_tree(node: Node) -> void:
62 | if node is GameplayEffect:
63 | add_child(node)
64 |
65 |
66 | func _get_attribute_at(index: int) -> AttributeResource:
67 | if Engine.is_editor_hint():
68 | if attributes.size() > index:
69 | return attributes[index]
70 |
71 | attributes.append(AttributeResource.new())
72 | return attributes[index]
73 | else:
74 | return null
75 |
76 |
77 | func _ready() -> void:
78 | if not Engine.is_editor_hint():
79 | _setup_attributes()
80 |
81 | if owning_character != null and not owning_character.is_empty():
82 | var character = get_node(owning_character)
83 |
84 | if character:
85 | character.child_entered_tree.connect(func (child):
86 | if child is GameplayEffect:
87 | apply_effect(child)
88 | )
89 |
90 | _apply_initial_effects()
91 |
92 |
93 | func _setup_attributes() -> void:
94 | _attributes_dict = {}
95 |
96 | for attribute in attributes:
97 | var previous = get_attribute_by_name(attribute.attribute_name)
98 |
99 | if previous:
100 | previous.free()
101 |
102 | var spec = AttributeSpec.from_attribute(attribute)
103 |
104 | spec.changed.connect(func (attribute):
105 | attribute_changed.emit(attribute)
106 | )
107 |
108 | _attributes_dict[spec.attribute_name] = spec
109 |
110 |
111 | func _setup_owning_character() -> void:
112 | if owning_character == null or owning_character.is_empty():
113 | return
114 |
115 | var owning_character = get_node(owning_character)
116 |
117 | if owning_character:
118 | owning_character.child_entered_tree.connect(func (child):
119 | if child is GameplayEffect:
120 | apply_effect(child)
121 | )
122 |
123 |
124 | func _update_attribute(index: int, key: String, value: float) -> void:
125 | if Engine.is_editor_hint():
126 | if attributes.size() >= index:
127 | if key in attributes[index]:
128 | attributes[index][key] = value
129 |
130 |
131 | ## Applies an effect on current GameplayAttributeMap
132 | func apply_effect(effect: GameplayEffect) -> void:
133 | var _effect = effect.duplicate()
134 |
135 | effect.queue_free()
136 |
137 | if multiplayer and not multiplayer.is_server():
138 | return
139 |
140 | if effect == null:
141 | return
142 |
143 | for attribute_affected in effect.attributes_affected:
144 | if not attribute_affected.attribute_name in _attributes_dict:
145 | return
146 |
147 | if attribute_affected.life_time == AttributeEffect.LIFETIME_ONE_SHOT:
148 | var spec = _attributes_dict[attribute_affected.attribute_name]
149 |
150 | if not attribute_affected.should_apply(_effect, self):
151 | continue
152 |
153 | _attributes_dict[attribute_affected.attribute_name].apply_attribute_effect(attribute_affected)
154 |
155 | attribute_effect_applied.emit(attribute_affected, spec)
156 | attribute_effect_removed.emit(attribute_affected, spec)
157 | elif attribute_affected.life_time == AttributeEffect.LIFETIME_TIME_BASED:
158 | var timer = Timer.new()
159 | var timer_id = timer.get_instance_id()
160 |
161 | _timeouts_count_dict[timer_id] = 0
162 |
163 | timer.autostart = true
164 | timer.wait_time = attribute_affected.apply_every_second
165 |
166 | timer.timeout.connect(func ():
167 | var spec = _attributes_dict[attribute_affected.attribute_name]
168 |
169 | if not attribute_affected.should_apply(_effect, self):
170 | return
171 |
172 | if attribute_affected.max_applications != 0 and attribute_affected.max_applications == _timeouts_count_dict[timer_id]:
173 | attribute_effect_removed.emit(attribute_affected, spec)
174 | timer.stop()
175 | _timeouts_count_dict.erase(timer_id)
176 | remove_child(timer)
177 | else:
178 | _attributes_dict[attribute_affected.attribute_name].apply_attribute_effect(attribute_affected)
179 |
180 | attribute_effect_applied.emit(attribute_affected, spec)
181 |
182 | if attribute_affected.max_applications != 0:
183 | _timeouts_count_dict[timer_id] += 1
184 | )
185 |
186 | add_child(timer)
187 |
188 | effect_applied.emit(_effect)
189 |
190 |
191 | ## Gets an instance of AttributeSpec by it's attribute_name
192 | func get_attribute_by_name(attribute_name: String) -> AttributeSpec:
193 | if _attributes_dict.has(attribute_name):
194 | return _attributes_dict.get(attribute_name)
195 |
196 | return null
197 |
198 |
199 | ## Gets all attributes as a dictionary
200 | ## [br]The dictionary keys are the attribute names and the values are the current buffed value of the attribute.
201 | func get_attributes_dict() -> Dictionary:
202 | var keys = _attributes_dict.keys()
203 | var out = {} as Dictionary
204 |
205 | for key in keys:
206 | var attr = get_attribute_by_name(key)
207 | out[key] = attr.current_buffed_value
208 | return out
209 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/attributes_and_abilities/resources/ability.gd:
--------------------------------------------------------------------------------
1 | @icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability.svg")
2 | class_name Ability extends Resource
3 |
4 | ## Represents an ability or skill
5 |
6 | ## Emitted the the ability has been activated.
7 | signal activated(Ability, ActivationEvent)
8 | ## Emitted the the ability has been blocked in [method Ability.try_activate].
9 | signal blocked(Ability, ActivationEvent)
10 | ## Emitted the the ability has been cancelled.
11 | signal cancelled(Ability, ActivationEvent)
12 | ## Emitted the the ability has been ended.
13 | signal ended(Ability, ActivationEvent)
14 |
15 | @export_group("User interface", "ui_")
16 | ## Is the icon shown in your user interface.
17 | @export var ui_icon: Texture2D = null
18 | ## Is the name shown in your user interface.
19 | @export var ui_name: String = ""
20 |
21 | @export_group("Cooldown", "cooldown_")
22 | ## Sets a cooldown duration before the ability can be used again.
23 | @export var cooldown_duration := 0.0
24 | ## Sets the cooldown as continuos, so it will keep on working every [member Ability.cooldown_duration] seconds.
25 | @export var cooldown_continuos := false
26 |
27 | @export_group("Ability Granting", "grant_")
28 | ## Automatically grants an ability when added to a [AbilityContainer].
29 | @export var grant_automatic := true
30 | ## Adds these tags to the owning [AbilityContainer] when the ability is granted.
31 | ## [br]Useful for skill trees or skill progression systems.
32 | @export var grant_tags: Array[String] = []
33 | ## Tags required for granting an ability
34 | ## [br]These tags will be checked to ensure that the [Ability] can be granted or not.
35 | @export var grant_tags_required: Array[String] = []
36 |
37 | @export_group("Tags", "tags_")
38 | ## Tags added once ability has been activated.
39 | ## [br]Use [member Ability.tags_to_remove_on_activation] to remove some of these tags after the activation.
40 | @export var tags_activation: Array[String] = []
41 | ## Tags required for activation.
42 | ## [br]The ability cannot be activated if the [AbilityContainer] does not have all the tags provided here.
43 | @export var tags_activation_required: Array[String] = []
44 | ## Blocks execution if ore or more tags are contained by [AbilityContainer]
45 | ## [br]Use these tags to block the activation of an ability.
46 | @export var tags_block: Array[String] = []
47 | ## Tags required for cancellation.
48 | ## [br]Use these tags to determine if an ability can be cancelled or not.
49 | @export var tags_cancellation_required: Array[String] = []
50 | ## Tags added when cooldown is started.
51 | ## [br]Use [member Ability.tags_to_remove_on_cooldown_start] to remove some tags from this array when a cooldown starts.
52 | @export var tags_cooldown_start: Array[String] = []
53 | ## Tags added when cooldown is ended.
54 | ## [br]Use [member Ability.tags_to_remove_on_cooldown_end] to remove some tags from this array when a cooldown ends.
55 | @export var tags_cooldown_end: Array[String] = []
56 | ## Tags which will block the end of an [Ability].
57 | @export var tags_end_blocking: Array[String] = []
58 | ## Tags which will be removed on activation.
59 | @export var tags_to_remove_on_activation: Array[String] = []
60 | ## Tags which will be removed on block.
61 | @export var tags_to_remove_on_block: Array[String] = []
62 | ## Tags which will be removed on cancellation.
63 | @export var tags_to_remove_on_cancellation: Array[String] = []
64 | ## Tags which will be removed when a cooldown start.
65 | @export var tags_to_remove_on_cooldown_start: Array[String] = []
66 | ## Tags which will be removed when a cooldown end.
67 | @export var tags_to_remove_on_cooldown_end: Array[String] = []
68 | ## Tags which will be removed when an ability ends.
69 | @export var tags_to_remove_on_end: Array[String] = []
70 |
71 |
72 | ## Is [code]true[/code] if [member Ability.cooldown_duration] is greater than [code]0.0[/code], [code]false[/code] otherwise.
73 | var has_cooldown: bool:
74 | get:
75 | return cooldown_duration > 0.0
76 |
77 |
78 | ## Activates the effect. This will forcefully activate it even if some criteria do not match.
79 | ## You should use [method Ability.try_activate] instead for a proper (and safer) flow.
80 | func activate(activation_event: ActivationEvent) -> void:
81 | activated.emit(self, activation_event)
82 |
83 |
84 | ## Return [code]true[/code] if the ability can be activated, [code]false[/code] otherwise.
85 | ## [br]Always return: [code]true[/code] if [member Ability.tags_activation_required] is empty.
86 | func can_activate(activation_event: ActivationEvent) -> bool:
87 | if tags_activation_required.size() > 0:
88 | return has_all_tags(tags_activation_required, activation_event.tags)
89 |
90 | return true
91 |
92 |
93 | ## Returns [code]true[/code] if the ability can be blocked, [code]false[/code] otherwise.
94 | ## [br]Always return: [code]true[/code] if [member Ability.tags_block] is empty.
95 | func can_block(activation_event: ActivationEvent) -> bool:
96 | if tags_block.size() > 0:
97 | return has_some_tags(tags_block, activation_event.tags)
98 |
99 | return false
100 |
101 |
102 | ## Return [code]true[/code] if the ability can be cancelled, [code]false[/code] otherwise.
103 | ## [br]Always return: [code]true[/code] if [member Ability.tags_cancellation_required] is empty.
104 | func can_cancel(activation_event: ActivationEvent) -> bool:
105 | if tags_cancellation_required.size() > 0:
106 | return has_some_tags(tags_cancellation_required, activation_event.tags)
107 |
108 | return false
109 |
110 |
111 | ## Return [code]true[/code] if the ability can be ended, [code]false[/code] otherwise.
112 | ## [br]Always return: [code]true[/code] if [member Ability.tags_end_blocking] is empty.
113 | func can_end(activation_event: ActivationEvent) -> bool:
114 | if tags_end_blocking.size() > 0:
115 | return !has_some_tags(tags_end_blocking, activation_event.tags)
116 |
117 | return true
118 |
119 |
120 | ## Cancels an ability forcefully. Remember to call [method Ability.can_cancel] first.
121 | ## [br]This will forcefully activate it even if some criteria do not match.
122 | ## [br]You should use [method Ability.try_activate] instead for a proper (and safer) flow.
123 | func cancel(activation_event: ActivationEvent) -> void:
124 | if can_cancel(activation_event):
125 | cancelled.emit(self, activation_event)
126 |
127 |
128 | ## Ends the ability forcefully. Remember to call [method Ability.can_end] first.
129 | ## [br]This will forcefully activate it even if some criteria do not match.
130 | ## [br]You should use [method Ability.try_activate] instead for a proper (and safer) flow.
131 | func end_ability(activation_event: ActivationEvent) -> void:
132 | if can_end(activation_event):
133 | ended.emit(self, activation_event)
134 |
135 |
136 | ## Checks if the parameter [code]tags[/code] has all tags included in [code]tags_to_check[/code].
137 | ## [br]It checks if [code]tags[/code] has all [code]tags_to_check[/code].
138 | func has_all_tags(tags: Array[String], tags_to_check: Array[String]) -> bool:
139 | for t in tags:
140 | if not tags_to_check.has(t):
141 | return false
142 |
143 | return true
144 |
145 | ## Checks if the parameter [code]tags[/code] has some tags included in [code]tags_to_check[/code].
146 | ## [br]It checks if [code]tags[/code] has all [code]tags_to_check[/code].
147 | func has_some_tags(tags: Array[String], tags_to_check: Array[String]) -> bool:
148 | for t in tags:
149 | if tags_to_check.has(t):
150 | return true
151 |
152 | return false
153 |
154 |
155 | ## Tries to activate an ability, then tries to cancel it and then tries to end it.
156 | func try_activate(activation_event: ActivationEvent) -> void:
157 | if can_block(activation_event):
158 | blocked.emit(self, activation_event)
159 | return
160 |
161 | if can_activate(activation_event):
162 | activate(activation_event)
163 |
--------------------------------------------------------------------------------
/project.godot:
--------------------------------------------------------------------------------
1 | ; Engine configuration file.
2 | ; It's best edited using the editor UI and not directly,
3 | ; since the parameters that go here are not all obvious.
4 | ;
5 | ; Format:
6 | ; [section] ; section goes between []
7 | ; param=value ; assign values to parameters
8 |
9 | config_version=5
10 |
11 | [application]
12 |
13 | config/name="Godot-gameplay-attributes"
14 | run/main_scene="res://examples/examples.tscn"
15 | config/features=PackedStringArray("4.2", "Forward Plus")
16 | config/icon="res://icon.svg"
17 |
18 | [autoload]
19 |
20 | TurnManager="*res://addons/godot_gameplay_systems/turn_based/./autoloads/turn_manager.gd"
21 |
22 | [display]
23 |
24 | window/stretch/aspect="keep_width"
25 |
26 | [dotnet]
27 |
28 | project/assembly_name="Godot-gameplay-attributes"
29 |
30 | [editor]
31 |
32 | version_control/plugin_name="GitPlugin"
33 | version_control/autoload_on_startup=true
34 |
35 | [editor_plugins]
36 |
37 | enabled=PackedStringArray("res://addons/godot_gameplay_systems/plugin.cfg", "res://addons/gut/plugin.cfg")
38 |
39 | [input]
40 |
41 | resurrect={
42 | "deadzone": 0.5,
43 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":114,"echo":false,"script":null)
44 | ]
45 | }
46 | fireball={
47 | "deadzone": 0.5,
48 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"echo":false,"script":null)
49 | ]
50 | }
51 | close_example={
52 | "deadzone": 0.5,
53 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"echo":false,"script":null)
54 | ]
55 | }
56 | fps_move_forward={
57 | "deadzone": 0.5,
58 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"echo":false,"script":null)
59 | ]
60 | }
61 | fps_move_backward={
62 | "deadzone": 0.5,
63 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"echo":false,"script":null)
64 | ]
65 | }
66 | fps_strafe_left={
67 | "deadzone": 0.5,
68 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null)
69 | ]
70 | }
71 | fps_strafe_right={
72 | "deadzone": 0.5,
73 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null)
74 | ]
75 | }
76 | fps_shoot={
77 | "deadzone": 0.5,
78 | "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(461, 38),"global_position":Vector2(469, 122),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
79 | ]
80 | }
81 | fps_jump={
82 | "deadzone": 0.5,
83 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"echo":false,"script":null)
84 | ]
85 | }
86 | fps_weapon_1={
87 | "deadzone": 0.5,
88 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":49,"key_label":0,"unicode":49,"echo":false,"script":null)
89 | ]
90 | }
91 | fps_weapon_2={
92 | "deadzone": 0.5,
93 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":50,"key_label":0,"unicode":50,"echo":false,"script":null)
94 | ]
95 | }
96 | diablo_like_inventory={
97 | "deadzone": 0.5,
98 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":67,"key_label":0,"unicode":99,"echo":false,"script":null)
99 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":73,"key_label":0,"unicode":105,"echo":false,"script":null)
100 | ]
101 | }
102 | diablo_like_ability_1={
103 | "deadzone": 0.5,
104 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":49,"key_label":0,"unicode":49,"echo":false,"script":null)
105 | ]
106 | }
107 | diablo_like_ability_2={
108 | "deadzone": 0.5,
109 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":50,"key_label":0,"unicode":50,"echo":false,"script":null)
110 | ]
111 | }
112 | diablo_like_ability_3={
113 | "deadzone": 0.5,
114 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":51,"key_label":0,"unicode":51,"echo":false,"script":null)
115 | ]
116 | }
117 | diablo_like_ability_4={
118 | "deadzone": 0.5,
119 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":52,"key_label":0,"unicode":52,"echo":false,"script":null)
120 | ]
121 | }
122 | diablo_like_move_to={
123 | "deadzone": 0.5,
124 | "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
125 | ]
126 | }
127 | diablo_like_add_gold={
128 | "deadzone": 0.5,
129 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":71,"key_label":0,"unicode":103,"echo":false,"script":null)
130 | ]
131 | }
132 | fps_interact={
133 | "deadzone": 0.5,
134 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"echo":false,"script":null)
135 | ]
136 | }
137 | fps_drop={
138 | "deadzone": 0.5,
139 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":88,"key_label":0,"unicode":120,"echo":false,"script":null)
140 | ]
141 | }
142 |
143 | [layer_names]
144 |
145 | 2d_physics/layer_1="terrain"
146 | 3d_physics/layer_1="terrain"
147 | 2d_physics/layer_2="player"
148 | 3d_physics/layer_2="player"
149 | 2d_physics/layer_3="hazard"
150 | 3d_physics/layer_3="enemies"
151 | 2d_physics/layer_4="collectible"
152 | 3d_physics/layer_4="items"
153 | 2d_physics/layer_5="projectile"
154 | 3d_physics/layer_5="interactables"
155 | 3d_physics/layer_6="bullet"
156 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/interactables/nodes/interaction_manager.gd:
--------------------------------------------------------------------------------
1 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png")
2 | @tool
3 | class_name InteractionManager extends Node
4 |
5 |
6 | ## Handles interactions.
7 |
8 |
9 | ## Emitted when a bound raycast detects and interactable.
10 | signal interactable_focus_in(interactable: Node)
11 | ## Emitted when a bound raycast loses a focus to an interactable.
12 | signal interactable_focus_out(interactable: Node)
13 | ## Emitted when an interaction is blocked.
14 | signal interaction_blocked(interactable: Node, interaction: Interaction)
15 | ## Emitted when an interaction is cancelled.
16 | signal interaction_cancelled(interactable: Node, interaction: Interaction)
17 | ## Emitted when an interaction is ended.
18 | signal interaction_ended(interactable: Node, interaction: Interaction)
19 | ## Emitted when an interaction is started.
20 | signal interaction_started(interactable: Node, interaction: Interaction)
21 | ## Emitted when an interaction is refused to block.
22 | signal interaction_block_refused(interactable: Node, interaction: Interaction)
23 | ## Emitted when an interaction is refused to cancel.
24 | signal interaction_cancel_refused(interactable: Node, interaction: Interaction)
25 | ## Emitted when an interaction is refused to end.
26 | signal interaction_end_refused(interactable: Node, interaction: Interaction)
27 | ## Emitted when an interaction is refused to start.
28 | signal interaction_start_refused(interactable: Node, interaction: Interaction)
29 | ## Emitted when tags have changed.
30 | signal tags_changed()
31 |
32 | @export_category("Interaction")
33 | ## The path to the interaction owner.
34 | @export_node_path("Node2D", "Node3D") var interacting_owner_path := NodePath()
35 | ## The path to the interaction raycast node.
36 | @export_node_path("InteractionRayCast2D", "InteractionRayCast3D") var interaction_raycast_path := NodePath()
37 |
38 | @export_group("Tags", "tags_")
39 | ## The tags associated to this interaction manager.
40 | @export var tags: Array[String] = []
41 | ## Tags which will blocks the interaction completely.
42 | @export var tags_blocking_interaction: Array[String] = []
43 | ## Tags required to trigger an interaction.
44 | @export var tags_required_to_interact: Array[String] = []
45 |
46 |
47 | ## Checks if it can interact.
48 | var can_interact: bool:
49 | get:
50 | if tags_blocking_interaction.size() == 0 and tags_required_to_interact.size() == 0:
51 | return true
52 |
53 | if tags_blocking_interaction.size() > 0:
54 | for tag in tags_blocking_interaction:
55 | if tags.has(tag):
56 | return false
57 |
58 | if tags_required_to_interact.size() > 0:
59 | for tag in tags_required_to_interact:
60 | if not tags.has(tag):
61 | return false
62 |
63 | return focused_interactable != null
64 | ## Is the interacting owner. Usually a [CharacterBody2D] or [CharacterBody3D].
65 | var interacting_owner: Node:
66 | get:
67 | if interacting_owner != null:
68 | return interacting_owner
69 |
70 | interacting_owner = get_node(interacting_owner_path)
71 | return interacting_owner
72 | set(value):
73 | interacting_owner = value
74 | ## Is an interaction raycast. Used to determine if the owner can see and reach the interactable.
75 | var interaction_raycast: Node:
76 | get:
77 | if interaction_raycast != null:
78 | return interaction_raycast
79 |
80 | interaction_raycast = get_node(interaction_raycast_path)
81 | return interaction_raycast
82 | set(value):
83 | interaction_raycast = value
84 | ## Is [code]true[/code] if the [InteractionManager]
85 | var is_interacting: bool:
86 | get:
87 | if current_interactable == null:
88 | return false
89 |
90 | if current_interactable.interaction == null:
91 | return false
92 |
93 | if current_interactable.interaction.tags_added_on_start.size() > 0:
94 | return has_tags(current_interactable.interaction.tags_added_on_start)
95 |
96 | return true
97 | ## It's the current interactable [Node]. This variable is stored when you start an interaction and cleared when you end an interaction.
98 | var current_interactable: Node = null
99 | ## It's the current focused interactable [Node].
100 | var focused_interactable: Node = null
101 | ## It's the current focused interactable [Node]'s [Interaction].
102 | var interaction: Interaction:
103 | get:
104 | if current_interactable == null:
105 | return null
106 |
107 | return current_interactable.interaction if is_interacting else null
108 |
109 |
110 | func _handle_focus_in(node: Node) -> void:
111 | interactable_focus_in.emit(node)
112 | focused_interactable = node
113 |
114 |
115 | func _handle_focus_out(node: Node) -> void:
116 | interactable_focus_out.emit(node)
117 | focused_interactable = null
118 |
119 |
120 | ## Overrides virtual and handles input events
121 | func _input(input: InputEvent) -> void:
122 | if is_interacting and interaction_raycast != null:
123 | current_interactable.interaction.handle_input(input, self, current_interactable)
124 |
125 |
126 | ## Overrides virtual and handles process
127 | func _process(delta: float) -> void:
128 | if is_interacting and interaction_raycast != null:
129 | current_interactable.interaction.handle_process(delta, self, current_interactable)
130 |
131 |
132 | ## Overrides virtual and handles physics process
133 | func _physics_process(delta: float) -> void:
134 | if is_interacting and interaction_raycast != null:
135 | current_interactable.interaction.handle_physics_process(delta, self, current_interactable)
136 |
137 |
138 | func _ready() -> void:
139 | if interaction_raycast != null:
140 | interaction_raycast.interactable_detected.connect(_handle_focus_in)
141 | interaction_raycast.interactable_focus_in.connect(_handle_focus_in)
142 | interaction_raycast.interactable_focus_out.connect(_handle_focus_out)
143 |
144 |
145 | ## Adds a tag.
146 | func add_tag(tag: String) -> void:
147 | if not tags.has(tag):
148 | tags.append(tag)
149 | tags_changed.emit()
150 |
151 |
152 | ## Adds many tags at once.
153 | ## [br]Note: this will emit the [signal InteractionManager.tags_changed] only once.
154 | func add_tags(tags: Array[String]) -> void:
155 | var updated = false
156 |
157 | for tag in tags:
158 | if not self.tags.has(tag):
159 | self.tags.append(tag)
160 | updated = true
161 |
162 | if updated:
163 | tags_changed.emit()
164 |
165 |
166 | ## Returns [code]true[/code] if can ends interaction, [code]false[/code] otherwise.
167 | func can_end_interaction() -> bool:
168 | if can_interact and interaction != null:
169 | if interaction.tags_required_to_block.size() > 0:
170 | return not has_tags(interaction.tags_required_to_block) and has_tags(interaction.tags_required_to_end)
171 | else:
172 | return has_tags(interaction.tags_required_to_end)
173 | return true
174 |
175 |
176 | ## Returns [code]true[/code] if can starts interaction, [code]false[/code] otherwise.
177 | func can_start_interaction() -> bool:
178 | return can_interact and focused_interactable.interaction != null
179 |
180 |
181 | ## Ends an interaction with the currently focused node.
182 | ## [br]Note: this will emit the [signal InteractionManager.tags_changed] only once.
183 | ## [br]Note: this will emit the [signal InteractionManager.interaction_ended] only once.
184 | func end_interaction() -> void:
185 | if can_end_interaction():
186 | if current_interactable.has_method("_on_interaction_ended"):
187 | current_interactable.call("_on_interaction_ended", self)
188 |
189 | current_interactable.interaction.on_before_interaction_end(self, current_interactable)
190 | add_tags(current_interactable.interaction.tags_added_on_end)
191 | remove_tags(current_interactable.interaction.tags_removed_on_end)
192 | interaction_ended.emit(current_interactable)
193 | current_interactable = null
194 |
195 |
196 | ## Starts an interaction with the currently focused node.
197 | ## [br]Note: this will emit the [signal InteractionManager.tags_changed] only once.
198 | ## [br]Note: this will emit the [signal InteractionManager.interaction_started] only once.
199 | func start_interaction() -> void:
200 | if can_start_interaction():
201 | focused_interactable.interaction.on_before_interaction_start(self, focused_interactable)
202 | add_tags(focused_interactable.interaction.tags_added_on_start)
203 | remove_tags(focused_interactable.interaction.tags_removed_on_start)
204 | interaction_started.emit(focused_interactable)
205 | current_interactable = focused_interactable
206 |
207 | if current_interactable.has_method("_on_interaction_started"):
208 | current_interactable.call("_on_interaction_started", self)
209 |
210 |
211 | ## Checks if has a tag.
212 | func has_tag(tag: String) -> bool:
213 | return tags.has(tag)
214 |
215 |
216 | ## Checks if has tags.
217 | ## [br]If the param [code]match_all[/code] is [code]true[/code], then this [InteractionManager] must have all the passed tags.
218 | ## [br]If the param [code]match_all[/code] is [code]false[/code], then this function returns [code]true[/code] if this [InteractionManager] has one or many tags passed.
219 | func has_tags(tags: Array[String], match_all: bool = true) -> bool:
220 | for tag in tags:
221 | if not self.tags.has(tag) and match_all:
222 | return false
223 | elif self.tags.has(tag) and not match_all:
224 | return true
225 | return true
226 |
227 |
228 | ## Removes a tag. Returns [code]true[/code] if the tag is removed, [code]false[/code] otherwise.
229 | func remove_tag(tag: String) -> bool:
230 | var index = tags.find(tag)
231 |
232 | if index < 0:
233 | return false
234 |
235 | tags.remove_at(index)
236 | tags_changed.emit()
237 |
238 | return true
239 |
240 |
241 | ## Removes a tag. Returns the count of the removed elements.
242 | func remove_tags(tags: Array[String]) -> int:
243 | var count = 0
244 |
245 | for tag in tags:
246 | var index = self.tags.find(tag)
247 |
248 | if index >= 0:
249 | self.tags.remove_at(index)
250 | count += 1
251 |
252 | if count > 0:
253 | tags_changed.emit()
254 |
255 | return count
256 |
--------------------------------------------------------------------------------
/addons/godot_gameplay_systems/inventory_system/nodes/inventory.gd:
--------------------------------------------------------------------------------
1 | @icon("res://addons/godot_gameplay_systems/inventory_system/assets/InventoryIcon.png")
2 | @tool
3 | class_name Inventory extends Node
4 |
5 |
6 | ## Represents an inventory.
7 | ##
8 | ## Note: an Inventory should be unique for a character.
9 |
10 |
11 | enum LifeCycle {
12 | ## An [Item] has been activated.
13 | Activated = 0,
14 | ## An [Item] has been added to the inventory.
15 | Added = 1,
16 | ## An [Item] has been removed from an inventory.
17 | Removed = 2,
18 | }
19 |
20 |
21 | ## Emitted when an [Item] is properly activated with a specific activation type.
22 | signal item_activated(item: Item, activation_type: int)
23 | ## Emitted when an [Item] has been added to this inventory.
24 | signal item_added(item: Item)
25 | ## Emitted when an [Item] has been activated and it's own stack has been consumed entirely.
26 | signal item_depleted(item: Item)
27 | ## Emitted when an [Item] has been removed from this inventory.
28 | signal item_removed(item: Item)
29 | ## Emitted when the [Inventory] refuses to add an [Item] because of lacking tags.
30 | signal refused_to_add(item: Item)
31 | ## Emitted when the [Inventory] refuses to remove an [Item] because of lacking tags.
32 | signal refused_to_remove(item: Item)
33 |
34 |
35 | @export_category("Inventory")
36 | ## Is the path to the owner (aka the character or entity which owns the inventory).
37 | @export_node_path("Node2D", "Node3D") var owner_path: NodePath = NodePath()
38 | ## Is the path to the related [Equipment] node.
39 | ## [br]This is not necessary unless you want to make this [Inventory] to talk with an [Equipment] automatically.
40 | ## [br]For example: a classic Doom/Quake-like game, will not have an inventory system,
41 | ## all weapons are set at startup and can be used only if some tags are set.
42 | @export_node_path("Equipment") var equipment_path: NodePath = NodePath()
43 | ## Is the array of items.
44 | @export var items: Array[Item] = []
45 | ## Tags associated to this [Inventory]
46 | @export var tags: Array[String] = []
47 |
48 | ## Related [Equipment] node if any.
49 | var equipment: Equipment
50 |
51 |
52 | ## Internal method. Handles all life cycle events managing the tagging system.
53 | func _handle_lifecycle_tags(life_cycle: LifeCycle, item: Item) -> void:
54 | assert(item != null, "Item cannot be null")
55 |
56 | match life_cycle:
57 | LifeCycle.Activated:
58 | add_tags(item.tags_added_on_activation)
59 | remove_tags(item.tags_removed_on_activation)
60 | LifeCycle.Added:
61 | add_tags(item.tags_added_on_add)
62 | remove_tags(item.tags_removed_on_add)
63 | LifeCycle.Removed:
64 | add_tags(item.tags_added_on_remove)
65 | remove_tags(item.tags_removed_on_remove)
66 |
67 |
68 | ## Ready function. Adds all starting [member Item.tags_added_on_add] when the node is ready.
69 | func _ready() -> void:
70 | setup()
71 |
72 |
73 |
74 | ## Activates an [Item] with an optional [code]activation_type[/code].
75 | ## Activation can occur only if [method Item._can_activate] returns [code]true[/code].
76 | func activate(item: Item, activation_type: int = 0) -> void:
77 | var activation_event = ItemActivationEvent.new(self, activation_type)
78 |
79 | if item._can_activate(activation_event):
80 | item._activate(activation_event)
81 | item_activated.emit(item, activation_type)
82 | _handle_lifecycle_tags(LifeCycle.Activated, item)
83 |
84 | if item.can_stack and item.decrease_stack_on_use:
85 | item.quantity_current = clampi(item.quantity_current - 1, 0, item.quantity_current)
86 |
87 | if item.quantity_current == 0:
88 | var index = items.find(item)
89 |
90 | if index >= 0:
91 | items.remove_at(index)
92 | item_depleted.emit(item)
93 |
94 |
95 | ## Adds an item to the inventory and connects it's signals
96 | ## [br][Item] is duplicated when added. If an [Item] has [member Item.can_stack], it will stack [member Item.quantity_current] until [member Item.quantity_max] is reached.
97 | ## [br]If the quantity exceed the maximum, another [Item] will be added to the inventory with the difference in stacks.
98 | ## [br]Returns the copied [Item].
99 | ## [br]Note: always check it the [Item] is [code]null[/code] or not, since it could not be added if some tags requirements are not met.
100 | func add_item(item: Item) -> Item:
101 | if not can_add(item):
102 | refused_to_add.emit(item)
103 | return null
104 |
105 | # Finds the first item which is already in the inventory, is stackable and it's own stack is not at the maximum quantity.
106 | var found = find_by(func (x: Item): return item.name == x.name and x.can_stack and x.quantity_current < x.quantity_max)
107 |
108 | # Check if there's another [Item] in the inventory and if is stackable.
109 | if found and found.can_stack:
110 | var new_quantity = found.quantity_current + item.quantity_current
111 |
112 | # It's not stackable to an infinite value
113 | if found.quantity_max != 0:
114 | found.quantity_current = clampi(new_quantity, 0, found.quantity_max)
115 |
116 | if new_quantity > found.quantity_max:
117 | var duplicated_item = found.duplicate() as Item
118 |
119 | duplicated_item.quantity_current = new_quantity - found.quantity_max
120 |
121 | return add_item(duplicated_item)
122 | # Stacking it to infinite value
123 | else:
124 | found.quantity_current = new_quantity
125 | item_added.emit(found)
126 | return found
127 |
128 | var item_to_return = item.duplicate()
129 | items.append(item_to_return)
130 |
131 | item_added.emit(item_to_return)
132 |
133 | _handle_lifecycle_tags(LifeCycle.Added, item_to_return)
134 |
135 | return item_to_return
136 |
137 |
138 | ## Adds many items.
139 | func add_items(_items: Array[Item]) -> Array[Item]:
140 | var out: Array[Item] = []
141 |
142 | for i in _items:
143 | out.append(add_item(i))
144 |
145 | return out
146 |
147 |
148 | ## Adds a tag to [member Inventory.tags].
149 | ## [br]Duplicated tags are discarded
150 | func add_tag(tag: String) -> void:
151 | if tag == null:
152 | return
153 |
154 | if not tags.has(tag):
155 | tags.append(tag)
156 |
157 |
158 | ## Adds many tags to [member Inventory.tags].
159 | ## [br]Duplicated tags are discarded
160 | func add_tags(_tags: Array[String]) -> void:
161 | if _tags.size() == 0:
162 | return
163 |
164 | for t in _tags:
165 | if not tags.has(t):
166 | tags.append(t)
167 |
168 |
169 | ## Checks if an item can be activated with an optional activation id.
170 | func can_activate(item: Item, activation_type: int = 0) -> bool:
171 | if item.tags_required_to_activate.size() > 0:
172 | for tag in item.tags_required_to_activate:
173 | if not tags.has(tag):
174 | return false
175 |
176 | return item._can_activate(ItemActivationEvent.new(self, activation_type))
177 |
178 |
179 | ## Checks if an [Item] can be added to this [Inventory] checking if all [member Item.tags_required_to_add] exists in this [Inventory]
180 | func can_add(item: Item) -> bool:
181 | if item.tags_required_to_add.size() == 0:
182 | return true
183 |
184 | for tag in item.tags_required_to_add:
185 | if not tags.has(tag):
186 | return false
187 |
188 | return true
189 |
190 |
191 | ## Checks if an [Item] can be added from this [Inventory] checking if all [member Item.tags_required_to_remove] exists in this [Inventory]
192 | func can_remove(item: Item) -> bool:
193 | if item.tags_required_to_remove.size() == 0:
194 | return true
195 |
196 | for tag in item.tags_required_to_remove:
197 | if not tags.has(tag):
198 | return false
199 |
200 | return true
201 |
202 | ## Counts all items.
203 | ## [br]If [code]count_stacks[/code] is passed to [code]true[/code], the count will returns also count all stacked quantities.
204 | func count_items(count_stacks: bool = false) -> int:
205 | if count_stacks:
206 | var count = 0
207 |
208 | for item in items:
209 | count += item.quantity_current
210 |
211 | return count
212 | else:
213 | return items.size()
214 |
215 |
216 | ## Counts all items by a given predicate.
217 | ## [br]If [code]count_stacks[/code] is passed to [code]true[/code], the count will returns also count all stacked quantities.
218 | func count_items_by(predicate: Callable, count_stacks: bool = false) -> int:
219 | var num := 0
220 |
221 | for i in items:
222 | if predicate.call(i):
223 | if count_stacks:
224 | num += i.quantity_current
225 | else:
226 | num += 1
227 |
228 | return num
229 |
230 |
231 | ## Filters [member Inventory.items] by the [Callable] predicate.
232 | ## [br]It returns all [Item]s which satisfy the predicate.
233 | func filter_by(predicate: Callable) -> Array[Item]:
234 | var out: Array[Item] = []
235 |
236 | for i in items:
237 | if predicate.call(i):
238 | out.append(i)
239 |
240 | return out
241 |
242 |
243 | ## Find first [Item] which satisfies the [Callable] predicate and returns it.
244 | ## [br]If none are found, then returns [code]null[/code]
245 | func find_by(predicate: Callable) -> Item:
246 | for i in items:
247 | if predicate.call(i):
248 | return i
249 | return null
250 |
251 |
252 | ## Instantiate the [member Item.scene] if any, otherwise returns [code]null[/code].
253 | ## [br]If it can be istantiated, it will try to call a [code]_from_item(item: Item, inventory: Inventory) -> void[/code] method bound to the packed scene.
254 | func instantiate_item(item: Item) -> Node:
255 | if item.scene != null and item.scene.can_instantiate():
256 | var instance = item.scene.instantiate()
257 |
258 | if instance.has_method("_from_item"):
259 | instance.call("_from_item", item, self)
260 |
261 | return instance
262 |
263 | return null
264 |
265 |
266 | ## Removes an item from the inventory
267 | ## [br]If [code]bypass_tag_check[/code] is set to [code]true[/code] then the check for [member Item.tags_required_to_remove] will not be performed.
268 | func remove_item(item: Item, bypass_tag_check: bool = false) -> void:
269 | if not can_remove(item) and not bypass_tag_check:
270 | refused_to_remove.emit(item)
271 | return
272 |
273 | var index = items.find(item)
274 |
275 | if index >= 0:
276 | items.remove_at(index)
277 | item_removed.emit(item)
278 | _handle_lifecycle_tags(LifeCycle.Removed, item)
279 |
280 |
281 | ## Removes many items from an inventory
282 | ## [br]If [code]bypass_tag_check[/code] is set to [code]true[/code] then the check for [member Item.tags_required_to_remove] will not be performed.
283 | func remove_items(_items: Array[Item], bypass_tag_check: bool = false) -> void:
284 | for i in _items:
285 | remove_item(i)
286 |
287 |
288 | ## Removes items using a [Callable] predicate. If the predicate is satisfied by the [Item], the [Item] is going to be removed entirely.
289 | ## [br]If [code]bypass_tag_check[/code] is set to [code]true[/code] then the check for [member Item.tags_required_to_remove] will not be performed.
290 | func remove_items_by(predicate: Callable, bypass_tag_check: bool = false) -> void:
291 | for i in items:
292 | if predicate.call(i):
293 | remove_item(i, bypass_tag_check)
294 |
295 |
296 | ## Removes a tag.
297 | func remove_tag(tag: String) -> void:
298 | if tags.has(tag):
299 | tags.erase(tag)
300 |
301 |
302 | ## Removes many tags.
303 | func remove_tags(_tags: Array[String]) -> void:
304 | for t in _tags:
305 | if tags.has(t):
306 | tags.erase(t)
307 |
308 |
309 | ## Programmatically setups an [Inventory]
310 | func setup() -> void:
311 | if not equipment_path.is_empty():
312 | equipment = get_node(equipment_path) as Equipment
313 |
314 | if not Engine.is_editor_hint() and not owner_path.is_empty():
315 | var _owner = get_node(owner_path)
316 | _owner.set_meta("ggsInventory", self)
317 |
318 | for i in items:
319 | add_tags(i.tags_added_on_add)
320 |
--------------------------------------------------------------------------------